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,
dependencies: List<ModuleDescriptor> = listOf()
): Result {
// Make compilation stable
// Shadow original list to prevent accidental usage
@Suppress("NAME_SHADOWING")
val files = files.sortedBy { it.virtualFilePath }
val analysisResult =
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.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> {
@@ -25,12 +26,13 @@ class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatemen
override fun visitField(declaration: IrField, context: JsGenerationContext): JsStatement {
val fieldName = context.getNameForSymbol(declaration.symbol)
val initExpression =
declaration.initializer?.accept(IrElementToJsExpressionTransformer(), context) ?: JsPrefixOperation(
JsUnaryOperator.VOID,
JsIntLiteral(1)
)
return JsVars(JsVars.JsVar(fieldName, initExpression))
if (declaration.initializer != null) {
val initializer = declaration.initializer!!.accept(IrElementToJsExpressionTransformer(), context)
context.staticContext.initializerBlock.statements += jsAssignment(fieldName.makeRef(), initializer).makeStmt()
}
return JsVars(JsVars.JsVar(fieldName))
}
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.declarations.IrModuleFragment
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.utils.DFS
class IrModuleToJsTransformer(val backendContext: JsIrBackendContext) : BaseIrElementToJsNodeTransformer<JsNode, Nothing?> {
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(throwableName, Namer.JS_ERROR))
val preDeclarationBlock = JsGlobalBlock()
val postDeclarationBlock = JsGlobalBlock()
program.globalBlock.statements += preDeclarationBlock
declaration.files.forEach {
program.globalBlock.statements.add(it.accept(IrFileToJsTransformer(), rootContext))
}
// 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
}
private fun addPostDeclarations(classModels: Map<JsName, JsClassModel>): List<JsStatement> {
val statements = mutableListOf<JsStatement>()
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>
private fun processClassModels(
classModelMap: Map<JsName, JsClassModel>,
preDeclarationBlock: JsGlobalBlock,
postDeclarationBlock: JsGlobalBlock
) {
if (visited.add(name)) {
classModels[name]?.run {
superName?.let { addPostDeclaration(it, visited, statements, classModels) }
interfaces.forEach { addPostDeclaration(it, visited, statements, classModels) }
statements += postDeclarationBlock.statements
val declarationHandler = object : DFS.AbstractNodeHandler<JsName, Unit>() {
override fun result() {}
override fun afterChildren(current: JsName) {
classModelMap[current]?.let {
preDeclarationBlock.statements += it.preDeclarationBlock.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) {
is IrConstructor -> {
classBlock.statements += declaration.accept(transformer, context)
classBlock.statements += generateInheritanceCode()
classModel.preDeclarationBlock.statements += generateInheritanceCode()
}
is IrSimpleFunction -> {
generateMemberFunction(declaration)?.let { classBlock.statements += it }
@@ -115,6 +115,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
functionBody.statements += JsIf(
JsBinaryOperation(JsBinaryOperator.REF_EQ, instanceVar.makeRef(), JsNullLiteral()),
// TODO: Fix initialization order
jsAssignment(instanceVar.makeRef(), JsNew(classNameRef)).makeStmt()
)
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}")
func.name = className
classBlock.statements += func.makeStmt()
classBlock.statements += generateInheritanceCode()
classModel.preDeclarationBlock.statements += generateInheritanceCode()
}
}
@@ -25,6 +25,8 @@ class JsStaticContext(
// TODO: use IrSymbol instead of JsName
val classModels = mutableMapOf<JsName, JsClassModel>()
val initializerBlock = JsGlobalBlock()
fun getNameForSymbol(irSymbol: IrSymbol, context: JsGenerationContext) = nameGenerator.getNameForSymbol(irSymbol, context)
fun getNameForLoop(loop: IrLoop, context: JsGenerationContext) = nameGenerator.getNameForLoop(loop, context)
}
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
import kotlin.reflect.KProperty
val four: Int by NumberDecrypter
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
import kotlin.reflect.KProperty
var result: String by Delegate
@@ -18,5 +18,6 @@ package org.jetbrains.kotlin.js.backend.ast
class JsClassModel(val name: JsName, val superName: JsName?) {
val interfaces: MutableSet<JsName> = mutableSetOf()
val preDeclarationBlock = JsGlobalBlock()
val postDeclarationBlock = JsGlobalBlock()
}
@@ -3616,6 +3616,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
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")
public void testValOverride() throws Exception {
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");
}
@TestMetadata("prototypeOrder.kt")
public void testPrototypeOrder() throws Exception {
runTest("js/js.translator/testData/box/inheritance/prototypeOrder.kt");
}
@TestMetadata("valOverride.kt")
public void testValOverride() throws Exception {
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"
}