[JS IR BE] Implement lowering to connect kotlin Throwable with JS Error

This commit is contained in:
romanart
2018-10-12 15:43:08 +03:00
parent 1a86411139
commit eb2a33ebee
8 changed files with 418 additions and 11 deletions
@@ -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)
@@ -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
}
}
}
}
@@ -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"
@@ -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]
@@ -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())
}