[K/JS] feat: eliminate usage of kotlin.reflection if there is just raw js class usage.

This commit is contained in:
Artem Kobzar
2022-07-29 14:22:19 +00:00
committed by Space
parent b9189be728
commit fb32af1c02
6 changed files with 115 additions and 8 deletions
@@ -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<Lowering>(
innerClassesLoweringPhase,
innerClassesMemberBodyLoweringPhase,
innerClassConstructorCallsLoweringPhase,
jsClassUsageInReflectionPhase,
propertiesLoweringPhase,
primaryConstructorLoweringPhase,
delegateToPrimaryConstructorLoweringPhase,
@@ -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)
}
}
}
@@ -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) {
@@ -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<IrDeclaration>()
protected val classesWithObjectAssociations = hashSetOf<IrClass>()
public val usefulPolyfilledDeclarations = hashSetOf<IrDeclaration>()
protected open fun processField(irField: IrField): Unit = Unit
protected open fun processClass(irClass: IrClass) {
@@ -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.<get-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
}
}
@@ -128,8 +128,9 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
override fun visitGetObjectValue(expression: IrGetObjectValue, context: JsGenerationContext): JsExpression {
val obj = expression.symbol.owner
assert(obj.kind == ClassKind.OBJECT)
assert(obj.isEffectivelyExternal()) { "Non external IrGetObjectValue must be lowered" }
assert(obj.kind == ClassKind.OBJECT)
assert(obj.isEffectivelyExternal()) { "Non external IrGetObjectValue must be lowered" }
return context.getRefForExternalClass(obj).withSource(expression, context)
}