From 115f6ced874797d8a5b95a501c9b6e3f55d1abda Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 29 Dec 2016 15:52:47 +0300 Subject: [PATCH] JS: fix support of throwable constructors without message and/or cause parameters --- .../box/specialBuiltins/exceptionCause.kt | 25 ++++++++++++++++++ .../box/specialBuiltins/throwableCause.kt | 8 +++++- .../specialBuiltins/exceptionCause.txt | 10 +++++++ .../throwableImplWithSecondaryConstructor.txt | 10 +++++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 6 +++++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 +++++ ...LightAnalysisModeCodegenTestGenerated.java | 6 +++++ js/js.libraries/src/core/builtins.kt | 2 +- .../semantics/JsCodegenBoxTestGenerated.java | 6 +++++ .../ClassInitializerTranslator.java | 26 +++++++++++++++---- .../ThrowableConstructorIntrinsicFactory.kt | 2 +- 11 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 compiler/testData/codegen/box/specialBuiltins/exceptionCause.kt create mode 100644 compiler/testData/codegen/light-analysis/specialBuiltins/exceptionCause.txt create mode 100644 compiler/testData/codegen/light-analysis/specialBuiltins/throwableImplWithSecondaryConstructor.txt diff --git a/compiler/testData/codegen/box/specialBuiltins/exceptionCause.kt b/compiler/testData/codegen/box/specialBuiltins/exceptionCause.kt new file mode 100644 index 00000000000..cd0501bdf1d --- /dev/null +++ b/compiler/testData/codegen/box/specialBuiltins/exceptionCause.kt @@ -0,0 +1,25 @@ +class CustomException : Throwable { + constructor(message: String?, cause: Throwable?) : super(message, cause) + + constructor(message: String?) : super(message, null) + + constructor(cause: Throwable?) : super(cause) + + constructor() : super() +} + +fun box(): String { + var t = CustomException("O", Throwable("K")) + if (t.message != "O" || t.cause?.message != "K") return "fail1" + + t = CustomException(Throwable("OK")) + if (t.message == null || t.message == "OK" || t.cause?.message != "OK") return "fail2" + + t = CustomException("OK") + if (t.message != "OK" || t.cause != null) return "fail3" + + t = CustomException() + if (t.message != null || t.cause != null) return "fail4" + + return "OK" +} diff --git a/compiler/testData/codegen/box/specialBuiltins/throwableCause.kt b/compiler/testData/codegen/box/specialBuiltins/throwableCause.kt index b6a27448bd1..ade7379bb50 100644 --- a/compiler/testData/codegen/box/specialBuiltins/throwableCause.kt +++ b/compiler/testData/codegen/box/specialBuiltins/throwableCause.kt @@ -3,7 +3,13 @@ fun box(): String { if (t.message != "O" || t.cause?.message != "K") return "fail1" t = Throwable(Throwable("OK")) - if (t.message != null || t.cause?.message != "OK") return "fail2" + if (t.message == null || t.message == "OK" || t.cause?.message != "OK") return "fail2" + + t = Throwable("OK") + if (t.message != "OK" || t.cause != null) return "fail3" + + t = Throwable() + if (t.message != null || t.cause != null) return "fail4" return "OK" } diff --git a/compiler/testData/codegen/light-analysis/specialBuiltins/exceptionCause.txt b/compiler/testData/codegen/light-analysis/specialBuiltins/exceptionCause.txt new file mode 100644 index 00000000000..837f17e39ce --- /dev/null +++ b/compiler/testData/codegen/light-analysis/specialBuiltins/exceptionCause.txt @@ -0,0 +1,10 @@ +public final class CustomException { + public method (): void + public method (@org.jetbrains.annotations.Nullable p0: java.lang.String): void + public method (@org.jetbrains.annotations.Nullable p0: java.lang.String, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): void + public method (@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void +} + +public final class ExceptionCauseKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/testData/codegen/light-analysis/specialBuiltins/throwableImplWithSecondaryConstructor.txt b/compiler/testData/codegen/light-analysis/specialBuiltins/throwableImplWithSecondaryConstructor.txt new file mode 100644 index 00000000000..88ec9a61706 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/specialBuiltins/throwableImplWithSecondaryConstructor.txt @@ -0,0 +1,10 @@ +public final class MyThrowable { + private final @org.jetbrains.annotations.NotNull field x: java.lang.String + public method (@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: java.lang.String, @org.jetbrains.annotations.Nullable p2: java.lang.Throwable): void + public synthetic method (p0: java.lang.String, p1: java.lang.String, p2: java.lang.Throwable, p3: int, p4: kotlin.jvm.internal.DefaultConstructorMarker): void + public final @org.jetbrains.annotations.NotNull method getX(): java.lang.String +} + +public final class ThrowableImplWithSecondaryConstructorKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 4a6fd5e525d..6bd7b624807 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -16169,6 +16169,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("exceptionCause.kt") + public void testExceptionCause() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/exceptionCause.kt"); + doTest(fileName); + } + @TestMetadata("explicitSuperCall.kt") public void testExplicitSuperCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/explicitSuperCall.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index c71fe292c37..3488dea6191 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -16169,6 +16169,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("exceptionCause.kt") + public void testExceptionCause() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/exceptionCause.kt"); + doTest(fileName); + } + @TestMetadata("explicitSuperCall.kt") public void testExplicitSuperCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/explicitSuperCall.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java index e07a197656e..cbc73944e01 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java @@ -16169,6 +16169,12 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis doTest(fileName); } + @TestMetadata("exceptionCause.kt") + public void testExceptionCause() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/exceptionCause.kt"); + doTest(fileName); + } + @TestMetadata("explicitSuperCall.kt") public void testExplicitSuperCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/explicitSuperCall.kt"); diff --git a/js/js.libraries/src/core/builtins.kt b/js/js.libraries/src/core/builtins.kt index 7e06c7818f4..9ca9c4a8d9f 100644 --- a/js/js.libraries/src/core/builtins.kt +++ b/js/js.libraries/src/core/builtins.kt @@ -61,7 +61,7 @@ internal fun captureStack(baseClass: JsClass, instance: Throwable) @JsName("newThrowable") internal fun newThrowable(message: String?, cause: Throwable?): Throwable { val throwable = js("new Error()") - throwable.message = message + throwable.message = if (jsTypeOf(message) == "undefined" && cause != null) cause.toString() else message throwable.cause = cause return throwable } \ No newline at end of file diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 2749ba877d5..c20f5e9a615 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -20421,6 +20421,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("exceptionCause.kt") + public void testExceptionCause() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/exceptionCause.kt"); + doTest(fileName); + } + @TestMetadata("explicitSuperCall.kt") public void testExplicitSuperCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/explicitSuperCall.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java index 45b03b7eb81..e1cfef6f71f 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java @@ -269,7 +269,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator { List statements = context.getCurrentBlock().getStatements(); statements.add(JsAstUtils.asSyntheticStatement(superInvocation)); - JsExpression messageArgument = JsLiteral.NULL; + JsExpression messageArgument = Namer.getUndefinedExpression(); JsExpression causeArgument = JsLiteral.NULL; for (ValueParameterDescriptor param : superCall.getResultingDescriptor().getValueParameters()) { ResolvedValueArgument argument = superCall.getValueArguments().get(param); @@ -283,10 +283,10 @@ public final class ClassInitializerTranslator extends AbstractTranslator { JsExpression jsValue = Translation.translateAsExpression(value, context); if (KotlinBuiltIns.isStringOrNullableString(param.getType())) { - messageArgument = jsValue; + messageArgument = context.cacheExpressionIfNeeded(jsValue); } else if (TypeUtilsKt.isConstructedFromClassWithGivenFqName(param.getType(), KotlinBuiltIns.FQ_NAMES.throwable)) { - causeArgument = jsValue; + causeArgument = context.cacheExpressionIfNeeded(jsValue); } else { statements.add(JsAstUtils.asSyntheticStatement(jsValue)); @@ -296,12 +296,28 @@ public final class ClassInitializerTranslator extends AbstractTranslator { PropertyDescriptor messageProperty = DescriptorUtils.getPropertyByName( classDescriptor.getUnsubstitutedMemberScope(), Name.identifier("message")); JsExpression messageRef = pureFqn(context.getNameForBackingField(messageProperty), receiver.deepCopy()); - statements.add(JsAstUtils.asSyntheticStatement(JsAstUtils.assignment(messageRef, messageArgument))); + JsExpression messageIsUndefined = JsAstUtils.typeOfIs(messageArgument, context.program().getStringLiteral("undefined")); + JsExpression causeIsNull = new JsBinaryOperation(JsBinaryOperator.NEQ, causeArgument, JsLiteral.NULL); + JsExpression causeToStringCond = JsAstUtils.and(messageIsUndefined, causeIsNull); + JsExpression causeToString = new JsInvocation(pureFqn("toString", Namer.kotlinObject()), causeArgument.deepCopy()); + + JsExpression correctedMessage; + if (causeArgument == JsLiteral.NULL) { + correctedMessage = messageArgument.deepCopy(); + } + else { + if (JsAstUtils.isUndefinedExpression(messageArgument)) { + causeToStringCond = causeIsNull; + } + correctedMessage = new JsConditional(causeToStringCond, causeToString, messageArgument); + } + + statements.add(JsAstUtils.asSyntheticStatement(JsAstUtils.assignment(messageRef, correctedMessage))); PropertyDescriptor causeProperty = DescriptorUtils.getPropertyByName( classDescriptor.getUnsubstitutedMemberScope(), Name.identifier("cause")); JsExpression causeRef = pureFqn(context.getNameForBackingField(causeProperty), receiver.deepCopy()); - statements.add(JsAstUtils.asSyntheticStatement(JsAstUtils.assignment(causeRef, causeArgument))); + statements.add(JsAstUtils.asSyntheticStatement(JsAstUtils.assignment(causeRef, causeArgument.deepCopy()))); } @NotNull diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ThrowableConstructorIntrinsicFactory.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ThrowableConstructorIntrinsicFactory.kt index 39426009a90..9ee2d680b76 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ThrowableConstructorIntrinsicFactory.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ThrowableConstructorIntrinsicFactory.kt @@ -47,7 +47,7 @@ object ThrowableConstructorIntrinsicFactory : FunctionIntrinsicFactory { } if (constructor.valueParameters.size == 1 && hasCauseParameter) { - argumentsToPass.add(0, JsLiteral.NULL) + argumentsToPass.add(0, Namer.getUndefinedExpression()) } return JsInvocation(JsAstUtils.pureFqn("newThrowable", Namer.kotlinObject()), argumentsToPass)