optimization of delegates by val property
This commit is contained in:
@@ -816,27 +816,56 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
int n,
|
int n,
|
||||||
JetDelegationSpecifier specifier
|
JetDelegationSpecifier specifier
|
||||||
) {
|
) {
|
||||||
iv.load(0, classType);
|
final JetExpression expression = ((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression();
|
||||||
codegen.genToJVMStack(((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());
|
JetType superType = bindingContext.get(BindingContext.TYPE, specifier.getTypeReference());
|
||||||
assert superType != null;
|
assert superType != null;
|
||||||
|
|
||||||
ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
|
ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
|
||||||
assert superClassDescriptor != null;
|
assert superClassDescriptor != null;
|
||||||
String delegateField = "$delegate_" + n;
|
|
||||||
Type fieldType = typeMapper.mapType(superClassDescriptor.getDefaultType(), JetTypeMapperMode.VALUE);
|
final Type superTypeAsmType = typeMapper.mapType(superType, JetTypeMapperMode.IMPL);
|
||||||
String fieldDesc = fieldType.getDescriptor();
|
|
||||||
v.newField(specifier, ACC_PRIVATE, delegateField, fieldDesc, /*TODO*/null, null);
|
StackValue field;
|
||||||
StackValue field = StackValue.field(fieldType, classname, delegateField, false);
|
if(propertyDescriptor != null && !propertyDescriptor.isVar() && Boolean.TRUE.equals(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor))) {
|
||||||
field.store(fieldType, iv);
|
// 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,
|
final CodegenContext delegateContext = context.intoClass(superClassDescriptor,
|
||||||
new OwnerKind.DelegateKind(StackValue.field(fieldType, classname,
|
new OwnerKind.DelegateKind(field, superTypeAsmType.getInternalName()),
|
||||||
delegateField, false),
|
|
||||||
typeMapper.mapType(superClassDescriptor
|
|
||||||
.getDefaultType(),
|
|
||||||
JetTypeMapperMode.IMPL)
|
|
||||||
.getInternalName()),
|
|
||||||
state);
|
state);
|
||||||
generateDelegates(superClassDescriptor, delegateContext, field);
|
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");
|
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 {
|
public void testInheritanceAndDelegation_DelegatingDefaultConstructorProperties() throws Exception {
|
||||||
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.JDK_ONLY);
|
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.JDK_ONLY);
|
||||||
blackBoxFile("classes/inheritance.jet");
|
blackBoxFile("classes/inheritance.jet");
|
||||||
|
|||||||
Reference in New Issue
Block a user