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.
*
* 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.
* 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.
*/
package org.jetbrains.kotlin.codegen
@@ -62,7 +51,7 @@ interface CallGenerator {
parameterType: Type,
parameterIndex: Int) {
val value = codegen.gen(argumentExpression)
value.put(parameterType, codegen.v)
value.put(parameterType, valueParameterDescriptor.original.type, codegen.v)
}
override fun putCapturedValueOnStack(
@@ -21,21 +21,25 @@ public class CallReceiver extends StackValue {
private final StackValue dispatchReceiver;
private final StackValue extensionReceiver;
private final Type secondReceiverType;
private final KotlinType secondReceiverKotlinType;
private CallReceiver(
@NotNull StackValue dispatchReceiver,
@NotNull StackValue extensionReceiver,
@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.extensionReceiver = extensionReceiver;
this.secondReceiverType = secondReceiverType;
this.secondReceiverKotlinType = secondReceiverKotlinType;
}
public StackValue withoutReceiverArgument() {
return new CallReceiver(dispatchReceiver, none(), type, secondReceiverType);
return new CallReceiver(dispatchReceiver, none(), type, kotlinType, secondReceiverType, secondReceiverKotlinType);
}
public static StackValue generateCallReceiver(
@@ -50,30 +54,42 @@ public class CallReceiver extends StackValue {
KotlinTypeMapper typeMapper = codegen.typeMapper;
GenerationState state = codegen.getState();
Type type;
AsmTypeAndKotlinType jvmKotlinType;
Type secondReceiverType = null;
KotlinType secondReceiverKotlinType = null;
if (extensionReceiverParameter != null) {
type = calcExtensionReceiverType(resolvedCall, extensionReceiverParameter, typeMapper, callableMethod, state);
jvmKotlinType = calcExtensionReceiverType(
resolvedCall, extensionReceiverParameter, typeMapper, callableMethod, state
);
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) {
type = calcDispatchReceiverType(resolvedCall, dispatchReceiverParameter, typeMapper, callableMethod);
jvmKotlinType = calcDispatchReceiverType(resolvedCall, dispatchReceiverParameter, typeMapper, 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 {
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,
@Nullable ReceiverParameterDescriptor dispatchReceiver,
@NotNull KotlinTypeMapper typeMapper,
@@ -84,27 +100,33 @@ public class CallReceiver extends StackValue {
CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
if (CodegenUtilKt.isJvmStaticInObjectOrClassOrInterface(descriptor)) {
return Type.VOID_TYPE;
return new AsmTypeAndKotlinType(Type.VOID_TYPE, null);
}
DeclarationDescriptor container = descriptor.getContainingDeclaration();
if (callableMethod != null) {
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
// 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) {
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,
@Nullable ReceiverParameterDescriptor extensionReceiver,
@NotNull KotlinTypeMapper typeMapper,
@@ -121,10 +143,12 @@ public class CallReceiver extends StackValue {
) {
ReceiverParameterDescriptor receiverCandidate = descriptor.getExtensionReceiverParameter();
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
@@ -132,14 +156,16 @@ public class CallReceiver extends StackValue {
StackValue currentExtensionReceiver = extensionReceiver;
boolean hasExtensionReceiver = extensionReceiver != none();
if (extensionReceiver instanceof SafeCall) {
currentExtensionReceiver.put(currentExtensionReceiver.type, v);
currentExtensionReceiver = StackValue.onStack(currentExtensionReceiver.type);
currentExtensionReceiver.put(currentExtensionReceiver.type, currentExtensionReceiver.kotlinType, v);
currentExtensionReceiver = StackValue.onStack(currentExtensionReceiver.type, currentExtensionReceiver.kotlinType);
}
Type dispatchReceiverType = secondReceiverType != null ? secondReceiverType :
hasExtensionReceiver ? dispatchReceiver.type :
type;
dispatchReceiver.put(dispatchReceiverType, v);
Type dispatchReceiverType = calcDispatchReceiver(secondReceiverType, hasExtensionReceiver, dispatchReceiver.type, type);
KotlinType dispatchReceiverKotlinType = calcDispatchReceiver(
secondReceiverKotlinType, hasExtensionReceiver, dispatchReceiver.kotlinType, kotlinType
);
dispatchReceiver.put(dispatchReceiverType, dispatchReceiverKotlinType, v);
currentExtensionReceiver
.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
public void dup(@NotNull InstructionAdapter v, boolean withReceiver) {
AsmUtil.dup(v, extensionReceiver.type, dispatchReceiver.type);
@@ -177,7 +177,12 @@ public abstract class StackValue {
@NotNull
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
@@ -852,7 +857,11 @@ public abstract class StackValue {
public static class OnStack extends StackValue {
public OnStack(Type type) {
super(type);
this(type, null);
}
public OnStack(Type type, KotlinType kotlinType) {
super(type, kotlinType);
}
@Override
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* 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.
*/
@@ -279,6 +268,8 @@ fun Collection<VariableDescriptor>.filterOutDescriptorsWithSpecialNames() = filt
class TypeAndNullability(@JvmField val type: Type, @JvmField val isNullable: Boolean)
class AsmTypeAndKotlinType(val type: Type, val kotlinType: KotlinType?)
fun calcTypeForIEEE754ArithmeticIfNeeded(
expression: KtExpression?,
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");
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")