org.gradle.internal.classpath.CallInterceptingMetaClass Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.internal.classpath;
import groovy.lang.AdaptingMetaClass;
import groovy.lang.MetaClass;
import groovy.lang.MetaClassImpl;
import groovy.lang.MetaClassRegistry;
import groovy.lang.MetaMethod;
import groovy.lang.MetaProperty;
import groovy.lang.MissingMethodException;
import groovy.lang.MissingPropertyException;
import groovy.lang.Tuple;
import org.codehaus.groovy.reflection.CachedClass;
import org.codehaus.groovy.reflection.ClassInfo;
import org.codehaus.groovy.runtime.MetaClassHelper;
import org.gradle.api.NonNullApi;
import org.gradle.internal.Cast;
import org.gradle.internal.Pair;
import org.gradle.internal.classpath.intercept.CallInterceptor;
import org.gradle.internal.classpath.intercept.CallInterceptorResolver;
import org.gradle.internal.classpath.intercept.InterceptScope;
import org.gradle.internal.classpath.intercept.Invocation;
import org.gradle.internal.classpath.intercept.InvocationImpl;
import org.gradle.internal.classpath.intercept.PropertyAwareCallInterceptor;
import org.gradle.internal.classpath.intercept.SignatureAwareCallInterceptor;
import org.gradle.internal.metaobject.InstrumentedMetaClass;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Callable;
import static org.gradle.internal.classpath.InstrumentedGroovyCallsTracker.CallKind.GET_PROPERTY;
import static org.gradle.internal.classpath.InstrumentedGroovyCallsTracker.CallKind.INVOKE_METHOD;
import static org.gradle.internal.classpath.InstrumentedGroovyCallsTracker.CallKind.SET_PROPERTY;
/**
* A metaclass implementation that consults the Groovy call interception infrastructure ({@link InstrumentedGroovyCallsTracker} and {@link CallInterceptorResolver}).
* It can both intercept calls to {@link MetaClass#invokeMethod} and provide a {@link MetaMethod} implementation in {@link MetaClass#pickMethod} that intercepts the calls.
*
* An important requirement for this implementation is to be able to intercept calls to declarations that are (no longer) present in the real class.
*
* It implements {@link AdaptingMetaClass} in order to tell the Groovy runtime that it cannot cache the metamethod instances, as it does with the default {@link MetaClassImpl}.
*/
@NonNullApi
public class CallInterceptingMetaClass extends MetaClassImpl implements AdaptingMetaClass, InstrumentedMetaClass {
private MetaClass adaptee;
private final InstrumentedGroovyCallsTracker callsTracker;
private final CallInterceptorResolver interceptorResolver;
private static final Object[] NO_ARG = new Object[0];
public CallInterceptingMetaClass(
MetaClassRegistry registry,
Class> javaClass,
MetaClass adaptee,
InstrumentedGroovyCallsTracker callsTracker,
CallInterceptorResolver interceptorResolver
) {
super(registry, javaClass);
this.adaptee = adaptee;
this.callsTracker = callsTracker;
this.interceptorResolver = interceptorResolver;
super.initialize();
}
@Override
@Nullable
public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) {
if (useSuper || fromInsideClass) {
return adaptee.getProperty(sender, object, name, useSuper, fromInsideClass);
} else {
return invokeIntercepted(object, GET_PROPERTY, name, NO_ARG, () -> adaptee.getProperty(sender, object, name, false, false));
}
}
@Override
@Nullable
public Object getProperty(Object object, String property) {
return invokeIntercepted(object, GET_PROPERTY, property, NO_ARG, () -> adaptee.getProperty(object, property));
}
@Override
public void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) {
if (useSuper || fromInsideClass) {
adaptee.setProperty(sender, object, name, newValue, useSuper, fromInsideClass);
} else {
invokeIntercepted(object, SET_PROPERTY, name, new Object[]{newValue}, () -> {
adaptee.setProperty(sender, object, name, newValue, useSuper, fromInsideClass);
return null;
});
}
}
@Override
public void setProperty(Object object, String property, Object newValue) {
invokeIntercepted(object, SET_PROPERTY, property, new Object[]{newValue}, () -> {
adaptee.setProperty(object, property, newValue);
return null;
});
}
@Override
@Nullable
public MetaProperty getMetaProperty(String name) {
MetaProperty original = adaptee.getMetaProperty(name);
Pair getterCallerAndInterceptor = findGetterCallerAndInterceptor(name);
Pair setterCallerAndInterceptor = getterCallerAndInterceptor != null ? null : findSetterCallerAndInterceptor(name);
if (getterCallerAndInterceptor != null || setterCallerAndInterceptor != null) {
// TODO: the interceptor should tell the type
String consumerClass = getterCallerAndInterceptor != null ? getterCallerAndInterceptor.left : setterCallerAndInterceptor.left;
Class> propertyType = interceptedPropertyType(
original,
Optional.ofNullable(getterCallerAndInterceptor).map(Pair::right).orElse(null),
Optional.ofNullable(setterCallerAndInterceptor).map(Pair::right).orElse(null)
);
if (propertyType != null) {
return new InterceptedMetaProperty(name,
propertyType, original,
theClass, callsTracker, getterCallerAndInterceptor != null ? getterCallerAndInterceptor.right : null,
setterCallerAndInterceptor != null ? setterCallerAndInterceptor.right : null,
Objects.requireNonNull(consumerClass));
}
}
return original;
}
private @Nullable Class> interceptedPropertyType(
@Nullable MetaProperty originalProperty,
@Nullable CallInterceptor getterInterceptor,
@Nullable CallInterceptor setterInterceptor
) {
if (getterInterceptor instanceof PropertyAwareCallInterceptor) {
Class> typeFromGetter = ((PropertyAwareCallInterceptor) getterInterceptor).matchesProperty(theClass);
if (typeFromGetter != null) {
return typeFromGetter;
}
}
if (setterInterceptor instanceof PropertyAwareCallInterceptor) {
Class> typeFromSetter = ((PropertyAwareCallInterceptor) setterInterceptor).matchesProperty(theClass);
if (typeFromSetter != null) {
return typeFromSetter;
}
}
if (originalProperty != null) {
return originalProperty.getType();
}
return null;
}
@Override
@Nullable
public Object invokeMethod(Object object, String methodName, @Nullable Object arguments) {
Object[] argsForInterceptor = arguments == null ? MetaClassHelper.EMPTY_ARRAY :
arguments instanceof Tuple ? ((Tuple>) arguments).toArray() :
arguments instanceof Object[] ? (Object[]) arguments :
new Object[]{arguments};
return invokeIntercepted(object, INVOKE_METHOD, methodName, argsForInterceptor, () -> adaptee.invokeMethod(object, methodName, arguments));
}
@Override
@Nullable
public Object invokeMethod(Object object, String methodName, Object[] arguments) {
return invokeIntercepted(object, INVOKE_METHOD, methodName, arguments, () -> adaptee.invokeMethod(object, methodName, arguments));
}
@Override
@Nullable
public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
if (isCallToSuper || fromInsideClass) {
// Calls to super are not supported by the call interception mechanisms as of now
return adaptee.invokeMethod(sender, object, methodName, originalArguments, isCallToSuper, fromInsideClass);
}
return invokeIntercepted(object, INVOKE_METHOD, methodName, originalArguments, () -> adaptee.invokeMethod(sender, object, methodName, originalArguments, isCallToSuper, fromInsideClass));
}
@Override
@Nullable
public MetaMethod pickMethod(String methodName, Class[] arguments) {
String matchedCaller = callsTracker.findCallerForCurrentCallIfNotIntercepted(methodName, INVOKE_METHOD);
MetaMethod original = adaptee.pickMethod(methodName, arguments);
if (matchedCaller != null) {
CallInterceptor callInterceptor = interceptorResolver.resolveCallInterceptor(InterceptScope.methodsNamed(methodName));
if (callInterceptor instanceof SignatureAwareCallInterceptor) {
SignatureAwareCallInterceptor.SignatureMatch signatureMatch = ((SignatureAwareCallInterceptor) callInterceptor).matchesMethodSignature(theClass, arguments, false);
if (signatureMatch != null) {
return new InterceptedMetaMethod(
original,
methodName,
theClass,
matchedCaller,
callInterceptor,
signatureMatch.argClasses,
signatureMatch.isVararg,
callsTracker);
}
}
}
return original;
}
@Nullable
private Object invokeIntercepted(Object receiver, InstrumentedGroovyCallsTracker.CallKind kind, String name, Object[] arguments, Callable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy