JVM IR: extract separate module backend.jvm.codegen
The main benefit is that now lowerings and codegen are compiled in parallel, which speeds up the build. Hopefully, this will also improve incremental compilation in case of implementation changes because the rest of the compiler (cli & tests) has no "api" dependency on lowerings and codegen.
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":compiler:ir.tree"))
|
||||
api(project(":compiler:ir.backend.common"))
|
||||
api(project(":compiler:backend.jvm"))
|
||||
implementation(project(":compiler:ir.tree.impl"))
|
||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core", rootProject = rootProject) }
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" {
|
||||
projectDefault()
|
||||
}
|
||||
"test" {}
|
||||
}
|
||||
+462
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import org.jetbrains.kotlin.backend.common.ir.ir2string
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmSymbols
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.mapClass
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.TypeAnnotationCollector
|
||||
import org.jetbrains.kotlin.codegen.TypePathInfo
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.JvmTarget.JVM_1_6
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.org.objectweb.asm.AnnotationVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.TypePath
|
||||
import org.jetbrains.org.objectweb.asm.TypeReference
|
||||
import java.lang.annotation.RetentionPolicy
|
||||
|
||||
abstract class AnnotationCodegen(
|
||||
private val innerClassConsumer: InnerClassConsumer,
|
||||
private val context: JvmBackendContext,
|
||||
private val skipNullabilityAnnotations: Boolean = false
|
||||
) {
|
||||
private val typeMapper = context.typeMapper
|
||||
private val methodSignatureMapper = context.methodSignatureMapper
|
||||
|
||||
/**
|
||||
* @param returnType can be null if not applicable (e.g. [annotated] is a class)
|
||||
*/
|
||||
fun genAnnotations(
|
||||
annotated: IrAnnotationContainer?,
|
||||
returnType: Type?,
|
||||
typeForTypeAnnotations: IrType?
|
||||
) {
|
||||
if (annotated == null) return
|
||||
|
||||
val annotationDescriptorsAlreadyPresent = mutableSetOf<String>()
|
||||
|
||||
val annotations = annotated.annotations
|
||||
|
||||
for (annotation in annotations) {
|
||||
val applicableTargets = annotation.applicableTargetSet()
|
||||
if (annotated is IrSimpleFunction &&
|
||||
annotated.origin === IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA &&
|
||||
KotlinTarget.FUNCTION !in applicableTargets &&
|
||||
KotlinTarget.PROPERTY_GETTER !in applicableTargets &&
|
||||
KotlinTarget.PROPERTY_SETTER !in applicableTargets
|
||||
) {
|
||||
assert(KotlinTarget.EXPRESSION in applicableTargets) {
|
||||
"Inconsistent target list for lambda annotation: $applicableTargets on $annotated"
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (annotated is IrClass &&
|
||||
KotlinTarget.CLASS !in applicableTargets &&
|
||||
KotlinTarget.ANNOTATION_CLASS !in applicableTargets
|
||||
) {
|
||||
if (annotated.visibility == DescriptorVisibilities.LOCAL) {
|
||||
assert(KotlinTarget.EXPRESSION in applicableTargets) {
|
||||
"Inconsistent target list for object literal annotation: $applicableTargets on $annotated"
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
genAnnotation(annotation, null, false)?.let { descriptor ->
|
||||
annotationDescriptorsAlreadyPresent.add(descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipNullabilityAnnotations && annotated is IrDeclaration && returnType != null && !AsmUtil.isPrimitive(returnType)) {
|
||||
generateNullabilityAnnotationForCallable(annotated, annotationDescriptorsAlreadyPresent)
|
||||
}
|
||||
|
||||
generateTypeAnnotations(annotated, typeForTypeAnnotations)
|
||||
}
|
||||
|
||||
abstract fun visitAnnotation(descr: String, visible: Boolean): AnnotationVisitor
|
||||
|
||||
open fun visitTypeAnnotation(
|
||||
descr: String,
|
||||
path: TypePath?,
|
||||
visible: Boolean,
|
||||
): AnnotationVisitor {
|
||||
throw RuntimeException("Not implemented")
|
||||
}
|
||||
|
||||
|
||||
private fun generateNullabilityAnnotationForCallable(
|
||||
declaration: IrDeclaration, // There is no superclass that encompasses IrFunction, IrField and nothing else.
|
||||
annotationDescriptorsAlreadyPresent: MutableSet<String>
|
||||
) {
|
||||
if (isInvisibleForNullabilityAnalysis(declaration)) return
|
||||
if (declaration is IrValueParameter) {
|
||||
val parent = declaration.parent as IrDeclaration
|
||||
if (isInvisibleForNullabilityAnalysis(parent)) return
|
||||
if (isMovedReceiverParameterOfStaticInlineClassReplacement(declaration, parent)) return
|
||||
}
|
||||
|
||||
// No need to annotate annotation methods since they're always non-null
|
||||
if (declaration is IrSimpleFunction && declaration.correspondingPropertySymbol != null &&
|
||||
declaration.parentAsClass.isAnnotationClass
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
val type = when (declaration) {
|
||||
is IrFunction -> declaration.returnType
|
||||
is IrValueDeclaration -> declaration.type
|
||||
is IrField ->
|
||||
if (declaration.correspondingPropertySymbol?.owner?.isLateinit == true) {
|
||||
// Don't generate nullability annotations on lateinit fields
|
||||
return
|
||||
} else {
|
||||
declaration.type
|
||||
}
|
||||
else -> return
|
||||
}
|
||||
|
||||
if (isBareTypeParameterWithNullableUpperBound(type)) {
|
||||
// This is to account for the case of, say
|
||||
// class Function<R> { fun invoke(): R }
|
||||
// it would be a shame to put @Nullable on the return type of the function, and force all callers to check for null,
|
||||
// so we put no annotations
|
||||
return
|
||||
}
|
||||
|
||||
// A flexible type whose lower bound in not-null and upper bound is nullable, should not be annotated
|
||||
if (type.isWithFlexibleNullability()) return
|
||||
|
||||
val annotationClass = if (type.isNullable()) Nullable::class.java else NotNull::class.java
|
||||
|
||||
generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent, annotationClass)
|
||||
}
|
||||
|
||||
private fun isMovedReceiverParameterOfStaticInlineClassReplacement(parameter: IrValueParameter, parent: IrDeclaration): Boolean =
|
||||
parent.origin == JvmLoweredDeclarationOrigin.STATIC_INLINE_CLASS_REPLACEMENT &&
|
||||
parameter.origin == IrDeclarationOrigin.MOVED_DISPATCH_RECEIVER
|
||||
|
||||
private fun generateAnnotationIfNotPresent(annotationDescriptorsAlreadyPresent: MutableSet<String>, annotationClass: Class<*>) {
|
||||
val descriptor = Type.getType(annotationClass).descriptor
|
||||
if (!annotationDescriptorsAlreadyPresent.contains(descriptor)) {
|
||||
visitAnnotation(descriptor, false).visitEnd()
|
||||
}
|
||||
}
|
||||
|
||||
fun generateAnnotationDefaultValue(value: IrExpression) {
|
||||
val visitor = visitAnnotation("", false) // Parameters are unimportant
|
||||
genCompileTimeValue(null, value, visitor)
|
||||
visitor.visitEnd()
|
||||
}
|
||||
|
||||
private fun genAnnotation(annotation: IrConstructorCall, path: TypePath?, isTypeAnnotation: Boolean): String? {
|
||||
val annotationClass = annotation.annotationClass
|
||||
val retentionPolicy = getRetentionPolicy(annotationClass)
|
||||
if (retentionPolicy == RetentionPolicy.SOURCE) return null
|
||||
|
||||
// FlexibleNullability is an internal annotation, used only inside the compiler
|
||||
if (annotationClass.fqNameWhenAvailable in internalAnnotations) return null
|
||||
|
||||
// We do not generate annotations whose classes are optional (annotated with `@OptionalExpectation`) because if an annotation entry
|
||||
// is resolved to the expected declaration, this means that annotation has no actual class, and thus should not be generated.
|
||||
// (Otherwise we would've resolved the entry to the actual annotation class.)
|
||||
if (annotationClass.isOptionalAnnotationClass) return null
|
||||
|
||||
innerClassConsumer.addInnerClassInfoFromAnnotation(annotationClass)
|
||||
|
||||
val asmTypeDescriptor = typeMapper.mapType(annotation.type).descriptor
|
||||
val annotationVisitor =
|
||||
if (!isTypeAnnotation) visitAnnotation(asmTypeDescriptor, retentionPolicy == RetentionPolicy.RUNTIME) else
|
||||
visitTypeAnnotation(asmTypeDescriptor, path, retentionPolicy == RetentionPolicy.RUNTIME)
|
||||
|
||||
genAnnotationArguments(annotation, annotationVisitor)
|
||||
annotationVisitor.visitEnd()
|
||||
|
||||
return asmTypeDescriptor
|
||||
}
|
||||
|
||||
private fun genAnnotationArguments(annotation: IrConstructorCall, annotationVisitor: AnnotationVisitor) {
|
||||
val annotationClass = annotation.annotationClass
|
||||
for (param in annotation.symbol.owner.valueParameters) {
|
||||
val value = annotation.getValueArgument(param.index)
|
||||
if (value != null)
|
||||
genCompileTimeValue(getAnnotationArgumentJvmName(annotationClass, param.name), value, annotationVisitor)
|
||||
else if (param.defaultValue != null)
|
||||
continue // Default value will be supplied by JVM at runtime.
|
||||
else
|
||||
error("No value for annotation parameter $param")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAnnotationArgumentJvmName(annotationClass: IrClass?, parameterName: Name): String {
|
||||
if (annotationClass == null) return parameterName.asString()
|
||||
|
||||
val propertyOrGetter = annotationClass.declarations.singleOrNull {
|
||||
// IrSimpleFunction if lowered, IrProperty with a getter if imported
|
||||
(it is IrSimpleFunction && it.correspondingPropertySymbol?.owner?.name == parameterName) ||
|
||||
(it is IrProperty && it.name == parameterName)
|
||||
} ?: return parameterName.asString()
|
||||
val getter = propertyOrGetter as? IrSimpleFunction
|
||||
?: (propertyOrGetter as IrProperty).getter
|
||||
?: error("No getter for annotation property: ${propertyOrGetter.render()}")
|
||||
return methodSignatureMapper.mapFunctionName(getter)
|
||||
}
|
||||
|
||||
private fun genCompileTimeValue(
|
||||
name: String?,
|
||||
value: IrExpression,
|
||||
annotationVisitor: AnnotationVisitor
|
||||
) {
|
||||
when (value) {
|
||||
is IrConst<*> -> annotationVisitor.visit(name, value.value)
|
||||
is IrConstructorCall -> {
|
||||
val callee = value.symbol.owner
|
||||
when {
|
||||
callee.parentAsClass.isAnnotationClass -> {
|
||||
val annotationClassType = callee.returnType
|
||||
val internalAnnName = typeMapper.mapType(annotationClassType).descriptor
|
||||
val visitor = annotationVisitor.visitAnnotation(name, internalAnnName)
|
||||
annotationClassType.classOrNull?.owner?.let(innerClassConsumer::addInnerClassInfoFromAnnotation)
|
||||
genAnnotationArguments(value, visitor)
|
||||
visitor.visitEnd()
|
||||
}
|
||||
else -> error("Not supported as annotation! ${ir2string(value)}")
|
||||
}
|
||||
}
|
||||
is IrGetEnumValue -> {
|
||||
val enumEntry = value.symbol.owner
|
||||
val enumClass = enumEntry.parentAsClass
|
||||
innerClassConsumer.addInnerClassInfoFromAnnotation(enumClass)
|
||||
annotationVisitor.visitEnum(name, typeMapper.mapClass(enumClass).descriptor, enumEntry.name.asString())
|
||||
}
|
||||
is IrVararg -> { // array constructor
|
||||
val visitor = annotationVisitor.visitArray(name)
|
||||
for (element in value.elements) {
|
||||
genCompileTimeValue(null, element as IrExpression, visitor)
|
||||
}
|
||||
visitor.visitEnd()
|
||||
}
|
||||
is IrClassReference -> {
|
||||
val classType = value.classType
|
||||
classType.classOrNull?.owner?.let(innerClassConsumer::addInnerClassInfoFromAnnotation)
|
||||
val mappedType =
|
||||
if (classType.isInlineClassType()) typeMapper.mapClass(classType.erasedUpperBound)
|
||||
else typeMapper.mapType(classType)
|
||||
annotationVisitor.visit(name, mappedType)
|
||||
}
|
||||
is IrErrorExpression -> error("Don't know how to compile annotation value ${ir2string(value)}")
|
||||
else -> error("Unsupported compile-time value ${ir2string(value)}")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun genAnnotationsOnTypeParametersAndBounds(
|
||||
context: JvmBackendContext,
|
||||
typeParameterContainer: IrTypeParametersContainer,
|
||||
classCodegen: ClassCodegen,
|
||||
referenceType: Int,
|
||||
boundType: Int,
|
||||
visitor: (typeRef: Int, typePath: TypePath?, descriptor: String, visible: Boolean) -> AnnotationVisitor
|
||||
) {
|
||||
if (context.state.target != JVM_1_6) {
|
||||
typeParameterContainer.typeParameters.forEachIndexed { index, typeParameter ->
|
||||
object : AnnotationCodegen(classCodegen, context, true) {
|
||||
override fun visitAnnotation(descr: String, visible: Boolean): AnnotationVisitor {
|
||||
|
||||
return visitor(
|
||||
TypeReference.newTypeParameterReference(referenceType, index).value,
|
||||
null,
|
||||
descr,
|
||||
visible
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitTypeAnnotation(descr: String, path: TypePath?, visible: Boolean): AnnotationVisitor {
|
||||
throw RuntimeException(
|
||||
"Error during generation: type annotation shouldn't be presented on type parameter: " +
|
||||
"${ir2string(typeParameter)} in ${ir2string(typeParameterContainer)}"
|
||||
)
|
||||
}
|
||||
}.genAnnotations(typeParameter, null, null)
|
||||
|
||||
if (context.state.configuration.getBoolean(JVMConfigurationKeys.EMIT_JVM_TYPE_ANNOTATIONS)) {
|
||||
var superInterfaceIndex = 1
|
||||
typeParameter.superTypes.forEach { superType ->
|
||||
val isClassOrTypeParameter = !superType.isInterface() && !superType.isAnnotation()
|
||||
val superIndex = if (isClassOrTypeParameter) 0 else superInterfaceIndex++
|
||||
object : AnnotationCodegen(classCodegen, context, true) {
|
||||
override fun visitAnnotation(descr: String, visible: Boolean): AnnotationVisitor {
|
||||
throw RuntimeException(
|
||||
"Error during generation: only type annotations should be presented on type parameters bounds: " +
|
||||
"${ir2string(typeParameter)} in ${ir2string(typeParameter)}"
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitTypeAnnotation(descr: String, path: TypePath?, visible: Boolean): AnnotationVisitor {
|
||||
return visitor(
|
||||
TypeReference.newTypeParameterBoundReference(boundType, index, superIndex).value,
|
||||
path,
|
||||
descr,
|
||||
visible
|
||||
)
|
||||
}
|
||||
}.generateTypeAnnotations(typeParameterContainer, superType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isInvisibleForNullabilityAnalysis(declaration: IrDeclaration): Boolean =
|
||||
when {
|
||||
declaration.origin.isSynthetic ->
|
||||
true
|
||||
declaration.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD ||
|
||||
declaration.origin == IrDeclarationOrigin.GENERATED_SAM_IMPLEMENTATION ->
|
||||
true
|
||||
else ->
|
||||
false
|
||||
}
|
||||
|
||||
private val annotationRetentionMap = mapOf(
|
||||
KotlinRetention.SOURCE to RetentionPolicy.SOURCE,
|
||||
KotlinRetention.BINARY to RetentionPolicy.CLASS,
|
||||
KotlinRetention.RUNTIME to RetentionPolicy.RUNTIME
|
||||
)
|
||||
|
||||
internal val internalAnnotations = setOf(
|
||||
JvmSymbols.FLEXIBLE_NULLABILITY_ANNOTATION_FQ_NAME,
|
||||
JvmSymbols.FLEXIBLE_MUTABILITY_ANNOTATION_FQ_NAME,
|
||||
JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION,
|
||||
JvmSymbols.RAW_TYPE_ANNOTATION_FQ_NAME
|
||||
)
|
||||
|
||||
private fun getRetentionPolicy(irClass: IrClass): RetentionPolicy {
|
||||
val retention = irClass.getAnnotationRetention()
|
||||
if (retention != null) {
|
||||
@Suppress("MapGetWithNotNullAssertionOperator")
|
||||
return annotationRetentionMap[retention]!!
|
||||
}
|
||||
irClass.getAnnotation(FqName(java.lang.annotation.Retention::class.java.name))?.let { retentionAnnotation ->
|
||||
val value = retentionAnnotation.getValueArgument(0)
|
||||
if (value is IrDeclarationReference) {
|
||||
val symbol = value.symbol
|
||||
if (symbol is IrEnumEntrySymbol) {
|
||||
val entry = symbol.owner
|
||||
val enumClassFqName = entry.parentAsClass.fqNameWhenAvailable
|
||||
if (RetentionPolicy::class.java.name == enumClassFqName?.asString()) {
|
||||
return RetentionPolicy.valueOf(entry.name.asString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RetentionPolicy.RUNTIME
|
||||
}
|
||||
|
||||
/* Temporary? */
|
||||
fun IrConstructorCall.applicableTargetSet() =
|
||||
annotationClass.applicableTargetSet() ?: KotlinTarget.DEFAULT_TARGET_SET
|
||||
|
||||
val IrConstructorCall.annotationClass get() = symbol.owner.parentAsClass
|
||||
}
|
||||
|
||||
internal fun generateTypeAnnotations(
|
||||
annotated: IrAnnotationContainer,
|
||||
type: IrType?
|
||||
) {
|
||||
if ((annotated as? IrDeclaration)?.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR ||
|
||||
type == null || context.state.target === JVM_1_6 ||
|
||||
!context.state.configuration.getBoolean(JVMConfigurationKeys.EMIT_JVM_TYPE_ANNOTATIONS)
|
||||
) {
|
||||
return
|
||||
}
|
||||
val infos: Iterable<TypePathInfo<IrConstructorCall>> =
|
||||
IrTypeAnnotationCollector(context.typeMapper.typeSystem).collectTypeAnnotations(type)
|
||||
for (info in infos) {
|
||||
for (annotation in info.annotations) {
|
||||
genAnnotation(annotation, info.path, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class IrTypeAnnotationCollector(context: TypeSystemCommonBackendContext) : TypeAnnotationCollector<IrConstructorCall>(context) {
|
||||
|
||||
override fun KotlinTypeMarker.extractAnnotations(): List<IrConstructorCall> {
|
||||
require(this is IrType)
|
||||
return annotations.filter {
|
||||
// We only generate annotations which have the TYPE_USE Java target.
|
||||
// Those are type annotations which were compiled with JVM target bytecode version 1.8 or greater
|
||||
(it.annotationClass.origin != IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB &&
|
||||
it.annotationClass.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) ||
|
||||
it.annotationClass.isCompiledToJvm8OrHigher
|
||||
}
|
||||
}
|
||||
|
||||
private val IrClass.isCompiledToJvm8OrHigher: Boolean
|
||||
get() =
|
||||
(origin == IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB || origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) &&
|
||||
isCompiledToJvm8OrHigher(source)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface InnerClassConsumer {
|
||||
fun addInnerClassInfoFromAnnotation(innerClass: IrClass)
|
||||
}
|
||||
|
||||
private fun isBareTypeParameterWithNullableUpperBound(type: IrType): Boolean {
|
||||
return type.classifierOrNull?.owner is IrTypeParameter && !type.isMarkedNullable() && type.isNullable()
|
||||
}
|
||||
|
||||
// Copied and modified from AnnotationChecker.kt
|
||||
|
||||
private val TARGET_ALLOWED_TARGETS = Name.identifier("allowedTargets")
|
||||
|
||||
private fun IrClass.applicableTargetSet(): Set<KotlinTarget>? {
|
||||
val targetEntry = getAnnotation(StandardNames.FqNames.target) ?: return null
|
||||
return loadAnnotationTargets(targetEntry)
|
||||
}
|
||||
|
||||
private fun loadAnnotationTargets(targetEntry: IrConstructorCall): Set<KotlinTarget>? {
|
||||
val valueArgument = targetEntry.getValueArgument(TARGET_ALLOWED_TARGETS)
|
||||
as? IrVararg ?: return null
|
||||
return valueArgument.elements.filterIsInstance<IrGetEnumValue>().mapNotNull {
|
||||
KotlinTarget.valueOrNull(it.symbol.owner.name.asString())
|
||||
}.toSet()
|
||||
}
|
||||
+614
@@ -0,0 +1,614 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.lower.ANNOTATION_IMPLEMENTATION
|
||||
import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.MultifileFacadeFileEntry
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.mapClass
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.mapType
|
||||
import org.jetbrains.kotlin.backend.jvm.metadata.MetadataSerializer
|
||||
import org.jetbrains.kotlin.codegen.DescriptorAsmUtil
|
||||
import org.jetbrains.kotlin.codegen.VersionIndependentOpcodes
|
||||
import org.jetbrains.kotlin.codegen.addRecordComponent
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.writeKotlinMetadata
|
||||
import org.jetbrains.kotlin.config.JvmAnalysisFlags
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.IrTypeProjection
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.BitEncoding
|
||||
import org.jetbrains.kotlin.name.JvmNames.JVM_RECORD_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.name.JvmNames.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.name.JvmNames.TRANSIENT_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.name.JvmNames.VOLATILE_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.jvm.checkers.JvmSimpleNameBacktickChecker
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import java.io.File
|
||||
|
||||
class ClassCodegen private constructor(
|
||||
val irClass: IrClass,
|
||||
val context: JvmBackendContext,
|
||||
private val parentFunction: IrFunction?,
|
||||
) : InnerClassConsumer {
|
||||
// We need to avoid recursive calls to getOrCreate() from within the constructor to prevent lockups
|
||||
// in ConcurrentHashMap context.classCodegens.
|
||||
private val parentClassCodegen by lazy {
|
||||
(parentFunction?.parentAsClass ?: irClass.parent as? IrClass)?.let { getOrCreate(it, context) }
|
||||
}
|
||||
private val withinInline: Boolean by lazy { parentClassCodegen?.withinInline == true || parentFunction?.isInline == true }
|
||||
private val metadataSerializer: MetadataSerializer by lazy {
|
||||
context.backendExtension.createSerializer(
|
||||
context, irClass, type, visitor.serializationBindings, parentClassCodegen?.metadataSerializer
|
||||
)
|
||||
}
|
||||
|
||||
private val state get() = context.state
|
||||
private val typeMapper get() = context.typeMapper
|
||||
|
||||
val type: Type = typeMapper.mapClass(irClass)
|
||||
|
||||
val reifiedTypeParametersUsages = ReifiedTypeParametersUsages()
|
||||
|
||||
private val jvmSignatureClashDetector = JvmSignatureClashDetector(irClass, type, context)
|
||||
|
||||
private val classOrigin = irClass.descriptorOrigin
|
||||
|
||||
private val visitor = state.factory.newVisitor(classOrigin, type, irClass.fileParent.loadSourceFilesInfo()).apply {
|
||||
val signature = typeMapper.mapClassSignature(irClass, type)
|
||||
// Ensure that the backend only produces class names that would be valid in the frontend for JVM.
|
||||
if (context.state.classBuilderMode.generateBodies && signature.hasInvalidName()) {
|
||||
throw IllegalStateException("Generating class with invalid name '${type.className}': ${irClass.dump()}")
|
||||
}
|
||||
defineClass(
|
||||
irClass.psiElement,
|
||||
state.classFileVersion,
|
||||
irClass.getFlags(context.state.languageVersionSettings),
|
||||
signature.name,
|
||||
signature.javaGenericSignature,
|
||||
signature.superclassName,
|
||||
signature.interfaces.toTypedArray()
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: the order of entries in this set depends on the order in which methods are generated; this means it is unstable
|
||||
// under incremental compilation, as calls to `inline fun`s declared in this class cause them to be generated out of order.
|
||||
private val innerClasses = linkedSetOf<IrClass>()
|
||||
|
||||
// TODO: the names produced by generators in this map depend on the order in which methods are generated; see above.
|
||||
private val regeneratedObjectNameGenerators = mutableMapOf<String, NameGenerator>()
|
||||
|
||||
fun getRegeneratedObjectNameGenerator(function: IrFunction): NameGenerator {
|
||||
val name = if (function.name.isSpecial) "special" else function.name.asString()
|
||||
return regeneratedObjectNameGenerators.getOrPut(name) {
|
||||
NameGenerator("${type.internalName}\$$name\$\$inlined")
|
||||
}
|
||||
}
|
||||
|
||||
private var generated = false
|
||||
|
||||
fun generate() {
|
||||
// TODO: reject repeated generate() calls; currently, these can happen for objects in finally
|
||||
// blocks since they are `accept`ed once per each CFG edge out of the try-finally.
|
||||
if (generated) return
|
||||
generated = true
|
||||
|
||||
// Generate PermittedSubclasses attribute for sealed class.
|
||||
if (state.languageVersionSettings.supportsFeature(LanguageFeature.JvmPermittedSubclassesAttributeForSealed) &&
|
||||
irClass.modality == Modality.SEALED &&
|
||||
state.target >= JvmTarget.JVM_17
|
||||
) {
|
||||
generatePermittedSubclasses()
|
||||
}
|
||||
|
||||
// Generating a method node may cause the addition of a field with an initializer if an inline function
|
||||
// call uses `assert` and the JVM assertions mode is enabled. To avoid concurrent modification errors,
|
||||
// there is a very specific generation order.
|
||||
val smap = context.getSourceMapper(irClass)
|
||||
// 1. Any method other than `<clinit>` can add a field and a `<clinit>` statement:
|
||||
for (method in irClass.declarations.filterIsInstance<IrFunction>()) {
|
||||
if (method.name.asString() != "<clinit>" &&
|
||||
method.origin != JvmLoweredDeclarationOrigin.INLINE_LAMBDA &&
|
||||
method.origin != IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR
|
||||
) {
|
||||
generateMethod(method, smap)
|
||||
}
|
||||
}
|
||||
// 2. `<clinit>` itself can add a field, but the statement is generated via the `return init` hack:
|
||||
irClass.functions.find { it.name.asString() == "<clinit>" }?.let { generateMethod(it, smap) }
|
||||
// 3. Now we have all the fields, including `$assertionsDisabled` if needed:
|
||||
for (field in irClass.fields) {
|
||||
generateField(field)
|
||||
}
|
||||
// 4. Generate nested classes at the end, to ensure that when the companion's metadata is serialized
|
||||
// everything moved to the outer class has already been recorded in `globalSerializationBindings`.
|
||||
for (declaration in irClass.declarations) {
|
||||
if (declaration is IrClass) {
|
||||
getOrCreate(declaration, context).generate()
|
||||
}
|
||||
}
|
||||
|
||||
object : AnnotationCodegen(this@ClassCodegen, context) {
|
||||
override fun visitAnnotation(descr: String, visible: Boolean): AnnotationVisitor {
|
||||
return visitor.visitor.visitAnnotation(descr, visible)
|
||||
}
|
||||
}.genAnnotations(irClass, null, null)
|
||||
|
||||
AnnotationCodegen.genAnnotationsOnTypeParametersAndBounds(
|
||||
context,
|
||||
irClass,
|
||||
this,
|
||||
TypeReference.CLASS_TYPE_PARAMETER,
|
||||
TypeReference.CLASS_TYPE_PARAMETER_BOUND
|
||||
) { typeRef: Int, typePath: TypePath?, descriptor: String, visible: Boolean ->
|
||||
visitor.visitor.visitTypeAnnotation(typeRef, typePath, descriptor, visible)
|
||||
}
|
||||
|
||||
generateKotlinMetadataAnnotation()
|
||||
|
||||
generateInnerAndOuterClasses()
|
||||
|
||||
if (withinInline || !smap.isTrivial) {
|
||||
visitor.visitSMAP(smap, !context.state.languageVersionSettings.supportsFeature(LanguageFeature.CorrectSourceMappingSyntax))
|
||||
} else {
|
||||
smap.sourceInfo!!.sourceFileName?.let {
|
||||
visitor.visitSource(it, null)
|
||||
}
|
||||
}
|
||||
|
||||
addReifiedParametersFromSignature()
|
||||
|
||||
visitor.done()
|
||||
jvmSignatureClashDetector.reportErrors(classOrigin)
|
||||
}
|
||||
|
||||
private fun generatePermittedSubclasses() {
|
||||
val sealedSubclasses = irClass.sealedSubclasses
|
||||
if (sealedSubclasses.isEmpty()) return
|
||||
val classVisitor = visitor.visitor
|
||||
for (sealedSubclassSymbol in sealedSubclasses) {
|
||||
classVisitor.visitPermittedSubclass(typeMapper.mapClass(sealedSubclassSymbol.owner).internalName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addReifiedParametersFromSignature() {
|
||||
for (type in irClass.superTypes) {
|
||||
processTypeParameters(type)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processTypeParameters(type: IrType) {
|
||||
for (supertypeArgument in (type as? IrSimpleType)?.arguments ?: emptyList()) {
|
||||
if (supertypeArgument is IrTypeProjection) {
|
||||
val typeArgument = supertypeArgument.type
|
||||
if (typeArgument.isReifiedTypeParameter) {
|
||||
reifiedTypeParametersUsages.addUsedReifiedParameter(typeArgument.classifierOrFail.cast<IrTypeParameterSymbol>().owner.name.asString())
|
||||
} else {
|
||||
processTypeParameters(typeArgument)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun generateAssertFieldIfNeeded(generatingClInit: Boolean): IrExpression? {
|
||||
if (irClass.hasAssertionsDisabledField(context))
|
||||
return null
|
||||
val topLevelClass = generateSequence(this) { it.parentClassCodegen }.last().irClass
|
||||
val field = irClass.buildAssertionsDisabledField(context, topLevelClass)
|
||||
irClass.declarations.add(0, field)
|
||||
// Normally, `InitializersLowering` would move the initializer to <clinit>, but it's obviously too late for that.
|
||||
val init = with(field) {
|
||||
IrSetFieldImpl(startOffset, endOffset, symbol, null, initializer!!.expression, context.irBuiltIns.unitType)
|
||||
}
|
||||
if (generatingClInit) {
|
||||
// Too late to modify the IR; have to ask the currently active `ExpressionCodegen` to generate this statement
|
||||
// directly. At least we know that nothing before this point uses the field.
|
||||
return init
|
||||
}
|
||||
val classInitializer = irClass.functions.singleOrNull { it.name.asString() == "<clinit>" } ?: irClass.addFunction {
|
||||
name = Name.special("<clinit>")
|
||||
returnType = context.irBuiltIns.unitType
|
||||
}.apply {
|
||||
body = IrBlockBodyImpl(startOffset, endOffset)
|
||||
}
|
||||
// Should be initialized first in case some inline function call in `<clinit>` also uses assertions.
|
||||
(classInitializer.body as IrBlockBody).statements.add(0, init)
|
||||
return null
|
||||
}
|
||||
|
||||
private fun generateKotlinMetadataAnnotation() {
|
||||
val facadeClassName = context.multifileFacadeForPart[irClass.attributeOwnerId]
|
||||
val metadata = irClass.metadata
|
||||
val entry = irClass.fileParent.fileEntry
|
||||
val kind = when {
|
||||
facadeClassName != null -> KotlinClassHeader.Kind.MULTIFILE_CLASS_PART
|
||||
metadata is MetadataSource.Class -> KotlinClassHeader.Kind.CLASS
|
||||
metadata is MetadataSource.Script -> KotlinClassHeader.Kind.CLASS
|
||||
metadata is MetadataSource.File -> KotlinClassHeader.Kind.FILE_FACADE
|
||||
metadata is MetadataSource.Function -> KotlinClassHeader.Kind.SYNTHETIC_CLASS
|
||||
entry is MultifileFacadeFileEntry -> KotlinClassHeader.Kind.MULTIFILE_CLASS
|
||||
else -> KotlinClassHeader.Kind.SYNTHETIC_CLASS
|
||||
}
|
||||
val serializedIr = when (metadata) {
|
||||
is MetadataSource.Class -> metadata.serializedIr
|
||||
is MetadataSource.File -> metadata.serializedIr
|
||||
else -> null
|
||||
}
|
||||
|
||||
val isMultifileClassOrPart = kind == KotlinClassHeader.Kind.MULTIFILE_CLASS || kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART
|
||||
|
||||
var extraFlags = context.backendExtension.generateMetadataExtraFlags(state.abiStability)
|
||||
if (isMultifileClassOrPart && state.languageVersionSettings.getFlag(JvmAnalysisFlags.inheritMultifileParts)) {
|
||||
extraFlags = extraFlags or JvmAnnotationNames.METADATA_MULTIFILE_PARTS_INHERIT_FLAG
|
||||
}
|
||||
if (metadata is MetadataSource.Script) {
|
||||
extraFlags = extraFlags or JvmAnnotationNames.METADATA_SCRIPT_FLAG
|
||||
}
|
||||
|
||||
// There are four kinds of classes which are regenerated during inlining.
|
||||
// 1) Anonymous classes which are in the scope of an inline function.
|
||||
// 2) SAM wrappers used in an inline function. These are identified by name, since they
|
||||
// can be reused in different functions and are thus generated in the enclosing top-level
|
||||
// class instead of inside of an inline function.
|
||||
// 3) WhenMapping classes used from public inline functions. These are collected in
|
||||
// `JvmBackendContext.publicAbiSymbols` in `MappedEnumWhenLowering`.
|
||||
// 4) Annotation implementation classes used from public inline function. Similar to
|
||||
// public WhenMapping classes, these are collected in `publicAbiSymbols` in
|
||||
// `JvmAnnotationImplementationTransformer`.
|
||||
val isPublicAbi = irClass.symbol in context.publicAbiSymbols || irClass.isInlineSamWrapper ||
|
||||
type.isAnonymousClass && irClass.isInPublicInlineScope
|
||||
|
||||
writeKotlinMetadata(visitor, state, kind, isPublicAbi, extraFlags) { av ->
|
||||
if (metadata != null) {
|
||||
metadataSerializer.serialize(metadata)?.let { (proto, stringTable) ->
|
||||
DescriptorAsmUtil.writeAnnotationData(av, proto, stringTable)
|
||||
}
|
||||
}
|
||||
|
||||
if (entry is MultifileFacadeFileEntry) {
|
||||
val arv = av.visitArray(JvmAnnotationNames.METADATA_DATA_FIELD_NAME)
|
||||
for (partFile in entry.partFiles) {
|
||||
val fileClass = partFile.declarations.singleOrNull { it.isFileClass } as IrClass?
|
||||
if (fileClass != null) arv.visit(null, typeMapper.mapClass(fileClass).internalName)
|
||||
}
|
||||
arv.visitEnd()
|
||||
}
|
||||
|
||||
if (facadeClassName != null) {
|
||||
av.visit(JvmAnnotationNames.METADATA_MULTIFILE_CLASS_NAME_FIELD_NAME, facadeClassName.internalName)
|
||||
}
|
||||
|
||||
if (irClass in context.classNameOverride) {
|
||||
val isFileClass = isMultifileClassOrPart || kind == KotlinClassHeader.Kind.FILE_FACADE
|
||||
assert(isFileClass) { "JvmPackageName is not supported for classes: ${irClass.render()}" }
|
||||
av.visit(JvmAnnotationNames.METADATA_PACKAGE_NAME_FIELD_NAME, irClass.fqNameWhenAvailable!!.parent().asString())
|
||||
}
|
||||
}
|
||||
serializedIr?.let { storeSerializedIr(it) }
|
||||
}
|
||||
|
||||
private fun IrFile.loadSourceFilesInfo(): List<File> {
|
||||
val entry = fileEntry
|
||||
if (entry is MultifileFacadeFileEntry) {
|
||||
return entry.partFiles.flatMap { it.loadSourceFilesInfo() }
|
||||
}
|
||||
return listOf(File(entry.name))
|
||||
}
|
||||
|
||||
private fun generateField(field: IrField) {
|
||||
val fieldType = typeMapper.mapType(field)
|
||||
val fieldSignature =
|
||||
if (field.origin == IrDeclarationOrigin.PROPERTY_DELEGATE) null
|
||||
else context.methodSignatureMapper.mapFieldSignature(field)
|
||||
val fieldName = field.name.asString()
|
||||
val flags = field.computeFieldFlags(context, state.languageVersionSettings)
|
||||
val fv = visitor.newField(
|
||||
field.descriptorOrigin, flags, fieldName, fieldType.descriptor,
|
||||
fieldSignature, (field.initializer?.expression as? IrConst<*>)?.value
|
||||
)
|
||||
|
||||
jvmSignatureClashDetector.trackField(field, RawSignature(fieldName, fieldType.descriptor, MemberKind.FIELD))
|
||||
|
||||
if (field.origin != JvmLoweredDeclarationOrigin.CONTINUATION_CLASS_RESULT_FIELD) {
|
||||
val skipNullabilityAnnotations =
|
||||
flags and (Opcodes.ACC_SYNTHETIC or Opcodes.ACC_ENUM) != 0 ||
|
||||
(field.origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE && irClass.isSyntheticSingleton)
|
||||
object : AnnotationCodegen(this@ClassCodegen, context, skipNullabilityAnnotations) {
|
||||
override fun visitAnnotation(descr: String, visible: Boolean): AnnotationVisitor {
|
||||
return fv.visitAnnotation(descr, visible)
|
||||
}
|
||||
|
||||
override fun visitTypeAnnotation(descr: String, path: TypePath?, visible: Boolean): AnnotationVisitor {
|
||||
return fv.visitTypeAnnotation(TypeReference.newTypeReference(TypeReference.FIELD).value, path, descr, visible)
|
||||
}
|
||||
}.genAnnotations(field, fieldType, field.type)
|
||||
}
|
||||
|
||||
(field.metadata as? MetadataSource.Property)?.let {
|
||||
metadataSerializer.bindFieldMetadata(it, fieldType to fieldName)
|
||||
}
|
||||
|
||||
if (irClass.hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME) && !field.isStatic) {
|
||||
// TODO: Write annotations to the component
|
||||
visitor.addRecordComponent(fieldName, fieldType.descriptor, fieldSignature)
|
||||
}
|
||||
}
|
||||
|
||||
private val generatedInlineMethods = mutableMapOf<IrFunction, SMAPAndMethodNode>()
|
||||
|
||||
fun generateMethodNode(method: IrFunction): SMAPAndMethodNode {
|
||||
if (!method.isInline && !method.isSuspendCapturingCrossinline()) {
|
||||
// Inline methods can be used multiple times by `IrSourceCompilerForInline`, suspend methods
|
||||
// are used twice (`f` and `f$$forInline`) if they capture crossinline lambdas, and everything
|
||||
// else is only generated by `generateMethod` below so does not need caching.
|
||||
// TODO: inline lambdas are not marked `isInline`, and are generally used once, but may be needed
|
||||
// multiple times if declared in a `finally` block - should they be cached?
|
||||
return FunctionCodegen(method, this).generate()
|
||||
}
|
||||
|
||||
// Only allow generation of one inline method at a time, to avoid deadlocks when files call inline methods of each other.
|
||||
val (node, smap) =
|
||||
generatedInlineMethods[method] ?: synchronized(context.inlineMethodGenerationLock) {
|
||||
generatedInlineMethods.getOrPut(method) { FunctionCodegen(method, this).generate() }
|
||||
}
|
||||
return SMAPAndMethodNode(cloneMethodNode(node), smap)
|
||||
}
|
||||
|
||||
private fun generateMethod(method: IrFunction, classSMAP: SourceMapper) {
|
||||
if (method.isFakeOverride) {
|
||||
jvmSignatureClashDetector.trackFakeOverrideMethod(method)
|
||||
return
|
||||
}
|
||||
|
||||
val (node, smap) = generateMethodNode(method)
|
||||
node.preprocessSuspendMarkers(
|
||||
method.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE || method.isEffectivelyInlineOnly(),
|
||||
method.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
|
||||
)
|
||||
val mv = with(node) { visitor.newMethod(method.descriptorOrigin, access, name, desc, signature, exceptions.toTypedArray()) }
|
||||
val smapCopier = SourceMapCopier(classSMAP, smap)
|
||||
val smapCopyingVisitor = object : MethodVisitor(Opcodes.API_VERSION, mv) {
|
||||
override fun visitLineNumber(line: Int, start: Label) =
|
||||
super.visitLineNumber(smapCopier.mapLineNumber(line), start)
|
||||
}
|
||||
if (method.hasContinuation()) {
|
||||
// Generate a state machine within this method. The continuation class for it should be generated
|
||||
// lazily so that if tail call optimization kicks in, the unused class will not be written to the output.
|
||||
val continuationClass = method.continuationClass() // null if `SuspendLambda.invokeSuspend` - `this` is continuation itself
|
||||
val continuationClassCodegen = lazy { if (continuationClass != null) getOrCreate(continuationClass, context, method) else this }
|
||||
|
||||
// For suspend lambdas continuation class is null, and we need to use containing class to put L$ fields
|
||||
val attributeContainer = continuationClass?.attributeOwnerId ?: irClass.attributeOwnerId
|
||||
|
||||
node.acceptWithStateMachine(
|
||||
method,
|
||||
this,
|
||||
smapCopyingVisitor,
|
||||
context.continuationClassesVarsCountByType[attributeContainer] ?: emptyMap()
|
||||
) {
|
||||
continuationClassCodegen.value.visitor
|
||||
}
|
||||
|
||||
if (continuationClass != null && (continuationClassCodegen.isInitialized() || method.isSuspendCapturingCrossinline())) {
|
||||
continuationClassCodegen.value.generate()
|
||||
}
|
||||
} else {
|
||||
node.accept(smapCopyingVisitor)
|
||||
}
|
||||
jvmSignatureClashDetector.trackMethod(method, RawSignature(node.name, node.desc, MemberKind.METHOD))
|
||||
|
||||
when (val metadata = method.metadata) {
|
||||
is MetadataSource.Property -> metadataSerializer.bindPropertyMetadata(metadata, Method(node.name, node.desc), method.origin)
|
||||
is MetadataSource.Function -> metadataSerializer.bindMethodMetadata(metadata, Method(node.name, node.desc))
|
||||
null -> Unit
|
||||
else -> error("Incorrect metadata source $metadata for:\n${method.dump()}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateInnerAndOuterClasses() {
|
||||
// JVMS7 (4.7.6): a nested class or interface member will have InnerClasses information
|
||||
// for each enclosing class and for each immediate member
|
||||
parentClassCodegen?.innerClasses?.add(irClass)
|
||||
for (codegen in generateSequence(this) { it.parentClassCodegen }.takeWhile { it.parentClassCodegen != null }) {
|
||||
innerClasses.add(codegen.irClass)
|
||||
}
|
||||
|
||||
// JVMS7 (4.7.7): A class must have an EnclosingMethod attribute if and only if
|
||||
// it is a local class or an anonymous class.
|
||||
//
|
||||
// The attribute contains the innermost class that encloses the declaration of
|
||||
// the current class. If the current class is immediately enclosed by a method
|
||||
// or constructor, the name and type of the function is recorded as well.
|
||||
if (parentClassCodegen != null) {
|
||||
// In case there's no primary constructor, it's unclear which constructor should be the enclosing one, so we select the first.
|
||||
val enclosingFunction = if (irClass.attributeOwnerId in context.isEnclosedInConstructor) {
|
||||
val containerClass = parentClassCodegen!!.irClass
|
||||
containerClass.primaryConstructor
|
||||
?: containerClass.declarations.firstIsInstanceOrNull<IrConstructor>()
|
||||
?: error("Class in a non-static initializer found, but container has no constructors: ${containerClass.render()}")
|
||||
} else parentFunction
|
||||
if (enclosingFunction != null || irClass.isAnonymousInnerClass) {
|
||||
val method = enclosingFunction?.let(context.methodSignatureMapper::mapAsmMethod)?.takeIf { it.name != "<clinit>" }
|
||||
visitor.visitOuterClass(parentClassCodegen!!.type.internalName, method?.name, method?.descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
for (klass in innerClasses) {
|
||||
val innerClass = typeMapper.classInternalName(klass)
|
||||
val outerClass =
|
||||
if (klass.isSamWrapper || klass.isAnnotationImplementation || klass.attributeOwnerId in context.isEnclosedInConstructor)
|
||||
null
|
||||
else {
|
||||
when (val parent = klass.parent) {
|
||||
is IrClass -> typeMapper.classInternalName(parent)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
val innerName = if (klass.isAnonymousInnerClass) null else klass.name.asString()
|
||||
val accessFlags = klass.calculateInnerClassAccessFlags(context)
|
||||
visitor.visitInnerClass(innerClass, outerClass, innerName, accessFlags)
|
||||
}
|
||||
}
|
||||
|
||||
private val IrClass.isAnonymousInnerClass: Boolean
|
||||
get() = isSamWrapper || name.isSpecial || isAnnotationImplementation // NB '<Continuation>' is treated as anonymous inner class here
|
||||
|
||||
private val IrClass.isInlineSamWrapper: Boolean
|
||||
get() = isSamWrapper && visibility == DescriptorVisibilities.PUBLIC
|
||||
|
||||
private val IrClass.isSamWrapper: Boolean
|
||||
get() = origin == IrDeclarationOrigin.GENERATED_SAM_IMPLEMENTATION
|
||||
|
||||
private val IrClass.isAnnotationImplementation: Boolean
|
||||
get() = origin == ANNOTATION_IMPLEMENTATION
|
||||
|
||||
override fun addInnerClassInfoFromAnnotation(innerClass: IrClass) {
|
||||
// It's necessary for proper recovering of classId by plain string JVM descriptor when loading annotations
|
||||
// See FileBasedKotlinClass.convertAnnotationVisitor
|
||||
generateSequence<IrDeclaration>(innerClass) { it.parent as? IrDeclaration }.takeWhile { !it.isTopLevelDeclaration }.forEach {
|
||||
if (it is IrClass) {
|
||||
innerClasses.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val IrDeclaration.descriptorOrigin: JvmDeclarationOrigin
|
||||
get() {
|
||||
val psiElement = PsiSourceManager.findPsiElement(this)
|
||||
return if (origin == IrDeclarationOrigin.FILE_CLASS)
|
||||
JvmDeclarationOrigin(JvmDeclarationOriginKind.PACKAGE_PART, psiElement, toIrBasedDescriptor())
|
||||
else
|
||||
OtherOrigin(psiElement, toIrBasedDescriptor())
|
||||
}
|
||||
|
||||
private fun storeSerializedIr(serializedIr: ByteArray) {
|
||||
val av = visitor.newAnnotation(JvmAnnotationNames.SERIALIZED_IR_DESC, true)
|
||||
val partsVisitor = av.visitArray(JvmAnnotationNames.SERIALIZED_IR_BYTES_FIELD_NAME)
|
||||
val serializedIrParts = BitEncoding.encodeBytes(serializedIr)
|
||||
for (part in serializedIrParts) {
|
||||
partsVisitor.visit(null, part)
|
||||
}
|
||||
partsVisitor.visitEnd()
|
||||
av.visitEnd()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getOrCreate(
|
||||
irClass: IrClass,
|
||||
context: JvmBackendContext,
|
||||
// The `parentFunction` is only set for classes nested inside of functions. This is usually safe, since there is no
|
||||
// way to refer to (inline) members of such a class from outside of the function unless the function in question is
|
||||
// itself declared as inline. In that case, the function will be compiled before we can refer to the nested class.
|
||||
//
|
||||
// The one exception to this rule are anonymous objects defined as members of a class. These are nested inside of the
|
||||
// class initializer, but can be referred to from anywhere within the scope of the class. That's why we have to ensure
|
||||
// that all references to classes inside of <clinit> have a non-null `parentFunction`.
|
||||
parentFunction: IrFunction? = irClass.parent.safeAs<IrFunction>()?.takeIf {
|
||||
it.origin == JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER
|
||||
},
|
||||
): ClassCodegen =
|
||||
context.getOrCreateClassCodegen(irClass) { ClassCodegen(irClass, context, parentFunction) }.also {
|
||||
assert(parentFunction == null || it.parentFunction == parentFunction) {
|
||||
"inconsistent parent function for ${irClass.render()}:\n" +
|
||||
"New: ${parentFunction!!.render()}\n" +
|
||||
"Old: ${it.parentFunction?.render()}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun JvmClassSignature.hasInvalidName() =
|
||||
name.splitToSequence('/').any { identifier -> identifier.any { it in JvmSimpleNameBacktickChecker.INVALID_CHARS } }
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrClass.getFlags(languageVersionSettings: LanguageVersionSettings): Int =
|
||||
origin.flags or
|
||||
getVisibilityAccessFlagForClass() or
|
||||
(if (isAnnotatedWithDeprecated) Opcodes.ACC_DEPRECATED else 0) or
|
||||
getSynthAccessFlag(languageVersionSettings) or
|
||||
when {
|
||||
isAnnotationClass -> Opcodes.ACC_ANNOTATION or Opcodes.ACC_INTERFACE or Opcodes.ACC_ABSTRACT
|
||||
isInterface -> Opcodes.ACC_INTERFACE or Opcodes.ACC_ABSTRACT
|
||||
isEnumClass -> Opcodes.ACC_ENUM or Opcodes.ACC_SUPER or modality.flags
|
||||
hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME) -> VersionIndependentOpcodes.ACC_RECORD or Opcodes.ACC_SUPER or modality.flags
|
||||
else -> Opcodes.ACC_SUPER or modality.flags
|
||||
}
|
||||
|
||||
private fun IrClass.getSynthAccessFlag(languageVersionSettings: LanguageVersionSettings): Int {
|
||||
if (hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME))
|
||||
return Opcodes.ACC_SYNTHETIC
|
||||
if (origin == IrDeclarationOrigin.GENERATED_SAM_IMPLEMENTATION &&
|
||||
languageVersionSettings.supportsFeature(LanguageFeature.SamWrapperClassesAreSynthetic)
|
||||
)
|
||||
return Opcodes.ACC_SYNTHETIC
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun IrField.computeFieldFlags(context: JvmBackendContext, languageVersionSettings: LanguageVersionSettings): Int =
|
||||
origin.flags or visibility.flags or
|
||||
(if (isDeprecatedCallable(context) ||
|
||||
correspondingPropertySymbol?.owner?.isDeprecatedCallable(context) == true
|
||||
) Opcodes.ACC_DEPRECATED else 0) or
|
||||
(if (isFinal) Opcodes.ACC_FINAL else 0) or
|
||||
(if (isStatic) Opcodes.ACC_STATIC else 0) or
|
||||
(if (hasAnnotation(VOLATILE_ANNOTATION_FQ_NAME)) Opcodes.ACC_VOLATILE else 0) or
|
||||
(if (hasAnnotation(TRANSIENT_ANNOTATION_FQ_NAME)) Opcodes.ACC_TRANSIENT else 0) or
|
||||
(if (hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) ||
|
||||
isPrivateCompanionFieldInInterface(languageVersionSettings)
|
||||
) Opcodes.ACC_SYNTHETIC else 0)
|
||||
|
||||
private fun IrField.isPrivateCompanionFieldInInterface(languageVersionSettings: LanguageVersionSettings): Boolean =
|
||||
origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE &&
|
||||
languageVersionSettings.supportsFeature(LanguageFeature.ProperVisibilityForCompanionObjectInstanceField) &&
|
||||
parentAsClass.isJvmInterface &&
|
||||
DescriptorVisibilities.isPrivate(parentAsClass.companionObject()!!.visibility)
|
||||
|
||||
private val IrDeclarationOrigin.flags: Int
|
||||
get() = (if (isSynthetic) Opcodes.ACC_SYNTHETIC else 0) or
|
||||
(if (this == IrDeclarationOrigin.FIELD_FOR_ENUM_ENTRY) Opcodes.ACC_ENUM else 0)
|
||||
|
||||
private val Modality.flags: Int
|
||||
get() = when (this) {
|
||||
Modality.ABSTRACT, Modality.SEALED -> Opcodes.ACC_ABSTRACT
|
||||
Modality.FINAL -> Opcodes.ACC_FINAL
|
||||
Modality.OPEN -> 0
|
||||
else -> throw AssertionError("Unsupported modality $this")
|
||||
}
|
||||
|
||||
private val DescriptorVisibility.flags: Int
|
||||
get() = DescriptorAsmUtil.getVisibilityAccessFlag(this) ?: throw AssertionError("Unsupported visibility $this")
|
||||
|
||||
// From `isAnonymousClass` in inlineCodegenUtils.kt
|
||||
private val Type.isAnonymousClass: Boolean
|
||||
get() = internalName.substringAfterLast("$", "").toIntOrNull() != null
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||
import org.jetbrains.kotlin.backend.common.ir.allOverridden
|
||||
import org.jetbrains.kotlin.backend.jvm.InlineClassAbi
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.backend.jvm.unboxInlineClass
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.coroutines.CoroutineTransformerMethodVisitor
|
||||
import org.jetbrains.kotlin.codegen.coroutines.reportSuspensionPointInsideMonitor
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
internal fun MethodNode.acceptWithStateMachine(
|
||||
irFunction: IrFunction,
|
||||
classCodegen: ClassCodegen,
|
||||
methodVisitor: MethodVisitor,
|
||||
varsCountByType: Map<Type, Int>,
|
||||
obtainContinuationClassBuilder: () -> ClassBuilder,
|
||||
) {
|
||||
val state = classCodegen.context.state
|
||||
val languageVersionSettings = state.languageVersionSettings
|
||||
assert(languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)) { "Experimental coroutines are unsupported in JVM_IR backend" }
|
||||
val element = if (irFunction.isSuspend)
|
||||
irFunction.psiElement ?: classCodegen.irClass.psiElement
|
||||
else
|
||||
classCodegen.context.suspendLambdaToOriginalFunctionMap[classCodegen.irClass.attributeOwnerId]!!.psiElement
|
||||
|
||||
val lineNumber = if (irFunction.isSuspend) {
|
||||
val irFile = irFunction.file
|
||||
if (irFunction.startOffset >= 0) {
|
||||
// if it suspend function like `suspend fun foo(...)`
|
||||
irFile.fileEntry.getLineNumber(irFunction.startOffset) + 1
|
||||
} else {
|
||||
val klass = classCodegen.irClass
|
||||
if (klass.startOffset >= 0) {
|
||||
// if it suspend lambda transformed into class `runSuspend { .... }`
|
||||
irFile.fileEntry.getLineNumber(klass.startOffset) + 1
|
||||
} else 0
|
||||
}
|
||||
} else element?.let { CodegenUtil.getLineNumberForElement(it, false) } ?: 0
|
||||
|
||||
val visitor = CoroutineTransformerMethodVisitor(
|
||||
methodVisitor, access, name, desc, signature, exceptions.toTypedArray(),
|
||||
containingClassInternalName = classCodegen.type.internalName,
|
||||
obtainClassBuilderForCoroutineState = obtainContinuationClassBuilder,
|
||||
isForNamedFunction = irFunction.isSuspend,
|
||||
disableTailCallOptimizationForFunctionReturningUnit = irFunction.isSuspend && irFunction.suspendFunctionOriginal().let {
|
||||
it.returnType.isUnit() && it.anyOfOverriddenFunctionsReturnsNonUnit()
|
||||
},
|
||||
reportSuspensionPointInsideMonitor = { reportSuspensionPointInsideMonitor(element as KtElement, state, it) },
|
||||
lineNumber = lineNumber,
|
||||
sourceFile = classCodegen.irClass.file.name, // SuspendLambda.invokeSuspend is not suspend
|
||||
needDispatchReceiver = irFunction.isSuspend && (irFunction.dispatchReceiverParameter != null
|
||||
|| irFunction.origin == JvmLoweredDeclarationOrigin.SUSPEND_IMPL_STATIC_FUNCTION),
|
||||
internalNameForDispatchReceiver = classCodegen.type.internalName,
|
||||
putContinuationParameterToLvt = false,
|
||||
initialVarsCountByType = varsCountByType,
|
||||
)
|
||||
accept(visitor)
|
||||
}
|
||||
|
||||
private fun IrFunction.anyOfOverriddenFunctionsReturnsNonUnit(): Boolean =
|
||||
this is IrSimpleFunction && allOverridden().any { !it.returnType.isUnit() }
|
||||
|
||||
internal fun IrFunction.suspendForInlineToOriginal(): IrSimpleFunction? {
|
||||
if (origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE &&
|
||||
origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
|
||||
) return null
|
||||
return parentAsClass.declarations.find {
|
||||
// The function may not be named `it.name.asString() + FOR_INLINE_SUFFIX` due to name mangling,
|
||||
// e.g., for internal declarations. We check for a function with the same `attributeOwnerId` instead.
|
||||
// This is copied in `AddContinuationLowering`.
|
||||
it is IrSimpleFunction && it.attributeOwnerId == (this as IrSimpleFunction).attributeOwnerId
|
||||
} as IrSimpleFunction?
|
||||
}
|
||||
|
||||
internal fun IrFunction.isSuspendCapturingCrossinline(): Boolean =
|
||||
this is IrSimpleFunction && hasContinuation() && parentAsClass.declarations.any {
|
||||
it is IrSimpleFunction && it.attributeOwnerId == attributeOwnerId &&
|
||||
it.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
|
||||
}
|
||||
|
||||
internal fun IrFunction.continuationClass(): IrClass? =
|
||||
(body as? IrBlockBody)?.statements?.find { it is IrClass && it.origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS }
|
||||
as IrClass?
|
||||
|
||||
internal fun IrExpression?.isReadOfInlineLambda(): Boolean = isReadOfCrossinline() ||
|
||||
(this is IrGetValue && origin == IrStatementOrigin.VARIABLE_AS_FUNCTION && (symbol.owner as? IrValueParameter)?.isNoinline == false)
|
||||
|
||||
internal fun IrFunction.originalReturnTypeOfSuspendFunctionReturningUnboxedInlineClass(): IrType? {
|
||||
if (this !is IrSimpleFunction || !isSuspend) return null
|
||||
// Unlike `suspendFunctionOriginal()`, this also maps `$default` stubs to the original function.
|
||||
val original = attributeOwnerId as IrSimpleFunction
|
||||
val unboxedReturnType = InlineClassAbi.unboxType(original.returnType) ?: return null
|
||||
// 1. Can't unbox into a primitive, since suspend functions have to return a reference type.
|
||||
// 2. Force boxing if the function overrides function with different type modulo nullability ignoring type parameters
|
||||
if (unboxedReturnType.isPrimitiveType() || original.overridesReturningDifferentType(original.returnType)) return null
|
||||
return original.returnType
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction.overridesReturningDifferentType(returnType: IrType): Boolean {
|
||||
val visited = hashSetOf<IrSimpleFunction>()
|
||||
|
||||
fun dfs(function: IrSimpleFunction): Boolean {
|
||||
if (!visited.add(function)) return false
|
||||
|
||||
for (overridden in function.overriddenSymbols) {
|
||||
val owner = overridden.owner
|
||||
val overriddenReturnType = owner.returnType
|
||||
|
||||
if (!overriddenReturnType.erasedUpperBound.isInline) return true
|
||||
|
||||
if (overriddenReturnType.isNullable() &&
|
||||
overriddenReturnType.makeNotNull().unboxInlineClass().isNullable()
|
||||
) return true
|
||||
|
||||
if (overriddenReturnType.classOrNull != returnType.classOrNull) return true
|
||||
|
||||
if (dfs(owner)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return dfs(this)
|
||||
}
|
||||
+1531
File diff suppressed because it is too large
Load Diff
+359
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.allOverridden
|
||||
import org.jetbrains.kotlin.backend.common.ir.ir2string
|
||||
import org.jetbrains.kotlin.backend.common.lower.BOUND_RECEIVER_PARAMETER
|
||||
import org.jetbrains.kotlin.backend.common.lower.BOUND_VALUE_PARAMETER
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.mapType
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.mapTypeAsDeclaration
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.codegen.visitAnnotableParameterCount
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
|
||||
import org.jetbrains.kotlin.name.JvmNames.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.name.JvmNames.STRICTFP_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.name.JvmNames.SYNCHRONIZED_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.resolve.annotations.JVM_THROWS_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
class FunctionCodegen(private val irFunction: IrFunction, private val classCodegen: ClassCodegen) {
|
||||
private val context = classCodegen.context
|
||||
|
||||
fun generate(
|
||||
inlinedInto: ExpressionCodegen? = null,
|
||||
reifiedTypeParameters: ReifiedTypeParametersUsages = classCodegen.reifiedTypeParametersUsages
|
||||
): SMAPAndMethodNode =
|
||||
try {
|
||||
doGenerate(inlinedInto, reifiedTypeParameters)
|
||||
} catch (e: Throwable) {
|
||||
throw RuntimeException("Exception while generating code for:\n${irFunction.dump()}", e)
|
||||
}
|
||||
|
||||
private fun doGenerate(inlinedInto: ExpressionCodegen?, reifiedTypeParameters: ReifiedTypeParametersUsages): SMAPAndMethodNode {
|
||||
val signature = context.methodSignatureMapper.mapSignatureWithGeneric(irFunction)
|
||||
val flags = irFunction.calculateMethodFlags()
|
||||
val isSynthetic = flags.and(Opcodes.ACC_SYNTHETIC) != 0
|
||||
val methodNode = MethodNode(
|
||||
Opcodes.API_VERSION,
|
||||
flags,
|
||||
signature.asmMethod.name,
|
||||
signature.asmMethod.descriptor,
|
||||
signature.genericsSignature
|
||||
.takeIf {
|
||||
(irFunction.isInline && irFunction.origin != IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) ||
|
||||
!isSynthetic && irFunction.origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA
|
||||
},
|
||||
getThrownExceptions(irFunction)?.toTypedArray()
|
||||
)
|
||||
val methodVisitor: MethodVisitor = wrapWithMaxLocalCalc(methodNode)
|
||||
|
||||
if (context.state.generateParametersMetadata && !isSynthetic) {
|
||||
generateParameterNames(irFunction, methodVisitor, context.state)
|
||||
}
|
||||
|
||||
if (irFunction.origin !in methodOriginsWithoutAnnotations) {
|
||||
val skipNullabilityAnnotations = flags and Opcodes.ACC_PRIVATE != 0 || flags and Opcodes.ACC_SYNTHETIC != 0
|
||||
object : AnnotationCodegen(classCodegen, context, skipNullabilityAnnotations) {
|
||||
override fun visitAnnotation(descr: String, visible: Boolean): AnnotationVisitor {
|
||||
return methodVisitor.visitAnnotation(descr, visible)
|
||||
}
|
||||
|
||||
override fun visitTypeAnnotation(descr: String, path: TypePath?, visible: Boolean): AnnotationVisitor {
|
||||
return methodVisitor.visitTypeAnnotation(
|
||||
TypeReference.newTypeReference(TypeReference.METHOD_RETURN).value, path, descr, visible
|
||||
)
|
||||
}
|
||||
}.genAnnotations(irFunction, signature.asmMethod.returnType, irFunction.returnType)
|
||||
|
||||
AnnotationCodegen.genAnnotationsOnTypeParametersAndBounds(
|
||||
context,
|
||||
irFunction,
|
||||
classCodegen,
|
||||
TypeReference.METHOD_TYPE_PARAMETER,
|
||||
TypeReference.METHOD_TYPE_PARAMETER_BOUND
|
||||
) { typeRef: Int, typePath: TypePath?, descriptor: String, visible: Boolean ->
|
||||
methodVisitor.visitTypeAnnotation(typeRef, typePath, descriptor, visible)
|
||||
}
|
||||
|
||||
if (shouldGenerateAnnotationsOnValueParameters()) {
|
||||
generateParameterAnnotations(irFunction, methodVisitor, signature, classCodegen, context, skipNullabilityAnnotations)
|
||||
}
|
||||
}
|
||||
|
||||
// `$$forInline` versions of suspend functions have the same bodies as the originals, but with different
|
||||
// name/flags/annotations and with no state machine.
|
||||
val notForInline = irFunction.suspendForInlineToOriginal()
|
||||
val smap = if (!context.state.classBuilderMode.generateBodies || flags.and(Opcodes.ACC_ABSTRACT) != 0 || irFunction.isExternal) {
|
||||
generateAnnotationDefaultValueIfNeeded(methodVisitor)
|
||||
SMAP(listOf())
|
||||
} else if (notForInline != null) {
|
||||
val (originalNode, smap) = classCodegen.generateMethodNode(notForInline)
|
||||
originalNode.accept(MethodBodyVisitor(methodVisitor))
|
||||
smap
|
||||
} else {
|
||||
val sourceMapper = context.getSourceMapper(classCodegen.irClass)
|
||||
val frameMap = irFunction.createFrameMapWithReceivers()
|
||||
context.state.globalInlineContext.enterDeclaration(irFunction.suspendFunctionOriginal().toIrBasedDescriptor())
|
||||
try {
|
||||
val adapter = InstructionAdapter(methodVisitor)
|
||||
ExpressionCodegen(
|
||||
irFunction, signature, frameMap, adapter, classCodegen, inlinedInto, sourceMapper, reifiedTypeParameters
|
||||
).generate()
|
||||
} finally {
|
||||
context.state.globalInlineContext.exitDeclaration()
|
||||
}
|
||||
methodVisitor.visitMaxs(-1, -1)
|
||||
SMAP(sourceMapper.resultMappings)
|
||||
}
|
||||
methodVisitor.visitEnd()
|
||||
return SMAPAndMethodNode(methodNode, smap)
|
||||
}
|
||||
|
||||
private fun shouldGenerateAnnotationsOnValueParameters(): Boolean =
|
||||
when {
|
||||
irFunction.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_OR_TYPEALIAS_ANNOTATIONS ->
|
||||
false
|
||||
irFunction is IrConstructor && irFunction.parentAsClass.shouldNotGenerateConstructorParameterAnnotations() ->
|
||||
false
|
||||
else ->
|
||||
true
|
||||
}
|
||||
|
||||
// Since the only arguments to anonymous object constructors are captured variables and complex
|
||||
// super constructor arguments, there shouldn't be any annotations on them other than @NonNull,
|
||||
// and those are meaningless on synthetic parameters. (Also, the inliner cannot handle them and
|
||||
// will throw an exception if we generate any.)
|
||||
// The same applies for continuations.
|
||||
private fun IrClass.shouldNotGenerateConstructorParameterAnnotations() =
|
||||
isAnonymousObject || origin == JvmLoweredDeclarationOrigin.CONTINUATION_CLASS || origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA
|
||||
|
||||
private fun IrFunction.getVisibilityForDefaultArgumentStub(): Int =
|
||||
when {
|
||||
// TODO: maybe best to generate private default in interface as private
|
||||
visibility == DescriptorVisibilities.PUBLIC || parentAsClass.isJvmInterface -> Opcodes.ACC_PUBLIC
|
||||
visibility == JavaDescriptorVisibilities.PACKAGE_VISIBILITY -> AsmUtil.NO_FLAG_PACKAGE_PRIVATE
|
||||
else -> throw IllegalStateException("Default argument stub should be either public or package private: ${ir2string(this)}")
|
||||
}
|
||||
|
||||
private fun IrFunction.calculateMethodFlags(): Int {
|
||||
if (origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) {
|
||||
return getVisibilityForDefaultArgumentStub() or Opcodes.ACC_SYNTHETIC or
|
||||
(if (isDeprecatedFunction(context)) Opcodes.ACC_DEPRECATED else 0) or
|
||||
(if (this is IrConstructor) 0 else Opcodes.ACC_STATIC)
|
||||
}
|
||||
|
||||
val isVararg = valueParameters.lastOrNull()?.varargElementType != null && !isBridge()
|
||||
val modalityFlag =
|
||||
if (parentAsClass.isAnnotationClass) {
|
||||
if (isStatic) 0 else Opcodes.ACC_ABSTRACT
|
||||
} else {
|
||||
when ((this as? IrSimpleFunction)?.modality) {
|
||||
Modality.FINAL -> when {
|
||||
origin == JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER -> 0
|
||||
origin == IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER -> 0
|
||||
parentAsClass.isInterface && body != null -> 0
|
||||
else -> Opcodes.ACC_FINAL
|
||||
}
|
||||
Modality.ABSTRACT -> Opcodes.ACC_ABSTRACT
|
||||
// TODO transform interface modality on lowering to DefaultImpls
|
||||
else -> if (parentAsClass.isJvmInterface && body == null) Opcodes.ACC_ABSTRACT else 0
|
||||
}
|
||||
}
|
||||
val isSynthetic = origin.isSynthetic ||
|
||||
hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) ||
|
||||
isReifiable() ||
|
||||
isDeprecatedHidden()
|
||||
|
||||
val isStrict = hasAnnotation(STRICTFP_ANNOTATION_FQ_NAME) && origin != JvmLoweredDeclarationOrigin.JVM_OVERLOADS_WRAPPER
|
||||
val isSynchronized = hasAnnotation(SYNCHRONIZED_ANNOTATION_FQ_NAME) && origin != JvmLoweredDeclarationOrigin.JVM_OVERLOADS_WRAPPER
|
||||
|
||||
return getVisibilityAccessFlag() or modalityFlag or
|
||||
(if (isDeprecatedFunction(context)) Opcodes.ACC_DEPRECATED else 0) or
|
||||
(if (isStatic) Opcodes.ACC_STATIC else 0) or
|
||||
(if (isVararg) Opcodes.ACC_VARARGS else 0) or
|
||||
(if (isExternal) Opcodes.ACC_NATIVE else 0) or
|
||||
(if (isBridge()) Opcodes.ACC_BRIDGE else 0) or
|
||||
(if (isSynthetic) Opcodes.ACC_SYNTHETIC else 0) or
|
||||
(if (isStrict) Opcodes.ACC_STRICT else 0) or
|
||||
(if (isSynchronized) Opcodes.ACC_SYNCHRONIZED else 0)
|
||||
}
|
||||
|
||||
private fun IrFunction.isDeprecatedHidden(): Boolean {
|
||||
val mightBeDeprecated = if (this is IrSimpleFunction) {
|
||||
allOverridden(true).any {
|
||||
it.isAnnotatedWithDeprecated || it.correspondingPropertySymbol?.owner?.isAnnotatedWithDeprecated == true
|
||||
}
|
||||
} else {
|
||||
isAnnotatedWithDeprecated
|
||||
}
|
||||
return mightBeDeprecated && context.state.deprecationProvider.isDeprecatedHidden(toIrBasedDescriptor())
|
||||
}
|
||||
|
||||
private fun getThrownExceptions(function: IrFunction): List<String>? {
|
||||
if (context.state.languageVersionSettings.supportsFeature(LanguageFeature.DoNotGenerateThrowsForDelegatedKotlinMembers) &&
|
||||
function.origin == IrDeclarationOrigin.DELEGATED_MEMBER
|
||||
) return null
|
||||
|
||||
// @Throws(vararg exceptionClasses: KClass<out Throwable>)
|
||||
val exceptionClasses = function.getAnnotation(JVM_THROWS_ANNOTATION_FQ_NAME)?.getValueArgument(0) ?: return null
|
||||
return (exceptionClasses as IrVararg).elements.map { exceptionClass ->
|
||||
context.typeMapper.mapType((exceptionClass as IrClassReference).classType).internalName
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateAnnotationDefaultValueIfNeeded(methodVisitor: MethodVisitor) {
|
||||
getAnnotationDefaultValueExpression()?.let { defaultValueExpression ->
|
||||
val annotationCodegen = object : AnnotationCodegen(classCodegen, context) {
|
||||
override fun visitAnnotation(descr: String, visible: Boolean): AnnotationVisitor {
|
||||
return methodVisitor.visitAnnotationDefault()
|
||||
}
|
||||
}
|
||||
annotationCodegen.generateAnnotationDefaultValue(defaultValueExpression)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAnnotationDefaultValueExpression(): IrExpression? {
|
||||
if (!classCodegen.irClass.isAnnotationClass) return null
|
||||
// TODO: any simpler way to get to the value expression?
|
||||
// Are there other valid IR structures that represent the default value?
|
||||
return irFunction.safeAs<IrSimpleFunction>()
|
||||
?.correspondingPropertySymbol?.owner
|
||||
?.backingField
|
||||
?.initializer.safeAs<IrExpressionBody>()
|
||||
?.expression?.safeAs<IrGetValue>()
|
||||
?.symbol?.owner?.safeAs<IrValueParameter>()
|
||||
?.defaultValue?.safeAs<IrExpressionBody>()
|
||||
?.expression
|
||||
}
|
||||
|
||||
private fun IrFunction.createFrameMapWithReceivers(): IrFrameMap {
|
||||
val frameMap = IrFrameMap()
|
||||
val receiver = if (this is IrConstructor) parentAsClass.thisReceiver else dispatchReceiverParameter
|
||||
receiver?.let {
|
||||
frameMap.enter(it, context.typeMapper.mapTypeAsDeclaration(it.type))
|
||||
}
|
||||
val contextReceivers = valueParameters.subList(0, contextReceiverParametersCount)
|
||||
for (contextReceiver in contextReceivers) {
|
||||
frameMap.enter(contextReceiver, context.typeMapper.mapType(contextReceiver.type))
|
||||
}
|
||||
extensionReceiverParameter?.let {
|
||||
frameMap.enter(it, context.typeMapper.mapType(it))
|
||||
}
|
||||
val regularParameters = valueParameters.subList(contextReceiverParametersCount, valueParameters.size)
|
||||
for (parameter in regularParameters) {
|
||||
frameMap.enter(parameter, context.typeMapper.mapType(parameter.type))
|
||||
}
|
||||
return frameMap
|
||||
}
|
||||
|
||||
// Borrowed from org.jetbrains.kotlin.codegen.FunctionCodegen.java
|
||||
private fun generateParameterAnnotations(
|
||||
irFunction: IrFunction,
|
||||
mv: MethodVisitor,
|
||||
jvmSignature: JvmMethodSignature,
|
||||
innerClassConsumer: InnerClassConsumer,
|
||||
context: JvmBackendContext,
|
||||
skipNullabilityAnnotations: Boolean = false
|
||||
) {
|
||||
val iterator = irFunction.valueParameters.iterator()
|
||||
val kotlinParameterTypes = jvmSignature.valueParameters
|
||||
val syntheticParameterCount = kotlinParameterTypes.count { it.kind.isSkippedInGenericSignature }
|
||||
|
||||
visitAnnotableParameterCount(mv, kotlinParameterTypes.size - syntheticParameterCount)
|
||||
|
||||
kotlinParameterTypes.forEachIndexed { i, parameterSignature ->
|
||||
val kind = parameterSignature.kind
|
||||
val annotated = when (kind) {
|
||||
JvmMethodParameterKind.RECEIVER -> irFunction.extensionReceiverParameter
|
||||
else -> iterator.next()
|
||||
}
|
||||
|
||||
if (annotated != null && !kind.isSkippedInGenericSignature && !annotated.isSyntheticMarkerParameter()) {
|
||||
object : AnnotationCodegen(innerClassConsumer, context, skipNullabilityAnnotations) {
|
||||
override fun visitAnnotation(descr: String, visible: Boolean): AnnotationVisitor {
|
||||
return mv.visitParameterAnnotation(
|
||||
i - syntheticParameterCount,
|
||||
descr,
|
||||
visible
|
||||
)
|
||||
}
|
||||
|
||||
override fun visitTypeAnnotation(descr: String, path: TypePath?, visible: Boolean): AnnotationVisitor {
|
||||
return mv.visitTypeAnnotation(
|
||||
TypeReference.newFormalParameterReference(i - syntheticParameterCount).value,
|
||||
path, descr, visible
|
||||
)
|
||||
}
|
||||
}.genAnnotations(annotated, parameterSignature.asmType, annotated.type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal val methodOriginsWithoutAnnotations =
|
||||
setOf(
|
||||
// Not generating parameter annotations for default stubs fixes KT-7892, though
|
||||
// this certainly looks like a workaround for a javac bug.
|
||||
IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER,
|
||||
JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR,
|
||||
IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER,
|
||||
IrDeclarationOrigin.GENERATED_INLINE_CLASS_MEMBER,
|
||||
IrDeclarationOrigin.BRIDGE,
|
||||
IrDeclarationOrigin.BRIDGE_SPECIAL,
|
||||
JvmLoweredDeclarationOrigin.ABSTRACT_BRIDGE_STUB,
|
||||
JvmLoweredDeclarationOrigin.TO_ARRAY,
|
||||
IrDeclarationOrigin.IR_BUILTINS_STUB,
|
||||
IrDeclarationOrigin.PROPERTY_DELEGATE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun IrValueParameter.isSyntheticMarkerParameter(): Boolean =
|
||||
origin == IrDeclarationOrigin.DEFAULT_CONSTRUCTOR_MARKER ||
|
||||
origin == JvmLoweredDeclarationOrigin.SYNTHETIC_MARKER_PARAMETER
|
||||
|
||||
private fun generateParameterNames(irFunction: IrFunction, mv: MethodVisitor, state: GenerationState) {
|
||||
irFunction.extensionReceiverParameter?.let {
|
||||
mv.visitParameter(irFunction.extensionReceiverName(state), Opcodes.ACC_MANDATED)
|
||||
}
|
||||
for (irParameter in irFunction.valueParameters) {
|
||||
// A construct emitted by a Java compiler must be marked as synthetic if it does not correspond to a construct declared
|
||||
// explicitly or implicitly in source code, unless the emitted construct is a class initialization method (JVMS §2.9).
|
||||
// A construct emitted by a Java compiler must be marked as mandated if it corresponds to a formal parameter
|
||||
// declared implicitly in source code (§8.8.1, §8.8.9, §8.9.3, §15.9.5.1).
|
||||
val access = when {
|
||||
irParameter.origin == JvmLoweredDeclarationOrigin.FIELD_FOR_OUTER_THIS -> Opcodes.ACC_MANDATED
|
||||
// TODO mark these backend-common origins as synthetic? (note: ExpressionCodegen is still expected
|
||||
// to generate LVT entries for them)
|
||||
irParameter.origin == IrDeclarationOrigin.MOVED_EXTENSION_RECEIVER -> Opcodes.ACC_MANDATED
|
||||
irParameter.origin == IrDeclarationOrigin.MOVED_DISPATCH_RECEIVER -> Opcodes.ACC_SYNTHETIC
|
||||
irParameter.origin == BOUND_VALUE_PARAMETER -> Opcodes.ACC_SYNTHETIC
|
||||
irParameter.origin == BOUND_RECEIVER_PARAMETER -> Opcodes.ACC_SYNTHETIC
|
||||
irParameter.origin.isSynthetic -> Opcodes.ACC_SYNTHETIC
|
||||
else -> 0
|
||||
}
|
||||
mv.visitParameter(irParameter.name.asString(), access)
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.IrCallableMethod
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
interface IrCallGenerator {
|
||||
fun genCall(
|
||||
callableMethod: IrCallableMethod,
|
||||
codegen: ExpressionCodegen,
|
||||
expression: IrFunctionAccessExpression,
|
||||
isInsideIfCondition: Boolean,
|
||||
) {
|
||||
with(callableMethod) {
|
||||
codegen.mv.visitMethodInsn(invokeOpcode, owner.internalName, asmMethod.name, asmMethod.descriptor, isInterfaceMethod)
|
||||
}
|
||||
}
|
||||
|
||||
fun beforeCallStart() {}
|
||||
|
||||
fun beforeValueParametersStart() {}
|
||||
|
||||
fun afterCallEnd() {}
|
||||
|
||||
fun genValueAndPut(
|
||||
irValueParameter: IrValueParameter,
|
||||
argumentExpression: IrExpression,
|
||||
parameterType: Type,
|
||||
codegen: ExpressionCodegen,
|
||||
blockInfo: BlockInfo
|
||||
) {
|
||||
with(codegen) { gen(argumentExpression, parameterType, irValueParameter.realType, blockInfo) }
|
||||
}
|
||||
|
||||
object DefaultCallGenerator : IrCallGenerator
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.psiElement
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.suspendFunctionOriginal
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.IrCallableMethod
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
|
||||
interface IrInlineCallGenerator : IrCallGenerator {
|
||||
override fun genCall(
|
||||
callableMethod: IrCallableMethod,
|
||||
codegen: ExpressionCodegen,
|
||||
expression: IrFunctionAccessExpression,
|
||||
isInsideIfCondition: Boolean,
|
||||
) {
|
||||
val element = PsiSourceManager.findPsiElement(expression, codegen.irFunction)
|
||||
?: PsiSourceManager.findPsiElement(codegen.irFunction)
|
||||
val descriptor = expression.symbol.owner.suspendFunctionOriginal().toIrBasedDescriptor()
|
||||
if (!codegen.state.globalInlineContext.enterIntoInlining(descriptor, element)) {
|
||||
genCycleStub(expression.psiElement?.text ?: "<no source>", codegen)
|
||||
return
|
||||
}
|
||||
try {
|
||||
genInlineCall(callableMethod, codegen, expression, isInsideIfCondition)
|
||||
} finally {
|
||||
codegen.state.globalInlineContext.exitFromInlining()
|
||||
}
|
||||
}
|
||||
|
||||
fun genInlineCall(
|
||||
callableMethod: IrCallableMethod,
|
||||
codegen: ExpressionCodegen,
|
||||
expression: IrFunctionAccessExpression,
|
||||
isInsideIfCondition: Boolean,
|
||||
)
|
||||
|
||||
fun genCycleStub(text: String, codegen: ExpressionCodegen) {
|
||||
AsmUtil.genThrow(codegen.visitor, "java/lang/UnsupportedOperationException", "Call is a part of inline call cycle: $text")
|
||||
}
|
||||
}
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineOnly
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.unwrapInlineLambda
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.IrCallableMethod
|
||||
import org.jetbrains.kotlin.codegen.IrExpressionLambda
|
||||
import org.jetbrains.kotlin.codegen.JvmKotlinType
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.ValueKind
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.util.explicitParameters
|
||||
import org.jetbrains.kotlin.ir.util.getArgumentsWithIr
|
||||
import org.jetbrains.kotlin.ir.util.isSuspendFunction
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
|
||||
class IrInlineCodegen(
|
||||
codegen: ExpressionCodegen,
|
||||
state: GenerationState,
|
||||
private val function: IrFunction,
|
||||
signature: JvmMethodSignature,
|
||||
typeParameterMappings: TypeParameterMappings<IrType>,
|
||||
sourceCompiler: SourceCompilerForInline,
|
||||
reifiedTypeInliner: ReifiedTypeInliner<IrType>
|
||||
) :
|
||||
InlineCodegen<ExpressionCodegen>(codegen, state, signature, typeParameterMappings, sourceCompiler, reifiedTypeInliner),
|
||||
IrInlineCallGenerator {
|
||||
|
||||
private val inlineArgumentsInPlace = canInlineArgumentsInPlace()
|
||||
|
||||
private fun canInlineArgumentsInPlace(): Boolean {
|
||||
if (!function.isInlineOnly())
|
||||
return false
|
||||
|
||||
var actualParametersCount = function.valueParameters.size
|
||||
if (function.dispatchReceiverParameter != null)
|
||||
++actualParametersCount
|
||||
if (function.extensionReceiverParameter != null)
|
||||
++actualParametersCount
|
||||
if (actualParametersCount == 0)
|
||||
return false
|
||||
|
||||
if (function.valueParameters.any { it.isInlineParameter() })
|
||||
return false
|
||||
|
||||
return canInlineArgumentsInPlace(sourceCompiler.compileInlineFunction(jvmSignature).node)
|
||||
}
|
||||
|
||||
override fun beforeCallStart() {
|
||||
if (inlineArgumentsInPlace) {
|
||||
codegen.visitor.addInplaceCallStartMarker()
|
||||
}
|
||||
}
|
||||
|
||||
override fun afterCallEnd() {
|
||||
if (inlineArgumentsInPlace) {
|
||||
codegen.visitor.addInplaceCallEndMarker()
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateAssertField() {
|
||||
// May be inlining code into `<clinit>`, in which case it's too late to modify the IR and
|
||||
// `generateAssertFieldIfNeeded` will return a statement for which we need to emit bytecode.
|
||||
val isClInit = sourceCompiler.inlineCallSiteInfo.method.name == "<clinit>"
|
||||
codegen.classCodegen.generateAssertFieldIfNeeded(isClInit)?.accept(codegen, BlockInfo())?.discard()
|
||||
}
|
||||
|
||||
override fun genValueAndPut(
|
||||
irValueParameter: IrValueParameter,
|
||||
argumentExpression: IrExpression,
|
||||
parameterType: Type,
|
||||
codegen: ExpressionCodegen,
|
||||
blockInfo: BlockInfo
|
||||
) {
|
||||
val inlineLambda = argumentExpression.unwrapInlineLambda()
|
||||
if (inlineLambda != null) {
|
||||
val lambdaInfo = IrExpressionLambdaImpl(codegen, inlineLambda)
|
||||
rememberClosure(parameterType, irValueParameter.index, lambdaInfo)
|
||||
lambdaInfo.generateLambdaBody(sourceCompiler)
|
||||
lambdaInfo.reference.getArgumentsWithIr().forEachIndexed { index, (_, ir) ->
|
||||
val param = lambdaInfo.capturedVars[index]
|
||||
val onStack = codegen.genOrGetLocal(ir, param.type, ir.type, BlockInfo())
|
||||
putCapturedToLocalVal(onStack, param, ir.type.toIrBasedKotlinType())
|
||||
}
|
||||
} else {
|
||||
val isInlineParameter = irValueParameter.isInlineParameter()
|
||||
val kind = when {
|
||||
irValueParameter.origin == IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION ->
|
||||
ValueKind.DEFAULT_MASK
|
||||
irValueParameter.origin == IrDeclarationOrigin.METHOD_HANDLER_IN_DEFAULT_FUNCTION ->
|
||||
ValueKind.METHOD_HANDLE_IN_DEFAULT
|
||||
argumentExpression is IrContainerExpression && argumentExpression.origin == IrStatementOrigin.DEFAULT_VALUE ->
|
||||
if (isInlineParameter)
|
||||
ValueKind.DEFAULT_INLINE_PARAMETER
|
||||
else
|
||||
ValueKind.DEFAULT_PARAMETER
|
||||
isInlineParameter && irValueParameter.type.isSuspendFunction() ->
|
||||
if (argumentExpression.isReadOfInlineLambda())
|
||||
ValueKind.READ_OF_INLINE_LAMBDA_FOR_INLINE_SUSPEND_PARAMETER
|
||||
else
|
||||
ValueKind.READ_OF_OBJECT_FOR_INLINE_SUSPEND_PARAMETER
|
||||
else ->
|
||||
ValueKind.GENERAL
|
||||
}
|
||||
|
||||
val onStack = when (kind) {
|
||||
ValueKind.METHOD_HANDLE_IN_DEFAULT ->
|
||||
StackValue.constant(null, AsmTypes.OBJECT_TYPE)
|
||||
ValueKind.DEFAULT_MASK ->
|
||||
StackValue.constant((argumentExpression as IrConst<*>).value, Type.INT_TYPE)
|
||||
ValueKind.DEFAULT_PARAMETER, ValueKind.DEFAULT_INLINE_PARAMETER ->
|
||||
StackValue.createDefaultValue(parameterType)
|
||||
else -> {
|
||||
if (inlineArgumentsInPlace) {
|
||||
codegen.visitor.addInplaceArgumentStartMarker()
|
||||
}
|
||||
// Here we replicate the old backend: reusing the locals for everything except extension receivers.
|
||||
// TODO when stopping at a breakpoint placed in an inline function, arguments which reuse an existing
|
||||
// local will not be visible in the debugger, so this needs to be reconsidered.
|
||||
val argValue = if (irValueParameter.index >= 0)
|
||||
codegen.genOrGetLocal(argumentExpression, parameterType, irValueParameter.type, blockInfo)
|
||||
else
|
||||
codegen.gen(argumentExpression, parameterType, irValueParameter.type, blockInfo)
|
||||
if (inlineArgumentsInPlace) {
|
||||
codegen.visitor.addInplaceArgumentEndMarker()
|
||||
}
|
||||
argValue
|
||||
}
|
||||
}
|
||||
|
||||
val expectedType = JvmKotlinType(parameterType, irValueParameter.type.toIrBasedKotlinType())
|
||||
putArgumentToLocalVal(expectedType, onStack, irValueParameter.index, kind)
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeValueParametersStart() {
|
||||
invocationParamBuilder.markValueParametersStart()
|
||||
}
|
||||
|
||||
override fun genInlineCall(
|
||||
callableMethod: IrCallableMethod,
|
||||
codegen: ExpressionCodegen,
|
||||
expression: IrFunctionAccessExpression,
|
||||
isInsideIfCondition: Boolean,
|
||||
) {
|
||||
performInline(isInsideIfCondition, function.isInlineOnly())
|
||||
}
|
||||
|
||||
override fun genCycleStub(text: String, codegen: ExpressionCodegen) {
|
||||
generateStub(text, codegen)
|
||||
}
|
||||
}
|
||||
|
||||
class IrExpressionLambdaImpl(
|
||||
codegen: ExpressionCodegen,
|
||||
val reference: IrFunctionReference,
|
||||
) : ExpressionLambda(), IrExpressionLambda {
|
||||
override val isExtensionLambda: Boolean = function.extensionReceiverParameter != null && reference.extensionReceiver == null
|
||||
|
||||
val function: IrFunction
|
||||
get() = reference.symbol.owner
|
||||
|
||||
override val hasDispatchReceiver: Boolean
|
||||
get() = false
|
||||
|
||||
// This name doesn't actually matter: it is used internally to tell this lambda's captured
|
||||
// arguments apart from any other scope's. So long as it's unique, any value is fine.
|
||||
// This particular string slightly aids in debugging internal compiler errors as it at least
|
||||
// points towards the function containing the lambda.
|
||||
override val lambdaClassType: Type = codegen.context.getLocalClassType(reference)
|
||||
?: throw AssertionError("callable reference ${reference.dump()} has no name in context")
|
||||
|
||||
override val capturedVars: List<CapturedParamDesc>
|
||||
override val invokeMethod: Method
|
||||
override val invokeMethodParameters: List<KotlinType?>
|
||||
override val invokeMethodReturnType: KotlinType
|
||||
|
||||
init {
|
||||
val asmMethod = codegen.methodSignatureMapper.mapAsmMethod(function)
|
||||
val capturedParameters = reference.getArgumentsWithIr()
|
||||
val captureStart = if (isExtensionLambda) 1 else 0 // extension receiver comes before captures
|
||||
val captureEnd = captureStart + capturedParameters.size
|
||||
capturedVars = capturedParameters.mapIndexed { index, (parameter, _) ->
|
||||
val isSuspend = parameter.isInlineParameter() && parameter.type.isSuspendFunction()
|
||||
capturedParamDesc(parameter.name.asString(), asmMethod.argumentTypes[captureStart + index], isSuspend)
|
||||
}
|
||||
// The parameter list should include the continuation if this is a suspend lambda. In the IR backend,
|
||||
// the lambda is suspend iff the inline function's parameter is marked suspend, so FunctionN.invoke call
|
||||
// inside the inline function already has a (real) continuation value as the last argument.
|
||||
val freeParameters = function.explicitParameters.let { it.take(captureStart) + it.drop(captureEnd) }
|
||||
val freeAsmParameters = asmMethod.argumentTypes.let { it.take(captureStart) + it.drop(captureEnd) }
|
||||
// The return type, on the other hand, should be the original type if this is a suspend lambda that returns
|
||||
// an unboxed inline class value so that the inliner will box it (FunctionN.invoke should return a boxed value).
|
||||
val unboxedReturnType = function.originalReturnTypeOfSuspendFunctionReturningUnboxedInlineClass()
|
||||
val unboxedAsmReturnType = unboxedReturnType?.let(codegen.typeMapper::mapType)
|
||||
invokeMethod = Method(asmMethod.name, unboxedAsmReturnType ?: asmMethod.returnType, freeAsmParameters.toTypedArray())
|
||||
invokeMethodParameters = freeParameters.map { it.type.toIrBasedKotlinType() }
|
||||
invokeMethodReturnType = (unboxedReturnType ?: function.returnType).toIrBasedKotlinType()
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.IrCallableMethod
|
||||
import org.jetbrains.kotlin.codegen.inline.MethodBodyVisitor
|
||||
import org.jetbrains.kotlin.codegen.inline.SourceMapCopier
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
/**
|
||||
* A specialization of IrInlineCodegen for calls to the underlying method in a $default handler.
|
||||
* Such calls are inlined verbatim in the JVM backend (see InlineCodegenForDefaultBody.kt).
|
||||
* For compatibility we have to do the same thing in the JVM IR backend.
|
||||
*/
|
||||
object IrInlineDefaultCodegen : IrInlineCallGenerator {
|
||||
override fun genValueAndPut(
|
||||
irValueParameter: IrValueParameter,
|
||||
argumentExpression: IrExpression,
|
||||
parameterType: Type,
|
||||
codegen: ExpressionCodegen,
|
||||
blockInfo: BlockInfo
|
||||
) {
|
||||
// This codegen is only used for calls to the underlying function in a $default stub.
|
||||
// For such calls we know that we are passing along the value parameters and reusing the same indices.
|
||||
// There is no need to generate any code.
|
||||
assert(argumentExpression is IrGetValue || argumentExpression is IrTypeOperatorCall && argumentExpression.argument is IrGetValue)
|
||||
}
|
||||
|
||||
override fun genInlineCall(
|
||||
callableMethod: IrCallableMethod,
|
||||
codegen: ExpressionCodegen,
|
||||
expression: IrFunctionAccessExpression,
|
||||
isInsideIfCondition: Boolean
|
||||
) {
|
||||
val function = expression.symbol.owner
|
||||
val nodeAndSmap = codegen.classCodegen.generateMethodNode(function)
|
||||
val childSourceMapper = SourceMapCopier(codegen.smap, nodeAndSmap.classSMAP)
|
||||
|
||||
val argsSize =
|
||||
(Type.getArgumentsAndReturnSizes(callableMethod.asmMethod.descriptor) ushr 2) - if (function.isStatic) 1 else 0
|
||||
nodeAndSmap.node.accept(object : MethodBodyVisitor(codegen.visitor) {
|
||||
override fun visitLocalVariable(name: String, desc: String, signature: String?, start: Label, end: Label, index: Int) {
|
||||
// We only copy LVT entries for local variables, since we already generated entries for the method parameters,
|
||||
if (index >= argsSize) super.visitLocalVariable(name, desc, signature, start, end, index)
|
||||
}
|
||||
|
||||
override fun visitLineNumber(line: Int, start: Label?) {
|
||||
super.visitLineNumber(childSourceMapper.mapLineNumber(line), start)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.allParametersCount
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.SignatureString
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getCallableReferenceOwnerKClassType
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getCallableReferenceTopLevelFlag
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.IrTypeMapper
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmBackendErrors
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.model.TypeParameterMarker
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.Type.INT_TYPE
|
||||
import org.jetbrains.org.objectweb.asm.Type.VOID_TYPE
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class IrInlineIntrinsicsSupport(
|
||||
private val context: JvmBackendContext,
|
||||
private val typeMapper: IrTypeMapper,
|
||||
private val reportErrorsOn: IrExpression,
|
||||
private val containingFile: IrFile,
|
||||
) : ReifiedTypeInliner.IntrinsicsSupport<IrType> {
|
||||
override val state: GenerationState
|
||||
get() = context.state
|
||||
|
||||
override fun putClassInstance(v: InstructionAdapter, type: IrType) {
|
||||
ExpressionCodegen.generateClassInstance(v, type, typeMapper)
|
||||
}
|
||||
|
||||
override fun generateTypeParameterContainer(v: InstructionAdapter, typeParameter: TypeParameterMarker) {
|
||||
require(typeParameter is IrTypeParameterSymbol)
|
||||
|
||||
when (val parent = typeParameter.owner.parent) {
|
||||
is IrClass -> putClassInstance(v, parent.defaultType).also { AsmUtil.wrapJavaClassIntoKClass(v) }
|
||||
is IrSimpleFunction -> {
|
||||
check(context.state.generateOptimizedCallableReferenceSuperClasses) {
|
||||
"typeOf() of a non-reified type parameter is only allowed if optimized callable references are enabled.\n" +
|
||||
"Please make sure API version is set to 1.4, and -Xno-optimized-callable-references is NOT used.\n" +
|
||||
"Container: $parent"
|
||||
}
|
||||
val property = parent.correspondingPropertySymbol
|
||||
if (property != null) {
|
||||
generatePropertyReference(v, property.owner)
|
||||
} else {
|
||||
generateFunctionReference(v, parent)
|
||||
}
|
||||
}
|
||||
else -> error("Unknown parent of type parameter: ${parent.render()} ${typeParameter.owner.name})")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateFunctionReference(v: InstructionAdapter, function: IrFunction) {
|
||||
generateCallableReference(v, function, function, FUNCTION_REFERENCE_IMPL, true)
|
||||
}
|
||||
|
||||
private fun generatePropertyReference(v: InstructionAdapter, property: IrProperty) {
|
||||
// We're sure that this property has a getter because if a property is generic, it necessarily has extension receiver and
|
||||
// thus cannot have a backing field, and is required to have a getter.
|
||||
val getter = property.getter
|
||||
?: error("Property without getter: ${property.render()}")
|
||||
val arity = getter.allParametersCount
|
||||
val implClass = (if (property.isVar) MUTABLE_PROPERTY_REFERENCE_IMPL else PROPERTY_REFERENCE_IMPL).getOrNull(arity)
|
||||
?: error("No property reference impl class with arity $arity (${property.render()}")
|
||||
|
||||
generateCallableReference(v, property, getter, implClass, false)
|
||||
}
|
||||
|
||||
private fun generateCallableReference(
|
||||
v: InstructionAdapter, declaration: IrDeclarationWithName, function: IrFunction, implClass: Type, withArity: Boolean
|
||||
) {
|
||||
v.anew(implClass)
|
||||
v.dup()
|
||||
if (withArity) {
|
||||
v.iconst(function.allParametersCount)
|
||||
}
|
||||
putClassInstance(v, declaration.parent.getCallableReferenceOwnerKClassType(context))
|
||||
v.aconst(declaration.name.asString())
|
||||
// TODO: generate correct signature for functions and property accessors which have inline class types in the signature.
|
||||
SignatureString.generateSignatureString(v, function, context)
|
||||
v.iconst(declaration.getCallableReferenceTopLevelFlag())
|
||||
val parameterTypes =
|
||||
(if (withArity) listOf(INT_TYPE) else emptyList()) +
|
||||
listOf(JAVA_CLASS_TYPE, JAVA_STRING_TYPE, JAVA_STRING_TYPE, INT_TYPE)
|
||||
v.invokespecial(
|
||||
implClass.internalName, "<init>",
|
||||
Type.getMethodDescriptor(VOID_TYPE, *parameterTypes.toTypedArray()),
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
override fun isMutableCollectionType(type: IrType): Boolean {
|
||||
val classifier = type.classOrNull
|
||||
return classifier != null && JavaToKotlinClassMap.isMutable(classifier.owner.fqNameWhenAvailable?.toUnsafe())
|
||||
}
|
||||
|
||||
override fun toKotlinType(type: IrType): KotlinType = type.toIrBasedKotlinType()
|
||||
|
||||
override fun reportSuspendTypeUnsupported() {
|
||||
context.ktDiagnosticReporter.at(reportErrorsOn, containingFile).report(JvmBackendErrors.TYPEOF_SUSPEND_TYPE)
|
||||
}
|
||||
|
||||
override fun reportNonReifiedTypeParameterWithRecursiveBoundUnsupported(typeParameterName: Name) {
|
||||
context.ktDiagnosticReporter.at(reportErrorsOn, containingFile)
|
||||
.report(JvmBackendErrors.TYPEOF_NON_REIFIED_TYPE_PARAMETER_WITH_RECURSIVE_BOUND, typeParameterName.asString())
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.codegen
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||
import org.jetbrains.kotlin.backend.common.ir.ir2string
|
||||
import org.jetbrains.kotlin.backend.jvm.hasMangledReturnType
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||
import org.jetbrains.kotlin.incremental.components.LocationInfo
|
||||
import org.jetbrains.kotlin.incremental.components.Position
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrLoop
|
||||
import org.jetbrains.kotlin.ir.util.isSuspend
|
||||
import org.jetbrains.kotlin.ir.util.kotlinFqName
|
||||
import org.jetbrains.kotlin.ir.util.module
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.psi.doNotAnalyze
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmBackendErrors
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
class IrSourceCompilerForInline(
|
||||
override val state: GenerationState,
|
||||
override val callElement: IrFunctionAccessExpression,
|
||||
private val callee: IrFunction,
|
||||
internal val codegen: ExpressionCodegen,
|
||||
private val data: BlockInfo
|
||||
) : SourceCompilerForInline {
|
||||
override val callElementText: String
|
||||
get() = ir2string(callElement)
|
||||
|
||||
override val inlineCallSiteInfo: InlineCallSiteInfo
|
||||
get() {
|
||||
val root = generateSequence(codegen) { it.inlinedInto }.last()
|
||||
return InlineCallSiteInfo(
|
||||
root.classCodegen.type.internalName,
|
||||
root.signature.asmMethod,
|
||||
root.irFunction.inlineScopeVisibility,
|
||||
root.irFunction.fileParent.getKtFile(),
|
||||
callElement.psiElement?.let { CodegenUtil.getLineNumberForElement(it, false) } ?: 0
|
||||
)
|
||||
}
|
||||
|
||||
override val sourceMapper: SourceMapper
|
||||
get() = codegen.smap
|
||||
|
||||
override fun generateLambdaBody(lambdaInfo: ExpressionLambda, reifiedTypeParameters: ReifiedTypeParametersUsages): SMAPAndMethodNode {
|
||||
require(lambdaInfo is IrExpressionLambdaImpl)
|
||||
for (typeParameter in lambdaInfo.function.typeParameters) {
|
||||
if (typeParameter.isReified) {
|
||||
reifiedTypeParameters.addUsedReifiedParameter(typeParameter.name.asString())
|
||||
}
|
||||
}
|
||||
return FunctionCodegen(lambdaInfo.function, codegen.classCodegen).generate(codegen, reifiedTypeParameters)
|
||||
}
|
||||
|
||||
override fun compileInlineFunction(jvmSignature: JvmMethodSignature): SMAPAndMethodNode {
|
||||
generateInlineIntrinsicForIr(callee.toIrBasedDescriptor())?.let {
|
||||
return it
|
||||
}
|
||||
if (jvmSignature.asmMethod.name != callee.name.asString()) {
|
||||
val ktFile = codegen.irFunction.fileParent.getKtFile()
|
||||
if (ktFile != null && ktFile.doNotAnalyze == null) {
|
||||
state.trackLookup(callee.parentAsClass.kotlinFqName, jvmSignature.asmMethod.name, object : LocationInfo {
|
||||
override val filePath = ktFile.virtualFilePath
|
||||
|
||||
override val position: Position
|
||||
get() = DiagnosticUtils.getLineAndColumnInPsiFile(
|
||||
ktFile,
|
||||
TextRange(callElement.startOffset, callElement.endOffset)
|
||||
).let { Position(it.line, it.column) }
|
||||
})
|
||||
}
|
||||
}
|
||||
callee.parentClassId?.let {
|
||||
return loadCompiledInlineFunction(it, jvmSignature.asmMethod, callee.isSuspend, callee.hasMangledReturnType, state)
|
||||
}
|
||||
return ClassCodegen.getOrCreate(callee.parentAsClass, codegen.context).generateMethodNode(callee)
|
||||
}
|
||||
|
||||
override fun hasFinallyBlocks() = data.hasFinallyBlocks()
|
||||
|
||||
override fun generateFinallyBlocks(finallyNode: MethodNode, curFinallyDepth: Int, returnType: Type, afterReturnLabel: Label, target: Label?) {
|
||||
ExpressionCodegen(
|
||||
codegen.irFunction, codegen.signature, codegen.frameMap, InstructionAdapter(finallyNode), codegen.classCodegen,
|
||||
codegen.inlinedInto, codegen.smap, codegen.reifiedTypeParametersUsages
|
||||
).also {
|
||||
it.finallyDepth = curFinallyDepth
|
||||
}.generateFinallyBlocksIfNeeded(returnType, afterReturnLabel, data, target)
|
||||
}
|
||||
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
override val isCallInsideSameModuleAsCallee: Boolean
|
||||
get() = callee.module == codegen.irFunction.module
|
||||
|
||||
override val isFinallyMarkerRequired: Boolean
|
||||
get() = codegen.isFinallyMarkerRequired
|
||||
|
||||
override fun isSuspendLambdaCapturedByOuterObjectOrLambda(name: String): Boolean =
|
||||
false // IR does not capture variables through outer this
|
||||
|
||||
override fun getContextLabels(): Map<String, Label?> {
|
||||
val result = mutableMapOf<String, Label?>(codegen.irFunction.name.asString() to null)
|
||||
for (info in data.infos) {
|
||||
if (info !is LoopInfo)
|
||||
continue
|
||||
result[info.loop.nonLocalReturnLabel(false)] = info.continueLabel
|
||||
result[info.loop.nonLocalReturnLabel(true)] = info.breakLabel
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// TODO: Find a way to avoid using PSI here
|
||||
override fun reportSuspensionPointInsideMonitor(stackTraceElement: String) {
|
||||
codegen.context.ktDiagnosticReporter
|
||||
.at(callElement.symbol.owner as IrDeclaration)
|
||||
.report(JvmBackendErrors.SUSPENSION_POINT_INSIDE_MONITOR, stackTraceElement)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun IrLoop.nonLocalReturnLabel(forBreak: Boolean): String = "${label!!}\$${if (forBreak) "break" else "continue"}"
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.lower.ANNOTATION_IMPLEMENTATION
|
||||
import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactory1
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||
import org.jetbrains.kotlin.ir.util.file
|
||||
import org.jetbrains.kotlin.ir.util.isFakeOverride
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.*
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
class JvmSignatureClashDetector(
|
||||
private val irClass: IrClass,
|
||||
private val type: Type,
|
||||
private val context: JvmBackendContext
|
||||
) {
|
||||
private val methodsBySignature = LinkedHashMap<RawSignature, MutableSet<IrFunction>>()
|
||||
private val fieldsBySignature = LinkedHashMap<RawSignature, MutableSet<IrField>>()
|
||||
|
||||
fun trackField(irField: IrField, rawSignature: RawSignature) {
|
||||
fieldsBySignature.getOrPut(rawSignature) { SmartSet.create() }.add(irField)
|
||||
}
|
||||
|
||||
fun trackMethod(irFunction: IrFunction, rawSignature: RawSignature) {
|
||||
methodsBySignature.getOrPut(rawSignature) { SmartSet.create() }.add(irFunction)
|
||||
}
|
||||
|
||||
fun trackFakeOverrideMethod(irFunction: IrFunction) {
|
||||
if (irFunction.dispatchReceiverParameter != null) {
|
||||
for (overriddenFunction in getOverriddenFunctions(irFunction as IrSimpleFunction)) {
|
||||
trackMethod(irFunction, mapRawSignature(overriddenFunction))
|
||||
}
|
||||
} else {
|
||||
trackMethod(irFunction, mapRawSignature(irFunction))
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapRawSignature(irFunction: IrFunction): RawSignature {
|
||||
val jvmSignature = context.methodSignatureMapper.mapSignatureSkipGeneric(irFunction)
|
||||
return RawSignature(jvmSignature.asmMethod.name, jvmSignature.asmMethod.descriptor, MemberKind.METHOD)
|
||||
}
|
||||
|
||||
private fun getOverriddenFunctions(irFunction: IrSimpleFunction): Set<IrFunction> {
|
||||
val result = LinkedHashSet<IrFunction>()
|
||||
collectOverridesOf(irFunction, result)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun collectOverridesOf(irFunction: IrSimpleFunction, result: MutableSet<IrFunction>) {
|
||||
for (overriddenSymbol in irFunction.overriddenSymbols) {
|
||||
collectOverridesTree(overriddenSymbol.owner, result)
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectOverridesTree(irFunction: IrSimpleFunction, visited: MutableSet<IrFunction>) {
|
||||
if (!visited.add(irFunction)) return
|
||||
collectOverridesOf(irFunction, visited)
|
||||
}
|
||||
|
||||
private fun IrFunction.isSpecialOverride(): Boolean =
|
||||
origin in SPECIAL_BRIDGES_AND_OVERRIDES
|
||||
|
||||
fun reportErrors(classOrigin: JvmDeclarationOrigin) {
|
||||
reportMethodSignatureConflicts(classOrigin)
|
||||
reportPredefinedMethodSignatureConflicts(classOrigin)
|
||||
reportFieldSignatureConflicts(classOrigin)
|
||||
}
|
||||
|
||||
private fun reportMethodSignatureConflicts(classOrigin: JvmDeclarationOrigin) {
|
||||
for ((rawSignature, methods) in methodsBySignature) {
|
||||
if (methods.size <= 1) continue
|
||||
|
||||
val fakeOverridesCount = methods.count { it.isFakeOverride }
|
||||
val specialOverridesCount = methods.count { it.isSpecialOverride() }
|
||||
val realMethodsCount = methods.size - fakeOverridesCount - specialOverridesCount
|
||||
|
||||
val conflictingJvmDeclarationsData = getConflictingJvmDeclarationsData(classOrigin, rawSignature, methods)
|
||||
|
||||
when {
|
||||
realMethodsCount == 0 && (fakeOverridesCount > 1 || specialOverridesCount > 1) ->
|
||||
if (irClass.origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS) {
|
||||
reportJvmSignatureClash(
|
||||
JvmBackendErrors.CONFLICTING_INHERITED_JVM_DECLARATIONS,
|
||||
listOf(irClass),
|
||||
conflictingJvmDeclarationsData
|
||||
)
|
||||
}
|
||||
|
||||
fakeOverridesCount == 0 && specialOverridesCount == 0 -> {
|
||||
// In IFoo$DefaultImpls we should report errors only if there are private methods among conflicting ones
|
||||
// (otherwise such errors would be reported twice: once for IFoo and once for IFoo$DefaultImpls).
|
||||
if (irClass.origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS ||
|
||||
methods.any { DescriptorVisibilities.isPrivate(it.visibility) }
|
||||
) {
|
||||
reportJvmSignatureClash(
|
||||
JvmBackendErrors.CONFLICTING_JVM_DECLARATIONS,
|
||||
methods,
|
||||
conflictingJvmDeclarationsData
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else ->
|
||||
if (irClass.origin != JvmLoweredDeclarationOrigin.DEFAULT_IMPLS) {
|
||||
reportJvmSignatureClash(
|
||||
JvmBackendErrors.ACCIDENTAL_OVERRIDE,
|
||||
methods.filter { !it.isFakeOverride && !it.isSpecialOverride() },
|
||||
conflictingJvmDeclarationsData
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportPredefinedMethodSignatureConflicts(classOrigin: JvmDeclarationOrigin) {
|
||||
for (predefinedSignature in PREDEFINED_SIGNATURES) {
|
||||
val knownMethods = methodsBySignature[predefinedSignature] ?: continue
|
||||
val methods = knownMethods.filter { !it.isFakeOverride && !it.isSpecialOverride() }
|
||||
if (methods.isEmpty()) continue
|
||||
val conflictingJvmDeclarationsData = ConflictingJvmDeclarationsData(
|
||||
type.internalName, classOrigin, predefinedSignature,
|
||||
methods.map { it.getJvmDeclarationOrigin() } + JvmDeclarationOrigin(JvmDeclarationOriginKind.OTHER, null, null)
|
||||
)
|
||||
reportJvmSignatureClash(JvmBackendErrors.ACCIDENTAL_OVERRIDE, methods, conflictingJvmDeclarationsData)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportFieldSignatureConflicts(classOrigin: JvmDeclarationOrigin) {
|
||||
for ((rawSignature, fields) in fieldsBySignature) {
|
||||
if (fields.size <= 1) continue
|
||||
val conflictingJvmDeclarationsData = getConflictingJvmDeclarationsData(classOrigin, rawSignature, fields)
|
||||
reportJvmSignatureClash(JvmBackendErrors.CONFLICTING_JVM_DECLARATIONS, fields, conflictingJvmDeclarationsData)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportJvmSignatureClash(
|
||||
diagnosticFactory1: KtDiagnosticFactory1<ConflictingJvmDeclarationsData>,
|
||||
irDeclarations: Collection<IrDeclaration>,
|
||||
conflictingJvmDeclarationsData: ConflictingJvmDeclarationsData
|
||||
) {
|
||||
irDeclarations.mapNotNullTo(LinkedHashSet()) { irDeclaration ->
|
||||
context.ktDiagnosticReporter.atFirstValidFrom(irDeclaration, irClass, containingIrFile = irDeclaration.file)
|
||||
}.forEach {
|
||||
it.report(diagnosticFactory1, conflictingJvmDeclarationsData)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getConflictingJvmDeclarationsData(
|
||||
classOrigin: JvmDeclarationOrigin,
|
||||
rawSignature: RawSignature,
|
||||
methods: Collection<IrDeclaration>
|
||||
): ConflictingJvmDeclarationsData =
|
||||
ConflictingJvmDeclarationsData(
|
||||
type.internalName,
|
||||
classOrigin,
|
||||
rawSignature,
|
||||
methods.map { it.getJvmDeclarationOrigin() }
|
||||
)
|
||||
|
||||
private fun IrDeclaration.getJvmDeclarationOrigin(): JvmDeclarationOrigin {
|
||||
// It looks like 'JvmDeclarationOriginKind' is not really used in error reporting.
|
||||
// However, if needed, we can provide more meaningful information regarding function origin.
|
||||
return JvmDeclarationOrigin(
|
||||
JvmDeclarationOriginKind.OTHER,
|
||||
PsiSourceManager.findPsiElement(this),
|
||||
toIrBasedDescriptor()
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val SPECIAL_BRIDGES_AND_OVERRIDES = setOf(
|
||||
IrDeclarationOrigin.BRIDGE,
|
||||
IrDeclarationOrigin.BRIDGE_SPECIAL,
|
||||
IrDeclarationOrigin.IR_BUILTINS_STUB,
|
||||
JvmLoweredDeclarationOrigin.TO_ARRAY,
|
||||
JvmLoweredDeclarationOrigin.SUPER_INTERFACE_METHOD_BRIDGE,
|
||||
ANNOTATION_IMPLEMENTATION
|
||||
)
|
||||
|
||||
val PREDEFINED_SIGNATURES = listOf(
|
||||
RawSignature("getClass", "()Ljava/lang/Class;", MemberKind.METHOD),
|
||||
RawSignature("notify", "()V", MemberKind.METHOD),
|
||||
RawSignature("notifyAll", "()V", MemberKind.METHOD),
|
||||
RawSignature("wait", "()V", MemberKind.METHOD),
|
||||
RawSignature("wait", "(J)V", MemberKind.METHOD),
|
||||
RawSignature("wait", "(JI)V", MemberKind.METHOD)
|
||||
)
|
||||
}
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.InlineClassAbi
|
||||
import org.jetbrains.kotlin.backend.jvm.inlineClassFieldName
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.eraseTypeParameters
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.IrTypeMapper
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.isTypeParameter
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
// A value that may not have been fully constructed yet. The ability to "roll back" code generation
|
||||
// is useful for certain optimizations.
|
||||
abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val irType: IrType) {
|
||||
// If this value is immaterial, construct an object on the top of the stack. This
|
||||
// must always be done before generating other values or emitting raw bytecode.
|
||||
open fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
|
||||
val erasedSourceType = irType.eraseTypeParameters()
|
||||
val erasedTargetType = irTarget.eraseTypeParameters()
|
||||
|
||||
// Coerce inline classes
|
||||
val isFromTypeUnboxed = InlineClassAbi.unboxType(erasedSourceType)?.let(typeMapper::mapType) == type
|
||||
val isToTypeUnboxed = InlineClassAbi.unboxType(erasedTargetType)?.let(typeMapper::mapType) == target
|
||||
if (isFromTypeUnboxed && !isToTypeUnboxed) {
|
||||
val boxed = typeMapper.mapType(erasedSourceType, TypeMappingMode.CLASS_DECLARATION)
|
||||
StackValue.boxInlineClass(type, boxed, erasedSourceType.isNullable(), mv)
|
||||
return
|
||||
}
|
||||
if (!isFromTypeUnboxed && isToTypeUnboxed) {
|
||||
val boxed = typeMapper.mapType(erasedTargetType, TypeMappingMode.CLASS_DECLARATION)
|
||||
val irClass = codegen.irFunction.parentAsClass
|
||||
if (irClass.isInline && irClass.symbol == irType.classifierOrNull && !irType.isNullable()) {
|
||||
// Use getfield instead of unbox-impl inside inline classes
|
||||
codegen.mv.getfield(boxed.internalName, irClass.inlineClassFieldName.asString(), target.descriptor)
|
||||
} else {
|
||||
StackValue.unboxInlineClass(type, boxed, target, erasedTargetType.isNullable(), mv)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (type != target || (castForReified && irType.anyTypeArgument { it.isReified })) {
|
||||
StackValue.coerce(type, target, mv, type == target)
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun discard()
|
||||
|
||||
val mv: InstructionAdapter
|
||||
get() = codegen.mv
|
||||
|
||||
val typeMapper: IrTypeMapper
|
||||
get() = codegen.typeMapper
|
||||
}
|
||||
|
||||
|
||||
fun IrType.anyTypeArgument(check: (IrTypeParameter) -> Boolean): Boolean {
|
||||
when {
|
||||
isTypeParameter() -> if (check((classifierOrNull as IrTypeParameterSymbol).owner)) return true
|
||||
this is IrSimpleType -> return arguments.any { it.typeOrNull?.anyTypeArgument(check) ?: false }
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// A value that *has* been fully constructed.
|
||||
class MaterialValue(codegen: ExpressionCodegen, type: Type, irType: IrType) : PromisedValue(codegen, type, irType) {
|
||||
override fun discard() {
|
||||
if (type !== Type.VOID_TYPE)
|
||||
AsmUtil.pop(mv, type)
|
||||
}
|
||||
}
|
||||
|
||||
// A value that can be branched on. JVM has certain branching instructions which can be used
|
||||
// to optimize these.
|
||||
abstract class BooleanValue(codegen: ExpressionCodegen) :
|
||||
PromisedValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType) {
|
||||
abstract fun jumpIfFalse(target: Label)
|
||||
abstract fun jumpIfTrue(target: Label)
|
||||
|
||||
override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
|
||||
val const0 = Label()
|
||||
val end = Label()
|
||||
jumpIfFalse(const0)
|
||||
mv.iconst(1)
|
||||
mv.goTo(end)
|
||||
mv.mark(const0)
|
||||
mv.iconst(0)
|
||||
mv.mark(end)
|
||||
if (Type.BOOLEAN_TYPE != target) {
|
||||
StackValue.coerce(Type.BOOLEAN_TYPE, target, mv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BooleanConstant(codegen: ExpressionCodegen, val value: Boolean) : BooleanValue(codegen) {
|
||||
override fun jumpIfFalse(target: Label) = if (value) Unit else mv.goTo(target)
|
||||
override fun jumpIfTrue(target: Label) = if (value) mv.goTo(target) else Unit
|
||||
override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
|
||||
mv.iconst(if (value) 1 else 0)
|
||||
if (Type.BOOLEAN_TYPE != target) {
|
||||
StackValue.coerce(Type.BOOLEAN_TYPE, target, mv)
|
||||
}
|
||||
}
|
||||
|
||||
override fun discard() {}
|
||||
}
|
||||
|
||||
fun PromisedValue.coerceToBoolean(): BooleanValue =
|
||||
when (this) {
|
||||
is BooleanValue -> this
|
||||
else -> object : BooleanValue(codegen) {
|
||||
override fun jumpIfFalse(target: Label) =
|
||||
this@coerceToBoolean.materializeAt(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType, false).also { mv.ifeq(target) }
|
||||
|
||||
override fun jumpIfTrue(target: Label) =
|
||||
this@coerceToBoolean.materializeAt(Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType, false).also { mv.ifne(target) }
|
||||
|
||||
override fun discard() {
|
||||
this@coerceToBoolean.discard()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun PromisedValue.materializeAt(target: Type, irTarget: IrType) {
|
||||
materializeAt(target, irTarget, false)
|
||||
}
|
||||
|
||||
fun PromisedValue.materializedAt(target: Type, irTarget: IrType, castForReified: Boolean = false): MaterialValue {
|
||||
materializeAt(target, irTarget, castForReified)
|
||||
return MaterialValue(codegen, target, irTarget)
|
||||
}
|
||||
|
||||
fun PromisedValue.materialized(): MaterialValue =
|
||||
materializedAt(type, irType)
|
||||
|
||||
fun PromisedValue.materializedAt(irTarget: IrType): MaterialValue =
|
||||
materializedAt(typeMapper.mapType(irTarget), irTarget)
|
||||
|
||||
fun PromisedValue.materializedAtBoxed(irTarget: IrType): MaterialValue =
|
||||
materializedAt(typeMapper.boxType(irTarget), irTarget)
|
||||
|
||||
fun PromisedValue.materialize() {
|
||||
materializeAt(type, irType)
|
||||
}
|
||||
|
||||
fun PromisedValue.materializeAt(irTarget: IrType) {
|
||||
materializeAt(typeMapper.mapType(irTarget), irTarget)
|
||||
}
|
||||
|
||||
fun PromisedValue.materializeAtBoxed(irTarget: IrType) {
|
||||
materializeAt(typeMapper.boxType(irTarget), irTarget)
|
||||
}
|
||||
|
||||
// A Non-materialized value of Unit type that is only materialized through coercion.
|
||||
val ExpressionCodegen.unitValue: PromisedValue
|
||||
get() = MaterialValue(this, Type.VOID_TYPE, context.irBuiltIns.unitType)
|
||||
|
||||
val ExpressionCodegen.nullConstant: PromisedValue
|
||||
get() = object : PromisedValue(this, AsmTypes.OBJECT_TYPE, context.irBuiltIns.nothingNType) {
|
||||
override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
|
||||
mv.aconst(null)
|
||||
}
|
||||
|
||||
override fun discard() {}
|
||||
}
|
||||
+512
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.codegen.`when`.SwitchCodegen.Companion.preferLookupOverSwitch
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.ir.util.isTrueConst
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
// TODO: eliminate the temporary variable
|
||||
class SwitchGenerator(private val expression: IrWhen, private val data: BlockInfo, private val codegen: ExpressionCodegen) {
|
||||
data class ExpressionToLabel(val expression: IrExpression, val label: Label)
|
||||
data class CallToLabel(val call: IrCall, val label: Label)
|
||||
data class ValueToLabel(val value: Any?, val label: Label)
|
||||
|
||||
// @return null if the IrWhen cannot be emitted as lookupswitch or tableswitch.
|
||||
fun generate(): PromisedValue? {
|
||||
val expressionToLabels = ArrayList<ExpressionToLabel>()
|
||||
var elseExpression: IrExpression? = null
|
||||
val callToLabels = ArrayList<CallToLabel>()
|
||||
|
||||
// Parse the when structure. Note that the condition can be nested. See matchConditions() for details.
|
||||
for (branch in expression.branches) {
|
||||
if (branch is IrElseBranch) {
|
||||
elseExpression = branch.result
|
||||
} else {
|
||||
val conditions = matchConditions(branch.condition) ?: return null
|
||||
val thenLabel = Label()
|
||||
expressionToLabels.add(ExpressionToLabel(branch.result, thenLabel))
|
||||
callToLabels += conditions.map { CallToLabel(it, thenLabel) }
|
||||
}
|
||||
}
|
||||
|
||||
// switch isn't applicable if there's no case at all, e.g., when() { else -> ... }
|
||||
if (callToLabels.size == 0)
|
||||
return null
|
||||
|
||||
val calls = callToLabels.map { it.call }
|
||||
|
||||
// To generate a switch from a when it must be a comparison of a single
|
||||
// variable, the "subject", against a series of constants. We assume the
|
||||
// subject is the left hand side of the first condition, provided the
|
||||
// first condition is a comparison. If the first condition is of the form:
|
||||
//
|
||||
// CALL EQEQ(<unsafe-coerce><UInt, Int>(var),_)
|
||||
//
|
||||
// we must be trying to generate an _unsigned_ int switch, and need to
|
||||
// account for unsafe-coerce in all comparisons that arise from the
|
||||
// wrapping and unwrapping of the UInt inline class wrapper. Otherwise,
|
||||
// this is a primitive Int or String switch, with a condition of the form
|
||||
//
|
||||
// CALL EQEQ(var,_)
|
||||
|
||||
val firstCondition = callToLabels[0].call
|
||||
if (firstCondition.symbol != codegen.classCodegen.context.irBuiltIns.eqeqSymbol) return null
|
||||
val subject = firstCondition.getValueArgument(0)
|
||||
return when {
|
||||
subject is IrCall && subject.isCoerceFromUIntToInt() ->
|
||||
generateUIntSwitch(subject.getValueArgument(0)!! as? IrGetValue, calls, callToLabels, expressionToLabels, elseExpression)
|
||||
subject is IrGetValue ->
|
||||
generatePrimitiveSwitch(subject, calls, callToLabels, expressionToLabels, elseExpression)
|
||||
else ->
|
||||
null
|
||||
}?.genOptimizedIfEnoughCases()
|
||||
}
|
||||
|
||||
fun IrCall.isCoerceFromUIntToInt(): Boolean =
|
||||
symbol == codegen.classCodegen.context.ir.symbols.unsafeCoerceIntrinsic
|
||||
&& getTypeArgument(0)?.isUInt() == true
|
||||
&& getTypeArgument(1)?.isInt() == true
|
||||
|
||||
private fun generateUIntSwitch(
|
||||
subject: IrGetValue?,
|
||||
conditions: List<IrCall>,
|
||||
callToLabels: ArrayList<CallToLabel>,
|
||||
expressionToLabels: ArrayList<ExpressionToLabel>,
|
||||
elseExpression: IrExpression?
|
||||
): Switch? {
|
||||
if (subject == null) return null
|
||||
// We check that all conditions are of the form
|
||||
// CALL EQEQ (<unsafe-coerce><UInt,Int>(subject),
|
||||
// <unsafe-coerce><UInt,Int>( Constant ))
|
||||
if (!areConstUIntComparisons(conditions)) return null
|
||||
|
||||
// Filter repeated cases. Allowed in Kotlin but unreachable.
|
||||
val cases = callToLabels.map {
|
||||
val constCoercion = it.call.getValueArgument(1)!! as IrCall
|
||||
val constValue = (constCoercion.getValueArgument(0) as IrConst<*>).value
|
||||
ValueToLabel(
|
||||
constValue,
|
||||
it.label
|
||||
)
|
||||
}.distinctBy { it.value }
|
||||
|
||||
expressionToLabels.removeUnreachableLabels(cases)
|
||||
|
||||
return IntSwitch(
|
||||
subject,
|
||||
elseExpression,
|
||||
expressionToLabels,
|
||||
cases
|
||||
)
|
||||
}
|
||||
|
||||
private fun generatePrimitiveSwitch(
|
||||
subject: IrGetValue,
|
||||
conditions: List<IrCall>,
|
||||
callToLabels: ArrayList<CallToLabel>,
|
||||
expressionToLabels: ArrayList<ExpressionToLabel>,
|
||||
elseExpression: IrExpression?
|
||||
): Switch? {
|
||||
// Checks if all conditions are CALL EQEQ(var,constant)
|
||||
if (!areConstantComparisons(conditions)) return null
|
||||
|
||||
return when {
|
||||
areConstIntComparisons(conditions) -> {
|
||||
val cases = extractSwitchCasesAndFilterUnreachableLabels(callToLabels, expressionToLabels)
|
||||
IntSwitch(
|
||||
subject,
|
||||
elseExpression,
|
||||
expressionToLabels,
|
||||
cases
|
||||
)
|
||||
}
|
||||
areConstStringComparisons(conditions) -> {
|
||||
val cases = extractSwitchCasesAndFilterUnreachableLabels(callToLabels, expressionToLabels)
|
||||
StringSwitch(
|
||||
subject,
|
||||
elseExpression,
|
||||
expressionToLabels,
|
||||
cases
|
||||
)
|
||||
}
|
||||
else ->
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all conditions are of the form
|
||||
//
|
||||
// CALL EQEQ (<unsafe-coerce><UInt,Int>(subject), <unsafe-coerce><UInt,Int>( Constant ))
|
||||
//
|
||||
// where subject is taken to be the first variable compared on the left hand side, if any.
|
||||
private fun areConstUIntComparisons(conditions: List<IrCall>): Boolean {
|
||||
val lhs = conditions.map { it.takeIf { it.symbol == codegen.context.irBuiltIns.eqeqSymbol }?.getValueArgument(0) as? IrCall }
|
||||
if (lhs.any { it == null || !it.isCoerceFromUIntToInt() }) return false
|
||||
val lhsVariableAccesses = lhs.map { it!!.getValueArgument(0) as? IrGetValue }
|
||||
if (lhsVariableAccesses.any { it == null || it.symbol != lhsVariableAccesses[0]!!.symbol }) return false
|
||||
|
||||
val rhs = conditions.map { it.getValueArgument(1) as? IrCall }
|
||||
if (rhs.any { it == null || !it.isCoerceFromUIntToInt() || it.getValueArgument(0) !is IrConst<*> }) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun areConstantComparisons(conditions: List<IrCall>): Boolean {
|
||||
// All conditions are equality checks && all LHS refer to the same tmp variable.
|
||||
val lhs = conditions.map { it.takeIf { it.symbol == codegen.context.irBuiltIns.eqeqSymbol }?.getValueArgument(0) as? IrGetValue }
|
||||
if (lhs.any { it == null || it.symbol != lhs[0]!!.symbol })
|
||||
return false
|
||||
|
||||
// All RHS are constants
|
||||
if (conditions.any { it.getValueArgument(1) !is IrConst<*> })
|
||||
return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun areConstIntComparisons(conditions: List<IrCall>): Boolean {
|
||||
return checkTypeSpecifics(conditions, { it.isInt() }, { it.kind == IrConstKind.Int })
|
||||
}
|
||||
|
||||
private fun areConstStringComparisons(conditions: List<IrCall>): Boolean {
|
||||
return checkTypeSpecifics(
|
||||
conditions,
|
||||
{ it.isString() || it.isNullableString() },
|
||||
{ it.kind == IrConstKind.String || it.kind == IrConstKind.Null })
|
||||
}
|
||||
|
||||
private fun checkTypeSpecifics(
|
||||
conditions: List<IrCall>,
|
||||
subjectTypePredicate: (IrType) -> Boolean,
|
||||
irConstPredicate: (IrConst<*>) -> Boolean
|
||||
): Boolean {
|
||||
val lhs = conditions.map { it.getValueArgument(0) as IrGetValue }
|
||||
if (lhs.any { !subjectTypePredicate(it.type) })
|
||||
return false
|
||||
|
||||
val rhs = conditions.map { it.getValueArgument(1) as IrConst<*> }
|
||||
if (rhs.any { !irConstPredicate(it) })
|
||||
return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun extractSwitchCasesAndFilterUnreachableLabels(
|
||||
callToLabels: List<CallToLabel>,
|
||||
expressionToLabels: ArrayList<ExpressionToLabel>
|
||||
): List<ValueToLabel> {
|
||||
// Don't generate repeated cases, which are unreachable but allowed in Kotlin.
|
||||
// Only keep the first encountered case:
|
||||
val cases =
|
||||
callToLabels.map { ValueToLabel((it.call.getValueArgument(1) as IrConst<*>).value, it.label) }.distinctBy { it.value }
|
||||
|
||||
expressionToLabels.removeUnreachableLabels(cases)
|
||||
|
||||
return cases
|
||||
}
|
||||
|
||||
private fun ArrayList<ExpressionToLabel>.removeUnreachableLabels(cases: List<ValueToLabel>) {
|
||||
val reachableLabels = HashSet(cases.map { it.label })
|
||||
removeIf { it.label !in reachableLabels }
|
||||
}
|
||||
|
||||
// psi2ir lowers multiple cases to nested conditions. For example,
|
||||
//
|
||||
// when (subject) {
|
||||
// a, b, c -> action
|
||||
// }
|
||||
//
|
||||
// is lowered to
|
||||
//
|
||||
// if (if (subject == a)
|
||||
// true
|
||||
// else
|
||||
// if (subject == b)
|
||||
// true
|
||||
// else
|
||||
// subject == c) {
|
||||
// action
|
||||
// }
|
||||
//
|
||||
// fir2ir lowers the same to an or sequence:
|
||||
//
|
||||
// if (((subject == a) || (subject == b)) || (subject = c)) action
|
||||
//
|
||||
// @return true if the conditions are equality checks of constants.
|
||||
private fun matchConditions(condition: IrExpression): ArrayList<IrCall>? {
|
||||
if (condition is IrWhen && condition.origin == IrStatementOrigin.WHEN_COMMA) {
|
||||
assert(condition.type.isBoolean()) { "WHEN_COMMA should always be a Boolean: ${condition.dump()}" }
|
||||
|
||||
val candidates = ArrayList<IrCall>()
|
||||
|
||||
// Match the following structure:
|
||||
//
|
||||
// when() {
|
||||
// cond_1 -> true
|
||||
// cond_2 -> true
|
||||
// ...
|
||||
// else -> cond_N
|
||||
// }
|
||||
//
|
||||
// Namely, the structure which returns true if any one of the condition is true.
|
||||
for (branch in condition.branches) {
|
||||
candidates += if (branch is IrElseBranch) {
|
||||
assert(branch.condition.isTrueConst()) { "IrElseBranch.condition should be const true: ${branch.condition.dump()}" }
|
||||
matchConditions(branch.result) ?: return null
|
||||
} else {
|
||||
if (!branch.result.isTrueConst())
|
||||
return null
|
||||
matchConditions(branch.condition) ?: return null
|
||||
}
|
||||
}
|
||||
|
||||
return if (candidates.isNotEmpty()) candidates else return null
|
||||
} else if (condition is IrCall && condition.symbol == codegen.context.irBuiltIns.ororSymbol) {
|
||||
val candidates = ArrayList<IrCall>()
|
||||
for (i in 0 until condition.valueArgumentsCount) {
|
||||
val argument = condition.getValueArgument(i)!!
|
||||
candidates += matchConditions(argument) ?: return null
|
||||
}
|
||||
return if (candidates.isNotEmpty()) candidates else return null
|
||||
} else if (condition is IrCall) {
|
||||
return arrayListOf(condition)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
abstract inner class Switch(
|
||||
val subject: IrGetValue,
|
||||
val elseExpression: IrExpression?,
|
||||
val expressionToLabels: ArrayList<ExpressionToLabel>
|
||||
) {
|
||||
protected val defaultLabel = Label()
|
||||
|
||||
open fun shouldOptimize() = false
|
||||
|
||||
open fun genOptimizedIfEnoughCases(): PromisedValue? {
|
||||
if (!shouldOptimize())
|
||||
return null
|
||||
|
||||
genSwitch()
|
||||
return genBranchTargets()
|
||||
}
|
||||
|
||||
protected abstract fun genSwitch()
|
||||
|
||||
protected fun genIntSwitch(unsortedIntCases: List<ValueToLabel>) {
|
||||
val intCases = unsortedIntCases.sortedBy { it.value as Int }
|
||||
val caseMin = intCases.first().value as Int
|
||||
val caseMax = intCases.last().value as Int
|
||||
val rangeLength = caseMax.toLong() - caseMin.toLong() + 1L
|
||||
|
||||
// Emit either tableswitch or lookupswitch, depending on the code size.
|
||||
//
|
||||
// lookupswitch is 2X as large as tableswitch with the same entries. However, lookupswitch is sparse while tableswitch must
|
||||
// enumerate all the entries in the range.
|
||||
with(codegen) {
|
||||
if (preferLookupOverSwitch(intCases.size, rangeLength)) {
|
||||
mv.lookupswitch(defaultLabel, intCases.map { it.value as Int }.toIntArray(), intCases.map { it.label }.toTypedArray())
|
||||
} else {
|
||||
val labels = Array(rangeLength.toInt()) { defaultLabel }
|
||||
for (case in intCases)
|
||||
labels[case.value as Int - caseMin] = case.label
|
||||
mv.tableswitch(caseMin, caseMax, defaultLabel, *labels)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun genBranchTargets(): PromisedValue {
|
||||
with(codegen) {
|
||||
val endLabel = Label()
|
||||
|
||||
for ((thenExpression, label) in expressionToLabels) {
|
||||
mv.visitLabel(label)
|
||||
thenExpression.accept(codegen, data).also {
|
||||
if (elseExpression != null) {
|
||||
it.materializedAt(expression.type)
|
||||
} else {
|
||||
it.discard()
|
||||
}
|
||||
}
|
||||
mv.goTo(endLabel)
|
||||
}
|
||||
|
||||
mv.visitLabel(defaultLabel)
|
||||
val result = elseExpression?.accept(codegen, data)?.materializedAt(expression.type) ?: unitValue
|
||||
mv.mark(endLabel)
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class IntSwitch(
|
||||
subject: IrGetValue,
|
||||
elseExpression: IrExpression?,
|
||||
expressionToLabels: ArrayList<ExpressionToLabel>,
|
||||
private val cases: List<ValueToLabel>
|
||||
) : Switch(subject, elseExpression, expressionToLabels) {
|
||||
|
||||
// IF is more compact when there are only 1 or fewer branches, in addition to else.
|
||||
override fun shouldOptimize() = cases.size > 1
|
||||
|
||||
override fun genSwitch() {
|
||||
// Do not generate line numbers for the table switching. In particular,
|
||||
// the subject is extracted from the condition of the first branch which
|
||||
// will give the wrong stepping behavior for code such as:
|
||||
//
|
||||
// when {
|
||||
// x == 42 -> 1
|
||||
// x == 32 -> 2
|
||||
// x == 24 -> 3
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// If the subject line number is generated, we will not stop on the line
|
||||
// of the `when` but instead stop on the `x == 42` line. When x is 24,
|
||||
// we would stop on the line `x == 42` and then step to the line `x == 24`.
|
||||
// That is confusing and we prefer to stop on the `when` line and then step
|
||||
// to the `x == 24` line. This is accomplished by ignoring the line number
|
||||
// information for the subject as the `when` line number has already been
|
||||
// emitted.
|
||||
codegen.noLineNumberScope {
|
||||
val subjectValue = subject.accept(codegen, data)
|
||||
subjectValue.materializeAt(Type.INT_TYPE, subjectValue.irType)
|
||||
}
|
||||
genIntSwitch(cases)
|
||||
}
|
||||
}
|
||||
|
||||
// The following when structure:
|
||||
//
|
||||
// when (s) {
|
||||
// s1, s2 -> e1,
|
||||
// s3 -> e2,
|
||||
// s4 -> e3,
|
||||
// ...
|
||||
// else -> e
|
||||
// }
|
||||
//
|
||||
// is implemented as:
|
||||
//
|
||||
// // if s is String?, generate the following null check:
|
||||
// if (s == null)
|
||||
// // jump to the case where null is handled, if defined.
|
||||
// // otherwise, jump out of the when().
|
||||
// ...
|
||||
// ...
|
||||
// when (s.hashCode()) {
|
||||
// h1 -> {
|
||||
// if (s == s1)
|
||||
// e1
|
||||
// else if (s == s2)
|
||||
// e1
|
||||
// else if (s == s3)
|
||||
// e2
|
||||
// else
|
||||
// e
|
||||
// }
|
||||
// h2 -> if (s == s3) e2 else e,
|
||||
// ...
|
||||
// else -> e
|
||||
// }
|
||||
//
|
||||
// where s1.hashCode() == s2.hashCode() == s3.hashCode() == h1,
|
||||
// s4.hashCode() == h2.
|
||||
//
|
||||
// A tableswitch or lookupswitch is then used for the hash code lookup.
|
||||
|
||||
inner class StringSwitch(
|
||||
subject: IrGetValue,
|
||||
elseExpression: IrExpression?,
|
||||
expressionToLabels: ArrayList<ExpressionToLabel>,
|
||||
private val cases: List<ValueToLabel>
|
||||
) : Switch(subject, elseExpression, expressionToLabels) {
|
||||
|
||||
private val hashToStringAndExprLabels = HashMap<Int, ArrayList<ValueToLabel>>()
|
||||
private val hashAndSwitchLabels = ArrayList<ValueToLabel>()
|
||||
|
||||
init {
|
||||
for (case in cases)
|
||||
if (case.value != null) // null is handled specially and will never be dispatched from the switch.
|
||||
hashToStringAndExprLabels.getOrPut(case.value.hashCode()) { ArrayList() }.add(
|
||||
ValueToLabel(case.value, case.label)
|
||||
)
|
||||
|
||||
for (key in hashToStringAndExprLabels.keys)
|
||||
hashAndSwitchLabels.add(ValueToLabel(key, Label()))
|
||||
}
|
||||
|
||||
// Using a switch, the subject string has to be traversed at least twice
|
||||
// (hash + comparison * N, where N is #strings hashed into the same bucket).
|
||||
// The optimization isn't better than an IF cascade when #switch-targets <= 2.
|
||||
//
|
||||
// Generate "optimized" version for @EnhancedNullability subject type
|
||||
// to model 1.0 behavior causing NPE in case of null value.
|
||||
// TODO make 'when' with String subject behavior consistent.
|
||||
// see:
|
||||
// box/when/stringOptimization/enhancedNullability.kt
|
||||
// box/when/stringOptimization/flexibleNullability.kt
|
||||
override fun shouldOptimize() =
|
||||
hashAndSwitchLabels.size > 2 ||
|
||||
subject.type.hasAnnotation(JvmAnnotationNames.ENHANCED_NULLABILITY_ANNOTATION) && hashAndSwitchLabels.isNotEmpty()
|
||||
|
||||
override fun genSwitch() {
|
||||
with(codegen) {
|
||||
// Do not generate line numbers for the table switching. In particular,
|
||||
// the subject is extracted from the condition of the first branch which
|
||||
// will give the wrong stepping behavior for code such as:
|
||||
//
|
||||
// when {
|
||||
// x == "x" -> 1
|
||||
// x == "y" -> 2
|
||||
// x == "z" -> 3
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// If the subject line number is generated, we will not stop on the line
|
||||
// of the `when` but instead stop on the `x == "x"` line. When x is "z",
|
||||
// we would stop on the line `x == "x"` and then step to the line `x == "z"`.
|
||||
// That is confusing and we prefer to stop on the `when` line and then step
|
||||
// to the `x == "z"` line. This is accomplished by ignoring the line number
|
||||
// information for the subject as the `when` line number has already been
|
||||
// emitted.
|
||||
noLineNumberScope {
|
||||
if (subject.type.isNullableString()) {
|
||||
subject.accept(codegen, data).materialize()
|
||||
mv.ifnull(cases.find { it.value == null }?.label ?: defaultLabel)
|
||||
}
|
||||
// Reevaluating the subject is fine here because it is a read of a temporary.
|
||||
subject.accept(codegen, data).materialize()
|
||||
mv.invokevirtual("java/lang/String", "hashCode", "()I", false)
|
||||
}
|
||||
genIntSwitch(hashAndSwitchLabels)
|
||||
|
||||
// Multiple strings can be hashed into the same bucket.
|
||||
// Generate an if cascade to resolve that for each bucket.
|
||||
for ((hash, switchLabel) in hashAndSwitchLabels) {
|
||||
mv.visitLabel(switchLabel)
|
||||
for ((string, label) in hashToStringAndExprLabels[hash]!!) {
|
||||
noLineNumberScope {
|
||||
subject.accept(codegen, data).materialize()
|
||||
}
|
||||
mv.aconst(string)
|
||||
mv.invokevirtual("java/lang/String", "equals", "(Ljava/lang/Object;)Z", false)
|
||||
mv.ifne(label)
|
||||
}
|
||||
mv.goTo(defaultLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.codegen
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.backend.common.ir.allOverridden
|
||||
import org.jetbrains.kotlin.backend.common.ir.ir2string
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.MultifileFacadeFileEntry
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.fileParent
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineOnly
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isJvmInterface
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isPrivateInlineSuspend
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.IrTypeMapper
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.mapClass
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.mapSupertype
|
||||
import org.jetbrains.kotlin.builtins.StandardNames.FqNames
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.FrameMapBase
|
||||
import org.jetbrains.kotlin.codegen.OwnerKind
|
||||
import org.jetbrains.kotlin.codegen.SourceInfo
|
||||
import org.jetbrains.kotlin.codegen.inline.SourceMapper
|
||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.getClass
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
class IrFrameMap : FrameMapBase<IrSymbol>() {
|
||||
private val typeMap = mutableMapOf<IrSymbol, Type>()
|
||||
|
||||
override fun enter(key: IrSymbol, type: Type): Int {
|
||||
typeMap[key] = type
|
||||
return super.enter(key, type)
|
||||
}
|
||||
|
||||
override fun leave(key: IrSymbol): Int {
|
||||
typeMap.remove(key)
|
||||
return super.leave(key)
|
||||
}
|
||||
|
||||
fun typeOf(symbol: IrSymbol): Type = typeMap[symbol]
|
||||
?: error("No mapping for symbol: ${symbol.owner.render()}")
|
||||
}
|
||||
|
||||
internal val IrFunction.isStatic
|
||||
get() = (this.dispatchReceiverParameter == null && this !is IrConstructor)
|
||||
|
||||
fun IrFrameMap.enter(irDeclaration: IrSymbolOwner, type: Type): Int {
|
||||
return enter(irDeclaration.symbol, type)
|
||||
}
|
||||
|
||||
fun IrFrameMap.leave(irDeclaration: IrSymbolOwner): Int {
|
||||
return leave(irDeclaration.symbol)
|
||||
}
|
||||
|
||||
fun JvmBackendContext.getSourceMapper(declaration: IrClass): SourceMapper {
|
||||
val fileEntry = declaration.fileParent.fileEntry
|
||||
// NOTE: apparently inliner requires the source range to cover the
|
||||
// whole file the class is declared in rather than the class only.
|
||||
val endLineNumber = when (fileEntry) {
|
||||
is MultifileFacadeFileEntry -> 0
|
||||
else -> fileEntry.getSourceRangeInfo(0, fileEntry.maxOffset).endLineNumber
|
||||
}
|
||||
val sourceFileName = when (fileEntry) {
|
||||
is MultifileFacadeFileEntry -> fileEntry.partFiles.singleOrNull()?.name
|
||||
else -> declaration.fileParent.name
|
||||
}
|
||||
return SourceMapper(
|
||||
SourceInfo(
|
||||
sourceFileName,
|
||||
typeMapper.mapClass(declaration).internalName,
|
||||
endLineNumber + 1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val IrType.isExtensionFunctionType: Boolean
|
||||
get() = isFunctionTypeOrSubtype() && hasAnnotation(FqNames.extensionFunctionType)
|
||||
|
||||
|
||||
/* Borrowed with modifications from AsmUtil.java */
|
||||
|
||||
private val NO_FLAG_LOCAL = 0
|
||||
|
||||
private fun IrDeclaration.getVisibilityAccessFlagForAnonymous(): Int =
|
||||
if (isInlineOrContainedInInline(parent as? IrDeclaration)) Opcodes.ACC_PUBLIC else AsmUtil.NO_FLAG_PACKAGE_PRIVATE
|
||||
|
||||
fun IrClass.calculateInnerClassAccessFlags(context: JvmBackendContext): Int {
|
||||
val isLambda = superTypes.any {
|
||||
it.safeAs<IrSimpleType>()?.classifier === context.ir.symbols.lambdaClass
|
||||
}
|
||||
val visibility = when {
|
||||
isLambda -> getVisibilityAccessFlagForAnonymous()
|
||||
visibility === DescriptorVisibilities.LOCAL -> Opcodes.ACC_PUBLIC
|
||||
else -> getVisibilityAccessFlag()
|
||||
}
|
||||
return visibility or
|
||||
if (origin.isSynthetic) Opcodes.ACC_SYNTHETIC else 0 or
|
||||
innerAccessFlagsForModalityAndKind() or
|
||||
if (isInner) 0 else Opcodes.ACC_STATIC
|
||||
}
|
||||
|
||||
private fun IrClass.innerAccessFlagsForModalityAndKind(): Int {
|
||||
when (kind) {
|
||||
ClassKind.INTERFACE -> return Opcodes.ACC_ABSTRACT or Opcodes.ACC_INTERFACE
|
||||
ClassKind.ENUM_CLASS -> return Opcodes.ACC_FINAL or Opcodes.ACC_ENUM
|
||||
ClassKind.ANNOTATION_CLASS -> return Opcodes.ACC_ABSTRACT or Opcodes.ACC_ANNOTATION or Opcodes.ACC_INTERFACE
|
||||
else -> {
|
||||
if (modality === Modality.FINAL) {
|
||||
return Opcodes.ACC_FINAL
|
||||
} else if (modality === Modality.ABSTRACT || modality === Modality.SEALED) {
|
||||
return Opcodes.ACC_ABSTRACT
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
fun IrDeclarationWithVisibility.getVisibilityAccessFlag(kind: OwnerKind? = null): Int {
|
||||
specialCaseVisibility(kind)?.let {
|
||||
return it
|
||||
}
|
||||
return when (visibility) {
|
||||
DescriptorVisibilities.PRIVATE -> Opcodes.ACC_PRIVATE
|
||||
DescriptorVisibilities.PRIVATE_TO_THIS -> Opcodes.ACC_PRIVATE
|
||||
DescriptorVisibilities.PROTECTED -> Opcodes.ACC_PROTECTED
|
||||
JavaDescriptorVisibilities.PROTECTED_STATIC_VISIBILITY -> Opcodes.ACC_PROTECTED
|
||||
JavaDescriptorVisibilities.PROTECTED_AND_PACKAGE -> Opcodes.ACC_PROTECTED
|
||||
DescriptorVisibilities.PUBLIC -> Opcodes.ACC_PUBLIC
|
||||
DescriptorVisibilities.INTERNAL -> Opcodes.ACC_PUBLIC
|
||||
DescriptorVisibilities.LOCAL -> NO_FLAG_LOCAL
|
||||
JavaDescriptorVisibilities.PACKAGE_VISIBILITY -> AsmUtil.NO_FLAG_PACKAGE_PRIVATE
|
||||
else -> throw IllegalStateException("$visibility is not a valid visibility in backend for ${ir2string(this)}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrDeclarationWithVisibility.specialCaseVisibility(kind: OwnerKind?): Int? {
|
||||
if (this is IrClass && DescriptorVisibilities.isPrivate(visibility) && isCompanion && hasInterfaceParent()) {
|
||||
// TODO: non-intrinsic
|
||||
return Opcodes.ACC_PUBLIC
|
||||
}
|
||||
|
||||
if (this is IrConstructor && parentAsClass.isInline && kind === OwnerKind.IMPLEMENTATION) {
|
||||
return Opcodes.ACC_PRIVATE
|
||||
}
|
||||
|
||||
if (isInlineOnlyPrivateInBytecode()) {
|
||||
return Opcodes.ACC_PRIVATE
|
||||
}
|
||||
|
||||
if (visibility === DescriptorVisibilities.LOCAL && this is IrFunction) {
|
||||
return Opcodes.ACC_PUBLIC
|
||||
}
|
||||
|
||||
if (this is IrClass && this.kind === ClassKind.ENUM_ENTRY) {
|
||||
return AsmUtil.NO_FLAG_PACKAGE_PRIVATE
|
||||
}
|
||||
|
||||
if (this is IrField && correspondingPropertySymbol?.owner?.isExternal == true) {
|
||||
val method = correspondingPropertySymbol?.owner?.getter ?: correspondingPropertySymbol?.owner?.setter
|
||||
?: error("No get/set method in SyntheticJavaPropertyDescriptor: ${ir2string(correspondingPropertySymbol?.owner)}")
|
||||
return method.getVisibilityAccessFlag()
|
||||
}
|
||||
|
||||
if (this is IrSimpleFunction && visibility === DescriptorVisibilities.PROTECTED &&
|
||||
allOverridden().any { it.parentAsClass.isJvmInterface }
|
||||
) {
|
||||
return Opcodes.ACC_PUBLIC
|
||||
}
|
||||
|
||||
if (!DescriptorVisibilities.isPrivate(visibility)) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (this is IrConstructor && parentAsClass.kind === ClassKind.ENUM_ENTRY) {
|
||||
return AsmUtil.NO_FLAG_PACKAGE_PRIVATE
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private tailrec fun isInlineOrContainedInInline(declaration: IrDeclaration?): Boolean = when {
|
||||
declaration === null -> false
|
||||
declaration is IrFunction && declaration.isInline -> true
|
||||
else -> isInlineOrContainedInInline(declaration.parent as? IrDeclaration)
|
||||
}
|
||||
|
||||
private fun IrDeclarationWithVisibility.isInlineOnlyPrivateInBytecode(): Boolean =
|
||||
this is IrFunction && (isInlineOnly() || isPrivateInlineSuspend())
|
||||
|
||||
// Borrowed with modifications from ImplementationBodyCodegen.java
|
||||
|
||||
private val KOTLIN_MARKER_INTERFACES: Map<FqName, String> = run {
|
||||
val kotlinMarkerInterfaces = mutableMapOf<FqName, String>()
|
||||
for (platformMutabilityMapping in JavaToKotlinClassMap.mutabilityMappings) {
|
||||
kotlinMarkerInterfaces[platformMutabilityMapping.kotlinReadOnly.asSingleFqName()] = "kotlin/jvm/internal/markers/KMappedMarker"
|
||||
|
||||
val mutableClassId = platformMutabilityMapping.kotlinMutable
|
||||
kotlinMarkerInterfaces[mutableClassId.asSingleFqName()] =
|
||||
"kotlin/jvm/internal/markers/K" + mutableClassId.relativeClassName.asString()
|
||||
.replace("MutableEntry", "Entry") // kotlin.jvm.internal.markers.KMutableMap.Entry for some reason
|
||||
.replace(".", "$")
|
||||
}
|
||||
kotlinMarkerInterfaces
|
||||
}
|
||||
|
||||
internal fun IrTypeMapper.mapClassSignature(irClass: IrClass, type: Type): JvmClassSignature {
|
||||
val sw = BothSignatureWriter(BothSignatureWriter.Mode.CLASS)
|
||||
writeFormalTypeParameters(irClass.typeParameters, sw)
|
||||
|
||||
sw.writeSuperclass()
|
||||
val superClassType = irClass.superTypes.find { it.getClass()?.isJvmInterface == false }
|
||||
val superClassAsmType = if (superClassType == null) {
|
||||
sw.writeClassBegin(AsmTypes.OBJECT_TYPE)
|
||||
sw.writeClassEnd()
|
||||
AsmTypes.OBJECT_TYPE
|
||||
} else {
|
||||
mapSupertype(superClassType, sw)
|
||||
}
|
||||
sw.writeSuperclassEnd()
|
||||
|
||||
val kotlinMarkerInterfaces = LinkedHashSet<String>()
|
||||
if (irClass.superTypes.any { it.isSuspendFunction() || it.isKSuspendFunction() }) {
|
||||
kotlinMarkerInterfaces.add("kotlin/coroutines/jvm/internal/SuspendFunction")
|
||||
}
|
||||
|
||||
val superInterfaces = LinkedHashSet<String>()
|
||||
for (superType in irClass.superTypes) {
|
||||
val superClass = superType.safeAs<IrSimpleType>()?.classifier?.safeAs<IrClassSymbol>()?.owner ?: continue
|
||||
if (superClass.isJvmInterface) {
|
||||
sw.writeInterface()
|
||||
superInterfaces.add(mapSupertype(superType, sw).internalName)
|
||||
sw.writeInterfaceEnd()
|
||||
kotlinMarkerInterfaces.addIfNotNull(KOTLIN_MARKER_INTERFACES[superClass.fqNameWhenAvailable!!])
|
||||
}
|
||||
}
|
||||
|
||||
for (kotlinMarkerInterface in kotlinMarkerInterfaces) {
|
||||
sw.writeInterface()
|
||||
sw.writeAsmType(Type.getObjectType(kotlinMarkerInterface))
|
||||
sw.writeInterfaceEnd()
|
||||
}
|
||||
|
||||
superInterfaces.addAll(kotlinMarkerInterfaces)
|
||||
|
||||
return JvmClassSignature(
|
||||
type.internalName, superClassAsmType.internalName,
|
||||
ArrayList(superInterfaces), sw.makeJavaGenericSignature()
|
||||
)
|
||||
}
|
||||
|
||||
/* Copied with modifications from AsmUtil.getVisibilityAccessFlagForClass */
|
||||
/*
|
||||
Use this method to get visibility flag for class to define it in byte code (v.defineClass method).
|
||||
For other cases use getVisibilityAccessFlag(MemberDescriptor descriptor)
|
||||
Classes in byte code should be public or package private
|
||||
*/
|
||||
fun IrClass.getVisibilityAccessFlagForClass(): Int {
|
||||
/* Original had a check for SyntheticClassDescriptorForJava, never invoked in th IR backend. */
|
||||
if (kind == ClassKind.ENUM_ENTRY) {
|
||||
return AsmUtil.NO_FLAG_PACKAGE_PRIVATE
|
||||
}
|
||||
return if (visibility === DescriptorVisibilities.PUBLIC ||
|
||||
visibility === DescriptorVisibilities.PROTECTED ||
|
||||
// TODO: should be package private, but for now Kotlin's reflection can't access members of such classes
|
||||
visibility === DescriptorVisibilities.LOCAL ||
|
||||
visibility === DescriptorVisibilities.INTERNAL
|
||||
) {
|
||||
Opcodes.ACC_PUBLIC
|
||||
} else AsmUtil.NO_FLAG_PACKAGE_PRIVATE
|
||||
}
|
||||
|
||||
val IrDeclaration.isAnnotatedWithDeprecated: Boolean
|
||||
get() = annotations.hasAnnotation(FqNames.deprecated)
|
||||
|
||||
internal fun IrDeclaration.isDeprecatedCallable(context: JvmBackendContext): Boolean =
|
||||
isAnnotatedWithDeprecated ||
|
||||
annotations.any { it.symbol == context.ir.symbols.javaLangDeprecatedConstructorWithDeprecatedFlag }
|
||||
|
||||
internal fun IrFunction.isDeprecatedFunction(context: JvmBackendContext): Boolean =
|
||||
origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_OR_TYPEALIAS_ANNOTATIONS ||
|
||||
isDeprecatedCallable(context) ||
|
||||
(this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.isDeprecatedCallable(context) == true ||
|
||||
isAccessorForDeprecatedPropertyImplementedByDelegation ||
|
||||
isAccessorForDeprecatedJvmStaticProperty(context)
|
||||
|
||||
private val IrFunction.isAccessorForDeprecatedPropertyImplementedByDelegation: Boolean
|
||||
get() =
|
||||
origin == IrDeclarationOrigin.DELEGATED_MEMBER &&
|
||||
this is IrSimpleFunction &&
|
||||
correspondingPropertySymbol != null &&
|
||||
overriddenSymbols.any {
|
||||
it.owner.correspondingPropertySymbol?.owner?.isAnnotatedWithDeprecated == true
|
||||
}
|
||||
|
||||
private fun IrFunction.isAccessorForDeprecatedJvmStaticProperty(context: JvmBackendContext): Boolean {
|
||||
if (origin != JvmLoweredDeclarationOrigin.JVM_STATIC_WRAPPER) return false
|
||||
val irExpressionBody = this.body as? IrExpressionBody
|
||||
?: throw AssertionError("IrExpressionBody expected for JvmStatic wrapper:\n${this.dump()}")
|
||||
val irCall = irExpressionBody.expression as? IrCall
|
||||
?: throw AssertionError("IrCall expected inside JvmStatic wrapper:\n${this.dump()}")
|
||||
val callee = irCall.symbol.owner
|
||||
val property = callee.correspondingPropertySymbol?.owner ?: return false
|
||||
return property.isDeprecatedCallable(context)
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2010-2019 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.receiverAndArgs
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
|
||||
object AndAnd : IntrinsicMethod() {
|
||||
|
||||
private class BooleanConjunction(val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo) :
|
||||
BooleanValue(codegen) {
|
||||
|
||||
override fun jumpIfFalse(target: Label) {
|
||||
arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(target)
|
||||
arg1.accept(codegen, data).coerceToBoolean().jumpIfFalse(target)
|
||||
}
|
||||
|
||||
override fun jumpIfTrue(target: Label) {
|
||||
val stayLabel = Label()
|
||||
arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(stayLabel)
|
||||
arg1.accept(codegen, data).coerceToBoolean().jumpIfTrue(target)
|
||||
mv.visitLabel(stayLabel)
|
||||
}
|
||||
|
||||
override fun discard() {
|
||||
val end = Label()
|
||||
arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(end)
|
||||
arg1.accept(codegen, data).discard()
|
||||
mv.visitLabel(end)
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val (left, right) = expression.receiverAndArgs()
|
||||
return BooleanConjunction(left, right, codegen, data)
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object ArrayGet : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val dispatchReceiver = expression.dispatchReceiver!!
|
||||
val receiver = dispatchReceiver.accept(codegen, data).materializedAt(dispatchReceiver.type)
|
||||
val elementType = AsmUtil.correctElementType(receiver.type)
|
||||
expression.getValueArgument(0)!!.accept(codegen, data)
|
||||
.materializeAt(Type.INT_TYPE, codegen.context.irBuiltIns.intType)
|
||||
codegen.mv.aload(elementType)
|
||||
return MaterialValue(codegen, elementType, expression.type)
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.mapClass
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
|
||||
object ArrayIterator : IntrinsicMethod() {
|
||||
override fun toCallable(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction {
|
||||
val owner = context.typeMapper.mapClass(expression.symbol.owner.parentAsClass)
|
||||
return IrIntrinsicFunction.create(expression, signature, context, owner) {
|
||||
val methodSignature = "(${owner.descriptor})${signature.returnType.descriptor}"
|
||||
val intrinsicOwner =
|
||||
if (AsmUtil.isPrimitive(owner.elementType))
|
||||
"kotlin/jvm/internal/ArrayIteratorsKt"
|
||||
else
|
||||
"kotlin/jvm/internal/ArrayIteratorKt"
|
||||
it.invokestatic(intrinsicOwner, "iterator", methodSignature, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.types.getArrayElementType
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object ArraySet : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val dispatchReceiver = expression.dispatchReceiver!!
|
||||
val receiver = dispatchReceiver.accept(codegen, data).materializedAt(dispatchReceiver.type)
|
||||
val elementType = AsmUtil.correctElementType(receiver.type)
|
||||
val elementIrType = receiver.irType.getArrayElementType(codegen.context.irBuiltIns)
|
||||
expression.getValueArgument(0)!!.accept(codegen, data).materializeAt(Type.INT_TYPE, codegen.context.irBuiltIns.intType)
|
||||
expression.getValueArgument(1)!!.accept(codegen, data).materializeAt(elementType, elementIrType)
|
||||
codegen.mv.astore(elementType)
|
||||
return codegen.unitValue
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
|
||||
object ArraySize : IntrinsicMethod() {
|
||||
|
||||
override fun toCallable(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
|
||||
return IrIntrinsicFunction.create(expression, signature, context) {
|
||||
it.arraylength()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.numberFunctionOperandType
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.types.isChar
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
class BinaryOp(private val opcode: Int) : IntrinsicMethod() {
|
||||
private fun shift(): Boolean =
|
||||
opcode == ISHL || opcode == ISHR || opcode == IUSHR
|
||||
|
||||
override fun toCallable(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction {
|
||||
val returnType = signature.returnType
|
||||
val intermediateResultType = numberFunctionOperandType(returnType)
|
||||
val argTypes = if (!expression.symbol.owner.parentAsClass.defaultType.isChar()) {
|
||||
listOf(intermediateResultType, if (shift()) Type.INT_TYPE else intermediateResultType)
|
||||
} else {
|
||||
listOf(Type.CHAR_TYPE, signature.valueParameters[0].asmType)
|
||||
}
|
||||
|
||||
return IrIntrinsicFunction.create(expression, signature, context, argTypes) {
|
||||
it.visitInsn(returnType.getOpcode(opcode))
|
||||
StackValue.coerce(intermediateResultType, returnType, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object Clone : IntrinsicMethod() {
|
||||
|
||||
private val CLONEABLE_TYPE = Type.getObjectType("java/lang/Cloneable")
|
||||
|
||||
override fun toCallable(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction {
|
||||
val isSuperCall = expression is IrCall && expression.superQualifierSymbol != null
|
||||
val opcode = if (isSuperCall) Opcodes.INVOKESPECIAL else Opcodes.INVOKEVIRTUAL
|
||||
val newSignature = signature.newReturnType(AsmTypes.OBJECT_TYPE)
|
||||
val argTypes0 = expression.argTypes(context)
|
||||
// Don't upcast receiver to java.lang.Cloneable, since 'clone' is protected in java.lang.Object.
|
||||
val argTypes = if (isSuperCall || argTypes0[0] == CLONEABLE_TYPE) listOf(AsmTypes.OBJECT_TYPE) else argTypes0
|
||||
return IrIntrinsicFunction.create(expression, newSignature, context, argTypes) { mv ->
|
||||
mv.visitMethodInsn(opcode, "java/lang/Object", "clone", "()Ljava/lang/Object;", false)
|
||||
}
|
||||
}
|
||||
}
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmSymbols
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isSmartcastFromHigherThanNullable
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.receiverAndArgs
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.comparisonOperandType
|
||||
import org.jetbrains.kotlin.codegen.BranchedValue
|
||||
import org.jetbrains.kotlin.codegen.NumberCompare
|
||||
import org.jetbrains.kotlin.codegen.ObjectCompare
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
object CompareTo : IntrinsicMethod() {
|
||||
private fun genInvoke(type: Type?, v: InstructionAdapter) {
|
||||
when (type) {
|
||||
Type.CHAR_TYPE, Type.BYTE_TYPE, Type.SHORT_TYPE, Type.INT_TYPE ->
|
||||
v.invokestatic(
|
||||
JvmSymbols.INTRINSICS_CLASS_NAME,
|
||||
"compare",
|
||||
"(II)I",
|
||||
false
|
||||
)
|
||||
Type.LONG_TYPE -> v.invokestatic(JvmSymbols.INTRINSICS_CLASS_NAME, "compare", "(JJ)I", false)
|
||||
Type.FLOAT_TYPE -> v.invokestatic("java/lang/Float", "compare", "(FF)I", false)
|
||||
Type.DOUBLE_TYPE -> v.invokestatic("java/lang/Double", "compare", "(DD)I", false)
|
||||
else -> throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun toCallable(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction {
|
||||
val parameterType = comparisonOperandType(
|
||||
expressionType(expression.dispatchReceiver ?: expression.extensionReceiver!!, context),
|
||||
signature.valueParameters.single().asmType
|
||||
)
|
||||
return IrIntrinsicFunction.create(expression, signature, context, listOf(parameterType, parameterType)) {
|
||||
genInvoke(parameterType, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IntegerZeroComparison(val op: IElementType, val a: MaterialValue) : BooleanValue(a.codegen) {
|
||||
override fun jumpIfFalse(target: Label) {
|
||||
mv.visitJumpInsn(Opcodes.IFNE, target)
|
||||
}
|
||||
|
||||
override fun jumpIfTrue(target: Label) {
|
||||
mv.visitJumpInsn(Opcodes.IFEQ, target)
|
||||
}
|
||||
|
||||
override fun discard() {
|
||||
a.discard()
|
||||
}
|
||||
}
|
||||
|
||||
class BooleanComparison(val op: IElementType, val a: MaterialValue, val b: MaterialValue) : BooleanValue(a.codegen) {
|
||||
override fun jumpIfFalse(target: Label) {
|
||||
// TODO 1. get rid of the dependency; 2. take `b.type` into account.
|
||||
val opcode = if (a.type.sort == Type.OBJECT)
|
||||
ObjectCompare.getObjectCompareOpcode(op)
|
||||
else
|
||||
NumberCompare.patchOpcode(NumberCompare.getNumberCompareOpcode(op), mv, op, a.type)
|
||||
mv.visitJumpInsn(opcode, target)
|
||||
}
|
||||
|
||||
override fun jumpIfTrue(target: Label) {
|
||||
val opcode = if (a.type.sort == Type.OBJECT)
|
||||
BranchedValue.negatedOperations[ObjectCompare.getObjectCompareOpcode(op)]!!
|
||||
else
|
||||
NumberCompare.patchOpcode(BranchedValue.negatedOperations[NumberCompare.getNumberCompareOpcode(op)]!!, mv, op, a.type)
|
||||
mv.visitJumpInsn(opcode, target)
|
||||
}
|
||||
|
||||
override fun discard() {
|
||||
b.discard()
|
||||
a.discard()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class NonIEEE754FloatComparison(val op: IElementType, val a: MaterialValue, val b: MaterialValue) : BooleanValue(a.codegen) {
|
||||
private val numberCompareOpcode = NumberCompare.getNumberCompareOpcode(op)
|
||||
|
||||
private fun invokeStaticComparison(type: Type) {
|
||||
when (type) {
|
||||
Type.FLOAT_TYPE -> mv.invokestatic("java/lang/Float", "compare", "(FF)I", false)
|
||||
Type.DOUBLE_TYPE -> mv.invokestatic("java/lang/Double", "compare", "(DD)I", false)
|
||||
else -> throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun jumpIfFalse(target: Label) {
|
||||
invokeStaticComparison(a.type)
|
||||
mv.visitJumpInsn(numberCompareOpcode, target)
|
||||
}
|
||||
|
||||
override fun jumpIfTrue(target: Label) {
|
||||
invokeStaticComparison(a.type)
|
||||
mv.visitJumpInsn(BranchedValue.negatedOperations[numberCompareOpcode]!!, target)
|
||||
}
|
||||
|
||||
override fun discard() {
|
||||
b.discard()
|
||||
a.discard()
|
||||
}
|
||||
}
|
||||
|
||||
class PrimitiveToObjectComparison(
|
||||
private val op: IElementType,
|
||||
private val leftIsPrimitive: Boolean,
|
||||
private val left: MaterialValue,
|
||||
private val right: MaterialValue
|
||||
) : BooleanValue(left.codegen) {
|
||||
private fun checkTypeAndCompare(onWrongType: Label): BooleanValue {
|
||||
val compareLabel = Label()
|
||||
// If it's the left value that needs unboxing, it should be moved to the top of the stack. `AsmUtil.swap`
|
||||
// is theoretically OK, but in practice breaks peephole optimization passes that unbox longs/doubles,
|
||||
// so just storing in a variable is safer.
|
||||
val tmp = if (leftIsPrimitive) -1 else codegen.frameMap.enterTemp(right.type).also { mv.store(it, right.type) }
|
||||
mv.dup()
|
||||
if (AsmUtil.isBoxedPrimitiveType(if (leftIsPrimitive) right.type else left.type)) {
|
||||
mv.ifnonnull(compareLabel)
|
||||
} else {
|
||||
mv.instanceOf(AsmUtil.boxType(if (leftIsPrimitive) left.type else right.type))
|
||||
mv.ifne(compareLabel)
|
||||
}
|
||||
// Type checking of the object failed, values are irrelevant now:
|
||||
if (leftIsPrimitive) right.discard() // else it's already popped by `mv.store`
|
||||
left.discard()
|
||||
mv.goTo(onWrongType)
|
||||
mv.mark(compareLabel)
|
||||
// Type checking OK, can unbox and compare:
|
||||
return if (leftIsPrimitive) {
|
||||
BooleanComparison(op, left, right.materializedAt(left.type, right.irType))
|
||||
} else {
|
||||
val leftUnboxed = left.materializedAt(right.type, left.irType)
|
||||
mv.load(tmp, right.type)
|
||||
codegen.frameMap.leaveTemp(right.type)
|
||||
BooleanComparison(op, leftUnboxed, right)
|
||||
}
|
||||
}
|
||||
|
||||
override fun jumpIfFalse(target: Label) {
|
||||
checkTypeAndCompare(target).jumpIfFalse(target)
|
||||
}
|
||||
|
||||
override fun jumpIfTrue(target: Label) {
|
||||
val wrongType = Label()
|
||||
checkTypeAndCompare(wrongType).jumpIfTrue(target)
|
||||
mv.mark(wrongType)
|
||||
}
|
||||
|
||||
override fun discard() {
|
||||
right.discard()
|
||||
left.discard()
|
||||
}
|
||||
}
|
||||
|
||||
class PrimitiveComparison(
|
||||
private val primitiveNumberType: PrimitiveType,
|
||||
private val operatorToken: KtSingleValueToken
|
||||
) : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val parameterType = Type.getType(JvmPrimitiveType.get(primitiveNumberType).desc)
|
||||
val (left, right) = expression.receiverAndArgs()
|
||||
val a = left.accept(codegen, data).materializedAt(parameterType, left.type)
|
||||
val b = right.accept(codegen, data).materializedAt(parameterType, right.type)
|
||||
|
||||
val useNonIEEE754Comparison =
|
||||
!codegen.context.state.languageVersionSettings.supportsFeature(LanguageFeature.ProperIeee754Comparisons)
|
||||
&& (parameterType == Type.FLOAT_TYPE || parameterType == Type.DOUBLE_TYPE)
|
||||
&& (left.isSmartcastFromHigherThanNullable(codegen.context) || right.isSmartcastFromHigherThanNullable(codegen.context))
|
||||
|
||||
return if (useNonIEEE754Comparison) {
|
||||
NonIEEE754FloatComparison(operatorToken, a, b)
|
||||
} else {
|
||||
BooleanComparison(operatorToken, a, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.MaterialValue
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.materializedAt
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
|
||||
import org.jetbrains.kotlin.codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object EnumValueOf : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) {
|
||||
val type = expression.getTypeArgument(0)!!
|
||||
val result = expression.getValueArgument(0)!!.accept(this, data)
|
||||
.materializedAt(AsmTypes.JAVA_STRING_TYPE, codegen.context.irBuiltIns.stringType)
|
||||
if (type.isReifiedTypeParameter) {
|
||||
// Note that the inliner expects exactly the following sequence of instructions.
|
||||
// <REIFIED-OPERATIONS-MARKER>
|
||||
// ACONST_NULL
|
||||
// ALOAD n
|
||||
// INVOKESTATIC java/lang/Enum.valueOf...
|
||||
val temporary = frameMap.enterTemp(result.type)
|
||||
mv.store(temporary, result.type)
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.ENUM_REIFIED)
|
||||
mv.aconst(null)
|
||||
mv.load(temporary, result.type)
|
||||
val descriptor = Type.getMethodDescriptor(AsmTypes.ENUM_TYPE, AsmTypes.JAVA_CLASS_TYPE, AsmTypes.JAVA_STRING_TYPE)
|
||||
mv.invokestatic("java/lang/Enum", "valueOf", descriptor, false)
|
||||
frameMap.leaveTemp(result.type)
|
||||
MaterialValue(codegen, AsmTypes.ENUM_TYPE, expression.type)
|
||||
} else {
|
||||
val returnType = typeMapper.mapType(type)
|
||||
val descriptor = Type.getMethodDescriptor(returnType, AsmTypes.JAVA_STRING_TYPE)
|
||||
mv.invokestatic(returnType.internalName, "valueOf", descriptor, false)
|
||||
MaterialValue(codegen, returnType, expression.type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object EnumValues : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) {
|
||||
val type = expression.getTypeArgument(0)!!
|
||||
if (type.isReifiedTypeParameter) {
|
||||
// Note that the inliner expects exactly the following sequence of instructions.
|
||||
// <REIFIED-OPERATIONS-MARKER>
|
||||
// ICONST_0
|
||||
// ANEWARRAY Ljava/lang/Enum;
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.ENUM_REIFIED)
|
||||
mv.iconst(0)
|
||||
mv.newarray(AsmTypes.ENUM_TYPE)
|
||||
MaterialValue(codegen, ENUM_ARRAY_TYPE, expression.type)
|
||||
} else {
|
||||
val enumType = typeMapper.mapType(type)
|
||||
val enumArrayType = AsmUtil.getArrayType(enumType)
|
||||
val descriptor = Type.getMethodDescriptor(enumArrayType)
|
||||
mv.invokestatic(enumType.internalName, "values", descriptor, false)
|
||||
MaterialValue(codegen, enumArrayType, expression.type)
|
||||
}
|
||||
}
|
||||
|
||||
private val ENUM_ARRAY_TYPE = AsmUtil.getArrayType(AsmTypes.ENUM_TYPE)
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isSmartcastFromHigherThanNullable
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.receiverAndArgs
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
|
||||
import org.jetbrains.kotlin.codegen.DescriptorAsmUtil.genAreEqualCall
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.isNullable
|
||||
import org.jetbrains.kotlin.ir.util.isEnumClass
|
||||
import org.jetbrains.kotlin.ir.util.isEnumEntry
|
||||
import org.jetbrains.kotlin.ir.util.isIntegerConst
|
||||
import org.jetbrains.kotlin.ir.util.isNullConst
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.types.isNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType
|
||||
import org.jetbrains.kotlin.types.typeUtil.upperBoundedByPrimitiveNumberOrNullableType
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class ExplicitEquals : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val (a, b) = expression.receiverAndArgs()
|
||||
|
||||
// TODO use specialized boxed type - this might require types like 'java.lang.Integer' in IR
|
||||
a.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
|
||||
b.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
|
||||
codegen.mv.visitMethodInsn(
|
||||
Opcodes.INVOKEVIRTUAL,
|
||||
AsmTypes.OBJECT_TYPE.internalName,
|
||||
"equals",
|
||||
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, AsmTypes.OBJECT_TYPE),
|
||||
false
|
||||
)
|
||||
|
||||
return MaterialValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType)
|
||||
}
|
||||
}
|
||||
|
||||
class Equals(val operator: IElementType) : IntrinsicMethod() {
|
||||
|
||||
private class BooleanNullCheck(val value: PromisedValue) : BooleanValue(value.codegen) {
|
||||
override fun jumpIfFalse(target: Label) = value.materialize().also { mv.ifnonnull(target) }
|
||||
override fun jumpIfTrue(target: Label) = value.materialize().also { mv.ifnull(target) }
|
||||
override fun discard() {
|
||||
value.discard()
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val (a, b) = expression.receiverAndArgs()
|
||||
if (a.isNullConst() || b.isNullConst()) {
|
||||
val irValue = if (a.isNullConst()) b else a
|
||||
val value = irValue.accept(codegen, data)
|
||||
return if (!isPrimitive(value.type) && (irValue.type.classOrNull?.owner?.isInline != true || irValue.type.isNullable()))
|
||||
BooleanNullCheck(value)
|
||||
else {
|
||||
value.discard()
|
||||
BooleanConstant(codegen, false)
|
||||
}
|
||||
}
|
||||
|
||||
val leftType = with(codegen) { a.asmType }
|
||||
val rightType = with(codegen) { b.asmType }
|
||||
val opToken = expression.origin
|
||||
|
||||
// Avoid boxing for `primitive == object` and `boxed primitive == primitive` where we know
|
||||
// what comparison means. The optimization does not apply to `object == primitive` as equals
|
||||
// could be overridden for the object.
|
||||
if ((opToken == IrStatementOrigin.EQEQ || opToken == IrStatementOrigin.EXCLEQ) &&
|
||||
((AsmUtil.isIntOrLongPrimitive(leftType) && !isPrimitive(rightType)) ||
|
||||
(AsmUtil.isIntOrLongPrimitive(rightType) && AsmUtil.isBoxedPrimitiveType(leftType)))
|
||||
) {
|
||||
val aValue = a.accept(codegen, data).materializedAt(leftType, a.type)
|
||||
val bValue = b.accept(codegen, data).materializedAt(rightType, b.type)
|
||||
return PrimitiveToObjectComparison(operator, AsmUtil.isIntOrLongPrimitive(leftType), aValue, bValue)
|
||||
}
|
||||
|
||||
val aIsEnum = a.type.classOrNull?.owner?.run { isEnumClass || isEnumEntry } == true
|
||||
val bIsEnum = b.type.classOrNull?.owner?.run { isEnumClass || isEnumEntry } == true
|
||||
val useEquals = opToken !== IrStatementOrigin.EQEQEQ && opToken !== IrStatementOrigin.EXCLEQEQ &&
|
||||
// `==` is `equals` for objects and floating-point numbers. In the latter case, the difference
|
||||
// is that `equals` is a total order (-0 < +0 and NaN == NaN) and `===` is IEEE754-compliant.
|
||||
(!isPrimitive(leftType) || leftType != rightType || leftType == Type.FLOAT_TYPE || leftType == Type.DOUBLE_TYPE)
|
||||
// Reference equality can be used for enums.
|
||||
&& !aIsEnum && !bIsEnum
|
||||
return if (useEquals) {
|
||||
a.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
|
||||
b.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
|
||||
genAreEqualCall(codegen.mv)
|
||||
MaterialValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType)
|
||||
} else {
|
||||
val operandType = if (!isPrimitive(leftType)) AsmTypes.OBJECT_TYPE else leftType
|
||||
if (operandType == Type.INT_TYPE && (a.isIntegerConst(0) || b.isIntegerConst(0))) {
|
||||
val nonZero = if (a.isIntegerConst(0)) b else a
|
||||
IntegerZeroComparison(operator, nonZero.accept(codegen, data).materializedAt(operandType, nonZero.type))
|
||||
} else {
|
||||
val aValue = a.accept(codegen, data).materializedAt(operandType, a.type)
|
||||
val bValue = b.accept(codegen, data).materializedAt(operandType, b.type)
|
||||
BooleanComparison(operator, aValue, bValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Ieee754Equals(val operandType: Type) : IntrinsicMethod() {
|
||||
private val boxedOperandType = AsmUtil.boxType(operandType)
|
||||
|
||||
override fun toCallable(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction {
|
||||
class Ieee754AreEqual(
|
||||
val left: Type,
|
||||
val right: Type
|
||||
) : IrIntrinsicFunction(expression, signature, context, listOf(left, right)) {
|
||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
||||
v.invokestatic(
|
||||
IntrinsicMethods.INTRINSICS_CLASS_NAME, "areEqual",
|
||||
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, left, right),
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val arg0 = expression.getValueArgument(0)!!
|
||||
val arg1 = expression.getValueArgument(1)!!
|
||||
|
||||
val arg0Type = arg0.type.toIrBasedKotlinType()
|
||||
if (!arg0Type.isPrimitiveNumberOrNullableType() && !arg0Type.upperBoundedByPrimitiveNumberOrNullableType())
|
||||
throw AssertionError("Should be primitive or nullable primitive type: $arg0Type")
|
||||
|
||||
val arg1Type = arg1.type.toIrBasedKotlinType()
|
||||
if (!arg1Type.isPrimitiveNumberOrNullableType() && !arg1Type.upperBoundedByPrimitiveNumberOrNullableType())
|
||||
throw AssertionError("Should be primitive or nullable primitive type: $arg1Type")
|
||||
|
||||
val arg0isNullable = arg0Type.isNullable()
|
||||
val arg1isNullable = arg1Type.isNullable()
|
||||
|
||||
val useNonIEEE754Comparison =
|
||||
!context.state.languageVersionSettings.supportsFeature(LanguageFeature.ProperIeee754Comparisons)
|
||||
&& (arg0.isSmartcastFromHigherThanNullable(context) || arg1.isSmartcastFromHigherThanNullable(context))
|
||||
|
||||
return when {
|
||||
useNonIEEE754Comparison ->
|
||||
Ieee754AreEqual(AsmTypes.OBJECT_TYPE, AsmTypes.OBJECT_TYPE)
|
||||
|
||||
!arg0isNullable && !arg1isNullable ->
|
||||
object : IrIntrinsicFunction(expression, signature, context, listOf(operandType, operandType)) {
|
||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
||||
StackValue.cmp(KtTokens.EQEQ, operandType, StackValue.onStack(operandType), StackValue.onStack(operandType))
|
||||
.put(Type.BOOLEAN_TYPE, v)
|
||||
}
|
||||
}
|
||||
|
||||
arg0isNullable && !arg1isNullable ->
|
||||
Ieee754AreEqual(boxedOperandType, operandType)
|
||||
|
||||
!arg0isNullable && arg1isNullable ->
|
||||
Ieee754AreEqual(operandType, boxedOperandType)
|
||||
|
||||
else ->
|
||||
Ieee754AreEqual(boxedOperandType, boxedOperandType)
|
||||
}
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.materialize
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.mapTypeAsDeclaration
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
|
||||
import org.jetbrains.kotlin.codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter
|
||||
import org.jetbrains.kotlin.ir.expressions.IrClassReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetClass
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object GetJavaObjectType : IntrinsicMethod() {
|
||||
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? =
|
||||
when (val receiver = expression.extensionReceiver) {
|
||||
is IrClassReference -> {
|
||||
val symbol = receiver.symbol
|
||||
if (symbol is IrTypeParameterSymbol) {
|
||||
val success = codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(
|
||||
receiver.classType,
|
||||
ReifiedTypeInliner.OperationKind.JAVA_CLASS
|
||||
)
|
||||
assert(success) {
|
||||
"Non-reified type parameter under ::class should be rejected by type checker: ${receiver.render()}"
|
||||
}
|
||||
}
|
||||
codegen.mv.aconst(AsmUtil.boxType(codegen.typeMapper.mapTypeAsDeclaration(receiver.classType)))
|
||||
|
||||
with(codegen) { expression.onStack }
|
||||
}
|
||||
|
||||
is IrGetClass -> {
|
||||
val argumentValue = receiver.argument.accept(codegen, data)
|
||||
argumentValue.materialize()
|
||||
val argumentType = argumentValue.type
|
||||
when {
|
||||
argumentType == Type.VOID_TYPE ->
|
||||
codegen.mv.aconst(AsmTypes.UNIT_TYPE)
|
||||
|
||||
AsmUtil.isPrimitive(argumentType) ||
|
||||
AsmUtil.unboxPrimitiveTypeOrNull(argumentType) != null -> {
|
||||
AsmUtil.pop(codegen.mv, argumentType)
|
||||
codegen.mv.aconst(AsmUtil.boxType(argumentType))
|
||||
}
|
||||
|
||||
else ->
|
||||
codegen.mv.invokevirtual("java/lang/Object", "getClass", "()Ljava/lang/Class;", false)
|
||||
}
|
||||
|
||||
with(codegen) { expression.onStack }
|
||||
}
|
||||
|
||||
else ->
|
||||
null
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.materialize
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.mapTypeAsDeclaration
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.ir.expressions.IrClassReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetClass
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.isNullablePrimitiveType
|
||||
import org.jetbrains.kotlin.ir.types.isPrimitiveType
|
||||
import org.jetbrains.kotlin.ir.util.isTypeParameter
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object GetJavaPrimitiveType : IntrinsicMethod() {
|
||||
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val receiver = expression.extensionReceiver ?: return null
|
||||
|
||||
val argumentType =
|
||||
when (receiver) {
|
||||
is IrGetClass -> receiver.argument.type
|
||||
is IrClassReference -> receiver.classType
|
||||
else -> return null
|
||||
}
|
||||
|
||||
if (argumentType.isTypeParameter()) return null
|
||||
|
||||
val argumentAsmType = codegen.typeMapper.mapTypeAsDeclaration(argumentType)
|
||||
|
||||
val isPrimitiveTypeOrWrapper =
|
||||
argumentType.isPrimitiveType() ||
|
||||
argumentType.isNullablePrimitiveType() ||
|
||||
!argumentType.isInlineClassType() && argumentAsmType.isVoidOrPrimitiveWrapper()
|
||||
|
||||
return when (receiver) {
|
||||
is IrGetClass -> {
|
||||
if (!isPrimitiveTypeOrWrapper) return null
|
||||
|
||||
val argumentValue = receiver.argument.accept(codegen, data)
|
||||
argumentValue.materialize()
|
||||
AsmUtil.pop(codegen.mv, argumentValue.type)
|
||||
putPrimitiveType(codegen, argumentAsmType)
|
||||
|
||||
with(codegen) { expression.onStack }
|
||||
}
|
||||
|
||||
is IrClassReference -> {
|
||||
if (!isPrimitiveTypeOrWrapper) {
|
||||
codegen.mv.aconst(null)
|
||||
} else {
|
||||
putPrimitiveType(codegen, argumentAsmType)
|
||||
}
|
||||
|
||||
with(codegen) { expression.onStack }
|
||||
}
|
||||
|
||||
else ->
|
||||
throw AssertionError("IrGetClass or IrClassReference expected: ${receiver.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun putPrimitiveType(codegen: ExpressionCodegen, type: Type) {
|
||||
codegen.mv.getstatic(AsmUtil.boxType(type).internalName, "TYPE", "Ljava/lang/Class;")
|
||||
}
|
||||
|
||||
private fun IrType.isInlineClassType(): Boolean {
|
||||
return (classOrNull ?: return false).owner.isInline
|
||||
}
|
||||
|
||||
private fun Type.isVoidOrPrimitiveWrapper(): Boolean =
|
||||
this == AsmTypes.VOID_WRAPPER_TYPE || AsmUtil.unboxPrimitiveTypeOrNull(this) != null
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.DescriptorAsmUtil
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object HashCode : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) {
|
||||
val receiver = expression.dispatchReceiver ?: error("No receiver for hashCode: ${expression.render()}")
|
||||
val receiverIrType = receiver.type
|
||||
val receiverJvmType = typeMapper.mapType(receiverIrType)
|
||||
val receiverValue = receiver.accept(this, data).materialized()
|
||||
val receiverType = receiverValue.type
|
||||
val target = context.state.target
|
||||
when {
|
||||
irFunction.origin == JvmLoweredDeclarationOrigin.INLINE_CLASS_GENERATED_IMPL_METHOD ||
|
||||
irFunction.origin == IrDeclarationOrigin.GENERATED_DATA_CLASS_MEMBER -> {
|
||||
// TODO generate or lower IR for data class / inline class 'hashCode'?
|
||||
DescriptorAsmUtil.genHashCode(mv, mv, receiverType, target)
|
||||
}
|
||||
target >= JvmTarget.JVM_1_8 && AsmUtil.isPrimitive(receiverJvmType) -> {
|
||||
val boxedType = AsmUtil.boxPrimitiveType(receiverJvmType)
|
||||
?: throw AssertionError("Primitive type expected: $receiverJvmType")
|
||||
receiverValue.materializeAt(receiverJvmType, receiverIrType)
|
||||
mv.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
boxedType.internalName,
|
||||
"hashCode",
|
||||
Type.getMethodDescriptor(Type.INT_TYPE, receiverJvmType),
|
||||
false
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
receiverValue.materializeAtBoxed(receiverIrType)
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I", false)
|
||||
}
|
||||
}
|
||||
MaterialValue(codegen, Type.INT_TYPE, codegen.context.irBuiltIns.intType)
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.codegen.DescriptorAsmUtil.genIncrement
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
|
||||
class Increment(private val myDelta: Int) : IntrinsicMethod() {
|
||||
override fun toCallable(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
|
||||
return IrIntrinsicFunction.create(expression, signature, context) {
|
||||
genIncrement(signature.returnType, myDelta, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.IntrinsicMarker
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.MaterialValue
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
|
||||
abstract class IntrinsicMethod : IntrinsicMarker {
|
||||
open fun toCallable(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction = TODO("implement toCallable() or invoke() of $this")
|
||||
|
||||
open fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? =
|
||||
with(codegen) {
|
||||
val descriptor = methodSignatureMapper.mapSignatureSkipGeneric(expression.symbol.owner)
|
||||
val stackValue = toCallable(expression, descriptor, context).invoke(mv, codegen, data, expression)
|
||||
stackValue.put(mv)
|
||||
return MaterialValue(this, stackValue.type, expression.type)
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
fun calcReceiverType(call: IrMemberAccessExpression<*>, context: JvmBackendContext): Type {
|
||||
return context.typeMapper.mapType(call.dispatchReceiver?.type ?: call.extensionReceiver!!.type)
|
||||
}
|
||||
|
||||
fun expressionType(expression: IrExpression, context: JvmBackendContext): Type {
|
||||
return context.typeMapper.mapType(expression.type)
|
||||
}
|
||||
|
||||
fun JvmMethodSignature.newReturnType(type: Type): JvmMethodSignature {
|
||||
val newMethod = with(asmMethod) {
|
||||
Method(name, type, argumentTypes)
|
||||
}
|
||||
return JvmMethodSignature(newMethod, valueParameters)
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
|
||||
object IntrinsicShouldHaveBeenLowered : IntrinsicMethod() {
|
||||
override fun toCallable(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction {
|
||||
error("Intrinsic should have been lowered: '${expression.symbol.owner.render()}'")
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.numberFunctionOperandType
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object Inv : IntrinsicMethod() {
|
||||
/*TODO new this type*/
|
||||
override fun toCallable(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
|
||||
val returnType = signature.returnType
|
||||
val type = numberFunctionOperandType(returnType)
|
||||
return IrIntrinsicFunction.create(expression, signature, context, type) {
|
||||
if (returnType == Type.LONG_TYPE) {
|
||||
it.lconst(-1)
|
||||
}
|
||||
else {
|
||||
it.iconst(-1)
|
||||
}
|
||||
it.xor(returnType)
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object IrCheckNotNull : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val arg0 = expression.getValueArgument(0)!!.accept(codegen, data)
|
||||
if (AsmUtil.isPrimitive(arg0.type)) return arg0
|
||||
return object : PromisedValue(codegen, arg0.type, arg0.irType) {
|
||||
override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) =
|
||||
arg0.materialized().also { codegen.checkTopValueForNull() }.materializeAt(target, irTarget, castForReified)
|
||||
|
||||
override fun discard() =
|
||||
arg0.materialized().also { codegen.checkTopValueForNull() }.discard()
|
||||
}
|
||||
}
|
||||
|
||||
private fun ExpressionCodegen.checkTopValueForNull() {
|
||||
mv.dup()
|
||||
if (state.unifiedNullChecks) {
|
||||
mv.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "checkNotNull", "(Ljava/lang/Object;)V", false)
|
||||
} else {
|
||||
val ifNonNullLabel = Label()
|
||||
mv.ifnonnull(ifNonNullLabel)
|
||||
mv.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "throwNpe", "()V", false)
|
||||
mv.mark(ifNonNullLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.util.isPrimitiveArray
|
||||
|
||||
object IrDataClassArrayMemberHashCode : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? =
|
||||
with(codegen) {
|
||||
val arrayType = expression.getValueArgument(0)!!.type
|
||||
val asmArrayType = context.typeMapper.mapType(arrayType)
|
||||
gen(expression.getValueArgument(0)!!, asmArrayType, arrayType, data)
|
||||
val hashCodeArgumentDescriptor = if (arrayType.isPrimitiveArray()) asmArrayType.descriptor else "[Ljava/lang/Object;"
|
||||
mv.invokestatic("java/util/Arrays", "hashCode", "($hashCodeArgumentDescriptor)I", false)
|
||||
return expression.onStack
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.util.isPrimitiveArray
|
||||
|
||||
object IrDataClassArrayMemberToString : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? =
|
||||
with(codegen) {
|
||||
val arrayType = expression.getValueArgument(0)!!.type
|
||||
val asmArrayType = context.typeMapper.mapType(arrayType)
|
||||
gen(expression.getValueArgument(0)!!, asmArrayType, arrayType, data)
|
||||
val toStringArgumentDescriptor = if (arrayType.isPrimitiveArray()) asmArrayType.descriptor else "[Ljava/lang/Object;"
|
||||
mv.invokestatic("java/util/Arrays", "toString", "($toStringArgumentDescriptor)Ljava/lang/String;", false)
|
||||
return expression.onStack
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_STRING_TYPE
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
object IrIllegalArgumentException : IntrinsicMethod() {
|
||||
val exceptionTypeDescriptor = Type.getType(IllegalArgumentException::class.java)!!
|
||||
|
||||
override fun toCallable(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction {
|
||||
return object : IrIntrinsicFunction(expression, signature, context, listOf(JAVA_STRING_TYPE)) {
|
||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
||||
v.invokespecial(
|
||||
exceptionTypeDescriptor.internalName,
|
||||
"<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, JAVA_STRING_TYPE),
|
||||
false
|
||||
)
|
||||
v.athrow()
|
||||
}
|
||||
|
||||
override fun invoke(
|
||||
v: InstructionAdapter,
|
||||
codegen: ExpressionCodegen,
|
||||
data: BlockInfo,
|
||||
expression: IrFunctionAccessExpression
|
||||
): StackValue {
|
||||
codegen.markLineNumber(expression)
|
||||
v.anew(exceptionTypeDescriptor)
|
||||
v.dup()
|
||||
return super.invoke(v, codegen, data, expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.mapping.mapClass
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.Callable
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.util.isVararg
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.ir.util.substitute
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
open class IrIntrinsicFunction(
|
||||
val expression: IrFunctionAccessExpression,
|
||||
val signature: JvmMethodSignature,
|
||||
val context: JvmBackendContext,
|
||||
val argsTypes: List<Type> = expression.argTypes(context)
|
||||
) : Callable {
|
||||
override val owner: Type
|
||||
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
|
||||
override val dispatchReceiverType: Type?
|
||||
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
|
||||
override val dispatchReceiverKotlinType: KotlinType?
|
||||
get() = null
|
||||
override val extensionReceiverType: Type?
|
||||
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
|
||||
override val extensionReceiverKotlinType: KotlinType?
|
||||
get() = null
|
||||
override val generateCalleeType: Type?
|
||||
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
|
||||
override val valueParameterTypes: List<Type>
|
||||
get() = signature.valueParameters.map { it.asmType }
|
||||
override val parameterTypes: Array<Type>
|
||||
get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
|
||||
override val returnType: Type
|
||||
get() = signature.returnType
|
||||
override val returnKotlinType: KotlinType?
|
||||
get() = null
|
||||
|
||||
override fun isStaticCall(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
||||
TODO("not implemented for $this")
|
||||
}
|
||||
|
||||
open fun genInvokeInstructionWithResult(v: InstructionAdapter): Type {
|
||||
genInvokeInstruction(v)
|
||||
return returnType
|
||||
}
|
||||
|
||||
open fun invoke(
|
||||
v: InstructionAdapter,
|
||||
codegen: ExpressionCodegen,
|
||||
data: BlockInfo,
|
||||
expression: IrFunctionAccessExpression
|
||||
): StackValue {
|
||||
loadArguments(codegen, data)
|
||||
codegen.markLineNumber(expression)
|
||||
return StackValue.onStack(genInvokeInstructionWithResult(v))
|
||||
}
|
||||
|
||||
private fun loadArguments(codegen: ExpressionCodegen, data: BlockInfo) {
|
||||
var offset = 0
|
||||
expression.dispatchReceiver?.let { genArg(it, codegen, offset++, data) }
|
||||
expression.extensionReceiver?.let { genArg(it, codegen, offset++, data) }
|
||||
for ((i, valueParameter) in expression.symbol.owner.valueParameters.withIndex()) {
|
||||
val argument = expression.getValueArgument(i)
|
||||
when {
|
||||
argument != null ->
|
||||
genArg(argument, codegen, i + offset, data)
|
||||
valueParameter.isVararg -> {
|
||||
// TODO: is there an easier way to get the substituted type of an empty vararg argument?
|
||||
val arrayType = codegen.typeMapper.mapType(
|
||||
valueParameter.type.substitute(expression.symbol.owner.typeParameters, expression.typeArguments)
|
||||
)
|
||||
StackValue.operation(arrayType) {
|
||||
it.aconst(0)
|
||||
it.newarray(AsmUtil.correctElementType(arrayType))
|
||||
}.put(arrayType, codegen.mv)
|
||||
}
|
||||
else -> error("Unknown parameter ${valueParameter.name} in: ${expression.dump()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun genArg(expression: IrExpression, codegen: ExpressionCodegen, index: Int, data: BlockInfo) {
|
||||
codegen.gen(expression, argsTypes[index], expression.type, data)
|
||||
}
|
||||
|
||||
private val IrFunctionAccessExpression.typeArguments: List<IrType>
|
||||
get() = (0 until typeArgumentsCount).map { getTypeArgument(it)!! }
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext,
|
||||
argsTypes: List<Type> = expression.argTypes(context),
|
||||
invokeInstruction: IrIntrinsicFunction.(InstructionAdapter) -> Unit
|
||||
): IrIntrinsicFunction {
|
||||
return object : IrIntrinsicFunction(expression, signature, context, argsTypes) {
|
||||
|
||||
override fun genInvokeInstruction(v: InstructionAdapter) = invokeInstruction(v)
|
||||
}
|
||||
}
|
||||
|
||||
fun createWithResult(
|
||||
expression: IrFunctionAccessExpression, signature: JvmMethodSignature,
|
||||
context: JvmBackendContext,
|
||||
argsTypes: List<Type> = expression.argTypes(context),
|
||||
invokeInstruction: IrIntrinsicFunction.(InstructionAdapter) -> Type
|
||||
): IrIntrinsicFunction {
|
||||
return object : IrIntrinsicFunction(expression, signature, context, argsTypes) {
|
||||
|
||||
override fun genInvokeInstructionWithResult(v: InstructionAdapter) = invokeInstruction(v)
|
||||
}
|
||||
}
|
||||
|
||||
fun create(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext,
|
||||
type: Type,
|
||||
invokeInstruction: IrIntrinsicFunction.(InstructionAdapter) -> Unit
|
||||
): IrIntrinsicFunction {
|
||||
return create(expression, signature, context, listOf(type), invokeInstruction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun IrFunctionAccessExpression.argTypes(context: JvmBackendContext): ArrayList<Type> {
|
||||
val callee = symbol.owner
|
||||
val signature = context.methodSignatureMapper.mapSignatureSkipGeneric(callee)
|
||||
return arrayListOf<Type>().apply {
|
||||
if (dispatchReceiver != null) {
|
||||
add(context.typeMapper.mapClass(callee.parentAsClass))
|
||||
}
|
||||
addAll(signature.asmMethod.argumentTypes)
|
||||
}
|
||||
}
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmSymbols
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.isFileClass
|
||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeAsciiOnly
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
class IrIntrinsicMethods(val irBuiltIns: IrBuiltIns, val symbols: JvmSymbols) {
|
||||
private val kotlinFqn = StandardNames.BUILT_INS_PACKAGE_FQ_NAME
|
||||
private val kotlinJvmFqn = FqName("kotlin.jvm")
|
||||
private val kotlinJvmInternalUnsafeFqn = FqName("kotlin.jvm.internal.unsafe")
|
||||
private val kotlinReflectFqn = StandardNames.KOTLIN_REFLECT_FQ_NAME
|
||||
|
||||
private val anyFqn = StandardNames.FqNames.any.toSafe()
|
||||
private val arrayFqn = StandardNames.FqNames.array.toSafe()
|
||||
private val cloneableFqn = StandardNames.FqNames.cloneable.toSafe()
|
||||
private val intFqn = StandardNames.FqNames._int.toSafe()
|
||||
private val kClassFqn = StandardNames.FqNames.kClass.toSafe()
|
||||
private val stringFqn = StandardNames.FqNames.string.toSafe()
|
||||
|
||||
private val intrinsicsMap = (
|
||||
listOf(
|
||||
Key(kotlinJvmFqn, FqName("T"), "<get-javaClass>", emptyList()) to JavaClassProperty,
|
||||
Key(kotlinJvmFqn, kClassFqn, "<get-javaObjectType>", emptyList()) to GetJavaObjectType,
|
||||
Key(kotlinJvmFqn, kClassFqn, "<get-javaPrimitiveType>", emptyList()) to GetJavaPrimitiveType,
|
||||
Key(kotlinJvmFqn, kClassFqn, "<get-java>", emptyList()) to KClassJavaProperty,
|
||||
Key(kotlinJvmInternalUnsafeFqn, null, "monitorEnter", listOf(anyFqn)) to MonitorInstruction.MONITOR_ENTER,
|
||||
Key(kotlinJvmInternalUnsafeFqn, null, "monitorExit", listOf(anyFqn)) to MonitorInstruction.MONITOR_EXIT,
|
||||
Key(kotlinJvmFqn, arrayFqn, "isArrayOf", emptyList()) to IsArrayOf,
|
||||
Key(kotlinFqn, null, "arrayOfNulls", listOf(intFqn)) to NewArray,
|
||||
Key(cloneableFqn, null, "clone", emptyList()) to Clone,
|
||||
Key(kotlinFqn, null, "enumValues", listOf()) to EnumValues,
|
||||
Key(kotlinFqn, null, "enumValueOf", listOf(stringFqn)) to EnumValueOf,
|
||||
Key(kotlinFqn, stringFqn, "plus", listOf(anyFqn)) to StringPlus,
|
||||
Key(kotlinReflectFqn, null, "typeOf", listOf()) to TypeOf,
|
||||
irBuiltIns.eqeqSymbol.toKey()!! to Equals(KtTokens.EQEQ),
|
||||
irBuiltIns.eqeqeqSymbol.toKey()!! to Equals(KtTokens.EQEQEQ),
|
||||
irBuiltIns.ieee754equalsFunByOperandType[irBuiltIns.floatClass]!!.toKey()!! to Ieee754Equals(Type.FLOAT_TYPE),
|
||||
irBuiltIns.ieee754equalsFunByOperandType[irBuiltIns.doubleClass]!!.toKey()!! to Ieee754Equals(Type.DOUBLE_TYPE),
|
||||
irBuiltIns.booleanNotSymbol.toKey()!! to Not,
|
||||
irBuiltIns.noWhenBranchMatchedExceptionSymbol.toKey()!! to IrNoWhenBranchMatchedException,
|
||||
irBuiltIns.illegalArgumentExceptionSymbol.toKey()!! to IrIllegalArgumentException,
|
||||
irBuiltIns.checkNotNullSymbol.toKey()!! to IrCheckNotNull,
|
||||
irBuiltIns.andandSymbol.toKey()!! to AndAnd,
|
||||
irBuiltIns.ororSymbol.toKey()!! to OrOr,
|
||||
irBuiltIns.dataClassArrayMemberHashCodeSymbol.toKey()!! to IrDataClassArrayMemberHashCode,
|
||||
irBuiltIns.dataClassArrayMemberToStringSymbol.toKey()!! to IrDataClassArrayMemberToString,
|
||||
symbols.unsafeCoerceIntrinsic.toKey()!! to UnsafeCoerce,
|
||||
symbols.signatureStringIntrinsic.toKey()!! to SignatureString,
|
||||
symbols.throwNullPointerException.toKey()!! to ThrowException(Type.getObjectType("java/lang/NullPointerException")),
|
||||
symbols.throwTypeCastException.toKey()!! to ThrowException(Type.getObjectType("kotlin/TypeCastException")),
|
||||
symbols.throwUnsupportedOperationException.toKey()!! to ThrowException(Type.getObjectType("java/lang/UnsupportedOperationException")),
|
||||
symbols.throwIllegalAccessException.toKey()!! to ThrowException(Type.getObjectType("java/lang/IllegalAccessException")),
|
||||
symbols.throwKotlinNothingValueException.toKey()!! to ThrowKotlinNothingValueException,
|
||||
symbols.jvmIndyIntrinsic.toKey()!! to JvmInvokeDynamic,
|
||||
symbols.jvmDebuggerInvokeSpecialIntrinsic.toKey()!! to JvmDebuggerInvokeSpecial,
|
||||
symbols.intPostfixIncr.toKey()!! to PostfixIinc(1),
|
||||
symbols.intPostfixDecr.toKey()!! to PostfixIinc(-1)
|
||||
) +
|
||||
numberConversionMethods() +
|
||||
unaryFunForPrimitives("plus", UnaryPlus) +
|
||||
unaryFunForPrimitives("unaryPlus", UnaryPlus) +
|
||||
unaryFunForPrimitives("minus", UnaryMinus) +
|
||||
unaryFunForPrimitives("unaryMinus", UnaryMinus) +
|
||||
unaryFunForPrimitives("inv", Inv) +
|
||||
unaryFunForPrimitives("inc", INC) +
|
||||
unaryFunForPrimitives("dec", DEC) +
|
||||
unaryFunForPrimitives("hashCode", HashCode) +
|
||||
binaryFunForPrimitives("equals", EXPLICIT_EQUALS, irBuiltIns.anyClass) +
|
||||
binaryFunForPrimitivesAcrossPrimitives("rangeTo", RangeTo) +
|
||||
binaryOp("plus", IADD) +
|
||||
binaryOp("minus", ISUB) +
|
||||
binaryOp("times", IMUL) +
|
||||
binaryOp("div", IDIV) +
|
||||
binaryOp("mod", IREM) +
|
||||
binaryOp("rem", IREM) +
|
||||
binaryOp("shl", ISHL) +
|
||||
binaryOp("shr", ISHR) +
|
||||
binaryOp("ushr", IUSHR) +
|
||||
binaryOp("and", IAND) +
|
||||
binaryOp("or", IOR) +
|
||||
binaryOp("xor", IXOR) +
|
||||
binaryFunForPrimitivesAcrossPrimitives("compareTo", CompareTo) +
|
||||
createKeyMapping(Not, irBuiltIns.booleanClass, "not") +
|
||||
createKeyMapping(StringGetChar, irBuiltIns.stringClass, "get", irBuiltIns.intClass) +
|
||||
symbols.primitiveIteratorsByType.values.map { iteratorClass ->
|
||||
createKeyMapping(IteratorNext, iteratorClass, "next")
|
||||
} +
|
||||
arrayMethods() +
|
||||
primitiveComparisonIntrinsics(irBuiltIns.lessFunByOperandType, KtTokens.LT) +
|
||||
primitiveComparisonIntrinsics(irBuiltIns.lessOrEqualFunByOperandType, KtTokens.LTEQ) +
|
||||
primitiveComparisonIntrinsics(irBuiltIns.greaterFunByOperandType, KtTokens.GT) +
|
||||
primitiveComparisonIntrinsics(irBuiltIns.greaterOrEqualFunByOperandType, KtTokens.GTEQ) +
|
||||
|
||||
intrinsicsThatShouldHaveBeenLowered()
|
||||
).toMap()
|
||||
|
||||
private fun intrinsicsThatShouldHaveBeenLowered() =
|
||||
(symbols.primitiveTypesToPrimitiveArrays.map { (_, primitiveClassSymbol) ->
|
||||
val name = primitiveClassSymbol.owner.name.asString()
|
||||
// IntArray -> intArrayOf
|
||||
val arrayOfFunName = name.decapitalizeAsciiOnly() + "Of"
|
||||
Key(kotlinFqn, null, arrayOfFunName, listOf(primitiveClassSymbol.owner.fqNameWhenAvailable))
|
||||
} + listOf(
|
||||
Key(kotlinFqn, anyFqn, "toString", emptyList()),
|
||||
Key(kotlinFqn, null, "arrayOf", listOf(arrayFqn)),
|
||||
Key(stringFqn, null, "plus", listOf(anyFqn)),
|
||||
)).map { it to IntrinsicShouldHaveBeenLowered }
|
||||
|
||||
private val PrimitiveType.symbol
|
||||
get() = irBuiltIns.primitiveTypeToIrType[this]!!.classOrNull!!
|
||||
|
||||
fun getIntrinsic(symbol: IrFunctionSymbol): IntrinsicMethod? = intrinsicsMap[symbol.toKey()]
|
||||
|
||||
private fun unaryFunForPrimitives(name: String, intrinsic: IntrinsicMethod): List<Pair<Key, IntrinsicMethod>> =
|
||||
PrimitiveType.values().map { type ->
|
||||
createKeyMapping(intrinsic, type.symbol, name)
|
||||
}
|
||||
|
||||
private fun binaryFunForPrimitivesAcrossPrimitives(name: String, intrinsic: IntrinsicMethod): List<Pair<Key, IntrinsicMethod>> =
|
||||
PrimitiveType.values().flatMap { parameter ->
|
||||
binaryFunForPrimitives(name, intrinsic, parameter.symbol)
|
||||
}
|
||||
|
||||
|
||||
private fun binaryFunForPrimitives(
|
||||
name: String,
|
||||
intrinsic: IntrinsicMethod,
|
||||
parameter: IrClassifierSymbol
|
||||
): List<Pair<Key, IntrinsicMethod>> =
|
||||
PrimitiveType.values().map { type ->
|
||||
createKeyMapping(intrinsic, type.symbol, name, parameter)
|
||||
}
|
||||
|
||||
private fun binaryOp(methodName: String, opcode: Int) = binaryFunForPrimitivesAcrossPrimitives(methodName, BinaryOp(opcode))
|
||||
|
||||
private fun numberConversionMethods(): List<Pair<Key, IntrinsicMethod>> =
|
||||
PrimitiveType.NUMBER_TYPES.flatMap { type -> numberConversionMethods(type.symbol) } +
|
||||
numberConversionMethods(irBuiltIns.numberClass)
|
||||
|
||||
private fun arrayMethods(): List<Pair<Key, IntrinsicMethod>> =
|
||||
symbols.primitiveArraysToPrimitiveTypes.flatMap { (array, primitiveType) -> arrayMethods(primitiveType.symbol, array) } +
|
||||
arrayMethods(symbols.array.owner.typeParameters.single().symbol, symbols.array)
|
||||
|
||||
private fun arrayMethods(elementClass: IrClassifierSymbol, arrayClass: IrClassSymbol) =
|
||||
listOf(
|
||||
createKeyMapping(ArraySize, arrayClass, "<get-size>"),
|
||||
createKeyMapping(NewArray, arrayClass, "<init>", irBuiltIns.intClass),
|
||||
createKeyMapping(ArraySet, arrayClass, "set", irBuiltIns.intClass, elementClass),
|
||||
createKeyMapping(ArrayGet, arrayClass, "get", irBuiltIns.intClass),
|
||||
createKeyMapping(Clone, arrayClass, "clone"),
|
||||
createKeyMapping(ArrayIterator, arrayClass, "iterator")
|
||||
)
|
||||
|
||||
private fun primitiveComparisonIntrinsics(
|
||||
typeToIrFun: Map<IrClassifierSymbol, IrSimpleFunctionSymbol>,
|
||||
operator: KtSingleValueToken
|
||||
): List<Pair<Key, PrimitiveComparison>> =
|
||||
PrimitiveType.values().mapNotNull { primitiveType ->
|
||||
val irPrimitiveClassifier = irBuiltIns.primitiveTypeToIrType[primitiveType]!!.classifierOrFail
|
||||
val irFunSymbol = typeToIrFun[irPrimitiveClassifier] ?: return@mapNotNull null
|
||||
irFunSymbol.toKey()!! to PrimitiveComparison(primitiveType, operator)
|
||||
}
|
||||
|
||||
data class Key(val owner: FqName, val receiverParameterTypeName: FqName?, val name: String, val valueParameterTypeNames: List<FqName?>)
|
||||
|
||||
companion object {
|
||||
private val INC = Increment(1)
|
||||
|
||||
private val DEC = Increment(-1)
|
||||
private val EXPLICIT_EQUALS = ExplicitEquals()
|
||||
|
||||
private fun IrFunctionSymbol.toKey(): Key? {
|
||||
val parent = owner.parent
|
||||
val ownerFqName = when {
|
||||
parent is IrClass && parent.isFileClass ->
|
||||
(parent.parent as IrPackageFragment).fqName
|
||||
parent is IrClass -> parent.fqNameWhenAvailable ?: return null
|
||||
parent is IrPackageFragment -> parent.fqName
|
||||
else -> return null
|
||||
}
|
||||
return Key(
|
||||
ownerFqName,
|
||||
getParameterFqName(owner.extensionReceiverParameter),
|
||||
owner.name.asString(),
|
||||
owner.valueParameters.map(::getParameterFqName)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getParameterFqName(parameter: IrValueParameter?): FqName? =
|
||||
getParameterFqName(parameter?.type?.classifierOrNull)
|
||||
|
||||
private fun getParameterFqName(parameter: IrClassifierSymbol?): FqName? =
|
||||
parameter?.owner?.let {
|
||||
when (it) {
|
||||
is IrClass -> it.fqNameWhenAvailable
|
||||
is IrTypeParameter -> FqName(it.name.asString())
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun createKeyMapping(
|
||||
intrinsic: IntrinsicMethod,
|
||||
klass: IrClassSymbol,
|
||||
name: String,
|
||||
vararg args: IrClassifierSymbol
|
||||
): Pair<Key, IntrinsicMethod> =
|
||||
Key(klass.owner.fqNameWhenAvailable!!, null, name, args.map { getParameterFqName(it) }) to
|
||||
intrinsic
|
||||
|
||||
private fun numberConversionMethods(numberClass: IrClassSymbol) =
|
||||
OperatorConventions.NUMBER_CONVERSIONS.map { method ->
|
||||
createKeyMapping(NumberCast, numberClass, method.asString())
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.genThrow
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
|
||||
object IrNoWhenBranchMatchedException : IntrinsicMethod() {
|
||||
override fun toCallable(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
|
||||
return IrIntrinsicFunction.create(expression, signature, context) {
|
||||
genThrow(it, "kotlin/NoWhenBranchMatchedException", null)
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.types.typeWith
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
|
||||
object IsArrayOf : IntrinsicMethod() {
|
||||
override fun toCallable(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction = IrIntrinsicFunction.create(expression, signature, context) { v ->
|
||||
val arrayType = context.irBuiltIns.arrayClass.typeWith(expression.getTypeArgument(0)!!)
|
||||
v.instanceOf(context.typeMapper.mapType(arrayType))
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.builtins.StandardNames.COLLECTIONS_PACKAGE_FQ_NAME
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.fileClasses.internalNameWithoutInnerClasses
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object IteratorNext : IntrinsicMethod() {
|
||||
|
||||
override fun toCallable(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
|
||||
// If the array element type is unboxed primitive, do not unbox. Otherwise AsmUtil.unbox throws exception
|
||||
val type = if (AsmUtil.isBoxedPrimitiveType(signature.returnType)) AsmUtil.unboxType(signature.returnType) else signature.returnType
|
||||
val newSignature = signature.newReturnType(type)
|
||||
val primitiveClassName = getKotlinPrimitiveClassName(type)
|
||||
return IrIntrinsicFunction.create(expression, newSignature, context, getPrimitiveIteratorType(primitiveClassName)) {
|
||||
it.invokevirtual(
|
||||
getPrimitiveIteratorType(primitiveClassName).internalName,
|
||||
"next${primitiveClassName.asString()}",
|
||||
"()" + type.descriptor,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Type.CHAR_TYPE -> "Char"
|
||||
private fun getKotlinPrimitiveClassName(type: Type): Name {
|
||||
return JvmPrimitiveType.get(type.className).primitiveType.typeName
|
||||
}
|
||||
|
||||
// "Char" -> type for kotlin.collections.CharIterator
|
||||
fun getPrimitiveIteratorType(primitiveClassName: Name): Type {
|
||||
val iteratorName = Name.identifier(primitiveClassName.asString() + "Iterator")
|
||||
return Type.getObjectType(COLLECTIONS_PACKAGE_FQ_NAME.child(iteratorName).internalNameWithoutInnerClasses)
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.boxType
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object JavaClassProperty : IntrinsicMethod() {
|
||||
private fun invokeGetClass(value: PromisedValue) {
|
||||
value.mv.invokevirtual("java/lang/Object", "getClass", "()Ljava/lang/Class;", false)
|
||||
}
|
||||
|
||||
fun invokeWith(value: PromisedValue) =
|
||||
when {
|
||||
value.type == Type.VOID_TYPE ->
|
||||
invokeGetClass(value.materializedAt(AsmTypes.UNIT_TYPE, value.codegen.context.irBuiltIns.unitType))
|
||||
value.irType.classOrNull?.owner?.isInline == true ->
|
||||
invokeGetClass(value.materializedAtBoxed(value.irType))
|
||||
isPrimitive(value.type) -> {
|
||||
value.discard()
|
||||
value.mv.getstatic(boxType(value.type).internalName, "TYPE", "Ljava/lang/Class;")
|
||||
}
|
||||
else ->
|
||||
invokeGetClass(value.materialized())
|
||||
}
|
||||
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
invokeWith(expression.extensionReceiver!!.accept(codegen, data))
|
||||
return with(codegen) { expression.onStack }
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getBooleanConstArgument
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getStringConstArgument
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
// This intrinsic enables IR lowerings to force the generation of a particular
|
||||
// invokeSpecial instruction in the resulting JVM bytecode.
|
||||
//
|
||||
// The need for this is to coerce the IR codegen backend to generate an
|
||||
// otherwise illegal invokeSpecial for the express purpose of being
|
||||
// _interpreted_ by eval4j in the fragment evaluator and not actually run on
|
||||
// the JVM. This allows the "evaluate expression" functionality of the Kotlin
|
||||
// JVM Debugger Plug-in in IntelliJ to simulate the invocation of `super` calls
|
||||
// in the context of a breakpoint.
|
||||
//
|
||||
// It uses the "trick" of encoding the desired operands as constants passed as
|
||||
// arguments to the intrinsic, opening a direct line from the producing
|
||||
// lowering straight through to JVM codegen without interference from
|
||||
// lowerings in between.
|
||||
object JvmDebuggerInvokeSpecial : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue {
|
||||
val owner = expression.getStringConstArgument(0)
|
||||
val name = expression.getStringConstArgument(1)
|
||||
val descriptor = expression.getStringConstArgument(2)
|
||||
val isInterface = expression.getBooleanConstArgument(3)
|
||||
|
||||
expression.dispatchReceiver!!.accept(codegen, data).materialize()
|
||||
codegen.mv.invokespecial(owner, name, descriptor, isInterface)
|
||||
|
||||
return MaterialValue(codegen, Type.getReturnType(descriptor), expression.type)
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getBooleanConstArgument
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getIntConstArgument
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getStringConstArgument
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.Handle
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object JvmInvokeDynamic : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue {
|
||||
fun fail(message: String): Nothing =
|
||||
throw AssertionError("$message; expression:\n${expression.dump()}")
|
||||
|
||||
val dynamicCall = expression.getValueArgument(0) as? IrCall
|
||||
?: fail("'dynamicCall' is expected to be a call")
|
||||
val dynamicCallee = dynamicCall.symbol.owner
|
||||
if (dynamicCallee.parent != codegen.context.ir.symbols.kotlinJvmInternalInvokeDynamicPackage ||
|
||||
dynamicCallee.origin != JvmLoweredDeclarationOrigin.INVOKEDYNAMIC_CALL_TARGET
|
||||
)
|
||||
fail("Unexpected dynamicCallee: '${dynamicCallee.render()}'")
|
||||
|
||||
val bootstrapMethodHandleArg = expression.getValueArgument(1) as? IrCall
|
||||
?: fail("'bootstrapMethodHandle' should be a call")
|
||||
val bootstrapMethodHandle = evalMethodHandle(bootstrapMethodHandleArg)
|
||||
|
||||
val bootstrapMethodArgs = (expression.getValueArgument(2)?.safeAs<IrVararg>()
|
||||
?: fail("'bootstrapMethodArgs' is expected to be a vararg"))
|
||||
val asmBootstrapMethodArgs = bootstrapMethodArgs.elements
|
||||
.map { generateBootstrapMethodArg(it, codegen) }
|
||||
.toTypedArray()
|
||||
|
||||
val dynamicCalleeMethod = codegen.methodSignatureMapper.mapAsmMethod(dynamicCallee)
|
||||
val dynamicCallGenerator = IrCallGenerator.DefaultCallGenerator
|
||||
val dynamicCalleeArgumentTypes = dynamicCalleeMethod.argumentTypes
|
||||
for (i in dynamicCallee.valueParameters.indices) {
|
||||
val dynamicCalleeParameter = dynamicCallee.valueParameters[i]
|
||||
val dynamicCalleeArgument = dynamicCall.getValueArgument(i)
|
||||
?: fail("No argument #$i in 'dynamicCall'")
|
||||
val dynamicCalleeArgumentType = dynamicCalleeArgumentTypes.getOrElse(i) {
|
||||
fail("No argument type #$i in dynamic callee: $dynamicCalleeMethod")
|
||||
}
|
||||
dynamicCallGenerator.genValueAndPut(dynamicCalleeParameter, dynamicCalleeArgument, dynamicCalleeArgumentType, codegen, data)
|
||||
}
|
||||
|
||||
codegen.mv.invokedynamic(dynamicCalleeMethod.name, dynamicCalleeMethod.descriptor, bootstrapMethodHandle, asmBootstrapMethodArgs)
|
||||
|
||||
return MaterialValue(codegen, dynamicCalleeMethod.returnType, expression.type)
|
||||
}
|
||||
|
||||
private fun generateBootstrapMethodArg(element: IrVarargElement, codegen: ExpressionCodegen): Any =
|
||||
when (element) {
|
||||
is IrRawFunctionReference ->
|
||||
generateMethodHandle(element, codegen)
|
||||
is IrCall ->
|
||||
evalBootstrapArgumentIntrinsicCall(element, codegen)
|
||||
?: throw AssertionError("Unexpected callee in bootstrap method argument:\n${element.dump()}")
|
||||
is IrConst<*> ->
|
||||
when (element.kind) {
|
||||
IrConstKind.Byte -> (element.value as Byte).toInt()
|
||||
IrConstKind.Short -> (element.value as Short).toInt()
|
||||
IrConstKind.Int -> element.value as Int
|
||||
IrConstKind.Long -> element.value as Long
|
||||
IrConstKind.Float -> element.value as Float
|
||||
IrConstKind.Double -> element.value as Double
|
||||
IrConstKind.String -> element.value as String
|
||||
else ->
|
||||
throw AssertionError("Unexpected constant expression in bootstrap method argument:\n${element.dump()}")
|
||||
}
|
||||
else ->
|
||||
throw AssertionError("Unexpected bootstrap method argument:\n${element.dump()}")
|
||||
}
|
||||
|
||||
private fun evalBootstrapArgumentIntrinsicCall(irCall: IrCall, codegen: ExpressionCodegen): Any? {
|
||||
return when (irCall.symbol) {
|
||||
codegen.context.ir.symbols.jvmOriginalMethodTypeIntrinsic ->
|
||||
evalOriginalMethodType(irCall, codegen)
|
||||
codegen.context.ir.symbols.jvmMethodType ->
|
||||
evalMethodType(irCall)
|
||||
codegen.context.ir.symbols.jvmMethodHandle ->
|
||||
evalMethodHandle(irCall)
|
||||
else ->
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun evalMethodType(irCall: IrCall): Type {
|
||||
val descriptor = irCall.getStringConstArgument(0)
|
||||
return Type.getMethodType(descriptor)
|
||||
}
|
||||
|
||||
private fun evalMethodHandle(irCall: IrCall): Handle {
|
||||
val tag = irCall.getIntConstArgument(0)
|
||||
val owner = irCall.getStringConstArgument(1)
|
||||
val name = irCall.getStringConstArgument(2)
|
||||
val descriptor = irCall.getStringConstArgument(3)
|
||||
val isInterface = irCall.getBooleanConstArgument(4)
|
||||
return Handle(tag, owner, name, descriptor, isInterface)
|
||||
}
|
||||
|
||||
private fun generateMethodHandle(irRawFunctionReference: IrRawFunctionReference, codegen: ExpressionCodegen): Handle =
|
||||
codegen.context.methodSignatureMapper.mapToMethodHandle(irRawFunctionReference.symbol.owner)
|
||||
|
||||
private fun evalOriginalMethodType(irCall: IrCall, codegen: ExpressionCodegen): Type {
|
||||
val irRawFunRef = irCall.getValueArgument(0) as? IrRawFunctionReference
|
||||
?: throw AssertionError(
|
||||
"Argument in ${irCall.symbol.owner.name} call is expected to be a raw function reference:\n" +
|
||||
irCall.dump()
|
||||
)
|
||||
val irFun = irRawFunRef.symbol.owner
|
||||
val asmMethod = codegen.methodSignatureMapper.mapAsmMethod(irFun)
|
||||
return Type.getMethodType(asmMethod.descriptor)
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrClassReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetClass
|
||||
|
||||
object KClassJavaProperty : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val extensionReceiver = expression.extensionReceiver
|
||||
if (extensionReceiver !is IrClassReference && extensionReceiver !is IrGetClass)
|
||||
return null
|
||||
return codegen.generateClassLiteralReference(extensionReceiver, false, data)
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
|
||||
class MonitorInstruction private constructor(private val opcode: Int) : IntrinsicMethod() {
|
||||
companion object {
|
||||
@JvmField
|
||||
val MONITOR_ENTER: MonitorInstruction = MonitorInstruction(Opcodes.MONITORENTER)
|
||||
|
||||
@JvmField
|
||||
val MONITOR_EXIT: MonitorInstruction = MonitorInstruction(Opcodes.MONITOREXIT)
|
||||
}
|
||||
|
||||
/*TODO void return type*/
|
||||
override fun toCallable(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
|
||||
return IrIntrinsicFunction.create(expression, signature, context, OBJECT_TYPE) {
|
||||
it.visitInsn(opcode)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
|
||||
import org.jetbrains.kotlin.codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.types.getArrayElementType
|
||||
import org.jetbrains.kotlin.ir.types.isArray
|
||||
import org.jetbrains.kotlin.ir.types.isNullableArray
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object NewArray : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue {
|
||||
codegen.gen(expression.getValueArgument(0)!!, Type.INT_TYPE, codegen.context.irBuiltIns.intType, data)
|
||||
return with(codegen) {
|
||||
val elementIrType = expression.type.getArrayElementType(context.irBuiltIns)
|
||||
if (expression.type.isArray() || expression.type.isNullableArray()) {
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(elementIrType, ReifiedTypeInliner.OperationKind.NEW_ARRAY)
|
||||
mv.newarray(typeMapper.boxType(elementIrType))
|
||||
} else {
|
||||
mv.newarray(typeMapper.mapType(elementIrType))
|
||||
}
|
||||
expression.onStack
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BooleanValue
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.coerceToBoolean
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
|
||||
object Not : IntrinsicMethod() {
|
||||
class BooleanNegation(val value: BooleanValue) : BooleanValue(value.codegen) {
|
||||
override fun jumpIfFalse(target: Label) = value.jumpIfTrue(target)
|
||||
override fun jumpIfTrue(target: Label) = value.jumpIfFalse(target)
|
||||
override fun discard() {
|
||||
value.discard()
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) =
|
||||
BooleanNegation(expression.dispatchReceiver!!.accept(codegen, data).coerceToBoolean())
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
|
||||
object NumberCast : IntrinsicMethod() {
|
||||
|
||||
override fun toCallable(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
|
||||
return IrIntrinsicFunction.create(expression, signature, context) {
|
||||
StackValue.coerce(argsTypes[0], signature.returnType, it)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.receiverAndArgs
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
|
||||
object OrOr : IntrinsicMethod() {
|
||||
|
||||
private class BooleanDisjunction(
|
||||
val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo
|
||||
) : BooleanValue(codegen) {
|
||||
|
||||
override fun jumpIfFalse(target: Label) {
|
||||
val stayLabel = Label()
|
||||
arg0.accept(codegen, data).coerceToBoolean().jumpIfTrue(stayLabel)
|
||||
arg1.accept(codegen, data).coerceToBoolean().jumpIfFalse(target)
|
||||
mv.visitLabel(stayLabel)
|
||||
}
|
||||
|
||||
override fun jumpIfTrue(target: Label) {
|
||||
arg0.accept(codegen, data).coerceToBoolean().jumpIfTrue(target)
|
||||
arg1.accept(codegen, data).coerceToBoolean().jumpIfTrue(target)
|
||||
}
|
||||
|
||||
override fun discard() {
|
||||
val end = Label()
|
||||
arg0.accept(codegen, data).coerceToBoolean().jumpIfTrue(end)
|
||||
arg1.accept(codegen, data).discard()
|
||||
mv.visitLabel(end)
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val (left, right) = expression.receiverAndArgs()
|
||||
return BooleanDisjunction(left, right, codegen, data)
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
class PostfixIinc(private val delta: Int) : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
val argument = expression.getValueArgument(0) as? IrGetValue
|
||||
?: error("IrGetValue expected: ${expression.dump()}")
|
||||
val varIndex = codegen.frameMap.getIndex(argument.symbol)
|
||||
if (varIndex == -1)
|
||||
error("Unmapped variable: ${argument.render()}")
|
||||
argument.accept(codegen, data).materialize()
|
||||
codegen.mv.iinc(varIndex, delta)
|
||||
return MaterialValue(codegen, Type.INT_TYPE, argument.type)
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.Type.*
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
object RangeTo : IntrinsicMethod() {
|
||||
private fun rangeTypeToPrimitiveType(rangeType: Type): Type {
|
||||
val fqName = rangeType.internalName
|
||||
val name = fqName.substringAfter("kotlin/ranges/").substringBefore("Range")
|
||||
return when (name) {
|
||||
"Double" -> DOUBLE_TYPE
|
||||
"Float" -> FLOAT_TYPE
|
||||
"Long" -> LONG_TYPE
|
||||
"Int" -> INT_TYPE
|
||||
"Short" -> SHORT_TYPE
|
||||
"Char" -> CHAR_TYPE
|
||||
"Byte" -> BYTE_TYPE
|
||||
else -> throw IllegalStateException("RangeTo intrinsic can only work for primitive types: $fqName")
|
||||
}
|
||||
}
|
||||
|
||||
override fun toCallable(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
|
||||
val argType = rangeTypeToPrimitiveType(signature.returnType)
|
||||
return object: IrIntrinsicFunction(expression, signature, context, listOf(argType) + signature.valueParameters.map { argType }) {
|
||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
||||
v.invokespecial(signature.returnType.internalName, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, argType, argType), false)
|
||||
}
|
||||
|
||||
override fun invoke(
|
||||
v: InstructionAdapter,
|
||||
codegen: ExpressionCodegen,
|
||||
data: BlockInfo,
|
||||
expression: IrFunctionAccessExpression
|
||||
): StackValue {
|
||||
codegen.markLineNumber(expression)
|
||||
v.anew(returnType)
|
||||
v.dup()
|
||||
return super.invoke(v, codegen, data, expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.MaterialValue
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
import org.jetbrains.kotlin.ir.util.collectRealOverrides
|
||||
import org.jetbrains.kotlin.ir.util.isSuspend
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
/**
|
||||
* Computes the JVM signature of a given IrFunction. The function is passed as an IrFunctionReference
|
||||
* to the single argument of the intrinsic.
|
||||
*/
|
||||
object SignatureString : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue {
|
||||
val function = (expression.getValueArgument(0) as IrFunctionReference).symbol.owner
|
||||
generateSignatureString(codegen.mv, function, codegen.context)
|
||||
return MaterialValue(codegen, AsmTypes.JAVA_STRING_TYPE, codegen.context.irBuiltIns.stringType)
|
||||
}
|
||||
|
||||
internal fun generateSignatureString(v: InstructionAdapter, function: IrFunction, context: JvmBackendContext) {
|
||||
var resolved = if (function is IrSimpleFunction) function.collectRealOverrides().first() else function
|
||||
if (resolved.isSuspend) {
|
||||
resolved = context.suspendFunctionOriginalToView[resolved] ?: resolved
|
||||
}
|
||||
val method = context.methodSignatureMapper.mapAsmMethod(resolved)
|
||||
val descriptor = method.name + method.descriptor
|
||||
v.aconst(descriptor)
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
|
||||
object StringGetChar : IntrinsicMethod() {
|
||||
override fun toCallable(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
|
||||
return IrIntrinsicFunction.create(expression, signature, context) {
|
||||
it.invokevirtual("java/lang/String", "charAt", "(I)C", false)
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object StringPlus : IntrinsicMethod() {
|
||||
|
||||
override fun toCallable(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction =
|
||||
IrIntrinsicFunction.create(expression, signature, context, listOf(AsmTypes.JAVA_STRING_TYPE, AsmTypes.OBJECT_TYPE)) {
|
||||
it.invokestatic(
|
||||
IntrinsicMethods.INTRINSICS_CLASS_NAME,
|
||||
"stringPlus",
|
||||
Type.getMethodDescriptor(AsmTypes.JAVA_STRING_TYPE, AsmTypes.JAVA_STRING_TYPE, AsmTypes.OBJECT_TYPE),
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.PromisedValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
class ThrowException(val exceptionClass: Type) : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue? {
|
||||
with(codegen) {
|
||||
mv.anew(exceptionClass)
|
||||
mv.dup()
|
||||
gen(expression.getValueArgument(0)!!, AsmTypes.JAVA_STRING_TYPE, codegen.context.irBuiltIns.stringType, data)
|
||||
mv.invokespecial(exceptionClass.internalName, "<init>", "(Ljava/lang/String;)V", false)
|
||||
mv.athrow()
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
object ThrowKotlinNothingValueException : IntrinsicMethod() {
|
||||
override fun toCallable(
|
||||
expression: IrFunctionAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction =
|
||||
IrIntrinsicFunction.create(expression, signature, context) { mv ->
|
||||
if (context.state.useKotlinNothingValueException) {
|
||||
mv.anew(Type.getObjectType("kotlin/KotlinNothingValueException"))
|
||||
mv.dup()
|
||||
mv.invokespecial("kotlin/KotlinNothingValueException", "<init>", "()V", false)
|
||||
} else {
|
||||
mv.aconst(null)
|
||||
}
|
||||
mv.athrow()
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.IrInlineIntrinsicsSupport
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.fileParent
|
||||
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
|
||||
import org.jetbrains.kotlin.codegen.inline.generateTypeOf
|
||||
import org.jetbrains.kotlin.codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
|
||||
object TypeOf : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) = with(codegen) {
|
||||
val type = expression.getTypeArgument(0)!!
|
||||
if (putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.TYPE_OF)) {
|
||||
mv.aconst(null) // see ReifiedTypeInliner.processTypeOf
|
||||
} else {
|
||||
val support = IrInlineIntrinsicsSupport(context, typeMapper, expression, codegen.irFunction.fileParent)
|
||||
typeMapper.typeSystem.generateTypeOf(mv, type, support)
|
||||
}
|
||||
expression.onStack
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.numberFunctionOperandType
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
|
||||
object UnaryMinus : IntrinsicMethod() {
|
||||
/*TODO return type*/
|
||||
override fun toCallable(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
|
||||
return IrIntrinsicFunction.create(expression, signature, context) {
|
||||
it.neg(numberFunctionOperandType(signature.returnType))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2016 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
|
||||
object UnaryPlus : IntrinsicMethod() {
|
||||
override fun toCallable(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, context: JvmBackendContext): IrIntrinsicFunction {
|
||||
return IrIntrinsicFunction.create(expression, signature, context) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* 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.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
/**
|
||||
* Implicit coercion between IrTypes with the same underlying representation.
|
||||
*
|
||||
* A call of the form `coerce<A,B>(x)` allows us to coerce the value of `x` to type `A` but treat the result as if
|
||||
* it had IrType `B`. This is useful for inline classes, whose coercion behavior depends on the IrType in
|
||||
* addition to the underlying asmType.
|
||||
*/
|
||||
object UnsafeCoerce : IntrinsicMethod() {
|
||||
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo): PromisedValue {
|
||||
val from = expression.getTypeArgument(0)!!
|
||||
val to = expression.getTypeArgument(1)!!
|
||||
val fromType = codegen.typeMapper.mapType(from)
|
||||
val toType = codegen.typeMapper.mapType(to)
|
||||
require(fromType == toType) {
|
||||
"Inline class types should have the same representation: $fromType != $toType"
|
||||
}
|
||||
val arg = expression.getValueArgument(0)!!
|
||||
val result = arg.accept(codegen, data)
|
||||
return object : PromisedValue(codegen, toType, to) {
|
||||
override fun materializeAt(target: Type, irTarget: IrType, castForReified: Boolean) {
|
||||
result.materializeAt(fromType, from)
|
||||
super.materializeAt(target, irTarget, castForReified)
|
||||
}
|
||||
|
||||
override fun discard() {
|
||||
result.discard()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user