org.gradle.internal.instantiation.AsmBackedClassGeneratorTest 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 2018 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.instantiation;
import groovy.lang.Closure;
import groovy.lang.GroovyObject;
import groovy.lang.MissingMethodException;
import org.gradle.api.Action;
import org.gradle.api.NonExtensible;
import org.gradle.api.file.FileCollection;
import org.gradle.api.internal.ConventionMapping;
import org.gradle.api.internal.DynamicObjectAware;
import org.gradle.api.internal.GeneratedSubclasses;
import org.gradle.api.internal.HasConvention;
import org.gradle.api.internal.IConventionAware;
import org.gradle.api.internal.plugins.DslObject;
import org.gradle.api.internal.provider.DefaultProviderFactory;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.plugins.Convention;
import org.gradle.api.plugins.ExtensionAware;
import org.gradle.api.plugins.ExtensionContainer;
import org.gradle.api.provider.Property;
import org.gradle.api.reflect.HasPublicType;
import org.gradle.api.reflect.TypeOf;
import org.gradle.internal.extensibility.ConventionAwareHelper;
import org.gradle.internal.extensibility.ExtensibleDynamicObject;
import org.gradle.internal.extensibility.NoConventionMapping;
import org.gradle.internal.metaobject.BeanDynamicObject;
import org.gradle.internal.metaobject.DynamicObject;
import org.gradle.internal.reflect.DirectInstantiator;
import org.gradle.internal.reflect.JavaReflectionUtil;
import org.gradle.internal.service.DefaultServiceRegistry;
import org.gradle.util.TestUtil;
import org.junit.Test;
import spock.lang.Issue;
import javax.inject.Inject;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import static org.gradle.api.reflect.TypeOf.typeOf;
import static org.gradle.internal.instantiation.AbstractClassGeneratorTestGroovy.BeanWithGroovyBoolean;
import static org.gradle.util.Matchers.isEmpty;
import static org.gradle.util.TestUtil.TEST_CLOSURE;
import static org.gradle.util.TestUtil.call;
import static org.gradle.util.WrapUtil.toList;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class AsmBackedClassGeneratorTest {
private final ClassGenerator generator = AsmBackedClassGenerator.decorateAndInject(Collections.emptyList(), Collections.>emptyList());
private T newInstance(Class clazz, Object... args) throws Exception {
ClassGenerator.GeneratedClass extends T> type = generator.generate(clazz);
for (ClassGenerator.GeneratedConstructor> constructor : type.getConstructors()) {
if (constructor.getParameterTypes().length == args.length) {
int i = 0;
for (; i < args.length; i++) {
Object arg = args[i];
Class> parameterType = constructor.getParameterTypes()[i];
if (parameterType.isPrimitive()) {
parameterType = JavaReflectionUtil.getWrapperTypeForPrimitiveType(parameterType);
}
if (!parameterType.isInstance(arg)) {
break;
}
}
if (i == args.length) {
return (T) constructor.newInstance(new DefaultServiceRegistry(), DirectInstantiator.INSTANCE, args);
}
}
}
throw new UnsupportedOperationException();
}
@Test
public void mixesInGeneratedSubclassInterface() {
Class extends Bean> generatedClass = generator.generate(Bean.class).getGeneratedClass();
assertTrue(GeneratedSubclasses.unpack(generatedClass).equals(Bean.class));
assertEquals(Bean.class, GeneratedSubclasses.unpack(generatedClass));
}
@Test
public void mixesInConventionAwareInterface() throws Exception {
Bean bean = newInstance(Bean.class);
assertTrue(bean instanceof IConventionAware);
IConventionAware conventionAware = (IConventionAware) bean;
assertThat(conventionAware.getConventionMapping(), instanceOf(ConventionAwareHelper.class));
conventionAware.getConventionMapping().map("prop", TEST_CLOSURE);
}
@Test
public void mixesInDynamicObjectAwareInterface() throws Exception {
Bean bean = newInstance(Bean.class);
assertTrue(bean instanceof DynamicObjectAware);
DynamicObjectAware dynamicBean = (DynamicObjectAware) bean;
dynamicBean.getAsDynamicObject().setProperty("prop", "value");
assertThat(bean.getProp(), equalTo("value"));
assertThat(bean.doStuff("some value"), equalTo("{some value}"));
}
@Test
public void mixesInExtensionAwareInterface() throws Exception {
Bean bean = newInstance(Bean.class);
assertTrue(bean instanceof ExtensionAware);
ExtensionAware extensionBean = (ExtensionAware) bean;
assertThat(extensionBean.getExtensions(), notNullValue());
Bean extn = extensionBean.getExtensions().create("nested", Bean.class);
DynamicObjectAware dynamicBean = (DynamicObjectAware) bean;
assertTrue(dynamicBean.getAsDynamicObject().getProperty("nested") == extn);
}
@Test
public void mixesInImplementationForExtensionAwareMethodsWhenGetterIsAbstract() throws Exception {
AbstractExtensibleBean bean = newInstance(AbstractExtensibleBean.class);
assertTrue(bean instanceof DynamicObjectAware);
assertTrue(bean instanceof IConventionAware);
assertThat(bean.getExtensions(), notNullValue());
Bean extension = bean.getExtensions().create("nested", Bean.class);
DynamicObjectAware dynamicBean = (DynamicObjectAware) bean;
assertTrue(dynamicBean.getAsDynamicObject().getProperty("nested") == extension);
}
@Test
public void mixesInGroovyObjectInterface() throws Exception {
Bean bean = newInstance(Bean.class);
assertTrue(bean instanceof GroovyObject);
GroovyObject groovyObject = (GroovyObject) bean;
assertThat(groovyObject.getMetaClass(), notNullValue());
groovyObject.setProperty("prop", "value");
assertThat(bean.getProp(), equalTo("value"));
assertThat(groovyObject.getProperty("prop"), equalTo((Object) "value"));
assertThat(groovyObject.invokeMethod("doStuff", new Object[]{"some value"}), equalTo((Object) "{some value}"));
}
@Test
public void cachesGeneratedSubclass() {
assertSame(generator.generate(Bean.class).getGeneratedClass(), generator.generate(Bean.class).getGeneratedClass());
}
@Test
public void doesNotDecorateAlreadyDecoratedClass() {
Class extends Bean> generatedClass = generator.generate(Bean.class).getGeneratedClass();
assertSame(generatedClass, generator.generate(generatedClass).getGeneratedClass());
}
@Test
public void overridesPublicConstructors() throws Exception {
Bean bean = newInstance(BeanWithConstructor.class, "value");
assertThat(bean.getProp(), equalTo("value"));
bean = newInstance(BeanWithConstructor.class);
assertThat(bean.getProp(), equalTo("default value"));
}
@Test
public void includesGenericTypeInformationForOverriddenConstructor() {
Class> generatedClass = generator.generate(BeanWithComplexConstructor.class).getGeneratedClass();
Constructor> constructor = generatedClass.getDeclaredConstructors()[0];
assertThat(constructor.getTypeParameters().length, equalTo(3));
assertThat(constructor.getGenericParameterTypes().length, equalTo(12));
// Callable
Type paramType = constructor.getGenericParameterTypes()[0];
assertThat(paramType, equalTo((Type) Callable.class));
// Callable
paramType = constructor.getGenericParameterTypes()[1];
assertThat(paramType, instanceOf(ParameterizedType.class));
ParameterizedType parameterizedType = (ParameterizedType) paramType;
assertThat(parameterizedType.getRawType(), equalTo((Type) Callable.class));
assertThat(parameterizedType.getActualTypeArguments()[0], equalTo((Type) String.class));
// Callable extends String>
paramType = constructor.getGenericParameterTypes()[2];
assertThat(paramType, instanceOf(ParameterizedType.class));
parameterizedType = (ParameterizedType) paramType;
assertThat(parameterizedType.getRawType(), equalTo((Type) Callable.class));
assertThat(parameterizedType.getActualTypeArguments()[0], instanceOf(WildcardType.class));
WildcardType wildcard = (WildcardType) parameterizedType.getActualTypeArguments()[0];
assertThat(wildcard.getUpperBounds().length, equalTo(1));
assertThat(wildcard.getUpperBounds()[0], equalTo((Type) String.class));
assertThat(wildcard.getLowerBounds().length, equalTo(0));
// Callable super String>
paramType = constructor.getGenericParameterTypes()[3];
assertThat(paramType, instanceOf(ParameterizedType.class));
parameterizedType = (ParameterizedType) paramType;
assertThat(parameterizedType.getRawType(), equalTo((Type) Callable.class));
assertThat(parameterizedType.getActualTypeArguments()[0], instanceOf(WildcardType.class));
wildcard = (WildcardType) parameterizedType.getActualTypeArguments()[0];
assertThat(wildcard.getUpperBounds().length, equalTo(1));
assertThat(wildcard.getUpperBounds()[0], equalTo((Type) Object.class));
assertThat(wildcard.getLowerBounds().length, equalTo(1));
assertThat(wildcard.getLowerBounds()[0], equalTo((Type) String.class));
// Callable>
paramType = constructor.getGenericParameterTypes()[4];
assertThat(paramType, instanceOf(ParameterizedType.class));
parameterizedType = (ParameterizedType) paramType;
assertThat(parameterizedType.getRawType(), equalTo((Type) Callable.class));
assertThat(parameterizedType.getActualTypeArguments()[0], instanceOf(WildcardType.class));
wildcard = (WildcardType) parameterizedType.getActualTypeArguments()[0];
assertThat(wildcard.getUpperBounds().length, equalTo(1));
assertThat(wildcard.getUpperBounds()[0], equalTo((Type) Object.class));
assertThat(wildcard.getLowerBounds().length, equalTo(0));
// Callable extends Callable>>
paramType = constructor.getGenericParameterTypes()[5];
assertThat(paramType, instanceOf(ParameterizedType.class));
parameterizedType = (ParameterizedType) paramType;
assertThat(parameterizedType.getRawType(), equalTo((Type) Callable.class));
assertThat(parameterizedType.getActualTypeArguments()[0], instanceOf(WildcardType.class));
wildcard = (WildcardType) parameterizedType.getActualTypeArguments()[0];
assertThat(wildcard.getUpperBounds().length, equalTo(1));
assertThat(wildcard.getLowerBounds().length, equalTo(0));
assertThat(wildcard.getUpperBounds()[0], instanceOf(ParameterizedType.class));
parameterizedType = (ParameterizedType) wildcard.getUpperBounds()[0];
assertThat(parameterizedType.getRawType(), equalTo((Type) Callable.class));
assertThat(parameterizedType.getActualTypeArguments()[0], instanceOf(WildcardType.class));
wildcard = (WildcardType) parameterizedType.getActualTypeArguments()[0];
assertThat(wildcard.getUpperBounds().length, equalTo(1));
assertThat(wildcard.getUpperBounds()[0], equalTo((Type) Object.class));
assertThat(wildcard.getLowerBounds().length, equalTo(0));
// Callable
paramType = constructor.getGenericParameterTypes()[6];
assertThat(paramType, instanceOf(ParameterizedType.class));
parameterizedType = (ParameterizedType) paramType;
assertThat(parameterizedType.getRawType(), equalTo((Type) Callable.class));
assertThat(parameterizedType.getActualTypeArguments()[0], instanceOf(TypeVariable.class));
TypeVariable typeVariable = (TypeVariable) parameterizedType.getActualTypeArguments()[0];
assertThat(typeVariable.getName(), equalTo("S"));
assertThat(typeVariable.getBounds()[0], instanceOf(ParameterizedType.class));
// Callable extends T>
paramType = constructor.getGenericParameterTypes()[7];
assertThat(paramType, instanceOf(ParameterizedType.class));
parameterizedType = (ParameterizedType) paramType;
assertThat(parameterizedType.getRawType(), equalTo((Type) Callable.class));
assertThat(parameterizedType.getActualTypeArguments()[0], instanceOf(WildcardType.class));
wildcard = (WildcardType) parameterizedType.getActualTypeArguments()[0];
assertThat(wildcard.getUpperBounds().length, equalTo(1));
assertThat(wildcard.getLowerBounds().length, equalTo(0));
assertThat(wildcard.getUpperBounds()[0], instanceOf(TypeVariable.class));
typeVariable = (TypeVariable) wildcard.getUpperBounds()[0];
assertThat(typeVariable.getName(), equalTo("T"));
assertThat(typeVariable.getBounds()[0], equalTo((Type) IOException.class));
// V
paramType = constructor.getGenericParameterTypes()[8];
assertThat(paramType, instanceOf(TypeVariable.class));
typeVariable = (TypeVariable) paramType;
assertThat(typeVariable.getName(), equalTo("V"));
assertThat(typeVariable.getBounds()[0], equalTo((Type) Object.class));
GenericArrayType arrayType;
// String[]
paramType = constructor.getGenericParameterTypes()[9];
assertThat(paramType, equalTo((Type) String[].class));
assertThat(((Class>) paramType).getComponentType(), equalTo((Type) String.class));
// List extends String>[]
paramType = constructor.getGenericParameterTypes()[10];
assertThat(paramType, instanceOf(GenericArrayType.class));
arrayType = (GenericArrayType) paramType;
assertThat(arrayType.getGenericComponentType(), instanceOf(ParameterizedType.class));
parameterizedType = (ParameterizedType) arrayType.getGenericComponentType();
assertThat(parameterizedType.getRawType(), equalTo((Type) List.class));
assertThat(parameterizedType.getActualTypeArguments().length, equalTo(1));
assertThat(parameterizedType.getActualTypeArguments()[0], instanceOf(WildcardType.class));
// boolean
paramType = constructor.getGenericParameterTypes()[11];
assertThat(paramType, equalTo((Type) Boolean.TYPE));
assertThat(constructor.getGenericExceptionTypes().length, equalTo(2));
// throws Exception
Type exceptionType = constructor.getGenericExceptionTypes()[0];
assertThat(exceptionType, equalTo((Type) Exception.class));
// throws T
exceptionType = constructor.getGenericExceptionTypes()[1];
assertThat(exceptionType, instanceOf(TypeVariable.class));
typeVariable = (TypeVariable) exceptionType;
assertThat(typeVariable.getName(), equalTo("T"));
}
@Test
public void includesAnnotationInformationForOverriddenConstructor() {
Class> generatedClass = generator.generate(BeanWithAnnotatedConstructor.class).getGeneratedClass();
Constructor> constructor = generatedClass.getDeclaredConstructors()[0];
assertThat(constructor.getAnnotation(Inject.class), notNullValue());
}
@Test
public void canConstructInstance() throws Exception {
Bean bean = newInstance(BeanWithConstructor.class, "value");
assertThat(bean.getClass(), sameInstance((Object) generator.generate(BeanWithConstructor.class).getGeneratedClass()));
assertThat(bean.getProp(), equalTo("value"));
bean = newInstance(BeanWithConstructor.class);
assertThat(bean.getProp(), equalTo("default value"));
bean = newInstance(BeanWithConstructor.class, 127);
assertThat(bean.getProp(), equalTo("127"));
}
@Test
public void reportsConstructionFailure() throws Exception {
try {
newInstance(UnconstructibleBean.class);
fail();
} catch (InvocationTargetException e) {
assertThat(e.getCause(), sameInstance(UnconstructibleBean.failure));
}
}
@Test
public void cannotCreateInstanceOfPrivateClass() throws Exception {
try {
newInstance(PrivateBean.class);
fail();
} catch (ClassGenerationException e) {
assertThat(e.getMessage(), equalTo("Class " + PrivateBean.class.getName() + " is private."));
}
}
@Test
public void cannotCreateInstanceOfFinalClass() throws Exception {
try {
newInstance(FinalBean.class);
fail();
} catch (ClassGenerationException e) {
assertThat(e.getMessage(), equalTo("Class " + FinalBean.class.getName() + " is final."));
}
}
@Test
public void cannotCreateInstanceOfClassWithAbstractMethod() throws Exception {
try {
newInstance(AbstractMethodBean.class);
fail();
} catch (ClassGenerationException e) {
assertThat(e.getMessage(), equalTo("Could not generate a decorated class for class " + AbstractMethodBean.class.getName() + "."));
assertThat(e.getCause().getMessage(), equalTo("Cannot have abstract method AbstractMethodBean.implementMe()."));
}
}
@Test
public void cannotCreateInstanceOfClassWithAbstractGetter() throws Exception {
try {
newInstance(AbstractGetterBean.class);
fail();
} catch (ClassGenerationException e) {
assertThat(e.getMessage(), equalTo("Could not generate a decorated class for class " + AbstractGetterBean.class.getName() + "."));
assertThat(e.getCause().getMessage(), equalTo("Cannot have abstract method AbstractGetterBean.getThing()."));
}
}
@Test
public void cannotCreateInstanceOfClassWithAbstractSetter() throws Exception {
try {
newInstance(AbstractSetterBean.class);
fail();
} catch (ClassGenerationException e) {
assertThat(e.getMessage(), equalTo("Could not generate a decorated class for class " + AbstractSetterBean.class.getName() + "."));
assertThat(e.getCause().getMessage(), equalTo("Cannot have abstract method AbstractSetterBean.setThing()."));
}
}
@Test
public void cannotCreateInstanceOfClassWithAbstractSetMethod() throws Exception {
try {
newInstance(AbstractSetMethodBean.class);
fail();
} catch (ClassGenerationException e) {
assertThat(e.getMessage(), equalTo("Could not generate a decorated class for class " + AbstractSetMethodBean.class.getName() + "."));
assertThat(e.getCause().getMessage(), equalTo("Cannot have abstract method AbstractSetMethodBean.thing()."));
}
}
@Test
public void canConstructInstanceOfInterfaceWithPropertyGetterAndSetter() throws Exception {
InterfaceBean bean = newInstance(InterfaceBean.class);
assertThat(bean.getName(), nullValue());
bean.setName("name");
assertThat(bean.getName(), equalTo("name"));
}
@Test
public void canConstructInstanceOfInterfaceWithDefaultMethodsOnly() throws Exception {
InterfaceWithDefaultMethods bean = newInstance(InterfaceWithDefaultMethods.class);
assertThat(bean.getName(), equalTo("name"));
}
@Test
public void cannotCreateInstanceOfInterfaceWithAbstractGetterAndNoSetter() throws Exception {
try {
newInstance(GetterBeanInterface.class);
fail();
} catch (ClassGenerationException e) {
assertThat(e.getMessage(), equalTo("Could not generate a decorated class for interface " + GetterBeanInterface.class.getName() + "."));
assertThat(e.getCause().getMessage(), equalTo("Cannot have abstract method GetterBeanInterface.getThing()."));
}
}
@Test
public void appliesConventionMappingToEachProperty() throws Exception {
Bean bean = newInstance(Bean.class);
assertTrue(IConventionAware.class.isInstance(bean));
IConventionAware conventionAware = (IConventionAware) bean;
assertThat(bean.getProp(), nullValue());
conventionAware.getConventionMapping().map("prop", new Callable() {
public String call() {
return "conventionValue";
}
});
assertThat(bean.getProp(), equalTo("conventionValue"));
bean.setProp("value");
assertThat(bean.getProp(), equalTo("value"));
bean.setProp(null);
assertThat(bean.getProp(), nullValue());
}
@Test
public void appliesConventionMappingToPropertyWithMultipleSetters() throws Exception {
BeanWithVariousGettersAndSetters bean = newInstance(BeanWithVariousGettersAndSetters.class);
new DslObject(bean).getConventionMapping().map("overloaded", new Callable() {
public String call() {
return "conventionValue";
}
});
assertThat(bean.getOverloaded(), equalTo("conventionValue"));
bean.setOverloaded("value");
assertThat(bean.getOverloaded(), equalTo("chars = value"));
bean = newInstance(BeanWithVariousGettersAndSetters.class);
new DslObject(bean).getConventionMapping().map("overloaded", new Callable() {
public String call() {
return "conventionValue";
}
});
assertThat(bean.getOverloaded(), equalTo("conventionValue"));
bean.setOverloaded(12);
assertThat(bean.getOverloaded(), equalTo("number = 12"));
bean = newInstance(BeanWithVariousGettersAndSetters.class);
new DslObject(bean).getConventionMapping().map("overloaded", new Callable() {
public String call() {
return "conventionValue";
}
});
assertThat(bean.getOverloaded(), equalTo("conventionValue"));
bean.setOverloaded(true);
assertThat(bean.getOverloaded(), equalTo("object = true"));
}
@Test
public void appliesConventionMappingToPropertyWithGetterCovariantType() throws Exception {
CovariantPropertyTypes bean = newInstance(CovariantPropertyTypes.class);
new DslObject(bean).getConventionMapping().map("value", new Callable() {
public String call() {
return "conventionValue";
}
});
assertThat(bean.getValue(), equalTo("conventionValue"));
bean.setValue(12);
assertThat(bean.getValue(), equalTo("12"));
}
@Test
public void appliesConventionMappingToPropertyWithCovariantOverrideOfAbstractGetter() throws Exception {
BeanWithCovariantAbstract bean = newInstance(BeanWithCovariantAbstract.class);
ConventionMapping conventionMapping = new DslObject(bean).getConventionMapping();
conventionMapping.map("prop", new Callable() {
public String call() {
return "conventionValue";
}
});
conventionMapping.map("number", new Callable() {
public Integer call() {
return 123;
}
});
assertThat(bean.getNumber(), equalTo(123));
assertThat(bean.getProp(), equalTo("conventionValue"));
assertThat(bean.getTypedProp(), equalTo(12L));
}
@Test
public void appliesConventionMappingToProtectedMethods() throws Exception {
BeanWithNonPublicProperties bean = newInstance(BeanWithNonPublicProperties.class);
assertThat(bean.getPackageProtected(), equalTo("package-protected"));
assertThat(bean.getProtected(), equalTo("protected"));
assertThat(bean.getPrivate(), equalTo("private"));
IConventionAware conventionAware = (IConventionAware) bean;
conventionAware.getConventionMapping().map("packageProtected", new Callable() {
public String call() {
return "1";
}
});
conventionAware.getConventionMapping().map("protected", new Callable() {
public String call() {
return "2";
}
});
assertThat(bean.getPackageProtected(), equalTo("1"));
assertThat(bean.getProtected(), equalTo("2"));
}
@Test
@Issue("GRADLE-2163")
public void appliesConventionMappingToGroovyBoolean() throws Exception {
BeanWithGroovyBoolean bean = newInstance(BeanWithGroovyBoolean.class);
assertTrue(bean instanceof IConventionAware);
assertThat(bean.getSmallB(), equalTo(false));
assertThat(bean.getBigB(), nullValue());
assertThat(bean.getMixedB(), equalTo(false));
IConventionAware conventionAware = (IConventionAware) bean;
conventionAware.getConventionMapping().map("smallB", new Callable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy