Prevent JS AST nodes of several types to be shared

This commit is contained in:
Alexey Andreev
2017-05-05 17:42:43 +03:00
parent 3077a0f640
commit e9a2c8c0f1
73 changed files with 537 additions and 485 deletions
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.js.inline.util.collectBreakContinueTargets
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.utils.DFS
class CoroutineBodyTransformer(private val program: JsProgram, private val context: CoroutineTransformationContext) : RecursiveJsVisitor() {
class CoroutineBodyTransformer(private val context: CoroutineTransformationContext) : RecursiveJsVisitor() {
private val entryBlock = context.entryBlock
private val globalCatchBlock = context.globalCatchBlock
private var currentBlock = entryBlock
@@ -55,7 +55,7 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
currentBlock.statements += JsReturn()
val graph = entryBlock.buildGraph(globalCatchBlock)
val orderedBlocks = DFS.topologicalOrder(listOf(entryBlock)) { graph[it].orEmpty() }
orderedBlocks.replaceCoroutineFlowStatements(context, program)
orderedBlocks.replaceCoroutineFlowStatements(context)
return orderedBlocks
}
@@ -121,7 +121,7 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
currentStatements += stateAndJump(bodyEntryBlock, x)
currentBlock = bodyEntryBlock
if (x.condition != JsLiteral.TRUE) {
if (!JsLiteral.isTrueBoolean(x.condition)) {
currentStatements += JsIf(JsAstUtils.notOptimized(x.condition), JsBlock(stateAndJump(successor, x))).apply { source = x.source }
}
@@ -143,7 +143,7 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
x.body.accept(this)
}
if (x.condition != JsLiteral.TRUE) {
if (!JsLiteral.isTrueBoolean(x.condition)) {
val jsIf = JsIf(JsAstUtils.notOptimized(x.condition), JsBlock(stateAndJump(successor, x))).apply { source = x.source }
currentStatements.add(jsIf)
}
@@ -168,7 +168,7 @@ class CoroutineBodyTransformer(private val program: JsProgram, private val conte
currentStatements += stateAndJump(bodyEntryBlock, x)
currentBlock = bodyEntryBlock
if (x.condition != null && x.condition != JsLiteral.TRUE) {
if (x.condition != null && !JsLiteral.isTrueBoolean(x.condition)) {
currentStatements += JsIf(JsAstUtils.notOptimized(x.condition), JsBlock(stateAndJump(successor, x))).apply { source = x.source }
}
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.js.inline.util.getInnerFunction
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
class CoroutineFunctionTransformer(private val program: JsProgram, private val function: JsFunction, name: String?) {
class CoroutineFunctionTransformer(private val function: JsFunction, name: String?) {
private val innerFunction = function.getInnerFunction()
private val functionWithBody = innerFunction ?: function
private val body = functionWithBody.body
@@ -35,7 +35,7 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
fun transform(): List<JsStatement> {
val context = CoroutineTransformationContext(function.scope, function)
val bodyTransformer = CoroutineBodyTransformer(program, context)
val bodyTransformer = CoroutineBodyTransformer(context)
bodyTransformer.preProcess(body)
body.statements.forEach { it.accept(bodyTransformer) }
val coroutineBlocks = bodyTransformer.postProcess()
@@ -83,11 +83,11 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
constructor.body.statements.run {
val baseClass = context.metadata.baseClassRef.deepCopy()
this += JsInvocation(Namer.getFunctionCallRef(baseClass), JsLiteral.THIS, interceptorRef).source(psiElement).makeStmt()
this += JsInvocation(Namer.getFunctionCallRef(baseClass), JsThisRef(), interceptorRef).source(psiElement).makeStmt()
if (controllerName != null) {
assignToField(context.controllerFieldName, controllerName.makeRef(), psiElement)
}
assignToField(context.metadata.exceptionStateName, program.getNumberLiteral(globalCatchBlockIndex), psiElement)
assignToField(context.metadata.exceptionStateName, JsIntLiteral(globalCatchBlockIndex), psiElement)
if (context.metadata.hasReceiver) {
assignToField(context.receiverFieldName, context.receiverFieldName.makeRef(), psiElement)
}
@@ -117,7 +117,7 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
propertyInitializers +=
JsPropertyInitializer(JsNameRef(Namer.METADATA_CLASS_KIND),
JsNameRef(Namer.CLASS_KIND_CLASS, JsNameRef(Namer.CLASS_KIND_ENUM, Namer.KOTLIN_NAME)))
propertyInitializers += JsPropertyInitializer(JsNameRef(Namer.METADATA_SIMPLE_NAME), JsLiteral.NULL)
propertyInitializers += JsPropertyInitializer(JsNameRef(Namer.METADATA_SIMPLE_NAME), JsNullLiteral())
propertyInitializers += JsPropertyInitializer(JsNameRef(Namer.METADATA_SUPERTYPES), JsArrayLiteral(listOf(baseClassRefRef)))
}
@@ -150,13 +150,13 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
val psiElement = context.metadata.psiElement
val instantiation = JsNew(className.makeRef()).apply { source = psiElement }
if (context.metadata.hasReceiver) {
instantiation.arguments += JsLiteral.THIS
instantiation.arguments += JsThisRef()
}
val parameters = function.parameters + innerFunction?.parameters.orEmpty()
instantiation.arguments += parameters.dropLast(1).map { it.name.makeRef() }
if (function.coroutineMetadata!!.hasController) {
instantiation.arguments += JsLiteral.THIS
instantiation.arguments += JsThisRef()
}
instantiation.arguments += parameters.last().name.makeRef()
@@ -167,7 +167,7 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
val instanceName = JsScope.declareTemporaryName("instance")
functionWithBody.body.statements += JsAstUtils.newVar(instanceName, instantiation)
val invokeResume = JsReturn(JsInvocation(JsNameRef(context.metadata.doResumeName, instanceName.makeRef()), JsLiteral.NULL)
val invokeResume = JsReturn(JsInvocation(JsNameRef(context.metadata.doResumeName, instanceName.makeRef()), JsNullLiteral())
.source(psiElement))
functionWithBody.body.statements += JsIf(
@@ -181,28 +181,28 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
blocks: List<CoroutineBlock>
): List<JsStatement> {
val indexOfGlobalCatch = blocks.indexOf(context.globalCatchBlock)
val stateRef = JsNameRef(context.metadata.stateName, JsLiteral.THIS)
val stateRef = JsNameRef(context.metadata.stateName, JsThisRef())
val isFromGlobalCatch = JsAstUtils.equality(stateRef, program.getNumberLiteral(indexOfGlobalCatch))
val isFromGlobalCatch = JsAstUtils.equality(stateRef, JsIntLiteral(indexOfGlobalCatch))
val catch = JsCatch(functionWithBody.scope, "e")
val continueWithException = JsBlock(
JsAstUtils.assignment(stateRef.deepCopy(), JsNameRef(context.metadata.exceptionStateName, JsLiteral.THIS)).makeStmt(),
JsAstUtils.assignment(JsNameRef(context.metadata.exceptionName, JsLiteral.THIS),
JsAstUtils.assignment(stateRef.deepCopy(), JsNameRef(context.metadata.exceptionStateName, JsThisRef())).makeStmt(),
JsAstUtils.assignment(JsNameRef(context.metadata.exceptionName, JsThisRef()),
catch.parameter.name.makeRef()).makeStmt()
)
catch.body = JsBlock(JsIf(isFromGlobalCatch, JsThrow(catch.parameter.name.makeRef()), continueWithException))
val throwResultRef = JsNameRef(context.metadata.exceptionName, JsLiteral.THIS)
val throwResultRef = JsNameRef(context.metadata.exceptionName, JsThisRef())
context.globalCatchBlock.statements += JsThrow(throwResultRef)
val cases = blocks.withIndex().map { (index, block) ->
JsCase().apply {
caseExpression = program.getNumberLiteral(index)
caseExpression = JsIntLiteral(index)
statements += block.statements
}
}
val switchStatement = JsSwitch(stateRef.deepCopy(), cases)
val loop = JsDoWhile(JsLiteral.TRUE, JsTry(JsBlock(switchStatement), catch, null))
val loop = JsDoWhile(JsBooleanLiteral(true), JsTry(JsBlock(switchStatement), catch, null))
return listOf(loop)
}
@@ -217,7 +217,7 @@ class CoroutineFunctionTransformer(private val program: JsProgram, private val f
}
private fun MutableList<JsStatement>.assignToField(fieldName: JsName, value: JsExpression, psiElement: PsiElement?) {
this += JsAstUtils.assignment(JsNameRef(fieldName, JsLiteral.THIS), value).source(psiElement).makeStmt()
this += JsAstUtils.assignment(JsNameRef(fieldName, JsThisRef()), value).source(psiElement).makeStmt()
}
private fun MutableList<JsStatement>.assignToPrototype(fieldName: JsName, value: JsExpression) {
@@ -113,7 +113,7 @@ fun JsNode.collectNodesToSplit(breakContinueTargets: Map<JsContinue, JsStatement
return nodes
}
fun List<CoroutineBlock>.replaceCoroutineFlowStatements(context: CoroutineTransformationContext, program: JsProgram) {
fun List<CoroutineBlock>.replaceCoroutineFlowStatements(context: CoroutineTransformationContext) {
val blockIndexes = withIndex().associate { (index, block) -> Pair(block, index) }
val blockReplacementVisitor = object : JsVisitorWithContextImpl() {
@@ -121,7 +121,7 @@ fun List<CoroutineBlock>.replaceCoroutineFlowStatements(context: CoroutineTransf
val target = x.targetBlock
if (target != null) {
val lhs = JsNameRef(context.metadata.stateName, JsAstUtils.stateMachineReceiver())
val rhs = program.getNumberLiteral(blockIndexes[target]!!)
val rhs = JsIntLiteral(blockIndexes[target]!!)
ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs).source(x.source)).apply {
targetBlock = true
})
@@ -130,7 +130,7 @@ fun List<CoroutineBlock>.replaceCoroutineFlowStatements(context: CoroutineTransf
val exceptionTarget = x.targetExceptionBlock
if (exceptionTarget != null) {
val lhs = JsNameRef(context.metadata.exceptionStateName, JsAstUtils.stateMachineReceiver())
val rhs = program.getNumberLiteral(blockIndexes[exceptionTarget]!!)
val rhs = JsIntLiteral(blockIndexes[exceptionTarget]!!)
ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs).source(x.source)).apply {
targetExceptionBlock = true
})
@@ -140,7 +140,7 @@ fun List<CoroutineBlock>.replaceCoroutineFlowStatements(context: CoroutineTransf
if (finallyPath != null) {
if (finallyPath.isNotEmpty()) {
val lhs = JsNameRef(context.metadata.finallyPathName, JsAstUtils.stateMachineReceiver())
val rhs = JsArrayLiteral(finallyPath.map { program.getNumberLiteral(blockIndexes[it]!!) })
val rhs = JsArrayLiteral(finallyPath.map { JsIntLiteral(blockIndexes[it]!!) })
ctx.replaceMe(JsExpressionStatement(JsAstUtils.assignment(lhs, rhs).source(x.source)).apply {
this.finallyPath = true
})
@@ -208,8 +208,8 @@ private fun CoroutineBlock.collectFinallyPaths(): List<List<CoroutineBlock>> {
fun JsBlock.replaceSpecialReferences(context: CoroutineTransformationContext) {
val visitor = object : JsVisitorWithContextImpl() {
override fun endVisit(x: JsLiteral.JsThisRef, ctx: JsContext<in JsNode>) {
ctx.replaceMe(JsNameRef(context.receiverFieldName, JsLiteral.THIS))
override fun endVisit(x: JsThisRef, ctx: JsContext<in JsNode>) {
ctx.replaceMe(JsNameRef(context.receiverFieldName, JsThisRef()))
}
override fun visit(x: JsFunction, ctx: JsContext<*>) = false
@@ -217,7 +217,7 @@ fun JsBlock.replaceSpecialReferences(context: CoroutineTransformationContext) {
override fun endVisit(x: JsNameRef, ctx: JsContext<in JsNode>) {
when {
x.coroutineReceiver -> {
ctx.replaceMe(JsLiteral.THIS)
ctx.replaceMe(JsThisRef())
}
x.coroutineController -> {
@@ -254,7 +254,7 @@ fun JsBlock.replaceLocalVariables(context: CoroutineTransformationContext, local
val nameMap = freeVars.associate { it to JsScope.declareTemporaryName(it.ident) }
for (freeVar in freeVars) {
wrapperFunction.parameters += JsParameter(nameMap[freeVar]!!)
wrapperInvocation.arguments += JsNameRef(context.getFieldName(freeVar), JsLiteral.THIS)
wrapperInvocation.arguments += JsNameRef(context.getFieldName(freeVar), JsThisRef())
}
x.body = replaceNames(x.body, nameMap.mapValues { it.value.makeRef() })
ctx.replaceMe(wrapperInvocation)
@@ -264,7 +264,7 @@ fun JsBlock.replaceLocalVariables(context: CoroutineTransformationContext, local
override fun endVisit(x: JsNameRef, ctx: JsContext<in JsNode>) {
if (x.qualifier == null && x.name in localVariables) {
val fieldName = context.getFieldName(x.name!!)
ctx.replaceMe(JsNameRef(fieldName, JsLiteral.THIS).source(x.source))
ctx.replaceMe(JsNameRef(fieldName, JsThisRef()).source(x.source))
}
}
@@ -273,7 +273,7 @@ fun JsBlock.replaceLocalVariables(context: CoroutineTransformationContext, local
val fieldName = context.getFieldName(it.name)
val initExpression = it.initExpression
if (initExpression != null) {
JsAstUtils.assignment(JsNameRef(fieldName, JsLiteral.THIS), it.initExpression)
JsAstUtils.assignment(JsNameRef(fieldName, JsThisRef()), it.initExpression)
}
else {
null
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.js.backend.ast.metadata.coroutineMetadata
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
class CoroutineTransformer(private val program: JsProgram) : JsVisitorWithContextImpl() {
class CoroutineTransformer() : JsVisitorWithContextImpl() {
private val additionalStatementsByNode = mutableMapOf<JsNode, List<JsStatement>>()
override fun endVisit(x: JsExpressionStatement, ctx: JsContext<in JsStatement>) {
@@ -44,13 +44,13 @@ class CoroutineTransformer(private val program: JsProgram) : JsVisitorWithContex
val function = rhs as? JsFunction ?: InlineMetadata.decompose(rhs)?.function
if (function?.coroutineMetadata != null) {
val name = ((lhs as? JsNameRef)?.name ?: function.name)?.ident
additionalStatementsByNode[x] = CoroutineFunctionTransformer(program, function, name).transform()
additionalStatementsByNode[x] = CoroutineFunctionTransformer(function, name).transform()
return false
}
}
else if (expression is JsFunction) {
if (expression.coroutineMetadata != null) {
additionalStatementsByNode[x] = CoroutineFunctionTransformer(program, expression, expression.name?.ident).transform()
additionalStatementsByNode[x] = CoroutineFunctionTransformer(expression, expression.name?.ident).transform()
return false
}
}
@@ -63,7 +63,7 @@ class CoroutineTransformer(private val program: JsProgram) : JsVisitorWithContex
val function = initExpression as? JsFunction ?: InlineMetadata.decompose(initExpression)?.function
if (function?.coroutineMetadata != null) {
val name = x.name.ident
additionalStatementsByNode[x] = CoroutineFunctionTransformer(program, function, name).transform()
additionalStatementsByNode[x] = CoroutineFunctionTransformer(function, name).transform()
return false
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -139,7 +139,7 @@ internal class ExpressionDecomposer private constructor(
}
body = additionalStatements.toStatement()
test = JsLiteral.TRUE
test = JsBooleanLiteral(true)
}
}
@@ -404,8 +404,8 @@ internal open class JsExpressionVisitor() : JsVisitorWithContextImpl() {
override fun visit(x: JsCase, ctx: JsContext<JsNode>): Boolean = false
override fun visit(x: JsDefault, ctx: JsContext<JsNode>): Boolean = false
override fun visit(x: JsEmpty, ctx: JsContext<JsNode>): Boolean = false
override fun visit(x: JsLiteral.JsBooleanLiteral, ctx: JsContext<JsNode>): Boolean = false
override fun visit(x: JsLiteral.JsThisRef, ctx: JsContext<JsNode>): Boolean = false
override fun visit(x: JsBooleanLiteral, ctx: JsContext<JsNode>): Boolean = false
override fun visit(x: JsThisRef, ctx: JsContext<JsNode>): Boolean = false
override fun visit(x: JsNullLiteral, ctx: JsContext<JsNode>): Boolean = false
override fun visit(x: JsNumberLiteral, ctx: JsContext<JsNode>): Boolean = false
override fun visit(x: JsRegExp, ctx: JsContext<JsNode>): Boolean = false
@@ -97,7 +97,7 @@ private constructor(
if (!hasThisReference(block)) return
var thisReplacement = getThisReplacement(call)
if (thisReplacement == null || thisReplacement is JsLiteral.JsThisRef) return
if (thisReplacement == null || thisReplacement is JsThisRef) return
val thisName = JsScope.declareTemporaryName(getThisAlias())
namingContext.newVar(thisName, thisReplacement)
@@ -195,7 +195,7 @@ private constructor(
}
private fun hasThisReference(body: JsBlock): Boolean {
val thisRefs = collectInstances(JsLiteral.JsThisRef::class.java, body)
val thisRefs = collectInstances(JsThisRef::class.java, body)
return !thisRefs.isEmpty()
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -78,7 +78,7 @@ internal class DeadCodeElimination(private val root: JsStatement) {
visitLoop(x.body) {
val condition = x.condition
condition !is JsLiteral.JsBooleanLiteral || !condition.value
condition !is JsBooleanLiteral || !condition.value
}
}
@@ -30,7 +30,7 @@ object LabeledBlockToDoWhileTransformation {
object : JsVisitorWithContextImpl() {
override fun endVisit(x: JsLabel, ctx: JsContext<JsNode>) {
if (x.statement is JsBlock) {
x.statement = JsDoWhile(JsLiteral.FALSE, x.statement)
x.statement = JsDoWhile(JsBooleanLiteral(false), x.statement)
}
super.endVisit(x, ctx)
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.js.backend.ast.metadata.sideEffects
import org.jetbrains.kotlin.js.backend.ast.metadata.synthetic
import org.jetbrains.kotlin.js.inline.util.collectFreeVariables
import org.jetbrains.kotlin.js.inline.util.collectLocalVariables
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.splitToRanges
@@ -165,7 +166,7 @@ internal class TemporaryVariableElimination(private val function: JsFunction) {
useVariable(name)
if (name !in currentScope) {
// Variable is inconsistent, i.e. it's defined after it's used.
assignVariable(name, JsLiteral.UNDEFINED)
assignVariable(name, Namer.getUndefinedExpression())
}
return
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -66,8 +66,8 @@ class WhileConditionFolding(val body: JsBlock) {
if (condition != null) {
statement.body = remove(statement.body)
val existingCondition = statement.condition
statement.condition = when (existingCondition) {
JsLiteral.TRUE -> condition
statement.condition = when {
JsLiteral.isTrueBoolean(existingCondition) -> condition
else -> combine(existingCondition, condition)
}
changed = true
@@ -105,7 +105,7 @@ class WhileConditionFolding(val body: JsBlock) {
// therefore for single `break` we should return `false`.
is JsBreak -> {
val target = statement.label?.name
if (label == target) JsLiteral.FALSE else null
if (label == target) JsBooleanLiteral(false) else null
}
// Code like this
@@ -138,11 +138,11 @@ class WhileConditionFolding(val body: JsBlock) {
val then = statement.thenStatement
if (statement.elseStatement == null) {
val nextCondition = extractCondition(then, label)
val result: JsExpression? = when (nextCondition) {
val result: JsExpression? = when {
// Just a little optimization. When inner statement is a single `break`, `nextCondition` would be false.
// However, `A || false` can be rewritten as simply `A`
JsLiteral.FALSE -> JsAstUtils.notOptimized(statement.ifExpression)
null -> null
nextCondition == null -> null
JsLiteral.isFalseBoolean(nextCondition) -> JsAstUtils.notOptimized(statement.ifExpression)
else -> JsAstUtils.or(JsAstUtils.notOptimized(statement.ifExpression), nextCondition)
}
result
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.js.inline.util.rewriters
import org.jetbrains.kotlin.js.backend.ast.*
class ThisReplacingVisitor(private val thisReplacement: JsExpression) : JsVisitorWithContextImpl() {
override fun endVisit(x: JsLiteral.JsThisRef, ctx: JsContext<JsNode>) {
override fun endVisit(x: JsThisRef, ctx: JsContext<JsNode>) {
ctx.replaceMe(thisReplacement)
}