Support boxing for inline classes on function calls

This commit is contained in:
Mikhail Zarechenskiy
2018-02-05 06:00:32 +03:00
parent 0d682879f5
commit 390c214943
7 changed files with 123 additions and 55 deletions
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* * that can be found in the license/LICENSE.txt file.
* 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 org.jetbrains.kotlin.codegen package org.jetbrains.kotlin.codegen
@@ -62,7 +51,7 @@ interface CallGenerator {
parameterType: Type, parameterType: Type,
parameterIndex: Int) { parameterIndex: Int) {
val value = codegen.gen(argumentExpression) val value = codegen.gen(argumentExpression)
value.put(parameterType, codegen.v) value.put(parameterType, valueParameterDescriptor.original.type, codegen.v)
} }
override fun putCapturedValueOnStack( override fun putCapturedValueOnStack(
@@ -21,21 +21,25 @@ public class CallReceiver extends StackValue {
private final StackValue dispatchReceiver; private final StackValue dispatchReceiver;
private final StackValue extensionReceiver; private final StackValue extensionReceiver;
private final Type secondReceiverType; private final Type secondReceiverType;
private final KotlinType secondReceiverKotlinType;
private CallReceiver( private CallReceiver(
@NotNull StackValue dispatchReceiver, @NotNull StackValue dispatchReceiver,
@NotNull StackValue extensionReceiver, @NotNull StackValue extensionReceiver,
@NotNull Type type, @NotNull Type type,
@Nullable Type secondReceiverType @Nullable KotlinType kotlinType,
@Nullable Type secondReceiverType,
@Nullable KotlinType secondReceiverKotlinType
) { ) {
super(type, dispatchReceiver.canHaveSideEffects() || extensionReceiver.canHaveSideEffects()); super(type, kotlinType, dispatchReceiver.canHaveSideEffects() || extensionReceiver.canHaveSideEffects());
this.dispatchReceiver = dispatchReceiver; this.dispatchReceiver = dispatchReceiver;
this.extensionReceiver = extensionReceiver; this.extensionReceiver = extensionReceiver;
this.secondReceiverType = secondReceiverType; this.secondReceiverType = secondReceiverType;
this.secondReceiverKotlinType = secondReceiverKotlinType;
} }
public StackValue withoutReceiverArgument() { public StackValue withoutReceiverArgument() {
return new CallReceiver(dispatchReceiver, none(), type, secondReceiverType); return new CallReceiver(dispatchReceiver, none(), type, kotlinType, secondReceiverType, secondReceiverKotlinType);
} }
public static StackValue generateCallReceiver( public static StackValue generateCallReceiver(
@@ -50,30 +54,42 @@ public class CallReceiver extends StackValue {
KotlinTypeMapper typeMapper = codegen.typeMapper; KotlinTypeMapper typeMapper = codegen.typeMapper;
GenerationState state = codegen.getState(); GenerationState state = codegen.getState();
Type type; AsmTypeAndKotlinType jvmKotlinType;
Type secondReceiverType = null; Type secondReceiverType = null;
KotlinType secondReceiverKotlinType = null;
if (extensionReceiverParameter != null) { if (extensionReceiverParameter != null) {
type = calcExtensionReceiverType(resolvedCall, extensionReceiverParameter, typeMapper, callableMethod, state); jvmKotlinType = calcExtensionReceiverType(
resolvedCall, extensionReceiverParameter, typeMapper, callableMethod, state
);
if (dispatchReceiverParameter != null) { if (dispatchReceiverParameter != null) {
secondReceiverType = calcDispatchReceiverType(resolvedCall, dispatchReceiverParameter, typeMapper, callableMethod); AsmTypeAndKotlinType dispatchReceiverInfo = calcDispatchReceiverType(
resolvedCall, dispatchReceiverParameter, typeMapper, callableMethod
);
secondReceiverType = dispatchReceiverInfo.getType();
secondReceiverKotlinType = dispatchReceiverInfo.getKotlinType();
} }
} }
else if (dispatchReceiverParameter != null) { else if (dispatchReceiverParameter != null) {
type = calcDispatchReceiverType(resolvedCall, dispatchReceiverParameter, typeMapper, callableMethod); jvmKotlinType = calcDispatchReceiverType(resolvedCall, dispatchReceiverParameter, typeMapper, callableMethod);
} }
else if (isLocalFunCall(callableMethod)) { else if (isLocalFunCall(callableMethod)) {
type = callableMethod.getGenerateCalleeType(); Type calleeType = callableMethod.getGenerateCalleeType();
assert calleeType != null : "Could not get callee type for " + resolvedCall;
jvmKotlinType = new AsmTypeAndKotlinType(calleeType, null);
} }
else { else {
type = Type.VOID_TYPE; jvmKotlinType = new AsmTypeAndKotlinType(Type.VOID_TYPE, null);
} }
assert type != null : "Could not map receiver type for " + resolvedCall;
return new CallReceiver(dispatchReceiver, extensionReceiver, type, secondReceiverType); return new CallReceiver(
dispatchReceiver, extensionReceiver, jvmKotlinType.getType(),
jvmKotlinType.getKotlinType(), secondReceiverType, secondReceiverKotlinType
);
} }
private static Type calcDispatchReceiverType( private static AsmTypeAndKotlinType calcDispatchReceiverType(
@NotNull ResolvedCall<?> resolvedCall, @NotNull ResolvedCall<?> resolvedCall,
@Nullable ReceiverParameterDescriptor dispatchReceiver, @Nullable ReceiverParameterDescriptor dispatchReceiver,
@NotNull KotlinTypeMapper typeMapper, @NotNull KotlinTypeMapper typeMapper,
@@ -84,27 +100,33 @@ public class CallReceiver extends StackValue {
CallableDescriptor descriptor = resolvedCall.getResultingDescriptor(); CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
if (CodegenUtilKt.isJvmStaticInObjectOrClassOrInterface(descriptor)) { if (CodegenUtilKt.isJvmStaticInObjectOrClassOrInterface(descriptor)) {
return Type.VOID_TYPE; return new AsmTypeAndKotlinType(Type.VOID_TYPE, null);
} }
DeclarationDescriptor container = descriptor.getContainingDeclaration(); DeclarationDescriptor container = descriptor.getContainingDeclaration();
if (callableMethod != null) { if (callableMethod != null) {
if (InlineClassesUtilsKt.isInlineClass(container)) { if (InlineClassesUtilsKt.isInlineClass(container)) {
return typeMapper.mapType((ClassDescriptor) container); ClassDescriptor classDescriptor = (ClassDescriptor) container;
return new AsmTypeAndKotlinType(typeMapper.mapType(classDescriptor), classDescriptor.getDefaultType());
} }
return callableMethod.getDispatchReceiverType(); //noinspection ConstantConditions
return new AsmTypeAndKotlinType(callableMethod.getDispatchReceiverType(), null);
} }
// Extract the receiver from the resolved call, workarounding the fact that ResolvedCall#dispatchReceiver doesn't have // Extract the receiver from the resolved call, workarounding the fact that ResolvedCall#dispatchReceiver doesn't have
// all the needed information, for example there's no way to find out whether or not a smart cast was applied to the receiver. // all the needed information, for example there's no way to find out whether or not a smart cast was applied to the receiver.
if (container instanceof ClassDescriptor) { if (container instanceof ClassDescriptor) {
return typeMapper.mapClass((ClassDescriptor) container); ClassDescriptor classDescriptor = (ClassDescriptor) container;
return new AsmTypeAndKotlinType(typeMapper.mapClass(classDescriptor), classDescriptor.getDefaultType());
} }
return typeMapper.mapType(dispatchReceiver); KotlinType dispatchReceiverType = dispatchReceiver.getReturnType();
//noinspection ConstantConditions
return new AsmTypeAndKotlinType(typeMapper.mapType(dispatchReceiverType), dispatchReceiverType);
} }
private static Type calcExtensionReceiverType( private static AsmTypeAndKotlinType calcExtensionReceiverType(
@NotNull ResolvedCall<?> resolvedCall, @NotNull ResolvedCall<?> resolvedCall,
@Nullable ReceiverParameterDescriptor extensionReceiver, @Nullable ReceiverParameterDescriptor extensionReceiver,
@NotNull KotlinTypeMapper typeMapper, @NotNull KotlinTypeMapper typeMapper,
@@ -121,10 +143,12 @@ public class CallReceiver extends StackValue {
) { ) {
ReceiverParameterDescriptor receiverCandidate = descriptor.getExtensionReceiverParameter(); ReceiverParameterDescriptor receiverCandidate = descriptor.getExtensionReceiverParameter();
assert receiverCandidate != null; assert receiverCandidate != null;
return typeMapper.mapType(receiverCandidate.getType()); return new AsmTypeAndKotlinType(typeMapper.mapType(receiverCandidate.getType()), receiverCandidate.getType());
} }
return callableMethod != null ? callableMethod.getExtensionReceiverType() : typeMapper.mapType(extensionReceiver.getType()); return callableMethod != null ?
new AsmTypeAndKotlinType(callableMethod.getExtensionReceiverType(), null) :
new AsmTypeAndKotlinType(typeMapper.mapType(extensionReceiver.getType()), extensionReceiver.getType());
} }
@Override @Override
@@ -132,14 +156,16 @@ public class CallReceiver extends StackValue {
StackValue currentExtensionReceiver = extensionReceiver; StackValue currentExtensionReceiver = extensionReceiver;
boolean hasExtensionReceiver = extensionReceiver != none(); boolean hasExtensionReceiver = extensionReceiver != none();
if (extensionReceiver instanceof SafeCall) { if (extensionReceiver instanceof SafeCall) {
currentExtensionReceiver.put(currentExtensionReceiver.type, v); currentExtensionReceiver.put(currentExtensionReceiver.type, currentExtensionReceiver.kotlinType, v);
currentExtensionReceiver = StackValue.onStack(currentExtensionReceiver.type); currentExtensionReceiver = StackValue.onStack(currentExtensionReceiver.type, currentExtensionReceiver.kotlinType);
} }
Type dispatchReceiverType = secondReceiverType != null ? secondReceiverType : Type dispatchReceiverType = calcDispatchReceiver(secondReceiverType, hasExtensionReceiver, dispatchReceiver.type, type);
hasExtensionReceiver ? dispatchReceiver.type : KotlinType dispatchReceiverKotlinType = calcDispatchReceiver(
type; secondReceiverKotlinType, hasExtensionReceiver, dispatchReceiver.kotlinType, kotlinType
dispatchReceiver.put(dispatchReceiverType, v); );
dispatchReceiver.put(dispatchReceiverType, dispatchReceiverKotlinType, v);
currentExtensionReceiver currentExtensionReceiver
.moveToTopOfStack( .moveToTopOfStack(
@@ -150,6 +176,15 @@ public class CallReceiver extends StackValue {
); );
} }
private static <T> T calcDispatchReceiver(T secondType, boolean hasExtensionReceiver, T dispatchReceiverType, T defaultType) {
if (secondType != null) {
return secondType;
}
else {
return hasExtensionReceiver ? dispatchReceiverType : defaultType;
}
}
@Override @Override
public void dup(@NotNull InstructionAdapter v, boolean withReceiver) { public void dup(@NotNull InstructionAdapter v, boolean withReceiver) {
AsmUtil.dup(v, extensionReceiver.type, dispatchReceiver.type); AsmUtil.dup(v, extensionReceiver.type, dispatchReceiver.type);
@@ -177,7 +177,12 @@ public abstract class StackValue {
@NotNull @NotNull
public static StackValue onStack(@NotNull Type type) { public static StackValue onStack(@NotNull Type type) {
return type == Type.VOID_TYPE ? none() : new OnStack(type); return onStack(type, null);
}
@NotNull
public static StackValue onStack(@NotNull Type type, @Nullable KotlinType kotlinType) {
return type == Type.VOID_TYPE ? none() : new OnStack(type, kotlinType);
} }
@NotNull @NotNull
@@ -852,7 +857,11 @@ public abstract class StackValue {
public static class OnStack extends StackValue { public static class OnStack extends StackValue {
public OnStack(Type type) { public OnStack(Type type) {
super(type); this(type, null);
}
public OnStack(Type type, KotlinType kotlinType) {
super(type, kotlinType);
} }
@Override @Override
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* * that can be found in the license/LICENSE.txt file.
* 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.
*/ */
@@ -279,6 +268,8 @@ fun Collection<VariableDescriptor>.filterOutDescriptorsWithSpecialNames() = filt
class TypeAndNullability(@JvmField val type: Type, @JvmField val isNullable: Boolean) class TypeAndNullability(@JvmField val type: Type, @JvmField val isNullable: Boolean)
class AsmTypeAndKotlinType(val type: Type, val kotlinType: KotlinType?)
fun calcTypeForIEEE754ArithmeticIfNeeded( fun calcTypeForIEEE754ArithmeticIfNeeded(
expression: KtExpression?, expression: KtExpression?,
bindingContext: BindingContext, bindingContext: BindingContext,
@@ -0,0 +1,21 @@
// !LANGUAGE: +InlineClasses
inline class InlineNotNullPrimitive(val x: Int)
inline class InlineNotNullReference(val y: String)
fun <T> testNotNullPrimitive(a: Any, b: T, c: InlineNotNullPrimitive, d: InlineNotNullPrimitive?) {}
fun <T> testNotNullReference(a: Any, b: T, c: InlineNotNullReference, d: InlineNotNullReference?) {}
fun test(a: InlineNotNullPrimitive, b: InlineNotNullReference) {
testNotNullPrimitive(a, a, a, a) // 3 box
testNotNullReference(b, b, b, b) // 2 box
}
fun box(): String {
val a = InlineNotNullPrimitive(10)
val b = InlineNotNullReference("some")
test(a, b)
return "OK"
}
@@ -0,0 +1,17 @@
// !LANGUAGE: +InlineClasses
inline class InlineNotNullPrimitive(val x: Int)
inline class InlineNotNullReference(val y: String)
fun <T> testNotNullPrimitive(a: Any, b: T, c: InlineNotNullPrimitive, d: InlineNotNullPrimitive?) {}
fun <T> testNotNullReference(a: Any, b: T, c: InlineNotNullReference, d: InlineNotNullReference?) {}
fun test(a: InlineNotNullPrimitive, b: InlineNotNullReference) {
testNotNullPrimitive(a, a, a, a) // 3 box
testNotNullReference(b, b, b, b) // 2 box
}
// 3 INVOKESTATIC InlineNotNullPrimitive\$Erased.box
// 2 INVOKESTATIC InlineNotNullReference\$Erased.box
// 0 valueOf
@@ -1931,6 +1931,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassBoxingOnAssignment.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassBoxingOnAssignment.kt");
doTest(fileName); doTest(fileName);
} }
@TestMetadata("inlineClassBoxingOnFunctionCall.kt")
public void testInlineClassBoxingOnFunctionCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassBoxingOnFunctionCall.kt");
doTest(fileName);
}
} }
@TestMetadata("compiler/testData/codegen/bytecodeText/interfaces") @TestMetadata("compiler/testData/codegen/bytecodeText/interfaces")