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));
|
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() {
|
public JsBlock getBody() {
|
||||||
return body;
|
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.context.TranslationContext;
|
||||||
import org.jetbrains.k2js.translate.declaration.ClassTranslator;
|
import org.jetbrains.k2js.translate.declaration.ClassTranslator;
|
||||||
import org.jetbrains.k2js.translate.expression.loopTranslator.LoopTranslatorPackage;
|
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.Translation;
|
||||||
import org.jetbrains.k2js.translate.general.TranslatorVisitor;
|
import org.jetbrains.k2js.translate.general.TranslatorVisitor;
|
||||||
import org.jetbrains.k2js.translate.operation.BinaryOperationTranslator;
|
import org.jetbrains.k2js.translate.operation.BinaryOperationTranslator;
|
||||||
@@ -445,9 +444,11 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsNode visitTryExpression(@NotNull JetTryExpression expression,
|
public JsNode visitTryExpression(
|
||||||
@NotNull TranslationContext context) {
|
@NotNull JetTryExpression expression,
|
||||||
return TryTranslatorPackage.translateTryExpression(expression, context);
|
@NotNull TranslationContext context
|
||||||
|
) {
|
||||||
|
return new TryTranslator(expression, context).translate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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