You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2020/07/27 23:46:47 UTC
[groovy] branch master updated: GROOVY-9631: Replace legacy data
structure with Java collection(master only)(closes #1306)
This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new acd53b3 GROOVY-9631: Replace legacy data structure with Java collection(master only)(closes #1306)
acd53b3 is described below
commit acd53b38e72a5e6f64a40f7cc0d06df3472774a1
Author: Daniel Sun <su...@apache.org>
AuthorDate: Sun Jul 12 23:06:46 2020 +0800
GROOVY-9631: Replace legacy data structure with Java collection(master only)(closes #1306)
---
src/main/java/groovy/lang/MetaClassImpl.java | 132 +++++++++---------
.../java/org/codehaus/groovy/ast/ClassHelper.java | 4 +-
.../org/codehaus/groovy/reflection/ClassInfo.java | 6 +-
.../reflection/GroovyClassValuePreJava7.java | 1 +
.../groovy/reflection/MixinInMetaClass.java | 19 ++-
.../groovy/reflection/ReflectionCache.java | 2 +
.../groovy/runtime/memoize/MemoizeCache.java | 2 +-
.../metaclass/ThreadManagedMetaBeanProperty.java | 16 +--
.../groovy/util/AbstractConcurrentMap.java | 1 +
.../groovy/util/AbstractConcurrentMapBase.java | 1 +
.../codehaus/groovy/util/ComplexKeyHashMap.java | 1 +
.../codehaus/groovy/util/ManagedConcurrentMap.java | 1 +
.../groovy/util/ManagedIdentityConcurrentMap.java | 149 +++++++++++++++++++++
.../org/codehaus/groovy/util/SingleKeyHashMap.java | 1 +
.../org/codehaus/groovy/util/TripleKeyHashMap.java | 1 +
.../util/ManagedIdentityConcurrentMapTest.groovy | 60 +++++++++
.../groovy-json/src/spec/test/json/JsonTest.groovy | 4 +-
.../test/groovy/groovy/json/JsonOutputTest.groovy | 28 ++--
18 files changed, 324 insertions(+), 105 deletions(-)
diff --git a/src/main/java/groovy/lang/MetaClassImpl.java b/src/main/java/groovy/lang/MetaClassImpl.java
index 59d45e6..3aa3a3d 100644
--- a/src/main/java/groovy/lang/MetaClassImpl.java
+++ b/src/main/java/groovy/lang/MetaClassImpl.java
@@ -101,9 +101,8 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
@@ -151,14 +150,14 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
protected final boolean isMap;
protected final MetaMethodIndex metaMethodIndex;
- private final Index classPropertyIndex = new MethodIndex();
- private final SingleKeyHashMap staticPropertyIndex = new SingleKeyHashMap();
- private final Map<String, MetaMethod> listeners = new HashMap<>();
+ private final Map<CachedClass, LinkedHashMap<String, MetaProperty>> classPropertyIndex = new LinkedHashMap<>();
+ private final Map<String, MetaProperty> staticPropertyIndex = new LinkedHashMap<>();
+ private final Map<String, MetaMethod> listeners = new LinkedHashMap<>();
private final List<MetaMethod> allMethods = new ArrayList<>();
// we only need one of these that can be reused over and over.
private final MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
- private final Index classPropertyIndexForSuper = new MethodIndex();
- private final Set<MetaMethod> newGroovyMethodsSet = new HashSet<>();
+ private final Map<CachedClass, LinkedHashMap<String, MetaProperty>> classPropertyIndexForSuper = new LinkedHashMap<>();
+ private final Set<MetaMethod> newGroovyMethodsSet = new LinkedHashSet<>();
private final MetaMethod[] myNewMetaMethods;
private final MetaMethod[] additionalMetaMethods;
@@ -290,13 +289,13 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
public MetaProperty getMetaProperty(final String name) {
MetaProperty metaProperty = null;
- SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
- metaProperty = (MetaProperty) propertyMap.get(name);
+ LinkedHashMap<String, MetaProperty> propertyMap = classPropertyIndex.computeIfAbsent(theCachedClass, k -> new LinkedHashMap<>());
+ metaProperty = propertyMap.get(name);
if (metaProperty == null) {
- metaProperty = (MetaProperty) staticPropertyIndex.get(name);
+ metaProperty = staticPropertyIndex.get(name);
if (metaProperty == null) {
- propertyMap = classPropertyIndexForSuper.getNotNull(theCachedClass);
- metaProperty = (MetaProperty) propertyMap.get(name);
+ propertyMap = classPropertyIndexForSuper.computeIfAbsent(theCachedClass, k -> new LinkedHashMap<>());
+ metaProperty = propertyMap.get(name);
if (metaProperty == null) {
MetaBeanProperty property = findPropertyInClassHierarchy(name, theCachedClass);
if (property != null) {
@@ -1632,7 +1631,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
};
l.sort(comp);
int found = -1;
- for (int i = 0; i < l.size(); i++) {
+ for (int i = 0, n = l.size(); i < n; i++) {
if (l.get(i) != constructor) continue;
found = i;
break;
@@ -2215,16 +2214,16 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
*/
public List<MetaProperty> getProperties() {
checkInitalised();
- SingleKeyHashMap propertyMap = classPropertyIndex.getNullable(theCachedClass);
+ LinkedHashMap<String, MetaProperty> propertyMap = classPropertyIndex.get(theCachedClass);
if (propertyMap == null) {
// GROOVY-6903: May happen in some special environment, like under Android, due
// to classloading issues
- propertyMap = new SingleKeyHashMap();
+ propertyMap = new LinkedHashMap<>();
}
// simply return the values of the metaproperty map as a List
- List ret = new ArrayList(propertyMap.size());
- for (ComplexKeyHashMap.EntryIterator iter = propertyMap.getEntrySetIterator(); iter.hasNext(); ) {
- MetaProperty element = (MetaProperty) ((SingleKeyHashMap.Entry) iter.next()).value;
+ List<MetaProperty> ret = new ArrayList<>(propertyMap.size());
+ for (Map.Entry<String, MetaProperty> stringMetaPropertyEntry : propertyMap.entrySet()) {
+ MetaProperty element = stringMetaPropertyEntry.getValue();
if (element instanceof CachedField) continue;
// filter out DGM beans
if (element instanceof MetaBeanProperty) {
@@ -2372,23 +2371,21 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
*
* @param propertyDescriptors the property descriptors
*/
- @SuppressWarnings("unchecked")
private void setupProperties(PropertyDescriptor[] propertyDescriptors) {
if (theCachedClass.isInterface) {
LinkedList<CachedClass> superClasses = new LinkedList<>();
superClasses.add(ReflectionCache.OBJECT_CLASS);
- Set interfaces = theCachedClass.getInterfaces();
+ LinkedList<CachedClass> superInterfaces = new LinkedList<>(theCachedClass.getInterfaces());
- LinkedList<CachedClass> superInterfaces = new LinkedList<CachedClass>(interfaces);
// sort interfaces so that we may ensure a deterministic behaviour in case of
// ambiguous fields (class implementing two interfaces using the same field)
if (superInterfaces.size() > 1) {
superInterfaces.sort(CACHED_CLASS_NAME_COMPARATOR);
}
- SingleKeyHashMap iPropertyIndex = classPropertyIndex.getNotNull(theCachedClass);
+ Map<String, MetaProperty> iPropertyIndex = classPropertyIndex.computeIfAbsent(theCachedClass, k -> new LinkedHashMap<>());
for (CachedClass iclass : superInterfaces) {
- SingleKeyHashMap sPropertyIndex = classPropertyIndex.getNotNull(iclass);
+ Map<String, MetaProperty> sPropertyIndex = classPropertyIndex.computeIfAbsent(iclass, k -> new LinkedHashMap<>());
copyNonPrivateFields(sPropertyIndex, iPropertyIndex, null);
addFields(iclass, iPropertyIndex);
}
@@ -2400,21 +2397,22 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
makeStaticPropertyIndex();
} else {
LinkedList<CachedClass> superClasses = getSuperClasses();
- LinkedList<CachedClass> interfaces = new LinkedList<>(theCachedClass.getInterfaces());
+ LinkedList<CachedClass> superInterfaces = new LinkedList<>(theCachedClass.getInterfaces());
+
// sort interfaces so that we may ensure a deterministic behaviour in case of
// ambiguous fields (class implementing two interfaces using the same field)
- if (interfaces.size() > 1) {
- interfaces.sort(CACHED_CLASS_NAME_COMPARATOR);
+ if (superInterfaces.size() > 1) {
+ superInterfaces.sort(CACHED_CLASS_NAME_COMPARATOR);
}
// if this an Array, then add the special read-only "length" property
if (theCachedClass.isArray) {
- SingleKeyHashMap map = new SingleKeyHashMap();
+ LinkedHashMap<String, MetaProperty> map = new LinkedHashMap<>();
map.put("length", arrayLengthProperty);
classPropertyIndex.put(theCachedClass, map);
}
- inheritStaticInterfaceFields(superClasses, new LinkedHashSet(interfaces));
+ inheritStaticInterfaceFields(superClasses, new LinkedHashSet<>(superInterfaces));
inheritFields(superClasses);
applyPropertyDescriptors(propertyDescriptors);
@@ -2428,11 +2426,9 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
}
private void makeStaticPropertyIndex() {
- SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
- for (ComplexKeyHashMap.EntryIterator iter = propertyMap.getEntrySetIterator(); iter.hasNext(); ) {
- SingleKeyHashMap.Entry entry = ((SingleKeyHashMap.Entry) iter.next());
-
- MetaProperty mp = (MetaProperty) entry.getValue();
+ LinkedHashMap<String, MetaProperty> propertyMap = classPropertyIndex.computeIfAbsent(theCachedClass, k -> new LinkedHashMap<>());
+ for (Map.Entry<String, MetaProperty> entry : propertyMap.entrySet()) {
+ MetaProperty mp = entry.getValue();
if (mp instanceof CachedField) {
CachedField mfp = (CachedField) mp;
if (!mfp.isStatic()) continue;
@@ -2499,32 +2495,29 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
return result;
}
- private void copyClassPropertyIndexForSuper(Index dest) {
- for (ComplexKeyHashMap.EntryIterator iter = classPropertyIndex.getEntrySetIterator(); iter.hasNext(); ) {
- SingleKeyHashMap.Entry entry = (SingleKeyHashMap.Entry) iter.next();
- SingleKeyHashMap newVal = new SingleKeyHashMap();
- dest.put((CachedClass) entry.getKey(), newVal);
+ private void copyClassPropertyIndexForSuper(Map<CachedClass, LinkedHashMap<String, MetaProperty>> dest) {
+ for (Map.Entry<CachedClass, LinkedHashMap<String, MetaProperty>> entry : classPropertyIndex.entrySet()) {
+ LinkedHashMap<String, MetaProperty> newVal = new LinkedHashMap<>();
+ dest.put(entry.getKey(), newVal);
}
}
- private void inheritStaticInterfaceFields(LinkedList superClasses, Set interfaces) {
- for (Object anInterface : interfaces) {
- CachedClass iclass = (CachedClass) anInterface;
- SingleKeyHashMap iPropertyIndex = classPropertyIndex.getNotNull(iclass);
+ private void inheritStaticInterfaceFields(List<CachedClass> superClasses, Set<CachedClass> interfaces) {
+ for (CachedClass iclass : interfaces) {
+ LinkedHashMap<String, MetaProperty> iPropertyIndex = classPropertyIndex.computeIfAbsent(iclass, k -> new LinkedHashMap<>());
addFields(iclass, iPropertyIndex);
- for (Object superClass : superClasses) {
- CachedClass sclass = (CachedClass) superClass;
- if (!iclass.getTheClass().isAssignableFrom(sclass.getTheClass())) continue;
- SingleKeyHashMap sPropertyIndex = classPropertyIndex.getNotNull(sclass);
+ for (CachedClass superClass : superClasses) {
+ if (!iclass.getTheClass().isAssignableFrom(superClass.getTheClass())) continue;
+ LinkedHashMap<String, MetaProperty> sPropertyIndex = classPropertyIndex.computeIfAbsent(superClass, k -> new LinkedHashMap<>());
copyNonPrivateFields(iPropertyIndex, sPropertyIndex, null);
}
}
}
private void inheritFields(LinkedList<CachedClass> superClasses) {
- SingleKeyHashMap last = null;
+ LinkedHashMap<String, MetaProperty> last = null;
for (CachedClass klass : superClasses) {
- SingleKeyHashMap propertyIndex = classPropertyIndex.getNotNull(klass);
+ LinkedHashMap<String, MetaProperty> propertyIndex = classPropertyIndex.computeIfAbsent(klass, k -> new LinkedHashMap<>());
if (last != null) {
copyNonPrivateFields(last, propertyIndex, klass);
}
@@ -2533,15 +2526,14 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
}
}
- private static void addFields(CachedClass klass, SingleKeyHashMap propertyIndex) {
+ private static void addFields(CachedClass klass, Map<String, MetaProperty> propertyIndex) {
for (CachedField field : klass.getFields()) {
propertyIndex.put(field.getName(), field);
}
}
- private static void copyNonPrivateFields(SingleKeyHashMap from, SingleKeyHashMap to, @Nullable CachedClass klass) {
- for (ComplexKeyHashMap.EntryIterator it = from.getEntrySetIterator(); it.hasNext(); ) {
- SingleKeyHashMap.Entry entry = (SingleKeyHashMap.Entry) it.next();
+ private static void copyNonPrivateFields(Map<String, MetaProperty> from, Map<String, MetaProperty> to, @Nullable CachedClass klass) {
+ for (Map.Entry<String, MetaProperty> entry : from.entrySet()) {
CachedField field = (CachedField) entry.getValue();
int modifiers = field.getModifiers();
if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers) || (!Modifier.isPrivate(modifiers)
@@ -2551,11 +2543,11 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
}
}
- private void applyStrayPropertyMethods(LinkedList<CachedClass> superClasses, Index classPropertyIndex, boolean isThis) {
+ private void applyStrayPropertyMethods(LinkedList<CachedClass> superClasses, Map<CachedClass, LinkedHashMap<String, MetaProperty>> classPropertyIndex, boolean isThis) {
// now look for any stray getters that may be used to define a property
for (CachedClass klass : superClasses) {
MetaMethodIndex.Header header = metaMethodIndex.getHeader(klass.getTheClass());
- SingleKeyHashMap propertyIndex = classPropertyIndex.getNotNull(klass);
+ Map<String, MetaProperty> propertyIndex = classPropertyIndex.computeIfAbsent(klass, k -> new LinkedHashMap<>());
for (MetaMethodIndex.Entry e = header.head; e != null; e = e.nextClassEntry) {
String methodName = e.name;
// name too short?
@@ -2639,9 +2631,9 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
}
}
- private static void createMetaBeanProperty(SingleKeyHashMap propertyIndex, String propName, boolean isGetter, MetaMethod propertyMethod) {
+ private static void createMetaBeanProperty(Map<String, MetaProperty> propertyIndex, String propName, boolean isGetter, MetaMethod propertyMethod) {
// is this property already accounted for?
- MetaProperty mp = (MetaProperty) propertyIndex.get(propName);
+ MetaProperty mp = propertyIndex.get(propName);
MetaProperty newMp = makeReplacementMetaProperty(mp, propName, isGetter, propertyMethod);
if (newMp != mp) {
propertyIndex.put(propName, newMp);
@@ -2695,10 +2687,10 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
if (staticProperty != null) {
staticPropertyIndex.put(mp.getName(), mp);
} else {
- SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
+ LinkedHashMap<String, MetaProperty> propertyMap = classPropertyIndex.computeIfAbsent(theCachedClass, k -> new LinkedHashMap<>());
//keep field
CachedField field;
- MetaProperty old = (MetaProperty) propertyMap.get(mp.getName());
+ MetaProperty old = propertyMap.get(mp.getName());
if (old != null) {
if (old instanceof MetaBeanProperty) {
field = ((MetaBeanProperty) old).getField();
@@ -2892,13 +2884,13 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
CachedClass clazz = ReflectionCache.getCachedClass(_clazz);
while (true) {
- SingleKeyHashMap propertyMap;
+ Map<String, MetaProperty> propertyMap;
if (useStatic) {
propertyMap = staticPropertyIndex;
} else if (useSuper) {
- propertyMap = classPropertyIndexForSuper.getNullable(clazz);
+ propertyMap = classPropertyIndexForSuper.get(clazz);
} else {
- propertyMap = classPropertyIndex.getNullable(clazz);
+ propertyMap = classPropertyIndex.get(clazz);
}
if (propertyMap == null) {
if (clazz != theCachedClass) {
@@ -2908,22 +2900,22 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
return null;
}
}
- return (MetaProperty) propertyMap.get(name);
+ return propertyMap.get(name);
}
}
private MetaProperty getMetaProperty(String name, boolean useStatic) {
CachedClass clazz = theCachedClass;
- SingleKeyHashMap propertyMap;
+ Map<String, MetaProperty> propertyMap;
if (useStatic) {
propertyMap = staticPropertyIndex;
} else {
- propertyMap = classPropertyIndex.getNullable(clazz);
+ propertyMap = classPropertyIndex.get(clazz);
}
if (propertyMap == null) {
return null;
}
- return (MetaProperty) propertyMap.get(name);
+ return propertyMap.get(name);
}
/**
@@ -3938,6 +3930,7 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
metaMethodIndex.clearCaches();
}
+ @Deprecated
private static final SingleKeyHashMap.Copier NAME_INDEX_COPIER = value -> {
if (value instanceof FastArray) {
return ((FastArray) value).copy();
@@ -3946,8 +3939,13 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
}
};
+ @Deprecated
private static final SingleKeyHashMap.Copier METHOD_INDEX_COPIER = value -> SingleKeyHashMap.copy(new SingleKeyHashMap(false), (SingleKeyHashMap) value, NAME_INDEX_COPIER);
+ /**
+ * @deprecated use {@link LinkedHashMap} instead
+ */
+ @Deprecated
static class MethodIndex extends Index {
public MethodIndex(boolean b) {
super(false);
@@ -3970,6 +3968,10 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
}
}
+ /**
+ * @deprecated use {@link LinkedHashMap} instead
+ */
+ @Deprecated
public static class Index extends SingleKeyHashMap {
public Index(int size) {
diff --git a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
index 17bcc8d..54b320f 100644
--- a/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
+++ b/src/main/java/org/codehaus/groovy/ast/ClassHelper.java
@@ -52,7 +52,7 @@ import org.codehaus.groovy.runtime.GeneratedClosure;
import org.codehaus.groovy.runtime.GeneratedLambda;
import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
import org.codehaus.groovy.transform.trait.Traits;
-import org.codehaus.groovy.util.ManagedConcurrentMap;
+import org.codehaus.groovy.util.ManagedIdentityConcurrentMap;
import org.codehaus.groovy.util.ReferenceBundle;
import org.codehaus.groovy.vmplugin.VMPluginFactory;
import org.objectweb.asm.Opcodes;
@@ -408,7 +408,7 @@ public class ClassHelper {
}
static class ClassHelperCache {
- static ManagedConcurrentMap<Class, SoftReference<ClassNode>> classCache = new ManagedConcurrentMap<Class, SoftReference<ClassNode>>(ReferenceBundle.getWeakBundle());
+ static ManagedIdentityConcurrentMap<Class, SoftReference<ClassNode>> classCache = new ManagedIdentityConcurrentMap<Class, SoftReference<ClassNode>>(ReferenceBundle.getWeakBundle());
}
public static boolean isSAMType(final ClassNode type) {
diff --git a/src/main/java/org/codehaus/groovy/reflection/ClassInfo.java b/src/main/java/org/codehaus/groovy/reflection/ClassInfo.java
index e55b2ed..b0c00e1 100644
--- a/src/main/java/org/codehaus/groovy/reflection/ClassInfo.java
+++ b/src/main/java/org/codehaus/groovy/reflection/ClassInfo.java
@@ -46,7 +46,7 @@ import org.codehaus.groovy.util.Finalizable;
import org.codehaus.groovy.util.LazyReference;
import org.codehaus.groovy.util.LockableObject;
import org.codehaus.groovy.util.ManagedConcurrentLinkedQueue;
-import org.codehaus.groovy.util.ManagedConcurrentMap;
+import org.codehaus.groovy.util.ManagedIdentityConcurrentMap;
import org.codehaus.groovy.util.ManagedReference;
import org.codehaus.groovy.util.ReferenceBundle;
import org.codehaus.groovy.vmplugin.VMPluginFactory;
@@ -82,7 +82,7 @@ public class ClassInfo implements Finalizable {
private ManagedReference<MetaClass> weakMetaClass;
MetaMethod[] dgmMetaMethods = MetaMethod.EMPTY_ARRAY;
MetaMethod[] newMetaMethods = MetaMethod.EMPTY_ARRAY;
- private ManagedConcurrentMap<Object, MetaClass> perInstanceMetaClassMap;
+ private ManagedIdentityConcurrentMap<Object, MetaClass> perInstanceMetaClassMap;
private static final ReferenceBundle softBundle = ReferenceBundle.getSoftBundle();
private static final ReferenceBundle weakBundle = ReferenceBundle.getWeakBundle();
@@ -409,7 +409,7 @@ public class ClassInfo implements Finalizable {
if (metaClass != null) {
if (perInstanceMetaClassMap == null)
- perInstanceMetaClassMap = new ManagedConcurrentMap<Object, MetaClass>(ReferenceBundle.getWeakBundle());
+ perInstanceMetaClassMap = new ManagedIdentityConcurrentMap<Object, MetaClass>(ReferenceBundle.getWeakBundle());
perInstanceMetaClassMap.put(obj, metaClass);
}
diff --git a/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java b/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java
index 02f893c..7b95d64 100644
--- a/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java
+++ b/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java
@@ -28,6 +28,7 @@ import org.codehaus.groovy.util.ReferenceBundle;
*
* @param <T>
*/
+@Deprecated
class GroovyClassValuePreJava7<T> implements GroovyClassValue<T> {
private static final ReferenceBundle weakBundle = ReferenceBundle.getWeakBundle();
diff --git a/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java b/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
index afaf956..f9763e1 100644
--- a/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
+++ b/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
@@ -32,7 +32,7 @@ import org.codehaus.groovy.runtime.metaclass.MixedInMetaClass;
import org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaMethod;
import org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaProperty;
import org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod;
-import org.codehaus.groovy.util.ManagedConcurrentMap;
+import org.codehaus.groovy.util.ManagedIdentityConcurrentMap;
import org.codehaus.groovy.util.ReferenceBundle;
import java.lang.reflect.Modifier;
@@ -40,19 +40,18 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-public class MixinInMetaClass extends ManagedConcurrentMap {
+public class MixinInMetaClass {
final ExpandoMetaClass emc;
final CachedClass mixinClass;
final CachedConstructor constructor;
-
- private static final ReferenceBundle softBundle = ReferenceBundle.getSoftBundle();
+ private final ManagedIdentityConcurrentMap managedIdentityConcurrentMap =
+ new ManagedIdentityConcurrentMap(ReferenceBundle.getSoftBundle());
public MixinInMetaClass(ExpandoMetaClass emc, CachedClass mixinClass) {
- super(softBundle);
this.emc = emc;
this.mixinClass = mixinClass;
- constructor = findDefaultConstructor(mixinClass);
+ this.constructor = findDefaultConstructor(mixinClass);
emc.addMixinClass(this);
}
@@ -70,20 +69,20 @@ public class MixinInMetaClass extends ManagedConcurrentMap {
}
public synchronized Object getMixinInstance(Object object) {
- Object mixinInstance = get(object);
+ Object mixinInstance = managedIdentityConcurrentMap.get(object);
if (mixinInstance == null) {
mixinInstance = constructor.invoke(MetaClassHelper.EMPTY_ARRAY);
new MixedInMetaClass(mixinInstance, object);
- put(object, mixinInstance);
+ managedIdentityConcurrentMap.put(object, mixinInstance);
}
return mixinInstance;
}
public synchronized void setMixinInstance(Object object, Object mixinInstance) {
if (mixinInstance == null) {
- remove(object);
+ managedIdentityConcurrentMap.remove(object);
} else {
- put(object, mixinInstance);
+ managedIdentityConcurrentMap.put(object, mixinInstance);
}
}
diff --git a/src/main/java/org/codehaus/groovy/reflection/ReflectionCache.java b/src/main/java/org/codehaus/groovy/reflection/ReflectionCache.java
index 7f6c525..2b33315 100644
--- a/src/main/java/org/codehaus/groovy/reflection/ReflectionCache.java
+++ b/src/main/java/org/codehaus/groovy/reflection/ReflectionCache.java
@@ -26,8 +26,10 @@ public class ReflectionCache {
return TypeUtil.autoboxType(type);
}
+ @Deprecated
static TripleKeyHashMap mopNames = new TripleKeyHashMap();
+ @Deprecated // the method is never called
public static String getMOPMethodName(CachedClass declaringClass, String name, boolean useThis) {
TripleKeyHashMap.Entry mopNameEntry = mopNames.getOrPut(declaringClass, name, useThis);
if (mopNameEntry.value == null) {
diff --git a/src/main/java/org/codehaus/groovy/runtime/memoize/MemoizeCache.java b/src/main/java/org/codehaus/groovy/runtime/memoize/MemoizeCache.java
index c525919..dfa4438 100644
--- a/src/main/java/org/codehaus/groovy/runtime/memoize/MemoizeCache.java
+++ b/src/main/java/org/codehaus/groovy/runtime/memoize/MemoizeCache.java
@@ -44,7 +44,7 @@ public interface MemoizeCache<K, V> {
* Try to get the value from cache.
* If not found, create the value by {@link ValueProvider} and put it into the cache, at last return the value.
*
- * @param key
+ * @param key the key to look up
* @param valueProvider provide the value if the associated value not found
* @return the cached value
*/
diff --git a/src/main/java/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java b/src/main/java/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java
index 5074908..8cae32f 100644
--- a/src/main/java/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java
+++ b/src/main/java/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java
@@ -23,7 +23,7 @@ import groovy.lang.MetaBeanProperty;
import groovy.lang.MetaMethod;
import org.codehaus.groovy.reflection.CachedClass;
import org.codehaus.groovy.reflection.ReflectionCache;
-import org.codehaus.groovy.util.ManagedConcurrentMap;
+import org.codehaus.groovy.util.ManagedIdentityConcurrentMap;
import org.codehaus.groovy.util.ReferenceBundle;
import java.lang.reflect.Modifier;
@@ -39,9 +39,9 @@ import java.util.concurrent.ConcurrentHashMap;
* @since 1.5
*/
public class ThreadManagedMetaBeanProperty extends MetaBeanProperty {
- private static final ConcurrentHashMap<String,ManagedConcurrentMap> PROPNAME_TO_MAP = new ConcurrentHashMap<String, ManagedConcurrentMap>();
+ private static final ConcurrentHashMap<String,ManagedIdentityConcurrentMap> PROPNAME_TO_MAP = new ConcurrentHashMap<String, ManagedIdentityConcurrentMap>();
- private final ManagedConcurrentMap instance2Prop;
+ private final ManagedIdentityConcurrentMap instance2Prop;
private final Class declaringClass;
private final ThreadBoundGetter getter;
@@ -117,11 +117,11 @@ public class ThreadManagedMetaBeanProperty extends MetaBeanProperty {
instance2Prop = getInstance2PropName(name);
}
- private static ManagedConcurrentMap getInstance2PropName(String name) {
- ManagedConcurrentMap res = PROPNAME_TO_MAP.get(name);
+ private static ManagedIdentityConcurrentMap getInstance2PropName(String name) {
+ ManagedIdentityConcurrentMap res = PROPNAME_TO_MAP.get(name);
if (res == null) {
- res = new ManagedConcurrentMap(SOFT_BUNDLE);
- ManagedConcurrentMap ores = PROPNAME_TO_MAP.putIfAbsent(name, res);
+ res = new ManagedIdentityConcurrentMap(SOFT_BUNDLE);
+ ManagedIdentityConcurrentMap ores = PROPNAME_TO_MAP.putIfAbsent(name, res);
if (ores != null)
return ores;
}
@@ -176,7 +176,7 @@ public class ThreadManagedMetaBeanProperty extends MetaBeanProperty {
* @see groovy.lang.MetaMethod#invoke(java.lang.Object, java.lang.Object[])
*/
public Object invoke(Object object, Object[] arguments) {
- return instance2Prop.getOrPut(object, getInitialValue()).getValue();
+ return instance2Prop.getOrPut(object, getInitialValue());
}
}
diff --git a/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMap.java b/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMap.java
index 6168877..d67fd88 100644
--- a/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMap.java
+++ b/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMap.java
@@ -18,6 +18,7 @@
*/
package org.codehaus.groovy.util;
+@Deprecated
public abstract class AbstractConcurrentMap<K, V> extends AbstractConcurrentMapBase {
public AbstractConcurrentMap(Object segmentInfo) {
diff --git a/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMapBase.java b/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMapBase.java
index 01f84af..57ac68e 100644
--- a/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMapBase.java
+++ b/src/main/java/org/codehaus/groovy/util/AbstractConcurrentMapBase.java
@@ -21,6 +21,7 @@ package org.codehaus.groovy.util;
import java.util.Collection;
import java.util.LinkedList;
+@Deprecated
public abstract class AbstractConcurrentMapBase {
protected static final int MAXIMUM_CAPACITY = 1 << 30;
static final int MAX_SEGMENTS = 1 << 16;
diff --git a/src/main/java/org/codehaus/groovy/util/ComplexKeyHashMap.java b/src/main/java/org/codehaus/groovy/util/ComplexKeyHashMap.java
index c74f0bb..5d8a1b8 100644
--- a/src/main/java/org/codehaus/groovy/util/ComplexKeyHashMap.java
+++ b/src/main/java/org/codehaus/groovy/util/ComplexKeyHashMap.java
@@ -21,6 +21,7 @@ package org.codehaus.groovy.util;
import java.util.Arrays;
import java.util.NoSuchElementException;
+@Deprecated
public class ComplexKeyHashMap
{
public static class Entry {
diff --git a/src/main/java/org/codehaus/groovy/util/ManagedConcurrentMap.java b/src/main/java/org/codehaus/groovy/util/ManagedConcurrentMap.java
index d1cf760..7329711 100644
--- a/src/main/java/org/codehaus/groovy/util/ManagedConcurrentMap.java
+++ b/src/main/java/org/codehaus/groovy/util/ManagedConcurrentMap.java
@@ -18,6 +18,7 @@
*/
package org.codehaus.groovy.util;
+@Deprecated
public class ManagedConcurrentMap<K,V> extends AbstractConcurrentMap<K,V> {
protected ReferenceBundle bundle;
public ManagedConcurrentMap(ReferenceBundle bundle) {
diff --git a/src/main/java/org/codehaus/groovy/util/ManagedIdentityConcurrentMap.java b/src/main/java/org/codehaus/groovy/util/ManagedIdentityConcurrentMap.java
new file mode 100644
index 0000000..2736244
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/util/ManagedIdentityConcurrentMap.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.groovy.util;
+
+import java.util.Collection;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This is a basic implementation of a map able to forget its keys. This
+ * map uses internally a ConcurrentHashMap, thus should be safe for concurrency.
+ * hashcode and equals are used to find the entries and should thus be implemented
+ * properly for the keys. This map does not support null keys.
+ *
+ * @param <K> the key type
+ * @param <V> the value type
+ * @since 4.0.0
+ */
+public class ManagedIdentityConcurrentMap<K, V> {
+ private final ConcurrentHashMap<ManagedReference<K>, V> internalMap;
+ private final ReferenceBundle bundle;
+
+ public ManagedIdentityConcurrentMap(ReferenceBundle bundle) {
+ this.internalMap = new ConcurrentHashMap<>();
+ this.bundle = bundle;
+ }
+
+ /**
+ * Returns the value stored for the given key at the point of call.
+ *
+ * @param key a non null key
+ * @return the value stored in the map for the given key
+ */
+ public V get(K key) {
+ return internalMap.get(new ManagedIdentityKey<K>(key));
+ }
+
+ /**
+ * Sets a new value for a given key. an older value is overwritten.
+ *
+ * @param key a non null key
+ * @param value the new value
+ */
+ public V put(final K key, V value) {
+ return internalMap.put(new ManagedIdentityKey<K>(key), value);
+ }
+
+ /**
+ * Returns the values of the map
+ */
+ public Collection<V> values() {
+ return internalMap.values();
+ }
+
+ /**
+ * Remove the key specified entry
+ *
+ * @param key the key to look up
+ * @return the removed value
+ */
+ public V remove(K key) {
+ return internalMap.remove(new ManagedIdentityKey<K>(key));
+ }
+
+ /**
+ * Get the key specified value, or put the default value and return it if the key is absent
+ *
+ * @param key the key to look up
+ * @param value the default value if the key is absent
+ * @return the value
+ */
+ public V getOrPut(K key, V value) {
+ return internalMap.computeIfAbsent(new ManagedIdentityKey<K>(key), k -> value);
+ }
+
+ /**
+ * Returns the map size
+ */
+ public Object size() {
+ return internalMap.size();
+ }
+
+ /**
+ * Check if the map is empty or not
+ *
+ * @return {@code true} when the map is empty, otherwise {@code false}
+ */
+ public boolean isEmpty() {
+ return internalMap.isEmpty();
+ }
+
+ /**
+ * Represents identity key of {@link ManagedIdentityConcurrentMap}
+ *
+ * @param <K> the key type
+ */
+ private class ManagedIdentityKey<K> extends ManagedReference<K> {
+ private final int hashCode;
+
+ private ManagedIdentityKey(K key) {
+ super(bundle, key);
+ this.hashCode = hash(key);
+ }
+
+ @Override
+ public void finalizeReference() {
+ internalMap.remove(this);
+ super.finalizeReference();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ManagedIdentityKey)) return false;
+ return get() == ((ManagedIdentityKey<?>) o).get();
+ }
+
+ @Override
+ public int hashCode() {
+ return this.hashCode;
+ }
+
+ private int hash(K key) {
+ int h = (null == key) ? 0 : System.identityHashCode(key);
+ h += (h << 15) ^ 0xffffcd7d;
+ h ^= (h >>> 10);
+ h += (h << 3);
+ h ^= (h >>> 6);
+ h += (h << 2) + (h << 14);
+ h ^= (h >>> 16);
+ return h;
+ }
+ }
+}
diff --git a/src/main/java/org/codehaus/groovy/util/SingleKeyHashMap.java b/src/main/java/org/codehaus/groovy/util/SingleKeyHashMap.java
index 974b24b..86e0990 100644
--- a/src/main/java/org/codehaus/groovy/util/SingleKeyHashMap.java
+++ b/src/main/java/org/codehaus/groovy/util/SingleKeyHashMap.java
@@ -18,6 +18,7 @@
*/
package org.codehaus.groovy.util;
+@Deprecated
public class SingleKeyHashMap extends ComplexKeyHashMap
{
public static class Entry extends ComplexKeyHashMap.Entry{
diff --git a/src/main/java/org/codehaus/groovy/util/TripleKeyHashMap.java b/src/main/java/org/codehaus/groovy/util/TripleKeyHashMap.java
index b2d891b..668db9a 100644
--- a/src/main/java/org/codehaus/groovy/util/TripleKeyHashMap.java
+++ b/src/main/java/org/codehaus/groovy/util/TripleKeyHashMap.java
@@ -18,6 +18,7 @@
*/
package org.codehaus.groovy.util;
+@Deprecated
public class TripleKeyHashMap extends ComplexKeyHashMap
{
public static class Entry extends ComplexKeyHashMap.Entry{
diff --git a/src/test/org/codehaus/groovy/util/ManagedIdentityConcurrentMapTest.groovy b/src/test/org/codehaus/groovy/util/ManagedIdentityConcurrentMapTest.groovy
new file mode 100644
index 0000000..d454b3f
--- /dev/null
+++ b/src/test/org/codehaus/groovy/util/ManagedIdentityConcurrentMapTest.groovy
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.codehaus.groovy.util
+
+
+import org.junit.Test
+
+class ManagedIdentityConcurrentMapTest {
+ @Test
+ void testRemovingEntriesFromMapAfterGC() {
+ def m = new ManagedIdentityConcurrentMap<Object, String>(ReferenceBundle.getWeakBundle())
+ def k1 = new Object()
+ m.put(k1, "a")
+ def k2 = new Object()
+ m.put(k2, "b")
+ def k3 = new Object()
+ m.put(k3, "c")
+
+ assert 3 == m.size()
+
+ // the related entries should be removed after GC happens
+ k1 = null
+ k2 = null
+ k3 = null
+
+ // finalize via GC, which is hard to predicate though it will happen at last
+ for (int i = 0; i < 20; i++) {
+ System.gc()
+
+ if (m.values().size() == 0) {
+ break
+ }
+
+ Thread.sleep(100)
+ }
+
+ // finalize manually
+ if (!m.isEmpty()) {
+ m.internalMap.keySet().stream().forEach(e -> e.finalizeReference())
+ }
+
+ assert m.isEmpty()
+ }
+}
diff --git a/subprojects/groovy-json/src/spec/test/json/JsonTest.groovy b/subprojects/groovy-json/src/spec/test/json/JsonTest.groovy
index 1069af1..02b6863 100644
--- a/subprojects/groovy-json/src/spec/test/json/JsonTest.groovy
+++ b/subprojects/groovy-json/src/spec/test/json/JsonTest.groovy
@@ -120,7 +120,7 @@ class JsonTest extends GroovyTestCase {
.excludeFieldsByType(URL)
.build()
- assert generator.toJson(person) == '{"dob":"1984@12","name":"John"}'
+ assert generator.toJson(person) == '{"name":"John","dob":"1984@12"}'
// end::json_output_generator[]
'''
}
@@ -148,7 +148,7 @@ class JsonTest extends GroovyTestCase {
}
.build()
- assert generator.toJson(person) == '{"favoriteUrl":"groovy-lang.org","name":"John"}'
+ assert generator.toJson(person) == '{"name":"John","favoriteUrl":"groovy-lang.org"}'
// No key available when generating a JSON Array
def list = [new URL('http://groovy-lang.org/json.html#_jsonoutput')]
diff --git a/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy b/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
index 3ed54bf..20c061d 100644
--- a/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
+++ b/subprojects/groovy-json/src/test/groovy/groovy/json/JsonOutputTest.groovy
@@ -306,30 +306,30 @@ class JsonOutputTest extends GroovyTestCase {
"name": "Paris",
"districts": [
{
+ "number": 1,
"streets": [
{
- "kind": "street",
- "streetName": "Saint-Honore"
+ "streetName": "Saint-Honore",
+ "kind": "street"
},
{
- "kind": "avenue",
- "streetName": "de l'Opera"
+ "streetName": "de l'Opera",
+ "kind": "avenue"
}
- ],
- "number": 1
+ ]
},
{
+ "number": 2,
"streets": [
{
- "kind": "boulevard",
- "streetName": "des Italiens"
+ "streetName": "des Italiens",
+ "kind": "boulevard"
},
{
- "kind": "boulevard",
- "streetName": "Bonne Nouvelle"
+ "streetName": "Bonne Nouvelle",
+ "kind": "boulevard"
}
- ],
- "number": 2
+ ]
}
]
}'''.stripIndent()
@@ -350,12 +350,12 @@ class JsonOutputTest extends GroovyTestCase {
void testObjectWithDeclaredPropertiesField() {
def person = new JsonObject(name: "pillow", properties: [state: "fluffy", color: "white"])
def json = toJson(person)
- assert json == '{"properties":{"state":"fluffy","color":"white"},"name":"pillow"}'
+ assert json == '{"name":"pillow","properties":{"state":"fluffy","color":"white"}}'
}
void testGROOVY5494() {
def json = toJson(new JsonFoo(name: "foo"))
- assert json == '{"properties":0,"name":"foo"}'
+ assert json == '{"name":"foo","properties":0}'
}
void testCharacter() {