diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index e1add8f4ea1..a0cd7334b8a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrFileSymbol @@ -61,6 +62,7 @@ class JsIrBackendContext( ) : JsCommonBackendContext { val fileToInitializationFuns: MutableMap = mutableMapOf() val fileToInitializerPureness: MutableMap = mutableMapOf() + val fieldToInitializer: MutableMap = mutableMapOf() val extractedLocalClasses: MutableSet = hashSetOf() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsCodeOutliningLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsCodeOutliningLowering.kt index ee92c54b356..62d441a8e5c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsCodeOutliningLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsCodeOutliningLowering.kt @@ -134,7 +134,7 @@ private class JsCodeOutlineTransformer( return null val jsCodeArg = expression.getValueArgument(0) ?: error("Expected js code string") - val jsStatements = translateJsCodeIntoStatementList(jsCodeArg) ?: return null + val jsStatements = translateJsCodeIntoStatementList(jsCodeArg, backendContext) ?: return null // Collect used Kotlin local variables and parameters. val kotlinLocalsUsedInJs = mutableListOf() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PropertyLazyInitLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PropertyLazyInitLowering.kt index 974ae839888..1fbc224946f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PropertyLazyInitLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/PropertyLazyInitLowering.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.lower import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.DeclarationTransformer +import org.jetbrains.kotlin.backend.common.ir.isPure import org.jetbrains.kotlin.backend.common.ir.isTopLevel import org.jetbrains.kotlin.descriptors.DescriptorVisibilities.INTERNAL import org.jetbrains.kotlin.ir.IrStatement @@ -15,7 +16,6 @@ import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.ir.JsIrArithBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.utils.prependFunctionCall -import org.jetbrains.kotlin.backend.common.ir.isPure import org.jetbrains.kotlin.ir.builders.declarations.addFunction import org.jetbrains.kotlin.ir.builders.declarations.buildField import org.jetbrains.kotlin.ir.declarations.* @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.ir.util.hasAnnotation import org.jetbrains.kotlin.name.Name import kotlin.collections.component1 import kotlin.collections.component2 +import kotlin.collections.set class PropertyLazyInitLowering( private val context: JsIrBackendContext @@ -71,24 +72,11 @@ class PropertyLazyInitLowering( val initializationCall = JsIrBuilder.buildCall( target = initFun.symbol, - type = initFun.returnType + type = initFun.returnType, + origin = PROPERTY_INIT_FUN_CALL ) - when (container) { - is IrSimpleFunction -> - irBody.prependFunctionCall(initializationCall) - is IrField -> { - container - .correspondingProperty - ?.takeIf { it.isForLazyInit() } - ?.takeIf { it.backingField?.initializer != null } - ?.let { listOf(it.getter, it.setter) } - ?.filterNotNull() - ?.forEach { - irBody.prependFunctionCall(initializationCall) - } - } - } + if (container is IrSimpleFunction) irBody.prependFunctionCall(initializationCall) } private fun createInitializationFunction( @@ -173,6 +161,10 @@ class PropertyLazyInitLowering( ) ).let { listOf(it) } } + + companion object { + object PROPERTY_INIT_FUN_CALL : IrStatementOriginImpl("PROPERTY_INIT_FUN_CALL") + } } private fun createIrGetField(field: IrField): IrGetField { @@ -191,8 +183,11 @@ private fun createIrSetField(field: IrField, expression: IrExpression): IrSetFie ) } -private fun allFieldsInFilePure(fieldToInitializer: Collection) = - fieldToInitializer.all { it.isPure(anyVariable = true) } +private fun allFieldsInFilePure(fieldToInitializer: Collection): Boolean = + fieldToInitializer + .all { expression -> + expression.isPure(anyVariable = true) + } class RemoveInitializersForLazyProperties( private val context: JsIrBackendContext 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 2499b536dfc..bce1c94032e 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 @@ -192,8 +192,10 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer JsEmpty 1 -> statements.single().withSource(expression, data) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt index c88a3288e4e..b14de2b4209 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt @@ -8,6 +8,8 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs import com.google.gwt.dev.js.ThrowExceptionOnErrorReporter import com.google.gwt.dev.js.rhino.CodePosition import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.lower.PropertyLazyInitLowering import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid @@ -20,55 +22,10 @@ import org.jetbrains.kotlin.js.backend.ast.JsStatement import org.jetbrains.kotlin.js.parser.parseExpressionOrStatement // Returns null if constant expression could not be parsed -fun translateJsCodeIntoStatementList(code: IrExpression): List? { +fun translateJsCodeIntoStatementList(code: IrExpression, context: JsIrBackendContext): List? { // TODO: support proper symbol linkage and label clash resolution - fun foldString(expression: IrExpression): String? { - val builder = StringBuilder() - var foldingFailed = false - expression.acceptVoid(object : IrElementVisitorVoid { - override fun visitElement(element: IrElement) { - foldingFailed = true - } - - override fun visitGetValue(expression: IrGetValue) { - expression.symbol.owner.acceptVoid(this) - } - - override fun visitVariable(declaration: IrVariable) { - declaration.initializer?.let { - it.acceptVoid(this) - return - } - - super.visitVariable(declaration) - } - - override fun visitGetField(expression: IrGetField) { - expression.symbol.owner.initializer?.expression?.acceptVoid(this) - } - - override fun visitCall(expression: IrCall) { - if (expression.origin != IrStatementOrigin.PLUS) { - return super.visitCall(expression) - } - - expression.acceptChildrenVoid(this) - } - - override fun visitConst(expression: IrConst) { - builder.append(expression.kind.valueOf(expression)) - } - - override fun visitStringConcatenation(expression: IrStringConcatenation) = expression.acceptChildrenVoid(this) - }) - - if (foldingFailed) return null - - return builder.toString() - } - - return parseJsCode(foldString(code) ?: return null) + return parseJsCode(foldString(code, context) ?: return null) } fun parseJsCode(jsCode: String): List? { @@ -82,4 +39,74 @@ fun parseJsCode(jsCode: String): List? { // TODO: write debug info, see how it's done in CallExpressionTranslator.parseJsCode return parseExpressionOrStatement(jsCode, ThrowExceptionOnErrorReporter, currentScope, CodePosition(0, 0), "") +} + +fun foldString(expression: IrExpression, context: JsIrBackendContext): String? { + val builder = StringBuilder() + var foldingFailed = false + expression.acceptVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + foldingFailed = true + } + + override fun visitGetValue(expression: IrGetValue) { + expression.symbol.owner.acceptVoid(this) + } + + override fun visitVariable(declaration: IrVariable) { + declaration.initializer?.let { + it.acceptVoid(this) + return + } + + super.visitVariable(declaration) + } + + override fun visitGetField(expression: IrGetField) { + val owner = expression.symbol.owner + owner.initializer?.expression?.acceptVoid(this) + ?: context.fieldToInitializer[owner]?.acceptVoid(this) + } + + override fun visitCall(expression: IrCall) { + val owner = expression.symbol.owner + return when { + expression.origin == IrStatementOrigin.PLUS -> + expression.acceptChildrenVoid(this) + expression.origin == PropertyLazyInitLowering.Companion.PROPERTY_INIT_FUN_CALL -> { + owner.body?.acceptChildrenVoid(InitFunVisitor(context)) + expression.acceptChildrenVoid(this) + } + owner == owner.correspondingPropertySymbol?.owner?.getter -> { + owner.body?.acceptChildrenVoid(this) + expression.acceptChildrenVoid(this) + } + else -> super.visitCall(expression) + } + } + + override fun visitReturn(expression: IrReturn) { + expression.acceptChildrenVoid(this) + } + + override fun visitConst(expression: IrConst) { + builder.append(expression.kind.valueOf(expression)) + } + + override fun visitStringConcatenation(expression: IrStringConcatenation) = expression.acceptChildrenVoid(this) + }) + + if (foldingFailed) return null + + return builder.toString() +} + +private class InitFunVisitor(private val context: JsIrBackendContext) : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitSetField(expression: IrSetField) { + context.fieldToInitializer[expression.symbol.owner] = expression.value + } } \ No newline at end of file