diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCatch.java b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCatch.java index cdc1d13f305..8f9758c4b18 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCatch.java +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/JsCatch.java @@ -24,6 +24,15 @@ public class JsCatch extends SourceInfoAwareJsNode implements HasCondition { param = new JsParameter(scope.findName(ident)); } + public JsCatch(JsScope parent, @NotNull String ident, @NotNull JsStatement catchBody) { + this(parent, ident); + if (catchBody instanceof JsBlock) { + body = (JsBlock) catchBody; + } else { + body = new JsBlock(catchBody); + } + } + public JsBlock getBody() { return body; } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/CatchTranslator.kt b/js/js.translator/src/org/jetbrains/k2js/translate/expression/CatchTranslator.kt new file mode 100644 index 00000000000..26fb445e033 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/CatchTranslator.kt @@ -0,0 +1,125 @@ +/* + * 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 + +import com.google.dart.compiler.backend.js.ast.* +import org.jetbrains.jet.lang.psi.JetCatchClause +import org.jetbrains.jet.lang.psi.JetExpression +import org.jetbrains.jet.lang.psi.JetTypeReference +import org.jetbrains.jet.lang.resolve.BindingContext +import org.jetbrains.jet.lang.resolve.BindingContextUtils.getNotNull +import org.jetbrains.jet.lang.resolve.DescriptorUtils +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns +import org.jetbrains.k2js.translate.context.TranslationContext +import org.jetbrains.k2js.translate.general.AbstractTranslator +import org.jetbrains.k2js.translate.general.Translation.patternTranslator +import org.jetbrains.k2js.translate.general.Translation.translateAsStatementAndMergeInBlockIfNeeded +import org.jetbrains.k2js.translate.utils.JsAstUtils.convertToBlock +import org.jetbrains.k2js.translate.utils.TranslationUtils +import java.util.Collections +import org.jetbrains.k2js.translate.utils.JsAstUtils + +class CatchTranslator( + val catches: List, + context: TranslationContext +) : AbstractTranslator(context) { + + /** + * In JavaScript there is no multiple catches, so we translate + * multiple catch to single catch with instanceof checks for + * every catch clause. + * + * For example this code: + * try { + * ... + * } catch(e: NullPointerException) { + * ... + * } catch(e: RuntimeException) { + * ... + * } + * + * is translated to the following JsCode + * + * try { + * ... + * } catch(e) { + * if (e instanceof NullPointerException) { + * ... + * } else { + * if (e instanceof RuntimeException) { + * ... + * } else throw e; + * } + * } + */ + public fun translate(): JsCatch? { + if (catches.empty) return null + + val firstCatch = catches.first!! + val catchParameter = firstCatch.getCatchParameter() + val parameterName = context().getNameForElement(catchParameter!!) + val parameterRef = parameterName.makeRef() + + return JsCatch(context().scope(), + parameterRef.getIdent(), + translateCatches(parameterRef, catches.iterator())) + } + + private fun translateCatches(parameterRef: JsNameRef, catches: Iterator): JsStatement { + if (!catches.hasNext()) return JsThrow(parameterRef) + + val catch = catches.next() + val param = catch.getCatchParameter()!! + val paramName = context().getNameForElement(param) + val paramType = param.getTypeReference()!! + + val thenBlock = translateCatchBody(context(), catch) + if (paramName.getIdent() != parameterRef.getIdent()) + thenBlock.getStatements().add(0, JsAstUtils.newVar(paramName, parameterRef)) + + if (paramType.isThrowable) return thenBlock + + val typeCheck = with (patternTranslator(context())) { + translateIsCheck(parameterRef, paramType) + } + + val elseBlock = translateCatches(parameterRef, catches) + return JsIf(typeCheck, thenBlock, elseBlock) + } + + private fun translateCatchBody(context: TranslationContext, catchClause: JetCatchClause): JsBlock { + val catchBody = catchClause.getCatchBody() + val jsCatchBody = + if (catchBody != null) + translateAsStatementAndMergeInBlockIfNeeded(catchBody, context) + else + context.getEmptyExpression().makeStmt() + + return convertToBlock(jsCatchBody) + } + + private val JetTypeReference.isThrowable: Boolean + get() { + val jetType = getNotNull(bindingContext(), BindingContext.TYPE, this) + val jetTypeName = TranslationUtils.getJetTypeFqName(jetType, false) + + val throwable = KotlinBuiltIns.getInstance().getThrowable() + val throwableClassName = DescriptorUtils.getFqNameSafe(throwable).asString() + + return jetTypeName == throwableClassName + } +} \ No newline at end of file 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 253bcead9a4..719b5d661e4 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 @@ -37,7 +37,6 @@ 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.LoopTranslatorPackage; -import org.jetbrains.k2js.translate.expression.tryTranslator.TryTranslatorPackage; import org.jetbrains.k2js.translate.general.Translation; import org.jetbrains.k2js.translate.general.TranslatorVisitor; import org.jetbrains.k2js.translate.operation.BinaryOperationTranslator; @@ -445,9 +444,11 @@ public final class ExpressionVisitor extends TranslatorVisitor { @Override @NotNull - public JsNode visitTryExpression(@NotNull JetTryExpression expression, - @NotNull TranslationContext context) { - return TryTranslatorPackage.translateTryExpression(expression, context); + public JsNode visitTryExpression( + @NotNull JetTryExpression expression, + @NotNull TranslationContext context + ) { + return new TryTranslator(expression, context).translate(); } @Override diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/TryTranslator.kt b/js/js.translator/src/org/jetbrains/k2js/translate/expression/TryTranslator.kt new file mode 100644 index 00000000000..48efcefe47d --- /dev/null +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/TryTranslator.kt @@ -0,0 +1,51 @@ +/* + * 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 + +import com.google.dart.compiler.backend.js.ast.* +import org.jetbrains.jet.lang.psi.JetTryExpression +import org.jetbrains.jet.lang.psi.JetExpression +import org.jetbrains.jet.lang.psi.JetCatchClause +import org.jetbrains.k2js.translate.context.TranslationContext +import org.jetbrains.k2js.translate.general.AbstractTranslator +import org.jetbrains.k2js.translate.general.Translation.translateAsStatementAndMergeInBlockIfNeeded +import org.jetbrains.k2js.translate.utils.JsAstUtils.convertToBlock + +public class TryTranslator( + val expression: JetTryExpression, + context: TranslationContext +) : AbstractTranslator(context) { + public fun translate(): JsTry { + val tryBlock = translateAsBlock(expression.getTryBlock()) + + val catchTranslator = CatchTranslator(expression.getCatchClauses(), context()) + val catchBlock = catchTranslator.translate() + + val finallyExpression = expression.getFinallyBlock()?.getFinalExpression() + val finallyBlock = translateAsBlock(finallyExpression) + + return JsTry(tryBlock, catchBlock, finallyBlock) + } + + private fun translateAsBlock(expression: JetExpression?): JsBlock? { + if (expression == null) return null + + val statement = translateAsStatementAndMergeInBlockIfNeeded(expression, context()) + return convertToBlock(statement) + } +} + diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/tryTranslator.kt b/js/js.translator/src/org/jetbrains/k2js/translate/expression/tryTranslator.kt deleted file mode 100644 index 0bc949a9a90..00000000000 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/tryTranslator.kt +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.tryTranslator - -import com.google.dart.compiler.backend.js.ast.* -import org.jetbrains.jet.lang.psi.JetCatchClause -import org.jetbrains.jet.lang.psi.JetTryExpression -import org.jetbrains.jet.lang.psi.JetTypeReference -import org.jetbrains.jet.lang.resolve.BindingContext -import org.jetbrains.jet.lang.resolve.BindingContextUtils.getNotNull -import org.jetbrains.jet.lang.resolve.DescriptorUtils -import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns -import org.jetbrains.k2js.translate.context.TranslationContext -import org.jetbrains.k2js.translate.general.Translation -import org.jetbrains.k2js.translate.general.Translation.translateAsStatementAndMergeInBlockIfNeeded -import org.jetbrains.k2js.translate.utils.JsAstUtils -import org.jetbrains.k2js.translate.utils.JsAstUtils.convertToBlock -import org.jetbrains.k2js.translate.utils.TranslationUtils - - -val THROWABLE_CLASS_NAME = DescriptorUtils.getFqNameSafe(KotlinBuiltIns.getInstance().getThrowable()).asString() - -val JetCatchClause.parameterTypeReference: JetTypeReference - get() = this.getCatchParameter()?.getTypeReference()!! - -public fun translateTryExpression(expression: JetTryExpression, context: TranslationContext): JsTry { - - fun translateTryBlock() = convertToBlock(translateAsStatementAndMergeInBlockIfNeeded(expression.getTryBlock(), context)) - - fun JetCatchClause.getParameterName(): JsName = context.getNameForElement(this.getCatchParameter()!!) - - fun JetTypeReference.isThrowable(): Boolean { - val jetType = getNotNull(context.bindingContext(), BindingContext.TYPE, this) - return THROWABLE_CLASS_NAME == TranslationUtils.getJetTypeFqName(jetType, false) - } - - fun translateCatchBody(catchClause: JetCatchClause): JsBlock { - val catchBody = catchClause.getCatchBody() - if (catchBody == null) { - return convertToBlock(context.getEmptyStatement()) - } - return convertToBlock(translateAsStatementAndMergeInBlockIfNeeded(catchBody, context)) - } - - fun translateCatches(): JsCatch? { - val patternTranslator = Translation.patternTranslator(context) - val catchClausesStream = expression.getCatchClauses().stream() - if (catchClausesStream.none()) return null - - val firstCatchClause = catchClausesStream.first() - val firstParameterRef = JsNameRef(firstCatchClause.getParameterName()) - - fun jsCatch(body: JsBlock): JsCatch { - val result = JsCatch(context.scope(), firstParameterRef.getIdent()) - result.setBody(body) - return result - } - - fun ifIsCheckThen(typeReference: JetTypeReference, catchBlock: JsBlock): JsIf = - JsIf(patternTranslator.translateIsCheck(firstParameterRef, typeReference), catchBlock) - - if (firstCatchClause.parameterTypeReference.isThrowable()) { - return jsCatch(translateCatchBody(firstCatchClause)) - } - - val resultIf = ifIsCheckThen(firstCatchClause.parameterTypeReference, translateCatchBody(firstCatchClause)) - val currentIf = catchClausesStream.drop(1).fold(resultIf) { (currentIf, catchClause) -> - val typeReference = catchClause.parameterTypeReference - val catchBlock = translateCatchBody(catchClause) - if (typeReference.isThrowable()) { - currentIf.setElseStatement(catchBlock) - currentIf - } - else { - val nextIf = ifIsCheckThen(typeReference, catchBlock) - catchBlock.getStatements().add(0, JsAstUtils.newVar(catchClause.getParameterName(), firstParameterRef)) - currentIf.setElseStatement(nextIf) - nextIf - } - } - - if (currentIf.getElseStatement() == null) { - currentIf.setElseStatement(JsThrow(firstParameterRef)) - } - - return jsCatch(JsAstUtils.convertToBlock(resultIf)) - } - - fun translateFinallyBlock(): JsBlock? { - val finalExpression = expression.getFinallyBlock()?.getFinalExpression() - if (finalExpression == null) return null - - return convertToBlock(translateAsStatementAndMergeInBlockIfNeeded(finalExpression, context)) - } - - return JsTry(translateTryBlock(), translateCatches(), translateFinallyBlock()) -}