From e5993ab5943bb40d1353e9f0d1d2a125ed57c5e3 Mon Sep 17 00:00:00 2001 From: Sergej Jaskiewicz Date: Thu, 9 Dec 2021 14:18:48 +0300 Subject: [PATCH] [JS IR] [Wasm] Extract InlineClassesUtils into CommonBackendContext --- .../backend/common/CommonBackendContext.kt | 35 +++++++++++++++++++ .../ir/backend/js/JsCommonBackendContext.kt | 28 ++++++++++----- .../backend/js/utils/JsInlineClassesUtils.kt | 34 +++++++++++++----- .../wasm/utils/WasmInlineClassesUtils.kt | 24 ++----------- 4 files changed, 82 insertions(+), 39 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt index 78cfc46a07e..3cbc7824dfc 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt @@ -11,6 +11,8 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope import org.jetbrains.kotlin.ir.builders.irCall import org.jetbrains.kotlin.ir.builders.irString +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.expressions.IrCall @@ -19,6 +21,9 @@ import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrFileSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull interface LoggingContext { var inVerbosePhase: Boolean @@ -67,4 +72,34 @@ interface CommonBackendContext : BackendContext, LoggingContext { val optimizeNullChecksUsingKotlinNullability: Boolean get() = true + + /** + * See [InlineClassesUtils]. + */ + val inlineClassesUtils: InlineClassesUtils + get() = DefaultInlineClassesUtils } + +/** + * Provides means for determining if a class should be treated as an inline/value class. + * + * In certain cases (for compatibility reasons) we don't want to mark a class as `inline`, but still want to treat it as one. + * + * See [org.jetbrains.kotlin.ir.backend.js.utils.JsInlineClassesUtils]. + */ +interface InlineClassesUtils { + /** + * Should this class be treated as inline class? + */ + fun isClassInlineLike(klass: IrClass): Boolean = klass.isInline + + /** + * Unlike [org.jetbrains.kotlin.ir.util.getInlineClassUnderlyingType], doesn't use [IrClass.inlineClassRepresentation] because + * for some reason it can be called for classes which are not inline, e.g. `kotlin.Double`. + */ + fun getInlineClassUnderlyingType(irClass: IrClass): IrType = + irClass.declarations.firstIsInstanceOrNull()?.takeIf { it.isPrimary }?.valueParameters?.get(0)?.type + ?: error("Class has no primary constructor: ${irClass.fqNameWhenAvailable}") +} + +object DefaultInlineClassesUtils : InlineClassesUtils diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsCommonBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsCommonBackendContext.kt index 6e2c4c35014..f320034cbff 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsCommonBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsCommonBackendContext.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js import org.jetbrains.kotlin.backend.common.BackendContext import org.jetbrains.kotlin.backend.common.CommonBackendContext +import org.jetbrains.kotlin.backend.common.InlineClassesUtils import org.jetbrains.kotlin.backend.common.atMostOne import org.jetbrains.kotlin.backend.common.ir.isOverridableOrOverrides import org.jetbrains.kotlin.descriptors.ClassDescriptor @@ -16,9 +17,7 @@ import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.backend.js.utils.isDispatchReceiver import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol -import org.jetbrains.kotlin.ir.types.IrDynamicType import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.getPropertyGetter @@ -32,7 +31,7 @@ interface JsCommonBackendContext : CommonBackendContext { val reflectionSymbols: ReflectionSymbols - val inlineClassesUtils: InlineClassesUtils + override val inlineClassesUtils: JsCommonInlineClassesUtils val coroutineSymbols: JsCommonCoroutineSymbols @@ -123,8 +122,16 @@ fun findClass(memberScope: MemberScope, name: Name): ClassDescriptor = fun findFunctions(memberScope: MemberScope, name: Name): List = memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).toList() -interface InlineClassesUtils { - fun isTypeInlined(type: IrType): Boolean +interface JsCommonInlineClassesUtils : InlineClassesUtils { + + /** + * Returns the inlined class for the given type, or `null` if the type is not inlined. + */ + fun getInlinedClass(type: IrType): IrClass? + + fun isTypeInlined(type: IrType): Boolean { + return getInlinedClass(type) != null + } fun shouldValueParameterBeBoxed(parameter: IrValueParameter): Boolean { val function = parameter.parent as? IrSimpleFunction ?: return false @@ -133,10 +140,13 @@ interface InlineClassesUtils { return parameter.isDispatchReceiver && function.isOverridableOrOverrides } - fun getInlinedClass(type: IrType): IrClass? - - fun isClassInlineLike(klass: IrClass): Boolean - + /** + * An intrinsic for creating an instance of an inline class from its underlying value. + */ val boxIntrinsic: IrSimpleFunctionSymbol + + /** + * An intrinsic for obtaining the underlying value from an instance of an inline class. + */ val unboxIntrinsic: IrSimpleFunctionSymbol } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsInlineClassesUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsInlineClassesUtils.kt index 1fc4b7caa88..5bbfbed2837 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsInlineClassesUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsInlineClassesUtils.kt @@ -5,23 +5,39 @@ package org.jetbrains.kotlin.ir.backend.js.utils -import org.jetbrains.kotlin.ir.backend.js.InlineClassesUtils +import org.jetbrains.kotlin.ir.backend.js.JsCommonInlineClassesUtils import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.isMarkedNullable -import org.jetbrains.kotlin.ir.util.getInlineClassUnderlyingType -class JsInlineClassesUtils(val context: JsIrBackendContext) : InlineClassesUtils { - override fun isTypeInlined(type: IrType): Boolean { - return getInlinedClass(type) != null +class JsInlineClassesUtils(val context: JsIrBackendContext) : JsCommonInlineClassesUtils { + + override fun getInlinedClass(type: IrType): IrClass? { + if (type is IrSimpleType) { + val erased = erase(type) ?: return null + if (isClassInlineLike(erased)) { + if (type.isMarkedNullable()) { + var fieldType: IrType + var fieldInlinedClass = erased + while (true) { + fieldType = getInlineClassUnderlyingType(fieldInlinedClass) + if (fieldType.isMarkedNullable()) { + return null + } + + fieldInlinedClass = getInlinedClass(fieldType) ?: break + } + } + + return erased + } + } + return null } - override fun getInlinedClass(type: IrType): IrClass? = - type.getJsInlinedClass() - override fun isClassInlineLike(klass: IrClass): Boolean = klass.isInline @@ -30,4 +46,4 @@ class JsInlineClassesUtils(val context: JsIrBackendContext) : InlineClassesUtils override val unboxIntrinsic: IrSimpleFunctionSymbol get() = context.intrinsics.jsUnboxIntrinsic -} \ No newline at end of file +} diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/WasmInlineClassesUtils.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/WasmInlineClassesUtils.kt index 8bbef3d48c1..fcaea33c4e6 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/WasmInlineClassesUtils.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/utils/WasmInlineClassesUtils.kt @@ -6,20 +6,15 @@ package org.jetbrains.kotlin.backend.wasm.utils import org.jetbrains.kotlin.backend.wasm.WasmSymbols -import org.jetbrains.kotlin.ir.backend.js.InlineClassesUtils +import org.jetbrains.kotlin.ir.backend.js.JsCommonInlineClassesUtils import org.jetbrains.kotlin.ir.backend.js.utils.erase import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.isNullable -import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable -class WasmInlineClassesUtils(private val wasmSymbols: WasmSymbols) : InlineClassesUtils { - override fun isTypeInlined(type: IrType): Boolean { - return getInlinedClass(type) != null - } +class WasmInlineClassesUtils(private val wasmSymbols: WasmSymbols) : JsCommonInlineClassesUtils { override fun getInlinedClass(type: IrType): IrClass? { if (type is IrSimpleType) { @@ -36,7 +31,7 @@ class WasmInlineClassesUtils(private val wasmSymbols: WasmSymbols) : InlineClass override fun isClassInlineLike(klass: IrClass): Boolean { // TODO: This hook is called from autoboxing lowering so we also handle autoboxing annotation here. In the future it's better // to separate autoboxing from the inline class handling. - return klass.isInline || klass.hasWasmAutoboxedAnnotation() + return super.isClassInlineLike(klass) || klass.hasWasmAutoboxedAnnotation() } override val boxIntrinsic: IrSimpleFunctionSymbol @@ -44,17 +39,4 @@ class WasmInlineClassesUtils(private val wasmSymbols: WasmSymbols) : InlineClass override val unboxIntrinsic: IrSimpleFunctionSymbol get() = wasmSymbols.unboxIntrinsic - - /** - * Unlike [org.jetbrains.kotlin.ir.util.getInlineClassUnderlyingType], doesn't use [IrClass.inlineClassRepresentation] because - * for some reason it can be called for classes which are not inline, e.g. `kotlin.Double`. - */ - fun getInlineClassUnderlyingType(irClass: IrClass): IrType { - for (declaration in irClass.declarations) { - if (declaration is IrConstructor && declaration.isPrimary) { - return declaration.valueParameters[0].type - } - } - error("Class has no primary constructor: ${irClass.fqNameWhenAvailable}") - } }