in src/main/java/groovy/lang/MetaClassImpl.java [504:596]
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();
}