Generate synthetic box method for each erased inline class
This commit is contained in:
@@ -8,7 +8,13 @@ package org.jetbrains.kotlin.codegen
|
|||||||
import com.intellij.util.ArrayUtil
|
import com.intellij.util.ArrayUtil
|
||||||
import org.jetbrains.kotlin.codegen.context.ClassContext
|
import org.jetbrains.kotlin.codegen.context.ClassContext
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
|
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtClass
|
import org.jetbrains.kotlin.psi.KtClass
|
||||||
|
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.Synthetic
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
|
|
||||||
class ErasedInlineClassBodyCodegen(
|
class ErasedInlineClassBodyCodegen(
|
||||||
@@ -27,6 +33,38 @@ class ErasedInlineClassBodyCodegen(
|
|||||||
v.visitSource(myClass.containingKtFile.name, null)
|
v.visitSource(myClass.containingKtFile.name, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun generateSyntheticPartsAfterBody() {
|
||||||
|
super.generateSyntheticPartsAfterBody()
|
||||||
|
|
||||||
|
val boxMethodDescriptor = InlineClassDescriptorResolver.createBoxFunctionDescriptor(descriptor) ?: return
|
||||||
|
functionCodegen.generateMethod(
|
||||||
|
Synthetic(null, boxMethodDescriptor), boxMethodDescriptor, object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||||
|
override fun mapMethodSignature(
|
||||||
|
functionDescriptor: FunctionDescriptor,
|
||||||
|
typeMapper: KotlinTypeMapper,
|
||||||
|
contextKind: OwnerKind,
|
||||||
|
hasSpecialBridge: Boolean
|
||||||
|
): JvmMethodGenericSignature {
|
||||||
|
// note that in signatures of functions inside erased inline class only underlying types are using,
|
||||||
|
// but return type of `box` function should be a wrapper type, therefore we handle this situation here differently
|
||||||
|
return typeMapper.mapSignatureForBoxMethodOfInlineClass(functionDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||||
|
val iv = codegen.v
|
||||||
|
val wrapperType = signature.returnType
|
||||||
|
val baseValueType = signature.valueParameters.single().asmType
|
||||||
|
val constructor = typeMapper.mapToCallableMethod(descriptor.unsubstitutedPrimaryConstructor!!, false)
|
||||||
|
iv.anew(wrapperType)
|
||||||
|
iv.dup()
|
||||||
|
iv.load(0, baseValueType)
|
||||||
|
constructor.genInvokeInstruction(iv)
|
||||||
|
iv.areturn(wrapperType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun generateKotlinMetadataAnnotation() {
|
override fun generateKotlinMetadataAnnotation() {
|
||||||
writeSyntheticClassMetadata(v, state)
|
writeSyntheticClassMetadata(v, state)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ public class FunctionCodegen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean hasSpecialBridge = hasSpecialBridgeMethod(functionDescriptor);
|
boolean hasSpecialBridge = hasSpecialBridgeMethod(functionDescriptor);
|
||||||
JvmMethodGenericSignature jvmSignature = typeMapper.mapSignatureWithGeneric(functionDescriptor, contextKind, hasSpecialBridge);
|
JvmMethodGenericSignature jvmSignature = strategy.mapMethodSignature(functionDescriptor, typeMapper, contextKind, hasSpecialBridge);
|
||||||
Method asmMethod = jvmSignature.getAsmMethod();
|
Method asmMethod = jvmSignature.getAsmMethod();
|
||||||
|
|
||||||
int flags = getMethodAsmFlags(functionDescriptor, contextKind, state);
|
int flags = getMethodAsmFlags(functionDescriptor, contextKind, state);
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -19,9 +8,12 @@ package org.jetbrains.kotlin.codegen;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.codegen.context.MethodContext;
|
import org.jetbrains.kotlin.codegen.context.MethodContext;
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||||
|
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
|
||||||
import org.jetbrains.kotlin.psi.KtDeclarationWithBody;
|
import org.jetbrains.kotlin.psi.KtDeclarationWithBody;
|
||||||
import org.jetbrains.kotlin.psi.KtExpression;
|
import org.jetbrains.kotlin.psi.KtExpression;
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
||||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||||
|
|
||||||
@@ -40,6 +32,16 @@ public abstract class FunctionGenerationStrategy {
|
|||||||
return mv;
|
return mv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public JvmMethodGenericSignature mapMethodSignature(
|
||||||
|
@NotNull FunctionDescriptor functionDescriptor,
|
||||||
|
@NotNull KotlinTypeMapper typeMapper,
|
||||||
|
@NotNull OwnerKind contextKind,
|
||||||
|
boolean hasSpecialBridge
|
||||||
|
) {
|
||||||
|
return typeMapper.mapSignatureWithGeneric(functionDescriptor, contextKind, hasSpecialBridge);
|
||||||
|
}
|
||||||
|
|
||||||
public static class FunctionDefault extends CodegenBased {
|
public static class FunctionDefault extends CodegenBased {
|
||||||
private final KtDeclarationWithBody declaration;
|
private final KtDeclarationWithBody declaration;
|
||||||
|
|
||||||
|
|||||||
@@ -373,9 +373,8 @@ public class KotlinTypeMapper {
|
|||||||
return Type.VOID_TYPE;
|
return Type.VOID_TYPE;
|
||||||
}
|
}
|
||||||
else if (descriptor instanceof FunctionDescriptor && forceBoxedReturnType((FunctionDescriptor) descriptor)) {
|
else if (descriptor instanceof FunctionDescriptor && forceBoxedReturnType((FunctionDescriptor) descriptor)) {
|
||||||
// GENERIC_TYPE is a hack to automatically box the return type
|
|
||||||
//noinspection ConstantConditions
|
//noinspection ConstantConditions
|
||||||
return mapType(descriptor.getReturnType(), sw, TypeMappingMode.GENERIC_ARGUMENT);
|
return mapType(descriptor.getReturnType(), sw, TypeMappingMode.RETURN_TYPE_BOXED);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mapReturnType(descriptor, sw, returnType);
|
return mapReturnType(descriptor, sw, returnType);
|
||||||
@@ -1031,10 +1030,16 @@ public class KotlinTypeMapper {
|
|||||||
return mapSignatureSkipGeneric(f, OwnerKind.IMPLEMENTATION);
|
return mapSignatureSkipGeneric(f, OwnerKind.IMPLEMENTATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
public JvmMethodSignature mapSignatureForInlineErasedClassSkipGeneric(@NotNull FunctionDescriptor f) {
|
public JvmMethodSignature mapSignatureForInlineErasedClassSkipGeneric(@NotNull FunctionDescriptor f) {
|
||||||
return mapSignatureSkipGeneric(f, OwnerKind.ERASED_INLINE_CLASS);
|
return mapSignatureSkipGeneric(f, OwnerKind.ERASED_INLINE_CLASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public JvmMethodGenericSignature mapSignatureForBoxMethodOfInlineClass(@NotNull FunctionDescriptor f) {
|
||||||
|
return mapSignature(f, OwnerKind.IMPLEMENTATION, true);
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public JvmMethodSignature mapSignatureSkipGeneric(@NotNull FunctionDescriptor f, @NotNull OwnerKind kind) {
|
public JvmMethodSignature mapSignatureSkipGeneric(@NotNull FunctionDescriptor f, @NotNull OwnerKind kind) {
|
||||||
return mapSignature(f, kind, true);
|
return mapSignature(f, kind, true);
|
||||||
@@ -1231,11 +1236,15 @@ public class KotlinTypeMapper {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true iff a given function descriptor should be compiled to a method with boxed return type regardless of whether return type
|
* @return true iff a given function descriptor should be compiled to a method with boxed return type regardless of whether return type
|
||||||
* of that descriptor is nullable or not. This happens when a function returning a value of a primitive type overrides another function
|
* of that descriptor is nullable or not. This happens in two cases:
|
||||||
* with a non-primitive return type. In that case the generated method's return type should be boxed: otherwise it's not possible to use
|
* - when a target function is a synthetic box method of erased inline class;
|
||||||
|
* - when a function returning a value of a primitive type overrides another function with a non-primitive return type.
|
||||||
|
* In that case the generated method's return type should be boxed: otherwise it's not possible to use
|
||||||
* this class from Java since javac issues errors when loading the class (incompatible return types)
|
* this class from Java since javac issues errors when loading the class (incompatible return types)
|
||||||
*/
|
*/
|
||||||
private static boolean forceBoxedReturnType(@NotNull FunctionDescriptor descriptor) {
|
private static boolean forceBoxedReturnType(@NotNull FunctionDescriptor descriptor) {
|
||||||
|
if (isBoxMethodForInlineClass(descriptor)) return true;
|
||||||
|
|
||||||
//noinspection ConstantConditions
|
//noinspection ConstantConditions
|
||||||
if (!KotlinBuiltIns.isPrimitiveType(descriptor.getReturnType())) return false;
|
if (!KotlinBuiltIns.isPrimitiveType(descriptor.getReturnType())) return false;
|
||||||
|
|
||||||
@@ -1247,6 +1256,14 @@ public class KotlinTypeMapper {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isBoxMethodForInlineClass(@NotNull FunctionDescriptor descriptor) {
|
||||||
|
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
|
||||||
|
if (!InlineClassesUtilsKt.isInlineClass(containingDeclaration)) return false;
|
||||||
|
|
||||||
|
return CallableMemberDescriptor.Kind.SYNTHESIZED == descriptor.getKind() &&
|
||||||
|
InlineClassDescriptorResolver.BOX_METHOD_NAME.equals(descriptor.getName());
|
||||||
|
}
|
||||||
|
|
||||||
private static void writeVoidReturn(@NotNull JvmSignatureWriter sw) {
|
private static void writeVoidReturn(@NotNull JvmSignatureWriter sw) {
|
||||||
sw.writeReturnType();
|
sw.writeReturnType();
|
||||||
sw.writeAsmType(Type.VOID_TYPE);
|
sw.writeAsmType(Type.VOID_TYPE);
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* 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.resolve
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
|
object InlineClassDescriptorResolver {
|
||||||
|
@JvmField
|
||||||
|
val BOX_METHOD_NAME = Name.identifier("box")
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
val UNBOX_METHOD_NAME = Name.identifier("unbox")
|
||||||
|
|
||||||
|
private val BOXING_VALUE_PARAMETER_NAME = Name.identifier("v")
|
||||||
|
|
||||||
|
fun createBoxFunctionDescriptor(owner: ClassDescriptor): SimpleFunctionDescriptor? {
|
||||||
|
return createConversionFunctionDescriptor(true, owner)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createUnboxFunctionDescriptor(owner: ClassDescriptor): SimpleFunctionDescriptor? =
|
||||||
|
createConversionFunctionDescriptor(false, owner)
|
||||||
|
|
||||||
|
private fun createConversionFunctionDescriptor(
|
||||||
|
isBoxMethod: Boolean,
|
||||||
|
owner: ClassDescriptor
|
||||||
|
): SimpleFunctionDescriptor? {
|
||||||
|
val inlinedValue = owner.underlyingRepresentation() ?: return null
|
||||||
|
|
||||||
|
val functionDescriptor = SimpleFunctionDescriptorImpl.create(
|
||||||
|
owner,
|
||||||
|
Annotations.EMPTY,
|
||||||
|
if (isBoxMethod) BOX_METHOD_NAME else UNBOX_METHOD_NAME,
|
||||||
|
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||||
|
SourceElement.NO_SOURCE
|
||||||
|
)
|
||||||
|
|
||||||
|
functionDescriptor.initialize(
|
||||||
|
null,
|
||||||
|
if (isBoxMethod) null else owner.thisAsReceiverParameter,
|
||||||
|
emptyList<TypeParameterDescriptor>(),
|
||||||
|
if (isBoxMethod) listOf(createValueParameterForBoxing(functionDescriptor, inlinedValue)) else emptyList(),
|
||||||
|
if (isBoxMethod) owner.defaultType else inlinedValue.returnType,
|
||||||
|
Modality.FINAL,
|
||||||
|
Visibilities.PUBLIC
|
||||||
|
)
|
||||||
|
|
||||||
|
return functionDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createValueParameterForBoxing(
|
||||||
|
functionDescriptor: FunctionDescriptor,
|
||||||
|
inlinedValue: ValueParameterDescriptor
|
||||||
|
): ValueParameterDescriptorImpl {
|
||||||
|
return ValueParameterDescriptorImpl(
|
||||||
|
functionDescriptor,
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
Annotations.EMPTY,
|
||||||
|
BOXING_VALUE_PARAMETER_NAME,
|
||||||
|
inlinedValue.type,
|
||||||
|
false, false, false, null, SourceElement.NO_SOURCE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+1
@@ -1,5 +1,6 @@
|
|||||||
@kotlin.Metadata
|
@kotlin.Metadata
|
||||||
public final static class Foo$Erased {
|
public final static class Foo$Erased {
|
||||||
|
public final static @org.jetbrains.annotations.NotNull method box(p0: long): Foo
|
||||||
public final static method empty(p0: long): void
|
public final static method empty(p0: long): void
|
||||||
public final static method extension(p0: long, @org.jetbrains.annotations.NotNull p1: java.lang.Object, @org.jetbrains.annotations.NotNull p2: java.lang.String): void
|
public final static method extension(p0: long, @org.jetbrains.annotations.NotNull p1: java.lang.Object, @org.jetbrains.annotations.NotNull p2: java.lang.String): void
|
||||||
public final static method param(p0: long, p1: double): void
|
public final static method param(p0: long, p1: double): void
|
||||||
|
|||||||
@@ -27,6 +27,13 @@ class TypeMappingMode private constructor(
|
|||||||
@JvmField
|
@JvmField
|
||||||
val GENERIC_ARGUMENT = TypeMappingMode()
|
val GENERIC_ARGUMENT = TypeMappingMode()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* see KotlinTypeMapper.forceBoxedReturnType()
|
||||||
|
* This configuration should be called only for method return type
|
||||||
|
*/
|
||||||
|
@JvmField
|
||||||
|
val RETURN_TYPE_BOXED = TypeMappingMode(needInlineClassWrapping = true)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kotlin.Int is mapped to I
|
* kotlin.Int is mapped to I
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user