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 ();
+    }
+
+}