Delegated Properties: Code generation for local properties (JVM)

This commit is contained in:
Dotlin
2015-09-30 02:33:45 +03:00
committed by Mikhael Bogdanov
parent fa523b9af4
commit 4c223845b6
22 changed files with 563 additions and 78 deletions
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl;
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
import org.jetbrains.kotlin.load.java.JvmAbi;
@@ -423,11 +424,16 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
for (DeclarationDescriptor descriptor : closure.getCaptureVariables().keySet()) {
if (descriptor instanceof VariableDescriptor && !(descriptor instanceof PropertyDescriptor)) {
Type sharedVarType = typeMapper.getSharedVarType(descriptor);
Type type = sharedVarType != null
? sharedVarType
: typeMapper.mapType((VariableDescriptor) descriptor);
Type type = typeMapper.getSharedVarType(descriptor);
if (type == null && descriptor instanceof LocalVariableDescriptor) {
KotlinType delegateType = JvmCodegenUtil.getPropertyDelegateType((LocalVariableDescriptor) descriptor, bindingContext);
if (delegateType != null) {
type = typeMapper.mapType(delegateType);
}
}
if (type == null) {
type = typeMapper.mapType((VariableDescriptor) descriptor);
}
args.add(FieldInfo.createForHiddenField(ownerType, type, "$" + descriptor.getName().asString()));
}
else if (ExpressionTypingUtils.isLocalFunction(descriptor)) {
@@ -47,6 +47,7 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.codegen.when.SwitchCodegen;
import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor;
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils;
import org.jetbrains.kotlin.diagnostics.Errors;
@@ -58,6 +59,7 @@ import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingContextUtils;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
@@ -74,6 +76,7 @@ import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluat
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluatorKt;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
@@ -1612,7 +1615,20 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@NotNull
private Type getVariableType(@NotNull VariableDescriptor variableDescriptor) {
Type sharedVarType = typeMapper.getSharedVarType(variableDescriptor);
return sharedVarType != null ? sharedVarType : asmType(variableDescriptor.getType());
return sharedVarType != null ? sharedVarType : getVariableTypeNoSharing(variableDescriptor);
}
@NotNull
private Type getVariableTypeNoSharing(@NotNull VariableDescriptor variableDescriptor) {
KotlinType varType = isDelegatedLocalVariable(variableDescriptor)
? JvmCodegenUtil.getPropertyDelegateType((VariableDescriptorWithAccessors) variableDescriptor, bindingContext)
: variableDescriptor.getType();
//noinspection ConstantConditions
return asmType(varType);
}
private static boolean isDelegatedLocalVariable(@NotNull DeclarationDescriptor descriptor) {
return descriptor instanceof LocalVariableDescriptor && ((VariableDescriptorWithAccessors) descriptor).getGetter() != null;
}
private static boolean isSharedVarType(@NotNull Type type) {
@@ -1647,6 +1663,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
Type type = getVariableType(variableDescriptor);
int index = myFrameMap.enter(variableDescriptor, type);
if (isDelegatedLocalVariable(variableDescriptor)) {
VariableDescriptor metadataVariableDescriptor = bindingContext.get(LOCAL_VARIABLE_PROPERTY_METADATA, variableDescriptor);
myFrameMap.enter(metadataVariableDescriptor, AsmTypes.K_PROPERTY0_TYPE);
}
if (isSharedVarType(type)) {
markLineNumber(statement, false);
v.anew(type);
@@ -1693,6 +1714,11 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
leaveTasks.add(new Function<StackValue, Void>() {
@Override
public Void fun(StackValue answer) {
if (isDelegatedLocalVariable(variableDescriptor)) {
VariableDescriptor metadataVariableDescriptor = bindingContext.get(LOCAL_VARIABLE_PROPERTY_METADATA, variableDescriptor);
myFrameMap.leave(metadataVariableDescriptor);
}
int index = myFrameMap.leave(variableDescriptor);
if (isSharedVarType(type)) {
@@ -2112,18 +2138,20 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return StackValue.shared(parameterOffsetInConstructor, parentResult.type);
}
return StackValue.local(parameterOffsetInConstructor, parentResult.type);
return adjustVariableValue(StackValue.local(parameterOffsetInConstructor, parentResult.type), descriptor);
}
private StackValue stackValueForLocal(DeclarationDescriptor descriptor, int index) {
if (descriptor instanceof VariableDescriptor) {
VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor;
Type sharedVarType = typeMapper.getSharedVarType(descriptor);
KotlinType outType = ((VariableDescriptor) descriptor).getType();
Type varType = getVariableTypeNoSharing(variableDescriptor);
if (sharedVarType != null) {
return StackValue.shared(index, asmType(outType));
return StackValue.shared(index, varType);
}
else {
return StackValue.local(index, asmType(outType));
return adjustVariableValue(StackValue.local(index, varType), variableDescriptor);
}
}
else {
@@ -2140,19 +2168,6 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return myFrameMap.getIndex(descriptor);
}
@Nullable
private static KotlinType getPropertyDelegateType(@NotNull PropertyDescriptor descriptor, @NotNull BindingContext bindingContext) {
PropertyGetterDescriptor getter = descriptor.getGetter();
if (getter != null) {
Call call = bindingContext.get(DELEGATED_PROPERTY_CALL, getter);
if (call != null) {
assert call.getExplicitReceiver() != null : "No explicit receiver for call:" + call;
return ((ReceiverValue) call.getExplicitReceiver()).getType();
}
}
return null;
}
@NotNull
public StackValue.Property intermediateValueForProperty(
@NotNull PropertyDescriptor propertyDescriptor,
@@ -2204,7 +2219,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
boolean isSuper = superCallTarget != null;
boolean isExtensionProperty = propertyDescriptor.getExtensionReceiverParameter() != null;
KotlinType delegateType = getPropertyDelegateType(propertyDescriptor, bindingContext);
KotlinType delegateType = JvmCodegenUtil.getPropertyDelegateType(propertyDescriptor, bindingContext);
boolean isDelegatedProperty = delegateType != null;
CallableMethod callableGetter = null;
@@ -2828,7 +2843,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression);
if (variableDescriptor != null) {
return generatePropertyReference(expression, variableDescriptor, resolvedCall);
return generatePropertyReference(expression, variableDescriptor,
(VariableDescriptor) resolvedCall.getResultingDescriptor(),
resolvedCall.getDispatchReceiver());
}
throw new UnsupportedOperationException("Unsupported callable reference expression: " + expression.getText());
@@ -2838,7 +2855,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
private StackValue generatePropertyReference(
@NotNull KtElement element,
@NotNull VariableDescriptor variableDescriptor,
@NotNull ResolvedCall<?> resolvedCall
@NotNull VariableDescriptor target,
@Nullable ReceiverValue dispatchReceiver
) {
ClassDescriptor classDescriptor = CodegenBinding.anonymousClassForCallable(bindingContext, variableDescriptor);
@@ -2850,7 +2868,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
PropertyReferenceCodegen codegen = new PropertyReferenceCodegen(
state, parentCodegen, context.intoAnonymousClass(classDescriptor, this, OwnerKind.IMPLEMENTATION),
element, classBuilder, resolvedCall
element, classBuilder, target, dispatchReceiver
);
codegen.generate();
@@ -3371,10 +3389,16 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@Override
public StackValue visitProperty(@NotNull KtProperty property, StackValue receiver) {
KtExpression initializer = property.getInitializer();
if (initializer == null) {
return StackValue.none();
KtExpression delegateExpression = property.getDelegateExpression();
if (initializer != null) {
assert delegateExpression == null : PsiUtilsKt.getElementTextWithContext(property);
initializeLocalVariable(property, gen(initializer));
}
initializeLocalVariable(property, gen(initializer));
else if (delegateExpression != null) {
initializeLocalVariable(property, gen(delegateExpression));
}
return StackValue.none();
}
@@ -3412,11 +3436,27 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return StackValue.none();
}
@Nullable
private StackValue getVariableMetadataValue(VariableDescriptor variableDescriptor) {
VariableDescriptor metadataVariableDescriptor = bindingContext.get(LOCAL_VARIABLE_PROPERTY_METADATA, variableDescriptor);
return metadataVariableDescriptor != null ? findLocalOrCapturedValue(metadataVariableDescriptor) : null;
}
@NotNull
private StackValue adjustVariableValue(@NotNull StackValue varValue, DeclarationDescriptor descriptor) {
if (!isDelegatedLocalVariable(descriptor)) return varValue;
VariableDescriptorWithAccessors variableDescriptor = (VariableDescriptorWithAccessors) descriptor;
StackValue metadataValue = getVariableMetadataValue(variableDescriptor);
assert metadataValue != null : variableDescriptor;
return JvmCodegenUtil.delegatedVariableValue(varValue, metadataValue, variableDescriptor, bindingContext, typeMapper);
}
private void initializeLocalVariable(
@NotNull KtVariableDeclaration variableDeclaration,
@NotNull StackValue initializer
) {
VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, variableDeclaration);
LocalVariableDescriptor variableDescriptor = (LocalVariableDescriptor) bindingContext.get(VARIABLE, variableDeclaration);
if (KtPsiUtil.isScriptDeclaration(variableDeclaration)) {
return;
@@ -3430,7 +3470,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
Type sharedVarType = typeMapper.getSharedVarType(variableDescriptor);
assert variableDescriptor != null;
Type varType = asmType(variableDescriptor.getType());
Type varType = getVariableTypeNoSharing(variableDescriptor);
StackValue storeTo = sharedVarType == null ? StackValue.local(index, varType) : StackValue.shared(index, varType);
@@ -3440,6 +3480,37 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
markLineNumber(variableDeclaration, false);
storeTo.storeSelector(initializer.type, v);
StackValue metadataValue = getVariableMetadataValue(variableDescriptor);
if (metadataValue != null) {
initializePropertyMetadata((KtProperty) variableDeclaration, variableDescriptor, metadataValue);
invokePropertyDelegatedOnLocalVar(variableDescriptor, storeTo, metadataValue);
}
}
private void invokePropertyDelegatedOnLocalVar(
@NotNull LocalVariableDescriptor variableDescriptor,
@NotNull StackValue delegateValue,
@NotNull StackValue metadataValue
) {
ResolvedCall<FunctionDescriptor> pdResolvedCall =
bindingContext.get(BindingContext.DELEGATED_PROPERTY_PD_RESOLVED_CALL, variableDescriptor);
if (pdResolvedCall == null) return;
tempVariables.put(pdResolvedCall.getCall().getValueArguments().get(0).asElement(), metadataValue);
invokeFunction(pdResolvedCall, delegateValue).put(Type.VOID_TYPE, v);
}
private void initializePropertyMetadata(
@NotNull KtProperty variable,
@NotNull LocalVariableDescriptor variableDescriptor,
@NotNull StackValue metadataVar
) {
//noinspection ConstantConditions
StackValue value =
generatePropertyReference(variable.getDelegate(), variableDescriptor, variableDescriptor, null);
value.put(K_PROPERTY0_TYPE, v);
metadataVar.storeSelector(K_PROPERTY0_TYPE, v);
}
@NotNull
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor;
import org.jetbrains.kotlin.load.kotlin.ModuleMapping;
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityUtilsKt;
import org.jetbrains.kotlin.psi.Call;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.psi.KtFunction;
import org.jetbrains.kotlin.psi.codeFragmentUtil.CodeFragmentUtilKt;
@@ -40,14 +41,17 @@ import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
import org.jetbrains.kotlin.types.KotlinType;
import java.io.File;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.LOCAL_VARIABLE_DELEGATE;
import static org.jetbrains.kotlin.descriptors.Modality.ABSTRACT;
import static org.jetbrains.kotlin.descriptors.Modality.FINAL;
import static org.jetbrains.kotlin.resolve.jvm.annotations.AnnotationUtilKt.hasJvmFieldAnnotation;
import static org.jetbrains.kotlin.resolve.BindingContext.DELEGATED_PROPERTY_CALL;
public class JvmCodegenUtil {
@@ -223,4 +227,40 @@ public class JvmCodegenUtil {
if (!(descriptor instanceof JavaPropertyDescriptor)) return false;
return descriptor.isConst();
}
@Nullable
public static KotlinType getPropertyDelegateType(
@NotNull VariableDescriptorWithAccessors descriptor,
@NotNull BindingContext bindingContext
) {
VariableAccessorDescriptor getter = descriptor.getGetter();
if (getter != null) {
Call call = bindingContext.get(DELEGATED_PROPERTY_CALL, getter);
if (call != null) {
assert call.getExplicitReceiver() != null : "No explicit receiver for call:" + call;
return ((ReceiverValue) call.getExplicitReceiver()).getType();
}
}
return null;
}
@NotNull
public static StackValue.Delegate delegatedVariableValue(
@NotNull StackValue delegateValue,
@NotNull StackValue metadataValue,
VariableDescriptorWithAccessors variableDescriptor,
@NotNull BindingContext bindingContext,
@NotNull KotlinTypeMapper typeMapper
) {
VariableDescriptor delegateVariableDescriptor = bindingContext.get(LOCAL_VARIABLE_DELEGATE, variableDescriptor);
assert delegateVariableDescriptor != null : variableDescriptor;
VariableAccessorDescriptor getterDescriptor = variableDescriptor.getGetter();
VariableAccessorDescriptor setterDescriptor = variableDescriptor.getSetter();
//noinspection ConstantConditions
CallableMethod getterMethod = typeMapper.mapToCallableMethod(getterDescriptor, false);
CallableMethod setterMethod = setterDescriptor != null ? typeMapper.mapToCallableMethod(setterDescriptor, false) : null;
return StackValue.delegate(typeMapper.mapType(variableDescriptor.getType()), delegateValue, metadataValue, getterMethod, setterMethod);
}
}
@@ -20,6 +20,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.builtins.DefaultBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl;
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor;
import org.jetbrains.kotlin.name.FqName;
@@ -39,6 +40,8 @@ public class JvmRuntimeTypes {
private final ClassDescriptor functionReference;
private final List<ClassDescriptor> propertyReferences;
private final List<ClassDescriptor> mutablePropertyReferences;
private final ClassDescriptor localVariableReference;
private final ClassDescriptor mutableLocalVariableReference;
public JvmRuntimeTypes() {
ModuleDescriptorImpl module = TargetPlatformKt.createModule(
@@ -47,9 +50,12 @@ public class JvmRuntimeTypes {
LockBasedStorageManager.NO_LOCKS,
DefaultBuiltIns.getInstance());
PackageFragmentDescriptor kotlinJvmInternal = new MutablePackageFragmentDescriptor(module, new FqName("kotlin.jvm.internal"));
PackageFragmentDescriptor kotlinReflectJvmInternal = new MutablePackageFragmentDescriptor(module, new FqName("kotlin.reflect.jvm.internal"));
this.lambda = createClass(kotlinJvmInternal, "Lambda");
this.functionReference = createClass(kotlinJvmInternal, "FunctionReference");
this.localVariableReference = createClass(kotlinReflectJvmInternal, "LocalVariableReference");
this.mutableLocalVariableReference = createClass(kotlinReflectJvmInternal, "MutableLocalVariableReference");
this.propertyReferences = new ArrayList<ClassDescriptor>(3);
this.mutablePropertyReferences = new ArrayList<ClassDescriptor>(3);
@@ -110,7 +116,11 @@ public class JvmRuntimeTypes {
}
@NotNull
public KotlinType getSupertypeForPropertyReference(@NotNull PropertyDescriptor descriptor) {
public KotlinType getSupertypeForPropertyReference(@NotNull VariableDescriptorWithAccessors descriptor) {
if (descriptor instanceof LocalVariableDescriptor) {
return (descriptor.isVar() ? mutableLocalVariableReference : localVariableReference).getDefaultType();
}
int arity = (descriptor.getExtensionReceiverParameter() != null ? 1 : 0) +
(descriptor.getDispatchReceiverParameter() != null ? 1 : 0);
return (descriptor.isVar() ? mutablePropertyReferences : propertyReferences).get(arity).getDefaultType();
@@ -21,17 +21,18 @@ import org.jetbrains.kotlin.codegen.context.ClassContext
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.PropertyImportedFromObject
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.utils.sure
import org.jetbrains.org.objectweb.asm.Opcodes.*
import org.jetbrains.org.objectweb.asm.Type
@@ -44,13 +45,13 @@ class PropertyReferenceCodegen(
context: ClassContext,
expression: KtElement,
classBuilder: ClassBuilder,
resolvedCall: ResolvedCall<*>
private val target: VariableDescriptor,
dispatchReceiver: ReceiverValue?
) : MemberCodegen<KtElement>(state, parentCodegen, context, expression, classBuilder) {
private val classDescriptor = context.contextDescriptor
private val asmType = typeMapper.mapClass(classDescriptor)
private val target = resolvedCall.resultingDescriptor as VariableDescriptor
private val dispatchReceiverType = resolvedCall.dispatchReceiver?.type
private val dispatchReceiverType = dispatchReceiver?.type
private val extensionReceiverType = target.extensionReceiverParameter?.type
private val receiverCount =
@@ -85,8 +86,10 @@ class PropertyReferenceCodegen(
invokespecial(superAsmType.internalName, "<init>", "()V", false)
}
generateMethod("property reference getOwner", ACC_PUBLIC, method("getOwner", K_DECLARATION_CONTAINER_TYPE)) {
ClosureCodegen.generateCallableReferenceDeclarationContainer(this, target, state)
if (target !is LocalVariableDescriptor) {
generateMethod("property reference getOwner", ACC_PUBLIC, method("getOwner", K_DECLARATION_CONTAINER_TYPE)) {
ClosureCodegen.generateCallableReferenceDeclarationContainer(this, target, state)
}
}
generateMethod("property reference getName", ACC_PUBLIC, method("getName", JAVA_STRING_TYPE)) {
@@ -94,7 +97,7 @@ class PropertyReferenceCodegen(
}
generateMethod("property reference getSignature", ACC_PUBLIC, method("getSignature", JAVA_STRING_TYPE)) {
aconst(getPropertyReferenceSignature(target as PropertyDescriptor, state))
aconst(getPropertyReferenceSignature(target as VariableDescriptorWithAccessors, state))
}
generateAccessors()
@@ -125,7 +128,10 @@ class PropertyReferenceCodegen(
StackValue.local(index + 1, OBJECT_TYPE).put(typeMapper.mapType(type), this)
}
val value = fakeCodegen.intermediateValueForProperty(target as PropertyDescriptor, false, null, StackValue.none())
val value = if (target is LocalVariableDescriptor) {
fakeCodegen.findLocalOrCapturedValue(target)!!
}
else fakeCodegen.intermediateValueForProperty(target as PropertyDescriptor, false, null, StackValue.none())
accessorBody(value)
}
@@ -168,9 +174,9 @@ class PropertyReferenceCodegen(
companion object {
@JvmStatic
fun getPropertyReferenceSignature(property: PropertyDescriptor, state: GenerationState): String {
fun getPropertyReferenceSignature(property: VariableDescriptorWithAccessors, state: GenerationState): String {
val getter =
property.getter ?: DescriptorFactory.createDefaultGetter(property, Annotations.EMPTY).apply {
property.getter ?: DescriptorFactory.createDefaultGetter(property as PropertyDescriptor, Annotations.EMPTY).apply {
initialize(property.type)
}
@@ -152,6 +152,17 @@ public abstract class StackValue {
return new Local(index, type);
}
@NotNull
public static Delegate delegate(
Type type,
@NotNull StackValue delegateValue,
@NotNull StackValue metadataValue,
@NotNull CallableMethod getter,
@Nullable CallableMethod setter
) {
return new Delegate(type, delegateValue, metadataValue, getter, setter);
}
@NotNull
public static StackValue shared(int index, @NotNull Type type) {
return new Shared(index, type);
@@ -245,17 +256,8 @@ public abstract class StackValue {
@NotNull
public static StackValue changeReceiverForFieldAndSharedVar(@NotNull StackValueWithSimpleReceiver stackValue, @Nullable StackValue newReceiver) {
//TODO static check
if (newReceiver != null) {
if (!stackValue.isStaticPut) {
if (stackValue instanceof Field) {
return field((Field) stackValue, newReceiver);
}
else if (stackValue instanceof FieldForSharedVar) {
return fieldForSharedVar((FieldForSharedVar) stackValue, newReceiver);
}
}
}
return stackValue;
if (newReceiver == null || stackValue.isStaticPut) return stackValue;
return stackValue.changeReceiver(newReceiver);
}
@NotNull
@@ -657,6 +659,56 @@ public abstract class StackValue {
}
}
public static class Delegate extends StackValueWithSimpleReceiver {
@NotNull private final StackValue delegateValue;
@NotNull private final StackValue metadataValue;
@NotNull private final CallableMethod getter;
@Nullable private final CallableMethod setter;
private Delegate(
Type type,
@NotNull StackValue delegateValue,
@NotNull StackValue metadataValue,
@NotNull CallableMethod getter,
@Nullable CallableMethod setter
) {
super(type, false, false, new Receiver(OBJECT_TYPE, delegateValue, constant(null, OBJECT_TYPE), metadataValue), false);
this.delegateValue = delegateValue;
this.metadataValue = metadataValue;
this.getter = getter;
this.setter = setter;
}
@NotNull
public StackValue getMetadataValue() {
return metadataValue;
}
@Override
public void putSelector(@NotNull Type type, @NotNull InstructionAdapter v) {
getter.genInvokeInstruction(v);
coerce(getter.getReturnType(), type, v);
}
@Override
public void storeSelector(@NotNull Type topOfStackType, @NotNull InstructionAdapter v) {
assert setter != null : "";
coerceFrom(topOfStackType, v);
setter.genInvokeInstruction(v);
}
@Override
protected StackValueWithSimpleReceiver changeReceiver(@NotNull StackValue newReceiver) {
StackValue newDelegateValue = delegateValue instanceof StackValueWithSimpleReceiver
? ((StackValueWithSimpleReceiver) delegateValue).changeReceiver(newReceiver)
: delegateValue;
StackValue newMetadataValue = metadataValue instanceof StackValueWithSimpleReceiver
? ((StackValueWithSimpleReceiver) metadataValue).changeReceiver(newReceiver)
: metadataValue;
return delegate(type, newDelegateValue, newMetadataValue, getter, setter);
}
}
public static class OnStack extends StackValue {
public OnStack(Type type) {
super(type);
@@ -1079,6 +1131,11 @@ public abstract class StackValue {
coerceFrom(topOfStackType, v);
v.visitFieldInsn(isStaticStore ? PUTSTATIC : PUTFIELD, owner.getInternalName(), name, this.type.getDescriptor());
}
@Override
protected StackValueWithSimpleReceiver changeReceiver(@NotNull StackValue newReceiver) {
return field(this, newReceiver);
}
}
static class Property extends StackValueWithSimpleReceiver {
@@ -1297,6 +1354,11 @@ public abstract class StackValue {
coerceFrom(topOfStackType, v);
v.visitFieldInsn(PUTFIELD, sharedTypeForType(type).getInternalName(), "element", refType(type).getDescriptor());
}
@Override
protected StackValueWithSimpleReceiver changeReceiver(@NotNull StackValue newReceiver) {
return fieldForSharedVar(this, newReceiver);
}
}
private static class ThisOuter extends StackValue {
@@ -1558,6 +1620,10 @@ public abstract class StackValue {
rightSide.put(rightSide.type, v);
storeSelector(rightSide.type, v);
}
protected StackValueWithSimpleReceiver changeReceiver(@NotNull StackValue newReceiver) {
return this;
}
}
private static class ComplexReceiver extends StackValue {
@@ -23,6 +23,7 @@ import com.intellij.util.containers.Stack;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.ReflectionTypes;
import org.jetbrains.kotlin.cfg.WhenChecker;
import org.jetbrains.kotlin.codegen.*;
import org.jetbrains.kotlin.codegen.state.GenerationState;
@@ -30,6 +31,8 @@ import org.jetbrains.kotlin.codegen.state.TypeMapperUtilsKt;
import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil;
import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.fileClasses.FileClasses;
import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor;
@@ -46,7 +49,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.EnumValue;
import org.jetbrains.kotlin.resolve.constants.NullValue;
import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.org.objectweb.asm.Type;
@@ -317,12 +320,43 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
fileClassesProvider);
}
private void recordLocalVariablePropertyMetadata(LocalVariableDescriptor variableDescriptor) {
KotlinType delegateType = JvmCodegenUtil.getPropertyDelegateType(variableDescriptor, bindingContext);
if (delegateType == null) return;
LocalVariableDescriptor delegateVariableDescriptor = new LocalVariableDescriptor(
variableDescriptor.getContainingDeclaration(),
Annotations.Companion.getEMPTY(),
variableDescriptor.getName(),
delegateType,
false,
false,
SourceElement.NO_SOURCE
);
bindingTrace.record(LOCAL_VARIABLE_DELEGATE, variableDescriptor, delegateVariableDescriptor);
LocalVariableDescriptor metadataVariableDescriptor = new LocalVariableDescriptor(
variableDescriptor.getContainingDeclaration(),
Annotations.Companion.getEMPTY(),
Name.identifier(variableDescriptor.getName().asString() + "$metadata"),
ReflectionTypes.Companion.createKPropertyStarType(DescriptorUtilsKt.getModule(variableDescriptor)),
false,
false,
SourceElement.NO_SOURCE
);
bindingTrace.record(LOCAL_VARIABLE_PROPERTY_METADATA, variableDescriptor, metadataVariableDescriptor);
}
@Override
public void visitProperty(@NotNull KtProperty property) {
DeclarationDescriptor descriptor = bindingContext.get(DECLARATION_TO_DESCRIPTOR, property);
// working around a problem with shallow analysis
if (descriptor == null) return;
if (descriptor instanceof LocalVariableDescriptor) {
recordLocalVariablePropertyMetadata((LocalVariableDescriptor) descriptor);
}
String nameForClassOrPackageMember = getNameForClassOrPackageMember(descriptor);
if (nameForClassOrPackageMember != null) {
nameStack.push(nameForClassOrPackageMember);
@@ -332,11 +366,11 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
}
KtPropertyDelegate delegate = property.getDelegate();
if (delegate != null && descriptor instanceof PropertyDescriptor) {
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
if (delegate != null && descriptor instanceof VariableDescriptorWithAccessors) {
VariableDescriptorWithAccessors variableDescriptor = (VariableDescriptorWithAccessors) descriptor;
String name = inventAnonymousClassName();
KotlinType supertype = runtimeTypes.getSupertypeForPropertyReference(propertyDescriptor);
ClassDescriptor classDescriptor = recordClassForCallable(delegate, propertyDescriptor, Collections.singleton(supertype), name);
KotlinType supertype = runtimeTypes.getSupertypeForPropertyReference(variableDescriptor);
ClassDescriptor classDescriptor = recordClassForCallable(delegate, variableDescriptor, Collections.singleton(supertype), name);
recordClosure(classDescriptor, name);
}
@@ -60,6 +60,12 @@ public class CodegenBinding {
public static final WritableSlice<String, List<WhenByEnumsMapping>> MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE =
Slices.createSimpleSlice();
public static final WritableSlice<VariableDescriptor, VariableDescriptor> LOCAL_VARIABLE_DELEGATE =
Slices.createSimpleSlice();
public static final WritableSlice<VariableDescriptor, VariableDescriptor> LOCAL_VARIABLE_PROPERTY_METADATA =
Slices.createSimpleSlice();
static {
BasicWritableSlice.initSliceDebugNames(CodegenBinding.class);
}
@@ -51,10 +51,19 @@ public interface LocalLookup {
Type classType
) {
VariableDescriptor vd = (VariableDescriptor) d;
VariableDescriptor originalVariableDescriptor = vd;
boolean idx = localLookup != null && localLookup.lookupLocal(vd);
if (!idx) return null;
boolean delegatedVar = false;
VariableDescriptor delegateVariableDescriptor = state.getBindingContext().get(LOCAL_VARIABLE_DELEGATE, vd);
VariableDescriptor metadataVariableDescriptor = state.getBindingContext().get(LOCAL_VARIABLE_PROPERTY_METADATA, vd);
if (delegateVariableDescriptor != null) {
vd = delegateVariableDescriptor;
delegatedVar = true;
}
Type sharedVarType = state.getTypeMapper().getSharedVarType(vd);
Type localType = state.getTypeMapper().mapType(vd);
Type type = sharedVarType != null ? sharedVarType : localType;
@@ -71,6 +80,18 @@ public interface LocalLookup {
}
else {
innerValue = StackValue.field(type, classType, fieldName, false, thiz, vd);
if (delegatedVar) {
StackValue metadataValue = innerValue(metadataVariableDescriptor, localLookup, state, closure, classType);
assert metadataValue != null : originalVariableDescriptor;
innerValue = JvmCodegenUtil.delegatedVariableValue(
innerValue,
metadataValue,
(VariableDescriptorWithAccessors) originalVariableDescriptor,
state.getBindingContext(),
state.getTypeMapper()
);
}
enclosedValueDescriptor = new EnclosedValueDescriptor(fieldName, d, innerValue, type);
}
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.codegen.signature.AsmTypeFactory;
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor;
import org.jetbrains.kotlin.fileClasses.FileClasses;
import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo;
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
@@ -630,6 +631,13 @@ public class KotlinTypeMapper {
return new CallableMethod(owner, owner, defaultImplDesc, method, INVOKESPECIAL, null, null, null);
}
if (descriptor instanceof LocalVariableAccessorDescriptor) {
ResolvedCall<FunctionDescriptor> delegateAccessorResolvedCall =
bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, (VariableAccessorDescriptor) descriptor);
//noinspection ConstantConditions
return mapToCallableMethod(delegateAccessorResolvedCall.getResultingDescriptor(), false);
}
DeclarationDescriptor functionParent = descriptor.getOriginal().getContainingDeclaration();
FunctionDescriptor functionDescriptor = unwrapFakeOverride(descriptor.getOriginal());
@@ -1354,14 +1362,22 @@ public class KotlinTypeMapper {
if (descriptor instanceof SimpleFunctionDescriptor && descriptor.getContainingDeclaration() instanceof FunctionDescriptor) {
return asmTypeForAnonymousClass(bindingContext, (FunctionDescriptor) descriptor);
}
else if (descriptor instanceof PropertyDescriptor || descriptor instanceof FunctionDescriptor) {
if (descriptor instanceof PropertyDescriptor || descriptor instanceof FunctionDescriptor) {
ReceiverParameterDescriptor receiverParameter = ((CallableDescriptor) descriptor).getExtensionReceiverParameter();
assert receiverParameter != null : "Callable should have a receiver parameter: " + descriptor;
return StackValue.sharedTypeForType(mapType(receiverParameter.getType()));
}
else if (descriptor instanceof VariableDescriptor && isVarCapturedInClosure(bindingContext, descriptor)) {
if (descriptor instanceof VariableDescriptorWithAccessors) {
VariableDescriptorWithAccessors withAccessors = (VariableDescriptorWithAccessors) descriptor;
if (withAccessors.getGetter() != null || withAccessors.getSetter() != null) return null;
}
if (descriptor instanceof VariableDescriptor && isVarCapturedInClosure(bindingContext, descriptor)) {
return StackValue.sharedTypeForType(mapType(((VariableDescriptor) descriptor).getType()));
}
return null;
}
}
@@ -0,0 +1,14 @@
package foo
import kotlin.reflect.KProperty
inline fun <T> run(f: () -> T) = f()
class Delegate {
operator fun getValue(t: Any?, p: KProperty<*>): Int = 1
}
fun box(): String {
val prop: Int by Delegate()
return run { if (prop == 1) "OK" else "fail" }
}
@@ -0,0 +1,14 @@
package foo
import kotlin.reflect.KProperty
fun <T> run(f: () -> T) = f()
class Delegate {
operator fun getValue(t: Any?, p: KProperty<*>): Int = 1
}
fun box(): String {
val prop: Int by Delegate()
return run { if (prop == 1) "OK" else "fail" }
}
@@ -0,0 +1,20 @@
package foo
import kotlin.reflect.KProperty
inline fun <T> run(f: () -> T) = f()
class Delegate {
var inner = 1
operator fun getValue(t: Any?, p: KProperty<*>): Int = inner
operator fun setValue(t: Any?, p: KProperty<*>, i: Int) {
inner = i
}
}
fun box(): String {
var prop: Int by Delegate()
run { prop = 2 }
if (prop != 2) return "fail get"
return run { if (prop != 2) "fail set" else "OK" }
}
@@ -0,0 +1,20 @@
package foo
import kotlin.reflect.KProperty
fun <T> run(f: () -> T) = f()
class Delegate {
var inner = 1
operator fun getValue(t: Any?, p: KProperty<*>): Int = inner
operator fun setValue(t: Any?, p: KProperty<*>, i: Int) {
inner = i
}
}
fun box(): String {
var prop: Int by Delegate()
run { prop = 2 }
if (prop != 2) return "fail get"
return run { if (prop != 2) "fail set" else "OK" }
}
@@ -0,0 +1,12 @@
package foo
import kotlin.reflect.KProperty
class Delegate {
operator fun getValue(t: Any?, p: KProperty<*>): Int = 1
}
fun box(): String {
val prop: Int by Delegate()
return if (prop == 1) "OK" else "fail"
}
@@ -0,0 +1,19 @@
package foo
import kotlin.reflect.KProperty
class Delegate {
var inner = 1
operator fun getValue(t: Any?, p: KProperty<*>): Int = inner
operator fun setValue(t: Any?, p: KProperty<*>, i: Int) {
inner = i
}
}
fun box(): String {
var prop: Int by Delegate()
if (prop != 1) return "fail get"
prop = 2
if (prop != 2) return "fail set"
return "OK"
}
@@ -0,0 +1,14 @@
import kotlin.reflect.KProperty
class Delegate {
operator fun getValue(t: Any?, p: KProperty<*>): Int = 3
}
fun foo(): Int {
val prop: Int by Delegate()
return prop
}
val x = foo()
// expected: x: 3
@@ -4747,6 +4747,30 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("capturedLocalVal.kt")
public void testCapturedLocalVal() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/capturedLocalVal.kt");
doTest(fileName);
}
@TestMetadata("capturedLocalValNoInline.kt")
public void testCapturedLocalValNoInline() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/capturedLocalValNoInline.kt");
doTest(fileName);
}
@TestMetadata("capturedLocalVar.kt")
public void testCapturedLocalVar() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/capturedLocalVar.kt");
doTest(fileName);
}
@TestMetadata("capturedLocalVarNoInline.kt")
public void testCapturedLocalVarNoInline() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/capturedLocalVarNoInline.kt");
doTest(fileName);
}
@TestMetadata("castGetReturnType.kt")
public void testCastGetReturnType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/castGetReturnType.kt");
@@ -4861,6 +4885,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("localVal.kt")
public void testLocalVal() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/localVal.kt");
doTest(fileName);
}
@TestMetadata("localVar.kt")
public void testLocalVar() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/localVar.kt");
doTest(fileName);
}
@TestMetadata("privateVar.kt")
public void testPrivateVar() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/privateVar.kt");
@@ -65,6 +65,12 @@ public class ScriptCodegenTestGenerated extends AbstractScriptCodegenTest {
doTest(fileName);
}
@TestMetadata("localDelegatedProperty.kts")
public void testLocalDelegatedProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/script/localDelegatedProperty.kts");
doTest(fileName);
}
@TestMetadata("localFunction.kts")
public void testLocalFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/script/localFunction.kts");
@@ -0,0 +1,42 @@
/*
* 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 kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.name.Name
import kotlin.reflect.KCallable
import kotlin.reflect.KotlinReflectionInternalError
internal object EmptyContainerForLocal : KDeclarationContainerImpl() {
override val jClass: Class<*>
get() = fail()
override val members: Collection<KCallable<*>>
get() = fail()
override val constructorDescriptors: Collection<ConstructorDescriptor>
get() = fail()
override fun getProperties(name: Name): Collection<PropertyDescriptor> = fail()
override fun getFunctions(name: Name): Collection<FunctionDescriptor> = fail()
private fun fail(): Nothing = throw KotlinReflectionInternalError("Introspecting local functions, lambdas and anonymous functions " +
"is not yet fully supported in Kotlin reflection")
}
@@ -62,20 +62,3 @@ internal class KFunctionFromReferenceImpl(
override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?, p17: Any?, p18: Any?, p19: Any?, p20: Any?, p21: Any?, p22: Any?): Any? = reference(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22)
}
internal object EmptyContainerForLocal : KDeclarationContainerImpl() {
override val jClass: Class<*>
get() = fail()
override val members: Collection<KCallable<*>>
get() = fail()
override val constructorDescriptors: Collection<ConstructorDescriptor>
get() = fail()
override fun getProperties(name: Name): Collection<PropertyDescriptor> = fail()
override fun getFunctions(name: Name): Collection<FunctionDescriptor> = fail()
private fun fail(): Nothing = throw KotlinReflectionInternalError("Introspecting local functions, lambdas and anonymous functions " +
"is not yet fully supported in Kotlin reflection")
}
@@ -0,0 +1,29 @@
/*
* 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 kotlin.reflect.jvm.internal
import kotlin.jvm.internal.MutablePropertyReference0
import kotlin.jvm.internal.PropertyReference0
import kotlin.reflect.KDeclarationContainer
open class LocalVariableReference : PropertyReference0() {
override fun getOwner(): KDeclarationContainer = EmptyContainerForLocal
}
open class MutableLocalVariableReference : MutablePropertyReference0() {
override fun getOwner(): KDeclarationContainer = EmptyContainerForLocal
}