Inline get/setValues for local delegated properties

This commit is contained in:
Mikhael Bogdanov
2016-06-14 13:03:14 +03:00
parent 6f761d4e7d
commit 679f53b449
12 changed files with 144 additions and 95 deletions
@@ -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");