http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/lang/MetaClassImpl.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/lang/MetaClassImpl.java b/src/main/groovy/lang/MetaClassImpl.java deleted file mode 100644 index c453a3f..0000000 --- a/src/main/groovy/lang/MetaClassImpl.java +++ /dev/null @@ -1,4021 +0,0 @@ -/* - * 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 groovy.lang; - -import org.apache.groovy.internal.util.UncheckedThrow; -import org.codehaus.groovy.GroovyBugError; -import org.codehaus.groovy.ast.ClassNode; -import org.codehaus.groovy.classgen.asm.BytecodeHelper; -import org.codehaus.groovy.control.CompilationUnit; -import org.codehaus.groovy.control.Phases; -import org.codehaus.groovy.reflection.CacheAccessControlException; -import org.codehaus.groovy.reflection.CachedClass; -import org.codehaus.groovy.reflection.CachedConstructor; -import org.codehaus.groovy.reflection.CachedField; -import org.codehaus.groovy.reflection.CachedMethod; -import org.codehaus.groovy.reflection.ClassInfo; -import org.codehaus.groovy.reflection.GeneratedMetaMethod; -import org.codehaus.groovy.reflection.ParameterTypes; -import org.codehaus.groovy.reflection.ReflectionCache; -import org.codehaus.groovy.reflection.android.AndroidSupport; -import org.codehaus.groovy.runtime.ArrayTypeUtils; -import org.codehaus.groovy.runtime.ConvertedClosure; -import org.codehaus.groovy.runtime.CurriedClosure; -import org.codehaus.groovy.runtime.DefaultGroovyMethods; -import org.codehaus.groovy.runtime.GeneratedClosure; -import org.codehaus.groovy.runtime.GroovyCategorySupport; -import org.codehaus.groovy.runtime.InvokerHelper; -import org.codehaus.groovy.runtime.InvokerInvocationException; -import org.codehaus.groovy.runtime.MetaClassHelper; -import org.codehaus.groovy.runtime.MethodClosure; -import org.codehaus.groovy.runtime.callsite.AbstractCallSite; -import org.codehaus.groovy.runtime.callsite.CallSite; -import org.codehaus.groovy.runtime.callsite.ConstructorSite; -import org.codehaus.groovy.runtime.callsite.MetaClassConstructorSite; -import org.codehaus.groovy.runtime.callsite.PogoMetaClassSite; -import org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite; -import org.codehaus.groovy.runtime.callsite.PojoMetaClassSite; -import org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite; -import org.codehaus.groovy.runtime.callsite.StaticMetaClassSite; -import org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite; -import org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod; -import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl; -import org.codehaus.groovy.runtime.metaclass.MetaMethodIndex; -import org.codehaus.groovy.runtime.metaclass.MethodMetaProperty.GetBeanMethodMetaProperty; -import org.codehaus.groovy.runtime.metaclass.MethodMetaProperty.GetMethodMetaProperty; -import org.codehaus.groovy.runtime.metaclass.MethodSelectionException; -import org.codehaus.groovy.runtime.metaclass.MissingMethodExceptionNoStack; -import org.codehaus.groovy.runtime.metaclass.MissingMethodExecutionFailed; -import org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack; -import org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaMethod; -import org.codehaus.groovy.runtime.metaclass.MultipleSetterProperty; -import org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod; -import org.codehaus.groovy.runtime.metaclass.NewMetaMethod; -import org.codehaus.groovy.runtime.metaclass.NewStaticMetaMethod; -import org.codehaus.groovy.runtime.metaclass.TransformMetaMethod; -import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; -import org.codehaus.groovy.runtime.typehandling.NumberMathModificationInfo; -import org.codehaus.groovy.runtime.wrappers.Wrapper; -import org.codehaus.groovy.util.ComplexKeyHashMap; -import org.codehaus.groovy.util.FastArray; -import org.codehaus.groovy.util.SingleKeyHashMap; -import org.objectweb.asm.ClassVisitor; - -import java.beans.BeanInfo; -import java.beans.EventSetDescriptor; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.lang.reflect.Array; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Proxy; -import java.net.URL; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import static org.codehaus.groovy.ast.tools.GeneralUtils.inSamePackage; -import static org.codehaus.groovy.ast.tools.GeneralUtils.isDefaultVisibility; -import static org.codehaus.groovy.reflection.ReflectionCache.isAssignableFrom; - -/** - * Allows methods to be dynamically added to existing classes at runtime - * @see groovy.lang.MetaClass - */ -public class MetaClassImpl implements MetaClass, MutableMetaClass { - - public static final Object[] EMPTY_ARGUMENTS = {}; - - protected static final String STATIC_METHOD_MISSING = "$static_methodMissing"; - protected static final String STATIC_PROPERTY_MISSING = "$static_propertyMissing"; - protected static final String METHOD_MISSING = "methodMissing"; - protected static final String PROPERTY_MISSING = "propertyMissing"; - protected static final String INVOKE_METHOD_METHOD = "invokeMethod"; - - private static final String CLOSURE_CALL_METHOD = "call"; - private static final String CLOSURE_DO_CALL_METHOD = "doCall"; - private static final String GET_PROPERTY_METHOD = "getProperty"; - private static final String SET_PROPERTY_METHOD = "setProperty"; - private static final Class[] METHOD_MISSING_ARGS = new Class[]{String.class, Object.class}; - private static final Class[] GETTER_MISSING_ARGS = new Class[]{String.class}; - private static final Class[] SETTER_MISSING_ARGS = METHOD_MISSING_ARGS; - private static final Comparator<CachedClass> CACHED_CLASS_NAME_COMPARATOR = new Comparator<CachedClass>() { - public int compare(final CachedClass o1, final CachedClass o2) { - return o1.getName().compareTo(o2.getName()); - } - }; - private static final MetaMethod[] EMPTY = new MetaMethod[0]; - private static final MetaMethod AMBIGUOUS_LISTENER_METHOD = new DummyMetaMethod(); - - protected final Class theClass; - protected final CachedClass theCachedClass; - protected final boolean isGroovyObject; - protected final boolean isMap; - protected final MetaMethodIndex metaMethodIndex; - - private final Index classPropertyIndex = new MethodIndex(); - private final SingleKeyHashMap staticPropertyIndex = new SingleKeyHashMap(); - private final Map<String, MetaMethod> listeners = new HashMap<String, MetaMethod>(); - private final List<MetaMethod> allMethods = new ArrayList<MetaMethod>(); - // we only need one of these that can be reused over and over. - private final MetaProperty arrayLengthProperty = new MetaArrayLengthProperty(); - private final Index classPropertyIndexForSuper = new MethodIndex(); - private final Set<MetaMethod> newGroovyMethodsSet = new HashSet<MetaMethod>(); - private final MetaMethod[] myNewMetaMethods; - private final MetaMethod[] additionalMetaMethods; - - protected MetaMethod getPropertyMethod; - protected MetaMethod invokeMethodMethod; - protected MetaMethod setPropertyMethod; - protected MetaClassRegistry registry; - private ClassNode classNode; - private FastArray constructors; - private volatile boolean initialized; - private MetaMethod genericGetMethod; - private MetaMethod genericSetMethod; - private MetaMethod propertyMissingGet; - private MetaMethod propertyMissingSet; - private MetaMethod methodMissing; - private MetaMethodIndex.Header mainClassMethodHeader; - - /** - * Constructor - * - * @param theClass The class this is the metaclass dor - * @param add The methods for this class - */ - public MetaClassImpl(final Class theClass, MetaMethod[] add) { - this.theClass = theClass; - theCachedClass = ReflectionCache.getCachedClass(theClass); - this.isGroovyObject = GroovyObject.class.isAssignableFrom(theClass); - this.isMap = Map.class.isAssignableFrom(theClass); - this.registry = GroovySystem.getMetaClassRegistry(); - metaMethodIndex = new MetaMethodIndex(theCachedClass); - final MetaMethod[] metaMethods = theCachedClass.getNewMetaMethods(); - if (add != null && !(add.length == 0)) { - List<MetaMethod> arr = new ArrayList<MetaMethod>(); - arr.addAll(Arrays.asList(metaMethods)); - arr.addAll(Arrays.asList(add)); - myNewMetaMethods = arr.toArray(new MetaMethod[arr.size()]); - additionalMetaMethods = metaMethods; - } - else { - myNewMetaMethods = metaMethods; - additionalMetaMethods = EMPTY; - } - } - - /** - * Constructor that sets the methods to null - * - * @param theClass The class this is the metaclass dor - */ - public MetaClassImpl(final Class theClass) { - this(theClass, null); - } - - /** - * Constructor with registry - * - * @param registry The metaclass registry for this MetaClass - * @param theClass The class - * @param add The methods - */ - public MetaClassImpl(MetaClassRegistry registry, final Class theClass, MetaMethod add []) { - this(theClass, add); - this.registry = registry; - this.constructors = new FastArray(theCachedClass.getConstructors()); - } - - /** - * Constructor with registry setting methods to null - * - * @param registry The metaclass registry for this MetaClass - * @param theClass The class - */ - public MetaClassImpl(MetaClassRegistry registry, final Class theClass) { - this(registry, theClass, null); - } - - /** - * Returns the cached class for this metaclass - * - * @return The cached class. - */ - public final CachedClass getTheCachedClass() { - return theCachedClass; - } - - /** - * Returns the registry for this metaclass - * - * @return The resgistry - */ - public MetaClassRegistry getRegistry() { - return registry; - } - - /** - * @see MetaObjectProtocol#respondsTo(Object, String, Object[]) - */ - public List respondsTo(Object obj, String name, Object[] argTypes) { - Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes); - MetaMethod m = getMetaMethod(name, classes); - if (m!=null) { - return Collections.singletonList(m); - } - return Collections.emptyList(); - } - - /** - * @see MetaObjectProtocol#respondsTo(Object, String) - */ - public List respondsTo(final Object obj, final String name) { - final Object o = getMethods(getTheClass(), name, false); - if (o instanceof FastArray) - return ((FastArray) o).toList(); - else - return Collections.singletonList(o); - } - - /** - * @see MetaObjectProtocol#hasProperty(Object,String) - */ - public MetaProperty hasProperty(Object obj, String name) { - return getMetaProperty(name); - } - - /** - * @see MetaObjectProtocol#getMetaProperty(String) - */ - public MetaProperty getMetaProperty(String name) { - SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass); - if (propertyMap.containsKey(name)) { - return (MetaProperty) propertyMap.get(name); - } else if (staticPropertyIndex.containsKey(name)) { - return (MetaProperty) staticPropertyIndex.get(name); - } else { - propertyMap = classPropertyIndexForSuper.getNotNull(theCachedClass); - if (propertyMap.containsKey(name)) - return (MetaProperty) propertyMap.get(name); - else { - CachedClass superClass = theCachedClass; - while (superClass != null && superClass != ReflectionCache.OBJECT_CLASS) { - final MetaBeanProperty property = findPropertyInClassHierarchy(name, superClass); - if (property != null) { - onSuperPropertyFoundInHierarchy(property); - return property; - } - superClass = superClass.getCachedSuperClass(); - } - return null; - } - } - } - - /** - * @see MetaObjectProtocol#getStaticMetaMethod(String, Object[]) - */ - public MetaMethod getStaticMetaMethod(String name, Object[] argTypes) { - Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes); - return pickStaticMethod(name, classes); - } - - - /** - * @see MetaObjectProtocol#getMetaMethod(String, Object[]) - */ - public MetaMethod getMetaMethod(String name, Object[] argTypes) { - Class[] classes = MetaClassHelper.castArgumentsToClassArray(argTypes); - return pickMethod(name, classes); - } - - /** - *Returns the class this object this is the metaclass of. - * - * @return The class contained by this metaclass - */ - public Class getTheClass() { - return this.theClass; - } - - /** - * Return wether the class represented by this metaclass instance is an instance of the GroovyObject class - * - * @return true if this is a groovy class, false otherwise. - */ - public boolean isGroovyObject() { - return isGroovyObject; - } - - /** - * Fills the method index - */ - private void fillMethodIndex() { - mainClassMethodHeader = metaMethodIndex.getHeader(theClass); - LinkedList<CachedClass> superClasses = getSuperClasses(); - CachedClass firstGroovySuper = calcFirstGroovySuperClass(superClasses); - - Set<CachedClass> interfaces = theCachedClass.getInterfaces(); - addInterfaceMethods(interfaces); - - populateMethods(superClasses, firstGroovySuper); - - inheritInterfaceNewMetaMethods(interfaces); - if (isGroovyObject) { - metaMethodIndex.copyMethodsToSuper(); - - connectMultimethods(superClasses, firstGroovySuper); - removeMultimethodsOverloadedWithPrivateMethods(); - - replaceWithMOPCalls(theCachedClass.mopMethods); - } - } - - private void populateMethods(LinkedList<CachedClass> superClasses, CachedClass firstGroovySuper) { - - MetaMethodIndex.Header header = metaMethodIndex.getHeader(firstGroovySuper.getTheClass()); - CachedClass c; - Iterator<CachedClass> iter = superClasses.iterator(); - for (; iter.hasNext();) { - c = iter.next(); - - CachedMethod[] cachedMethods = c.getMethods(); - for (CachedMethod metaMethod : cachedMethods) { - addToAllMethodsIfPublic(metaMethod); - if (!metaMethod.isPrivate() || c == firstGroovySuper) - addMetaMethodToIndex(metaMethod, header); - } - - MetaMethod[] cachedMethods1 = getNewMetaMethods(c); - for (final MetaMethod method : cachedMethods1) { - if (!newGroovyMethodsSet.contains(method)) { - newGroovyMethodsSet.add(method); - addMetaMethodToIndex(method, header); - } - } - - if (c == firstGroovySuper) - break; - } - - MetaMethodIndex.Header last = header; - for (;iter.hasNext();) { - c = iter.next(); - header = metaMethodIndex.getHeader(c.getTheClass()); - - if (last != null) { - metaMethodIndex.copyNonPrivateMethods(last, header); - } - last = header; - - for (CachedMethod metaMethod : c.getMethods()) { - addToAllMethodsIfPublic(metaMethod); - addMetaMethodToIndex(metaMethod, header); - } - - for (final MetaMethod method : getNewMetaMethods(c)) { - if (method.getName().equals("<init>") && !method.getDeclaringClass().equals(theCachedClass)) continue; - if (!newGroovyMethodsSet.contains(method)) { - newGroovyMethodsSet.add(method); - addMetaMethodToIndex(method, header); - } - } - } - } - - private MetaMethod[] getNewMetaMethods(CachedClass c) { - if (theCachedClass != c) - return c.getNewMetaMethods(); - - return myNewMetaMethods; - } - - private void addInterfaceMethods(Set<CachedClass> interfaces) { - MetaMethodIndex.Header header = metaMethodIndex.getHeader(theClass); - for (CachedClass c : interfaces) { - final CachedMethod[] m = c.getMethods(); - for (int i = 0; i != m.length; ++i) { - MetaMethod method = m[i]; - addMetaMethodToIndex(method, header); - } - } - } - - protected LinkedList<CachedClass> getSuperClasses() { - LinkedList<CachedClass> superClasses = new LinkedList<CachedClass>(); - - if (theClass.isInterface()) { - superClasses.addFirst(ReflectionCache.OBJECT_CLASS); - } else { - for (CachedClass c = theCachedClass; c != null; c = c.getCachedSuperClass()) { - superClasses.addFirst(c); - } - if (theCachedClass.isArray && theClass != Object[].class && !theClass.getComponentType().isPrimitive()) { - superClasses.addFirst(ReflectionCache.OBJECT_ARRAY_CLASS); - } - } - return superClasses; - } - - private void removeMultimethodsOverloadedWithPrivateMethods() { - MethodIndexAction mia = new MethodIndexAction() { - public boolean skipClass(Class clazz) { - return clazz == theClass; - } - - public void methodNameAction(Class clazz, MetaMethodIndex.Entry e) { - if (e.methods == null) - return; - - boolean hasPrivate = false; - if (e.methods instanceof FastArray) { - FastArray methods = (FastArray) e.methods; - final int len = methods.size(); - final Object[] data = methods.getArray(); - for (int i = 0; i != len; ++i) { - MetaMethod method = (MetaMethod) data[i]; - if (method.isPrivate() && clazz == method.getDeclaringClass().getTheClass()) { - hasPrivate = true; - break; - } - } - } - else { - MetaMethod method = (MetaMethod) e.methods; - if (method.isPrivate() && clazz == method.getDeclaringClass().getTheClass()) { - hasPrivate = true; - } - } - - if (!hasPrivate) return; - - // We have private methods for that name, so remove the - // multimethods. That is the same as in our index for - // super, so just copy the list from there. It is not - // possible to use a pointer here, because the methods - // in the index for super are replaced later by MOP - // methods like super$5$foo - final Object o = e.methodsForSuper; - if (o instanceof FastArray) - e.methods = ((FastArray) o).copy(); - else - e.methods = o; - } - }; - mia.iterate(); - } - - - private void replaceWithMOPCalls(final CachedMethod[] mopMethods) { - // no MOP methods if not a child of GroovyObject - if (!isGroovyObject) return; - - class MOPIter extends MethodIndexAction { - boolean useThis; - - @Override - public void methodNameAction(Class clazz, MetaMethodIndex.Entry e) { - if (useThis) { - if (e.methods == null) - return; - - if (e.methods instanceof FastArray) { - FastArray methods = (FastArray) e.methods; - processFastArray(methods); - } - else { - MetaMethod method = (MetaMethod) e.methods; - if (method instanceof NewMetaMethod) - return; - if (useThis ^ Modifier.isPrivate(method.getModifiers())) return; - String mopName = method.getMopName(); - int index = Arrays.binarySearch(mopMethods, mopName, CachedClass.CachedMethodComparatorWithString.INSTANCE); - if (index >= 0) { - int from = index; - while (from > 0 && mopMethods[from-1].getName().equals(mopName)) - from--; - int to = index; - while (to < mopMethods.length-1 && mopMethods[to+1].getName().equals(mopName)) - to++; - - int matchingMethod = findMatchingMethod(mopMethods, from, to, method); - if (matchingMethod != -1) { - e.methods = mopMethods[matchingMethod]; - } - } - } - } - else { - if (e.methodsForSuper == null) - return; - - if (e.methodsForSuper instanceof FastArray) { - FastArray methods = (FastArray) e.methodsForSuper; - processFastArray(methods); - } - else { - MetaMethod method = (MetaMethod) e.methodsForSuper; - if (method instanceof NewMetaMethod) - return; - if (useThis ^ Modifier.isPrivate(method.getModifiers())) return; - String mopName = method.getMopName(); - // GROOVY-4922: Due to a numbering scheme change, we must find the super$X$method which exists - // with the highest number. If we don't, no method may be found, leading to a stack overflow - String[] decomposedMopName = decomposeMopName(mopName); - int distance = Integer.parseInt(decomposedMopName[1]); - while (distance>0) { - String fixedMopName = decomposedMopName[0] + distance + decomposedMopName[2]; - int index = Arrays.binarySearch(mopMethods, fixedMopName, CachedClass.CachedMethodComparatorWithString.INSTANCE); - if (index >= 0) { - int from = index; - while (from > 0 && mopMethods[from-1].getName().equals(fixedMopName)) - from--; - int to = index; - while (to < mopMethods.length-1 && mopMethods[to+1].getName().equals(fixedMopName)) - to++; - - int matchingMethod = findMatchingMethod(mopMethods, from, to, method); - if (matchingMethod != -1) { - e.methodsForSuper = mopMethods[matchingMethod]; - distance = 0; - } - } - distance--; - } - } - } - } - - private String[] decomposeMopName(final String mopName) { - int idx = mopName.indexOf("$"); - if (idx>0) { - int eidx = mopName.indexOf("$", idx+1); - if (eidx>0) { - return new String[] { - mopName.substring(0, idx+1), - mopName.substring(idx+1, eidx), - mopName.substring(eidx) - }; - } - } - return new String[]{"","0",mopName}; - } - - private void processFastArray(FastArray methods) { - final int len = methods.size(); - final Object[] data = methods.getArray(); - for (int i = 0; i != len; ++i) { - MetaMethod method = (MetaMethod) data[i]; - if (method instanceof NewMetaMethod) continue; - boolean isPrivate = Modifier.isPrivate(method.getModifiers()); - if (useThis ^ isPrivate) continue; - String mopName = method.getMopName(); - int index = Arrays.binarySearch(mopMethods, mopName, CachedClass.CachedMethodComparatorWithString.INSTANCE); - if (index >= 0) { - int from = index; - while (from > 0 && mopMethods[from-1].getName().equals(mopName)) - from--; - int to = index; - while (to < mopMethods.length-1 && mopMethods[to+1].getName().equals(mopName)) - to++; - - int matchingMethod = findMatchingMethod(mopMethods, from, to, method); - if (matchingMethod != -1) { - methods.set(i, mopMethods[matchingMethod]); - } - } - } - } - } - MOPIter iter = new MOPIter(); - - // replace all calls for super with the correct MOP method - iter.useThis = false; - iter.iterate(); - // replace all calls for this with the correct MOP method - iter.useThis = true; - iter.iterate(); - } - - private void inheritInterfaceNewMetaMethods(Set<CachedClass> interfaces) { - // add methods declared by DGM for interfaces - for (CachedClass cls : interfaces) { - MetaMethod methods[] = getNewMetaMethods(cls); - for (MetaMethod method : methods) { - boolean skip = false; - // skip DGM methods on an interface if the class already has the method - // but don't skip for GroovyObject-related methods as it breaks things :-( - if (method instanceof GeneratedMetaMethod && !isAssignableFrom(GroovyObject.class, method.getDeclaringClass().getTheClass())) { - for (Method m : theClass.getMethods()) { - if (method.getName().equals(m.getName()) - // below not true for DGM#push and also co-variant return scenarios - //&& method.getReturnType().equals(m.getReturnType()) - && MetaMethod.equal(method.getParameterTypes(), m.getParameterTypes())) { - skip = true; - break; - } - } - } - if (!skip) { - if (!newGroovyMethodsSet.contains(method)) { - newGroovyMethodsSet.add(method); - } - addMetaMethodToIndex(method, mainClassMethodHeader); - } - } - } - } - - private void connectMultimethods(List<CachedClass> superClasses, CachedClass firstGroovyClass) { - superClasses = DefaultGroovyMethods.reverse(superClasses); - MetaMethodIndex.Header last = null; - for (final CachedClass c : superClasses) { - MetaMethodIndex.Header methodIndex = metaMethodIndex.getHeader(c.getTheClass()); - // We don't copy DGM methods to superclasses' indexes - // The reason we can do that is particular set of DGM methods in use, - // if at some point we will define DGM method for some Groovy class or - // for a class derived from such, we will need to revise this condition. - // It saves us a lot of space and some noticeable time - if (last != null) metaMethodIndex.copyNonPrivateNonNewMetaMethods(last, methodIndex); - last = methodIndex; - - if (c == firstGroovyClass) - break; - } - } - - private CachedClass calcFirstGroovySuperClass(Collection superClasses) { - if (theCachedClass.isInterface) - return ReflectionCache.OBJECT_CLASS; - - CachedClass firstGroovy = null; - Iterator iter = superClasses.iterator(); - for (; iter.hasNext();) { - CachedClass c = (CachedClass) iter.next(); - if (GroovyObject.class.isAssignableFrom(c.getTheClass())) { - firstGroovy = c; - break; - } - } - - if (firstGroovy == null) - firstGroovy = theCachedClass; - else { - if (firstGroovy.getTheClass() == GroovyObjectSupport.class && iter.hasNext()) { - firstGroovy = (CachedClass) iter.next(); - if (firstGroovy.getTheClass() == Closure.class && iter.hasNext()) { - firstGroovy = (CachedClass) iter.next(); - } - } - } - - return GroovyObject.class.isAssignableFrom(firstGroovy.getTheClass()) ? firstGroovy.getCachedSuperClass() : firstGroovy; - } - - /** - * Gets all instance methods available on this class for the given name - * - * @return all the normal instance methods available on this class for the - * given name - */ - private Object getMethods(Class sender, String name, boolean isCallToSuper) { - Object answer; - - final MetaMethodIndex.Entry entry = metaMethodIndex.getMethods(sender, name); - if (entry == null) - answer = FastArray.EMPTY_LIST; - else - if (isCallToSuper) { - answer = entry.methodsForSuper; - } else { - answer = entry.methods; - } - - if (answer == null) answer = FastArray.EMPTY_LIST; - - if (!isCallToSuper) { - List used = GroovyCategorySupport.getCategoryMethods(name); - if (used != null) { - FastArray arr; - if (answer instanceof MetaMethod) { - arr = new FastArray(); - arr.add(answer); - } - else - arr = ((FastArray) answer).copy(); - - for (Iterator iter = used.iterator(); iter.hasNext();) { - MetaMethod element = (MetaMethod) iter.next(); - if (!element.getDeclaringClass().getTheClass().isAssignableFrom(sender)) - continue; - filterMatchingMethodForCategory(arr, element); - } - answer = arr; - } - } - return answer; - } - - /** - * Returns all the normal static methods on this class for the given name - * - * @return all the normal static methods available on this class for the - * given name - */ - private Object getStaticMethods(Class sender, String name) { - final MetaMethodIndex.Entry entry = metaMethodIndex.getMethods(sender, name); - if (entry == null) - return FastArray.EMPTY_LIST; - Object answer = entry.staticMethods; - if (answer == null) - return FastArray.EMPTY_LIST; - return answer; - } - - /** - * Returns whether this MetaClassImpl has been modified. Since MetaClassImpl - * is not designed for modification this method always returns false - * - * @return false - */ - public boolean isModified() { - return false; // MetaClassImpl not designed for modification, just return false - } - - /** - *Adds an instance method to this metaclass. - * - * @param method The method to be added - */ - public void addNewInstanceMethod(Method method) { - final CachedMethod cachedMethod = CachedMethod.find(method); - NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(cachedMethod); - final CachedClass declaringClass = newMethod.getDeclaringClass(); - addNewInstanceMethodToIndex(newMethod, metaMethodIndex.getHeader(declaringClass.getTheClass())); - } - - private void addNewInstanceMethodToIndex(MetaMethod newMethod, MetaMethodIndex.Header header) { - if (!newGroovyMethodsSet.contains(newMethod)) { - newGroovyMethodsSet.add(newMethod); - addMetaMethodToIndex(newMethod, header); - } - } - - /** - *Adds a static method to this metaclass. - * - * @param method The method to be added - */ - public void addNewStaticMethod(Method method) { - final CachedMethod cachedMethod = CachedMethod.find(method); - NewStaticMetaMethod newMethod = new NewStaticMetaMethod(cachedMethod); - final CachedClass declaringClass = newMethod.getDeclaringClass(); - addNewStaticMethodToIndex(newMethod, metaMethodIndex.getHeader(declaringClass.getTheClass())); - } - - private void addNewStaticMethodToIndex(MetaMethod newMethod, MetaMethodIndex.Header header) { - if (!newGroovyMethodsSet.contains(newMethod)) { - newGroovyMethodsSet.add(newMethod); - addMetaMethodToIndex(newMethod, header); - } - } - - /** - * Invoke a method on the given object with the given arguments. - * - * @param object The object the method should be invoked on. - * @param methodName The name of the method to invoke. - * @param arguments The arguments to the invoked method as null, a Tuple, an array or a single argument of any type. - * - * @return The result of the method invocation. - */ - public Object invokeMethod(Object object, String methodName, Object arguments) { - if (arguments == null) { - return invokeMethod(object, methodName, MetaClassHelper.EMPTY_ARRAY); - } - if (arguments instanceof Tuple) { - Tuple tuple = (Tuple) arguments; - return invokeMethod(object, methodName, tuple.toArray()); - } - if (arguments instanceof Object[]) { - return invokeMethod(object, methodName, (Object[]) arguments); - } else { - return invokeMethod(object, methodName, new Object[]{arguments}); - } - } - - /** - * Invoke a missing method on the given object with the given arguments. - * - * @param instance The object the method should be invoked on. - * @param methodName The name of the method to invoke. - * @param arguments The arguments to the invoked method. - * - * @return The result of the method invocation. - */ - public Object invokeMissingMethod(Object instance, String methodName, Object[] arguments) { - return invokeMissingMethod(instance, methodName, arguments, null, false); - } - - /** - * Invoke a missing property on the given object with the given arguments. - * - * @param instance The object the method should be invoked on. - * @param propertyName The name of the property to invoke. - * @param optionalValue The (optional) new value for the property - * @param isGetter Wether the method is a getter - * - * @return The result of the method invocation. - */ - public Object invokeMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) { - Class theClass = instance instanceof Class ? (Class)instance : instance.getClass(); - CachedClass superClass = theCachedClass; - while(superClass != null && superClass != ReflectionCache.OBJECT_CLASS) { - final MetaBeanProperty property = findPropertyInClassHierarchy(propertyName, superClass); - if(property != null) { - onSuperPropertyFoundInHierarchy(property); - if(!isGetter) { - property.setProperty(instance, optionalValue); - return null; - } - else { - return property.getProperty(instance); - } - } - superClass = superClass.getCachedSuperClass(); - } - // got here to property not found, look for getProperty or setProperty overrides - if(isGetter) { - final Class[] getPropertyArgs = {String.class}; - final MetaMethod method = findMethodInClassHierarchy(instance.getClass(), GET_PROPERTY_METHOD, getPropertyArgs, this); - if(method != null && method instanceof ClosureMetaMethod) { - onGetPropertyFoundInHierarchy(method); - return method.invoke(instance,new Object[]{propertyName}); - } - } - else { - final Class[] setPropertyArgs = {String.class, Object.class}; - final MetaMethod method = findMethodInClassHierarchy(instance.getClass(), SET_PROPERTY_METHOD, setPropertyArgs, this); - if(method != null && method instanceof ClosureMetaMethod) { - onSetPropertyFoundInHierarchy(method); - return method.invoke(instance, new Object[]{propertyName, optionalValue}); - } - } - - try { - if (!(instance instanceof Class)) { - if (isGetter) { - if (propertyMissingGet != null) { - return propertyMissingGet.invoke(instance, new Object[]{propertyName}); - } - } else { - if (propertyMissingSet != null) { - return propertyMissingSet.invoke(instance, new Object[]{propertyName, optionalValue}); - } - } - } - } catch (InvokerInvocationException iie) { - boolean shouldHandle = isGetter && propertyMissingGet != null; - if (!shouldHandle) shouldHandle = !isGetter && propertyMissingSet != null; - if (shouldHandle && iie.getCause() instanceof MissingPropertyException) { - throw (MissingPropertyException) iie.getCause(); - } - throw iie; - } - - if (instance instanceof Class && theClass != Class.class) { - final MetaProperty metaProperty = InvokerHelper.getMetaClass(Class.class).hasProperty(instance, propertyName); - if (metaProperty != null) - if (isGetter) - return metaProperty.getProperty(instance); - else { - metaProperty.setProperty(instance, optionalValue); - return null; - } - } - throw new MissingPropertyExceptionNoStack(propertyName, theClass); - } - - private Object invokeMissingMethod(Object instance, String methodName, Object[] arguments, RuntimeException original, boolean isCallToSuper) { - if (!isCallToSuper) { - Class instanceKlazz = instance.getClass(); - if (theClass != instanceKlazz && theClass.isAssignableFrom(instanceKlazz)) - instanceKlazz = theClass; - - Class[] argClasses = MetaClassHelper.castArgumentsToClassArray(arguments); - - MetaMethod method = findMixinMethod(methodName, argClasses); - if(method != null) { - onMixinMethodFound(method); - return method.invoke(instance, arguments); - } - - method = findMethodInClassHierarchy(instanceKlazz, methodName, argClasses, this); - if(method != null) { - onSuperMethodFoundInHierarchy(method); - return method.invoke(instance, arguments); - } - - // still not method here, so see if there is an invokeMethod method up the hierarchy - final Class[] invokeMethodArgs = {String.class, Object[].class}; - method = findMethodInClassHierarchy(instanceKlazz, INVOKE_METHOD_METHOD, invokeMethodArgs, this ); - if(method != null && method instanceof ClosureMetaMethod) { - onInvokeMethodFoundInHierarchy(method); - return method.invoke(instance, invokeMethodArgs); - } - } - - if (methodMissing != null) { - try { - return methodMissing.invoke(instance, new Object[]{methodName, arguments}); - } catch (InvokerInvocationException iie) { - if (methodMissing instanceof ClosureMetaMethod && iie.getCause() instanceof MissingMethodException) { - MissingMethodException mme = (MissingMethodException) iie.getCause(); - throw new MissingMethodExecutionFailed(mme.getMethod(), mme.getClass(), - mme.getArguments(),mme.isStatic(),mme); - } - throw iie; - } catch (MissingMethodException mme) { - if (methodMissing instanceof ClosureMetaMethod) - throw new MissingMethodExecutionFailed(mme.getMethod(), mme.getClass(), - mme.getArguments(),mme.isStatic(),mme); - else - throw mme; - } - } else if (original != null) throw original; - else throw new MissingMethodExceptionNoStack(methodName, theClass, arguments, false); - } - - protected void onSuperPropertyFoundInHierarchy(MetaBeanProperty property) { - } - - protected void onMixinMethodFound(MetaMethod method) { - } - - protected void onSuperMethodFoundInHierarchy(MetaMethod method) { - } - - protected void onInvokeMethodFoundInHierarchy(MetaMethod method) { - } - - protected void onSetPropertyFoundInHierarchy(MetaMethod method) { - } - - protected void onGetPropertyFoundInHierarchy(MetaMethod method) { - } - - - /** - * Hook to deal with the case of MissingProperty for static properties. The method will look attempt to look up - * "propertyMissing" handlers and invoke them otherwise thrown a MissingPropertyException - * - * @param instance The instance - * @param propertyName The name of the property - * @param optionalValue The value in the case of a setter - * @param isGetter True if its a getter - * @return The value in the case of a getter or a MissingPropertyException - */ - protected Object invokeStaticMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) { - MetaClass mc = instance instanceof Class ? registry.getMetaClass((Class) instance) : this; - if (isGetter) { - MetaMethod propertyMissing = mc.getMetaMethod(STATIC_PROPERTY_MISSING, GETTER_MISSING_ARGS); - if (propertyMissing != null) { - return propertyMissing.invoke(instance, new Object[]{propertyName}); - } - } else { - MetaMethod propertyMissing = mc.getMetaMethod(STATIC_PROPERTY_MISSING, SETTER_MISSING_ARGS); - if (propertyMissing != null) { - return propertyMissing.invoke(instance, new Object[]{propertyName, optionalValue}); - } - } - - if (instance instanceof Class) { - throw new MissingPropertyException(propertyName, (Class) instance); - } - throw new MissingPropertyException(propertyName, theClass); - } - - - /** - * Invokes a method on the given receiver for the specified arguments. - * The MetaClass will attempt to establish the method to invoke based on the name and arguments provided. - * - * - * @param object The object which the method was invoked on - * @param methodName The name of the method - * @param originalArguments The arguments to the method - * - * @return The return value of the method - * - * @see MetaClass#invokeMethod(Class, Object, String, Object[], boolean, boolean) - */ - public Object invokeMethod(Object object, String methodName, Object[] originalArguments) { - return invokeMethod(theClass, object, methodName, originalArguments, false, false); - } - - private Object invokeMethodClosure(Object object, String methodName, Object[] arguments) { - final MethodClosure mc = (MethodClosure) object; - final Object owner = mc.getOwner(); - - methodName = mc.getMethod(); - final Class ownerClass = owner instanceof Class ? (Class) owner : owner.getClass(); - final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass); - - // To conform to "Least Surprise" principle, try to invoke method with original arguments first, which can match most of use cases - try { - return ownerMetaClass.invokeMethod(ownerClass, owner, methodName, arguments, false, false); - } catch (MissingMethodExceptionNoStack e) { - // CONSTRUCTOR REFERENCE - if (owner instanceof Class && MethodClosure.NEW.equals(methodName)) { - if (ownerClass.isArray()) { - if (0 == arguments.length) { - throw new GroovyRuntimeException("The arguments(specifying size) are required to create array[" + ownerClass.getCanonicalName() + "]"); - } - - int arrayDimension = ArrayTypeUtils.dimension(ownerClass); - - if (arguments.length > arrayDimension) { - throw new GroovyRuntimeException("The length[" + arguments.length + "] of arguments should not be greater than the dimensions[" + arrayDimension + "] of array[" + ownerClass.getCanonicalName() + "]"); - } - - int[] sizeArray = new int[arguments.length]; - - for (int i = 0, n = sizeArray.length; i < n; i++) { - Object argument = arguments[i]; - - if (argument instanceof Integer) { - sizeArray[i] = (Integer) argument; - } else { - sizeArray[i] = Integer.parseInt(String.valueOf(argument)); - } - } - - Class arrayType = - arguments.length == arrayDimension - ? ArrayTypeUtils.elementType(ownerClass) // Just for better performance, though we can use reduceDimension only - : ArrayTypeUtils.reduceDimension(ownerClass, (arrayDimension - arguments.length)); - return Array.newInstance(arrayType, sizeArray); - } - - return ownerMetaClass.invokeConstructor(arguments); - } - - // METHOD REFERENCE - // if and only if the owner is a class and the method closure can be related to some instance methods, - // try to invoke method with adjusted arguments(first argument is the actual owner) again. - // otherwise throw the MissingMethodExceptionNoStack. - if (!(owner instanceof Class - && ((Boolean) mc.getProperty(MethodClosure.ANY_INSTANCE_METHOD_EXISTS)).booleanValue())) { - - throw e; - } - - if (arguments.length <= 0) { - return invokeMissingMethod(object, methodName, arguments); - } - - Object newOwner = arguments[0]; - Object[] newArguments = Arrays.copyOfRange(arguments, 1, arguments.length); - return ownerMetaClass.invokeMethod(ownerClass, newOwner, methodName, newArguments, false, false); - } - } - - /** - * <p>Invokes a method on the given receiver for the specified arguments. The sender is the class that invoked the method on the object. - * The MetaClass will attempt to establish the method to invoke based on the name and arguments provided. - * - * <p>The isCallToSuper and fromInsideClass help the Groovy runtime perform optimisations on the call to go directly - * to the super class if necessary - * - * @param sender The java.lang.Class instance that invoked the method - * @param object The object which the method was invoked on - * @param methodName The name of the method - * @param originalArguments The arguments to the method - * @param isCallToSuper Whether the method is a call to a super class method - * @param fromInsideClass Whether the call was invoked from the inside or the outside of the class - * - * @return The return value of the method - * - * @see MetaClass#invokeMethod(Class, Object, String, Object[], boolean, boolean) - */ - public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) { - checkInitalised(); - if (object == null) { - throw new NullPointerException("Cannot invoke method: " + methodName + " on null object"); - } - - final Object[] arguments = originalArguments == null ? EMPTY_ARGUMENTS : originalArguments; -// final Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments); -// -// unwrap(arguments); - - MetaMethod method = null; - if (CLOSURE_CALL_METHOD.equals(methodName) && object instanceof GeneratedClosure) { - method = getMethodWithCaching(sender, "doCall", arguments, isCallToSuper); - } - if (method==null) { - method = getMethodWithCaching(sender, methodName, arguments, isCallToSuper); - } - MetaClassHelper.unwrap(arguments); - - if (method == null) - method = tryListParamMetaMethod(sender, methodName, isCallToSuper, arguments); - - final boolean isClosure = object instanceof Closure; - if (isClosure) { - final Closure closure = (Closure) object; - final Object owner = closure.getOwner(); - - if (CLOSURE_CALL_METHOD.equals(methodName) || CLOSURE_DO_CALL_METHOD.equals(methodName)) { - final Class objectClass = object.getClass(); - if (objectClass == MethodClosure.class) { - return this.invokeMethodClosure(object, methodName, arguments); - } else if (objectClass == CurriedClosure.class) { - final CurriedClosure cc = (CurriedClosure) object; - // change the arguments for an uncurried call - final Object[] curriedArguments = cc.getUncurriedArguments(arguments); - final Class ownerClass = owner instanceof Class ? (Class) owner : owner.getClass(); - final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass); - return ownerMetaClass.invokeMethod(owner, methodName, curriedArguments); - } - if (method==null) invokeMissingMethod(object,methodName,arguments); - } - - final Object delegate = closure.getDelegate(); - final boolean isClosureNotOwner = owner != closure; - final int resolveStrategy = closure.getResolveStrategy(); - - final Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments); - - switch (resolveStrategy) { - case Closure.TO_SELF: - method = closure.getMetaClass().pickMethod(methodName, argClasses); - if (method != null) return method.invoke(closure, arguments); - break; - case Closure.DELEGATE_ONLY: - if (method == null && delegate != closure && delegate != null) { - MetaClass delegateMetaClass = lookupObjectMetaClass(delegate); - method = delegateMetaClass.pickMethod(methodName, argClasses); - if (method != null) - return delegateMetaClass.invokeMethod(delegate, methodName, originalArguments); - else if (delegate != closure && (delegate instanceof GroovyObject)) { - return invokeMethodOnGroovyObject(methodName, originalArguments, delegate); - } - } - break; - case Closure.OWNER_ONLY: - if (method == null && owner != closure) { - MetaClass ownerMetaClass = lookupObjectMetaClass(owner); - return ownerMetaClass.invokeMethod(owner, methodName, originalArguments); - } - break; - case Closure.DELEGATE_FIRST: - if (method == null && delegate != closure && delegate != null) { - MetaClass delegateMetaClass = lookupObjectMetaClass(delegate); - method = delegateMetaClass.pickMethod(methodName, argClasses); - if (method != null) - return delegateMetaClass.invokeMethod(delegate, methodName, originalArguments); - } - if (method == null && owner != closure) { - MetaClass ownerMetaClass = lookupObjectMetaClass(owner); - method = ownerMetaClass.pickMethod(methodName, argClasses); - if (method != null) return ownerMetaClass.invokeMethod(owner, methodName, originalArguments); - } - if (method == null && resolveStrategy != Closure.TO_SELF) { - // still no methods found, test if delegate or owner are GroovyObjects - // and invoke the method on them if so. - MissingMethodException last = null; - if (delegate != closure && (delegate instanceof GroovyObject)) { - try { - return invokeMethodOnGroovyObject(methodName, originalArguments, delegate); - } catch (MissingMethodException mme) { - if (last == null) last = mme; - } - } - if (isClosureNotOwner && (owner instanceof GroovyObject)) { - try { - return invokeMethodOnGroovyObject(methodName, originalArguments, owner); - } catch (MissingMethodException mme) { - last = mme; - } - } - if (last != null) return invokeMissingMethod(object, methodName, originalArguments, last, isCallToSuper); - } - - break; - default: - if (method == null && owner != closure) { - MetaClass ownerMetaClass = lookupObjectMetaClass(owner); - method = ownerMetaClass.pickMethod(methodName, argClasses); - if (method != null) return ownerMetaClass.invokeMethod(owner, methodName, originalArguments); - } - if (method == null && delegate != closure && delegate != null) { - MetaClass delegateMetaClass = lookupObjectMetaClass(delegate); - method = delegateMetaClass.pickMethod(methodName, argClasses); - if (method != null) - return delegateMetaClass.invokeMethod(delegate, methodName, originalArguments); - } - if (method == null && resolveStrategy != Closure.TO_SELF) { - // still no methods found, test if delegate or owner are GroovyObjects - // and invoke the method on them if so. - MissingMethodException last = null; - if (isClosureNotOwner && (owner instanceof GroovyObject)) { - try { - return invokeMethodOnGroovyObject(methodName, originalArguments, owner); - } catch (MissingMethodException mme) { - if (methodName.equals(mme.getMethod())) { - if (last == null) last = mme; - } else { - throw mme; - } - } - catch (InvokerInvocationException iie) { - if (iie.getCause() instanceof MissingMethodException) { - MissingMethodException mme = (MissingMethodException) iie.getCause(); - if (methodName.equals(mme.getMethod())) { - if (last == null) last = mme; - } else { - throw iie; - } - } - else - throw iie; - } - } - if (delegate != closure && (delegate instanceof GroovyObject)) { - try { - return invokeMethodOnGroovyObject(methodName, originalArguments, delegate); - } catch (MissingMethodException mme) { - last = mme; - } - catch (InvokerInvocationException iie) { - if (iie.getCause() instanceof MissingMethodException) { - last = (MissingMethodException) iie.getCause(); - } - else - throw iie; - } - } - if (last != null) return invokeMissingMethod(object, methodName, originalArguments, last, isCallToSuper); - } - } - } - - if (method != null) { - return method.doMethodInvoke(object, arguments); - } else { - return invokePropertyOrMissing(object, methodName, originalArguments, fromInsideClass, isCallToSuper); - } - } - - private MetaMethod tryListParamMetaMethod(Class sender, String methodName, boolean isCallToSuper, Object[] arguments) { - MetaMethod method = null; - if (arguments.length == 1 && arguments[0] instanceof List) { - Object[] newArguments = ((List) arguments[0]).toArray(); - method = createTransformMetaMethod(getMethodWithCaching(sender, methodName, newArguments, isCallToSuper)); - } - return method; - } - - protected MetaMethod createTransformMetaMethod(MetaMethod method) { - if (method == null) { - return null; - } - - return new TransformMetaMethod(method) { - public Object invoke(Object object, Object[] arguments) { - Object firstArgument = arguments[0]; - List list = (List) firstArgument; - arguments = list.toArray(); - return super.invoke(object, arguments); - } - }; - } - - private Object invokePropertyOrMissing(Object object, String methodName, Object[] originalArguments, boolean fromInsideClass, boolean isCallToSuper) { - // if no method was found, try to find a closure defined as a field of the class and run it - Object value = null; - final MetaProperty metaProperty = this.getMetaProperty(methodName, false); - if (metaProperty != null) - value = metaProperty.getProperty(object); - else { - if (object instanceof Map) - value = ((Map)object).get(methodName); - } - - if (value instanceof Closure) { // This test ensures that value != this If you ever change this ensure that value != this - Closure closure = (Closure) value; - MetaClass delegateMetaClass = closure.getMetaClass(); - return delegateMetaClass.invokeMethod(closure.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, fromInsideClass); - } - - if (object instanceof Script) { - Object bindingVar = ((Script) object).getBinding().getVariables().get(methodName); - if (bindingVar != null) { - MetaClass bindingVarMC = ((MetaClassRegistryImpl) registry).getMetaClass(bindingVar); - return bindingVarMC.invokeMethod(bindingVar, CLOSURE_CALL_METHOD, originalArguments); - } - } - return invokeMissingMethod(object, methodName, originalArguments, null, isCallToSuper); - } - - private MetaClass lookupObjectMetaClass(Object object) { - if (object instanceof GroovyObject) { - GroovyObject go = (GroovyObject) object; - return go.getMetaClass(); - } - Class ownerClass = object.getClass(); - if (ownerClass == Class.class) ownerClass = (Class) object; - MetaClass metaClass = registry.getMetaClass(ownerClass); - return metaClass; - } - - private static Object invokeMethodOnGroovyObject(String methodName, Object[] originalArguments, Object owner) { - GroovyObject go = (GroovyObject) owner; - return go.invokeMethod(methodName, originalArguments); - } - - public MetaMethod getMethodWithCaching(Class sender, String methodName, Object[] arguments, boolean isCallToSuper) { - // let's try use the cache to find the method - if (!isCallToSuper && GroovyCategorySupport.hasCategoryInCurrentThread()) { - return getMethodWithoutCaching(sender, methodName, MetaClassHelper.convertToTypeArray(arguments), isCallToSuper); - } else { - final MetaMethodIndex.Entry e = metaMethodIndex.getMethods(sender, methodName); - if (e == null) - return null; - - return isCallToSuper ? getSuperMethodWithCaching(arguments, e) : getNormalMethodWithCaching(arguments, e); - } - } - - private static boolean sameClasses(Class[] params, Class[] arguments) { - // we do here a null check because the params field might not have been set yet - if (params == null) return false; - - if (params.length != arguments.length) - return false; - - for (int i = params.length - 1; i >= 0; i--) { - Object arg = arguments[i]; - if (arg != null) { - if (params[i] != arguments[i]) return false; - } else return false; - } - - return true; - } - - // This method should be called by CallSite only - private MetaMethod getMethodWithCachingInternal (Class sender, CallSite site, Class [] params) { - if (GroovyCategorySupport.hasCategoryInCurrentThread()) - return getMethodWithoutCaching(sender, site.getName (), params, false); - - final MetaMethodIndex.Entry e = metaMethodIndex.getMethods(sender, site.getName()); - if (e == null) { - return null; - } - - MetaMethodIndex.CacheEntry cacheEntry; - final Object methods = e.methods; - if (methods == null) - return null; - - cacheEntry = e.cachedMethod; - if (cacheEntry != null && (sameClasses(cacheEntry.params, params))) { - return cacheEntry.method; - } - - cacheEntry = new MetaMethodIndex.CacheEntry (params, (MetaMethod) chooseMethod(e.name, methods, params)); - e.cachedMethod = cacheEntry; - return cacheEntry.method; - } - - private MetaMethod getSuperMethodWithCaching(Object[] arguments, MetaMethodIndex.Entry e) { - MetaMethodIndex.CacheEntry cacheEntry; - if (e.methodsForSuper == null) - return null; - - cacheEntry = e.cachedMethodForSuper; - - if (cacheEntry != null && - MetaClassHelper.sameClasses(cacheEntry.params, arguments, e.methodsForSuper instanceof MetaMethod)) - { - MetaMethod method = cacheEntry.method; - if (method!=null) return method; - } - - final Class[] classes = MetaClassHelper.convertToTypeArray(arguments); - MetaMethod method = (MetaMethod) chooseMethod(e.name, e.methodsForSuper, classes); - cacheEntry = new MetaMethodIndex.CacheEntry (classes, method.isAbstract()?null:method); - - e.cachedMethodForSuper = cacheEntry; - - return cacheEntry.method; - } - - private MetaMethod getNormalMethodWithCaching(Object[] arguments, MetaMethodIndex.Entry e) { - MetaMethodIndex.CacheEntry cacheEntry; - final Object methods = e.methods; - if (methods == null) - return null; - - cacheEntry = e.cachedMethod; - - if (cacheEntry != null && - MetaClassHelper.sameClasses(cacheEntry.params, arguments, methods instanceof MetaMethod)) - { - MetaMethod method = cacheEntry.method; - if (method!=null) return method; - } - - final Class[] classes = MetaClassHelper.convertToTypeArray(arguments); - cacheEntry = new MetaMethodIndex.CacheEntry (classes, (MetaMethod) chooseMethod(e.name, methods, classes)); - - e.cachedMethod = cacheEntry; - - return cacheEntry.method; - } - - public Constructor retrieveConstructor(Class[] arguments) { - CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, arguments); - if (constructor != null) { - return constructor.cachedConstructor; - } - constructor = (CachedConstructor) chooseMethod("<init>", constructors, arguments); - if (constructor != null) { - return constructor.cachedConstructor; - } - return null; - } - - public MetaMethod retrieveStaticMethod(String methodName, Object[] arguments) { - final MetaMethodIndex.Entry e = metaMethodIndex.getMethods(theClass, methodName); - MetaMethodIndex.CacheEntry cacheEntry; - if (e != null) { - cacheEntry = e.cachedStaticMethod; - - if (cacheEntry != null && - MetaClassHelper.sameClasses(cacheEntry.params, arguments, e.staticMethods instanceof MetaMethod)) - { - return cacheEntry.method; - } - - final Class[] classes = MetaClassHelper.convertToTypeArray(arguments); - cacheEntry = new MetaMethodIndex.CacheEntry (classes, pickStaticMethod(methodName, classes)); - - e.cachedStaticMethod = cacheEntry; - - return cacheEntry.method; - } - else - return pickStaticMethod(methodName, MetaClassHelper.convertToTypeArray(arguments)); - } - - public MetaMethod getMethodWithoutCaching(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) { - MetaMethod method = null; - Object methods = getMethods(sender, methodName, isCallToSuper); - if (methods != null) { - method = (MetaMethod) chooseMethod(methodName, methods, arguments); - } - return method; - } - - public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) { - checkInitalised(); - - final Class sender = object instanceof Class ? (Class) object : object.getClass(); - if (sender != theClass) { - MetaClass mc = registry.getMetaClass(sender); - return mc.invokeStaticMethod(sender, methodName, arguments); - } - if (sender == Class.class) { - return invokeMethod(object, methodName, arguments); - } - - if (arguments == null) arguments = EMPTY_ARGUMENTS; -// Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments); - - MetaMethod method = retrieveStaticMethod(methodName, arguments); - // let's try use the cache to find the method - - if (method != null) { - MetaClassHelper.unwrap(arguments); - return method.doMethodInvoke(object, arguments); - } - Object prop = null; - try { - prop = getProperty(theClass, theClass, methodName, false, false); - } catch (MissingPropertyException mpe) { - // ignore - } - - if (prop instanceof Closure) { - return invokeStaticClosureProperty(arguments, prop); - } - - Object[] originalArguments = arguments.clone(); - MetaClassHelper.unwrap(arguments); - - Class superClass = sender.getSuperclass(); - Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments); - while (superClass != Object.class && superClass != null) { - MetaClass mc = registry.getMetaClass(superClass); - method = mc.getStaticMetaMethod(methodName, argClasses); - if (method != null) return method.doMethodInvoke(object, arguments); - - try { - prop = mc.getProperty(superClass, superClass, methodName, false, false); - } catch (MissingPropertyException mpe) { - // ignore - } - - if (prop instanceof Closure) { - return invokeStaticClosureProperty(originalArguments, prop); - } - - superClass = superClass.getSuperclass(); - } - - if (prop != null) { - MetaClass propMC = registry.getMetaClass(prop.getClass()); - return propMC.invokeMethod(prop, CLOSURE_CALL_METHOD, arguments); - } - - return invokeStaticMissingMethod(sender, methodName, arguments); - } - - private static Object invokeStaticClosureProperty(Object[] originalArguments, Object prop) { - Closure closure = (Closure) prop; - MetaClass delegateMetaClass = closure.getMetaClass(); - return delegateMetaClass.invokeMethod(closure.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, false); - } - - private Object invokeStaticMissingMethod(Class sender, String methodName, Object[] arguments) { - MetaMethod metaMethod = getStaticMetaMethod(STATIC_METHOD_MISSING, METHOD_MISSING_ARGS); - if (metaMethod != null) { - return metaMethod.invoke(sender, new Object[]{methodName, arguments}); - } - throw new MissingMethodException(methodName, sender, arguments, true); - } - - private MetaMethod pickStaticMethod(String methodName, Class[] arguments) { - MetaMethod method = null; - MethodSelectionException mse = null; - Object methods = getStaticMethods(theClass, methodName); - - if (!(methods instanceof FastArray) || !((FastArray)methods).isEmpty()) { - try { - method = (MetaMethod) chooseMethod(methodName, methods, arguments); - } catch(MethodSelectionException msex) { - mse = msex; - } - } - if (method == null && theClass != Class.class) { - MetaClass classMetaClass = registry.getMetaClass(Class.class); - method = classMetaClass.pickMethod(methodName, arguments); - } - if (method == null) { - method = (MetaMethod) chooseMethod(methodName, methods, MetaClassHelper.convertToTypeArray(arguments)); - } - - if (method == null && mse != null) { - throw mse; - } else { - return method; - } - } - - public Object invokeConstructor(Object[] arguments) { - return invokeConstructor(theClass, arguments); - } - - public int selectConstructorAndTransformArguments(int numberOfConstructors, Object[] arguments) { - if (numberOfConstructors==-1) { - return selectConstructorAndTransformArguments1(arguments); - } else { - // falling back to pre 2.1.9 selection algorithm - // in practice this branch will only be reached if the class calling this code is a Groovy class - // compiled with an earlier version of the Groovy compiler - return selectConstructorAndTransformArguments0(numberOfConstructors, arguments); - } - - - } - - private int selectConstructorAndTransformArguments0(final int numberOfConstructors, Object[] arguments) { - //TODO: that is just a quick prototype, not the real thing! - if (numberOfConstructors != constructors.size()) { - throw new IncompatibleClassChangeError("the number of constructors during runtime and compile time for " + - this.theClass.getName() + " do not match. Expected " + numberOfConstructors + " but got " + constructors.size()); - } - - CachedConstructor constructor = createCachedConstructor(arguments); - List l = new ArrayList(constructors.toList()); - Comparator comp = new Comparator() { - public int compare(Object arg0, Object arg1) { - CachedConstructor c0 = (CachedConstructor) arg0; - CachedConstructor c1 = (CachedConstructor) arg1; - String descriptor0 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c0.getNativeParameterTypes()); - String descriptor1 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c1.getNativeParameterTypes()); - return descriptor0.compareTo(descriptor1); - } - }; - Collections.sort(l, comp); - int found = -1; - for (int i = 0; i < l.size(); i++) { - if (l.get(i) != constructor) continue; - found = i; - break; - } - // NOTE: must be changed to "1 |" if constructor was vargs - return 0 | (found << 8); - } - - private CachedConstructor createCachedConstructor(Object[] arguments) { - if (arguments == null) arguments = EMPTY_ARGUMENTS; - Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments); - MetaClassHelper.unwrap(arguments); - CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses); - if (constructor == null) { - constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses); - } - if (constructor == null) { - throw new GroovyRuntimeException( - "Could not find matching constructor for: " - + theClass.getName() - + "(" + InvokerHelper.toTypeString(arguments) + ")"); - } - return constructor; - } - - /** - * Constructor selection algorithm for Groovy 2.1.9+. - * This selection algorithm was introduced as a workaround for GROOVY-6080. Instead of generating an index between - * 0 and N where N is the number of super constructors at the time the class is compiled, this algorithm uses - * a hash of the constructor descriptor instead. - * - * This has the advantage of letting the super class add new constructors while being binary compatible. But there - * are still problems with this approach: - * <ul> - * <li>There's a risk of hash collision, even if it's very low (two constructors of the same class must have the same hash)</li> - * <li>If the super class adds a new constructor which takes as an argument a superclass of an existing constructor parameter and - * that this new constructor is selected at runtime, it would not find it.</li> - * </ul> - * - * Hopefully in the last case, the error message is much nicer now since it explains that it's a binary incompatible change. - * - * @param arguments the actual constructor call arguments - * @return a hash used to identify the constructor to be called - * @since 2.1.9 - */ - private int selectConstructorAndTransformArguments1(Object[] arguments) { - CachedConstructor constructor = createCachedConstructor(arguments); - final String methodDescriptor = BytecodeHelper.getMethodDescriptor(Void.TYPE, constructor.getNativeParameterTypes()); - // keeping 3 bits for additional information such as vargs - return BytecodeHelper.hashCode(methodDescriptor); - } - - - /** - * checks if the initialisation of the class id complete. - * This method should be called as a form of assert, it is no - * way to test if there is still initialisation work to be done. - * Such logic must be implemented in a different way. - * - * @throws IllegalStateException if the initialisation is incomplete yet - */ - protected void checkInitalised() { - if (!isInitialized()) - throw new IllegalStateException( - "initialize must be called for meta " + - "class of " + theClass + - "(" + this.getClass() + ") " + - "to complete initialisation process " + - "before any invocation or field/property " + - "access can be done"); - } - - /** - * This is a helper class introduced in Groovy 2.1.0, which is used only by - * indy. This class is for internal use only. - * @since Groovy 2.1.0 - */ - public static final class MetaConstructor extends MetaMethod { - private final CachedConstructor cc; - private final boolean beanConstructor; - private MetaConstructor(CachedConstructor cc, boolean bean) { - super(cc.getNativeParameterTypes()); - this.setParametersTypes(cc.getParameterTypes()); - this.cc = cc; - this.beanConstructor = bean; - } - @Override - public int getModifiers() { return cc.getModifiers(); } - @Override - public String getName() { return "<init>"; } - @Override - public Class getReturnType() { return cc.getCachedClass().getTheClass(); } - @Override - public CachedClass getDeclaringClass() { return cc.getCachedClass(); } - @Override - public Object invoke(Object object, Object[] arguments) { - return cc.doConstructorInvoke(arguments); - } - public CachedConstructor getCachedConstrcutor() { return cc; } - public boolean isBeanConstructor() { return beanConstructor; } - } - - /** - * This is a helper method added in Groovy 2.1.0, which is used only by indy. - * This method is for internal use only. - * @since Groovy 2.1.0 - */ - public MetaMethod retrieveConstructor(Object[] arguments) { - checkInitalised(); - if (arguments == null) arguments = EMPTY_ARGUMENTS; - Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments); - MetaClassHelper.unwrap(arguments); - Object res = chooseMethod("<init>", constructors, argClasses); - if (res instanceof MetaMethod) return (MetaMethod) res; - CachedConstructor constructor = (CachedConstructor) res; - if (constructor != null) return new MetaConstructor(constructor, false); - if (arguments.length == 1 && arguments[0] instanceof Map) { - res = chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY); - } else if ( - arguments.length == 2 && arguments[1] instanceof Map && - theClass.getEnclosingClass()!=null && - theClass.getEnclosingClass().isAssignableFrom(argClasses[0])) - { - res = chooseMethod("<init>", constructors, new Class[]{argClasses[0]}); - } - if (res instanceof MetaMethod) return (MetaMethod) res; - constructor = (CachedConstructor) res; - if (constructor != null) return new MetaConstructor(constructor, true); - - return null; - } - - private Object invokeConstructor(Class at, Object[] arguments) { - checkInitalised(); - if (arguments == null) arguments = EMPTY_ARGUMENTS; - Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments); - MetaClassHelper.unwrap(arguments); - CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses); - if (constructor != null) { - return constructor.doConstructorInvoke(arguments); - } - - if (arguments.length == 1) { - Object firstArgument = arguments[0]; - if (firstArgument instanceof Map) { - constructor = (CachedConstructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY); - if (constructor != null) { - Object bean = constructor.doConstructorInvoke(MetaClassHelper.EMPTY_ARRAY); - setProperties(bean, ((Map) firstArgument)); - return bean; - } - } - } - throw new GroovyRuntimeException( - "Could not find matching constructor for: " - + theClass.getName() - + "(" + InvokerHelper.toTypeString(arguments) + ")"); - } - - /** - * Sets a number of bean properties from the given Map where the keys are - * the String names of properties and the values are the values of the - * properties to set - */ - public void setProperties(Object bean, Map map) { - checkInitalised(); - for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) { - Map.Entry entry = (Map.Entry) iter.next(); - String key = entry.getKey().toString(); - - Object value = entry.getValue(); - setProperty(bean, key, value); - } - } - - /** - * @return the given property's value on the object - */ - public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) { - - //---------------------------------------------------------------------- - // handling of static - //---------------------------------------------------------------------- - boolean isStatic = theClass != Class.class && object instanceof Class; - if (isStatic && object != theClass) { - MetaClass mc = registry.getMetaClass((Class) object); - return mc.getProperty(sender, object, name, useSuper, false); - } - - checkInitalised(); - - //---------------------------------------------------------------------- - // turn getProperty on a Map to get on the Map itself - //---------------------------------------------------------------------- - if (!isStatic && this.isMap) { - return ((Map) object).get(name); - } - - Tuple2<MetaMethod, MetaProperty> methodAndProperty = createMetaMethodAndMetaProperty(sender, sender, name, useSuper, isStatic); - MetaMethod method = methodAndProperty.getFirst(); - - //---------------------------------------------------------------------- - // getter - //---------------------------------------------------------------------- - MetaProperty mp = methodAndProperty.getSecond(); - - //---------------------------------------------------------------------- - // field - //---------------------------------------------------------------------- - if (method == null && mp != null) { - try { - return mp.getProperty(object); - } catch (IllegalArgumentException e) { - // can't access the field directly but there may be a getter - mp = null; - } catch (CacheAccessControlException e) { - // can't access the field directly but there may be a getter - mp = null; - } - } - - //---------------------------------------------------------------------- - // generic get method - //---------------------------------------------------------------------- - // check for a generic get method provided through a category - Object[] arguments = EMPTY_ARGUMENTS; - if (method == null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInCurrentThread()) { - method = getCategoryMethodGetter(sender, "get", true); - if (method != null) arguments = new Object[]{name}; - } - - // the generic method is valid, if available (!=null), if static or - // if it is not static and we do no static access - if (method == null && genericGetMethod != null && !(!genericGetMethod.isStatic() && isStatic)) { - arguments = new Object[]{name}; - method = genericGetMethod; - } - - //---------------------------------------------------------------------- - // special cases - //---------------------------------------------------------------------- - if (method == null) { - /** todo these special cases should be special MetaClasses maybe */ - if (theClass != Class.class && object instanceof Class) { - MetaClass mc = registry.getMetaClass(Class.class); - return mc.getProperty(Class.class, object, name, useSuper, false); - } else if (object instanceof Collection) { - return DefaultGroovyMethods.getAt((Collection) object, name); - } else if (object instanceof Object[]) { - return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), name); - } else { - MetaMethod addListenerMethod = listeners.get(name); - if (addListenerMethod != null) { - //TODO: one day we could try return the previously registered Closure listener for easy removal - return null; - } - } - } else { - - //---------------------------------------------------------------------- - // executing the getter method - //---------------------------------------------------------------------- - return method.doMethodInvoke(object, arguments); - } - - //---------------------------------------------------------------------- - // error due to missing method/field - //---------------------------------------------------------------------- - if (isStatic || object instanceof Class) - return invokeStaticMissingProperty(object, name, null, true); - else - return invokeMissingProperty(object, name, null, true); - } - - public MetaProperty getEffectiveGetMetaProperty(final Class sender, final Object object, String name, final boolean useSuper) { - - //---------------------------------------------------------------------- - // handling of static - //---------------------------------------------------------------------- - boolean isStatic = theClass != Class.class && object instanceof Class; - if (isStatic && object != theClass) { - return new MetaProperty(name, Object.class) { - final MetaClass mc = registry.getMetaClass((Class) object); - - public Object getProperty(Object object) { - return mc.getProperty(sender, object, name, useSuper,false); - } - - publ
<TRUNCATED>
