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 org.jetbrains.kotlin.codegen.context.ClassContext
|
||||
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.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
|
||||
|
||||
class ErasedInlineClassBodyCodegen(
|
||||
@@ -27,6 +33,38 @@ class ErasedInlineClassBodyCodegen(
|
||||
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() {
|
||||
writeSyntheticClassMetadata(v, state)
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ public class FunctionCodegen {
|
||||
}
|
||||
|
||||
boolean hasSpecialBridge = hasSpecialBridgeMethod(functionDescriptor);
|
||||
JvmMethodGenericSignature jvmSignature = typeMapper.mapSignatureWithGeneric(functionDescriptor, contextKind, hasSpecialBridge);
|
||||
JvmMethodGenericSignature jvmSignature = strategy.mapMethodSignature(functionDescriptor, typeMapper, contextKind, hasSpecialBridge);
|
||||
Method asmMethod = jvmSignature.getAsmMethod();
|
||||
|
||||
int flags = getMethodAsmFlags(functionDescriptor, contextKind, state);
|
||||
|
||||
@@ -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;
|
||||
@@ -19,9 +8,12 @@ package org.jetbrains.kotlin.codegen;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.codegen.context.MethodContext;
|
||||
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.KtExpression;
|
||||
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.org.objectweb.asm.MethodVisitor;
|
||||
|
||||
@@ -40,6 +32,16 @@ public abstract class FunctionGenerationStrategy {
|
||||
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 {
|
||||
private final KtDeclarationWithBody declaration;
|
||||
|
||||
|
||||
@@ -373,9 +373,8 @@ public class KotlinTypeMapper {
|
||||
return Type.VOID_TYPE;
|
||||
}
|
||||
else if (descriptor instanceof FunctionDescriptor && forceBoxedReturnType((FunctionDescriptor) descriptor)) {
|
||||
// GENERIC_TYPE is a hack to automatically box the return type
|
||||
//noinspection ConstantConditions
|
||||
return mapType(descriptor.getReturnType(), sw, TypeMappingMode.GENERIC_ARGUMENT);
|
||||
return mapType(descriptor.getReturnType(), sw, TypeMappingMode.RETURN_TYPE_BOXED);
|
||||
}
|
||||
|
||||
return mapReturnType(descriptor, sw, returnType);
|
||||
@@ -1031,10 +1030,16 @@ public class KotlinTypeMapper {
|
||||
return mapSignatureSkipGeneric(f, OwnerKind.IMPLEMENTATION);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JvmMethodSignature mapSignatureForInlineErasedClassSkipGeneric(@NotNull FunctionDescriptor f) {
|
||||
return mapSignatureSkipGeneric(f, OwnerKind.ERASED_INLINE_CLASS);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JvmMethodGenericSignature mapSignatureForBoxMethodOfInlineClass(@NotNull FunctionDescriptor f) {
|
||||
return mapSignature(f, OwnerKind.IMPLEMENTATION, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JvmMethodSignature mapSignatureSkipGeneric(@NotNull FunctionDescriptor f, @NotNull OwnerKind kind) {
|
||||
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
|
||||
* of that descriptor is nullable or not. This happens 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
|
||||
* of that descriptor is nullable or not. This happens in two cases:
|
||||
* - 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)
|
||||
*/
|
||||
private static boolean forceBoxedReturnType(@NotNull FunctionDescriptor descriptor) {
|
||||
if (isBoxMethodForInlineClass(descriptor)) return true;
|
||||
|
||||
//noinspection ConstantConditions
|
||||
if (!KotlinBuiltIns.isPrimitiveType(descriptor.getReturnType())) return false;
|
||||
|
||||
@@ -1247,6 +1256,14 @@ public class KotlinTypeMapper {
|
||||
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) {
|
||||
sw.writeReturnType();
|
||||
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
|
||||
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 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
|
||||
|
||||
@@ -27,6 +27,13 @@ class TypeMappingMode private constructor(
|
||||
@JvmField
|
||||
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
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user