optimization of delegates by val property

This commit is contained in:
Alex Tkachman
2012-09-08 09:34:02 +03:00
parent b4746ff53c
commit 53f145b533
3 changed files with 100 additions and 14 deletions
@@ -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");