Support increment and '*=' operations on inline properties

This commit is contained in:
Mikhael Bogdanov
2017-01-05 14:24:40 +01:00
parent 043f3199c7
commit 5071baf970
17 changed files with 403 additions and 37 deletions
@@ -45,7 +45,11 @@ abstract class CallGenerator {
}
override fun putHiddenParams() {
override fun processAndPutHiddenParameters(justProcess: Boolean) {
}
override fun putHiddenParamsIntoLocals() {
}
@@ -121,7 +125,10 @@ abstract class CallGenerator {
stackValue: StackValue,
valueType: Type, paramIndex: Int)
abstract fun putHiddenParams()
abstract fun processAndPutHiddenParameters(justProcess: Boolean)
/*should be called if justProcess = true in processAndPutHiddenParameters*/
abstract fun putHiddenParamsIntoLocals()
abstract fun reorderArgumentsIfNeeded(actualArgsWithDeclIndex: List<ArgumentAndDeclIndex>, valueParameterTypes: List<Type>)
}
@@ -68,7 +68,6 @@ import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingContextUtils;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.CallResolverUtilKt;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.model.*;
@@ -2869,7 +2868,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
callableMethod.afterReceiverGeneration(v);
}
callGenerator.putHiddenParams();
callGenerator.processAndPutHiddenParameters(false);
List<ResolvedValueArgument> valueArguments = resolvedCall.getValueArgumentsByIndex();
assert valueArguments != null : "Failed to arrange value arguments by index: " + resolvedCall.getResultingDescriptor();
@@ -468,7 +468,7 @@ public class FunctionCodegen {
//TODO: it's best to move all below logic to 'generateLocalVariableTable' method
if (context.isInlineMethodContext() && functionFakeIndex != -1) {
mv.visitLocalVariable(
JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION + functionDescriptor.getName().asString(),
JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION + typeMapper.mapAsmMethod(functionDescriptor).getName(),
Type.INT_TYPE.getDescriptor(), null,
methodBegin, methodEnd,
functionFakeIndex);
@@ -30,8 +30,6 @@ import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods;
import org.jetbrains.kotlin.codegen.intrinsics.JavaClassProperty;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.config.LanguageFeature;
import org.jetbrains.kotlin.config.LanguageVersionSettings;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor;
import org.jetbrains.kotlin.load.java.JvmAbi;
@@ -44,7 +42,6 @@ import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature;
@@ -884,7 +881,7 @@ public abstract class StackValue {
StackValue newReceiver = StackValue.receiver(call, receiver, codegen, callable);
ArgumentGenerator generator = createArgumentGenerator();
newReceiver.put(newReceiver.type, v);
callGenerator.putHiddenParams();
callGenerator.processAndPutHiddenParameters(false);
defaultArgs = generator.generate(valueArguments, valueArguments);
}
@@ -1208,7 +1205,7 @@ public abstract class StackValue {
assert getterDescriptor != null : "Getter descriptor should be not null for " + descriptor;
if (resolvedCall != null && getterDescriptor.isInline()) {
CallGenerator callGenerator = codegen.getOrCreateCallGenerator(resolvedCall, getterDescriptor);
callGenerator.putHiddenParams();
callGenerator.processAndPutHiddenParameters(false);
callGenerator.genCall(getter, resolvedCall, false, codegen);
}
else {
@@ -1274,8 +1271,9 @@ public abstract class StackValue {
if (!skipReceiver) {
putReceiver(v, false);
}
callGenerator.putHiddenParams();
callGenerator.processAndPutHiddenParameters(true);
callGenerator.putValueIfNeeded(rightSide.type, rightSide);
callGenerator.putHiddenParamsIntoLocals();
callGenerator.genCall(setter, resolvedCall, false, codegen);
}
else {
@@ -1773,6 +1771,14 @@ public abstract class StackValue {
originalValue.putSelector(type, v);
}
@Override
public void store(@NotNull StackValue rightSide, @NotNull InstructionAdapter v, boolean skipReceiver) {
if (!skipReceiver) {
putReceiver(v, false);
}
originalValue.store(rightSide, v, true);
}
@Override
public void storeSelector(
@NotNull Type topOfStackType, @NotNull InstructionAdapter v
@@ -1791,16 +1797,7 @@ public abstract class StackValue {
}
private static StackValue complexReceiver(StackValue stackValue, boolean... isReadOperations) {
if (stackValue instanceof Property) {
ResolvedCall resolvedCall = ((Property) stackValue).resolvedCall;
if (resolvedCall != null &&
resolvedCall.getResultingDescriptor() instanceof PropertyDescriptor &&
InlineUtil.hasInlineAccessors((PropertyDescriptor) resolvedCall.getResultingDescriptor())) {
//TODO need to support
throwUnsupportedComplexOperation(resolvedCall.getResultingDescriptor());
}
}
else if (stackValue instanceof Delegate) {
if (stackValue instanceof Delegate) {
//TODO need to support
throwUnsupportedComplexOperation(((Delegate) stackValue).variableDescriptor);
}
@@ -98,6 +98,8 @@ public class InlineCodegen extends CallGenerator {
private final SourceMapper sourceMapper;
private Runnable delayedHiddenWriting;
public InlineCodegen(
@NotNull ExpressionCodegen codegen,
@NotNull GenerationState state,
@@ -414,6 +416,7 @@ public class InlineCodegen extends CallGenerator {
@NotNull
private InlineResult inlineCall(@NotNull SMAPAndMethodNode nodeAndSmap) {
assert delayedHiddenWriting == null : "'putHiddenParamsIntoLocals' should be called after 'processAndPutHiddenParameters(true)'";
DefaultSourceMapper defaultSourceMapper = codegen.getParentCodegen().getOrCreateSourceMapper();
defaultSourceMapper.setCallSiteMarker(new CallSiteMarker(codegen.getLastLineNumber()));
MethodNode node = nodeAndSmap.getNode();
@@ -693,7 +696,7 @@ public class InlineCodegen extends CallGenerator {
info = invocationParamBuilder.addNextValueParameter(type, false, remappedValue, parameterIndex);
}
recordParameterValueInLocalVal(info);
recordParameterValueInLocalVal(false, info);
}
}
@@ -732,8 +735,8 @@ public class InlineCodegen extends CallGenerator {
return true;
}
private void recordParameterValueInLocalVal(@NotNull ParameterInfo... infos) {
int[] index = new int[infos.length];
private Runnable recordParameterValueInLocalVal(boolean delayedWritingToLocals, @NotNull final ParameterInfo... infos) {
final int[] index = new int[infos.length];
for (int i = 0; i < infos.length; i++) {
ParameterInfo info = infos[i];
if (!info.isSkippedOrRemapped()) {
@@ -744,22 +747,31 @@ public class InlineCodegen extends CallGenerator {
}
}
for (int i = infos.length - 1; i >= 0; i--) {
ParameterInfo info = infos[i];
if (!info.isSkippedOrRemapped()) {
Type type = info.type;
StackValue.Local local = StackValue.local(index[i], type);
local.store(StackValue.onStack(type), codegen.v);
if (info instanceof CapturedParamInfo) {
info.setRemapValue(local);
((CapturedParamInfo) info).setSynthetic(true);
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = infos.length - 1; i >= 0; i--) {
ParameterInfo info = infos[i];
if (!info.isSkippedOrRemapped()) {
Type type = info.type;
StackValue.Local local = StackValue.local(index[i], type);
local.store(StackValue.onStack(type), codegen.v);
if (info instanceof CapturedParamInfo) {
info.setRemapValue(local);
((CapturedParamInfo) info).setSynthetic(true);
}
}
}
}
}
};
if (delayedWritingToLocals) return runnable;
runnable.run();
return null;
}
@Override
public void putHiddenParams() {
public void processAndPutHiddenParameters(boolean justProcess) {
if ((getMethodAsmFlags(functionDescriptor, context.getContextKind(), state) & Opcodes.ACC_STATIC) == 0) {
invocationParamBuilder.addNextParameter(AsmTypes.OBJECT_TYPE, false);
}
@@ -773,7 +785,8 @@ public class InlineCodegen extends CallGenerator {
invocationParamBuilder.markValueParametersStart();
List<ParameterInfo> hiddenParameters = invocationParamBuilder.buildParameters().getParameters();
recordParameterValueInLocalVal(hiddenParameters.toArray(new ParameterInfo[hiddenParameters.size()]));
delayedHiddenWriting = recordParameterValueInLocalVal(justProcess, hiddenParameters.toArray(new ParameterInfo[hiddenParameters.size()]));
}
private void leaveTemps() {
@@ -1045,4 +1058,11 @@ public class InlineCodegen extends CallGenerator {
@NotNull List<ArgumentAndDeclIndex> actualArgsWithDeclIndex, @NotNull List<? extends Type> valueParameterTypes
) {
}
@Override
public void putHiddenParamsIntoLocals() {
assert delayedHiddenWriting != null : "processAndPutHiddenParameters(true) should be called before putHiddenParamsIntoLocals";
delayedHiddenWriting.run();
delayedHiddenWriting = null;
}
}
@@ -104,7 +104,11 @@ class InlineCodegenForDefaultBody(
throw UnsupportedOperationException("Shouldn't be called")
}
override fun putHiddenParams() {
override fun processAndPutHiddenParameters(justProcess: Boolean) {
throw UnsupportedOperationException("Shouldn't be called")
}
override fun putHiddenParamsIntoLocals() {
throw UnsupportedOperationException("Shouldn't be called")
}
@@ -0,0 +1,8 @@
class A {
inline val s: Int
get() = 1
}
// METHOD : A.getS()I
// VARIABLE : NAME=this TYPE=LA; INDEX=0
// VARIABLE : NAME=$i$f$getS TYPE=I INDEX=1
@@ -0,0 +1,28 @@
// FILE: 1.kt
package test
var result = 1
inline var z: Int
get() = result
set(value) {
result = value
}
// FILE: 2.kt
import test.*
fun box(): String {
z += 1
if (z != 2) return "fail 1: $z"
var p = z++
if (result != 3) return "fail 2: $result"
if (p != 2) return "fail 3: $p"
p = ++z
if (result != 4) return "fail 4: $result"
if (p != 4) return "fail 5: $p"
return "OK"
}
@@ -0,0 +1,33 @@
// FILE: 1.kt
// IGNORE_BACKEND: JS
package test
class A {
var result = 1
inline var z: Int
get() = result
set(value) {
result = value
}
}
// FILE: 2.kt
import test.*
fun box(): String {
val a = A()
a.z += 1
if (a.z != 2) return "fail 1: $a.z"
var p = a.z++
if (a.z != 3) return "fail 2: $a.z"
if (p != 2) return "fail 3: $p"
p = ++a.z
if (a.z != 4) return "fail 4: $a.z"
if (p != 4) return "fail 5: $p"
return "OK"
}
@@ -0,0 +1,43 @@
// FILE: 1.kt
// IGNORE_BACKEND: JS
package test
class Test(var result: Int)
class A {
var result = Test(1)
inline var z: Test
get() = result
set(value) {
result = value
}
}
operator fun Test.plus(p: Int): Test {
return Test(result + p)
}
operator fun Test.inc(): Test {
return Test(result + 1)
}
// FILE: 2.kt
import test.*
fun box(): String {
val a = A()
a.z = Test(1)
a.z += 1
if (a.result.result != 2) return "fail 1: ${a.result.result}"
var p = a.z++
if (a.result.result != 3) return "fail 2: ${a.result.result}"
if (p.result != 2) return "fail 3: ${p.result}"
p = ++a.z
if (a.result.result != 4) return "fail 4: ${a.result.result}"
if (p.result != 4) return "fail 5: ${p.result}"
return "OK"
}
@@ -0,0 +1,28 @@
// FILE: 1.kt
package test
var result = 1
inline var Int.z: Int
get() = result
set(value) {
result = value + this
}
// FILE: 2.kt
import test.*
fun box(): String {
1.z += 0
if (result != 2) return "fail 1: $result"
var p = 1.z++
if (result != 4) return "fail 2: $result"
if (p != 2) return "fail 3: $p"
p = ++1.z
if (result != 6) return "fail 4: $result"
if (p != 6) return "fail 5: $p"
return "OK"
}
@@ -0,0 +1,34 @@
// FILE: 1.kt
package test
class A {
var result = 1
inline var Int.z: Int
get() = result
set(value) {
result = value + this
}
}
// FILE: 2.kt
import test.*
fun box(): String {
with(A()) {
1.z += 0
if (1.z != 2) return "fail 1: $result"
var p = 1.z++
if (1.z != 4) return "fail 2: $result"
if (p != 2) return "fail 3: $p"
p = ++1.z
if (1.z != 6) return "fail 4: $result"
if (p != 6) return "fail 5: $p"
}
return "OK"
}
@@ -0,0 +1,39 @@
// FILE: 1.kt
package test
class Test(var result: Int)
var result = Test(1)
inline var z: Test
get() = result
set(value) {
result = value
}
operator fun Test.plus(p: Int): Test {
return Test(result + p)
}
operator fun Test.inc(): Test {
return Test(result + 1)
}
// FILE: 2.kt
import test.*
fun box(): String {
z = Test(1)
z += 1
if (result.result != 2) return "fail 1: ${result.result}"
var p = z++
if (result.result != 3) return "fail 2: ${result.result}"
if (p.result != 2) return "fail 3: ${p.result}"
p = ++z
if (result.result != 4) return "fail 4: ${result.result}"
if (p.result != 4) return "fail 5: ${p.result}"
return "OK"
}
@@ -1757,6 +1757,42 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/property"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("augAssignmentAndInc.kt")
public void testAugAssignmentAndInc() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndInc.kt");
doTest(fileName);
}
@TestMetadata("augAssignmentAndIncInClass.kt")
public void testAugAssignmentAndIncInClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncInClass.kt");
doTest(fileName);
}
@TestMetadata("augAssignmentAndIncInClassViaConvention.kt")
public void testAugAssignmentAndIncInClassViaConvention() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncInClassViaConvention.kt");
doTest(fileName);
}
@TestMetadata("augAssignmentAndIncOnExtension.kt")
public void testAugAssignmentAndIncOnExtension() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncOnExtension.kt");
doTest(fileName);
}
@TestMetadata("augAssignmentAndIncOnExtensionInClass.kt")
public void testAugAssignmentAndIncOnExtensionInClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncOnExtensionInClass.kt");
doTest(fileName);
}
@TestMetadata("augAssignmentAndIncViaConvention.kt")
public void testAugAssignmentAndIncViaConvention() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncViaConvention.kt");
doTest(fileName);
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/property.kt");
@@ -72,6 +72,12 @@ public class CheckLocalVariablesTableTestGenerated extends AbstractCheckLocalVar
doTest(fileName);
}
@TestMetadata("inlineProperty.kt")
public void testInlineProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/checkLocalVariablesTable/inlineProperty.kt");
doTest(fileName);
}
@TestMetadata("inlineSimple.kt")
public void testInlineSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/checkLocalVariablesTable/inlineSimple.kt");
@@ -1757,6 +1757,42 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/property"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("augAssignmentAndInc.kt")
public void testAugAssignmentAndInc() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndInc.kt");
doTest(fileName);
}
@TestMetadata("augAssignmentAndIncInClass.kt")
public void testAugAssignmentAndIncInClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncInClass.kt");
doTest(fileName);
}
@TestMetadata("augAssignmentAndIncInClassViaConvention.kt")
public void testAugAssignmentAndIncInClassViaConvention() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncInClassViaConvention.kt");
doTest(fileName);
}
@TestMetadata("augAssignmentAndIncOnExtension.kt")
public void testAugAssignmentAndIncOnExtension() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncOnExtension.kt");
doTest(fileName);
}
@TestMetadata("augAssignmentAndIncOnExtensionInClass.kt")
public void testAugAssignmentAndIncOnExtensionInClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncOnExtensionInClass.kt");
doTest(fileName);
}
@TestMetadata("augAssignmentAndIncViaConvention.kt")
public void testAugAssignmentAndIncViaConvention() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncViaConvention.kt");
doTest(fileName);
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/property.kt");
@@ -36,6 +36,54 @@ public class PropertyAccessorsInlineTestsGenerated extends AbstractPropertyAcces
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/property"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("augAssignmentAndInc.kt")
public void testAugAssignmentAndInc() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndInc.kt");
doTest(fileName);
}
@TestMetadata("augAssignmentAndIncInClass.kt")
public void testAugAssignmentAndIncInClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncInClass.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
}
@TestMetadata("augAssignmentAndIncInClassViaConvention.kt")
public void testAugAssignmentAndIncInClassViaConvention() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncInClassViaConvention.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
}
@TestMetadata("augAssignmentAndIncOnExtension.kt")
public void testAugAssignmentAndIncOnExtension() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncOnExtension.kt");
doTest(fileName);
}
@TestMetadata("augAssignmentAndIncOnExtensionInClass.kt")
public void testAugAssignmentAndIncOnExtensionInClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncOnExtensionInClass.kt");
doTest(fileName);
}
@TestMetadata("augAssignmentAndIncViaConvention.kt")
public void testAugAssignmentAndIncViaConvention() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/augAssignmentAndIncViaConvention.kt");
doTest(fileName);
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/property/property.kt");