[JS BE] implement es6-classes's codegen

This commit is contained in:
Vitaly
2020-05-09 14:16:32 +03:00
committed by romanart
parent 90bcf8bf88
commit adf6831d7f
5 changed files with 67 additions and 14 deletions
@@ -15,14 +15,13 @@ import org.jetbrains.kotlin.ir.backend.js.utils.isAssociatedObjectAnnotatedAnnot
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
fun eliminateDeadDeclarations(
@@ -320,6 +319,22 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
toStringMethod.enqueue("intrinsic: jsPlus")
}
}
context.intrinsics.jsConstruct -> {
val callType = expression.getTypeArgument(0)!!
val constructor = callType.getClass()!!.primaryConstructor
constructor!!.enqueue("ctor call from jsConstruct-intrinsic")
}
context.intrinsics.es6DefaultType -> {
//same as jsClass
val ref = expression.getTypeArgument(0)!!.classifierOrFail.owner as IrDeclaration
ref.enqueue("intrinsic: jsClass")
referencedJsClasses += ref
//Generate klass in `val currResultType = resultType || klass`
val arg = expression.getTypeArgument(0)!!
val klass = arg.getClass()
constructedClasses.addIfNotNull(klass)
}
}
}
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.ir.backend.js.lower.ES6AddInternalParametersToConstructorPhase
import org.jetbrains.kotlin.ir.backend.js.lower.ES6AddInternalParametersToConstructorPhase.*
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
import org.jetbrains.kotlin.ir.backend.js.utils.isJsExport
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
@@ -79,7 +81,8 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
private fun exportConstructor(constructor: IrConstructor): ExportedDeclaration? {
if (!constructor.isPrimary) return null
val allValueParameters = listOfNotNull(constructor.extensionReceiverParameter) + constructor.valueParameters
val allValueParameters = listOfNotNull(constructor.extensionReceiverParameter) +
constructor.valueParameters.filterNot { it.origin === ES6_RESULT_TYPE_PARAMETER || it.origin === ES6_INIT_BOX_PARAMETER }
return ExportedConstructor(allValueParameters.map { exportParameter(it) })
}
@@ -135,7 +135,11 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
return JsBinaryOperation(JsBinaryOperator.ASG, thisRef, arguments.single())
}
return JsInvocation(callFuncRef, listOf(thisRef) + arguments)
return if (context.staticContext.backendContext.es6mode) {
JsInvocation(JsNameRef("super"), arguments)
} else {
JsInvocation(callFuncRef, listOf(thisRef) + arguments)
}
}
override fun visitConstructorCall(expression: IrConstructorCall, context: JsGenerationContext): JsExpression {
@@ -14,6 +14,7 @@ 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.classifierOrNull
import org.jetbrains.kotlin.ir.types.isAny
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.backend.ast.*
@@ -31,25 +32,55 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
private val classBlock = JsGlobalBlock()
private val classModel = JsIrClassModel(irClass)
private val es6mode = context.staticContext.backendContext.es6mode
fun generate(): JsStatement {
assert(!irClass.descriptor.isExpect)
maybeGeneratePrimaryConstructor()
if (!es6mode) maybeGeneratePrimaryConstructor()
val transformer = IrDeclarationToJsTransformer()
// Properties might be lowered out of classes
// We'll use IrSimpleFunction::correspondingProperty to collect them into set
val properties = mutableSetOf<IrProperty>()
val jsClass = JsClass(name = className)
if (baseClass != null && !baseClass.isAny()) {
jsClass.baseClass = baseClassName?.makeRef()
}
if (es6mode) classModel.preDeclarationBlock.statements += jsClass.makeStmt()
for (declaration in irClass.declarations) {
when (declaration) {
is IrConstructor -> {
classBlock.statements += declaration.accept(transformer, context)
classModel.preDeclarationBlock.statements += generateInheritanceCode()
if (es6mode) {
declaration.accept(IrFunctionToJsTransformer(), context).let {
//HACK: add superCall to Error
if ((baseClass?.classifierOrNull?.owner as? IrClass)?.symbol === context.staticContext.backendContext.throwableClass) {
it.body.statements.add(0, JsInvocation(JsNameRef("super")).makeStmt())
}
if (it.body.statements.any { it !is JsEmpty }) {
jsClass.constructor = it
}
}
} else {
classBlock.statements += declaration.accept(transformer, context)
classModel.preDeclarationBlock.statements += generateInheritanceCode()
}
}
is IrSimpleFunction -> {
properties.addIfNotNull(declaration.correspondingPropertySymbol?.owner)
generateMemberFunction(declaration)?.let { classBlock.statements += it }
if (es6mode) {
val (_, function) = generateMemberFunction(declaration)
function?.let { jsClass.members += it }
} else {
val (memberRef, function) = generateMemberFunction(declaration)
function?.let { classBlock.statements += jsAssignment(memberRef, it.apply { name = null }).makeStmt() }
}
}
is IrClass -> {
classBlock.statements += JsClassGenerator(declaration, context).generate()
@@ -111,17 +142,15 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
return this.overriddenSymbols.any { it.owner.overridesExternal() }
}
private fun generateMemberFunction(declaration: IrSimpleFunction): JsStatement? {
private fun generateMemberFunction(declaration: IrSimpleFunction): Pair<JsNameRef, JsFunction?> {
val memberName = context.getNameForMemberFunction(declaration.realOverrideTarget)
val memberRef = JsNameRef(memberName, classPrototypeRef)
if (declaration.isReal && declaration.body != null) {
val translatedFunction = declaration.accept(IrFunctionToJsTransformer(), context)
assert(!declaration.isStaticMethodOfClass)
return jsAssignment(memberRef, translatedFunction.apply { name = null }).makeStmt()
return Pair(memberRef, translatedFunction)
}
// do not generate code like
@@ -144,7 +173,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
}
}
return null
return Pair(memberRef, null)
}
private fun maybeGeneratePrimaryConstructor() {
@@ -208,6 +208,8 @@ fun translateCallArguments(expression: IrMemberAccessExpression, context: JsGene
val argument = expression.getValueArgument(index)
val result = argument?.accept(transformer, context)
if (result == null) {
if (context.staticContext.backendContext.es6mode) return@mapTo JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(2))
assert(expression is IrFunctionAccessExpression && expression.symbol.owner.isExternalOrInheritedFromExternal())
JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1))
} else