[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.declarations.*
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.* import java.util.*
fun eliminateDeadDeclarations( fun eliminateDeadDeclarations(
@@ -320,6 +319,22 @@ fun usefulDeclarations(roots: Iterable<IrDeclaration>, context: JsIrBackendConte
toStringMethod.enqueue("intrinsic: jsPlus") 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.Modality
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.backend.js.* 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.getJsNameOrKotlinName
import org.jetbrains.kotlin.ir.backend.js.utils.isJsExport import org.jetbrains.kotlin.ir.backend.js.utils.isJsExport
import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName import org.jetbrains.kotlin.ir.backend.js.utils.sanitizeName
@@ -79,7 +81,8 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
private fun exportConstructor(constructor: IrConstructor): ExportedDeclaration? { private fun exportConstructor(constructor: IrConstructor): ExportedDeclaration? {
if (!constructor.isPrimary) return null 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) }) return ExportedConstructor(allValueParameters.map { exportParameter(it) })
} }
@@ -135,7 +135,11 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
return JsBinaryOperation(JsBinaryOperator.ASG, thisRef, arguments.single()) 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 { 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.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail 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.types.isAny
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.backend.ast.* 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 classBlock = JsGlobalBlock()
private val classModel = JsIrClassModel(irClass) private val classModel = JsIrClassModel(irClass)
private val es6mode = context.staticContext.backendContext.es6mode
fun generate(): JsStatement { fun generate(): JsStatement {
assert(!irClass.descriptor.isExpect) assert(!irClass.descriptor.isExpect)
maybeGeneratePrimaryConstructor() if (!es6mode) maybeGeneratePrimaryConstructor()
val transformer = IrDeclarationToJsTransformer() val transformer = IrDeclarationToJsTransformer()
// Properties might be lowered out of classes // Properties might be lowered out of classes
// We'll use IrSimpleFunction::correspondingProperty to collect them into set // We'll use IrSimpleFunction::correspondingProperty to collect them into set
val properties = mutableSetOf<IrProperty>() 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) { for (declaration in irClass.declarations) {
when (declaration) { when (declaration) {
is IrConstructor -> { is IrConstructor -> {
classBlock.statements += declaration.accept(transformer, context) if (es6mode) {
classModel.preDeclarationBlock.statements += generateInheritanceCode() 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 -> { is IrSimpleFunction -> {
properties.addIfNotNull(declaration.correspondingPropertySymbol?.owner) 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 -> { is IrClass -> {
classBlock.statements += JsClassGenerator(declaration, context).generate() 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() } 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 memberName = context.getNameForMemberFunction(declaration.realOverrideTarget)
val memberRef = JsNameRef(memberName, classPrototypeRef) val memberRef = JsNameRef(memberName, classPrototypeRef)
if (declaration.isReal && declaration.body != null) { if (declaration.isReal && declaration.body != null) {
val translatedFunction = declaration.accept(IrFunctionToJsTransformer(), context) val translatedFunction = declaration.accept(IrFunctionToJsTransformer(), context)
assert(!declaration.isStaticMethodOfClass) assert(!declaration.isStaticMethodOfClass)
return jsAssignment(memberRef, translatedFunction.apply { name = null }).makeStmt() return Pair(memberRef, translatedFunction)
} }
// do not generate code like // 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() { private fun maybeGeneratePrimaryConstructor() {
@@ -208,6 +208,8 @@ fun translateCallArguments(expression: IrMemberAccessExpression, context: JsGene
val argument = expression.getValueArgument(index) val argument = expression.getValueArgument(index)
val result = argument?.accept(transformer, context) val result = argument?.accept(transformer, context)
if (result == null) { if (result == null) {
if (context.staticContext.backendContext.es6mode) return@mapTo JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(2))
assert(expression is IrFunctionAccessExpression && expression.symbol.owner.isExternalOrInheritedFromExternal()) assert(expression is IrFunctionAccessExpression && expression.symbol.owner.isExternalOrInheritedFromExternal())
JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1)) JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1))
} else } else