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