JS backend: catch fix refactoring
This commit is contained in:
committed by
Michael Nedzelsky
parent
787b8f72c5
commit
5649797461
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<JetCatchClause>,
|
||||
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<JetCatchClause>): 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
|
||||
}
|
||||
}
|
||||
@@ -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<JsNode> {
|
||||
|
||||
@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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
Reference in New Issue
Block a user