Generate synthetic box method for each erased inline class

This commit is contained in:
Mikhail Zarechenskiy
2018-02-05 03:37:27 +03:00
parent f856455841
commit a59917b6f8
7 changed files with 154 additions and 18 deletions
@@ -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
)
}
}
@@ -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
*/