[K/JS] feat: eliminate usage of kotlin.reflection if there is just raw js class usage.
This commit is contained in:
@@ -734,10 +734,18 @@ private val blockDecomposerLoweringPhase = makeBodyLoweringPhase(
|
|||||||
prerequisite = setOf(typeOperatorLoweringPhase, suspendFunctionsLoweringPhase)
|
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(
|
private val classReferenceLoweringPhase = makeBodyLoweringPhase(
|
||||||
::ClassReferenceLowering,
|
::ClassReferenceLowering,
|
||||||
name = "ClassReferenceLowering",
|
name = "ClassReferenceLowering",
|
||||||
description = "Handle class references"
|
description = "Handle class references",
|
||||||
|
prerequisite = setOf(jsClassUsageInReflectionPhase)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val primitiveCompanionLoweringPhase = makeBodyLoweringPhase(
|
private val primitiveCompanionLoweringPhase = makeBodyLoweringPhase(
|
||||||
@@ -845,6 +853,7 @@ val loweringList = listOf<Lowering>(
|
|||||||
innerClassesLoweringPhase,
|
innerClassesLoweringPhase,
|
||||||
innerClassesMemberBodyLoweringPhase,
|
innerClassesMemberBodyLoweringPhase,
|
||||||
innerClassConstructorCallsLoweringPhase,
|
innerClassConstructorCallsLoweringPhase,
|
||||||
|
jsClassUsageInReflectionPhase,
|
||||||
propertiesLoweringPhase,
|
propertiesLoweringPhase,
|
||||||
primaryConstructorLoweringPhase,
|
primaryConstructorLoweringPhase,
|
||||||
delegateToPrimaryConstructorLoweringPhase,
|
delegateToPrimaryConstructorLoweringPhase,
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ fun eliminateDeadDeclarations(
|
|||||||
context.configuration.getBoolean(JSConfigurationKeys.PRINT_REACHABILITY_INFO) ||
|
context.configuration.getBoolean(JSConfigurationKeys.PRINT_REACHABILITY_INFO) ||
|
||||||
java.lang.Boolean.getBoolean("kotlin.js.ir.dce.print.reachability.info")
|
java.lang.Boolean.getBoolean("kotlin.js.ir.dce.print.reachability.info")
|
||||||
|
|
||||||
val usefulDeclarations = JsUsefulDeclarationProcessor(context, printReachabilityInfo, removeUnusedAssociatedObjects)
|
val usefulDeclarationProcessor = JsUsefulDeclarationProcessor(context, printReachabilityInfo, removeUnusedAssociatedObjects)
|
||||||
.collectDeclarations(allRoots)
|
val usefulDeclarations = usefulDeclarationProcessor.collectDeclarations(allRoots)
|
||||||
|
|
||||||
val uselessDeclarationsProcessor =
|
val uselessDeclarationsProcessor =
|
||||||
UselessDeclarationsRemover(removeUnusedAssociatedObjects, usefulDeclarations, context, context.dceRuntimeDiagnostic)
|
UselessDeclarationsRemover(removeUnusedAssociatedObjects, usefulDeclarations, context, context.dceRuntimeDiagnostic)
|
||||||
@@ -38,7 +38,7 @@ fun eliminateDeadDeclarations(
|
|||||||
modules.forEach { module ->
|
modules.forEach { module ->
|
||||||
module.files.forEach {
|
module.files.forEach {
|
||||||
it.acceptVoid(uselessDeclarationsProcessor)
|
it.acceptVoid(uselessDeclarationsProcessor)
|
||||||
context.polyfills.saveOnlyIntersectionOfNextDeclarationsFor(it, usefulDeclarations)
|
context.polyfills.saveOnlyIntersectionOfNextDeclarationsFor(it, usefulDeclarationProcessor.usefulPolyfilledDeclarations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -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.backend.js.utils.invokeFunForLambda
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
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.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||||
@@ -109,6 +110,7 @@ internal class JsUsefulDeclarationProcessor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun processClass(irClass: IrClass) {
|
override fun processClass(irClass: IrClass) {
|
||||||
|
|||||||
+14
-2
@@ -52,8 +52,10 @@ abstract class UsefulDeclarationProcessor(
|
|||||||
|
|
||||||
override fun visitBlock(expression: IrBlock, data: IrDeclaration) {
|
override fun visitBlock(expression: IrBlock, data: IrDeclaration) {
|
||||||
super.visitBlock(expression, data)
|
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) {
|
override fun visitFieldAccess(expression: IrFieldAccessExpression, data: IrDeclaration) {
|
||||||
@@ -111,6 +113,14 @@ abstract class UsefulDeclarationProcessor(
|
|||||||
if (this !in result) {
|
if (this !in result) {
|
||||||
result.add(this)
|
result.add(this)
|
||||||
queue.addLast(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 result = hashSetOf<IrDeclaration>()
|
||||||
protected val classesWithObjectAssociations = hashSetOf<IrClass>()
|
protected val classesWithObjectAssociations = hashSetOf<IrClass>()
|
||||||
|
|
||||||
|
public val usefulPolyfilledDeclarations = hashSetOf<IrDeclaration>()
|
||||||
|
|
||||||
protected open fun processField(irField: IrField): Unit = Unit
|
protected open fun processField(irField: IrField): Unit = Unit
|
||||||
|
|
||||||
protected open fun processClass(irClass: IrClass) {
|
protected open fun processClass(irClass: IrClass) {
|
||||||
|
|||||||
+83
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-2
@@ -128,8 +128,9 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
|
|
||||||
override fun visitGetObjectValue(expression: IrGetObjectValue, context: JsGenerationContext): JsExpression {
|
override fun visitGetObjectValue(expression: IrGetObjectValue, context: JsGenerationContext): JsExpression {
|
||||||
val obj = expression.symbol.owner
|
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)
|
return context.getRefForExternalClass(obj).withSource(expression, context)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user