Fix for KT-9140: Default parameters are passed as nulls when using indexing [ ] invocation

#KT-9140 Fixed
This commit is contained in:
Michael Bogdanov
2015-11-03 15:49:20 +03:00
parent b556037915
commit 0fcaaa80df
7 changed files with 143 additions and 18 deletions
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.org.objectweb.asm.Type
import java.util.*
class DefaultCallMask(val size: Int) {
@@ -48,4 +49,12 @@ class DefaultCallMask(val size: Int) {
return masks
}
public fun generateOnStackIfNeeded(callGenerator: CallGenerator): Boolean {
val toInts = toInts()
for (mask in toInts) {
callGenerator.putValueIfNeeded(null, Type.INT_TYPE, StackValue.constant(mask, Type.INT_TYPE))
}
return toInts.isNotEmpty();
}
}
@@ -2477,18 +2477,15 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
List<ResolvedValueArgument> valueArguments = resolvedCall.getValueArgumentsByIndex();
assert valueArguments != null : "Failed to arrange value arguments by index: " + resolvedCall.getResultingDescriptor();
List<Integer> masks =
argumentGenerator.generate(valueArguments, new ArrayList<ResolvedValueArgument>(resolvedCall.getValueArguments().values()))
.toInts();
DefaultCallMask masks =
argumentGenerator.generate(valueArguments, new ArrayList<ResolvedValueArgument>(resolvedCall.getValueArguments().values()));
if (tailRecursionCodegen.isTailRecursion(resolvedCall)) {
tailRecursionCodegen.generateTailRecursion(resolvedCall);
return;
}
for (int mask : masks) {
callGenerator.putValueIfNeeded(null, Type.INT_TYPE, StackValue.constant(mask, Type.INT_TYPE));
}
boolean defaultMaskWasGenerated = masks.generateOnStackIfNeeded(callGenerator);
// Extra constructor marker argument
if (callableMethod instanceof CallableMethod) {
@@ -2500,7 +2497,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
}
callGenerator.genCall(callableMethod, resolvedCall, !masks.isEmpty(), this);
callGenerator.genCall(callableMethod, resolvedCall, defaultMaskWasGenerated, this);
}
@NotNull
@@ -3532,7 +3529,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
Callable callableMethod = resolveToCallableMethod(operationDescriptor, false);
Type[] argumentTypes = callableMethod.getParameterTypes();
StackValue collectionElementReceiver = createCollectionElementReceiver(
StackValue.CollectionElementReceiver collectionElementReceiver = createCollectionElementReceiver(
expression, receiver, operationDescriptor, isGetter, resolvedGetCall, resolvedSetCall, callable
);
@@ -3542,7 +3539,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
@NotNull
private StackValue createCollectionElementReceiver(
private StackValue.CollectionElementReceiver createCollectionElementReceiver(
@NotNull KtArrayAccessExpression expression,
@NotNull StackValue receiver,
@NotNull FunctionDescriptor operationDescriptor,
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.codegen;
import com.intellij.psi.tree.IElementType;
import kotlin.ArraysKt;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.Contract;
@@ -35,6 +36,7 @@ import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.ImportedFromObjectCallableDescriptor;
import org.jetbrains.kotlin.resolve.annotations.AnnotationUtilKt;
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;
@@ -201,7 +203,7 @@ public abstract class StackValue {
@NotNull
public static StackValue collectionElement(
StackValue collectionElementReceiver,
CollectionElementReceiver collectionElementReceiver,
Type type,
ResolvedCall<FunctionDescriptor> getter,
ResolvedCall<FunctionDescriptor> setter,
@@ -769,11 +771,12 @@ public abstract class StackValue {
private final boolean isGetter;
private final ExpressionCodegen codegen;
private final ArgumentGenerator argumentGenerator;
private final List<ResolvedValueArgument> valueArguments;
final List<ResolvedValueArgument> valueArguments;
private final FrameMap frame;
private final StackValue receiver;
private final ResolvedCall<FunctionDescriptor> resolvedGetCall;
private final ResolvedCall<FunctionDescriptor> resolvedSetCall;
DefaultCallMask mask;
public CollectionElementReceiver(
@NotNull Callable callable,
@@ -803,7 +806,7 @@ public abstract class StackValue {
ResolvedCall<?> call = isGetter ? resolvedGetCall : resolvedSetCall;
StackValue newReceiver = StackValue.receiver(call, receiver, codegen, callable);
newReceiver.put(newReceiver.type, v);
argumentGenerator.generate(valueArguments, valueArguments);
mask = argumentGenerator.generate(valueArguments, valueArguments);
}
@Override
@@ -912,7 +915,7 @@ public abstract class StackValue {
private final FunctionDescriptor getterDescriptor;
public CollectionElement(
@NotNull StackValue collectionElementReceiver,
@NotNull CollectionElementReceiver collectionElementReceiver,
@NotNull Type type,
@Nullable ResolvedCall<FunctionDescriptor> resolvedGetCall,
@Nullable ResolvedCall<FunctionDescriptor> resolvedSetCall,
@@ -933,11 +936,16 @@ public abstract class StackValue {
if (getter == null) {
throw new UnsupportedOperationException("no getter specified");
}
getter.genInvokeInstruction(v);
CallGenerator callGenerator = codegen.defaultCallGenerator;
callGenerator.genCall(getter, resolvedGetCall, genDefaultMaskIfPresent(callGenerator), codegen);
coerceTo(type, v);
}
private boolean genDefaultMaskIfPresent(CallGenerator callGenerator) {
DefaultCallMask mask = ((CollectionElementReceiver) receiver).mask;
return mask.generateOnStackIfNeeded(callGenerator);
}
@Override
public int receiverSize() {
if (isStandardStack(codegen.typeMapper, resolvedGetCall, 1) && isStandardStack(codegen.typeMapper, resolvedSetCall, 2)) {
@@ -985,9 +993,25 @@ public abstract class StackValue {
throw new UnsupportedOperationException("no setter specified");
}
Type[] argumentTypes = setter.getParameterTypes();
coerce(topOfStackType, argumentTypes[argumentTypes.length - 1], v);
setter.genInvokeInstruction(v);
Type lastParameterType = ArraysKt.last(setter.getParameterTypes());
coerce(topOfStackType, lastParameterType, v);
//*Convention setter couldn't have default parameters, just getter can have it at last positions
//We should remove default parameters of getter from stack*/
CollectionElementReceiver collectionElementReceiver = (CollectionElementReceiver) receiver;
if (collectionElementReceiver.isGetter) {
List<ResolvedValueArgument> arguments = collectionElementReceiver.valueArguments;
List<Type> types = getter.getValueParameterTypes();
for (int i = arguments.size() - 1; i >= 0; i--) {
ResolvedValueArgument argument = arguments.get(i);
if (argument instanceof DefaultValueArgument) {
Type defaultType = types.get(i);
AsmUtil.swap(v, lastParameterType, defaultType);
AsmUtil.pop(v, defaultType);
}
}
}
codegen.defaultCallGenerator.genCall(setter, resolvedSetCall, false, codegen);
Type returnType = setter.getReturnType();
if (returnType != Type.VOID_TYPE) {
pop(v, returnType);
@@ -0,0 +1,26 @@
var inc: String = ""
class X {
var result: String = "fail"
operator fun get(name: String, type: String = "none") = name + inc + type
operator fun set(name: String, s: String) {
result = name + s;
}
}
operator fun String.inc(): String {
inc = this + "1"
return this + "1"
}
fun box(): String {
var x = X()
val res = ++x["a"]
if (x.result != "aanone1") return "fail 1: ${x.result}"
if (res != "aanone1none") return "fail 2: ${res}"
return "OK"
}
@@ -0,0 +1,11 @@
class X {
operator fun get(name: String, type: String = "none") = name + type
}
fun box(): String {
if (X().get("a") != "anone") return "fail 1: ${X().get("a")}"
if (X()["a"] != "anone") return "fail 2: ${X()["a"]}"
return "OK"
}
@@ -0,0 +1,31 @@
class X {
var result: String = "fail"
operator fun get(name: String, type: String = "none") = name + type
operator fun set(name: String, s: String) {
result = name + s;
}
}
class Y {
var result: String = "fail"
operator fun get(name: String, type: String = "no", type2: String = "ne") = name + type + type2
operator fun set(name: String, s: String) {
result = name + s;
}
}
fun box(): String {
var x = X()
x["a"] += "OK"
if (x.result != "aanoneOK") return "fail: ${x.result}"
var y = Y()
y["a"] += "OK"
if (y.result != "aanoneOK") return "fail: ${y.result}"
return "OK"
}
@@ -2707,6 +2707,33 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
}
}
@TestMetadata("compiler/testData/codegen/box/defaultArguments/convention")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Convention extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInConvention() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/defaultArguments/convention"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("incWithDefaultInGetter.kt")
public void testIncWithDefaultInGetter() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/convention/incWithDefaultInGetter.kt");
doTest(fileName);
}
@TestMetadata("kt9140.kt")
public void testKt9140() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/convention/kt9140.kt");
doTest(fileName);
}
@TestMetadata("plusAssignWithDefaultInGetter.kt")
public void testPlusAssignWithDefaultInGetter() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/defaultArguments/convention/plusAssignWithDefaultInGetter.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/defaultArguments/function")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)