From f34093db9ff27b41830ebedd0070d45cb641e0fa Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 29 Dec 2016 13:37:30 +0300 Subject: [PATCH] JS: make kotlin.Throwable = Error. See KT-15312 --- .../box/specialBuiltins/throwableCause.kt | 9 +++ .../specialBuiltins/throwableCause.txt | 3 + .../ir/IrBlackBoxCodegenTestGenerated.java | 6 ++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 ++ ...LightAnalysisModeCodegenTestGenerated.java | 6 ++ .../kotlin/resolve/DescriptorUtils.java | 13 +++ .../kotlin/js/backend/ast/JsExpression.java | 4 +- js/js.libraries/src/builtins/Throwable.kt | 47 ----------- js/js.libraries/src/core/builtins.kt | 18 +++++ .../semantics/JsCodegenBoxTestGenerated.java | 6 ++ .../js/translate/context/StaticContext.java | 4 + .../translate/declaration/ClassTranslator.kt | 19 +++++ .../ClassInitializerTranslator.java | 80 ++++++++++++++++++- .../functions/FunctionIntrinsics.java | 2 + .../ExceptionPropertyIntrinsicFactory.kt | 58 ++++++++++++++ .../ThrowableConstructorIntrinsicFactory.kt | 56 +++++++++++++ .../js/translate/utils/TranslationUtils.java | 12 +++ .../box/expression/try/exceptionToString.kt | 2 - 18 files changed, 299 insertions(+), 52 deletions(-) create mode 100644 compiler/testData/codegen/box/specialBuiltins/throwableCause.kt create mode 100644 compiler/testData/codegen/light-analysis/specialBuiltins/throwableCause.txt delete mode 100644 js/js.libraries/src/builtins/Throwable.kt create mode 100644 js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ExceptionPropertyIntrinsicFactory.kt create mode 100644 js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ThrowableConstructorIntrinsicFactory.kt diff --git a/compiler/testData/codegen/box/specialBuiltins/throwableCause.kt b/compiler/testData/codegen/box/specialBuiltins/throwableCause.kt new file mode 100644 index 00000000000..b6a27448bd1 --- /dev/null +++ b/compiler/testData/codegen/box/specialBuiltins/throwableCause.kt @@ -0,0 +1,9 @@ +fun box(): String { + var t = Throwable("O", Throwable("K")) + if (t.message != "O" || t.cause?.message != "K") return "fail1" + + t = Throwable(Throwable("OK")) + if (t.message != null || t.cause?.message != "OK") return "fail2" + + return "OK" +} diff --git a/compiler/testData/codegen/light-analysis/specialBuiltins/throwableCause.txt b/compiler/testData/codegen/light-analysis/specialBuiltins/throwableCause.txt new file mode 100644 index 00000000000..370ec4225fb --- /dev/null +++ b/compiler/testData/codegen/light-analysis/specialBuiltins/throwableCause.txt @@ -0,0 +1,3 @@ +public final class ThrowableCauseKt { + 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 512b871678d..1f37b9095ec 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 @@ -16223,6 +16223,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("throwableCause.kt") + public void testThrowableCause() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/throwableCause.kt"); + doTest(fileName); + } + @TestMetadata("throwableImpl.kt") public void testThrowableImpl() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/throwableImpl.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index b222a33b344..af8500a45a5 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -16223,6 +16223,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("throwableCause.kt") + public void testThrowableCause() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/throwableCause.kt"); + doTest(fileName); + } + @TestMetadata("throwableImpl.kt") public void testThrowableImpl() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/throwableImpl.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java index 45273d83d14..7df8cea0b33 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java @@ -16223,6 +16223,12 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis doTest(fileName); } + @TestMetadata("throwableCause.kt") + public void testThrowableCause() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/throwableCause.kt"); + doTest(fileName); + } + @TestMetadata("throwableImpl.kt") public void testThrowableImpl() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/throwableImpl.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java index 4b52c7c9cf3..da8f3edd3c1 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java @@ -601,6 +601,19 @@ public class DescriptorUtils { throw new IllegalStateException("Function not found"); } + @NotNull + public static PropertyDescriptor getPropertyByName(@NotNull MemberScope scope, @NotNull Name name) { + Collection callables = scope.getContributedDescriptors( + DescriptorKindFilter.CALLABLES, MemberScope.Companion.getALL_NAME_FILTER()); + for (DeclarationDescriptor d : callables) { + if (d instanceof PropertyDescriptor && name.equals(d.getOriginal().getName())) { + return (PropertyDescriptor) d; + } + } + + throw new IllegalStateException("Property not found"); + } + @NotNull public static CallableMemberDescriptor getDirectMember(@NotNull CallableMemberDescriptor descriptor) { return descriptor instanceof PropertyAccessorDescriptor diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsExpression.java b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsExpression.java index 7c19c21f5b6..0f42e98e0f8 100644 --- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsExpression.java +++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsExpression.java @@ -25,10 +25,10 @@ public abstract class JsExpression extends SourceInfoAwareJsNode { return new JsExpressionStatement(this); } - protected abstract static class JsExpressionHasArguments extends JsExpression implements HasArguments { + public abstract static class JsExpressionHasArguments extends JsExpression implements HasArguments { protected final List arguments; - public JsExpressionHasArguments(List arguments) { + protected JsExpressionHasArguments(List arguments) { this.arguments = arguments; } diff --git a/js/js.libraries/src/builtins/Throwable.kt b/js/js.libraries/src/builtins/Throwable.kt deleted file mode 100644 index 83fd7699bf6..00000000000 --- a/js/js.libraries/src/builtins/Throwable.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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/core/builtins.kt b/js/js.libraries/src/core/builtins.kt index ad935517732..7e06c7818f4 100644 --- a/js/js.libraries/src/core/builtins.kt +++ b/js/js.libraries/src/core/builtins.kt @@ -46,4 +46,22 @@ fun subSequence(c: CharSequence, startIndex: Int, endIndex: Int): CharSequence { else { return c.asDynamic().`subSequence_vux9f0$`(startIndex, endIndex) } +} + +@JsName("captureStack") +internal fun captureStack(baseClass: JsClass, instance: Throwable) { + if (js("Error").captureStackTrace) { + js("Error").captureStackTrace(instance, instance::class.js); + } + else { + instance.asDynamic().stack = js("new Error()").stack; + } +} + +@JsName("newThrowable") +internal fun newThrowable(message: String?, cause: Throwable?): Throwable { + val throwable = js("new Error()") + throwable.message = 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 cc1ce7f3195..eddc178011b 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 @@ -20487,6 +20487,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("throwableCause.kt") + public void testThrowableCause() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/throwableCause.kt"); + doTest(fileName); + } + @TestMetadata("throwableImpl.kt") public void testThrowableImpl() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/specialBuiltins/throwableImpl.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 27bb5840ef5..8d936db5e9a 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 @@ -46,6 +46,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.serialization.js.ModuleKind; +import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt; import java.util.*; @@ -248,6 +249,9 @@ public final class StaticContext { if (KotlinBuiltIns.isAny(classDescriptor)) { return pureFqn("Object", null); } + else if (TypeUtilsKt.isThrowable(classDescriptor.getDefaultType())) { + return pureFqn("Error", null); + } } SuggestedName suggested = nameSuggestion.suggest(descriptor); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt index f01c780304a..06cbb6aa193 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt @@ -38,6 +38,7 @@ import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getSupertypesWithoutFakes import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getPrimaryConstructorParameters import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.toInvocationWith +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtEnumEntry import org.jetbrains.kotlin.psi.KtSecondaryConstructor @@ -87,6 +88,7 @@ class ClassTranslator private constructor( translatePropertiesAsConstructorParameters(nonConstructorContext) val bodyVisitor = DeclarationBodyVisitor(descriptor, nonConstructorContext, enumInitFunction) bodyVisitor.traverseContainer(classDeclaration, nonConstructorContext) + mayBeAddThrowableProperties(context) constructorFunction.body.statements += bodyVisitor.initializerStatements delegationTranslator.generateDelegated() @@ -455,6 +457,23 @@ class ClassTranslator private constructor( EnumTranslator(context(), descriptor, entries).generateStandardMethods() } + private fun mayBeAddThrowableProperties(context: TranslationContext) { + if (!TranslationUtils.isImmediateSubtypeOfError(descriptor)) return + + val properties = listOf("message", "cause") + .map { Name.identifier(it) } + .map { DescriptorUtils.getPropertyByName(descriptor.unsubstitutedMemberScope, it) } + .filter { !it.kind.isReal } + for (property in properties) { + val propertyTranslator = DefaultPropertyTranslator(property, context, JsLiteral.NULL) + val literal = JsObjectLiteral(true) + val getterFunction = context.getFunctionObject(property.getter!!) + propertyTranslator.generateDefaultGetterFunction(property.getter!!, getterFunction) + literal.propertyInitializers += JsPropertyInitializer(context.program().getStringLiteral("get"), getterFunction) + context.addAccessorsToPrototype(descriptor, property, literal) + } + } + private fun generatedBridgeMethods() { if (isAnnotation()) return 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 52ca342b565..3f57e42b142 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 @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.js.translate.initializer; import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor; import org.jetbrains.kotlin.js.backend.ast.*; @@ -26,21 +27,28 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.context.UsageTracker; import org.jetbrains.kotlin.js.translate.declaration.DelegationTranslator; import org.jetbrains.kotlin.js.translate.general.AbstractTranslator; +import org.jetbrains.kotlin.js.translate.general.Translation; import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator; +import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator; import org.jetbrains.kotlin.js.translate.utils.BindingUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils; +import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; import org.jetbrains.kotlin.js.translate.utils.jsAstUtils.AstUtilsKt; import org.jetbrains.kotlin.lexer.KtTokens; +import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.KtClassOrObject; import org.jetbrains.kotlin.psi.KtEnumEntry; +import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtParameter; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument; +import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.types.KotlinType; +import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt; import java.util.ArrayList; import java.util.Arrays; @@ -63,6 +71,8 @@ public final class ClassInitializerTranslator extends AbstractTranslator { @NotNull private final ClassDescriptor classDescriptor; + private final ConstructorDescriptor primaryConstructor; + private int ordinal; public ClassInitializerTranslator( @@ -75,6 +85,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator { this.initFunction = initFunction; this.context = context.contextWithScope(initFunction); classDescriptor = BindingUtils.getClassDescriptor(bindingContext(), classDeclaration); + primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor(); } public void setOrdinal(int ordinal) { @@ -89,7 +100,6 @@ public final class ClassInitializerTranslator extends AbstractTranslator { public void generateInitializeMethod(DelegationTranslator delegationTranslator) { addOuterClassReference(classDescriptor); - ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor(); if (primaryConstructor != null) { initFunction.getBody().getStatements().addAll(setDefaultValueForArguments(primaryConstructor, context())); @@ -106,6 +116,8 @@ public final class ClassInitializerTranslator extends AbstractTranslator { } } + addThrowableCall(); + delegationTranslator.addInitCode(initFunction.getBody().getStatements()); new InitializerVisitor().traverseContainer(classDeclaration, context().innerBlock(initFunction.getBody())); } @@ -168,6 +180,7 @@ public final class ClassInitializerTranslator extends AbstractTranslator { } else if (hasAncestorClass(bindingContext(), classDeclaration)) { ResolvedCall superCall = getSuperCall(bindingContext(), classDeclaration); + if (superCall == null) { if (DescriptorUtils.isEnumEntry(classDescriptor)) { addCallToSuperMethod(getAdditionalArgumentsForEnumConstructor(), initializer); @@ -175,6 +188,11 @@ public final class ClassInitializerTranslator extends AbstractTranslator { return; } + if (TranslationUtils.isImmediateSubtypeOfError(classDescriptor)) { + emulateSuperCallToNativeError(superCall); + return; + } + if (classDeclaration instanceof KtEnumEntry) { JsExpression expression = CallTranslator.translate(context(), superCall, null); @@ -239,6 +257,49 @@ public final class ClassInitializerTranslator extends AbstractTranslator { } } + private void emulateSuperCallToNativeError(@NotNull ResolvedCall superCall) { + ClassDescriptor superClass = DescriptorUtilsKt.getSuperClassOrAny(classDescriptor); + JsExpression superClassRef = ReferenceTranslator.translateAsTypeReference(superClass, context); + JsExpression superInvocation = new JsInvocation(Namer.getFunctionCallRef(superClassRef), JsLiteral.THIS); + initFunction.getBody().getStatements().add(JsAstUtils.asSyntheticStatement(superInvocation)); + + JsExpression messageArgument = JsLiteral.NULL; + JsExpression causeArgument = JsLiteral.NULL; + for (ValueParameterDescriptor param : superCall.getResultingDescriptor().getValueParameters()) { + ResolvedValueArgument argument = superCall.getValueArguments().get(param); + if (!(argument instanceof ExpressionValueArgument)) continue; + + ExpressionValueArgument exprArgument = (ExpressionValueArgument) argument; + assert exprArgument.getValueArgument() != null; + + KtExpression value = exprArgument.getValueArgument().getArgumentExpression(); + assert value != null; + JsExpression jsValue = Translation.translateAsExpression(value, context); + + if (KotlinBuiltIns.isStringOrNullableString(param.getType())) { + messageArgument = jsValue; + } + else if (TypeUtilsKt.isConstructedFromClassWithGivenFqName(param.getType(), KotlinBuiltIns.FQ_NAMES.throwable)) { + causeArgument = jsValue; + } + else { + initFunction.getBody().getStatements().add(JsAstUtils.asSyntheticStatement(jsValue)); + } + } + + PropertyDescriptor messageProperty = DescriptorUtils.getPropertyByName( + classDescriptor.getUnsubstitutedMemberScope(), Name.identifier("message")); + JsExpression messageRef = pureFqn(context.getNameForBackingField(messageProperty), JsLiteral.THIS); + initFunction.getBody().getStatements().add(JsAstUtils.asSyntheticStatement( + JsAstUtils.assignment(messageRef, messageArgument))); + + PropertyDescriptor causeProperty = DescriptorUtils.getPropertyByName( + classDescriptor.getUnsubstitutedMemberScope(), Name.identifier("cause")); + JsExpression causeRef = pureFqn(context.getNameForBackingField(causeProperty), JsLiteral.THIS); + initFunction.getBody().getStatements().add(JsAstUtils.asSyntheticStatement( + JsAstUtils.assignment(causeRef, causeArgument))); + } + @NotNull private List getAdditionalArgumentsForEnumConstructor() { List additionalArguments = new ArrayList(); @@ -302,4 +363,21 @@ public final class ClassInitializerTranslator extends AbstractTranslator { initFunction.getBody().getStatements().add( InitializerUtils.generateInitializerForProperty(context(), propertyDescriptor, initialValue)); } + + private void addThrowableCall() { + if (!TranslationUtils.isExceptionClass(classDescriptor)) return; + + if (TranslationUtils.isImmediateSubtypeOfError(classDescriptor)) { + ClassDescriptor superClass = DescriptorUtilsKt.getSuperClassOrAny(classDescriptor); + JsExpression invocation = new JsInvocation( + pureFqn("captureStack", Namer.kotlinObject()), + ReferenceTranslator.translateAsTypeReference(superClass, context()), + JsLiteral.THIS); + initFunction.getBody().getStatements().add(JsAstUtils.asSyntheticStatement(invocation)); + } + + JsExpression nameLiteral = context.program().getStringLiteral(context.getInnerNameForDescriptor(classDescriptor).getIdent()); + JsExpression nameAssignment = JsAstUtils.assignment(pureFqn("name", JsLiteral.THIS), nameLiteral); + initFunction.getBody().getStatements().add(JsAstUtils.asSyntheticStatement(nameAssignment)); + } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/FunctionIntrinsics.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/FunctionIntrinsics.java index f6d1bbad01e..66502e29437 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/FunctionIntrinsics.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/FunctionIntrinsics.java @@ -46,6 +46,8 @@ public final class FunctionIntrinsics { register(ArrayFIF.INSTANCE); register(TopLevelFIF.INSTANCE); register(NumberAndCharConversionFIF.INSTANCE); + register(ThrowableConstructorIntrinsicFactory.INSTANCE); + register(ExceptionPropertyIntrinsicFactory.INSTANCE); } private void register(@NotNull FunctionIntrinsicFactory instance) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ExceptionPropertyIntrinsicFactory.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ExceptionPropertyIntrinsicFactory.kt new file mode 100644 index 00000000000..11a65eec9a9 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ExceptionPropertyIntrinsicFactory.kt @@ -0,0 +1,58 @@ +/* + * 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 org.jetbrains.kotlin.js.translate.intrinsic.functions.factories + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor +import org.jetbrains.kotlin.js.backend.ast.JsExpression +import org.jetbrains.kotlin.js.backend.ast.JsNameRef +import org.jetbrains.kotlin.js.translate.callTranslator.CallInfo +import org.jetbrains.kotlin.js.translate.context.TranslationContext +import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic +import org.jetbrains.kotlin.js.translate.utils.JsAstUtils +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.receivers.SuperCallReceiverValue +import org.jetbrains.kotlin.types.typeUtil.isThrowable + +object ExceptionPropertyIntrinsicFactory : FunctionIntrinsicFactory { + override fun getIntrinsic(descriptor: FunctionDescriptor): FunctionIntrinsic? { + if (descriptor !is PropertyGetterDescriptor) return null + val classDescriptor = descriptor.correspondingProperty.containingDeclaration as? ClassDescriptor ?: return null + if (!classDescriptor.defaultType.isThrowable()) return null + + return Intrinsic + } + + object Intrinsic : FunctionIntrinsic() { + override fun apply(callInfo: CallInfo, arguments: List, context: TranslationContext): JsExpression { + val property = callInfo.resolvedCall.resultingDescriptor as PropertyDescriptor + if (callInfo.resolvedCall.call.explicitReceiver !is SuperCallReceiverValue) { + val name = context.getNameForDescriptor(property) + return JsNameRef(name, callInfo.dispatchReceiver!!) + } + + val currentClassProperty = context.classDescriptor!!.unsubstitutedMemberScope + .getContributedDescriptors(DescriptorKindFilter.CALLABLES) + .filterIsInstance() + .first { it.overriddenDescriptors.any { it == property } } + val fieldRef = JsAstUtils.pureFqn(context.getNameForBackingField(currentClassProperty), callInfo.dispatchReceiver!!) + return fieldRef + } + } +} \ No newline at end of file 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 new file mode 100644 index 00000000000..39426009a90 --- /dev/null +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/ThrowableConstructorIntrinsicFactory.kt @@ -0,0 +1,56 @@ +/* + * 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 org.jetbrains.kotlin.js.translate.intrinsic.functions.factories + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.js.backend.ast.JsExpression +import org.jetbrains.kotlin.js.backend.ast.JsInvocation +import org.jetbrains.kotlin.js.backend.ast.JsLiteral +import org.jetbrains.kotlin.js.translate.callTranslator.CallInfo +import org.jetbrains.kotlin.js.translate.context.Namer +import org.jetbrains.kotlin.js.translate.context.TranslationContext +import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic +import org.jetbrains.kotlin.js.translate.utils.JsAstUtils +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.types.typeUtil.isThrowable + +object ThrowableConstructorIntrinsicFactory : FunctionIntrinsicFactory { + override fun getIntrinsic(descriptor: FunctionDescriptor): FunctionIntrinsic? { + if (descriptor !is ConstructorDescriptor) return null + if (!descriptor.constructedClass.defaultType.isThrowable()) return null + + return Intrinsic + } + + object Intrinsic : FunctionIntrinsic() { + override fun apply(callInfo: CallInfo, arguments: List, context: TranslationContext): JsExpression { + val constructor = callInfo.resolvedCall.resultingDescriptor + val argumentsToPass = arguments.toMutableList() + val hasCauseParameter = constructor.valueParameters.any { + it.type.constructor.declarationDescriptor?.fqNameSafe == KotlinBuiltIns.FQ_NAMES.throwable + } + + if (constructor.valueParameters.size == 1 && hasCauseParameter) { + argumentsToPass.add(0, JsLiteral.NULL) + } + + return JsInvocation(JsAstUtils.pureFqn("newThrowable", Namer.kotlinObject()), argumentsToPass) + } + } +} \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java index 4fbf490d6d1..fc583af8c07 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java @@ -41,6 +41,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.serialization.deserialization.FindClassInModuleKt; import org.jetbrains.kotlin.types.KotlinType; +import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt; import java.util.ArrayList; import java.util.List; @@ -399,4 +400,15 @@ public final class TranslationUtils { ModalityKt.isOverridable(descriptor); } + + public static boolean isImmediateSubtypeOfError(@NotNull ClassDescriptor descriptor) { + if (!isExceptionClass(descriptor)) return false; + ClassDescriptor superClass = DescriptorUtilsKt.getSuperClassOrAny(descriptor); + return TypeUtilsKt.isThrowable(superClass.getDefaultType()) || AnnotationsUtils.isNativeObject(superClass); + } + + public static boolean isExceptionClass(@NotNull ClassDescriptor descriptor) { + ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor); + return TypeUtilsKt.isSubtypeOf(descriptor.getDefaultType(), module.getBuiltIns().getThrowable().getDefaultType()); + } } diff --git a/js/js.translator/testData/box/expression/try/exceptionToString.kt b/js/js.translator/testData/box/expression/try/exceptionToString.kt index 176457674e0..6bc6efb5c1f 100644 --- a/js/js.translator/testData/box/expression/try/exceptionToString.kt +++ b/js/js.translator/testData/box/expression/try/exceptionToString.kt @@ -13,8 +13,6 @@ fun check(e: Throwable, expectedString: String) { } 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")