From aa2a4f0794362a5a29d2ae8692165d1eb76b0f22 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Mon, 14 Nov 2016 22:36:49 +0300 Subject: [PATCH] KJS: implement Throwable in Kotlin instead of use it as alias of JS Error * Make it an inheritor of JS Error. Otherwise, Chakra engine doesn't fill stack trace of exception; In other engines inheritance has some good effects too. * Copy all properties from internally created instance of JS Error #KT-6985 Fixed #KT-2328 Fixed #KT-8019 Fixed #KT-10911 Fixed --- .../box/specialBuiltins/throwableImpl.kt | 3 -- js/js.libraries/src/builtins/Throwable.kt | 47 +++++++++++++++++++ js/js.libraries/src/builtins/declarations.kt | 21 +++++++++ js/js.libraries/src/core/exceptions.kt | 36 +++++--------- .../js/test/semantics/BoxJsTestGenerated.java | 6 +++ .../semantics/JsCodegenBoxTestGenerated.java | 8 +--- .../js/translate/context/StaticContext.java | 3 -- .../functions/factories/TopLevelFIF.java | 2 - .../box/expression/try/exceptionToString.kt | 26 ++++++++++ js/js.translator/testData/kotlin_lib.js | 2 - 10 files changed, 114 insertions(+), 40 deletions(-) create mode 100644 js/js.libraries/src/builtins/Throwable.kt create mode 100644 js/js.libraries/src/builtins/declarations.kt create mode 100644 js/js.translator/testData/box/expression/try/exceptionToString.kt diff --git a/compiler/testData/codegen/box/specialBuiltins/throwableImpl.kt b/compiler/testData/codegen/box/specialBuiltins/throwableImpl.kt index 7b92e9cf12f..2f6aeaa6b55 100644 --- a/compiler/testData/codegen/box/specialBuiltins/throwableImpl.kt +++ b/compiler/testData/codegen/box/specialBuiltins/throwableImpl.kt @@ -1,6 +1,3 @@ -// TODO: muted automatically, investigate should it be ran for JS or not -// IGNORE_BACKEND: JS - class MyThrowable(message: String? = null, cause: Throwable? = null) : Throwable(message, cause) { override val message: String? diff --git a/js/js.libraries/src/builtins/Throwable.kt b/js/js.libraries/src/builtins/Throwable.kt new file mode 100644 index 00000000000..83fd7699bf6 --- /dev/null +++ b/js/js.libraries/src/builtins/Throwable.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2010-2016 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 kotlin + +import kotlin.js.internal.JsError + +/** + * The base class for all errors and exceptions. Only instances of this class can be thrown or caught. + * + * @param message the detail message string. + * @param cause the cause of this throwable. + */ +public open class Throwable(open val message: String?, open val cause: Throwable?) : JsError(message) { + constructor(message: String?) : this(message, null) + + constructor(cause: Throwable?) : this(cause?.toString(), cause) + + constructor() : this(null, null) + + init { + var e = JsError(message) + copyOwnProperties(e, this) + val t: dynamic = this + t.name = t.constructor.name + } +} + +private fun copyOwnProperties(from: dynamic, to: dynamic) { + val names: Array = js("Object").getOwnPropertyNames(from); + for (name in names) { + to[name] = from[name]; + } +} diff --git a/js/js.libraries/src/builtins/declarations.kt b/js/js.libraries/src/builtins/declarations.kt new file mode 100644 index 00000000000..c0ac8ab5aea --- /dev/null +++ b/js/js.libraries/src/builtins/declarations.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2010-2016 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 kotlin.js.internal + +@native +@JsName("Error") +public open class JsError(message: String?) diff --git a/js/js.libraries/src/core/exceptions.kt b/js/js.libraries/src/core/exceptions.kt index 45d33dcb963..3d01a7d0432 100644 --- a/js/js.libraries/src/core/exceptions.kt +++ b/js/js.libraries/src/core/exceptions.kt @@ -16,38 +16,28 @@ package kotlin -open class Error(message: String? = null) : Throwable(message, null) { - init { - val self: dynamic = this - self.message = message - } -} +open class Error(message: String? = null) : Throwable(message, null) -open class Exception(message: String? = null) : Throwable(message, null) { - init { - val self: dynamic = this - self.message = message - } -} +open class Exception(message: String? = null) : Throwable(message, null) -open class RuntimeException(message: String? = null) : Exception(message) {} +open class RuntimeException(message: String? = null) : Exception(message) -open class IllegalArgumentException(message: String? = null) : RuntimeException(message) {} +open class IllegalArgumentException(message: String? = null) : RuntimeException(message) -open class IllegalStateException(message: String? = null) : RuntimeException(message) {} +open class IllegalStateException(message: String? = null) : RuntimeException(message) -open class IndexOutOfBoundsException(message: String? = null) : RuntimeException(message) {} +open class IndexOutOfBoundsException(message: String? = null) : RuntimeException(message) -open class ConcurrentModificationException(message: String? = null) : RuntimeException(message) {} +open class ConcurrentModificationException(message: String? = null) : RuntimeException(message) -open class UnsupportedOperationException(message: String? = null) : RuntimeException(message) {} +open class UnsupportedOperationException(message: String? = null) : RuntimeException(message) -open class NumberFormatException(message: String? = null) : RuntimeException(message) {} +open class NumberFormatException(message: String? = null) : RuntimeException(message) -open class NullPointerException(message: String? = null) : RuntimeException(message) {} +open class NullPointerException(message: String? = null) : RuntimeException(message) -open class ClassCastException(message: String? = null) : RuntimeException(message) {} +open class ClassCastException(message: String? = null) : RuntimeException(message) -open class AssertionError(message: String? = null) : Error(message) {} +open class AssertionError(message: String? = null) : Error(message) -open class NoSuchElementException(message: String? = null) : Exception() {} +open class NoSuchElementException(message: String? = null) : Exception() diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index e9a420a6356..2fa1f110fb0 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -2953,6 +2953,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/expression/try"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("exceptionToString.kt") + public void testExceptionToString() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/expression/try/exceptionToString.kt"); + doTest(fileName); + } + @TestMetadata("multipleCatchBlocks.kt") public void testMultipleCatchBlocks() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/expression/try/multipleCatchBlocks.kt"); 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 b72e7ef9151..5524c10e1d3 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 @@ -19805,13 +19805,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("throwableImpl.kt") public void testThrowableImpl() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/throwableImpl.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("valuesInsideEnum.kt") diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java index 4ad2e7b1211..cf8cf3f6cb0 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java @@ -240,9 +240,6 @@ public final class StaticContext { if (KotlinBuiltIns.isAny(classDescriptor)) { return pureFqn("Object", null); } - if (DescriptorUtils.getFqName(classDescriptor).asString().equals("kotlin.Throwable")) { - return pureFqn("Error", null); - } } SuggestedName suggested = nameSuggestion.suggest(descriptor); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java index dd233f1df55..8d7b16bf6a7 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java @@ -186,8 +186,6 @@ public final class TopLevelFIF extends CompositeFIF { add(pattern("kotlin.js", "Json", "get"), ArrayFIF.GET_INTRINSIC); add(pattern("kotlin.js", "Json", "set"), ArrayFIF.SET_INTRINSIC); - add(pattern("kotlin", "Throwable", "getMessage"), MESSAGE_PROPERTY_INTRINSIC); - add(pattern("kotlin.js", "jsClass"), JS_CLASS_FUN_INTRINSIC); } diff --git a/js/js.translator/testData/box/expression/try/exceptionToString.kt b/js/js.translator/testData/box/expression/try/exceptionToString.kt new file mode 100644 index 00000000000..176457674e0 --- /dev/null +++ b/js/js.translator/testData/box/expression/try/exceptionToString.kt @@ -0,0 +1,26 @@ +package foo + +class MyException(m: String? = null): Exception(m) +class MyException2(m: String? = null): Throwable(m) + +fun check(e: Throwable, expectedString: String) { + try { + throw e + } + catch (e: Throwable) { + assertEquals(expectedString, e.toString()) + } +} + +fun box(): String { + check(Throwable(), "Throwable: null") + check(Throwable("ccc"), "Throwable: ccc") + check(Exception(), "Exception: null") + check(Exception("bbb"), "Exception: bbb") + check(MyException(), "MyException: null") + check(MyException("aaa"), "MyException: aaa") + check(MyException2(), "MyException2: null") + check(MyException2("aaa"), "MyException2: aaa") + + return "OK" +} diff --git a/js/js.translator/testData/kotlin_lib.js b/js/js.translator/testData/kotlin_lib.js index 11db08cc249..61e294f51eb 100644 --- a/js/js.translator/testData/kotlin_lib.js +++ b/js/js.translator/testData/kotlin_lib.js @@ -199,8 +199,6 @@ Kotlin.intDownto = function (from, to) { return new Kotlin.kotlin.ranges.IntProgression(from, to, -1); }; -Kotlin.Throwable = Error; - Kotlin.throwNPE = function (message) { throw new Kotlin.kotlin.NullPointerException(message); };