You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2017/12/17 14:01:20 UTC
[09/62] [abbrv] [partial] groovy git commit: Move Java source set
into `src/main/java`
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/reflection/CachedMethod.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/reflection/CachedMethod.java b/src/main/java/org/codehaus/groovy/reflection/CachedMethod.java
new file mode 100644
index 0000000..58314e2
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/reflection/CachedMethod.java
@@ -0,0 +1,345 @@
+/*
+ * 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.reflection;
+
+import groovy.lang.MetaClassImpl;
+import groovy.lang.MetaMethod;
+import groovy.lang.MissingMethodException;
+import org.codehaus.groovy.classgen.asm.BytecodeHelper;
+import org.codehaus.groovy.runtime.InvokerInvocationException;
+import org.codehaus.groovy.runtime.callsite.CallSite;
+import org.codehaus.groovy.runtime.callsite.CallSiteGenerator;
+import org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite;
+import org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite;
+import org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite;
+import org.codehaus.groovy.runtime.metaclass.MethodHelper;
+
+import java.io.Serializable;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * @author Alex.Tkachman
+ */
+public class CachedMethod extends MetaMethod implements Comparable {
+ public final CachedClass cachedClass;
+
+ private final Method cachedMethod;
+ private int hashCode;
+
+ private static final MyComparator COMPARATOR = new MyComparator();
+
+ private SoftReference<Constructor> pogoCallSiteConstructor, pojoCallSiteConstructor, staticCallSiteConstructor;
+
+ private boolean skipCompiled;
+
+ public CachedMethod(CachedClass clazz, Method method) {
+ this.cachedMethod = method;
+ this.cachedClass = clazz;
+ }
+
+ public CachedMethod(Method method) {
+ this(ReflectionCache.getCachedClass(method.getDeclaringClass()),method);
+ }
+
+ public static CachedMethod find(Method method) {
+ CachedMethod[] methods = ReflectionCache.getCachedClass(method.getDeclaringClass()).getMethods();
+// for (int i = 0; i < methods.length; i++) {
+// CachedMethod cachedMethod = methods[i];
+// if (cachedMethod.cachedMethod.equals(method))
+// return cachedMethod;
+// }
+// return null;
+ int i = Arrays.binarySearch(methods, method, COMPARATOR);
+ if (i < 0)
+ return null;
+
+ return methods[i];
+ }
+
+ protected Class[] getPT() {
+ return cachedMethod.getParameterTypes();
+ }
+
+ public String getName() {
+ return cachedMethod.getName();
+ }
+
+ public String getDescriptor() {
+ return BytecodeHelper.getMethodDescriptor(getReturnType(), getNativeParameterTypes());
+ }
+
+ public CachedClass getDeclaringClass() {
+ return cachedClass;
+ }
+
+ public final Object invoke(Object object, Object[] arguments) {
+ try {
+ AccessPermissionChecker.checkAccessPermission(cachedMethod);
+ } catch (CacheAccessControlException ex) {
+ throw new InvokerInvocationException(ex);
+ }
+ try {
+ return cachedMethod.invoke(object, arguments);
+ } catch (IllegalArgumentException e) {
+ throw new InvokerInvocationException(e);
+ } catch (IllegalAccessException e) {
+ throw new InvokerInvocationException(e);
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ throw (cause instanceof RuntimeException && !(cause instanceof MissingMethodException)) ?
+ (RuntimeException) cause : new InvokerInvocationException(e);
+ }
+ }
+
+ public ParameterTypes getParamTypes() {
+ return null;
+ }
+
+ public Class getReturnType() {
+ return cachedMethod.getReturnType();
+ }
+
+ public int getParamsCount() {
+ return getParameterTypes().length;
+ }
+
+ public int getModifiers() {
+ return cachedMethod.getModifiers();
+ }
+
+
+ public String getSignature() {
+ return getName() + getDescriptor();
+ }
+
+ public final Method setAccessible() {
+ AccessPermissionChecker.checkAccessPermission(cachedMethod);
+// if (queuedToCompile.compareAndSet(false,true)) {
+// if (isCompilable())
+// CompileThread.addMethod(this);
+// }
+ return cachedMethod;
+ }
+
+ public boolean isStatic() {
+ return MethodHelper.isStatic(cachedMethod);
+ }
+
+ public int compareTo(Object o) {
+ if (o instanceof CachedMethod)
+ return compareToCachedMethod((CachedMethod)o);
+ else
+ return compareToMethod((Method)o);
+ }
+
+ private int compareToCachedMethod(CachedMethod other) {
+ if (other == null)
+ return -1;
+
+ final int strComp = getName().compareTo(other.getName());
+ if (strComp != 0)
+ return strComp;
+
+ final int retComp = getReturnType().getName().compareTo(other.getReturnType().getName());
+ if (retComp != 0)
+ return retComp;
+
+ CachedClass[] params = getParameterTypes();
+ CachedClass[] otherParams = other.getParameterTypes();
+
+ final int pd = params.length - otherParams.length;
+ if (pd != 0)
+ return pd;
+
+ for (int i = 0; i != params.length; ++i) {
+ final int nameComp = params[i].getName().compareTo(otherParams[i].getName());
+ if (nameComp != 0)
+ return nameComp;
+ }
+
+ final int classComp = cachedClass.toString().compareTo(other.getDeclaringClass().toString());
+ if (classComp != 0)
+ return classComp;
+
+ throw new RuntimeException("Should never happen");
+ }
+
+ private int compareToMethod(Method other) {
+ if (other == null)
+ return -1;
+
+ final int strComp = getName().compareTo(other.getName());
+ if (strComp != 0)
+ return strComp;
+
+ final int retComp = getReturnType().getName().compareTo(other.getReturnType().getName());
+ if (retComp != 0)
+ return retComp;
+
+ CachedClass[] params = getParameterTypes();
+ Class[] mparams = other.getParameterTypes();
+
+ final int pd = params.length - mparams.length;
+ if (pd != 0)
+ return pd;
+
+ for (int i = 0; i != params.length; ++i) {
+ final int nameComp = params[i].getName().compareTo(mparams[i].getName());
+ if (nameComp != 0)
+ return nameComp;
+ }
+
+ return 0;
+ }
+
+ public boolean equals(Object o) {
+ return (o instanceof CachedMethod && cachedMethod.equals(((CachedMethod)o).cachedMethod))
+ || (o instanceof Method && cachedMethod.equals(o));
+ }
+
+ public int hashCode() {
+ if (hashCode == 0) {
+ hashCode = cachedMethod.hashCode();
+ if (hashCode == 0)
+ hashCode = 0xcafebebe;
+ }
+ return hashCode;
+ }
+
+ public String toString() {
+ return cachedMethod.toString();
+ }
+
+ private static Constructor getConstructor(SoftReference<Constructor> ref) {
+ if (ref==null) return null;
+ return ref.get();
+ }
+
+ public CallSite createPogoMetaMethodSite(CallSite site, MetaClassImpl metaClass, Class[] params) {
+ if (!skipCompiled) {
+ Constructor constr = getConstructor(pogoCallSiteConstructor);
+ if (constr==null) {
+ if (CallSiteGenerator.isCompilable(this)) {
+ constr = CallSiteGenerator.compilePogoMethod(this);
+ }
+ if (constr != null) {
+ pogoCallSiteConstructor = new SoftReference<Constructor> (constr);
+ } else {
+ skipCompiled = true;
+ }
+ }
+
+ if (constr!=null) {
+ try {
+ return (CallSite) constr.newInstance(site, metaClass, this, params, constr);
+ } catch (Error e) {
+ skipCompiled=true;
+ throw e;
+ } catch (Throwable e) {
+ skipCompiled=true;
+ }
+ }
+ }
+ return new PogoMetaMethodSite.PogoCachedMethodSiteNoUnwrapNoCoerce(site, metaClass, this, params);
+ }
+
+
+ public CallSite createPojoMetaMethodSite(CallSite site, MetaClassImpl metaClass, Class[] params) {
+ if (!skipCompiled) {
+ Constructor constr = getConstructor(pojoCallSiteConstructor);
+ if (constr==null) {
+ if (CallSiteGenerator.isCompilable(this)) {
+ constr = CallSiteGenerator.compilePojoMethod(this);
+ }
+ if (constr != null) {
+ pojoCallSiteConstructor = new SoftReference<Constructor> (constr);
+ } else {
+ skipCompiled = true;
+ }
+ }
+
+ if (constr!=null) {
+ try {
+ return (CallSite) constr.newInstance(site, metaClass, this, params, constr);
+ } catch (Error e) {
+ skipCompiled=true;
+ throw e;
+ } catch (Throwable e) {
+ skipCompiled=true;
+ }
+ }
+ }
+ return new PojoMetaMethodSite.PojoCachedMethodSiteNoUnwrapNoCoerce(site, metaClass, this, params);
+ }
+
+ public CallSite createStaticMetaMethodSite(CallSite site, MetaClassImpl metaClass, Class[] params) {
+ if (!skipCompiled) {
+ Constructor constr = getConstructor(staticCallSiteConstructor);
+ if (constr==null) {
+ if (CallSiteGenerator.isCompilable(this)) {
+ constr = CallSiteGenerator.compileStaticMethod(this);
+ }
+ if (constr != null) {
+ staticCallSiteConstructor = new SoftReference<Constructor> (constr);
+ } else {
+ skipCompiled = true;
+ }
+ }
+
+ if (constr!=null) {
+ try {
+ return (CallSite) constr.newInstance(site, metaClass, this, params, constr);
+ } catch (Error e) {
+ skipCompiled=true;
+ throw e;
+ } catch (Throwable e) {
+ skipCompiled=true;
+ }
+ }
+ }
+
+ return new StaticMetaMethodSite.StaticMetaMethodSiteNoUnwrapNoCoerce(site, metaClass, this, params);
+ }
+
+ private static class MyComparator implements Comparator, Serializable {
+ private static final long serialVersionUID = 8909277090690131302L;
+
+ public int compare(Object o1, Object o2) {
+ if (o1 instanceof CachedMethod)
+ return ((CachedMethod)o1).compareTo(o2);
+ else if (o2 instanceof CachedMethod)
+ return -((CachedMethod)o2).compareTo(o1);
+ else
+ // really, this should never happen, it's evidence of corruption if it does
+ throw new ClassCastException("One of the two comparables must be a CachedMethod");
+ }
+ }
+
+ public Method getCachedMethod() {
+ AccessPermissionChecker.checkAccessPermission(cachedMethod);
+ return cachedMethod;
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/reflection/ClassInfo.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/reflection/ClassInfo.java b/src/main/java/org/codehaus/groovy/reflection/ClassInfo.java
new file mode 100644
index 0000000..b4dc133
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/reflection/ClassInfo.java
@@ -0,0 +1,505 @@
+/*
+ * 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.reflection;
+
+import groovy.lang.Closure;
+import groovy.lang.ExpandoMetaClass;
+import groovy.lang.ExpandoMetaClassCreationHandle;
+import groovy.lang.GroovySystem;
+import groovy.lang.MetaClass;
+import groovy.lang.MetaClassRegistry;
+import groovy.lang.MetaMethod;
+import org.codehaus.groovy.reflection.GroovyClassValue.ComputeValue;
+import org.codehaus.groovy.reflection.stdclasses.ArrayCachedClass;
+import org.codehaus.groovy.reflection.stdclasses.BigDecimalCachedClass;
+import org.codehaus.groovy.reflection.stdclasses.BigIntegerCachedClass;
+import org.codehaus.groovy.reflection.stdclasses.BooleanCachedClass;
+import org.codehaus.groovy.reflection.stdclasses.ByteCachedClass;
+import org.codehaus.groovy.reflection.stdclasses.CachedClosureClass;
+import org.codehaus.groovy.reflection.stdclasses.CachedSAMClass;
+import org.codehaus.groovy.reflection.stdclasses.CharacterCachedClass;
+import org.codehaus.groovy.reflection.stdclasses.DoubleCachedClass;
+import org.codehaus.groovy.reflection.stdclasses.FloatCachedClass;
+import org.codehaus.groovy.reflection.stdclasses.IntegerCachedClass;
+import org.codehaus.groovy.reflection.stdclasses.LongCachedClass;
+import org.codehaus.groovy.reflection.stdclasses.NumberCachedClass;
+import org.codehaus.groovy.reflection.stdclasses.ObjectCachedClass;
+import org.codehaus.groovy.reflection.stdclasses.ShortCachedClass;
+import org.codehaus.groovy.reflection.stdclasses.StringCachedClass;
+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.ManagedReference;
+import org.codehaus.groovy.util.ReferenceBundle;
+import org.codehaus.groovy.vmplugin.VMPluginFactory;
+
+import java.lang.ref.WeakReference;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Handle for all information we want to keep about the class
+ * <p>
+ * This class handles caching internally and its advisable to not store
+ * references directly to objects of this class. The static factory method
+ * {@link ClassInfo#getClassInfo(Class)} should be used to retrieve an instance
+ * from the cache. Internally the {@code Class} associated with a {@code ClassInfo}
+ * instance is kept as {@link WeakReference}, so it not safe to reference
+ * and instance without the Class being either strongly or softly reachable.
+ *
+ * @author Alex.Tkachman
+ */
+public class ClassInfo implements Finalizable {
+
+ private final LazyCachedClassRef cachedClassRef;
+ private final LazyClassLoaderRef artifactClassLoader;
+ private final LockableObject lock = new LockableObject();
+ public final int hash = -1;
+ private final WeakReference<Class<?>> classRef;
+
+ // TODO: should be able to remove the klazz field once 2.5 becomes the mainline release
+ // Gradle has a cleanup mechanism in place to reflectively access this klazz field.
+ // The klazz field is being kept for compatibility so as to not break builds that depend
+ // on versions of Groovy after the field was changed to a WeakReference (classRef). It
+ // appears that Gradle only performs the cleanup when it detects a groovy version of 2.4.x,
+ // so the klazz field and placeholder Sentinel class can likely be safely removed once
+ // the release version bumps to 2.5 (or beyond).
+ // See:
+ // https://github.com/gradle/gradle/blob/711f64/subprojects/core/src/main/java/org/gradle/api/internal/classloading/LeakyOnJava7GroovySystemLoader.java#L74
+ private static final class Sentinel {}
+ private static final Class<?> klazz = Sentinel.class;
+
+ private final AtomicInteger version = new AtomicInteger();
+
+ private MetaClass strongMetaClass;
+ private ManagedReference<MetaClass> weakMetaClass;
+ MetaMethod[] dgmMetaMethods = CachedClass.EMPTY;
+ MetaMethod[] newMetaMethods = CachedClass.EMPTY;
+ private ManagedConcurrentMap<Object, MetaClass> perInstanceMetaClassMap;
+
+ private static final ReferenceBundle softBundle = ReferenceBundle.getSoftBundle();
+ private static final ReferenceBundle weakBundle = ReferenceBundle.getWeakBundle();
+
+ private static final ManagedConcurrentLinkedQueue<ClassInfo> modifiedExpandos =
+ new ManagedConcurrentLinkedQueue<ClassInfo>(weakBundle);
+
+ 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.classRef = new WeakReference<Class<?>>(klazz);
+ cachedClassRef = new LazyCachedClassRef(softBundle, this);
+ artifactClassLoader = new LazyClassLoaderRef(softBundle, this);
+ }
+
+ public int getVersion() {
+ return version.get();
+ }
+
+ public void incVersion() {
+ version.incrementAndGet();
+ VMPluginFactory.getPlugin().invalidateCallSites();
+ }
+
+ public ExpandoMetaClass getModifiedExpando() {
+ // safe value here to avoid multiple reads with possibly
+ // differing values due to concurrency
+ MetaClass strongRef = strongMetaClass;
+ return strongRef == null ? null : strongRef instanceof ExpandoMetaClass ? (ExpandoMetaClass)strongRef : null;
+ }
+
+ public static void clearModifiedExpandos() {
+ for (Iterator<ClassInfo> itr = modifiedExpandos.iterator(); itr.hasNext(); ) {
+ ClassInfo info = itr.next();
+ itr.remove();
+ info.setStrongMetaClass(null);
+ }
+ }
+
+ /**
+ * Returns the {@code Class} associated with this {@code ClassInfo}.
+ * <p>
+ * This method can return {@code null} if the {@code Class} is no longer reachable
+ * through any strong or soft references. A non-null return value indicates that this
+ * {@code ClassInfo} is valid.
+ *
+ * @return the {@code Class} associated with this {@code ClassInfo}, else {@code null}
+ */
+ public final Class<?> getTheClass() {
+ return classRef.get();
+ }
+
+ public CachedClass getCachedClass() {
+ return cachedClassRef.get();
+ }
+
+ public ClassLoaderForClassArtifacts getArtifactClassLoader() {
+ return artifactClassLoader.get();
+ }
+
+ public static ClassInfo getClassInfo (Class cls) {
+ return globalClassValue.get(cls);
+ }
+
+ /**
+ * Removes a {@code ClassInfo} from the cache.
+ *
+ * This is useful in cases where the Class is parsed from a script, such as when
+ * using GroovyClassLoader#parseClass, and is executed for its result but the Class
+ * is not retained or cached. Removing the {@code ClassInfo} associated with the Class
+ * will make the Class and its ClassLoader eligible for garbage collection sooner that
+ * it would otherwise.
+ *
+ * @param cls the Class associated with the ClassInfo to remove
+ * from cache
+ */
+ public static void remove(Class<?> cls) {
+ globalClassValue.remove(cls);
+ }
+
+ public static Collection<ClassInfo> getAllClassInfo () {
+ return getAllGlobalClassInfo();
+ }
+
+ public static void onAllClassInfo(ClassInfoAction action) {
+ for (ClassInfo classInfo : getAllGlobalClassInfo()) {
+ action.onClassInfo(classInfo);
+ }
+ }
+
+ private static Collection<ClassInfo> getAllGlobalClassInfo() {
+ return globalClassSet.values();
+ }
+
+ public MetaClass getStrongMetaClass() {
+ return strongMetaClass;
+ }
+
+ public void setStrongMetaClass(MetaClass answer) {
+ version.incrementAndGet();
+
+ // safe value here to avoid multiple reads with possibly
+ // differing values due to concurrency
+ MetaClass strongRef = strongMetaClass;
+
+ if (strongRef instanceof ExpandoMetaClass) {
+ ((ExpandoMetaClass)strongRef).inRegistry = false;
+ for (Iterator<ClassInfo> itr = modifiedExpandos.iterator(); itr.hasNext(); ) {
+ ClassInfo info = itr.next();
+ if(info == this) {
+ itr.remove();
+ }
+ }
+ }
+
+ strongMetaClass = answer;
+
+ if (answer instanceof ExpandoMetaClass) {
+ ((ExpandoMetaClass)answer).inRegistry = true;
+ modifiedExpandos.add(this);
+ }
+
+ replaceWeakMetaClassRef(null);
+ }
+
+ public MetaClass getWeakMetaClass() {
+ // safe value here to avoid multiple reads with possibly
+ // differing values due to concurrency
+ ManagedReference<MetaClass> weakRef = weakMetaClass;
+ return weakRef == null ? null : weakRef.get();
+ }
+
+ public void setWeakMetaClass(MetaClass answer) {
+ version.incrementAndGet();
+
+ strongMetaClass = null;
+ ManagedReference<MetaClass> newRef = null;
+ if (answer != null) {
+ newRef = new ManagedReference<MetaClass> (softBundle,answer);
+ }
+ replaceWeakMetaClassRef(newRef);
+ }
+
+ private void replaceWeakMetaClassRef(ManagedReference<MetaClass> newRef) {
+ // safe value here to avoid multiple reads with possibly
+ // differing values due to concurrency
+ ManagedReference<MetaClass> weakRef = weakMetaClass;
+ if (weakRef != null) {
+ weakRef.clear();
+ }
+ weakMetaClass = newRef;
+ }
+
+ public MetaClass getMetaClassForClass() {
+ // safe value here to avoid multiple reads with possibly
+ // differing values due to concurrency
+ MetaClass strongMc = strongMetaClass;
+ if (strongMc!=null) return strongMc;
+ MetaClass weakMc = getWeakMetaClass();
+ if (isValidWeakMetaClass(weakMc)) {
+ return weakMc;
+ }
+ return null;
+ }
+
+ private MetaClass getMetaClassUnderLock() {
+ MetaClass answer = getStrongMetaClass();
+ if (answer!=null) return answer;
+
+ answer = getWeakMetaClass();
+ final MetaClassRegistry metaClassRegistry = GroovySystem.getMetaClassRegistry();
+ MetaClassRegistry.MetaClassCreationHandle mccHandle = metaClassRegistry.getMetaClassCreationHandler();
+
+ if (isValidWeakMetaClass(answer, mccHandle)) {
+ return answer;
+ }
+
+ answer = mccHandle.create(classRef.get(), metaClassRegistry);
+ answer.initialize();
+
+ if (GroovySystem.isKeepJavaMetaClasses()) {
+ setStrongMetaClass(answer);
+ } else {
+ setWeakMetaClass(answer);
+ }
+ return answer;
+ }
+
+ private static boolean isValidWeakMetaClass(MetaClass metaClass) {
+ return isValidWeakMetaClass(metaClass, GroovySystem.getMetaClassRegistry().getMetaClassCreationHandler());
+ }
+
+ /**
+ * if EMC.enableGlobally() is OFF, return whatever the cached answer is.
+ * but if EMC.enableGlobally() is ON and the cached answer is not an EMC, come up with a fresh answer
+ */
+ private static boolean isValidWeakMetaClass(MetaClass metaClass, MetaClassRegistry.MetaClassCreationHandle mccHandle) {
+ if(metaClass==null) return false;
+ boolean enableGloballyOn = (mccHandle instanceof ExpandoMetaClassCreationHandle);
+ boolean cachedAnswerIsEMC = (metaClass instanceof ExpandoMetaClass);
+ return (!enableGloballyOn || cachedAnswerIsEMC);
+ }
+
+ /**
+ * Returns the {@code MetaClass} for the {@code Class} associated with this {@code ClassInfo}.
+ * If no {@code MetaClass} exists one will be created.
+ * <p>
+ * It is not safe to call this method without a {@code Class} associated with this {@code ClassInfo}.
+ * It is advisable to aways retrieve a ClassInfo instance from the cache by using the static
+ * factory method {@link ClassInfo#getClassInfo(Class)} to ensure the referenced Class is
+ * strongly reachable.
+ *
+ * @return a {@code MetaClass} instance
+ */
+ public final MetaClass getMetaClass() {
+ MetaClass answer = getMetaClassForClass();
+ if (answer != null) return answer;
+
+ lock();
+ try {
+ return getMetaClassUnderLock();
+ } finally {
+ unlock();
+ }
+ }
+
+ public MetaClass getMetaClass(Object obj) {
+ final MetaClass instanceMetaClass = getPerInstanceMetaClass(obj);
+ if (instanceMetaClass != null)
+ return instanceMetaClass;
+ return getMetaClass();
+ }
+
+ public static int size () {
+ return globalClassSet.size();
+ }
+
+ public static int fullSize () {
+ return globalClassSet.fullSize();
+ }
+
+ private static CachedClass createCachedClass(Class klazz, ClassInfo classInfo) {
+ if (klazz == Object.class)
+ return new ObjectCachedClass(classInfo);
+
+ if (klazz == String.class)
+ return new StringCachedClass(classInfo);
+
+ CachedClass cachedClass;
+ if (Number.class.isAssignableFrom(klazz) || klazz.isPrimitive()) {
+ if (klazz == Number.class) {
+ cachedClass = new NumberCachedClass(klazz, classInfo);
+ } else if (klazz == Integer.class || klazz == Integer.TYPE) {
+ cachedClass = new IntegerCachedClass(klazz, classInfo, klazz==Integer.class);
+ } else if (klazz == Double.class || klazz == Double.TYPE) {
+ cachedClass = new DoubleCachedClass(klazz, classInfo, klazz==Double.class);
+ } else if (klazz == BigDecimal.class) {
+ cachedClass = new BigDecimalCachedClass(klazz, classInfo);
+ } else if (klazz == Long.class || klazz == Long.TYPE) {
+ cachedClass = new LongCachedClass(klazz, classInfo, klazz==Long.class);
+ } else if (klazz == Float.class || klazz == Float.TYPE) {
+ cachedClass = new FloatCachedClass(klazz, classInfo, klazz==Float.class);
+ } else if (klazz == Short.class || klazz == Short.TYPE) {
+ cachedClass = new ShortCachedClass(klazz, classInfo, klazz==Short.class);
+ } else if (klazz == Boolean.TYPE) {
+ cachedClass = new BooleanCachedClass(klazz, classInfo, false);
+ } else if (klazz == Character.TYPE) {
+ cachedClass = new CharacterCachedClass(klazz, classInfo, false);
+ } else if (klazz == BigInteger.class) {
+ cachedClass = new BigIntegerCachedClass(klazz, classInfo);
+ } else if (klazz == Byte.class || klazz == Byte.TYPE) {
+ cachedClass = new ByteCachedClass(klazz, classInfo, klazz==Byte.class);
+ } else {
+ cachedClass = new CachedClass(klazz, classInfo);
+ }
+ } else {
+ if (klazz.getName().charAt(0) == '[')
+ cachedClass = new ArrayCachedClass(klazz, classInfo);
+ else if (klazz == Boolean.class) {
+ cachedClass = new BooleanCachedClass(klazz, classInfo, true);
+ } else if (klazz == Character.class) {
+ cachedClass = new CharacterCachedClass(klazz, classInfo, true);
+ } else if (Closure.class.isAssignableFrom(klazz)) {
+ cachedClass = new CachedClosureClass (klazz, classInfo);
+ } else if (isSAM(klazz)) {
+ cachedClass = new CachedSAMClass(klazz, classInfo);
+ } else {
+ cachedClass = new CachedClass(klazz, classInfo);
+ }
+ }
+ return cachedClass;
+ }
+
+ private static boolean isSAM(Class<?> c) {
+ return CachedSAMClass.getSAMMethod(c) !=null;
+ }
+
+ public void lock () {
+ lock.lock();
+ }
+
+ public void unlock () {
+ lock.unlock();
+ }
+
+ public MetaClass getPerInstanceMetaClass(Object obj) {
+ if (perInstanceMetaClassMap == null)
+ return null;
+
+ return perInstanceMetaClassMap.get(obj);
+ }
+
+ public void setPerInstanceMetaClass(Object obj, MetaClass metaClass) {
+ version.incrementAndGet();
+
+ if (metaClass != null) {
+ if (perInstanceMetaClassMap == null)
+ perInstanceMetaClassMap = new ManagedConcurrentMap<Object, MetaClass>(ReferenceBundle.getWeakBundle());
+
+ perInstanceMetaClassMap.put(obj, metaClass);
+ }
+ else {
+ if (perInstanceMetaClassMap != null) {
+ perInstanceMetaClassMap.remove(obj);
+ }
+ }
+ }
+
+ public boolean hasPerInstanceMetaClasses () {
+ return perInstanceMetaClassMap != null;
+ }
+
+ private static class LazyCachedClassRef extends LazyReference<CachedClass> {
+ private final ClassInfo info;
+
+ LazyCachedClassRef(ReferenceBundle bundle, ClassInfo info) {
+ super(bundle);
+ this.info = info;
+ }
+
+ public CachedClass initValue() {
+ return createCachedClass(info.classRef.get(), info);
+ }
+ }
+
+ private static class LazyClassLoaderRef extends LazyReference<ClassLoaderForClassArtifacts> {
+ private final ClassInfo info;
+
+ LazyClassLoaderRef(ReferenceBundle bundle, ClassInfo info) {
+ super(bundle);
+ this.info = info;
+ }
+
+ public ClassLoaderForClassArtifacts initValue() {
+ return AccessController.doPrivileged(new PrivilegedAction<ClassLoaderForClassArtifacts>() {
+ public ClassLoaderForClassArtifacts run() {
+ return new ClassLoaderForClassArtifacts(info.classRef.get());
+ }
+ });
+ }
+ }
+
+ @Override
+ public void finalizeReference() {
+ setStrongMetaClass(null);
+ cachedClassRef.clear();
+ artifactClassLoader.clear();
+ }
+
+ private static class GlobalClassSet {
+
+ private final ManagedConcurrentLinkedQueue<ClassInfo> items = new ManagedConcurrentLinkedQueue<ClassInfo>(weakBundle);
+
+ public int size(){
+ return values().size();
+ }
+
+ public int fullSize(){
+ return values().size();
+ }
+
+ public Collection<ClassInfo> values(){
+ return items.values();
+ }
+
+ public void add(ClassInfo value){
+ items.add(value);
+ }
+
+ }
+
+ public interface ClassInfoAction {
+ void onClassInfo(ClassInfo classInfo);
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/reflection/ClassLoaderForClassArtifacts.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/reflection/ClassLoaderForClassArtifacts.java b/src/main/java/org/codehaus/groovy/reflection/ClassLoaderForClassArtifacts.java
new file mode 100644
index 0000000..ba24a6d
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/reflection/ClassLoaderForClassArtifacts.java
@@ -0,0 +1,88 @@
+/*
+ * 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.reflection;
+
+import groovy.lang.MetaClassImpl;
+import groovy.lang.MetaMethod;
+import org.codehaus.groovy.runtime.callsite.CallSite;
+import org.codehaus.groovy.runtime.callsite.GroovySunClassLoader;
+
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ClassLoaderForClassArtifacts extends ClassLoader {
+ public final SoftReference<Class> klazz;
+ private final AtomicInteger classNamesCounter = new AtomicInteger(-1);
+
+ public ClassLoaderForClassArtifacts(Class klazz) {
+ super(klazz.getClassLoader());
+ this.klazz = new SoftReference<Class> (klazz);
+ }
+
+ public Class define (String name, byte [] bytes) {
+ Class cls = defineClass(name, bytes, 0, bytes.length, klazz.get().getProtectionDomain());
+ resolveClass(cls);
+ return cls;
+ }
+
+ public Class loadClass(String name) throws ClassNotFoundException {
+ Class cls = findLoadedClass(name);
+ if (cls != null)
+ return cls;
+
+ if (GroovySunClassLoader.sunVM != null) {
+ cls = GroovySunClassLoader.sunVM.doesKnow(name);
+ if (cls != null)
+ return cls;
+ }
+
+ return super.loadClass(name);
+ }
+
+ public String createClassName(Method method) {
+ final String name;
+ final String clsName = klazz.get().getName();
+ if (clsName.startsWith("java."))
+ name = clsName.replace('.','_') + "$" + method.getName();
+ else
+ name = clsName + "$" + method.getName();
+ int suffix = classNamesCounter.getAndIncrement();
+ return suffix == -1? name : name + "$" + suffix;
+ }
+
+ public Constructor defineClassAndGetConstructor(final String name, final byte[] bytes) {
+ final Class cls = AccessController.doPrivileged( new PrivilegedAction<Class>(){
+ public Class run() {
+ return define(name, bytes);
+ }
+ });
+
+ if (cls != null) {
+ try {
+ return cls.getConstructor(CallSite.class, MetaClassImpl.class, MetaMethod.class, Class[].class, Constructor.class);
+ } catch (NoSuchMethodException e) { //
+ }
+ }
+ return null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/reflection/GeneratedMetaMethod.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/reflection/GeneratedMetaMethod.java b/src/main/java/org/codehaus/groovy/reflection/GeneratedMetaMethod.java
new file mode 100644
index 0000000..3f830fd
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/reflection/GeneratedMetaMethod.java
@@ -0,0 +1,240 @@
+/*
+ * 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.reflection;
+
+import groovy.lang.GroovyRuntimeException;
+import groovy.lang.MetaMethod;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public abstract class GeneratedMetaMethod extends MetaMethod {
+ private final String name;
+ private final CachedClass declaringClass;
+ private final Class returnType;
+
+ public GeneratedMetaMethod(String name, CachedClass declaringClass, Class returnType, Class[] parameters) {
+ this.name = name;
+ this.declaringClass = declaringClass;
+ this.returnType = returnType;
+ nativeParamTypes = parameters;
+ }
+
+ public int getModifiers() {
+ return Modifier.PUBLIC;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Class getReturnType() {
+ return returnType;
+ }
+
+ public CachedClass getDeclaringClass() {
+ return declaringClass;
+ }
+
+ public static class Proxy extends GeneratedMetaMethod {
+ private volatile MetaMethod proxy;
+ private final String className;
+
+ public Proxy(String className, String name, CachedClass declaringClass, Class returnType, Class[] parameters) {
+ super(name, declaringClass, returnType, parameters);
+ this.className = className;
+ }
+
+ @Override
+ public boolean isValidMethod(Class[] arguments) {
+ return proxy().isValidMethod(arguments);
+ }
+
+ @Override
+ public Object doMethodInvoke(Object object, Object[] argumentArray) {
+ return proxy().doMethodInvoke(object, argumentArray);
+ }
+
+ public Object invoke(Object object, Object[] arguments) {
+ return proxy().invoke(object, arguments);
+ }
+
+ public final MetaMethod proxy() {
+ if (proxy == null) {
+ synchronized(this) {
+ if (proxy == null) createProxy();
+ }
+ }
+ return proxy;
+ }
+
+ private void createProxy() {
+ try {
+ Class<?> aClass = getClass().getClassLoader().loadClass(className.replace('/', '.'));
+ Constructor<?> constructor = aClass.getConstructor(String.class, CachedClass.class, Class.class, Class[].class);
+ proxy = (MetaMethod) constructor.newInstance(getName(), getDeclaringClass(), getReturnType(), getNativeParameterTypes());
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw new GroovyRuntimeException("Failed to create DGM method proxy : " + t, t);
+ }
+ }
+ }
+
+ public static class DgmMethodRecord implements Serializable {
+ private static final long serialVersionUID = -5639988016452884450L;
+ public String className;
+ public String methodName;
+ public Class returnType;
+ public Class[] parameters;
+
+ private static final Class[] PRIMITIVE_CLASSES = {
+ Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE,
+ Integer.TYPE, Long.TYPE, Double.TYPE, Float.TYPE, Void.TYPE,
+
+ boolean[].class, char[].class, byte[].class, short[].class,
+ int[].class, long[].class, double[].class, float[].class,
+
+ Object[].class, String[].class, Class[].class, Byte[].class, CharSequence[].class,
+ };
+
+ public static void saveDgmInfo(List<DgmMethodRecord> records, String file) throws IOException {
+ DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
+ Map<String, Integer> classes = new LinkedHashMap<String, Integer>();
+
+ int nextClassId = 0;
+ for (Class primitive : PRIMITIVE_CLASSES) {
+ classes.put(primitive.getName(), nextClassId++);
+ }
+
+ for (DgmMethodRecord record : records) {
+ String name = record.returnType.getName();
+ Integer id = classes.get(name);
+ if (id == null) {
+ id = nextClassId++;
+ classes.put(name, id);
+ }
+
+ for (int i = 0; i < record.parameters.length; i++) {
+ name = record.parameters[i].getName();
+ id = classes.get(name);
+ if (id == null) {
+ id = nextClassId++;
+ classes.put(name, id);
+ }
+ }
+ }
+
+ for (Map.Entry<String, Integer> stringIntegerEntry : classes.entrySet()) {
+ out.writeUTF(stringIntegerEntry.getKey());
+ out.writeInt(stringIntegerEntry.getValue());
+ }
+ out.writeUTF("");
+
+ out.writeInt(records.size());
+ for (DgmMethodRecord record : records) {
+ out.writeUTF(record.className);
+ out.writeUTF(record.methodName);
+ out.writeInt(classes.get(record.returnType.getName()));
+
+ out.writeInt(record.parameters.length);
+ for (int i = 0; i < record.parameters.length; i++) {
+ Integer key = classes.get(record.parameters[i].getName());
+ out.writeInt(key);
+ }
+ }
+ out.close();
+ }
+
+ public static List<DgmMethodRecord> loadDgmInfo() throws IOException {
+
+ ClassLoader loader = DgmMethodRecord.class.getClassLoader();
+ DataInputStream in = new DataInputStream(new BufferedInputStream(loader.getResourceAsStream("META-INF/dgminfo")));
+
+ Map<Integer, Class> classes = new HashMap<Integer, Class>();
+ for (int i = 0; i < PRIMITIVE_CLASSES.length; i++) {
+ classes.put(i, PRIMITIVE_CLASSES[i]);
+ }
+
+ int skip = 0;
+ for (; ; ) {
+ String name = in.readUTF();
+ if (name.length() == 0)
+ break;
+
+ int key = in.readInt();
+
+ if (skip++ < PRIMITIVE_CLASSES.length)
+ continue;
+
+ Class cls = null;
+ try {
+ cls = loader.loadClass(name);
+ } catch (ClassNotFoundException e) {
+ // under certain restrictive environments, loading certain classes may be forbidden
+ // and could yield a ClassNotFoundException (Google App Engine)
+ continue;
+ }
+ classes.put(key, cls);
+ }
+
+ int size = in.readInt();
+ List<DgmMethodRecord> res = new ArrayList<DgmMethodRecord>(size);
+ for (int i = 0; i != size; ++i) {
+ boolean skipRecord = false;
+ DgmMethodRecord record = new DgmMethodRecord();
+ record.className = in.readUTF();
+ record.methodName = in.readUTF();
+ record.returnType = classes.get(in.readInt());
+
+ if (record.returnType == null) {
+ skipRecord = true;
+ }
+
+ int psize = in.readInt();
+ record.parameters = new Class[psize];
+ for (int j = 0; j < record.parameters.length; j++) {
+ record.parameters[j] = classes.get(in.readInt());
+
+ if (record.parameters[j] == null) {
+ skipRecord = true;
+ }
+ }
+ if (!skipRecord) {
+ res.add(record);
+ }
+ }
+
+ in.close();
+
+ return res;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/reflection/GroovyClassValue.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/reflection/GroovyClassValue.java b/src/main/java/org/codehaus/groovy/reflection/GroovyClassValue.java
new file mode 100644
index 0000000..909f9b1
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/reflection/GroovyClassValue.java
@@ -0,0 +1,36 @@
+/*
+ * 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.reflection;
+
+/** Abstraction for Java version dependent ClassValue implementations.
+ * @see java.lang.ClassValue
+ *
+ * @param <T>
+ */
+public interface GroovyClassValue<T> {
+
+ interface ComputeValue<T>{
+ T computeValue(Class<?> type);
+ }
+
+ T get(Class<?> type);
+
+ void remove(Class<?> type);
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/reflection/GroovyClassValueFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/reflection/GroovyClassValueFactory.java b/src/main/java/org/codehaus/groovy/reflection/GroovyClassValueFactory.java
new file mode 100644
index 0000000..c367791
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/reflection/GroovyClassValueFactory.java
@@ -0,0 +1,42 @@
+/*
+ * 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.reflection;
+
+import org.codehaus.groovy.reflection.GroovyClassValue.ComputeValue;
+import org.codehaus.groovy.reflection.v7.GroovyClassValueJava7;
+
+class GroovyClassValueFactory {
+ /**
+ * This flag is introduced as a (hopefully) temporary workaround for a JVM bug, that is to say that using
+ * ClassValue prevents the classes and classloaders from being unloaded.
+ * See https://bugs.openjdk.java.net/browse/JDK-8136353
+ * This issue does not exist on IBM Java (J9) so use ClassValue by default on that JVM.
+ */
+ private static final boolean USE_CLASSVALUE;
+ static {
+ String isJ9 = "IBM J9 VM".equals(System.getProperty("java.vm.name")) ? "true" : "false";
+ USE_CLASSVALUE = Boolean.valueOf(System.getProperty("groovy.use.classvalue", isJ9));
+ }
+
+ public static <T> GroovyClassValue<T> createGroovyClassValue(ComputeValue<T> computeValue) {
+ return (USE_CLASSVALUE)
+ ? new GroovyClassValueJava7<>(computeValue)
+ : new GroovyClassValuePreJava7<>(computeValue);
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java b/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java
new file mode 100644
index 0000000..56ad0c1
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/reflection/GroovyClassValuePreJava7.java
@@ -0,0 +1,104 @@
+/*
+ * 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.reflection;
+
+import org.codehaus.groovy.util.Finalizable;
+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);
+ }
+
+ @Override
+ public void finalizeReference() {
+ T value = getValue();
+ if (value instanceof Finalizable) {
+ ((Finalizable) value).finalizeReference();
+ }
+ super.finalizeReference();
+ }
+ }
+
+ 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);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java b/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
new file mode 100644
index 0000000..4dfa083
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/reflection/MixinInMetaClass.java
@@ -0,0 +1,206 @@
+/*
+ * 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.reflection;
+
+import groovy.lang.DelegatingMetaClass;
+import groovy.lang.ExpandoMetaClass;
+import groovy.lang.GroovyRuntimeException;
+import groovy.lang.GroovySystem;
+import groovy.lang.MetaClass;
+import groovy.lang.MetaMethod;
+import groovy.lang.MetaProperty;
+import org.codehaus.groovy.runtime.HandleMetaClass;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+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.ReferenceBundle;
+
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MixinInMetaClass extends ManagedConcurrentMap {
+ final ExpandoMetaClass emc;
+ final CachedClass mixinClass;
+ final CachedConstructor constructor;
+
+ private static final ReferenceBundle softBundle = ReferenceBundle.getSoftBundle();
+
+ public MixinInMetaClass(ExpandoMetaClass emc, CachedClass mixinClass) {
+ super(softBundle);
+ this.emc = emc;
+ this.mixinClass = mixinClass;
+
+ constructor = findDefaultConstructor(mixinClass);
+ emc.addMixinClass(this);
+ }
+
+ private static CachedConstructor findDefaultConstructor(CachedClass mixinClass) {
+ for (CachedConstructor constr : mixinClass.getConstructors()) {
+ if (!Modifier.isPublic(constr.getModifiers()))
+ continue;
+
+ CachedClass[] classes = constr.getParameterTypes();
+ if (classes.length == 0)
+ return constr;
+ }
+
+ throw new GroovyRuntimeException("No default constructor for class " + mixinClass.getName() + "! Can't be mixed in.");
+ }
+
+ public synchronized Object getMixinInstance(Object object) {
+ Object mixinInstance = get(object);
+ if (mixinInstance == null) {
+ mixinInstance = constructor.invoke(MetaClassHelper.EMPTY_ARRAY);
+ new MixedInMetaClass(mixinInstance, object);
+ put(object, mixinInstance);
+ }
+ return mixinInstance;
+ }
+
+ public synchronized void setMixinInstance(Object object, Object mixinInstance) {
+ if (mixinInstance == null) {
+ remove(object);
+ } else {
+ put(object, mixinInstance);
+ }
+ }
+
+ public CachedClass getInstanceClass() {
+ return emc.getTheCachedClass();
+ }
+
+ public CachedClass getMixinClass() {
+ return mixinClass;
+ }
+
+ public static void mixinClassesToMetaClass(MetaClass self, List<Class> categoryClasses) {
+ final Class selfClass = self.getTheClass();
+
+ if (self instanceof HandleMetaClass) {
+ self = (MetaClass) ((HandleMetaClass) self).replaceDelegate();
+ }
+
+ if (!(self instanceof ExpandoMetaClass)) {
+ if (self instanceof DelegatingMetaClass && ((DelegatingMetaClass) self).getAdaptee() instanceof ExpandoMetaClass) {
+ self = ((DelegatingMetaClass) self).getAdaptee();
+ } else {
+ throw new GroovyRuntimeException("Can't mixin methods to meta class: " + self);
+ }
+ }
+
+ ExpandoMetaClass mc = (ExpandoMetaClass) self;
+
+ List<MetaMethod> arr = new ArrayList<MetaMethod>();
+ for (Class categoryClass : categoryClasses) {
+
+ final CachedClass cachedCategoryClass = ReflectionCache.getCachedClass(categoryClass);
+ final MixinInMetaClass mixin = new MixinInMetaClass(mc, cachedCategoryClass);
+
+ final MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(categoryClass);
+ final List<MetaProperty> propList = metaClass.getProperties();
+ for (MetaProperty prop : propList)
+ if (self.getMetaProperty(prop.getName()) == null) {
+ mc.registerBeanProperty(prop.getName(), new MixinInstanceMetaProperty(prop, mixin));
+ }
+
+ for (MetaProperty prop : cachedCategoryClass.getFields())
+ if (self.getMetaProperty(prop.getName()) == null) {
+ mc.registerBeanProperty(prop.getName(), new MixinInstanceMetaProperty(prop, mixin));
+ }
+
+ for (MetaMethod method : metaClass.getMethods()) {
+ final int mod = method.getModifiers();
+
+ if (!Modifier.isPublic(mod))
+ continue;
+
+ if (method instanceof CachedMethod && ((CachedMethod) method).getCachedMethod().isSynthetic())
+ continue;
+
+ if (Modifier.isStatic(mod)) {
+ if (method instanceof CachedMethod)
+ staticMethod(self, arr, (CachedMethod) method);
+ } else if (method.getDeclaringClass().getTheClass() != Object.class || method.getName().equals("toString")) {
+// if (self.pickMethod(method.getName(), method.getNativeParameterTypes()) == null) {
+ final MixinInstanceMetaMethod metaMethod = new MixinInstanceMetaMethod(method, mixin);
+ arr.add(metaMethod);
+// }
+ }
+ }
+ }
+
+ for (Object res : arr) {
+ final MetaMethod metaMethod = (MetaMethod) res;
+ if (metaMethod.getDeclaringClass().isAssignableFrom(selfClass))
+ mc.registerInstanceMethod(metaMethod);
+ else {
+ mc.registerSubclassInstanceMethod(metaMethod);
+ }
+ }
+ }
+
+ private static void staticMethod(final MetaClass self, List<MetaMethod> arr, final CachedMethod method) {
+ CachedClass[] paramTypes = method.getParameterTypes();
+
+ if (paramTypes.length == 0)
+ return;
+
+ NewInstanceMetaMethod metaMethod;
+ if (paramTypes[0].isAssignableFrom(self.getTheClass())) {
+ if (paramTypes[0].getTheClass() == self.getTheClass())
+ metaMethod = new NewInstanceMetaMethod(method);
+ else
+ metaMethod = new NewInstanceMetaMethod(method) {
+ public CachedClass getDeclaringClass() {
+ return ReflectionCache.getCachedClass(self.getTheClass());
+ }
+ };
+ arr.add(metaMethod);
+ } else {
+ if (self.getTheClass().isAssignableFrom(paramTypes[0].getTheClass())) {
+ metaMethod = new NewInstanceMetaMethod(method);
+ arr.add(metaMethod);
+ }
+ }
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof MixinInMetaClass)) return false;
+ if (!super.equals(o)) return false;
+
+ MixinInMetaClass that = (MixinInMetaClass) o;
+
+ if (mixinClass != null ? !mixinClass.equals(that.mixinClass) : that.mixinClass != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (emc != null ? emc.hashCode() : 0);
+ result = 31 * result + (mixinClass != null ? mixinClass.hashCode() : 0);
+ result = 31 * result + (constructor != null ? constructor.hashCode() : 0);
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/reflection/ParameterTypes.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/reflection/ParameterTypes.java b/src/main/java/org/codehaus/groovy/reflection/ParameterTypes.java
new file mode 100644
index 0000000..4c5d5fa
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/reflection/ParameterTypes.java
@@ -0,0 +1,385 @@
+/*
+ * 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.reflection;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+import org.codehaus.groovy.runtime.wrappers.Wrapper;
+
+import java.lang.reflect.Array;
+
+public class ParameterTypes {
+ private final static Class[] NO_PARAMETERS = new Class[0];
+
+ protected volatile Class[] nativeParamTypes;
+ protected volatile CachedClass[] parameterTypes;
+
+ protected boolean isVargsMethod;
+
+ public ParameterTypes() {
+ }
+
+ public ParameterTypes(Class pt[]) {
+ nativeParamTypes = pt;
+ }
+
+ public ParameterTypes(String pt[]) {
+ nativeParamTypes = new Class[pt.length];
+ for (int i = 0; i != pt.length; ++i) {
+ try {
+ nativeParamTypes[i] = Class.forName(pt[i]);
+ } catch (ClassNotFoundException e) {
+ NoClassDefFoundError err = new NoClassDefFoundError();
+ err.initCause(e);
+ throw err;
+ }
+ }
+ }
+
+ public ParameterTypes(CachedClass[] parameterTypes) {
+ setParametersTypes(parameterTypes);
+ }
+
+ protected final void setParametersTypes(CachedClass[] pt) {
+ this.parameterTypes = pt;
+ isVargsMethod = pt.length > 0 && pt[pt.length - 1].isArray;
+ }
+
+ public CachedClass[] getParameterTypes() {
+ if (parameterTypes == null) {
+ getParametersTypes0();
+ }
+
+ return parameterTypes;
+ }
+
+ private synchronized void getParametersTypes0() {
+ if (parameterTypes != null)
+ return;
+
+ Class[] npt = nativeParamTypes == null ? getPT() : nativeParamTypes;
+ if (npt.length == 0) {
+ nativeParamTypes = NO_PARAMETERS;
+ setParametersTypes(CachedClass.EMPTY_ARRAY);
+ } else {
+
+ CachedClass[] pt = new CachedClass[npt.length];
+ for (int i = 0; i != npt.length; ++i)
+ pt[i] = ReflectionCache.getCachedClass(npt[i]);
+
+ nativeParamTypes = npt;
+ setParametersTypes(pt);
+ }
+ }
+
+ public Class[] getNativeParameterTypes() {
+ if (nativeParamTypes == null) {
+ getNativeParameterTypes0();
+ }
+ return nativeParamTypes;
+ }
+
+ private synchronized void getNativeParameterTypes0() {
+ if (nativeParamTypes != null)
+ return;
+
+ Class[] npt;
+ if (parameterTypes != null) {
+ npt = new Class[parameterTypes.length];
+ for (int i = 0; i != parameterTypes.length; ++i) {
+ npt[i] = parameterTypes[i].getTheClass();
+ }
+ } else
+ npt = getPT();
+ nativeParamTypes = npt;
+ }
+
+ protected Class[] getPT() {
+ throw new UnsupportedOperationException(getClass().getName());
+ }
+
+ public boolean isVargsMethod() {
+ return isVargsMethod;
+ }
+
+ public boolean isVargsMethod(Object[] arguments) {
+ // Uncomment if at some point this method can be called before parameterTypes initialized
+ // getParameterTypes();
+ if (!isVargsMethod)
+ return false;
+
+ final int lenMinus1 = parameterTypes.length - 1;
+ // -1 because the varg part is optional
+ if (lenMinus1 == arguments.length) return true;
+ if (lenMinus1 > arguments.length) return false;
+ if (arguments.length > parameterTypes.length) return true;
+
+ // only case left is arguments.length == parameterTypes.length
+ Object last = arguments[arguments.length - 1];
+ if (last == null) return true;
+ Class clazz = last.getClass();
+ return !clazz.equals(parameterTypes[lenMinus1].getTheClass());
+
+ }
+
+ public final Object[] coerceArgumentsToClasses(Object[] argumentArray) {
+ // Uncomment if at some point this method can be called before parameterTypes initialized
+ // getParameterTypes();
+ argumentArray = correctArguments(argumentArray);
+
+ final CachedClass[] pt = parameterTypes;
+ final int len = argumentArray.length;
+ for (int i = 0; i < len; i++) {
+ final Object argument = argumentArray[i];
+ if (argument != null) {
+ argumentArray[i] = pt[i].coerceArgument(argument);
+ }
+ }
+ return argumentArray;
+ }
+
+ public Object[] correctArguments(Object[] argumentArray) {
+ // correct argumentArray's length
+ if (argumentArray == null) {
+ return MetaClassHelper.EMPTY_ARRAY;
+ }
+
+ final CachedClass[] pt = getParameterTypes();
+ if (pt.length == 1 && argumentArray.length == 0) {
+ if (isVargsMethod)
+ return new Object[]{Array.newInstance(pt[0].getTheClass().getComponentType(), 0)};
+ else
+ return MetaClassHelper.ARRAY_WITH_NULL;
+ }
+
+ if (isVargsMethod && isVargsMethod(argumentArray)) {
+ return fitToVargs(argumentArray, pt);
+ }
+
+ return argumentArray;
+ }
+
+ /**
+ * this method is called when the number of arguments to a method is greater than 1
+ * and if the method is a vargs method. This method will then transform the given
+ * arguments to make the method callable
+ *
+ * @param argumentArrayOrig the arguments used to call the method
+ * @param paramTypes the types of the parameters the method takes
+ */
+ private static Object[] fitToVargs(Object[] argumentArrayOrig, CachedClass[] paramTypes) {
+ Class vargsClassOrig = paramTypes[paramTypes.length - 1].getTheClass().getComponentType();
+ Class vargsClass = ReflectionCache.autoboxType(vargsClassOrig);
+ Object[] argumentArray = argumentArrayOrig.clone();
+ MetaClassHelper.unwrap(argumentArray);
+
+ if (argumentArray.length == paramTypes.length - 1) {
+ // the vargs argument is missing, so fill it with an empty array
+ Object[] newArgs = new Object[paramTypes.length];
+ System.arraycopy(argumentArray, 0, newArgs, 0, argumentArray.length);
+ Object vargs = Array.newInstance(vargsClass, 0);
+ newArgs[newArgs.length - 1] = vargs;
+ return newArgs;
+ } else if (argumentArray.length == paramTypes.length) {
+ // the number of arguments is correct, but if the last argument
+ // is no array we have to wrap it in a array. If the last argument
+ // is null, then we don't have to do anything
+ Object lastArgument = argumentArray[argumentArray.length - 1];
+ if (lastArgument != null && !lastArgument.getClass().isArray()) {
+ // no array so wrap it
+ Object wrapped = makeCommonArray(argumentArray, paramTypes.length - 1, vargsClass);
+ Object[] newArgs = new Object[paramTypes.length];
+ System.arraycopy(argumentArray, 0, newArgs, 0, paramTypes.length - 1);
+ newArgs[newArgs.length - 1] = wrapped;
+ return newArgs;
+ } else {
+ // we may have to box the argument!
+ return argumentArray;
+ }
+ } else if (argumentArray.length > paramTypes.length) {
+ // the number of arguments is too big, wrap all exceeding elements
+ // in an array, but keep the old elements that are no vargs
+ Object[] newArgs = new Object[paramTypes.length];
+ // copy arguments that are not a varg
+ System.arraycopy(argumentArray, 0, newArgs, 0, paramTypes.length - 1);
+ // create a new array for the vargs and copy them
+ Object vargs = makeCommonArray(argumentArray, paramTypes.length - 1, vargsClass);
+ newArgs[newArgs.length - 1] = vargs;
+ return newArgs;
+ } else {
+ throw new GroovyBugError("trying to call a vargs method without enough arguments");
+ }
+ }
+
+ private static Object makeCommonArray(Object[] arguments, int offset, Class baseClass) {
+ Object[] result = (Object[]) Array.newInstance(baseClass, arguments.length - offset);
+ for (int i = offset; i < arguments.length; i++) {
+ Object v = arguments[i];
+ v = DefaultTypeTransformation.castToType(v, baseClass);
+ result[i - offset] = v;
+ }
+ return result;
+ }
+
+ public boolean isValidMethod(Class[] arguments) {
+ if (arguments == null) return true;
+
+ final int size = arguments.length;
+ CachedClass[] pt = getParameterTypes();
+ final int paramMinus1 = pt.length - 1;
+
+ if (isVargsMethod && size >= paramMinus1)
+ return isValidVarargsMethod(arguments, size, pt, paramMinus1);
+ else if (pt.length == size)
+ return isValidExactMethod(arguments, pt);
+ else if (pt.length == 1 && size == 0 && !pt[0].isPrimitive)
+ return true;
+ return false;
+ }
+
+ private static boolean isValidExactMethod(Class[] arguments, CachedClass[] pt) {
+ // lets check the parameter types match
+ int size = pt.length;
+ for (int i = 0; i < size; i++) {
+ if (!pt[i].isAssignableFrom(arguments[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isValidExactMethod(Object[] args) {
+ // lets check the parameter types match
+ getParametersTypes0();
+ int size = args.length;
+ if (size != parameterTypes.length)
+ return false;
+
+ for (int i = 0; i < size; i++) {
+ if (args[i] != null && !parameterTypes[i].isAssignableFrom(args[i].getClass())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isValidExactMethod(Class[] args) {
+ // lets check the parameter types match
+ getParametersTypes0();
+ int size = args.length;
+ if (size != parameterTypes.length)
+ return false;
+
+ for (int i = 0; i < size; i++) {
+ if (args[i] != null && !parameterTypes[i].isAssignableFrom(args[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean testComponentAssignable(Class toTestAgainst, Class toTest) {
+ Class component = toTest.getComponentType();
+ if (component == null) return false;
+ return MetaClassHelper.isAssignableFrom(toTestAgainst, component);
+ }
+
+ private static boolean isValidVarargsMethod(Class[] arguments, int size, CachedClass[] pt, int paramMinus1) {
+ // first check normal number of parameters
+ for (int i = 0; i < paramMinus1; i++) {
+ if (pt[i].isAssignableFrom(arguments[i])) continue;
+ return false;
+ }
+
+ // check direct match
+ CachedClass varg = pt[paramMinus1];
+ Class clazz = varg.getTheClass().getComponentType();
+ if (size == pt.length &&
+ (varg.isAssignableFrom(arguments[paramMinus1]) ||
+ testComponentAssignable(clazz, arguments[paramMinus1]))) {
+ return true;
+ }
+
+ // check varged
+ for (int i = paramMinus1; i < size; i++) {
+ if (MetaClassHelper.isAssignableFrom(clazz, arguments[i])) continue;
+ return false;
+ }
+ return true;
+ }
+
+ public boolean isValidMethod(Object[] arguments) {
+ if (arguments == null) return true;
+
+ final int size = arguments.length;
+ CachedClass[] paramTypes = getParameterTypes();
+ final int paramMinus1 = paramTypes.length - 1;
+
+ if (size >= paramMinus1 && paramTypes.length > 0 &&
+ paramTypes[(paramMinus1)].isArray) {
+ // first check normal number of parameters
+ for (int i = 0; i < paramMinus1; i++) {
+ if (paramTypes[i].isAssignableFrom(getArgClass(arguments[i]))) continue;
+ return false;
+ }
+
+
+ // check direct match
+ CachedClass varg = paramTypes[paramMinus1];
+ Class clazz = varg.getTheClass().getComponentType();
+ if (size == paramTypes.length &&
+ (varg.isAssignableFrom(getArgClass(arguments[paramMinus1])) ||
+ testComponentAssignable(clazz, getArgClass(arguments[paramMinus1])))) {
+ return true;
+ }
+
+
+ // check varged
+ for (int i = paramMinus1; i < size; i++) {
+ if (MetaClassHelper.isAssignableFrom(clazz, getArgClass(arguments[i]))) continue;
+ return false;
+ }
+ return true;
+ } else if (paramTypes.length == size) {
+ // lets check the parameter types match
+ for (int i = 0; i < size; i++) {
+ if (paramTypes[i].isAssignableFrom(getArgClass(arguments[i]))) continue;
+ return false;
+ }
+ return true;
+ } else if (paramTypes.length == 1 && size == 0 && !paramTypes[0].isPrimitive) {
+ return true;
+ }
+ return false;
+ }
+
+ private static Class getArgClass(Object arg) {
+ Class cls;
+ if (arg == null) {
+ cls = null;
+ } else {
+ if (arg instanceof Wrapper) {
+ cls = ((Wrapper) arg).getType();
+ } else
+ cls = arg.getClass();
+ }
+ return cls;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/reflection/ReflectionCache.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/reflection/ReflectionCache.java b/src/main/java/org/codehaus/groovy/reflection/ReflectionCache.java
new file mode 100644
index 0000000..372a0a5
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/reflection/ReflectionCache.java
@@ -0,0 +1,113 @@
+/*
+ * 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.reflection;
+
+import org.codehaus.groovy.util.TripleKeyHashMap;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ReflectionCache {
+ private static final Map primitiveTypesMap = new HashMap();
+
+ static {
+ primitiveTypesMap.put(byte.class, Byte.class);
+ primitiveTypesMap.put(boolean.class, Boolean.class);
+ primitiveTypesMap.put(char.class, Character.class);
+ primitiveTypesMap.put(double.class, Double.class);
+ primitiveTypesMap.put(float.class, Float.class);
+ primitiveTypesMap.put(int.class, Integer.class);
+ primitiveTypesMap.put(long.class, Long.class);
+ primitiveTypesMap.put(short.class, Short.class);
+ }
+
+ public static Class autoboxType(Class type) {
+ final Class res = (Class) primitiveTypesMap.get(type);
+ return res == null ? type : res;
+ }
+
+ static TripleKeyHashMap mopNames = new TripleKeyHashMap();
+
+ public static String getMOPMethodName(CachedClass declaringClass, String name, boolean useThis) {
+ TripleKeyHashMap.Entry mopNameEntry = mopNames.getOrPut(declaringClass, name, Boolean.valueOf(useThis));
+ if (mopNameEntry.value == null) {
+ mopNameEntry.value = new StringBuffer().append(useThis ? "this$" : "super$").append(declaringClass.getSuperClassDistance()).append("$").append(name).toString();
+ }
+ return (String) mopNameEntry.value;
+ }
+
+ static final CachedClass STRING_CLASS = getCachedClass(String.class);
+
+ public static boolean isArray(Class klazz) {
+ return klazz.getName().charAt(0) == '[';
+ }
+
+ static void setAssignableFrom(Class klazz, Class aClass) {
+// SoftDoubleKeyMap.Entry val = (SoftDoubleKeyMap.Entry) assignableMap.getOrPut(klazz, aClass, null);
+// if (val.getValue() == null) {
+// val.setValue(Boolean.TRUE);
+// }
+ }
+
+ public static boolean isAssignableFrom(Class klazz, Class aClass) {
+ if (klazz == aClass)
+ return true;
+
+// SoftDoubleKeyMap.Entry val = (SoftDoubleKeyMap.Entry) assignableMap.getOrPut(klazz, aClass, null);
+// if (val.getValue() == null) {
+// val.setValue(Boolean.valueOf(klazz.isAssignableFrom(aClass)));
+// }
+// return ((Boolean)val.getValue()).booleanValue();
+ return klazz.isAssignableFrom(aClass);
+ }
+
+ static boolean arrayContentsEq(Object[] a1, Object[] a2) {
+ if (a1 == null) {
+ return a2 == null || a2.length == 0;
+ }
+
+ if (a2 == null) {
+ return a1.length == 0;
+ }
+
+ if (a1.length != a2.length) {
+ return false;
+ }
+
+ for (int i = 0; i < a1.length; i++) {
+ if (a1[i] != a2[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static final CachedClass OBJECT_CLASS = getCachedClass(Object.class);
+
+ public static final CachedClass OBJECT_ARRAY_CLASS = getCachedClass(Object[].class);
+
+ public static CachedClass getCachedClass(Class klazz) {
+ if (klazz == null)
+ return null;
+
+ return ClassInfo.getClassInfo(klazz).getCachedClass ();
+ }
+
+}