JS: make kotlin.Throwable = Error. See KT-15312

This commit is contained in:
Alexey Andreev
2016-12-29 13:37:30 +03:00
parent c565697e77
commit f34093db9f
18 changed files with 299 additions and 52 deletions
@@ -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);
@@ -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
@@ -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<FunctionDescriptor> 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<FunctionDescriptor> 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<JsExpression> getAdditionalArgumentsForEnumConstructor() {
List<JsExpression> additionalArguments = new ArrayList<JsExpression>();
@@ -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));
}
}
@@ -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) {
@@ -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<JsExpression>, 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<PropertyDescriptor>()
.first { it.overriddenDescriptors.any { it == property } }
val fieldRef = JsAstUtils.pureFqn(context.getNameForBackingField(currentClassProperty), callInfo.dispatchReceiver!!)
return fieldRef
}
}
}
@@ -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<JsExpression>, 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)
}
}
}
@@ -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());
}
}
@@ -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")