[JS IR BE] Implement lowering to connect kotlin Throwable with JS Error
This commit is contained in:
@@ -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
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
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)
|
||||
@@ -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("<code>")
|
||||
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
|
||||
|
||||
@@ -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("<get-context>")
|
||||
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<IrFunction>().atMostOne { it.descriptor.name == CONTINUATION_CONTEXT_GETTER_NAME }
|
||||
?: continuation.owner.declarations.filterIsInstance<IrProperty>().atMostOne { it.descriptor.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!!
|
||||
continuation.owner.declarations.filterIsInstance<IrFunction>().atMostOne { it.name == CONTINUATION_CONTEXT_GETTER_NAME }
|
||||
?: continuation.owner.declarations.filterIsInstance<IrProperty>().atMostOne { it.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!!
|
||||
return contextGetter.symbol
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@ private fun JsIrBackendContext.performInlining(moduleFragment: IrModuleFragment)
|
||||
}
|
||||
|
||||
private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment, dependencies: List<IrModuleFragment>) {
|
||||
moduleFragment.files.forEach(ThrowableSuccessorsLowering(this)::lower)
|
||||
moduleFragment.files.forEach(UnitMaterializationLowering(this)::lower)
|
||||
moduleFragment.files.forEach(EnumClassLowering(this)::runOnFilePostfix)
|
||||
moduleFragment.files.forEach(EnumUsageLowering(this)::lower)
|
||||
|
||||
+309
@@ -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<IrConstructor>()
|
||||
|
||||
private val defaultCtor = throwableConstructors.single { it.valueParameters.size == 0 }
|
||||
private val toString =
|
||||
throwableClass.declarations.filterIsInstance<IrSimpleFunction>().single { it.name == Name.identifier("toString") }
|
||||
|
||||
private val messagePropertyName = Name.identifier("message")
|
||||
private val causePropertyName = Name.identifier("cause")
|
||||
|
||||
private val messageGetter =
|
||||
throwableClass.declarations.filterIsInstance<IrFunction>().atMostOne { it.name == Name.special("<get-message>") }
|
||||
?: throwableClass.declarations.filterIsInstance<IrProperty>().atMostOne { it.name == messagePropertyName }?.getter!!
|
||||
private val causeGetter =
|
||||
throwableClass.declarations.filterIsInstance<IrFunction>().atMostOne { it.name == Name.special("<get-cause>") }
|
||||
?: throwableClass.declarations.filterIsInstance<IrProperty>().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<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.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<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, 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<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?.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<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 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<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)
|
||||
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<IrProperty>().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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
-1
@@ -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<JsStatement> {
|
||||
val functions = irClass.declarations.filterIsInstance<IrSimpleFunction>()
|
||||
|
||||
val messageGetter = functions.single { it.name == Name.special("<get-message>") }
|
||||
val causeGetter = functions.single { it.name == Name.special("<get-cause>") }
|
||||
|
||||
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<JsStatement> {
|
||||
val instanceVarName = className.objectInstanceName()
|
||||
val getInstanceFunName = "${className.ident}_getInstance"
|
||||
|
||||
+10
@@ -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]
|
||||
|
||||
+6
-1
@@ -85,4 +85,9 @@ val IrFunction.isStatic: Boolean
|
||||
|
||||
fun JsStatement.asBlock() = this as? JsBlock ?: JsBlock(this)
|
||||
|
||||
fun JsName.objectInstanceName() = "${ident}_instance"
|
||||
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())
|
||||
}
|
||||
Reference in New Issue
Block a user