Support boxing for inline classes on function calls
This commit is contained in:
@@ -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"
|
||||
}
|
||||
+17
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user