[JS IR BE] Simplify throwable lowering.

- Make kotlin.Throwable class extenral
- Add runtime function 'extendThrowable' to use instead of delegating
  constructors to Throwable
This commit is contained in:
Svyatoslav Kuzmich
2019-07-02 17:43:13 +03:00
parent 6d1f893a5a
commit 918d470e1b
12 changed files with 188 additions and 448 deletions
@@ -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())
@@ -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"
)
@@ -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<IrDeclarationParent> {
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)
}
}
}
}
}
@@ -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<IrSimpleFunction>().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<IrFunction>().atMostOne { it.name == Name.special("<get-message>") }?.symbol
?: throwableClass.owner.declarations.filterIsInstance<IrProperty>().atMostOne { it.name == messagePropertyName }?.getter?.symbol!!
}
private val causeGetter by lazy {
throwableClass.owner.declarations.filterIsInstance<IrFunction>().atMostOne { it.name == Name.special("<get-cause>") }?.symbol
?: throwableClass.owner.declarations.filterIsInstance<IrProperty>().atMostOne { it.name == causePropertyName }?.getter?.symbol!!
}
private val captureStackFunction = context.captureStackSymbol
private val newThrowableFunction = context.newThrowableSymbol
private val pendingSuperUsages = mutableListOf<DirectThrowableSuccessors>()
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<IrDeclarationParent> {
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<IrExpression, IrExpression> {
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<IrDeclarationParent> {
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<IrStatement> {
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<IrExpression, IrExpression, List<IrStatement>> {
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<IrProperty>().mapNotNull { it.getter }
.singleOrNull { it.overriddenSymbols.any { s -> s == irBase } }
?: irClass.declarations.filterIsInstance<IrSimpleFunction>().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
}
}
}
}
@@ -25,7 +25,6 @@ class IrFunctionToJsTransformer : BaseIrElementToJsNodeTransformer<JsFunction, J
override fun visitConstructor(declaration: IrConstructor, context: JsGenerationContext): JsFunction {
assert(declaration.isPrimary)
val funcName = context.getNameForConstructor(declaration)
val constructedClass = declaration.parent as IrClass
return translateFunction(declaration, funcName, context)
}
}
@@ -35,11 +35,9 @@ class IrModuleToJsTransformer(
// TODO: fix it up with new name generator
val anyName = context.getNameForClass(backendContext.irBuiltIns.anyClass.owner)
val throwableName = context.getNameForClass(backendContext.irBuiltIns.throwableClass.owner)
val stringName = context.getNameForClass(backendContext.irBuiltIns.stringClass.owner)
statements += JsVars(JsVars.JsVar(anyName, Namer.JS_OBJECT))
statements += JsVars(JsVars.JsVar(throwableName, Namer.JS_ERROR))
statements += JsVars(JsVars.JsVar(stringName, JsNameRef("String")))
val preDeclarationBlock = JsBlock()
@@ -5,8 +5,6 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.backend.common.onlyIf
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
@@ -66,69 +64,43 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
}
classBlock.statements += generateClassMetadata()
if (irClass.superTypes.any { it.isThrowable() }) {
classBlock.statements += generateThrowableProperties()
} else if (!irClass.defaultType.isThrowable()) {
// TODO: Test export properties of throwable subtype
if (!irClass.isInterface && !irClass.isEnumClass && !irClass.isEnumEntry) {
for (property in properties) {
if (property.getter?.extensionReceiverParameter != null || property.setter?.extensionReceiverParameter != null)
continue
if (property.visibility != Visibilities.PUBLIC)
continue
if (!irClass.isInterface && !irClass.isEnumClass && !irClass.isEnumEntry) {
for (property in properties) {
if (property.getter?.extensionReceiverParameter != null || property.setter?.extensionReceiverParameter != null)
continue
if (property.origin == IrDeclarationOrigin.FAKE_OVERRIDE)
continue
if (property.visibility != Visibilities.PUBLIC)
continue
fun IrSimpleFunction.accessorRef(): JsNameRef? =
when (visibility) {
Visibilities.PRIVATE -> 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<JsStatement> {
val functions = irClass.declarations.filterIsInstance<IrSimpleFunction>()
// TODO: Fix `Name.special` deserialization
val messageGetter = functions.single { it.name.asString() == "<get-message>" }
val causeGetter = functions.single { it.name.asString() == "<get-cause>" }
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}")
@@ -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)
@@ -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?
-1
View File
@@ -28,7 +28,6 @@ task prepareNativeBuiltinsSources(type: Sync) {
include "Nothing.kt"
include "Number.kt"
include "String.kt"
include "Throwable.kt"
}
into nativeBuiltinsSrcDir
@@ -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()
}
@@ -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<Throwable>()
}
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 <T, R> boxIntrinsic(x: T): R = error("Should be lowered")