Fix initialization order

* put inheritance code in the beginning
 * put top-level initializer after any declaration
This commit is contained in:
Roman Artemev
2018-07-24 18:53:44 +03:00
committed by Roman Artemev
parent cff066f702
commit 0c6256d003
11 changed files with 69 additions and 41 deletions
@@ -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())
@@ -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 {
@@ -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)
} }
} }
@@ -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()
} }
@@ -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");
@@ -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"
}