From 53f145b533edf78b8959feed348b37264f2c0a63 Mon Sep 17 00:00:00 2001 From: Alex Tkachman Date: Sat, 8 Sep 2012 09:34:02 +0300 Subject: [PATCH] optimization of delegates by val property --- .../codegen/ImplementationBodyCodegen.java | 57 ++++++++++++++----- .../codegen/classes/delegationToVal.kt | 25 ++++++++ .../jetbrains/jet/codegen/ClassGenTest.java | 32 +++++++++++ 3 files changed, 100 insertions(+), 14 deletions(-) create mode 100644 compiler/testData/codegen/classes/delegationToVal.kt diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index 8b3ac87c030..1053734e19a 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -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 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); } diff --git a/compiler/testData/codegen/classes/delegationToVal.kt b/compiler/testData/codegen/classes/delegationToVal.kt new file mode 100644 index 00000000000..ec6242f5d16 --- /dev/null +++ b/compiler/testData/codegen/classes/delegationToVal.kt @@ -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() +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/ClassGenTest.java b/compiler/tests/org/jetbrains/jet/codegen/ClassGenTest.java index c6aa5d9b5c0..5f42939c40a 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/ClassGenTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/ClassGenTest.java @@ -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");