From 40e5d6c8f58241804e02f999efc19251b6d85fa7 Mon Sep 17 00:00:00 2001 From: Michael Nedzelsky Date: Fri, 22 Aug 2014 20:11:03 +0400 Subject: [PATCH] JS backend: refactor LoopTranslator, add function translateForExpression --- .../expression/ExpressionVisitor.java | 3 +- .../translate/expression/LoopTranslator.kt | 460 ++++++------------ 2 files changed, 160 insertions(+), 303 deletions(-) diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java index eb7c927b5ca..ca1ba3c0094 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java @@ -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 { @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 diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/LoopTranslator.kt b/js/js.translator/src/org/jetbrains/k2js/translate/expression/LoopTranslator.kt index 4ed324a11c1..01bfe10cf8b 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/LoopTranslator.kt +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/LoopTranslator.kt @@ -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) { if(!tmpExprVar) break; } else tmpSecondRun=true; } 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) { if(!tmpExprVar) break; } - 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(), 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): 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(), 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): 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() + } +} \ No newline at end of file