diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index f0ea120a56c..40002bc258b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -247,8 +247,8 @@ class JsIrBackendContext( return vars.single() } - val captureStackSymbol = symbolTable.referenceSimpleFunction(getJsInternalFunction("captureStack")) val newThrowableSymbol = symbolTable.referenceSimpleFunction(getJsInternalFunction("newThrowable")) + val extendThrowableSymbol = symbolTable.referenceSimpleFunction(getJsInternalFunction("extendThrowable")) val throwISEymbol = symbolTable.referenceSimpleFunction(getFunctions(kotlinPackageFqn.child(Name.identifier("THROW_ISE"))).single()) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 1171e7e9919..d71c241faf1 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -122,8 +122,8 @@ private val removeInlineFunctionsWithReifiedTypeParametersLoweringPhase = makeJs ) private val throwableSuccessorsLoweringPhase = makeJsModulePhase( - ::ThrowableSuccessorsLowering, - name = "ThrowableSuccessorsLowering", + ::ThrowableLowering, + name = "ThrowableLowering", description = "Link kotlin.Throwable and JavaScript Error together to provide proper interop between language and platform exceptions" ) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableLowering.kt new file mode 100644 index 00000000000..c721ea90cbe --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableLowering.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrConstructor +import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.types.makeNotNull +import org.jetbrains.kotlin.ir.util.defaultType +import org.jetbrains.kotlin.ir.util.isThrowable +import org.jetbrains.kotlin.ir.util.isThrowableTypeOrSubtype +import org.jetbrains.kotlin.ir.visitors.IrElementTransformer + + +class ThrowableLowering(val context: JsIrBackendContext) : FileLoweringPass { + private val unitType get() = context.irBuiltIns.unitType + private val nothingNType get() = context.irBuiltIns.nothingNType + private val stringType get() = context.irBuiltIns.stringType + private val propertySetter get() = context.intrinsics.jsSetJSField.symbol + private val nameName get() = JsIrBuilder.buildString(stringType, "name") + + private val throwableConstructors = context.throwableConstructors + + private val newThrowableFunction = context.newThrowableSymbol + private val extendThrowableFunction = context.extendThrowableSymbol + + fun nullValue(): IrExpression = IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, nothingNType) + + data class ThrowableArguments( + val message: IrExpression, + val cause: IrExpression + ) + + override fun lower(irFile: IrFile) { + irFile.transformChildren(Transformer(), irFile) + } + + private fun IrFunctionAccessExpression.extractThrowableArguments(): ThrowableArguments = + when (valueArgumentsCount) { + 0 -> ThrowableArguments(nullValue(), nullValue()) + 2 -> ThrowableArguments( + message = getValueArgument(0)!!, + cause = getValueArgument(1)!! + ) + else -> { + val arg = getValueArgument(0)!! + when { + arg.type.makeNotNull().isThrowable() -> ThrowableArguments(message = nullValue(), cause = arg) + else -> ThrowableArguments(message = arg, cause = nullValue()) + } + } + } + + inner class Transformer : IrElementTransformer { + override fun visitClass(declaration: IrClass, data: IrDeclarationParent) = super.visitClass(declaration, declaration) + + override fun visitConstructorCall(expression: IrConstructorCall, data: IrDeclarationParent): IrExpression { + expression.transformChildren(this, data) + if (expression.symbol !in throwableConstructors) return expression + + val (messageArg, causeArg) = expression.extractThrowableArguments() + + return expression.run { + IrCallImpl(startOffset, endOffset, type, newThrowableFunction, newThrowableFunction.descriptor).also { + it.putValueArgument(0, messageArg) + it.putValueArgument(1, causeArg) + } + } + } + + override fun visitConstructor(declaration: IrConstructor, data: IrDeclarationParent): IrStatement { + declaration.transformChildren(this, data) + + if (!declaration.isPrimary) return declaration + + val klass = data as IrClass + if (klass.defaultType.isThrowableTypeOrSubtype()) { + (declaration.body as? IrBlockBody)?.let { + it.statements += JsIrBuilder.buildCall(propertySetter, unitType).apply { + putValueArgument(0, JsIrBuilder.buildGetValue(klass.thisReceiver!!.symbol)) + putValueArgument(1, nameName) + putValueArgument(2, JsIrBuilder.buildString(stringType, klass.name.asString())) + } + } + } + + return declaration + } + + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: IrDeclarationParent): IrExpression { + expression.transformChildren(this, data) + if (expression.symbol !in throwableConstructors) return expression + + val (messageArg, causeArg) = expression.extractThrowableArguments() + + val klass = data as IrClass + val thisReceiver = IrGetValueImpl(expression.startOffset, expression.endOffset, klass.thisReceiver!!.symbol) + + return expression.run { + IrCallImpl(startOffset, endOffset, type, extendThrowableFunction, extendThrowableFunction.descriptor).also { + it.putValueArgument(0, thisReceiver) + it.putValueArgument(1, messageArg) + it.putValueArgument(2, causeArg) + } + } + } + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableSuccessorsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableSuccessorsLowering.kt deleted file mode 100644 index f4abc48af60..00000000000 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableSuccessorsLowering.kt +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. - */ - -package org.jetbrains.kotlin.ir.backend.js.lower - -import org.jetbrains.kotlin.backend.common.FileLoweringPass -import org.jetbrains.kotlin.backend.common.atMostOne -import org.jetbrains.kotlin.backend.common.descriptors.WrappedFieldDescriptor -import org.jetbrains.kotlin.backend.common.ir.copyTo -import org.jetbrains.kotlin.descriptors.Visibilities -import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET -import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext -import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder -import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.* -import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol -import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.makeNotNull -import org.jetbrains.kotlin.ir.types.makeNullable -import org.jetbrains.kotlin.ir.util.defaultType -import org.jetbrains.kotlin.ir.util.isThrowable -import org.jetbrains.kotlin.ir.util.isThrowableTypeOrSubtype -import org.jetbrains.kotlin.ir.util.transformFlat -import org.jetbrains.kotlin.ir.visitors.* -import org.jetbrains.kotlin.name.Name - -class ThrowableSuccessorsLowering(val context: JsIrBackendContext) : FileLoweringPass { - private val unitType get() = context.irBuiltIns.unitType - private val nothingNType get() = context.irBuiltIns.nothingNType - private val nothingType get() = context.irBuiltIns.nothingType - private val stringType get() = context.irBuiltIns.stringType - private val booleanType get() = context.irBuiltIns.booleanType - private val throwableType get() = context.irBuiltIns.throwableType - - private val propertyGetter get() = context.intrinsics.jsGetJSField.symbol - private val propertySetter get() = context.intrinsics.jsSetJSField.symbol - private val eqeqeqSymbol get() = context.irBuiltIns.eqeqSymbol - - private val messageName get() = JsIrBuilder.buildString(stringType, "message") - private val causeName get() = JsIrBuilder.buildString(stringType, "cause") - private val nameName get() = JsIrBuilder.buildString(stringType, "name") - - private val throwableClass = context.throwableClass - private val throwableConstructors = context.throwableConstructors - - private val defaultCtor = context.defaultThrowableCtor - private val toString = - throwableClass.owner.declarations.filterIsInstance().single { it.name == Name.identifier("toString") }.symbol - - private val messagePropertyName = Name.identifier("message") - private val causePropertyName = Name.identifier("cause") - - private val messageGetter by lazy { - throwableClass.owner.declarations.filterIsInstance().atMostOne { it.name == Name.special("") }?.symbol - ?: throwableClass.owner.declarations.filterIsInstance().atMostOne { it.name == messagePropertyName }?.getter?.symbol!! - } - private val causeGetter by lazy { - throwableClass.owner.declarations.filterIsInstance().atMostOne { it.name == Name.special("") }?.symbol - ?: throwableClass.owner.declarations.filterIsInstance().atMostOne { it.name == causePropertyName }?.getter?.symbol!! - } - - private val captureStackFunction = context.captureStackSymbol - private val newThrowableFunction = context.newThrowableSymbol - private val pendingSuperUsages = mutableListOf() - - private data class DirectThrowableSuccessors(val klass: IrClass, val message: IrField, val cause: IrField) - - override fun lower(irFile: IrFile) { - - pendingSuperUsages.clear() - irFile.acceptChildrenVoid(ThrowableAccessorCreationVisitor()) - pendingSuperUsages.forEach { it.klass.transformChildren(ThrowableDirectSuccessorTransformer(it), it.klass) } - irFile.transformChildren(ThrowableNameSetterTransformer(), irFile) - irFile.transformChildrenVoid(ThrowablePropertiesUsageTransformer()) - irFile.transformChildrenVoid(ThrowableInstanceCreationLowering()) - } - - inner class ThrowableNameSetterTransformer : IrElementTransformer { - override fun visitClass(declaration: IrClass, data: IrDeclarationParent) = super.visitClass(declaration, declaration) - - override fun visitConstructor(declaration: IrConstructor, data: IrDeclarationParent): IrStatement { - declaration.transformChildren(this, data) - - if (!declaration.isPrimary) return declaration - - val klass = data as IrClass - if (klass.defaultType.isThrowableTypeOrSubtype()) { - - (declaration.body as? IrBlockBody)?.let { - it.statements += JsIrBuilder.buildCall(propertySetter, unitType).apply { - putValueArgument(0, JsIrBuilder.buildGetValue(klass.thisReceiver!!.symbol)) - putValueArgument(1, nameName) - putValueArgument(2, JsIrBuilder.buildString(stringType, klass.name.asString())) - } - } - } - - return declaration - } - - } - - inner class ThrowableInstanceCreationLowering : IrElementTransformerVoid() { - override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { - if (expression.symbol !in throwableConstructors) return super.visitConstructorCall(expression) - - expression.transformChildrenVoid(this) - - val (messageArg, causeArg) = extractConstructorParameters(expression) - - return expression.run { - IrCallImpl(startOffset, endOffset, type, newThrowableFunction, newThrowableFunction.descriptor).also { - it.putValueArgument(0, messageArg) - it.putValueArgument(1, causeArg) - } - } - } - - private fun extractConstructorParameters(expression: IrFunctionAccessExpression): Pair { - val nullValue = { IrConstImpl.constNull(expression.startOffset, expression.endOffset, nothingNType) } - return when { - expression.valueArgumentsCount == 0 -> Pair(nullValue(), nullValue()) - expression.valueArgumentsCount == 2 -> expression.run { Pair(getValueArgument(0)!!, getValueArgument(1)!!) } - else -> { - val arg = expression.getValueArgument(0)!! - when { - arg.type.makeNotNull().isThrowable() -> Pair(nullValue(), arg) - else -> Pair(arg, nullValue()) - } - } - } - } - } - - inner class ThrowableAccessorCreationVisitor : IrElementVisitorVoid { - override fun visitElement(element: IrElement) = element.acceptChildrenVoid(this) - - - override fun visitClass(declaration: IrClass) { - - if (isDirectChildOfThrowable(declaration)) { - val messageField = createBackingField(declaration, messagePropertyName, stringType) - val causeField = createBackingField(declaration, causePropertyName, throwableType) - - val existedMessageAccessor = ownPropertyAccessor(declaration, messageGetter) - val newMessageAccessor = if (existedMessageAccessor.origin == IrDeclarationOrigin.FAKE_OVERRIDE) { - createPropertyAccessor(existedMessageAccessor, messageField) - } else existedMessageAccessor - - val existedCauseAccessor = ownPropertyAccessor(declaration, causeGetter) - val newCauseAccessor = if (existedCauseAccessor.origin == IrDeclarationOrigin.FAKE_OVERRIDE) { - createPropertyAccessor(existedCauseAccessor, causeField) - } else existedCauseAccessor - - - declaration.declarations.transformFlat { - when (it) { - existedMessageAccessor -> listOf(newMessageAccessor) - existedCauseAccessor -> listOf(newCauseAccessor) - else -> null - } - } - - pendingSuperUsages += DirectThrowableSuccessors(declaration, messageField, causeField) - } - } - - private fun createBackingField(declaration: IrClass, name: Name, type: IrType): IrField { - val fieldDescriptor = WrappedFieldDescriptor() - val fieldSymbol = IrFieldSymbolImpl(fieldDescriptor) - val fieldDeclaration = IrFieldImpl( - UNDEFINED_OFFSET, - UNDEFINED_OFFSET, - JsIrBuilder.SYNTHESIZED_DECLARATION, - fieldSymbol, - name, - type, - Visibilities.PRIVATE, - true, - false, - false - ).apply { - parent = declaration - fieldDescriptor.bind(this) - } - - declaration.declarations += fieldDeclaration - return fieldDeclaration - } - - private fun createPropertyAccessor(fakeAccessor: IrSimpleFunction, field: IrField): IrSimpleFunction { - val name = fakeAccessor.name - val function = JsIrBuilder.buildFunction(name, fakeAccessor.returnType, fakeAccessor.parent).apply { - overriddenSymbols += fakeAccessor.overriddenSymbols - correspondingPropertySymbol = fakeAccessor.correspondingPropertySymbol - correspondingPropertySymbol!!.owner.getter = this - dispatchReceiverParameter = fakeAccessor.dispatchReceiverParameter?.copyTo(this) - } - - val thisReceiver = JsIrBuilder.buildGetValue(function.dispatchReceiverParameter!!.symbol) - val returnValue = JsIrBuilder.buildGetField(field.symbol, thisReceiver, type = field.type) - val returnStatement = JsIrBuilder.buildReturn(function.symbol, returnValue, nothingType) - function.body = JsIrBuilder.buildBlockBody(listOf(returnStatement)) - - return function - } - } - - private inner class ThrowableDirectSuccessorTransformer(private val successor: DirectThrowableSuccessors) : - IrElementTransformer { - - override fun visitClass(declaration: IrClass, data: IrDeclarationParent) = declaration - - override fun visitFunction(declaration: IrFunction, data: IrDeclarationParent) = super.visitFunction(declaration, declaration) - - override fun visitCall(expression: IrCall, data: IrDeclarationParent): IrElement { - if (expression.superQualifierSymbol != throwableClass) return super.visitCall(expression, data) - - expression.transformChildren(this, data) - - val superField = when { - expression.symbol == messageGetter -> successor.message - expression.symbol == causeGetter -> successor.cause - else -> error("Unknown accessor") - } - - return expression.run { IrGetFieldImpl(startOffset, endOffset, superField.symbol, type, dispatchReceiver, origin) } - } - - override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: IrDeclarationParent): IrElement { - if (expression.symbol !in throwableConstructors) return super.visitDelegatingConstructorCall(expression, data) - - expression.transformChildren(this, data) - - val (messageArg, causeArg, paramStatements) = extractConstructorParameters(expression, data) - - val newDelegation = expression.run { - IrDelegatingConstructorCallImpl(startOffset, endOffset, type, defaultCtor, defaultCtor.descriptor) - } - - val klass = successor.klass - val receiver = { IrGetValueImpl(expression.startOffset, expression.endOffset, klass.thisReceiver!!.symbol) } - - val fillStatements = fillThrowableInstance(expression, receiver, messageArg, causeArg) - - return expression.run { - IrCompositeImpl(startOffset, endOffset, type, origin, paramStatements + newDelegation + fillStatements) - } - } - - private fun fillThrowableInstance( - expression: IrFunctionAccessExpression, - receiver: () -> IrExpression, - messageArg: IrExpression, - causeArg: IrExpression - ): List { - - val setMessage = expression.run { - IrSetFieldImpl(startOffset, endOffset, successor.message.symbol, receiver(), messageArg, unitType) - } - - val setCause = expression.run { - IrSetFieldImpl(startOffset, endOffset, successor.cause.symbol, receiver(), causeArg, unitType) - } - - val setStackTrace = IrCallImpl(expression.startOffset, expression.endOffset, unitType, captureStackFunction).apply { - putValueArgument(0, receiver()) - } - - return listOf(setMessage, setCause, setStackTrace) - } - - private fun extractConstructorParameters( - expression: IrFunctionAccessExpression, - parent: IrDeclarationParent - ): Triple> { - val nullValue = { IrConstImpl.constNull(expression.startOffset, expression.endOffset, nothingNType) } - // Wrap parameters into variables to keep original evaluation order - return when { - expression.valueArgumentsCount == 0 -> Triple(nullValue(), nullValue(), emptyList()) - expression.valueArgumentsCount == 2 -> { - val msg = expression.getValueArgument(0)!! - val cus = expression.getValueArgument(1)!! - val irValM = JsIrBuilder.buildVar(msg.type, parent, initializer = msg) - val irValC = JsIrBuilder.buildVar(cus.type, parent, initializer = cus) - - val check = JsIrBuilder.buildCall(eqeqeqSymbol, booleanType).apply { - putValueArgument(0, JsIrBuilder.buildGetValue(irValM.symbol)) - putValueArgument(1, nullValue()) - } - - val msgElvis = JsIrBuilder.buildIfElse( - stringType.makeNullable(), check, - safeCallToString(irValC), JsIrBuilder.buildGetValue(irValM.symbol) - ) - - Triple(msgElvis, JsIrBuilder.buildGetValue(irValC.symbol), listOf(irValM, irValC)) - } - else -> { - val arg = expression.getValueArgument(0)!! - val irVal = JsIrBuilder.buildVar(arg.type, parent, initializer = arg) - val argValue = JsIrBuilder.buildGetValue(irVal.symbol) - when { - arg.type.makeNotNull().isThrowable() -> Triple(safeCallToString(irVal), argValue, listOf(irVal)) - else -> Triple(argValue, nullValue(), listOf(irVal)) - } - } - } - } - } - - private fun safeCallToString(receiver: IrValueDeclaration): IrExpression { - val value = JsIrBuilder.buildGetValue(receiver.symbol) - val check = JsIrBuilder.buildCall(eqeqeqSymbol, booleanType).apply { - putValueArgument(0, value) - putValueArgument(1, JsIrBuilder.buildNull(value.type)) - } - val value2 = JsIrBuilder.buildGetValue(receiver.symbol) - val call = JsIrBuilder.buildCall(toString, stringType).apply { dispatchReceiver = value2 } - return JsIrBuilder.buildIfElse(stringType.makeNullable(), check, JsIrBuilder.buildNull(stringType), call) - } - - private fun isDirectChildOfThrowable(irClass: IrClass) = irClass.superTypes.any { it.isThrowable() } - private fun ownPropertyAccessor(irClass: IrClass, irBase: IrFunctionSymbol) = - irClass.declarations.filterIsInstance().mapNotNull { it.getter } - .singleOrNull { it.overriddenSymbols.any { s -> s == irBase } } - ?: irClass.declarations.filterIsInstance().single { it.overriddenSymbols.any { s -> s == irBase } } - - inner class ThrowablePropertiesUsageTransformer : IrElementTransformerVoid() { - override fun visitCall(expression: IrCall): IrExpression { - val transformRequired = expression.superQualifierSymbol == null || expression.superQualifierSymbol?.owner == throwableClass - - if (!transformRequired) return super.visitCall(expression) - - expression.transformChildrenVoid(this) - - val owner = expression.symbol - return when (owner) { - messageGetter -> { - IrCallImpl(expression.startOffset, expression.endOffset, expression.type, propertyGetter).apply { - putValueArgument(0, expression.dispatchReceiver!!) - putValueArgument(1, messageName) - } - } - causeGetter -> { - IrCallImpl(expression.startOffset, expression.endOffset, expression.type, propertyGetter).apply { - putValueArgument(0, expression.dispatchReceiver!!) - putValueArgument(1, causeName) - } - } - else -> expression - } - } - } -} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFunctionToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFunctionToJsTransformer.kt index f296a9512c9..0b3e3370e1a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFunctionToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrFunctionToJsTransformer.kt @@ -25,7 +25,6 @@ class IrFunctionToJsTransformer : BaseIrElementToJsNodeTransformer null - else -> JsNameRef( - context.getNameForMemberFunction(this), - classPrototypeRef - ) - } + if (property.origin == IrDeclarationOrigin.FAKE_OVERRIDE) + continue - val getterRef = property.getter?.accessorRef() - val setterRef = property.setter?.accessorRef() - classBlock.statements += JsExpressionStatement( - defineProperty( - classPrototypeRef, - context.getNameForProperty(property).ident, - getter = getterRef, - setter = setterRef + fun IrSimpleFunction.accessorRef(): JsNameRef? = + when (visibility) { + Visibilities.PRIVATE -> null + else -> JsNameRef( + context.getNameForMemberFunction(this), + classPrototypeRef ) + } + + val getterRef = property.getter?.accessorRef() + val setterRef = property.setter?.accessorRef() + classBlock.statements += JsExpressionStatement( + defineProperty( + classPrototypeRef, + context.getNameForProperty(property).ident, + getter = getterRef, + setter = setterRef ) - } + ) } } context.staticContext.classModels[irClass.symbol] = classModel return classBlock } - private fun generateThrowableProperties(): List { - val functions = irClass.declarations.filterIsInstance() - - - // TODO: Fix `Name.special` deserialization - val messageGetter = functions.single { it.name.asString() == "" } - val causeGetter = functions.single { it.name.asString() == "" } - - val msgProperty = defineProperty(classPrototypeRef, "message", getter = buildGetterFunction(messageGetter)) - - val causeProperty = defineProperty(classPrototypeRef, "cause", getter = buildGetterFunction(causeGetter)) - - return listOf(msgProperty.makeStmt(), causeProperty.makeStmt()) - } - - private fun buildGetterFunction(delegate: IrSimpleFunction): JsFunction { - val getterName = context.getNameForMemberFunction(delegate) - val returnStatement = JsReturn(JsInvocation(JsNameRef(getterName, JsThisRef()))) - - return JsFunction(emptyScope, JsBlock(returnStatement), "") - } - private fun generateMemberFunction(declaration: IrSimpleFunction): JsStatement? { val translatedFunction = declaration.run { if (isReal) accept(IrFunctionToJsTransformer(), context) else null } @@ -145,12 +117,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo // II.prototype.foo = I.prototype.foo if (!irClass.isInterface) { declaration.realOverrideTarget.let { it -> - var implClassDeclaration = it.parent as IrClass - - // special case - if (implClassDeclaration.defaultType.isThrowable()) { - implClassDeclaration = throwableAccessor() - } + val implClassDeclaration = it.parent as IrClass if (!implClassDeclaration.defaultType.isAny() && !it.isEffectivelyExternal()) { val implMethodName = context.getNameForMemberFunction(it) @@ -167,18 +134,6 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo return null } - private fun throwableAccessor(): IrClass { - - fun throwableAccessorImpl(type: IrType): IrType { - val klass = (type.classifierOrFail as IrClassSymbol) - val superType = klass.owner.superTypes.first { (it.classifierOrFail as? IrClassSymbol)?.owner?.kind == ClassKind.CLASS } - return if (superType.isThrowable()) type else throwableAccessorImpl(superType) - } - - - return (throwableAccessorImpl(irClass.defaultType).classifierOrFail as IrClassSymbol).owner - } - private fun maybeGeneratePrimaryConstructor() { if (!irClass.declarations.any { it is IrConstructor }) { val func = JsFunction(emptyScope, JsBlock(), "Ctor for ${irClass.name}") diff --git a/compiler/testData/codegen/box/specialBuiltins/throwableComplex.kt b/compiler/testData/codegen/box/specialBuiltins/throwableComplex.kt index 094664b432d..deb29039e10 100644 --- a/compiler/testData/codegen/box/specialBuiltins/throwableComplex.kt +++ b/compiler/testData/codegen/box/specialBuiltins/throwableComplex.kt @@ -1,3 +1,6 @@ +// Super calls to Throwable properties are not supported +// IGNORE_BACKEND: JS_IR + open class Base(message: String? = null, cause: Throwable? = null) : Throwable(message, cause) open class Base2(message: String? = null, cause: Throwable? = null): Base(message, cause) diff --git a/compiler/testData/codegen/box/specialBuiltins/throwableImpl.kt b/compiler/testData/codegen/box/specialBuiltins/throwableImpl.kt index ad070a0298c..d33162bd698 100644 --- a/compiler/testData/codegen/box/specialBuiltins/throwableImpl.kt +++ b/compiler/testData/codegen/box/specialBuiltins/throwableImpl.kt @@ -1,3 +1,6 @@ +// Super calls to Throwable properties are not supported +// IGNORE_BACKEND: JS_IR + class MyThrowable(message: String? = null, cause: Throwable? = null) : Throwable(message, cause) { override val message: String? diff --git a/libraries/stdlib/js-ir/build.gradle b/libraries/stdlib/js-ir/build.gradle index a2ee11822ea..574d63ed315 100644 --- a/libraries/stdlib/js-ir/build.gradle +++ b/libraries/stdlib/js-ir/build.gradle @@ -28,7 +28,6 @@ task prepareNativeBuiltinsSources(type: Sync) { include "Nothing.kt" include "Number.kt" include "String.kt" - include "Throwable.kt" } into nativeBuiltinsSrcDir diff --git a/libraries/stdlib/js-ir/builtins/Throwable.kt b/libraries/stdlib/js-ir/builtins/Throwable.kt new file mode 100644 index 00000000000..83fdc12aedf --- /dev/null +++ b/libraries/stdlib/js-ir/builtins/Throwable.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin + +/** + * 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. + */ +@JsName("Error") +public external open class Throwable { + open val message: String? + open val cause: Throwable? + + constructor(message: String?, cause: Throwable?) + constructor(message: String?) + constructor(cause: Throwable?) + constructor() +} \ No newline at end of file diff --git a/libraries/stdlib/js-ir/runtime/coreRuntime.kt b/libraries/stdlib/js-ir/runtime/coreRuntime.kt index e24f5454c3a..45163037654 100644 --- a/libraries/stdlib/js-ir/runtime/coreRuntime.kt +++ b/libraries/stdlib/js-ir/runtime/coreRuntime.kt @@ -77,7 +77,6 @@ fun getStringHashCode(str: String): Int { fun identityHashCode(obj: Any?): Int = getObjectHashCode(obj) -@JsName("captureStack") internal fun captureStack(instance: Throwable) { if (js("Error").captureStackTrace != null) { js("Error").captureStackTrace(instance, instance::class.js) @@ -86,17 +85,19 @@ internal fun captureStack(instance: Throwable) { } } -@JsName("newThrowable") internal fun newThrowable(message: String?, cause: Throwable?): Throwable { val throwable = js("new Error()") - throwable.message = if (message == null) { - if (cause != null) (cause.asDynamic().toString)() else null - } else { - message - } + throwable.message = message ?: cause?.toString() throwable.cause = cause throwable.name = "Throwable" - return throwable + return throwable.unsafeCast() +} + +internal fun extendThrowable(this_: dynamic, message: String?, cause: Throwable?) { + js("Error").call(this_) + this_.message = message ?: cause?.toString() + this_.cause = cause + captureStack(this_) } internal fun boxIntrinsic(x: T): R = error("Should be lowered")