Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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.apache.groovy.util.BeanUtils;
import org.apache.groovy.util.SystemUtil;
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.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.FormatHelper;
import org.codehaus.groovy.runtime.GeneratedClosure;
import org.codehaus.groovy.runtime.GroovyCategorySupport;
import org.codehaus.groovy.runtime.GroovyCategorySupport.CategoryMethod;
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.codehaus.groovy.vmplugin.VMPlugin;
import org.codehaus.groovy.vmplugin.VMPluginFactory;
import org.objectweb.asm.Opcodes;
import java.beans.BeanInfo;
import java.beans.EventSetDescriptor;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
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.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;
import static groovy.lang.Tuple.tuple;
import static java.lang.Character.isUpperCase;
import static org.apache.groovy.util.Arrays.concat;
import static org.codehaus.groovy.ast.tools.GeneralUtils.inSamePackage;
import static org.codehaus.groovy.reflection.ReflectionUtils.checkAccessible;
/**
* 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 = MetaClassHelper.EMPTY_ARRAY;
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 CALL_METHOD = "call";
private static final String DO_CALL_METHOD = "doCall";
private static final String CONSTRUCTOR_NAME = "";
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 MetaMethod AMBIGUOUS_LISTENER_METHOD = new DummyMetaMethod();
private static final Comparator CACHED_CLASS_NAME_COMPARATOR = Comparator.comparing(CachedClass::getName);
private static final boolean PERMISSIVE_PROPERTY_ACCESS = SystemUtil.getBooleanSafe("groovy.permissive.property.access");
private static final VMPlugin VM_PLUGIN = VMPluginFactory.getPlugin();
protected final Class theClass;
protected final CachedClass theCachedClass;
protected final boolean isGroovyObject;
protected final boolean isMap;
protected final MetaMethodIndex metaMethodIndex;
private final Map> classPropertyIndex = new LinkedHashMap<>();
private final Map staticPropertyIndex = new LinkedHashMap<>();
private final Map listeners = new LinkedHashMap<>();
private final List allMethods = new ArrayList<>();
// we only need one of these that can be reused over and over.
private final MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
private final Map> classPropertyIndexForSuper = new LinkedHashMap<>();
private final Set newGroovyMethodsSet = new LinkedHashSet<>();
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 Map mainClassMethodHeader;
private boolean permissivePropertyAccess = PERMISSIVE_PROPERTY_ACCESS;
/**
* Constructor
*
* @param theClass The class this is the metaclass for
* @param add The methods for this class
*/
public MetaClassImpl(final Class theClass, final 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) {
myNewMetaMethods = concat(metaMethods, add);
additionalMetaMethods = metaMethods;
} else {
myNewMetaMethods = metaMethods;
additionalMetaMethods = MetaMethod.EMPTY_ARRAY;
}
}
/**
* Constructor that sets the methods to null
*
* @param theClass The class this is the metaclass for
*/
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(final MetaClassRegistry registry, final Class theClass, final 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 registry
*/
public MetaClassRegistry getRegistry() {
return registry;
}
/**
* @see MetaObjectProtocol#respondsTo(Object, String, Object[])
*/
@Override
public List respondsTo(final Object obj, final String name, final Object[] argTypes) {
MetaMethod m = getMetaMethod(name, MetaClassHelper.castArgumentsToClassArray(argTypes));
return (m != null ? Collections.singletonList(m) : Collections.emptyList());
}
/**
* @see MetaObjectProtocol#respondsTo(Object, String)
*/
@Override
public List respondsTo(final Object obj, final String name) {
final Object o = getMethods(getTheClass(), name, false);
if (o instanceof FastArray) {
return ((FastArray) o).toList();
}
return Collections.singletonList((MetaMethod) o);
}
/**
* @see MetaObjectProtocol#hasProperty(Object, String)
*/
@Override
public MetaProperty hasProperty(final Object obj, final String name) {
return getMetaProperty(name);
}
/**
* @see MetaObjectProtocol#getMetaProperty(String)
*/
@Override
public MetaProperty getMetaProperty(final String name) {
MetaProperty metaProperty = null;
LinkedHashMap propertyMap = classPropertyIndex.computeIfAbsent(theCachedClass, k -> new LinkedHashMap<>());
metaProperty = propertyMap.get(name);
if (metaProperty == null) {
metaProperty = staticPropertyIndex.get(name);
if (metaProperty == null) {
propertyMap = classPropertyIndexForSuper.computeIfAbsent(theCachedClass, k -> new LinkedHashMap<>());
metaProperty = propertyMap.get(name);
if (metaProperty == null) {
MetaBeanProperty property = findPropertyInClassHierarchy(name, theCachedClass);
if (property != null) {
onSuperPropertyFoundInHierarchy(property);
metaProperty = property;
}
}
}
}
return metaProperty;
}
/**
* @see MetaObjectProtocol#getStaticMetaMethod(String, Object[])
*/
@Override
public MetaMethod getStaticMetaMethod(final String name, final Object[] argTypes) {
return pickStaticMethod(name, MetaClassHelper.castArgumentsToClassArray(argTypes));
}
/**
* @see MetaObjectProtocol#getMetaMethod(String, Object[])
*/
@Override
public MetaMethod getMetaMethod(final String name, final Object[] argTypes) {
return pickMethod(name, MetaClassHelper.castArgumentsToClassArray(argTypes));
}
/**
* Returns the class this object this is the metaclass of.
*
* @return The class contained by this metaclass
*/
@Override
public Class getTheClass() {
return this.theClass;
}
/**
* Return whether 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;
}
private void fillMethodIndex() {
mainClassMethodHeader = metaMethodIndex.getHeader(theClass);
Set interfaces = theCachedClass.getInterfaces();
List superClasses = getSuperClasses(); // in reverse order
CachedClass firstGroovySuper = calcFirstGroovySuperClass(superClasses);
for (CachedClass c : interfaces) {
for (CachedMethod m : c.getMethods()) {
if (c == theCachedClass || (m.isPublic() && !m.isStatic())) { // GROOVY-8164
addMetaMethodToIndex(m, mainClassMethodHeader);
}
}
}
populateMethods(superClasses, firstGroovySuper);
inheritInterfaceNewMetaMethods(interfaces);
if (isGroovyObject) {
metaMethodIndex.copyMethodsToSuper(); // methods --> methodsForSuper
connectMultimethods(superClasses, firstGroovySuper);
removeMultimethodsOverloadedWithPrivateMethods();
replaceWithMOPCalls(theCachedClass.mopMethods);
}
}
private void populateMethods(final List superClasses, final CachedClass firstGroovySuper) {
var header = metaMethodIndex.getHeader(firstGroovySuper.getTheClass());
CachedClass c;
Iterator iter = superClasses.iterator();
while (iter.hasNext()) {
c = iter.next();
for (var metaMethod : c.getMethods()) {
addToAllMethodsIfPublic(metaMethod);
if (c == firstGroovySuper || (metaMethod.isPublic() || metaMethod.isProtected() ||
(!metaMethod.isPrivate() && inSamePackage(metaMethod.getDeclaringClass().getTheClass(), theClass)))) { // GROOVY-11357
addMetaMethodToIndex(metaMethod, header);
}
}
for (var metaMethod : getNewMetaMethods(c)) {
if (newGroovyMethodsSet.add(metaMethod)) {
addMetaMethodToIndex(metaMethod, header);
}
}
if (c == firstGroovySuper)
break;
}
var last = header;
while (iter.hasNext()) {
c = iter.next();
header = metaMethodIndex.getHeader(c.getTheClass());
if (last != null) {
metaMethodIndex.copyNonPrivateMethods(last, header);
}
last = header;
for (var metaMethod : c.getMethods()) {
addToAllMethodsIfPublic(metaMethod);
addMetaMethodToIndex(metaMethod, header);
}
for (var metaMethod : getNewMetaMethods(c)) {
if (metaMethod.getName().equals(CONSTRUCTOR_NAME)
&& !metaMethod.getDeclaringClass().equals(theCachedClass)) continue;
if (newGroovyMethodsSet.add(metaMethod)) {
addMetaMethodToIndex(metaMethod, header);
}
}
}
}
private MetaMethod[] getNewMetaMethods(final CachedClass c) {
if (theCachedClass != c)
return c.getNewMetaMethods();
return myNewMetaMethods;
}
protected LinkedList getSuperClasses() {
LinkedList superClasses = new LinkedList<>();
if (theClass.isInterface()) {
superClasses.add(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() {
@Override
public boolean skipClass(final Class> clazz) {
return clazz == theClass;
}
@Override
public void methodNameAction(final Class> clazz, final MetaMethodIndex.Cache 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) {
class MOPIter extends MethodIndexAction {
boolean useThis;
@Override
public void methodNameAction(final Class> c, final MetaMethodIndex.Cache e) {
Object arrayOrMethod = (useThis ? e.methods : e.methodsForSuper);
if (arrayOrMethod instanceof FastArray) {
FastArray methods = (FastArray) arrayOrMethod;
Object[] data = methods.getArray();
for (int i = 0; i < methods.size(); i += 1) {
MetaMethod method = (MetaMethod) data[i];
int matchedMethod = mopArrayIndex(method, c);
if (matchedMethod >= 0) {
methods.set(i, mopMethods[matchedMethod]);
} else if (!useThis && !isDGM(method) && (isBridge(method)
|| c == method.getDeclaringClass().getTheClass())) {
methods.remove(i--); // not fit for super usage
}
}
if (!useThis) {
int n = methods.size();
if (n == 0) e.methodsForSuper = null;
else if (n == 1) e.methodsForSuper = data[0];
}
} else if (arrayOrMethod != null) {
MetaMethod method = (MetaMethod) arrayOrMethod;
int matchedMethod = mopArrayIndex(method, c);
if (matchedMethod >= 0) {
if (useThis) e.methods = mopMethods[matchedMethod];
else e.methodsForSuper = mopMethods[matchedMethod];
} else if (!useThis && !isDGM(method) && (isBridge(method)
|| c == method.getDeclaringClass().getTheClass())) {
e.methodsForSuper = null; // not fit for super usage
}
}
}
private int mopArrayIndex(final MetaMethod method, final Class> c) {
if (mopMethods == null || mopMethods.length == 0) return -1;
if (isDGM(method) || (useThis ^ method.isPrivate())) return -1;
if (useThis) return mopArrayIndex(method, method.getMopName());
// GROOVY-4922: Due to a numbering scheme change, find the super$number$methodName with
// the highest value. If we don't, no method may be found, leading to a stack overflow!
int distance = ReflectionCache.getCachedClass(c).getSuperClassDistance() - 1;
if (isBridge(method)) // GROOVY-6663
return mopArrayIndex(method, "super$" + distance + "$" + method.getName());
while (distance > 0) {
int index = mopArrayIndex(method, "super$" + distance + "$" + method.getName());
if (index >= 0) return index;
distance -= 1;
}
return -1;
}
private int mopArrayIndex(final MetaMethod method, final String mopName) {
int index = Arrays.binarySearch(mopMethods, mopName, CachedClass.CachedMethodComparatorWithString.INSTANCE);
if (index >= 0) {
int from = index, to = index; // include overloads in search
while (from > 0 && mopMethods[from - 1].getName().equals(mopName)) from -= 1;
while (to < mopMethods.length - 1 && mopMethods[to + 1].getName().equals(mopName)) to += 1;
for (index = from; index <= to; index += 1) {
CachedClass[] params1 = mopMethods[index].getParameterTypes();
CachedClass[] params2 = method.getParameterTypes();
if (MetaMethod.equal(params1, params2)) {
return index;
}
}
}
return -1;
}
private boolean isBridge(final MetaMethod method) {
return (method.getModifiers() & Opcodes.ACC_BRIDGE) != 0;
}
private boolean isDGM(final MetaMethod method) {
return method instanceof GeneratedMetaMethod || method instanceof NewMetaMethod;
}
}
MOPIter iter = new MOPIter();
// replace all calls for super with the correct MOP method
iter.useThis = false;
iter.iterate();
if (mopMethods == null || mopMethods.length == 0) return;
// replace all calls for this with the correct MOP method
iter.useThis = true;
iter.iterate();
}
private void inheritInterfaceNewMetaMethods(final Set interfaces) {
Method[] theClassMethods = null;
// add methods declared by DGM for interfaces
for (CachedClass face : interfaces) {
for (MetaMethod method : getNewMetaMethods(face)) {
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 && !GroovyObject.class.isAssignableFrom(method.getDeclaringClass().getTheClass())) {
final String generatedMethodName = method.getName();
final CachedClass[] generatedMethodParameterTypes = method.getParameterTypes();
for (Method m : (null == theClassMethods ? theClassMethods = theClass.getMethods() : theClassMethods)) {
if (generatedMethodName.equals(m.getName())
// below not true for DGM#push and also co-variant return scenarios
//&& method.getReturnType().equals(m.getReturnType())
&& MetaMethod.equal(generatedMethodParameterTypes, m.getParameterTypes())) {
skip = true;
break;
}
}
}
if (!skip) {
newGroovyMethodsSet.add(method);
addMetaMethodToIndex(method, mainClassMethodHeader);
}
}
}
}
private void connectMultimethods(final List superClasses, final CachedClass firstGroovyClass) {
Map last = null;
for (ListIterator iter = superClasses.listIterator(superClasses.size()); iter.hasPrevious(); ) {
CachedClass c = iter.previous();
var 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(final List superClasses) {
if (theCachedClass.isInterface)
return ReflectionCache.OBJECT_CLASS;
CachedClass firstGroovy = null;
Iterator iter = superClasses.iterator();
while (iter.hasNext()) {
CachedClass c = iter.next();
if (GroovyObject.class.isAssignableFrom(c.getTheClass())) {
firstGroovy = c;
break;
}
}
if (firstGroovy == null) {
return isGroovyObject ? theCachedClass.getCachedSuperClass() : theCachedClass;
}
// Closure for closures and GroovyObjectSupport for extenders (including Closure)
if (firstGroovy.getTheClass() == GroovyObjectSupport.class) {
if (iter.hasNext()) { var nextGroovy = iter.next() ;
if (nextGroovy.getTheClass() == Closure.class) {
if (iter.hasNext()) return nextGroovy;
}
return firstGroovy; // GroovyObjectSupport
}
}
return firstGroovy.getCachedSuperClass();
}
/**
* Gets all the normal object methods of this class for the given name.
*
* @return object methods available from this class for given name
*/
private Object getMethods(final Class> sender, final String name, final boolean isCallToSuper) {
Object answer;
final MetaMethodIndex.Cache 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 (CategoryMethod cm : used) {
if (!cm.getDeclaringClass().getTheClass().isAssignableFrom(sender))
continue;
filterMatchingMethodForCategory(arr, cm);
}
answer = arr;
}
}
return answer;
}
/**
* Gets all the normal static methods of this class for the given name.
*
* @return static methods available from this class for given name
*/
private Object getStaticMethods(final Class> sender, final String name) {
final MetaMethodIndex.Cache 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
*/
@Override
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
*/
@Override
public void addNewInstanceMethod(final Method method) {
CachedMethod cachedMethod = CachedMethod.find(method);
NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(cachedMethod);
addNewInstanceMethodToIndex(newMethod, metaMethodIndex.getHeader(newMethod.getDeclaringClass().getTheClass()));
}
private void addNewInstanceMethodToIndex(final MetaMethod newMethod, final Map 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
*/
@Override
public void addNewStaticMethod(final Method method) {
CachedMethod cachedMethod = CachedMethod.find(method);
NewStaticMetaMethod newMethod = new NewStaticMetaMethod(cachedMethod);
addNewStaticMethodToIndex(newMethod, metaMethodIndex.getHeader(newMethod.getDeclaringClass().getTheClass()));
}
private void addNewStaticMethodToIndex(final MetaMethod newMethod, final Map 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.
*/
@Override
public Object invokeMethod(final Object object, final String methodName, final Object arguments) {
if (arguments == null) {
return invokeMethod(object, methodName, EMPTY_ARGUMENTS);
}
if (arguments instanceof Tuple) {
return invokeMethod(object, methodName, ((Tuple>) arguments).toArray());
}
if (arguments instanceof Object[]) {
return invokeMethod(object, methodName, (Object[]) arguments);
}
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.
*/
@Override
public Object invokeMissingMethod(final Object instance, final String methodName, final 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 Whether the method is a getter
* @return The result of the method invocation.
*/
@Override
public Object invokeMissingProperty(final Object instance, final String propertyName, final Object optionalValue, final boolean isGetter) {
MetaBeanProperty property = findPropertyInClassHierarchy(propertyName, theCachedClass);
if (property != null) {
onSuperPropertyFoundInHierarchy(property);
if (!isGetter) {
property.setProperty(instance, optionalValue);
return null;
}
return property.getProperty(instance);
}
// 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 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 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;
}
Class> theClass = instance instanceof Class ? (Class>) instance : instance.getClass();
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);
}
metaProperty.setProperty(instance, optionalValue);
return null;
}
}
throw new MissingPropertyExceptionNoStack(propertyName, theClass);
}
private Object invokeMissingMethod(final Object instance, final String methodName, final Object[] arguments, final RuntimeException original, final boolean isCallToSuper) {
if (isCallToSuper) {
MetaClass metaClass = InvokerHelper.getMetaClass(theClass.getSuperclass());
return metaClass.invokeMissingMethod(instance, methodName, arguments);
}
Class> instanceKlazz = instance.getClass();
if (theClass != instanceKlazz && theClass.isAssignableFrom(instanceKlazz))
instanceKlazz = theClass;
Class>[] argClasses = MetaClassHelper.convertToTypeArray(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 instanceof ClosureMetaMethod) {
onInvokeMethodFoundInHierarchy(method);
return method.invoke(instance, invokeMethodArgs);
}
// last resort look in the category
if (method == null && GroovyCategorySupport.hasCategoryInCurrentThread()) {
method = getCategoryMethodMissing(instanceKlazz);
if (method != null) {
return method.invoke(instance, new Object[]{methodName, arguments});
}
}
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 it's 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 arguments The arguments to the method
* @return The return value of the method
* @see MetaClass#invokeMethod(Class, Object, String, Object[], boolean, boolean)
*/
@Override
public Object invokeMethod(Object object, String methodName, Object[] arguments) {
return invokeMethod(theClass, object, methodName, arguments, false, false);
}
private Object invokeMethodClosure(final MethodClosure closure, final Object[] arguments) {
var owner = closure.getOwner();
var method = closure.getMethod();
var ownerClass = closure.getOwnerClass();
var ownerIsClass = (owner instanceof Class);
var ownerMetaClass = registry.getMetaClass(ownerClass);
try {
return ownerMetaClass.invokeMethod(ownerClass, owner, method, arguments, false, false);
} catch (GroovyRuntimeException e) { // GroovyRuntimeException(cause:IllegalArgumentException) thrown for final fields
// InvokerInvocationException(cause:IllegalArgumentException) thrown for not this
if (!ownerIsClass || !(e instanceof MissingMethodException || e.getCause() instanceof IllegalArgumentException)) {
throw e;
}
if (MethodClosure.NEW.equals(method)) {
// CONSTRUCTOR REFERENCE
if (!ownerClass.isArray())
return ownerMetaClass.invokeConstructor(arguments);
int nArguments = arguments.length;
if (nArguments == 0) {
throw new GroovyRuntimeException("The arguments(specifying size) are required to create array[" + ownerClass.getCanonicalName() + "]");
}
int arrayDimension = ArrayTypeUtils.dimension(ownerClass);
if (arrayDimension < nArguments) {
throw new GroovyRuntimeException("The length[" + nArguments + "] of arguments should not be greater than the dimensions[" + arrayDimension + "] of array[" + ownerClass.getCanonicalName() + "]");
}
var elementType = arrayDimension == nArguments
? ArrayTypeUtils.elementType(ownerClass)
: ArrayTypeUtils.elementType(ownerClass, arrayDimension - nArguments);
return Array.newInstance(elementType, Arrays.stream(arguments).mapToInt(argument ->
argument instanceof Integer ? (Integer) argument : Integer.parseInt(String.valueOf(argument))
).toArray());
} else {
// METHOD REFERENCE
// if the owner is a class and the method closure can be related to some instance method(s)
// try to invoke method with adjusted arguments -- first argument is instance of owner type
if (arguments.length > 0 && ownerClass.isInstance(arguments[0])
&& (Boolean) closure.getProperty(MethodClosure.ANY_INSTANCE_METHOD_EXISTS)) {
try {
Object newReceiver = arguments[0];
Object[] newArguments = Arrays.copyOfRange(arguments, 1, arguments.length);
return ownerMetaClass.invokeMethod(ownerClass, newReceiver, method, newArguments, false, false);
} catch (MissingMethodException ignore) {}
}
if (ownerClass != Class.class) { // maybe it's a reference to a Class method
try {
MetaClass cmc = registry.getMetaClass(Class.class);
return cmc.invokeMethod(Class.class, owner, method, arguments, false, false);
} catch (MissingMethodException ignore) {}
}
return invokeMissingMethod(closure, method, arguments);
}
}
}
/**
* 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.
*
* 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)
*/
@Override
public Object invokeMethod(final Class sender, final Object object, final String methodName, final Object[] originalArguments, final boolean isCallToSuper, final boolean fromInsideClass) {
checkInitalised();
if (object == null) {
throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
}
final Object[] arguments = originalArguments == null ? EMPTY_ARGUMENTS : originalArguments;
MetaMethod method = getMetaMethod(sender, object, methodName, isCallToSuper, arguments);
if (object instanceof Closure) {
final Closure closure = (Closure)object;
final Object owner = closure.getOwner();
if (CALL_METHOD.equals(methodName) || DO_CALL_METHOD.equals(methodName)) {
var closureClass = closure.getClass();
if (closureClass == MethodClosure.class) {
return invokeMethodClosure((MethodClosure) closure, arguments);
} else if (closureClass == CurriedClosure.class) {
MetaClass ownerMetaClass = registry.getMetaClass(owner instanceof Class ? (Class) owner : owner.getClass());
return ownerMetaClass.invokeMethod(owner, methodName, ((CurriedClosure) closure).getUncurriedArguments(arguments));
}
if (method == null) invokeMissingMethod(object, methodName, arguments);
}
final Object delegate = closure.getDelegate();
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 != null && delegate != closure) {
MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
method = delegateMetaClass.pickMethod(methodName, argClasses);
if (method != null) {
return delegateMetaClass.invokeMethod(delegate, methodName, originalArguments);
}
if (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:
Tuple2