Inline get/setValues for local delegated properties
This commit is contained in:
@@ -2330,16 +2330,17 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
return lookupCapturedValueInConstructorParameters(descriptor);
|
||||
}
|
||||
|
||||
return lookupInContext(descriptor, StackValue.LOCAL_0, state, false, context);
|
||||
return lookupInContext(descriptor, StackValue.LOCAL_0, state, false, context, this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
StackValue lookupInContext(
|
||||
static StackValue lookupInContext(
|
||||
@NotNull DeclarationDescriptor descriptor,
|
||||
@NotNull StackValue prefix,
|
||||
@NotNull GenerationState state,
|
||||
boolean ignoreNoOuter,
|
||||
@NotNull CodegenContext context
|
||||
@NotNull CodegenContext context,
|
||||
@Nullable ExpressionCodegen codegen
|
||||
) {
|
||||
StackValue value = context.lookupInContext(descriptor, prefix, state, ignoreNoOuter);
|
||||
if(!isDelegatedLocalVariable(descriptor) || value == null) {
|
||||
@@ -2350,7 +2351,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
VariableDescriptor metadata = getDelegatedLocalVariableMetadata((VariableDescriptor) descriptor, state.getBindingContext());
|
||||
StackValue metadataValue = context.lookupInContext(metadata, prefix, state, ignoreNoOuter);
|
||||
assert metadataValue != null : "Metadata stack value should be non-null for local delegated property";
|
||||
return delegatedVariableValue(value, metadataValue, (VariableDescriptorWithAccessors) descriptor, state.getBindingContext(),
|
||||
//required for ImplementationBodyCodegen.lookupConstructorExpressionsInClosureIfPresent
|
||||
if (codegen == null) return null;
|
||||
return codegen.delegatedVariableValue(value, metadataValue, (VariableDescriptorWithAccessors) descriptor, state.getBindingContext(),
|
||||
state.getTypeMapper());
|
||||
}
|
||||
|
||||
@@ -4477,22 +4480,13 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static StackValue.Delegate delegatedVariableValue(
|
||||
private StackValue.Delegate delegatedVariableValue(
|
||||
@NotNull StackValue delegateValue,
|
||||
@NotNull StackValue metadataValue,
|
||||
@NotNull 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);
|
||||
return StackValue.delegate(typeMapper.mapType(variableDescriptor.getType()), delegateValue, metadataValue, variableDescriptor, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1256,7 +1256,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
|
||||
|
||||
private void lookupInContext(@NotNull DeclarationDescriptor toLookup) {
|
||||
ExpressionCodegen.lookupInContext(toLookup, StackValue.LOCAL_0, state, true, context);
|
||||
ExpressionCodegen.lookupInContext(toLookup, StackValue.LOCAL_0, state, true, context, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,6 +35,8 @@ import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor;
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor;
|
||||
import org.jetbrains.kotlin.psi.KtExpression;
|
||||
import org.jetbrains.kotlin.psi.ValueArgument;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor;
|
||||
import org.jetbrains.kotlin.resolve.annotations.AnnotationUtilKt;
|
||||
@@ -154,13 +156,13 @@ public abstract class StackValue {
|
||||
|
||||
@NotNull
|
||||
public static Delegate delegate(
|
||||
Type type,
|
||||
@NotNull Type type,
|
||||
@NotNull StackValue delegateValue,
|
||||
@NotNull StackValue metadataValue,
|
||||
@NotNull CallableMethod getter,
|
||||
@Nullable CallableMethod setter
|
||||
@NotNull VariableDescriptorWithAccessors variableDescriptor,
|
||||
@NotNull ExpressionCodegen codegen
|
||||
) {
|
||||
return new Delegate(type, delegateValue, metadataValue, getter, setter);
|
||||
return new Delegate(type, delegateValue, metadataValue, variableDescriptor, codegen);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -660,62 +662,72 @@ 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;
|
||||
public static class Delegate extends StackValue {
|
||||
@NotNull
|
||||
private final StackValue delegateValue;
|
||||
@NotNull
|
||||
private final StackValue metadataValue;
|
||||
@NotNull
|
||||
private final VariableDescriptorWithAccessors variableDescriptor;
|
||||
@NotNull
|
||||
private final ExpressionCodegen codegen;
|
||||
|
||||
private Delegate(
|
||||
Type type,
|
||||
@NotNull Type type,
|
||||
@NotNull StackValue delegateValue,
|
||||
@NotNull StackValue metadataValue,
|
||||
@NotNull CallableMethod getter,
|
||||
@Nullable CallableMethod setter
|
||||
@NotNull VariableDescriptorWithAccessors variableDescriptor,
|
||||
@NotNull ExpressionCodegen codegen
|
||||
) {
|
||||
super(type, false, false, new Receiver(OBJECT_TYPE, delegateValue, constant(null, OBJECT_TYPE), metadataValue) {
|
||||
@Override
|
||||
public void dup(@NotNull InstructionAdapter v, boolean withReceiver) {
|
||||
//UGLY HACK
|
||||
//TODO rethink Receiver/StackValue concept
|
||||
//We need to make duplication of delegated var, owner (null) and property metadata: dup 3.
|
||||
//As HACK Type.LONG_TYPE and Type.INT_TYPE passed to dup util to simulate dup 3.
|
||||
AsmUtil.dup(v, Type.LONG_TYPE, Type.INT_TYPE);
|
||||
}
|
||||
}, false);
|
||||
super(type);
|
||||
this.delegateValue = delegateValue;
|
||||
this.metadataValue = metadataValue;
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
this.variableDescriptor = variableDescriptor;
|
||||
this.codegen = codegen;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public StackValue getMetadataValue() {
|
||||
return metadataValue;
|
||||
|
||||
private ResolvedCall<FunctionDescriptor> getResolvedCall(boolean isGetter) {
|
||||
BindingContext bindingContext = codegen.getState().getBindingContext();
|
||||
VariableAccessorDescriptor accessor = isGetter ? variableDescriptor.getGetter(): variableDescriptor.getSetter();
|
||||
assert accessor != null : "Accessor descriptor for delegated local property should be present " + variableDescriptor;
|
||||
ResolvedCall<FunctionDescriptor> resolvedCall = bindingContext.get(BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, accessor);
|
||||
assert resolvedCall != null : "Resolve call should be recorded for delegate call " + variableDescriptor;
|
||||
return resolvedCall;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putSelector(@NotNull Type type, @NotNull InstructionAdapter v) {
|
||||
getter.genInvokeInstruction(v);
|
||||
coerce(getter.getReturnType(), type, v);
|
||||
ResolvedCall<FunctionDescriptor> resolvedCall = getResolvedCall(true);
|
||||
List<? extends ValueArgument> arguments = resolvedCall.getCall().getValueArguments();
|
||||
assert arguments.size() == 2 : "Resolved call for 'getValue' should have 2 arguments, but was " +
|
||||
arguments.size() + ": " + resolvedCall;
|
||||
|
||||
codegen.tempVariables.put(arguments.get(0).asElement(), StackValue.constant(null, OBJECT_TYPE));
|
||||
codegen.tempVariables.put(arguments.get(1).asElement(), metadataValue);
|
||||
StackValue lastValue = codegen.invokeFunction(resolvedCall, delegateValue);
|
||||
lastValue.put(type, v);
|
||||
|
||||
codegen.tempVariables.remove(arguments.get(0).asElement());
|
||||
codegen.tempVariables.remove(arguments.get(1).asElement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeSelector(@NotNull Type topOfStackType, @NotNull InstructionAdapter v) {
|
||||
assert setter != null : "";
|
||||
coerceFrom(topOfStackType, v);
|
||||
setter.genInvokeInstruction(v);
|
||||
}
|
||||
public void store(@NotNull StackValue rightSide, @NotNull InstructionAdapter v, boolean skipReceiver) {
|
||||
ResolvedCall<FunctionDescriptor> resolvedCall = getResolvedCall(false);
|
||||
List<? extends ValueArgument> arguments = resolvedCall.getCall().getValueArguments();
|
||||
assert arguments.size() == 3 : "Resolved call for 'setValue' should have 3 arguments, but was " +
|
||||
arguments.size() + ": " + resolvedCall;
|
||||
|
||||
@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);
|
||||
codegen.tempVariables.put(arguments.get(0).asElement(), StackValue.constant(null, OBJECT_TYPE));
|
||||
codegen.tempVariables.put(arguments.get(1).asElement(), metadataValue);
|
||||
codegen.tempVariables.put(arguments.get(2).asElement(), rightSide);
|
||||
StackValue lastValue = codegen.invokeFunction(resolvedCall, delegateValue);
|
||||
lastValue.put(Type.VOID_TYPE, v);
|
||||
|
||||
codegen.tempVariables.remove(arguments.get(0).asElement());
|
||||
codegen.tempVariables.remove(arguments.get(1).asElement());
|
||||
codegen.tempVariables.remove(arguments.get(2).asElement());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package foo
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class Delegate {
|
||||
inline operator fun getValue(t: Any?, p: KProperty<*>): String = p.name
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val OK: String by Delegate()
|
||||
return OK
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package foo
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class Delegate {
|
||||
var inner = 1
|
||||
inline operator fun getValue(t: Any?, p: KProperty<*>): Int = inner
|
||||
inline 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"
|
||||
}
|
||||
@@ -5032,30 +5032,6 @@ 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");
|
||||
@@ -5170,18 +5146,6 @@ 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("privateSetterKPropertyIsNotMutable.kt")
|
||||
public void testPrivateSetterKPropertyIsNotMutable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/privateSetterKPropertyIsNotMutable.kt");
|
||||
@@ -5274,12 +5238,60 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/delegatedProperty/local"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedLocalVal.kt")
|
||||
public void testCapturedLocalVal() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/local/capturedLocalVal.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedLocalValNoInline.kt")
|
||||
public void testCapturedLocalValNoInline() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/local/capturedLocalValNoInline.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedLocalVar.kt")
|
||||
public void testCapturedLocalVar() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/local/capturedLocalVar.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("capturedLocalVarNoInline.kt")
|
||||
public void testCapturedLocalVarNoInline() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/local/capturedLocalVarNoInline.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("iinc.kt")
|
||||
public void testIinc() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/local/iinc.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inlineGetValue.kt")
|
||||
public void testInlineGetValue() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/local/inlineGetValue.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inlineOperators.kt")
|
||||
public void testInlineOperators() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/local/inlineOperators.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localVal.kt")
|
||||
public void testLocalVal() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/local/localVal.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localVar.kt")
|
||||
public void testLocalVar() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/local/localVar.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("plusAssign.kt")
|
||||
public void testPlusAssign() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/local/plusAssign.kt");
|
||||
|
||||
Reference in New Issue
Block a user