Optimize KProperty objects for delegated properties

Don't generate anonymous classes with a lot of methods because this hurts
Android. Add special classes to the runtime instead (also add
FunctionReferenceImpl, to be used later) and just create their instances in the
static initializer of each class
This commit is contained in:
Alexander Udalov
2015-10-25 15:42:13 +03:00
parent 2d1a7163a3
commit a97567ea0c
10 changed files with 444 additions and 41 deletions
@@ -66,8 +66,7 @@ import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYN
import static org.jetbrains.kotlin.resolve.BindingContext.VARIABLE;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isCompanionObject;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.isTopLevelDeclaration;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.PROPERTY_METADATA_TYPE;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt.Synthetic;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
@@ -509,18 +508,41 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
iv.newarray(PROPERTY_METADATA_TYPE);
for (int i = 0, size = delegatedProperties.size(); i < size; i++) {
VariableDescriptor property = BindingContextUtils.getNotNull(bindingContext, VARIABLE, delegatedProperties.get(i));
PropertyDescriptor property =
(PropertyDescriptor) BindingContextUtils.getNotNull(bindingContext, VARIABLE, delegatedProperties.get(i));
iv.dup();
iv.iconst(i);
ReceiverParameterDescriptor dispatchReceiver = property.getDispatchReceiverParameter();
StackValue value;
// TODO: remove this option and always generate PropertyReferenceNImpl creation
if ("true".equalsIgnoreCase(System.getProperty("kotlin.jvm.optimize.delegated.properties"))) {
int receiverCount = (property.getDispatchReceiverParameter() != null ? 1 : 0) +
(property.getExtensionReceiverParameter() != null ? 1 : 0);
Type implType = property.isVar() ? MUTABLE_PROPERTY_REFERENCE_IMPL[receiverCount] : PROPERTY_REFERENCE_IMPL[receiverCount];
iv.anew(implType);
iv.dup();
// TODO: generate the container once and save to a local field instead
ClosureCodegen.generateCallableReferenceDeclarationContainer(iv, property, state);
iv.aconst(property.getName().asString());
iv.aconst(PropertyReferenceCodegen.getPropertyReferenceSignature(property, state));
iv.invokespecial(
implType.getInternalName(), "<init>",
Type.getMethodDescriptor(Type.VOID_TYPE, K_DECLARATION_CONTAINER_TYPE, JAVA_STRING_TYPE, JAVA_STRING_TYPE), false
);
value = StackValue.onStack(implType);
Method wrapper = PropertyReferenceCodegen.getWrapperMethodForPropertyReference(property, receiverCount);
iv.invokestatic(REFLECTION, wrapper.getName(), wrapper.getDescriptor(), false);
}
else {
ReceiverParameterDescriptor dispatchReceiver = property.getDispatchReceiverParameter();
//noinspection ConstantConditions
StackValue value = createOrGetClInitCodegen().generatePropertyReference(
delegatedProperties.get(i).getDelegate(), property, property,
dispatchReceiver != null ? new TransientReceiver(dispatchReceiver.getType()) : ReceiverValue.NO_RECEIVER
);
//noinspection ConstantConditions
value = createOrGetClInitCodegen().generatePropertyReference(
delegatedProperties.get(i).getDelegate(), property, property,
dispatchReceiver != null ? new TransientReceiver(dispatchReceiver.getType()) : ReceiverValue.NO_RECEIVER
);
}
value.put(PROPERTY_METADATA_TYPE, iv);
@@ -35,10 +35,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.resolve.scopes.receivers.ScriptReceiver
import org.jetbrains.kotlin.utils.sure
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_FINAL
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_PUBLIC
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_SUPER
import org.jetbrains.org.objectweb.asm.Opcodes.V1_6
import org.jetbrains.org.objectweb.asm.Opcodes.*
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.commons.Method
@@ -74,24 +71,7 @@ public class PropertyReferenceCodegen(
private val superAsmType = typeMapper.mapClass(classDescriptor.getSuperClassNotAny().sure { "No super class for $classDescriptor" })
// e.g. mutableProperty0(Lkotlin/jvm/internal/MutablePropertyReference0;)Lkotlin/reflect/KMutableProperty0;
private val wrapperMethod: Method
init {
wrapperMethod = when (receiverCount) {
2 -> when {
target.isVar -> method("mutableProperty2", K_MUTABLE_PROPERTY2_TYPE, MUTABLE_PROPERTY_REFERENCE2)
else -> method("property2", K_PROPERTY2_TYPE, PROPERTY_REFERENCE2)
}
1 -> when {
target.isVar -> method("mutableProperty1", K_MUTABLE_PROPERTY1_TYPE, MUTABLE_PROPERTY_REFERENCE1)
else -> method("property1", K_PROPERTY1_TYPE, PROPERTY_REFERENCE1)
}
else -> when {
target.isVar -> method("mutableProperty0", K_MUTABLE_PROPERTY0_TYPE, MUTABLE_PROPERTY_REFERENCE0)
else -> method("property0", K_PROPERTY0_TYPE, PROPERTY_REFERENCE0)
}
}
}
private val wrapperMethod = getWrapperMethodForPropertyReference(target, receiverCount)
override fun generateDeclaration() {
v.defineClass(
@@ -127,16 +107,7 @@ public class PropertyReferenceCodegen(
}
generateMethod("property reference getSignature", ACC_PUBLIC, method("getSignature", JAVA_STRING_TYPE)) {
target as PropertyDescriptor
val getter = target.getGetter() ?: run {
val defaultGetter = DescriptorFactory.createDefaultGetter(target, Annotations.EMPTY)
defaultGetter.initialize(target.getType())
defaultGetter
}
val method = typeMapper.mapSignature(getter).getAsmMethod()
aconst(method.getName() + method.getDescriptor())
aconst(getPropertyReferenceSignature(target as PropertyDescriptor, state))
}
generateAccessors()
@@ -207,4 +178,35 @@ public class PropertyReferenceCodegen(
StackValue.operation(wrapperMethod.getReturnType()) { iv ->
iv.getstatic(asmType.getInternalName(), JvmAbi.INSTANCE_FIELD, wrapperMethod.getReturnType().getDescriptor())
}
companion object {
@JvmStatic
fun getPropertyReferenceSignature(property: PropertyDescriptor, state: GenerationState): String {
val getter =
property.getter ?: DescriptorFactory.createDefaultGetter(property, Annotations.EMPTY).apply {
initialize(property.type)
}
val method = state.typeMapper.mapSignature(getter).asmMethod
return method.name + method.descriptor
}
@JvmStatic
fun getWrapperMethodForPropertyReference(property: VariableDescriptor, receiverCount: Int): Method {
return when (receiverCount) {
2 -> when {
property.isVar -> method("mutableProperty2", K_MUTABLE_PROPERTY2_TYPE, MUTABLE_PROPERTY_REFERENCE2)
else -> method("property2", K_PROPERTY2_TYPE, PROPERTY_REFERENCE2)
}
1 -> when {
property.isVar -> method("mutableProperty1", K_MUTABLE_PROPERTY1_TYPE, MUTABLE_PROPERTY_REFERENCE1)
else -> method("property1", K_PROPERTY1_TYPE, PROPERTY_REFERENCE1)
}
else -> when {
property.isVar -> method("mutableProperty0", K_MUTABLE_PROPERTY0_TYPE, MUTABLE_PROPERTY_REFERENCE0)
else -> method("property0", K_PROPERTY0_TYPE, PROPERTY_REFERENCE0)
}
}
}
}
}