diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt index 08ceb54b5b9..033e0acc3b9 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt @@ -12,7 +12,10 @@ import org.jetbrains.kotlin.ir.declarations.IrPackageFragment import org.jetbrains.kotlin.ir.declarations.IrTypeParameter import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol -import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.types.IrDynamicType +import org.jetbrains.kotlin.ir.types.IrSimpleType +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.utils.DFS @@ -73,4 +76,16 @@ fun IrType.isNullable(): Boolean = DFS.ifAny(listOf(this), { it.typeParameterSup is IrSimpleType -> it.hasQuestionMark else -> it is IrDynamicType } -}) \ No newline at end of file +}) + + +fun IrType.isThrowable(): Boolean { + if (this is IrSimpleType) { + val classClassifier = classifier as? IrClassSymbol ?: return false + if (classClassifier.owner.name.asString() != "Throwable") return false + val parent = classClassifier.owner.parent as? IrPackageFragment ?: return false + return parent.fqName == kotlinPackageFqn + } else return false +} + +fun IrType.isThrowableTypeOrSubtype() = DFS.ifAny(listOf(this), IrType::superTypes, IrType::isThrowable) \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt index a060a22dd13..d9d2cc411e6 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.ir.backend.js -import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.utils.Namer @@ -20,7 +19,6 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi2ir.findSingleFunction import org.jetbrains.kotlin.types.KotlinType - import java.util.* class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendContext) { @@ -139,6 +137,7 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC // Other: val jsObjectCreate = defineObjectCreateIntrinsic() // Object.create + val jsGetJSField = defineGetJSPropertyIntrinsic() // till we don't have dynamic type we use intrinsic which sets a field with any name val jsSetJSField = defineSetJSPropertyIntrinsic() // till we don't have dynamic type we use intrinsic which sets a field with any name val jsCode = getInternalFunction("js") // js("") val jsHashCode = getInternalFunction("hashCode") @@ -275,6 +274,16 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC externalPackageFragment.declarations += it } + private fun defineGetJSPropertyIntrinsic() = + JsIrBuilder.buildFunction("\$getJSProperty\$", origin = JsLoweredDeclarationOrigin.JS_INTRINSICS_STUB).also { + it.returnType = irBuiltIns.anyNType + listOf("receiver", "fieldName").mapIndexedTo(it.valueParameters) { i, p -> + JsIrBuilder.buildValueParameter(p, i, irBuiltIns.anyType).also { v -> v.parent = it } + } + it.parent = externalPackageFragment + externalPackageFragment.declarations += it + } + private fun defineSetJSPropertyIntrinsic() = JsIrBuilder.buildFunction("\$setJSProperty\$", origin = JsLoweredDeclarationOrigin.JS_INTRINSICS_STUB).also { it.returnType = irBuiltIns.unitType 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 0e6ea87fd0d..34a8d58762e 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 @@ -84,6 +84,8 @@ class JsIrBackendContext( } companion object { + val KOTLIN_PACKAGE_FQN = FqName.fromSegments(listOf("kotlin")) + private val INTRINSICS_PACKAGE_NAME = Name.identifier("intrinsics") private val COROUTINE_SUSPENDED_NAME = Name.identifier("COROUTINE_SUSPENDED") private val COROUTINE_CONTEXT_NAME = Name.identifier("coroutineContext") @@ -91,10 +93,10 @@ class JsIrBackendContext( private val CONTINUATION_NAME = Name.identifier("Continuation") // TODO: what is more clear way reference this getter? private val CONTINUATION_CONTEXT_GETTER_NAME = Name.special("") - private val CONTINUATION_CONTEXT_PROPERTY_NAME = Name.identifier("context") - private val REFLECT_PACKAGE_FQNAME = FqName.fromSegments(listOf("kotlin", "reflect")) - private val JS_PACKAGE_FQNAME = FqName.fromSegments(listOf("kotlin", "js")) + private val CONTINUATION_CONTEXT_PROPERTY_NAME = Name.identifier("context") + private val REFLECT_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("reflect")) + private val JS_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("js")) private val JS_INTERNAL_PACKAGE_FQNAME = JS_PACKAGE_FQNAME.child(Name.identifier("internal")) private val COROUTINE_PACKAGE_FQNAME_12 = FqName.fromSegments(listOf("kotlin", "coroutines", "experimental")) private val COROUTINE_PACKAGE_FQNAME_13 = FqName.fromSegments(listOf("kotlin", "coroutines")) @@ -122,8 +124,8 @@ class JsIrBackendContext( ) as ClassDescriptor ) val contextGetter = - continuation.owner.declarations.filterIsInstance().atMostOne { it.descriptor.name == CONTINUATION_CONTEXT_GETTER_NAME } - ?: continuation.owner.declarations.filterIsInstance().atMostOne { it.descriptor.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!! + continuation.owner.declarations.filterIsInstance().atMostOne { it.name == CONTINUATION_CONTEXT_GETTER_NAME } + ?: continuation.owner.declarations.filterIsInstance().atMostOne { it.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!! return contextGetter.symbol } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index e0700c925fd..1df2730c592 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -104,6 +104,7 @@ private fun JsIrBackendContext.performInlining(moduleFragment: IrModuleFragment) } private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment, dependencies: List) { + moduleFragment.files.forEach(ThrowableSuccessorsLowering(this)::lower) moduleFragment.files.forEach(UnitMaterializationLowering(this)::lower) moduleFragment.files.forEach(EnumClassLowering(this)::runOnFilePostfix) moduleFragment.files.forEach(EnumUsageLowering(this)::lower) 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 new file mode 100644 index 00000000000..69c04bdc47a --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableSuccessorsLowering.kt @@ -0,0 +1,309 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.WrappedPropertyDescriptor +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.IrCall +import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression +import org.jetbrains.kotlin.ir.expressions.impl.* +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.util.isThrowable +import org.jetbrains.kotlin.ir.visitors.* +import org.jetbrains.kotlin.name.Name + +class ThrowableSuccessorsLowering(context: JsIrBackendContext) : FileLoweringPass { + private val unitType = context.irBuiltIns.unitType + private val nothingNType = context.irBuiltIns.nothingNType + private val nothingType = context.irBuiltIns.nothingType + private val stringType = context.irBuiltIns.stringType + + private val propertyGetter = context.intrinsics.jsGetJSField.symbol + private val propertySetter = context.intrinsics.jsSetJSField.symbol + + private val messageName = JsIrBuilder.buildString(stringType, "message") + private val causeName = JsIrBuilder.buildString(stringType, "cause") + private val nameName = JsIrBuilder.buildString(stringType, "name") + + private val throwableClass = context.symbolTable.referenceClass( + context.getClass(JsIrBackendContext.KOTLIN_PACKAGE_FQN.child(Name.identifier("Throwable"))) + ).owner + private val throwableConstructors = throwableClass.declarations.filterIsInstance() + + private val defaultCtor = throwableConstructors.single { it.valueParameters.size == 0 } + private val toString = + throwableClass.declarations.filterIsInstance().single { it.name == Name.identifier("toString") } + + private val messagePropertyName = Name.identifier("message") + private val causePropertyName = Name.identifier("cause") + + private val messageGetter = + throwableClass.declarations.filterIsInstance().atMostOne { it.name == Name.special("") } + ?: throwableClass.declarations.filterIsInstance().atMostOne { it.name == messagePropertyName }?.getter!! + private val causeGetter = + throwableClass.declarations.filterIsInstance().atMostOne { it.name == Name.special("") } + ?: throwableClass.declarations.filterIsInstance().atMostOne { it.name == causePropertyName }?.getter!! + + private val captureStackFunction = context.symbolTable.referenceSimpleFunction(context.getInternalFunctions("captureStack").single()) + private val newThrowableFunction = context.symbolTable.referenceSimpleFunction(context.getInternalFunctions("newThrowable").single()) + 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.transformChildrenVoid(ThrowablePropertiesUsageTransformer()) + irFile.transformChildrenVoid(ThrowableInstanceCreationLowering()) + } + + inner class ThrowableInstanceCreationLowering : IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression { + if (expression.symbol.owner !in throwableConstructors) return super.visitCall(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, messageGetter.returnType) + val causeField = createBackingField(declaration, causePropertyName, causeGetter.returnType) + val existedMessageAccessor = ownPropertyAccessor(declaration, messageGetter) + if (existedMessageAccessor.origin == IrDeclarationOrigin.FAKE_OVERRIDE) + createPropertyAccessor(existedMessageAccessor, messageField) + val existedCauseAccessor = ownPropertyAccessor(declaration, causeGetter) + if (existedCauseAccessor.origin == IrDeclarationOrigin.FAKE_OVERRIDE) + createPropertyAccessor(existedCauseAccessor, causeField) + + pendingSuperUsages += DirectThrowableSuccessors(declaration, messageField, causeField) + } + } + + private fun createBackingField(declaration: IrClass, name: Name, type: IrType): IrField { + val fieldDescriptor = WrappedPropertyDescriptor() + 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) { + val name = fakeAccessor.name + val function = JsIrBuilder.buildFunction(name).apply { + parent = fakeAccessor.parent + overriddenSymbols += fakeAccessor.overriddenSymbols + returnType = fakeAccessor.returnType + correspondingProperty = fakeAccessor.correspondingProperty + dispatchReceiverParameter = fakeAccessor.dispatchReceiverParameter + } + + 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)) + + fakeAccessor.correspondingProperty?.getter = 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?.owner != throwableClass) return super.visitCall(expression, data) + + expression.transformChildren(this, data) + + val superField = when { + expression.symbol.owner == messageGetter -> successor.message + expression.symbol.owner == 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.owner !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.symbol, defaultCtor.descriptor) + } + + val klass = successor.klass + val receiver = IrGetValueImpl(expression.startOffset, expression.endOffset, klass.thisReceiver!!.symbol) + + val nameArg = JsIrBuilder.buildString(stringType, klass.name.asString()) + + val fillStatements = fillThrowableInstance(expression, receiver, messageArg, causeArg, nameArg) + + return expression.run { + IrCompositeImpl(startOffset, endOffset, type, origin, paramStatements + newDelegation + fillStatements) + } + } + + private fun fillThrowableInstance( + expression: IrFunctionAccessExpression, + receiver: IrExpression, + messageArg: IrExpression, + causeArg: IrExpression, + name: 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 setName = IrCallImpl(expression.startOffset, expression.endOffset, unitType, propertySetter).apply { + putValueArgument(0, receiver) + putValueArgument(1, nameName) + putValueArgument(2, name) + } + + val setStackTrace = IrCallImpl(expression.startOffset, expression.endOffset, unitType, captureStackFunction).apply { + putValueArgument(0, receiver) + } + + return listOf(setMessage, setCause, setName, 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) + Triple(JsIrBuilder.buildGetValue(irValM.symbol), 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() -> { + val messageExpr = JsIrBuilder.buildCall(toString.symbol, stringType).apply { + dispatchReceiver = argValue + } + Triple(messageExpr, argValue, listOf(irVal)) + } + else -> Triple(argValue, nullValue, listOf(irVal)) + } + } + } + } + } + + private fun isDirectChildOfThrowable(irClass: IrClass) = irClass.superTypes.any { it.isThrowable() } + private fun ownPropertyAccessor(irClass: IrClass, irBase: IrFunction) = + irClass.declarations.filterIsInstance().mapNotNull { it.getter } + .single { it.overriddenSymbols.any { s -> s.owner == 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.owner + 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/JsClassGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt index 2031c1326ab..850cb04843f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt @@ -10,11 +10,14 @@ import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext import org.jetbrains.kotlin.ir.backend.js.utils.Namer import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol +import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.types.isAny import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.js.backend.ast.* +import org.jetbrains.kotlin.name.Name class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) { @@ -55,10 +58,45 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo classBlock.statements += generateClassMetadata() irClass.onlyIf({ kind == ClassKind.OBJECT }) { classBlock.statements += maybeGenerateObjectInstance() } + if (irClass.superTypes.any { it.isThrowable() }) { + classBlock.statements += genereateThrowableProperties() + } context.staticContext.classModels[className] = classModel return classBlock } + private fun genereateThrowableProperties(): List { + val functions = irClass.declarations.filterIsInstance() + + val messageGetter = functions.single { it.name == Name.special("") } + val causeGetter = functions.single { it.name == Name.special("") } + + val msgProperty = defineProperty(classPrototypeRef, "message") { + val literal = JsObjectLiteral(true) + val function = buildGetterFunction(messageGetter) + literal.apply { + propertyInitializers += JsPropertyInitializer(JsStringLiteral("get"), function) + } + } + + val causeProperty = defineProperty(classPrototypeRef, "cause") { + val literal = JsObjectLiteral(true) + val function = buildGetterFunction(causeGetter) + literal.apply { + propertyInitializers += JsPropertyInitializer(JsStringLiteral("get"), function) + } + } + + return listOf(msgProperty.makeStmt(), causeProperty.makeStmt()) + } + + private fun buildGetterFunction(delegate: IrSimpleFunction): JsFunction { + val getterName = context.getNameForSymbol(delegate.symbol) + val returnStatement = JsReturn(JsInvocation(JsNameRef(getterName, JsThisRef()))) + + return JsFunction(JsFunctionScope(context.currentScope, ""), JsBlock(returnStatement), "") + } + private fun generateMemberFunction(declaration: IrSimpleFunction): JsStatement? { val translatedFunction = declaration.run { if (isReal) accept(IrFunctionToJsTransformer(), context) else null } @@ -77,7 +115,13 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo // II.prototype.foo = I.prototype.foo if (!irClass.isInterface) { declaration.resolveFakeOverride()?.let { - val implClassDeclaration = it.parent as IrClass + var implClassDeclaration = it.parent as IrClass + + // special case + if (implClassDeclaration.defaultType.isThrowable()) { + implClassDeclaration = throwableAccessor() + } + if (!implClassDeclaration.defaultType.isAny() && !it.isEffectivelyExternal()) { val implMethodName = context.getNameForSymbol(it.symbol) val implClassName = context.getNameForSymbol(implClassDeclaration.symbol) @@ -93,6 +137,18 @@ 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 maybeGenerateObjectInstance(): List { val instanceVarName = className.objectInstanceName() val getInstanceFunName = "${className.ident}_getInstance" diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt index c18b2820e67..113faf571ca 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt @@ -79,6 +79,16 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) { JsInvocation(Namer.JS_OBJECT_CREATE_FUNCTION, prototype) } + add(intrinsics.jsGetJSField) { call, context -> + val args = translateCallArguments(call, context) + val receiver = args[0] + val fieldName = args[1] as JsStringLiteral + + val fieldNameLiteral = fieldName.value!! + + JsNameRef(fieldNameLiteral, receiver) + } + add(intrinsics.jsSetJSField) { call, context -> val args = translateCallArguments(call, context) val receiver = args[0] diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt index 03ad060a371..ebd00ea2fe5 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt @@ -85,4 +85,9 @@ val IrFunction.isStatic: Boolean fun JsStatement.asBlock() = this as? JsBlock ?: JsBlock(this) -fun JsName.objectInstanceName() = "${ident}_instance" \ No newline at end of file +fun JsName.objectInstanceName() = "${ident}_instance" + +fun defineProperty(receiver: JsExpression, name: String, value: () -> JsExpression): JsInvocation { + val objectDefineProperty = JsNameRef("defineProperty", Namer.JS_OBJECT) + return JsInvocation(objectDefineProperty, receiver, JsStringLiteral(name), value()) +} \ No newline at end of file