From fb32af1c023fbfdb8aeb87312d03de46fcdc9886 Mon Sep 17 00:00:00 2001 From: Artem Kobzar Date: Fri, 29 Jul 2022 14:22:19 +0000 Subject: [PATCH] [K/JS] feat: eliminate usage of kotlin.reflection if there is just raw js class usage. --- .../kotlin/ir/backend/js/JsLoweringPhases.kt | 11 ++- .../jetbrains/kotlin/ir/backend/js/dce/Dce.kt | 6 +- .../js/dce/JsUsefulDeclarationProcessor.kt | 2 + .../js/dce/UsefulDeclarationProcessor.kt | 16 +++- .../lower/JsClassUsageInReflectionLowering.kt | 83 +++++++++++++++++++ .../IrElementToJsExpressionTransformer.kt | 5 +- 6 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsClassUsageInReflectionLowering.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 7aed689dc0d..5fa193fb5a9 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -734,10 +734,18 @@ private val blockDecomposerLoweringPhase = makeBodyLoweringPhase( prerequisite = setOf(typeOperatorLoweringPhase, suspendFunctionsLoweringPhase) ) +private val jsClassUsageInReflectionPhase = makeBodyLoweringPhase( + ::JsClassUsageInReflectionLowering, + name = "JsClassUsageInReflectionLowering", + description = "[Optimization] Eliminate ClassReference and GetClassExpression usages in a simple case of usage raw js constructor", + prerequisite = setOf(functionInliningPhase) +) + private val classReferenceLoweringPhase = makeBodyLoweringPhase( ::ClassReferenceLowering, name = "ClassReferenceLowering", - description = "Handle class references" + description = "Handle class references", + prerequisite = setOf(jsClassUsageInReflectionPhase) ) private val primitiveCompanionLoweringPhase = makeBodyLoweringPhase( @@ -845,6 +853,7 @@ val loweringList = listOf( innerClassesLoweringPhase, innerClassesMemberBodyLoweringPhase, innerClassConstructorCallsLoweringPhase, + jsClassUsageInReflectionPhase, propertiesLoweringPhase, primaryConstructorLoweringPhase, delegateToPrimaryConstructorLoweringPhase, diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/Dce.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/Dce.kt index 3a8ee77c33d..fe8022c5516 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/Dce.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/Dce.kt @@ -29,8 +29,8 @@ fun eliminateDeadDeclarations( context.configuration.getBoolean(JSConfigurationKeys.PRINT_REACHABILITY_INFO) || java.lang.Boolean.getBoolean("kotlin.js.ir.dce.print.reachability.info") - val usefulDeclarations = JsUsefulDeclarationProcessor(context, printReachabilityInfo, removeUnusedAssociatedObjects) - .collectDeclarations(allRoots) + val usefulDeclarationProcessor = JsUsefulDeclarationProcessor(context, printReachabilityInfo, removeUnusedAssociatedObjects) + val usefulDeclarations = usefulDeclarationProcessor.collectDeclarations(allRoots) val uselessDeclarationsProcessor = UselessDeclarationsRemover(removeUnusedAssociatedObjects, usefulDeclarations, context, context.dceRuntimeDiagnostic) @@ -38,7 +38,7 @@ fun eliminateDeadDeclarations( modules.forEach { module -> module.files.forEach { it.acceptVoid(uselessDeclarationsProcessor) - context.polyfills.saveOnlyIntersectionOfNextDeclarationsFor(it, usefulDeclarations) + context.polyfills.saveOnlyIntersectionOfNextDeclarationsFor(it, usefulDeclarationProcessor.usefulPolyfilledDeclarations) } } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt index 01172468dfb..387d6d2808c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName import org.jetbrains.kotlin.ir.backend.js.utils.invokeFunForLambda import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.types.classifierOrFail @@ -109,6 +110,7 @@ internal class JsUsefulDeclarationProcessor( } } } + } override fun processClass(irClass: IrClass) { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt index 6a6f391ed1b..11f9f1856b5 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt @@ -52,8 +52,10 @@ abstract class UsefulDeclarationProcessor( override fun visitBlock(expression: IrBlock, data: IrDeclaration) { super.visitBlock(expression, data) - if (expression !is IrReturnableBlock) return - expression.inlineFunctionSymbol?.owner?.enqueue(data, "inline function usage") + + if (expression is IrReturnableBlock) { + expression.inlineFunctionSymbol?.owner?.addToUsefulPolyfilledDeclarations() + } } override fun visitFieldAccess(expression: IrFieldAccessExpression, data: IrDeclaration) { @@ -111,6 +113,14 @@ abstract class UsefulDeclarationProcessor( if (this !in result) { result.add(this) queue.addLast(this) + + addToUsefulPolyfilledDeclarations() + } + } + + private fun IrDeclaration.addToUsefulPolyfilledDeclarations() { + if (hasJsPolyfill() && this !in usefulPolyfilledDeclarations) { + usefulPolyfilledDeclarations.add(this) } } @@ -128,6 +138,8 @@ abstract class UsefulDeclarationProcessor( protected val result = hashSetOf() protected val classesWithObjectAssociations = hashSetOf() + public val usefulPolyfilledDeclarations = hashSetOf() + protected open fun processField(irField: IrField): Unit = Unit protected open fun processClass(irClass: IrClass) { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsClassUsageInReflectionLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsClassUsageInReflectionLowering.kt new file mode 100644 index 00000000000..2f145c474ec --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsClassUsageInReflectionLowering.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2010-2022 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.ir.backend.js.lower + +import org.jetbrains.kotlin.backend.common.BodyLoweringPass +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins +import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrDynamicMemberExpressionImpl +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.util.defaultType +import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.name.FqName + +private val JS_CLASS_GETTER = FqName("kotlin.js.") + +class JsClassUsageInReflectionLowering(val backendContext: JsIrBackendContext) : BodyLoweringPass { + override fun lower(irBody: IrBody, container: IrDeclaration) { + irBody.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression { + if (expression.origin != IrStatementOrigin.GET_PROPERTY || !expression.isGetJsCall()) { + return super.visitCall(expression) + } + + return when (val extensionReceiver = expression.extensionReceiver) { + is IrClassReference -> extensionReceiver.generateDirectValueUsage() ?: super.visitCall(expression) + is IrGetClass -> extensionReceiver.generateDirectConstructorUsage() + else -> super.visitCall(expression) + } + } + + }) + } + + private fun IrClassReference.generateDirectValueUsage(): IrExpression? { + return with(backendContext) { + when(val classSymbol = symbol as? IrClassSymbol ?: return null) { + irBuiltIns.nothingClass -> null + irBuiltIns.anyClass -> + JsIrBuilder.buildCall(intrinsics.jsCode).apply { + putValueArgument( + 0, + IrConstImpl.string( + UNDEFINED_OFFSET, + UNDEFINED_OFFSET, + irBuiltIns.stringType, + "Object" + ) + ) + } + + else -> + JsIrBuilder.buildCall(intrinsics.jsClass, origin = JsStatementOrigins.CLASS_REFERENCE).apply { + putTypeArgument(0, classSymbol.owner.defaultType) + } + } + } + } + + private fun IrGetClass.generateDirectConstructorUsage(): IrDynamicMemberExpression { + return IrDynamicMemberExpressionImpl( + startOffset, + endOffset, + backendContext.dynamicType, + "constructor", + argument + ) + } + + private fun IrCall.isGetJsCall(): Boolean { + return symbol.owner.fqNameWhenAvailable == JS_CLASS_GETTER + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt index 2fa2ce7e9a0..1b0384cf578 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt @@ -128,8 +128,9 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer