optimization of delegates by val property
This commit is contained in:
@@ -816,27 +816,56 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
int n,
|
||||
JetDelegationSpecifier specifier
|
||||
) {
|
||||
iv.load(0, classType);
|
||||
codegen.genToJVMStack(((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression());
|
||||
final JetExpression expression = ((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression();
|
||||
PropertyDescriptor propertyDescriptor = null;
|
||||
if(expression instanceof JetSimpleNameExpression) {
|
||||
final ResolvedCall<? extends CallableDescriptor> call = bindingContext.get(BindingContext.RESOLVED_CALL, expression);
|
||||
if(call != null) {
|
||||
final CallableDescriptor callResultingDescriptor = call.getResultingDescriptor();
|
||||
if(callResultingDescriptor instanceof ValueParameterDescriptor) {
|
||||
final ValueParameterDescriptor valueParameterDescriptor = (ValueParameterDescriptor) callResultingDescriptor;
|
||||
// constructor parameter
|
||||
if(valueParameterDescriptor.getContainingDeclaration() instanceof ConstructorDescriptor) {
|
||||
// constructor of my class
|
||||
if(valueParameterDescriptor.getContainingDeclaration().getContainingDeclaration() == descriptor) {
|
||||
propertyDescriptor = bindingContext.get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, valueParameterDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo: when and if frontend will allow properties defined not as constructor parameters to be used in delegation specifier
|
||||
}
|
||||
}
|
||||
|
||||
JetType superType = bindingContext.get(BindingContext.TYPE, specifier.getTypeReference());
|
||||
assert superType != null;
|
||||
|
||||
ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
|
||||
assert superClassDescriptor != null;
|
||||
String delegateField = "$delegate_" + n;
|
||||
Type fieldType = typeMapper.mapType(superClassDescriptor.getDefaultType(), JetTypeMapperMode.VALUE);
|
||||
String fieldDesc = fieldType.getDescriptor();
|
||||
v.newField(specifier, ACC_PRIVATE, delegateField, fieldDesc, /*TODO*/null, null);
|
||||
StackValue field = StackValue.field(fieldType, classname, delegateField, false);
|
||||
field.store(fieldType, iv);
|
||||
|
||||
final Type superTypeAsmType = typeMapper.mapType(superType, JetTypeMapperMode.IMPL);
|
||||
|
||||
StackValue field;
|
||||
if(propertyDescriptor != null && !propertyDescriptor.isVar() && Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor))) {
|
||||
// final property with backing field
|
||||
field = StackValue.field(typeMapper.mapType(propertyDescriptor.getType(), JetTypeMapperMode.VALUE), classname, propertyDescriptor.getName().getName(), false);
|
||||
}
|
||||
else {
|
||||
iv.load(0, classType);
|
||||
codegen.genToJVMStack(expression);
|
||||
|
||||
String delegateField = "$delegate_" + n;
|
||||
Type fieldType = typeMapper.mapType(superClassDescriptor.getDefaultType(), JetTypeMapperMode.VALUE);
|
||||
String fieldDesc = fieldType.getDescriptor();
|
||||
|
||||
v.newField(specifier, ACC_PRIVATE|ACC_FINAL|ACC_SYNTHETIC, delegateField, fieldDesc, /*TODO*/null, null);
|
||||
|
||||
field = StackValue.field(fieldType, classname, delegateField, false);
|
||||
field.store(fieldType, iv);
|
||||
}
|
||||
|
||||
final CodegenContext delegateContext = context.intoClass(superClassDescriptor,
|
||||
new OwnerKind.DelegateKind(StackValue.field(fieldType, classname,
|
||||
delegateField, false),
|
||||
typeMapper.mapType(superClassDescriptor
|
||||
.getDefaultType(),
|
||||
JetTypeMapperMode.IMPL)
|
||||
.getInternalName()),
|
||||
new OwnerKind.DelegateKind(field, superTypeAsmType.getInternalName()),
|
||||
state);
|
||||
generateDelegates(superClassDescriptor, delegateContext, field);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
trait IActing {
|
||||
fun act(): String
|
||||
}
|
||||
|
||||
class CActing(val value: String = "OK"): IActing {
|
||||
override fun act(): String = value
|
||||
}
|
||||
|
||||
// final so no need in delegate field
|
||||
class Test(val acting: CActing = CActing()): IActing by acting {
|
||||
}
|
||||
|
||||
// even if open so we don't need delegate field
|
||||
open class Test2(open val acting: CActing = CActing()): IActing by acting {
|
||||
}
|
||||
|
||||
// even if open the backing field is final, so we don't need delegate field
|
||||
class Test3() : Test2() {
|
||||
override val acting = CActing("OKOK")
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val test = Test()
|
||||
return test.act()
|
||||
}
|
||||
@@ -58,6 +58,38 @@ public class ClassGenTest extends CodegenTestCase {
|
||||
blackBoxFile("classes/delegationJava.kt");
|
||||
}
|
||||
|
||||
public void testDelegationToVal() throws Exception {
|
||||
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.JDK_ONLY);
|
||||
loadFile("classes/delegationToVal.kt");
|
||||
// System.out.println(generateToText());
|
||||
final ClassFileFactory state = generateClassesInFile();
|
||||
final GeneratedClassLoader loader = createClassLoader(state);
|
||||
final Class aClass = loader.loadClass("namespace");
|
||||
assertEquals("OK", aClass.getMethod("box").invoke(null));
|
||||
|
||||
final Class test = loader.loadClass("Test");
|
||||
try {
|
||||
test.getDeclaredField("$delegate_0");
|
||||
fail("$delegate_0 field generated for class Test but should not");
|
||||
}
|
||||
catch (NoSuchFieldException e) {}
|
||||
|
||||
final Class test2 = loader.loadClass("Test2");
|
||||
try {
|
||||
test2.getDeclaredField("$delegate_0");
|
||||
fail("$delegate_0 field generated for class Test2 but should not");
|
||||
}
|
||||
catch (NoSuchFieldException e) {}
|
||||
|
||||
final Class test3 = loader.loadClass("Test3");
|
||||
final Class iActing = loader.loadClass("IActing");
|
||||
final Object obj = test3.newInstance();
|
||||
assertTrue(iActing.isInstance(obj));
|
||||
final Method iActingMethod = iActing.getMethod("act");
|
||||
assertEquals("OK", iActingMethod.invoke(obj));
|
||||
assertEquals("OKOK", iActingMethod.invoke(test3.getMethod("getActing").invoke(obj)));
|
||||
}
|
||||
|
||||
public void testInheritanceAndDelegation_DelegatingDefaultConstructorProperties() throws Exception {
|
||||
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.JDK_ONLY);
|
||||
blackBoxFile("classes/inheritance.jet");
|
||||
|
||||
Reference in New Issue
Block a user