A new kind of synthetic accessors for backing fields, if accessed inside lambda / object literal / local class #KT-9657 Fixed

A set of tests provided
Also #KT-4867 Fixed
Also #KT-8750 Fixed
Slight codegen refactoring
This commit is contained in:
Mikhail Glukhikh
2015-10-21 12:06:21 +03:00
parent ccad435850
commit 4b6cb3ebce
18 changed files with 298 additions and 73 deletions
@@ -0,0 +1,31 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.psi.KtSuperExpression
import org.jetbrains.kotlin.types.KotlinType
abstract class AccessorForPropertyBackingField(property: PropertyDescriptor,
type: KotlinType,
receiverType: KotlinType?,
dispatchReceiver: ReceiverParameterDescriptor?,
containingDeclaration: DeclarationDescriptor,
suffix: String
) : AccessorForPropertyDescriptor(property, type, receiverType, dispatchReceiver, containingDeclaration, null, suffix)
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.resolve.DescriptorUtils
class AccessorForPropertyBackingFieldFromLocal(property: PropertyDescriptor,
containingDeclaration: DeclarationDescriptor,
nameSuffix: String
) : AccessorForPropertyBackingField(property, property.type,
DescriptorUtils.getReceiverParameterType(property.extensionReceiverParameter),
property.dispatchReceiverParameter, containingDeclaration, nameSuffix)
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.types.KotlinType
class AccessorForPropertyBackingFieldInClassCompanion(property: PropertyDescriptor,
containingDeclaration: DeclarationDescriptor,
delegationType: KotlinType?,
nameSuffix: String
) : AccessorForPropertyBackingField(property, delegationType ?: property.type, null, null, containingDeclaration, nameSuffix)
@@ -1,34 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
import org.jetbrains.kotlin.types.KotlinType;
public class AccessorForPropertyBackingFieldInOuterClass extends AccessorForPropertyDescriptor {
public AccessorForPropertyBackingFieldInOuterClass(
@NotNull PropertyDescriptor property,
@NotNull DeclarationDescriptor containingDeclaration,
@Nullable KotlinType delegationType,
@NotNull String nameSuffix
) {
super(property, delegationType != null ? delegationType : property.getType(), null, null, containingDeclaration, null, nameSuffix);
}
}
@@ -2016,7 +2016,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
receiver = StackValue.receiverWithoutReceiverArgument(receiver);
}
return intermediateValueForProperty(propertyDescriptor, directToField, superExpression, receiver);
return intermediateValueForProperty(propertyDescriptor, directToField, directToField, superExpression, false, receiver);
}
if (descriptor instanceof ClassDescriptor) {
@@ -2140,12 +2140,27 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@Nullable KtSuperExpression superExpression,
@NotNull StackValue receiver
) {
return intermediateValueForProperty(propertyDescriptor, forceField, superExpression, false, receiver);
return intermediateValueForProperty(propertyDescriptor, forceField, false, superExpression, false, receiver);
}
private CodegenContext getBackingFieldContext(
@NotNull FieldAccessorKind accessorKind,
@NotNull DeclarationDescriptor containingDeclaration
) {
switch (accessorKind) {
case NORMAL: return context.getParentContext();
// For companion object property, backing field lives in object containing class
// Otherwise, it lives in its containing declaration
case IN_CLASS_COMPANION: return context.findParentContextWithDescriptor(containingDeclaration.getContainingDeclaration());
case FIELD_FROM_LOCAL: return context.findParentContextWithDescriptor(containingDeclaration);
default: throw new IllegalStateException();
}
}
public StackValue.Property intermediateValueForProperty(
@NotNull PropertyDescriptor propertyDescriptor,
boolean forceField,
boolean syntheticBackingField,
@Nullable KtSuperExpression superExpression,
boolean skipAccessorsForPrivateFieldInOuterClass,
StackValue receiver
@@ -2156,7 +2171,14 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();
boolean isBackingFieldInAnotherClass = AsmUtil.isPropertyWithBackingFieldInOuterClass(propertyDescriptor);
FieldAccessorKind fieldAccessorKind = FieldAccessorKind.NORMAL;
boolean isBackingFieldInClassCompanion = AsmUtil.isPropertyWithBackingFieldInOuterClass(propertyDescriptor);
if (isBackingFieldInClassCompanion && forceField) {
fieldAccessorKind = FieldAccessorKind.IN_CLASS_COMPANION;
}
else if (syntheticBackingField && context.getParentContext().getContextDescriptor() != containingDeclaration) {
fieldAccessorKind = FieldAccessorKind.FIELD_FROM_LOCAL;
}
boolean isStaticBackingField = DescriptorUtils.isStaticDeclaration(propertyDescriptor) ||
AsmUtil.isInstancePropertyWithStaticBackingField(propertyDescriptor);
boolean isSuper = superExpression != null;
@@ -2168,28 +2190,27 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
CallableMethod callableGetter = null;
CallableMethod callableSetter = null;
CodegenContext backingFieldContext;
boolean changeOwnerOnTypeMapping;
CodegenContext backingFieldContext = getBackingFieldContext(fieldAccessorKind, containingDeclaration);
DeclarationDescriptor ownerDescriptor = containingDeclaration;
boolean skipPropertyAccessors;
if (isBackingFieldInAnotherClass && forceField) {
backingFieldContext = context.findParentContextWithDescriptor(containingDeclaration.getContainingDeclaration());
if (fieldAccessorKind != FieldAccessorKind.NORMAL) {
int flags = AsmUtil.getVisibilityForSpecialPropertyBackingField(propertyDescriptor, isDelegatedProperty);
skipPropertyAccessors = (flags & ACC_PRIVATE) == 0 || skipAccessorsForPrivateFieldInOuterClass;
if (!skipPropertyAccessors) {
//noinspection ConstantConditions
propertyDescriptor = (PropertyDescriptor) backingFieldContext.getAccessor(
propertyDescriptor, true, delegateType, superExpression
propertyDescriptor, fieldAccessorKind, delegateType, superExpression
);
changeOwnerOnTypeMapping = !(propertyDescriptor instanceof AccessorForPropertyBackingFieldInOuterClass);
}
else {
changeOwnerOnTypeMapping = true;
assert propertyDescriptor instanceof AccessorForPropertyBackingField :
"Unexpected accessor descriptor: " + propertyDescriptor;
ownerDescriptor = propertyDescriptor;
}
}
else {
backingFieldContext = context.getParentContext();
changeOwnerOnTypeMapping = isBackingFieldInAnotherClass;
if (!isBackingFieldInClassCompanion) {
ownerDescriptor = propertyDescriptor;
}
skipPropertyAccessors = forceField;
}
@@ -2226,8 +2247,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
propertyDescriptor = DescriptorUtils.unwrapFakeOverride(propertyDescriptor);
}
Type backingFieldOwner =
typeMapper.mapOwner(changeOwnerOnTypeMapping ? propertyDescriptor.getContainingDeclaration() : propertyDescriptor);
Type backingFieldOwner = typeMapper.mapOwner(ownerDescriptor);
String fieldName;
if (isExtensionProperty && !isDelegatedProperty) {
@@ -389,7 +389,7 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
KtExpression initializer = property.getDelegateExpressionOrInitializer();
assert initializer != null : "shouldInitializeProperty must return false if initializer is null";
StackValue.Property propValue = codegen.intermediateValueForProperty(propertyDescriptor, true, null, true, StackValue.LOCAL_0);
StackValue.Property propValue = codegen.intermediateValueForProperty(propertyDescriptor, true, false, null, true, StackValue.LOCAL_0);
propValue.store(codegen.gen(initializer), codegen.v);
}
@@ -613,10 +613,11 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
@Override
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
boolean forceField = AsmUtil.isPropertyWithBackingFieldInOuterClass(original) &&
!isCompanionObject(accessor.getContainingDeclaration());
boolean syntheticBackingField = accessor instanceof AccessorForPropertyBackingFieldFromLocal;
boolean forceField = (AsmUtil.isPropertyWithBackingFieldInOuterClass(original) &&
!isCompanionObject(accessor.getContainingDeclaration())) || syntheticBackingField;
StackValue property = codegen.intermediateValueForProperty(
original, forceField, accessor.getSuperCallExpression(), true, StackValue.none()
original, forceField, syntheticBackingField, accessor.getSuperCallExpression(), true, StackValue.none()
);
InstructionAdapter iv = codegen.v;
@@ -293,14 +293,14 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
@NotNull
public <D extends CallableMemberDescriptor> D getAccessor(@NotNull D descriptor, @Nullable KtSuperExpression superCallExpression) {
return getAccessor(descriptor, false, null, superCallExpression);
return getAccessor(descriptor, FieldAccessorKind.NORMAL, null, superCallExpression);
}
@SuppressWarnings("unchecked")
@NotNull
public <D extends CallableMemberDescriptor> D getAccessor(
@NotNull D possiblySubstitutedDescriptor,
boolean isForBackingFieldInOuterClass,
@NotNull FieldAccessorKind accessorKind,
@Nullable KotlinType delegateType,
@Nullable KtSuperExpression superCallExpression
) {
@@ -315,11 +315,11 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
AccessorForCallableDescriptor<?> accessor = accessors.get(key);
if (accessor != null) {
assert !isForBackingFieldInOuterClass ||
accessor instanceof AccessorForPropertyBackingFieldInOuterClass : "There is already exists accessor with isForBackingFieldInOuterClass = false in this context";
assert accessorKind == FieldAccessorKind.NORMAL ||
accessor instanceof AccessorForPropertyBackingField : "There is already exists accessor with isForBackingField = false in this context";
return (D) accessor;
}
String nameSuffix = SyntheticAccessorUtilKt.getAccessorNameSuffix(descriptor, key.superCallLabelTarget, isForBackingFieldInOuterClass);
String nameSuffix = SyntheticAccessorUtilKt.getAccessorNameSuffix(descriptor, key.superCallLabelTarget, accessorKind);
if (descriptor instanceof SimpleFunctionDescriptor) {
accessor = new AccessorForFunctionDescriptor(
(FunctionDescriptor) descriptor, contextDescriptor, superCallExpression, nameSuffix
@@ -329,13 +329,18 @@ public abstract class CodegenContext<T extends DeclarationDescriptor> {
accessor = new AccessorForConstructorDescriptor((ConstructorDescriptor) descriptor, contextDescriptor, superCallExpression);
}
else if (descriptor instanceof PropertyDescriptor) {
if (isForBackingFieldInOuterClass) {
accessor = new AccessorForPropertyBackingFieldInOuterClass((PropertyDescriptor) descriptor, contextDescriptor,
delegateType, nameSuffix);
}
else {
accessor = new AccessorForPropertyDescriptor((PropertyDescriptor) descriptor, contextDescriptor,
superCallExpression, nameSuffix);
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
switch (accessorKind) {
case NORMAL:
accessor = new AccessorForPropertyDescriptor(propertyDescriptor, contextDescriptor, superCallExpression, nameSuffix);
break;
case IN_CLASS_COMPANION:
accessor = new AccessorForPropertyBackingFieldInClassCompanion(propertyDescriptor, contextDescriptor,
delegateType, nameSuffix);
break;
case FIELD_FROM_LOCAL:
accessor = new AccessorForPropertyBackingFieldFromLocal(propertyDescriptor, contextDescriptor, nameSuffix);
break;
}
}
else {
@@ -18,14 +18,23 @@ package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.descriptors.*
fun getAccessorNameSuffix(descriptor: CallableMemberDescriptor, superCallDescriptor: ClassDescriptor?, isForBackingFieldInOuterClass: Boolean): String {
enum class FieldAccessorKind(val suffix: String) {
NORMAL("p"),
IN_CLASS_COMPANION("cp"),
FIELD_FROM_LOCAL("lp");
override fun toString() = suffix
}
fun getAccessorNameSuffix(descriptor: CallableMemberDescriptor, superCallDescriptor: ClassDescriptor?,
accessorKind: FieldAccessorKind): String {
val suffix = when (descriptor) {
is ConstructorDescriptor ->
return "will be ignored"
is SimpleFunctionDescriptor ->
descriptor.name.asString()
is PropertyDescriptor ->
descriptor.name.asString() + "$" + (if (isForBackingFieldInOuterClass) "cp" else "p")
descriptor.name.asString() + "$" + accessorKind
else ->
throw UnsupportedOperationException("Do not know how to create accessor for descriptor " + descriptor)
}