JS backend: add LoopTranslator.kt, move ExpressionVisitor.createWhile and ForTranslator into LoopTranslart, remove foreach package

This commit is contained in:
Michael Nedzelsky
2014-08-22 17:17:51 +04:00
parent 2fd13a0fb4
commit 478d3baf59
7 changed files with 377 additions and 536 deletions
@@ -32,7 +32,8 @@ 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.foreach.ForTranslator;
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;
import org.jetbrains.k2js.translate.operation.BinaryOperationTranslator;
@@ -215,17 +216,6 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
return ReferenceTranslator.translateSimpleNameWithQualifier(expression, null, context).source(expression);
}
@NotNull
private static JsStatement translateNullableExpressionAsNotNullStatement(
@Nullable JetExpression nullableExpression,
@NotNull TranslationContext context
) {
if (nullableExpression == null) {
return context.program().getEmptyStatement();
}
return Translation.translateAsStatement(nullableExpression, context);
}
@NotNull
private JsExpression translateConditionExpression(@Nullable JetExpression expression,
@NotNull TranslationContext context) {
@@ -246,46 +236,13 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@Override
@NotNull
public JsNode visitWhileExpression(@NotNull JetWhileExpression expression, @NotNull TranslationContext context) {
return createWhile(false, expression, context);
return LoopTranslatorPackage.createWhile(false, expression, context);
}
@Override
@NotNull
public JsNode visitDoWhileExpression(@NotNull JetDoWhileExpression expression, @NotNull TranslationContext context) {
return createWhile(true, expression, context);
}
private static JsNode createWhile(boolean doWhile, @NotNull JetWhileExpressionBase expression, @NotNull TranslationContext context) {
JetExpression conditionExpression = expression.getCondition();
assert conditionExpression != null : "condition expression should not be null: " + expression.getText();
JsBlock conditionBlock = new JsBlock();
JsExpression jsCondition = Translation.translateAsExpression(conditionExpression, context, conditionBlock);
JsStatement bodyStatement = translateNullableExpressionAsNotNullStatement(expression.getBody(), context);
if (!conditionBlock.isEmpty()) {
JsIf IfStatement = new JsIf(not(jsCondition), new JsBreak());
JsBlock bodyBlock = JsAstUtils.convertToBlock(bodyStatement);
jsCondition = JsLiteral.TRUE;
if (doWhile) {
// translate to: tmpSecondRun = false; do { if(tmpSecondRun) { <expr> if(!tmpExprVar) break; } else tmpSecondRun=true; <body> } while(true)
TemporaryVariable secondRun = context.declareTemporary(JsLiteral.FALSE);
context.addStatementToCurrentBlock(secondRun.assignmentExpression().makeStmt());
conditionBlock.getStatements().add(IfStatement);
bodyBlock.getStatements().add(0,
new JsIf(secondRun.reference(), conditionBlock,
JsAstUtils.assignment(secondRun.reference(), JsLiteral.TRUE).makeStmt())
);
} else {
// translate to: while (true) { <expr> if(!tmpExprVar) break; <body> }
conditionBlock.getStatements().add(IfStatement);
bodyBlock.getStatements().addAll(0, conditionBlock.getStatements());
}
bodyStatement = bodyBlock;
}
JsWhile result = doWhile ? new JsDoWhile() : new JsWhile();
result.setCondition(jsCondition);
result.setBody(bodyStatement);
return result.source(expression);
return LoopTranslatorPackage.createWhile(true, expression, context);
}
@Override
@@ -471,7 +428,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@NotNull
public JsNode visitForExpression(@NotNull JetForExpression expression,
@NotNull TranslationContext context) {
return ForTranslator.translate(expression, context).source(expression);
return ForTranslator.OBJECT$.translate(expression, context).source(expression);
}
@Override
@@ -0,0 +1,372 @@
/*
* Copyright 2010-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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 org.jetbrains.jet.lang.descriptors.FunctionDescriptor
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.expression.MultiDeclarationTranslator
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)
if (!conditionBlock.isEmpty()) {
val IfStatement = JsIf(JsAstUtils.not(jsCondition), JsBreak())
val bodyBlock = JsAstUtils.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()))
}
else {
// translate to: while (true) { <expr> if(!tmpExprVar) break; <body> }
conditionBlock.getStatements().add(IfStatement)
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)
// 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)
}
private fun translate(): JsStatement {
context().addStatementToCurrentBlock(temporariesInitialization(rangeEnd).makeStmt())
return JsFor(initExpression(), getCondition(), getIncrExpression(), translateBody(null))
}
private fun initExpression(): JsVars {
return JsAstUtils.newVar(parameterName, rangeStart)
}
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 {
val loopParameter = getLoopParameter(expression)
if (loopParameter != null) {
return context().getNameForElement(loopParameter)
}
assert(parameterIsMultiDeclaration(), "If loopParameter is null, multi parameter must be not null")
return context().scope().declareTemporary()
}
private fun parameterIsMultiDeclaration(): Boolean {
return multiParameter != null
}
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()) {
return realBody
}
else {
val currentVarInit = makeCurrentVarInit(itemValue)
val block = JsAstUtils.convertToBlock(realBody)
block.getStatements().add(0, currentVarInit)
return block
}
}
class object {
public fun translate(expression: JetForExpression, context: TranslationContext): JsStatement {
if (RangeLiteralForTranslator.isApplicable(expression, context)) {
return RangeLiteralForTranslator.doTranslate(expression, context)
}
if (RangeForTranslator.isApplicable(expression, context)) {
return RangeForTranslator.doTranslate(expression, context)
}
if (ArrayForTranslator.isApplicable(expression, context)) {
return ArrayForTranslator.doTranslate(expression, context)
}
return IteratorForTranslator.doTranslate(expression, context)
}
}
}
@@ -1,102 +0,0 @@
/*
* Copyright 2010-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.translate.expression.foreach;
import com.google.dart.compiler.backend.js.ast.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetForExpression;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.intrinsic.functions.factories.CompositeFIF;
import org.jetbrains.k2js.translate.utils.BindingUtils;
import java.util.Collections;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassDescriptorForType;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange;
import static org.jetbrains.k2js.translate.utils.TemporariesUtils.temporariesInitialization;
public final class ArrayForTranslator extends ForTranslator {
@NotNull
public static JsStatement doTranslate(@NotNull JetForExpression expression,
@NotNull TranslationContext context) {
return (new ArrayForTranslator(expression, context).translate());
}
public static boolean isApplicable(@NotNull JetForExpression expression,
@NotNull TranslationContext context) {
JetExpression loopRange = getLoopRange(expression);
JetType rangeType = BindingUtils.getTypeForExpression(context.bindingContext(), loopRange);
//TODO: better check
//TODO: IMPORTANT!
return getClassDescriptorForType(rangeType).getName().asString().equals("Array")
|| getClassDescriptorForType(rangeType).getName().asString().equals("IntArray");
}
@NotNull
private final TemporaryVariable loopRange;
@NotNull
private final TemporaryVariable end;
@NotNull
private final TemporaryVariable index;
private ArrayForTranslator(@NotNull JetForExpression forExpression, @NotNull TranslationContext context) {
super(forExpression, context);
loopRange = context.declareTemporary(Translation.translateAsExpression(getLoopRange(expression), context));
JsExpression length = CompositeFIF.LENGTH_PROPERTY_INTRINSIC.apply(loopRange.reference(),
Collections.<JsExpression>emptyList(),
context());
end = context().declareTemporary(length);
index = context().declareTemporary(program().getNumberLiteral(0));
}
@NotNull
private JsStatement translate() {
context().addStatementToCurrentBlock(temporariesInitialization(loopRange, end).makeStmt());
return new JsFor(getInitExpression(), getCondition(), getIncrementExpression(), getBody());
}
@NotNull
private JsStatement getBody() {
JsArrayAccess arrayAccess = new JsArrayAccess(loopRange.reference(), index.reference());
return translateBody(arrayAccess);
}
@NotNull
private JsVars getInitExpression() {
return newVar(index.name(), program().getNumberLiteral(0));
}
@NotNull
private JsExpression getCondition() {
return inequality(index.reference(), end.reference());
}
@NotNull
private JsExpression getIncrementExpression() {
return new JsPrefixOperation(JsUnaryOperator.INC, index.reference());
}
}
@@ -1,105 +0,0 @@
/*
* Copyright 2010-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.translate.expression.foreach;
import com.google.dart.compiler.backend.js.ast.JsBlock;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsName;
import com.google.dart.compiler.backend.js.ast.JsStatement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetForExpression;
import org.jetbrains.jet.lang.psi.JetMultiDeclaration;
import org.jetbrains.jet.lang.psi.JetParameter;
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.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.newVar;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopBody;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopParameter;
public abstract class ForTranslator extends AbstractTranslator {
@NotNull
public static JsStatement translate(@NotNull JetForExpression expression,
@NotNull TranslationContext context) {
if (RangeLiteralForTranslator.isApplicable(expression, context)) {
return RangeLiteralForTranslator.doTranslate(expression, context);
}
if (RangeForTranslator.isApplicable(expression, context)) {
return RangeForTranslator.doTranslate(expression, context);
}
if (ArrayForTranslator.isApplicable(expression, context)) {
return ArrayForTranslator.doTranslate(expression, context);
}
return IteratorForTranslator.doTranslate(expression, context);
}
@NotNull
protected final JetForExpression expression;
@NotNull
protected final JsName parameterName;
@Nullable
protected final JetMultiDeclaration multiParameter;
protected ForTranslator(@NotNull JetForExpression forExpression, @NotNull TranslationContext context) {
super(context);
this.expression = forExpression;
this.multiParameter = forExpression.getMultiParameter();
this.parameterName = declareParameter();
}
@NotNull
private JsName declareParameter() {
JetParameter loopParameter = getLoopParameter(expression);
if (loopParameter != null) {
return context().getNameForElement(loopParameter);
}
assert parameterIsMultiDeclaration() : "If loopParameter is null, multi parameter must be not null";
return context().scope().declareTemporary();
}
private boolean parameterIsMultiDeclaration() {
return multiParameter != null;
}
@NotNull
private JsStatement makeCurrentVarInit(@Nullable JsExpression itemValue) {
if (multiParameter == null) {
return newVar(parameterName, itemValue);
} else {
return MultiDeclarationTranslator.translate(multiParameter, parameterName, itemValue, context());
}
}
@NotNull
protected JsStatement translateBody(@Nullable JsExpression itemValue) {
JsStatement realBody = Translation.translateAsStatement(getLoopBody(expression), context());
if (itemValue == null && !parameterIsMultiDeclaration()) {
return realBody;
} else {
JsStatement currentVarInit = makeCurrentVarInit(itemValue);
JsBlock block = JsAstUtils.convertToBlock(realBody);
block.getStatements().add(0, currentVarInit);
return block;
}
}
}
@@ -1,79 +0,0 @@
/*
* Copyright 2010-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.translate.expression.foreach;
import com.google.dart.compiler.backend.js.ast.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetForExpression;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.k2js.translate.callTranslator.CallTranslator;
import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.Translation;
import static org.jetbrains.k2js.translate.utils.BindingUtils.*;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange;
public final class IteratorForTranslator extends ForTranslator {
@NotNull
private final TemporaryVariable iteratorVar;
@NotNull
public static JsStatement doTranslate(@NotNull JetForExpression expression,
@NotNull TranslationContext context) {
return (new IteratorForTranslator(expression, context).translate());
}
private IteratorForTranslator(@NotNull JetForExpression forExpression, @NotNull TranslationContext context) {
super(forExpression, context);
iteratorVar = context.declareTemporary(iteratorMethodInvocation());
}
@NotNull
private JsStatement translate() {
context().addStatementToCurrentBlock(iteratorVar.assignmentExpression().makeStmt());
return new JsWhile(hasNextMethodInvocation(), translateBody(nextMethodInvocation()));
}
@NotNull
private JsExpression nextMethodInvocation() {
return translateMethodInvocation(iteratorVar.reference(), getNextFunction(bindingContext(), getLoopRange(expression)));
}
@NotNull
private JsExpression hasNextMethodInvocation() {
ResolvedCall<FunctionDescriptor> resolvedCall = getHasNextCallable(bindingContext(), getLoopRange(expression));
return translateMethodInvocation(iteratorVar.reference(), resolvedCall);
}
@NotNull
private JsExpression iteratorMethodInvocation() {
JetExpression rangeExpression = getLoopRange(expression);
JsExpression range = Translation.translateAsExpression(rangeExpression, context());
ResolvedCall<FunctionDescriptor> resolvedCall = getIteratorFunction(bindingContext(), rangeExpression);
return translateMethodInvocation(range, resolvedCall);
}
@NotNull
private JsExpression translateMethodInvocation(@Nullable JsExpression receiver,
@NotNull ResolvedCall<FunctionDescriptor> resolvedCall) {
return CallTranslator.INSTANCE$.translate(context(), resolvedCall, receiver);
}
}
@@ -1,100 +0,0 @@
/*
* Copyright 2010-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.translate.expression.foreach;
import com.google.dart.compiler.backend.js.ast.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetForExpression;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.utils.BindingUtils;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassDescriptorForType;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange;
import static org.jetbrains.k2js.translate.utils.TemporariesUtils.temporariesInitialization;
public final class RangeForTranslator extends ForTranslator {
@NotNull
public static JsStatement doTranslate(@NotNull JetForExpression expression,
@NotNull TranslationContext context) {
return (new RangeForTranslator(expression, context).translate());
}
public static boolean isApplicable(@NotNull JetForExpression expression,
@NotNull TranslationContext context) {
JetExpression loopRange = getLoopRange(expression);
JetType rangeType = BindingUtils.getTypeForExpression(context.bindingContext(), loopRange);
//TODO: better check
//TODO: long range?
return getClassDescriptorForType(rangeType).getName().asString().equals("IntRange");
}
@NotNull
private final TemporaryVariable rangeExpression;
@NotNull
private final TemporaryVariable start;
@NotNull
private final TemporaryVariable end;
@NotNull
private final TemporaryVariable increment;
private RangeForTranslator(@NotNull JetForExpression forExpression, @NotNull TranslationContext context) {
super(forExpression, context);
rangeExpression = context.declareTemporary(Translation.translateAsExpression(getLoopRange(expression), context));
start = context().declareTemporary(getProperty("start"));
end = context().declareTemporary(getProperty("end"));
increment = context().declareTemporary(getProperty("increment"));
}
@NotNull
private JsStatement translate() {
context().addStatementToCurrentBlock(temporariesInitialization(rangeExpression, start, end, increment).makeStmt());
return generateForExpression();
}
@NotNull
private JsFor generateForExpression() {
JsFor result = new JsFor(initExpression(), getCondition(), getIncrExpression());
result.setBody(translateBody(null));
return result;
}
@NotNull
private JsVars initExpression() {
return newVar(parameterName, start.reference());
}
@NotNull
private JsExpression getCondition() {
return lessThanEq(parameterName.makeRef(), end.reference());
}
@NotNull
private JsExpression getIncrExpression() {
return addAssign(parameterName.makeRef(), increment.reference());
}
@NotNull
private JsExpression getProperty(@NotNull String funName) {
return new JsNameRef(funName, rangeExpression.reference());
}
}
@@ -1,102 +0,0 @@
/*
* Copyright 2010-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.translate.expression.foreach;
import com.google.dart.compiler.backend.js.ast.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetBinaryExpression;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetForExpression;
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.utils.TranslationUtils;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.lessThanEq;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.newVar;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getLoopRange;
import static org.jetbrains.k2js.translate.utils.TemporariesUtils.temporariesInitialization;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateLeftExpression;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateRightExpression;
// TODO: implement reverse semantics
public final class RangeLiteralForTranslator extends ForTranslator {
@NotNull
public static JsStatement doTranslate(@NotNull JetForExpression expression,
@NotNull TranslationContext context) {
return (new RangeLiteralForTranslator(expression, context).translate());
}
public static boolean isApplicable(@NotNull JetForExpression expression,
@NotNull TranslationContext context) {
JetExpression loopRange = getLoopRange(expression);
if (!(loopRange instanceof JetBinaryExpression)) {
return false;
}
boolean isRangeToOperation = ((JetBinaryExpression) loopRange).getOperationToken() == JetTokens.RANGE;
return isRangeToOperation && RangeForTranslator.isApplicable(expression, context);
}
@NotNull
private final JsExpression rangeStart;
@NotNull
private final TemporaryVariable rangeEnd;
private RangeLiteralForTranslator(@NotNull JetForExpression forExpression, @NotNull TranslationContext context) {
super(forExpression, context);
JetExpression loopRange = getLoopRange(expression);
assert loopRange instanceof JetBinaryExpression;
JetBinaryExpression loopRangeAsBinary = ((JetBinaryExpression) loopRange);
JsBlock startBlock = new JsBlock();
JsExpression rangeStartExpression = translateLeftExpression(context, loopRangeAsBinary, startBlock);
JsBlock endBlock = new JsBlock();
JsExpression rightExpression = translateRightExpression(context(), loopRangeAsBinary, endBlock);
if (TranslationUtils.isCacheNeeded(rangeStartExpression)) {
TemporaryVariable startVar = context.declareTemporary(rangeStartExpression);
rangeStartExpression = startVar.reference();
context.addStatementToCurrentBlock(startVar.assignmentExpression().makeStmt());
}
rangeStart = rangeStartExpression;
context.addStatementsToCurrentBlockFrom(startBlock);
context.addStatementsToCurrentBlockFrom(endBlock);
rangeEnd = context.declareTemporary(rightExpression);
}
@NotNull
private JsStatement translate() {
context().addStatementToCurrentBlock(temporariesInitialization(rangeEnd).makeStmt());
return new JsFor(initExpression(), getCondition(), getIncrExpression(), translateBody(null));
}
@NotNull
private JsVars initExpression() {
return newVar(parameterName, rangeStart);
}
@NotNull
private JsExpression getCondition() {
return lessThanEq(parameterName.makeRef(), rangeEnd.reference());
}
@NotNull
private JsExpression getIncrExpression() {
return new JsPostfixOperation(JsUnaryOperator.INC, parameterName.makeRef());
}
}