Skip to content

Commit

Permalink
Use java.lang.ClassValue in ClassInfo (when available)
Browse files Browse the repository at this point in the history
  • Loading branch information
candrews committed Jun 22, 2014
1 parent 1f0b5e9 commit 97d78e9
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 47 deletions.
115 changes: 68 additions & 47 deletions src/main/org/codehaus/groovy/reflection/ClassInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import groovy.lang.*;

import org.codehaus.groovy.reflection.GroovyClassValue.ComputeValue;
import org.codehaus.groovy.reflection.stdclasses.*;
import org.codehaus.groovy.util.*;
import org.codehaus.groovy.vmplugin.VMPluginFactory;
Expand All @@ -34,34 +35,45 @@
*
* @author Alex.Tkachman
*/
public class ClassInfo extends ManagedConcurrentMap.Entry<Class,ClassInfo> {
public class ClassInfo {

private static final Set<ClassInfo> modifiedExpandos = new HashSet<ClassInfo>();

private final LazyCachedClassRef cachedClassRef;
private final LazyClassLoaderRef artifactClassLoader;
private final LockableObject lock = new LockableObject();
public final int hash;
private final Class klazz;

private volatile int version;

private MetaClass strongMetaClass;
private ManagedReference<MetaClass> weakMetaClass;
MetaMethod[] dgmMetaMethods = CachedClass.EMPTY;
MetaMethod[] newMetaMethods = CachedClass.EMPTY;
private ManagedConcurrentMap perInstanceMetaClassMap;
private ManagedConcurrentMap<Object, MetaClass> perInstanceMetaClassMap;

private static ReferenceBundle softBundle = ReferenceBundle.getSoftBundle();
private static ReferenceBundle weakBundle = ReferenceBundle.getWeakBundle();
private static final ClassInfoSet globalClassSet = new ClassInfoSet(softBundle);

ClassInfo(ManagedConcurrentMap.Segment segment, Class klazz, int hash) {
super (softBundle, segment, klazz, hash);

private static final GroovyClassValue<ClassInfo> globalClassValue = GroovyClassValueFactory.createGroovyClassValue(new ComputeValue<ClassInfo>(){
@Override
public ClassInfo computeValue(Class<?> type) {
ClassInfo ret = new ClassInfo(type);
globalClassSet.add(ret);
return ret;
}
});

private static final GlobalClassSet globalClassSet = new GlobalClassSet();

ClassInfo(Class klazz) {
this.hash = System.identityHashCode(klazz);
this.klazz = klazz;
if (ClassInfo.DebugRef.debug)
new DebugRef(klazz);
new ClassInfoCleanup(this);

this.hash = hash;
cachedClassRef = new LazyCachedClassRef(softBundle, this);
artifactClassLoader = new LazyClassLoaderRef(softBundle, this);
}
Expand Down Expand Up @@ -101,7 +113,7 @@ public ClassLoaderForClassArtifacts getArtifactClassLoader() {
public static ClassInfo getClassInfo (Class cls) {
LocalMap map = getLocalClassInfoMap();
if (map!=null) return map.get(cls);
return (ClassInfo) globalClassSet.getOrPut(cls,null);
return (ClassInfo) globalClassValue.get(cls);
}

private static LocalMap getLocalClassInfoMap() {
Expand All @@ -118,7 +130,7 @@ public static Collection<ClassInfo> getAllClassInfo () {
return localClassInfos != null ? localClassInfos : getAllGlobalClassInfo();
}

private static Collection getAllGlobalClassInfo() {
private static Collection<ClassInfo> getAllGlobalClassInfo() {
return globalClassSet.values();
}

Expand Down Expand Up @@ -206,7 +218,7 @@ private MetaClass getMetaClassUnderLock() {
return answer;
}

answer = mccHandle.create(get(), metaClassRegistry);
answer = mccHandle.create(klazz, metaClassRegistry);
answer.initialize();

if (GroovySystem.isKeepJavaMetaClasses()) {
Expand Down Expand Up @@ -259,14 +271,6 @@ public static int fullSize () {
return globalClassSet.fullSize();
}

public void finalizeRef() {
setStrongMetaClass(null);
cachedClassRef.clear();
artifactClassLoader.clear();

super.finalizeRef();
}

private static CachedClass createCachedClass(Class klazz, ClassInfo classInfo) {
if (klazz == Object.class)
return new ObjectCachedClass(classInfo);
Expand Down Expand Up @@ -343,7 +347,7 @@ public void setPerInstanceMetaClass(Object obj, MetaClass metaClass) {

if (metaClass != null) {
if (perInstanceMetaClassMap == null)
perInstanceMetaClassMap = new ManagedConcurrentMap(ReferenceBundle.getWeakBundle());
perInstanceMetaClassMap = new ManagedConcurrentMap<Object, MetaClass>(ReferenceBundle.getWeakBundle());

perInstanceMetaClassMap.put(obj, metaClass);
}
Expand All @@ -358,29 +362,6 @@ public boolean hasPerInstanceMetaClasses () {
return perInstanceMetaClassMap != null;
}

public static class ClassInfoSet extends ManagedConcurrentMap<Class,ClassInfo> {
public ClassInfoSet(ReferenceBundle bundle) {
super(bundle);
}

protected Segment createSegment(Object segmentInfo, int cap) {
ReferenceBundle bundle = (ReferenceBundle) segmentInfo;
if (bundle==null) throw new IllegalArgumentException("bundle must not be null ");

return new Segment(bundle, cap);
}

static final class Segment extends ManagedConcurrentMap.Segment<Class,ClassInfo> {
Segment(ReferenceBundle bundle, int initialCapacity) {
super(bundle, initialCapacity);
}

protected ClassInfo createEntry(Class key, int hash, ClassInfo unused) {
return new ClassInfo(this, key, hash);
}
}
}

private static final class LocalMap extends HashMap<Class,ClassInfo> {

private static final int CACHE_SIZE = 5;
Expand All @@ -397,7 +378,7 @@ private static final class LocalMap extends HashMap<Class,ClassInfo> {
private int nextCacheEntry;

private final ClassInfo[] cache = new ClassInfo[CACHE_SIZE];
private static final ClassInfo NOINFO = new ClassInfo(null,null,0);
private static final ClassInfo NOINFO = new ClassInfo(Void.class);

private LocalMap() {
for (int i = 0; i < cache.length; i++) {
Expand All @@ -414,7 +395,7 @@ public ClassInfo get(Class key) {
if (info != null)
return putToCache(info);

return putToCache((ClassInfo) globalClassSet.getOrPut(key,null));
return putToCache((ClassInfo) globalClassValue.get(key));
}

private ClassInfo getFromCache (Class klazz) {
Expand All @@ -423,7 +404,7 @@ private ClassInfo getFromCache (Class klazz) {
k += CACHE_SIZE;

final ClassInfo info = cache[k];
if (klazz == info.get()) {
if (klazz == info.klazz) {
nextCacheEntry = k+1;
if (nextCacheEntry == CACHE_SIZE)
nextCacheEntry = 0;
Expand Down Expand Up @@ -481,7 +462,7 @@ private static class LazyCachedClassRef extends LazyReference<CachedClass> {
}

public CachedClass initValue() {
return createCachedClass(info.get(), info);
return createCachedClass(info.klazz, info);
}
}

Expand All @@ -494,7 +475,21 @@ private static class LazyClassLoaderRef extends LazyReference<ClassLoaderForClas
}

public ClassLoaderForClassArtifacts initValue() {
return new ClassLoaderForClassArtifacts(info.get());
return new ClassLoaderForClassArtifacts(info.klazz);
}
}

private static class ClassInfoCleanup extends ManagedReference<ClassInfo> {

public ClassInfoCleanup(ClassInfo classInfo) {
super(weakBundle, classInfo);
}

public void finalizeRef() {
ClassInfo classInfo = get();
classInfo.setStrongMetaClass(null);
classInfo.cachedClassRef.clear();
classInfo.artifactClassLoader.clear();
}
}

Expand All @@ -516,4 +511,30 @@ public void finalizeRef() {
super.finalizeReference();
}
}

private static class GlobalClassSet {

private ManagedLinkedList<ClassInfo> items = new ManagedLinkedList<ClassInfo>(weakBundle);

public int size(){
return values().size();
}

public int fullSize(){
return values().size();
}

public Collection<ClassInfo> values(){
synchronized(items){
return Arrays.asList(items.toArray(new ClassInfo[0]));
}
}

public void add(ClassInfo value){
synchronized(items){
items.add(value);
}
}

}
}
18 changes: 18 additions & 0 deletions src/main/org/codehaus/groovy/reflection/GroovyClassValue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.codehaus.groovy.reflection;

/** Abstraction for Java version dependent ClassValue implementations.
* @see java.lang.ClassValue
*
* @param <T>
*/
public interface GroovyClassValue<T> {

public static interface ComputeValue<T>{
T computeValue(Class<?> type);
}

T get(Class<?> type);

void remove(Class<?> type);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.codehaus.groovy.reflection;

import java.lang.reflect.Constructor;

import org.codehaus.groovy.reflection.GroovyClassValue.ComputeValue;

class GroovyClassValueFactory {
private static final Constructor groovyClassValueConstructor;

static {
Class groovyClassValueClass;
try{
Class.forName("java.lang.ClassValue");
try{
groovyClassValueClass = Class.forName("org.codehaus.groovy.reflection.v7.GroovyClassValueJava7");
}catch(Exception e){
throw new RuntimeException(e); // this should never happen, but if it does, let it propagate and be fatal
}
}catch(ClassNotFoundException e){
groovyClassValueClass = GroovyClassValuePreJava7.class;
}
try{
groovyClassValueConstructor = groovyClassValueClass.getConstructor(ComputeValue.class);
}catch(Exception e){
throw new RuntimeException(e); // this should never happen, but if it does, let it propagate and be fatal
}
}

public static <T> GroovyClassValue<T> createGroovyClassValue(ComputeValue<T> computeValue){
try {
return (GroovyClassValue<T>) groovyClassValueConstructor.newInstance(computeValue);
} catch (Exception e) {
throw new RuntimeException(e); // this should never happen, but if it does, let it propagate and be fatal
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.codehaus.groovy.reflection;

import org.codehaus.groovy.util.ManagedConcurrentMap;
import org.codehaus.groovy.util.ReferenceBundle;

/** Approximation of Java 7's {@link java.lang.ClassValue} that works on earlier versions of Java.
* Note that this implementation isn't as good at Java 7's; it doesn't allow for some GC'ing that Java 7 would allow.
* But, it's good enough for our use.
*
* @param <T>
*/
class GroovyClassValuePreJava7<T> implements GroovyClassValue<T> {
private static final ReferenceBundle weakBundle = ReferenceBundle.getWeakBundle();

private class EntryWithValue extends ManagedConcurrentMap.EntryWithValue<Class<?>,T>{

public EntryWithValue(GroovyClassValuePreJava7Segment segment, Class<?> key, int hash) {
super(weakBundle, segment, key, hash, computeValue.computeValue(key));
}

@Override
public void setValue(T value) {
if(value!=null) super.setValue(value);
}
}

private class GroovyClassValuePreJava7Segment extends ManagedConcurrentMap.Segment<Class<?>,T> {

GroovyClassValuePreJava7Segment(ReferenceBundle bundle, int initialCapacity) {
super(bundle, initialCapacity);
}

@Override
protected EntryWithValue createEntry(Class<?> key, int hash,
T unused) {
return new EntryWithValue(this, key, hash);
}
}

private class GroovyClassValuePreJava7Map extends ManagedConcurrentMap<Class<?>,T> {

public GroovyClassValuePreJava7Map() {
super(weakBundle);
}

@Override
protected GroovyClassValuePreJava7Segment createSegment(Object segmentInfo, int cap) {
ReferenceBundle bundle = (ReferenceBundle) segmentInfo;
if (bundle==null) throw new IllegalArgumentException("bundle must not be null ");
return new GroovyClassValuePreJava7Segment(bundle, cap);
}

}

private final ComputeValue<T> computeValue;

private final GroovyClassValuePreJava7Map map = new GroovyClassValuePreJava7Map();

public GroovyClassValuePreJava7(ComputeValue<T> computeValue){
this.computeValue = computeValue;
}

@Override
public T get(Class<?> type) {
// the value isn't use in the getOrPut call - see the EntryWithValue constructor above
T value = ((EntryWithValue)map.getOrPut(type, null)).getValue();
//all entries are guaranteed to be EntryWithValue. Value can only be null if computeValue returns null
return value;
}

@Override
public void remove(Class<?> type) {
map.remove(type);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.codehaus.groovy.reflection.v7;

import org.codehaus.groovy.reflection.GroovyClassValue;
import org.codehaus.groovy.reflection.GroovyClassValue.ComputeValue;

/** GroovyClassValue implementaion that simply delegates to Java 7's java.lang.ClassValue
* @see java.lang.ClassValue
*
* @param <T>
*/
public class GroovyClassValueJava7<T> extends ClassValue<T> implements GroovyClassValue<T> {
private final ComputeValue<T> computeValue;
public GroovyClassValueJava7(ComputeValue<T> computeValue){
this.computeValue = computeValue;
}
@Override
protected T computeValue(Class<?> type) {
return computeValue.computeValue(type);
}
}
Loading

0 comments on commit 97d78e9

Please sign in to comment.