JS backend: refactor LoopTranslator, add function translateForExpression

This commit is contained in:
Michael Nedzelsky
2014-08-22 20:11:03 +04:00
parent 478d3baf59
commit 40e5d6c8f5
2 changed files with 160 additions and 303 deletions
@@ -32,7 +32,6 @@ import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.declaration.ClassTranslator;
import org.jetbrains.k2js.translate.expression.loopTranslator.ForTranslator;
import org.jetbrains.k2js.translate.expression.loopTranslator.LoopTranslatorPackage;
import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.general.TranslatorVisitor;
@@ -428,7 +427,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@NotNull
public JsNode visitForExpression(@NotNull JetForExpression expression,
@NotNull TranslationContext context) {
return ForTranslator.OBJECT$.translate(expression, context).source(expression);
return LoopTranslatorPackage.translateForExpression(expression, context).source(expression);
}
@Override
@@ -16,357 +16,215 @@
package org.jetbrains.k2js.translate.expression.loopTranslator
import org.jetbrains.jet.lang.psi.JetWhileExpressionBase
import org.jetbrains.k2js.translate.context.TranslationContext
import com.google.dart.compiler.backend.js.ast.JsNode
import com.google.dart.compiler.backend.js.ast.JsBlock
import org.jetbrains.k2js.translate.general.Translation
import com.google.dart.compiler.backend.js.ast.JsIf
import org.jetbrains.k2js.translate.utils.JsAstUtils
import com.google.dart.compiler.backend.js.ast.JsLiteral
import com.google.dart.compiler.backend.js.ast.JsBreak
import com.google.dart.compiler.backend.js.ast.JsDoWhile
import com.google.dart.compiler.backend.js.ast.JsWhile
import com.google.dart.compiler.backend.js.ast.JsStatement
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.lang.psi.JetForExpression
import com.google.dart.compiler.backend.js.ast.JsExpression
import org.jetbrains.k2js.translate.context.TemporaryVariable
import org.jetbrains.jet.lang.psi.JetBinaryExpression
import org.jetbrains.k2js.translate.utils.PsiUtils.getLoopBody
import org.jetbrains.k2js.translate.utils.PsiUtils.getLoopParameter
import org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange
import org.jetbrains.k2js.translate.utils.TranslationUtils
import com.google.dart.compiler.backend.js.ast.JsFor
import com.google.dart.compiler.backend.js.ast.JsVars
import org.jetbrains.jet.lexer.JetTokens
import com.google.dart.compiler.backend.js.ast.JsUnaryOperator
import com.google.dart.compiler.backend.js.ast.JsPostfixOperation
import org.jetbrains.k2js.translate.utils.TemporariesUtils.temporariesInitialization
import com.google.dart.compiler.backend.js.ast.JsNameRef
import org.jetbrains.k2js.translate.utils.BindingUtils.*
import org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassDescriptorForType
import org.jetbrains.k2js.translate.intrinsic.functions.factories.CompositeFIF
import com.google.dart.compiler.backend.js.ast.JsArrayAccess
import com.google.dart.compiler.backend.js.ast.JsPrefixOperation
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall
import com.google.dart.compiler.backend.js.ast.*
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.lang.psi.*
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall
import org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassDescriptorForType
import org.jetbrains.jet.lexer.JetTokens
import org.jetbrains.k2js.translate.callTranslator.CallTranslator
import org.jetbrains.k2js.translate.general.AbstractTranslator
import com.google.dart.compiler.backend.js.ast.JsName
import org.jetbrains.jet.lang.psi.JetMultiDeclaration
import org.jetbrains.k2js.translate.context.TemporaryVariable
import org.jetbrains.k2js.translate.context.TranslationContext
import org.jetbrains.k2js.translate.expression.MultiDeclarationTranslator
import org.jetbrains.k2js.translate.general.AbstractTranslator
import org.jetbrains.k2js.translate.general.Translation
import org.jetbrains.k2js.translate.intrinsic.functions.factories.CompositeFIF
import org.jetbrains.k2js.translate.utils.BindingUtils.*
import org.jetbrains.k2js.translate.utils.JsAstUtils.*
import org.jetbrains.k2js.translate.utils.PsiUtils.*
import org.jetbrains.k2js.translate.utils.TemporariesUtils.temporariesInitialization
import org.jetbrains.k2js.translate.utils.TranslationUtils
public fun createWhile(doWhile: Boolean, expression: JetWhileExpressionBase, context: TranslationContext): JsNode {
val conditionExpression = expression.getCondition() ?:
throw IllegalArgumentException("condition expression should not be null: ${expression.getText()}")
val conditionBlock = JsBlock()
var jsCondition = Translation.translateAsExpression(conditionExpression, context, conditionBlock)
var bodyStatement = translateNullableExpressionAsNotNullStatement(expression.getBody(), context)
val body = expression.getBody()
var bodyStatement =
if (body != null)
Translation.translateAsStatement(body, context)
else
context.program().getEmptyStatement()
if (!conditionBlock.isEmpty()) {
val IfStatement = JsIf(JsAstUtils.not(jsCondition), JsBreak())
val bodyBlock = JsAstUtils.convertToBlock(bodyStatement)
val breakIfConditionIsFalseStatement = JsIf(not(jsCondition), JsBreak())
val bodyBlock = convertToBlock(bodyStatement)
jsCondition = JsLiteral.TRUE
if (doWhile) {
// translate to: tmpSecondRun = false; do { if(tmpSecondRun) { <expr> if(!tmpExprVar) break; } else tmpSecondRun=true; <body> } while(true)
val secondRun = context.declareTemporary(JsLiteral.FALSE)
context.addStatementToCurrentBlock(secondRun.assignmentExpression().makeStmt())
conditionBlock.getStatements().add(IfStatement)
bodyBlock.getStatements().add(0, JsIf(secondRun.reference(),
conditionBlock,
JsAstUtils.assignment(secondRun.reference(), JsLiteral.TRUE).makeStmt()))
conditionBlock.getStatements().add(breakIfConditionIsFalseStatement)
val ifStatement = JsIf(secondRun.reference(), conditionBlock, assignment(secondRun.reference(), JsLiteral.TRUE).makeStmt())
bodyBlock.getStatements().add(0, ifStatement)
}
else {
// translate to: while (true) { <expr> if(!tmpExprVar) break; <body> }
conditionBlock.getStatements().add(IfStatement)
conditionBlock.getStatements().add(breakIfConditionIsFalseStatement)
bodyBlock.getStatements().addAll(0, conditionBlock.getStatements())
}
bodyStatement = bodyBlock
}
val result = if (doWhile) JsDoWhile() else JsWhile()
result.setCondition(jsCondition)
result.setBody(bodyStatement)
return result.source(expression)!!
}
private fun translateNullableExpressionAsNotNullStatement(nullableExpression: JetExpression?, context: TranslationContext): JsStatement =
if (nullableExpression == null)
context.program().getEmptyStatement()
else
Translation.translateAsStatement(nullableExpression, context)
public fun translateForExpression(expression: JetForExpression, context: TranslationContext): JsStatement {
val loopRange = getLoopRange(expression)
val rangeType = getTypeForExpression(context.bindingContext(), loopRange)
// TODO: implement reverse semantics
public class RangeLiteralForTranslator private(forExpression: JetForExpression, context: TranslationContext) : ForTranslator(forExpression, context) {
private val rangeStart: JsExpression
private val rangeEnd: TemporaryVariable
{
val loopRange = getLoopRange(expression)
assert(loopRange is JetBinaryExpression)
val loopRangeAsBinary = (loopRange as JetBinaryExpression)
val startBlock = JsBlock()
var rangeStartExpression = TranslationUtils.translateLeftExpression(context, loopRangeAsBinary, startBlock)
val endBlock = JsBlock()
val rightExpression = TranslationUtils.translateRightExpression(context(), loopRangeAsBinary, endBlock)
if (TranslationUtils.isCacheNeeded(rangeStartExpression)) {
val startVar = context.declareTemporary(rangeStartExpression)
rangeStartExpression = startVar.reference()
context.addStatementToCurrentBlock(startVar.assignmentExpression().makeStmt())
}
rangeStart = rangeStartExpression
context.addStatementsToCurrentBlockFrom(startBlock)
context.addStatementsToCurrentBlockFrom(endBlock)
rangeEnd = context.declareTemporary(rightExpression)
fun isForOverRange(): Boolean {
//TODO: better check
//TODO: long range?
return getClassDescriptorForType(rangeType).getName().asString() == "IntRange"
}
private fun translate(): JsStatement {
context().addStatementToCurrentBlock(temporariesInitialization(rangeEnd).makeStmt())
return JsFor(initExpression(), getCondition(), getIncrExpression(), translateBody(null))
fun isForOverRangeLiteral(): Boolean =
loopRange is JetBinaryExpression && loopRange.getOperationToken() == JetTokens.RANGE && isForOverRange()
fun isForOverArray(): Boolean {
//TODO: better check
//TODO: IMPORTANT!
return getClassDescriptorForType(rangeType).getName().asString() == "Array" ||
getClassDescriptorForType(rangeType).getName().asString() == "IntArray"
}
private fun initExpression(): JsVars {
return JsAstUtils.newVar(parameterName, rangeStart)
}
val multiParameter: JetMultiDeclaration? = expression.getMultiParameter();
private fun getCondition(): JsExpression {
return JsAstUtils.lessThanEq(parameterName.makeRef(), rangeEnd.reference())
}
private fun getIncrExpression(): JsExpression {
return JsPostfixOperation(JsUnaryOperator.INC, parameterName.makeRef())
}
class object {
public fun doTranslate(expression: JetForExpression, context: TranslationContext): JsStatement {
return (RangeLiteralForTranslator(expression, context).translate())
}
public fun isApplicable(expression: JetForExpression, context: TranslationContext): Boolean {
val loopRange = getLoopRange(expression)
if (loopRange !is JetBinaryExpression) {
return false
}
val isRangeToOperation = (loopRange as JetBinaryExpression).getOperationToken() == JetTokens.RANGE
return isRangeToOperation && RangeForTranslator.isApplicable(expression, context)
}
}
}
public class RangeForTranslator private(forExpression: JetForExpression, context: TranslationContext) : ForTranslator(forExpression, context) {
private val rangeExpression: TemporaryVariable
private val start: TemporaryVariable
private val end: TemporaryVariable
private val increment: TemporaryVariable
{
rangeExpression = context.declareTemporary(Translation.translateAsExpression(getLoopRange(expression), context))
start = context().declareTemporary(getProperty("start"))
end = context().declareTemporary(getProperty("end"))
increment = context().declareTemporary(getProperty("increment"))
}
private fun translate(): JsStatement {
context().addStatementToCurrentBlock(temporariesInitialization(rangeExpression, start, end, increment).makeStmt())
return generateForExpression()
}
private fun generateForExpression(): JsFor {
val result = JsFor(initExpression(), getCondition(), getIncrExpression())
result.setBody(translateBody(null))
return result
}
private fun initExpression(): JsVars {
return JsAstUtils.newVar(parameterName, start.reference())
}
private fun getCondition(): JsExpression {
return JsAstUtils.lessThanEq(parameterName.makeRef(), end.reference())
}
private fun getIncrExpression(): JsExpression {
return JsAstUtils.addAssign(parameterName.makeRef(), increment.reference())
}
private fun getProperty(funName: String): JsExpression {
return JsNameRef(funName, rangeExpression.reference())
}
class object {
public fun doTranslate(expression: JetForExpression, context: TranslationContext): JsStatement {
return (RangeForTranslator(expression, context).translate())
}
public fun isApplicable(expression: JetForExpression, context: TranslationContext): Boolean {
val loopRange = getLoopRange(expression)
val rangeType = getTypeForExpression(context.bindingContext(), loopRange)
//TODO: better check
//TODO: long range?
return getClassDescriptorForType(rangeType).getName().asString() == "IntRange"
}
}
}
public class ArrayForTranslator private(forExpression: JetForExpression, context: TranslationContext) : ForTranslator(forExpression, context) {
private val loopRange: TemporaryVariable
private val end: TemporaryVariable
private val index: TemporaryVariable
{
loopRange = context.declareTemporary(Translation.translateAsExpression(getLoopRange(expression), context))
val length = CompositeFIF.LENGTH_PROPERTY_INTRINSIC.apply(loopRange.reference(), listOf<JsExpression>(), context())
end = context().declareTemporary(length)
index = context().declareTemporary(program().getNumberLiteral(0))
}
private fun translate(): JsStatement {
context().addStatementToCurrentBlock(temporariesInitialization(loopRange, end).makeStmt())
return JsFor(getInitExpression(), getCondition(), getIncrementExpression(), getBody())
}
private fun getBody(): JsStatement {
val arrayAccess = JsArrayAccess(loopRange.reference(), index.reference())
return translateBody(arrayAccess)
}
private fun getInitExpression(): JsVars {
return JsAstUtils.newVar(index.name(), program().getNumberLiteral(0))
}
private fun getCondition(): JsExpression {
return JsAstUtils.inequality(index.reference(), end.reference())
}
private fun getIncrementExpression(): JsExpression {
return JsPrefixOperation(JsUnaryOperator.INC, index.reference())
}
class object {
public fun doTranslate(expression: JetForExpression, context: TranslationContext): JsStatement {
return (ArrayForTranslator(expression, context).translate())
}
public fun isApplicable(expression: JetForExpression, context: TranslationContext): Boolean {
val loopRange = getLoopRange(expression)
val rangeType = getTypeForExpression(context.bindingContext(), loopRange)
//TODO: better check
//TODO: IMPORTANT!
return getClassDescriptorForType(rangeType).getName().asString() == "Array" || getClassDescriptorForType(rangeType).getName().asString() == "IntArray"
}
}
}
public class IteratorForTranslator private(forExpression: JetForExpression, context: TranslationContext) : ForTranslator(forExpression, context) {
private val iteratorVar: TemporaryVariable
{
iteratorVar = context.declareTemporary(iteratorMethodInvocation())
}
private fun translate(): JsStatement {
context().addStatementToCurrentBlock(iteratorVar.assignmentExpression().makeStmt())
return JsWhile(hasNextMethodInvocation(), translateBody(nextMethodInvocation()))
}
private fun nextMethodInvocation(): JsExpression {
return translateMethodInvocation(iteratorVar.reference(), getNextFunction(bindingContext(), getLoopRange(expression)))
}
private fun hasNextMethodInvocation(): JsExpression {
val resolvedCall = getHasNextCallable(bindingContext(), getLoopRange(expression))
return translateMethodInvocation(iteratorVar.reference(), resolvedCall)
}
private fun iteratorMethodInvocation(): JsExpression {
val rangeExpression = getLoopRange(expression)
val range = Translation.translateAsExpression(rangeExpression, context())
val resolvedCall = getIteratorFunction(bindingContext(), rangeExpression)
return translateMethodInvocation(range, resolvedCall)
}
private fun translateMethodInvocation(receiver: JsExpression?, resolvedCall: ResolvedCall<FunctionDescriptor>): JsExpression {
return CallTranslator.translate(context(), resolvedCall, receiver)
}
class object {
public fun doTranslate(expression: JetForExpression, context: TranslationContext): JsStatement {
return (IteratorForTranslator(expression, context).translate())
}
}
}
public abstract class ForTranslator protected(protected val expression: JetForExpression, context: TranslationContext) : AbstractTranslator(context) {
protected val parameterName: JsName
protected val multiParameter: JetMultiDeclaration?
{
this.multiParameter = expression.getMultiParameter()
this.parameterName = declareParameter()
}
private fun declareParameter(): JsName {
fun declareParameter(): JsName {
val loopParameter = getLoopParameter(expression)
if (loopParameter != null) {
return context().getNameForElement(loopParameter)
return context.getNameForElement(loopParameter)
}
assert(parameterIsMultiDeclaration(), "If loopParameter is null, multi parameter must be not null")
return context().scope().declareTemporary()
assert(multiParameter != null, "If loopParameter is null, multi parameter must be not null ${expression.getText()}")
return context.scope().declareTemporary()
}
private fun parameterIsMultiDeclaration(): Boolean {
return multiParameter != null
}
val parameterName: JsName = declareParameter()
private fun makeCurrentVarInit(itemValue: JsExpression?): JsStatement {
if (multiParameter == null) {
return JsAstUtils.newVar(parameterName, itemValue)
}
else {
return MultiDeclarationTranslator.translate(multiParameter!!, parameterName, itemValue, context())
}
}
protected fun translateBody(itemValue: JsExpression?): JsStatement {
val realBody = Translation.translateAsStatement(getLoopBody(expression), context())
if (itemValue == null && !parameterIsMultiDeclaration()) {
fun translateBody(itemValue: JsExpression?): JsStatement {
val realBody = Translation.translateAsStatement(getLoopBody(expression), context)
if (itemValue == null && multiParameter == null) {
return realBody
}
else {
val currentVarInit = makeCurrentVarInit(itemValue)
val block = JsAstUtils.convertToBlock(realBody)
val currentVarInit =
if (multiParameter == null)
newVar(parameterName, itemValue)
else
MultiDeclarationTranslator.translate(multiParameter, parameterName, itemValue, context)
val block = convertToBlock(realBody)
block.getStatements().add(0, currentVarInit)
return block
}
}
class object {
// TODO: implement reverse semantics
fun translateForOverLiteralRange(): JsStatement {
if (loopRange !is JetBinaryExpression) throw IllegalStateException("expected JetBinaryExpression, but ${loopRange.getText()}")
public fun translate(expression: JetForExpression, context: TranslationContext): JsStatement {
if (RangeLiteralForTranslator.isApplicable(expression, context)) {
return RangeLiteralForTranslator.doTranslate(expression, context)
val startBlock = JsBlock()
val leftExpression = TranslationUtils.translateLeftExpression(context, loopRange, startBlock)
val endBlock = JsBlock()
val rightExpression = TranslationUtils.translateRightExpression(context, loopRange, endBlock)
val rangeStart =
if (TranslationUtils.isCacheNeeded(leftExpression)) {
val startVar = context.declareTemporary(leftExpression)
context.addStatementToCurrentBlock(startVar.assignmentExpression().makeStmt())
startVar.reference()
}
if (RangeForTranslator.isApplicable(expression, context)) {
return RangeForTranslator.doTranslate(expression, context)
else {
leftExpression
}
if (ArrayForTranslator.isApplicable(expression, context)) {
return ArrayForTranslator.doTranslate(expression, context)
}
return IteratorForTranslator.doTranslate(expression, context)
}
context.addStatementsToCurrentBlockFrom(startBlock)
context.addStatementsToCurrentBlockFrom(endBlock)
val rangeEnd = context.declareTemporary(rightExpression)
val body = translateBody(null)
val initExpression = newVar(parameterName, rangeStart)
val conditionExpression = lessThanEq(parameterName.makeRef(), rangeEnd.reference())
val incrementExpression = JsPostfixOperation(JsUnaryOperator.INC, parameterName.makeRef())
context.addStatementToCurrentBlock(temporariesInitialization(rangeEnd).makeStmt())
return JsFor(initExpression, conditionExpression, incrementExpression, body)
}
}
fun translateForOverRange(): JsStatement {
val rangeExpression = context.declareTemporary(Translation.translateAsExpression(loopRange, context))
fun getProperty(funName: String): JsExpression = JsNameRef(funName, rangeExpression.reference())
val start = context.declareTemporary(getProperty("start"))
val end = context.declareTemporary(getProperty("end"))
val increment = context.declareTemporary(getProperty("increment"))
val body = translateBody(null)
val initExpression = newVar(parameterName, start.reference())
val conditionExpression = lessThanEq(parameterName.makeRef(), end.reference())
val incrementExpression = addAssign(parameterName.makeRef(), increment.reference())
context.addStatementToCurrentBlock(temporariesInitialization(rangeExpression, start, end, increment).makeStmt())
return JsFor(initExpression, conditionExpression, incrementExpression, body)
}
fun translateForOverArray(): JsStatement {
val rangeExpression = context.declareTemporary(Translation.translateAsExpression(loopRange, context))
val length = CompositeFIF.LENGTH_PROPERTY_INTRINSIC.apply(rangeExpression.reference(), listOf<JsExpression>(), context)
val end = context.declareTemporary(length)
val index = context.declareTemporary(context.program().getNumberLiteral(0))
val arrayAccess = JsArrayAccess(rangeExpression.reference(), index.reference())
val body = translateBody(arrayAccess)
val initExpression = newVar(index.name(), context.program().getNumberLiteral(0))
val conditionExpression = inequality(index.reference(), end.reference())
val incrementExpression = JsPrefixOperation(JsUnaryOperator.INC, index.reference())
context.addStatementToCurrentBlock(temporariesInitialization(rangeExpression, end).makeStmt())
return JsFor(initExpression, conditionExpression, incrementExpression, body)
}
fun translateForOverIterator(): JsStatement {
fun translateMethodInvocation(receiver: JsExpression?, resolvedCall: ResolvedCall<FunctionDescriptor>): JsExpression =
CallTranslator.translate(context, resolvedCall, receiver)
fun iteratorMethodInvocation(): JsExpression {
val range = Translation.translateAsExpression(loopRange, context)
val resolvedCall = getIteratorFunction(context.bindingContext(), loopRange)
return translateMethodInvocation(range, resolvedCall)
}
val iteratorVar: TemporaryVariable = context.declareTemporary(iteratorMethodInvocation())
fun nextMethodInvocation(): JsExpression =
translateMethodInvocation(iteratorVar.reference(), getNextFunction(context.bindingContext(), loopRange))
fun hasNextMethodInvocation(): JsExpression {
val resolvedCall = getHasNextCallable(context.bindingContext(), loopRange)
return translateMethodInvocation(iteratorVar.reference(), resolvedCall)
}
context.addStatementToCurrentBlock(iteratorVar.assignmentExpression().makeStmt())
return JsWhile(hasNextMethodInvocation(), translateBody(nextMethodInvocation()))
}
return when {
isForOverRangeLiteral() ->
translateForOverLiteralRange()
isForOverRange() ->
translateForOverRange()
isForOverArray() ->
translateForOverArray()
else ->
translateForOverIterator()
}
}