Fix initialization order
* put inheritance code in the beginning * put top-level initializer after any declaration
This commit is contained in:
committed by
Roman Artemev
parent
cff066f702
commit
0c6256d003
@@ -33,11 +33,6 @@ fun compile(
|
|||||||
export: FqName? = null,
|
export: FqName? = null,
|
||||||
dependencies: List<ModuleDescriptor> = listOf()
|
dependencies: List<ModuleDescriptor> = listOf()
|
||||||
): Result {
|
): Result {
|
||||||
// Make compilation stable
|
|
||||||
// Shadow original list to prevent accidental usage
|
|
||||||
@Suppress("NAME_SHADOWING")
|
|
||||||
val files = files.sortedBy { it.virtualFilePath }
|
|
||||||
|
|
||||||
val analysisResult =
|
val analysisResult =
|
||||||
TopDownAnalyzerFacadeForJS.analyzeFiles(files, project, configuration, dependencies.filterIsInstance(), emptyList())
|
TopDownAnalyzerFacadeForJS.analyzeFiles(files, project, configuration, dependencies.filterIsInstance(), emptyList())
|
||||||
|
|
||||||
|
|||||||
+9
-7
@@ -7,7 +7,8 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.JsStatement
|
||||||
|
import org.jetbrains.kotlin.js.backend.ast.JsVars
|
||||||
|
|
||||||
class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
|
class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
|
||||||
|
|
||||||
@@ -25,12 +26,13 @@ class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatemen
|
|||||||
|
|
||||||
override fun visitField(declaration: IrField, context: JsGenerationContext): JsStatement {
|
override fun visitField(declaration: IrField, context: JsGenerationContext): JsStatement {
|
||||||
val fieldName = context.getNameForSymbol(declaration.symbol)
|
val fieldName = context.getNameForSymbol(declaration.symbol)
|
||||||
val initExpression =
|
|
||||||
declaration.initializer?.accept(IrElementToJsExpressionTransformer(), context) ?: JsPrefixOperation(
|
if (declaration.initializer != null) {
|
||||||
JsUnaryOperator.VOID,
|
val initializer = declaration.initializer!!.accept(IrElementToJsExpressionTransformer(), context)
|
||||||
JsIntLiteral(1)
|
context.staticContext.initializerBlock.statements += jsAssignment(fieldName.makeRef(), initializer).makeStmt()
|
||||||
)
|
}
|
||||||
return JsVars(JsVars.JsVar(fieldName, initExpression))
|
|
||||||
|
return JsVars(JsVars.JsVar(fieldName))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitVariable(declaration: IrVariable, context: JsGenerationContext): JsStatement {
|
override fun visitVariable(declaration: IrVariable, context: JsGenerationContext): JsStatement {
|
||||||
|
|||||||
+30
-25
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
|
|||||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
import org.jetbrains.kotlin.js.backend.ast.*
|
import org.jetbrains.kotlin.js.backend.ast.*
|
||||||
|
import org.jetbrains.kotlin.utils.DFS
|
||||||
|
|
||||||
class IrModuleToJsTransformer(val backendContext: JsIrBackendContext) : BaseIrElementToJsNodeTransformer<JsNode, Nothing?> {
|
class IrModuleToJsTransformer(val backendContext: JsIrBackendContext) : BaseIrElementToJsNodeTransformer<JsNode, Nothing?> {
|
||||||
override fun visitModuleFragment(declaration: IrModuleFragment, data: Nothing?): JsNode {
|
override fun visitModuleFragment(declaration: IrModuleFragment, data: Nothing?): JsNode {
|
||||||
@@ -27,42 +28,46 @@ class IrModuleToJsTransformer(val backendContext: JsIrBackendContext) : BaseIrEl
|
|||||||
program.globalBlock.statements += JsVars(JsVars.JsVar(anyName, Namer.JS_OBJECT))
|
program.globalBlock.statements += JsVars(JsVars.JsVar(anyName, Namer.JS_OBJECT))
|
||||||
program.globalBlock.statements += JsVars(JsVars.JsVar(throwableName, Namer.JS_ERROR))
|
program.globalBlock.statements += JsVars(JsVars.JsVar(throwableName, Namer.JS_ERROR))
|
||||||
|
|
||||||
|
val preDeclarationBlock = JsGlobalBlock()
|
||||||
|
val postDeclarationBlock = JsGlobalBlock()
|
||||||
|
|
||||||
|
program.globalBlock.statements += preDeclarationBlock
|
||||||
|
|
||||||
declaration.files.forEach {
|
declaration.files.forEach {
|
||||||
program.globalBlock.statements.add(it.accept(IrFileToJsTransformer(), rootContext))
|
program.globalBlock.statements.add(it.accept(IrFileToJsTransformer(), rootContext))
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort member forwarding code
|
// sort member forwarding code
|
||||||
program.globalBlock.statements += addPostDeclarations(rootContext.staticContext.classModels)
|
processClassModels(rootContext.staticContext.classModels, preDeclarationBlock, postDeclarationBlock)
|
||||||
|
|
||||||
|
program.globalBlock.statements += postDeclarationBlock
|
||||||
|
program.globalBlock.statements += rootContext.staticContext.initializerBlock
|
||||||
|
|
||||||
return program
|
return program
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun processClassModels(
|
||||||
private fun addPostDeclarations(classModels: Map<JsName, JsClassModel>): List<JsStatement> {
|
classModelMap: Map<JsName, JsClassModel>,
|
||||||
|
preDeclarationBlock: JsGlobalBlock,
|
||||||
val statements = mutableListOf<JsStatement>()
|
postDeclarationBlock: JsGlobalBlock
|
||||||
val visited = mutableSetOf<JsName>()
|
|
||||||
|
|
||||||
for (name in classModels.keys) {
|
|
||||||
addPostDeclaration(name, visited, statements, classModels)
|
|
||||||
}
|
|
||||||
|
|
||||||
return statements
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addPostDeclaration(
|
|
||||||
name: JsName,
|
|
||||||
visited: MutableSet<JsName>,
|
|
||||||
statements: MutableList<JsStatement>,
|
|
||||||
classModels: Map<JsName, JsClassModel>
|
|
||||||
) {
|
) {
|
||||||
if (visited.add(name)) {
|
val declarationHandler = object : DFS.AbstractNodeHandler<JsName, Unit>() {
|
||||||
classModels[name]?.run {
|
override fun result() {}
|
||||||
superName?.let { addPostDeclaration(it, visited, statements, classModels) }
|
override fun afterChildren(current: JsName) {
|
||||||
interfaces.forEach { addPostDeclaration(it, visited, statements, classModels) }
|
classModelMap[current]?.let {
|
||||||
|
preDeclarationBlock.statements += it.preDeclarationBlock.statements
|
||||||
statements += postDeclarationBlock.statements
|
postDeclarationBlock.statements += it.postDeclarationBlock.statements
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DFS.dfs(classModelMap.keys, {
|
||||||
|
val neighbors = mutableListOf<JsName>()
|
||||||
|
classModelMap[it]?.run {
|
||||||
|
if (superName != null) neighbors += superName!!
|
||||||
|
neighbors += interfaces
|
||||||
|
}
|
||||||
|
neighbors
|
||||||
|
}, declarationHandler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+3
-2
@@ -43,7 +43,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
when (declaration) {
|
when (declaration) {
|
||||||
is IrConstructor -> {
|
is IrConstructor -> {
|
||||||
classBlock.statements += declaration.accept(transformer, context)
|
classBlock.statements += declaration.accept(transformer, context)
|
||||||
classBlock.statements += generateInheritanceCode()
|
classModel.preDeclarationBlock.statements += generateInheritanceCode()
|
||||||
}
|
}
|
||||||
is IrSimpleFunction -> {
|
is IrSimpleFunction -> {
|
||||||
generateMemberFunction(declaration)?.let { classBlock.statements += it }
|
generateMemberFunction(declaration)?.let { classBlock.statements += it }
|
||||||
@@ -115,6 +115,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
|
|
||||||
functionBody.statements += JsIf(
|
functionBody.statements += JsIf(
|
||||||
JsBinaryOperation(JsBinaryOperator.REF_EQ, instanceVar.makeRef(), JsNullLiteral()),
|
JsBinaryOperation(JsBinaryOperator.REF_EQ, instanceVar.makeRef(), JsNullLiteral()),
|
||||||
|
// TODO: Fix initialization order
|
||||||
jsAssignment(instanceVar.makeRef(), JsNew(classNameRef)).makeStmt()
|
jsAssignment(instanceVar.makeRef(), JsNew(classNameRef)).makeStmt()
|
||||||
)
|
)
|
||||||
functionBody.statements += JsReturn(instanceVar.makeRef())
|
functionBody.statements += JsReturn(instanceVar.makeRef())
|
||||||
@@ -127,7 +128,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
val func = JsFunction(JsFunctionScope(context.currentScope, "Ctor for ${irClass.name}"), JsBlock(), "Ctor for ${irClass.name}")
|
val func = JsFunction(JsFunctionScope(context.currentScope, "Ctor for ${irClass.name}"), JsBlock(), "Ctor for ${irClass.name}")
|
||||||
func.name = className
|
func.name = className
|
||||||
classBlock.statements += func.makeStmt()
|
classBlock.statements += func.makeStmt()
|
||||||
classBlock.statements += generateInheritanceCode()
|
classModel.preDeclarationBlock.statements += generateInheritanceCode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ class JsStaticContext(
|
|||||||
// TODO: use IrSymbol instead of JsName
|
// TODO: use IrSymbol instead of JsName
|
||||||
val classModels = mutableMapOf<JsName, JsClassModel>()
|
val classModels = mutableMapOf<JsName, JsClassModel>()
|
||||||
|
|
||||||
|
val initializerBlock = JsGlobalBlock()
|
||||||
|
|
||||||
fun getNameForSymbol(irSymbol: IrSymbol, context: JsGenerationContext) = nameGenerator.getNameForSymbol(irSymbol, context)
|
fun getNameForSymbol(irSymbol: IrSymbol, context: JsGenerationContext) = nameGenerator.getNameForSymbol(irSymbol, context)
|
||||||
fun getNameForLoop(loop: IrLoop, context: JsGenerationContext) = nameGenerator.getNameForLoop(loop, context)
|
fun getNameForLoop(loop: IrLoop, context: JsGenerationContext) = nameGenerator.getNameForLoop(loop, context)
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
// IGNORE_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
val four: Int by NumberDecrypter
|
val four: Int by NumberDecrypter
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
// IGNORE_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
var result: String by Delegate
|
var result: String by Delegate
|
||||||
|
|||||||
@@ -18,5 +18,6 @@ package org.jetbrains.kotlin.js.backend.ast
|
|||||||
|
|
||||||
class JsClassModel(val name: JsName, val superName: JsName?) {
|
class JsClassModel(val name: JsName, val superName: JsName?) {
|
||||||
val interfaces: MutableSet<JsName> = mutableSetOf()
|
val interfaces: MutableSet<JsName> = mutableSetOf()
|
||||||
|
val preDeclarationBlock = JsGlobalBlock()
|
||||||
val postDeclarationBlock = JsGlobalBlock()
|
val postDeclarationBlock = JsGlobalBlock()
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -3616,6 +3616,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
|||||||
runTest("js/js.translator/testData/box/inheritance/overrideAnyMethods.kt");
|
runTest("js/js.translator/testData/box/inheritance/overrideAnyMethods.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("prototypeOrder.kt")
|
||||||
|
public void testPrototypeOrder() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/inheritance/prototypeOrder.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("valOverride.kt")
|
@TestMetadata("valOverride.kt")
|
||||||
public void testValOverride() throws Exception {
|
public void testValOverride() throws Exception {
|
||||||
runTest("js/js.translator/testData/box/inheritance/valOverride.kt");
|
runTest("js/js.translator/testData/box/inheritance/valOverride.kt");
|
||||||
|
|||||||
+5
@@ -3616,6 +3616,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
|||||||
runTest("js/js.translator/testData/box/inheritance/overrideAnyMethods.kt");
|
runTest("js/js.translator/testData/box/inheritance/overrideAnyMethods.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("prototypeOrder.kt")
|
||||||
|
public void testPrototypeOrder() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/inheritance/prototypeOrder.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("valOverride.kt")
|
@TestMetadata("valOverride.kt")
|
||||||
public void testValOverride() throws Exception {
|
public void testValOverride() throws Exception {
|
||||||
runTest("js/js.translator/testData/box/inheritance/valOverride.kt");
|
runTest("js/js.translator/testData/box/inheritance/valOverride.kt");
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// EXPECTED_REACHABLE_NODES: 1117
|
||||||
|
package foo
|
||||||
|
|
||||||
|
class C: B()
|
||||||
|
open class B: A()
|
||||||
|
open class A
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val c = C()
|
||||||
|
|
||||||
|
if (c !is A) return "FAIL"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user