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);