Add Secondary constructor lowering

This commit is contained in:
Roman Artemev
2018-03-29 12:26:14 +03:00
parent 3ce324fa9d
commit 2bea8816a6
16 changed files with 469 additions and 108 deletions
@@ -37,14 +37,18 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import java.util.*
class InitializersLowering(val context: CommonBackendContext, val declarationOrigin: IrDeclarationOrigin) : ClassLoweringPass {
class InitializersLowering(
val context: CommonBackendContext,
val declarationOrigin: IrDeclarationOrigin,
private val clinitNeeded: Boolean
) : ClassLoweringPass {
override fun lower(irClass: IrClass) {
val classInitializersBuilder = ClassInitializersBuilder(irClass)
irClass.acceptChildrenVoid(classInitializersBuilder)
classInitializersBuilder.transformInstanceInitializerCallsInConstructors(irClass)
classInitializersBuilder.createStaticInitializationMethod(irClass)
if (clinitNeeded) classInitializersBuilder.createStaticInitializationMethod(irClass)
}
private inner class ClassInitializersBuilder(val irClass: IrClass) : IrElementVisitorVoid {
@@ -60,22 +64,23 @@ class InitializersLowering(val context: CommonBackendContext, val declarationOri
val irFieldInitializer = declaration.initializer?.expression ?: return
val receiver =
if (declaration.descriptor.dispatchReceiverParameter != null) // TODO isStaticField
IrGetValueImpl(irFieldInitializer.startOffset, irFieldInitializer.endOffset,
irClass.descriptor.thisAsReceiverParameter)
else null
if (declaration.descriptor.dispatchReceiverParameter != null) // TODO isStaticField
IrGetValueImpl(
irFieldInitializer.startOffset, irFieldInitializer.endOffset,
irClass.descriptor.thisAsReceiverParameter
)
else null
val irSetField = IrSetFieldImpl(
irFieldInitializer.startOffset, irFieldInitializer.endOffset,
declaration.descriptor,
receiver,
irFieldInitializer,
null, null
irFieldInitializer.startOffset, irFieldInitializer.endOffset,
declaration.descriptor,
receiver,
irFieldInitializer,
null, null
)
if (DescriptorUtils.isStaticDeclaration(declaration.descriptor)) {
staticInitializerStatements.add(irSetField)
}
else {
} else {
instanceInitializerStatements.add(irSetField)
}
}
@@ -100,15 +105,17 @@ class InitializersLowering(val context: CommonBackendContext, val declarationOri
SourceElement.NO_SOURCE
)
staticInitializerDescriptor.initialize(
null, null, emptyList(), emptyList(),
irClass.descriptor.builtIns.unitType,
Modality.FINAL, Visibilities.PUBLIC
null, null, emptyList(), emptyList(),
irClass.descriptor.builtIns.unitType,
Modality.FINAL, Visibilities.PUBLIC
)
irClass.declarations.add(
IrFunctionImpl(irClass.startOffset, irClass.endOffset, declarationOrigin,
staticInitializerDescriptor,
IrBlockBodyImpl(irClass.startOffset, irClass.endOffset,
staticInitializerStatements.map { it.copy() }))
IrFunctionImpl(
irClass.startOffset, irClass.endOffset, declarationOrigin,
staticInitializerDescriptor,
IrBlockBodyImpl(irClass.startOffset, irClass.endOffset,
staticInitializerStatements.map { it.copy() })
)
)
}
}
@@ -10,4 +10,6 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
object JsLoweredDeclarationOrigin : IrDeclarationOrigin {
object CLASS_STATIC_INITIALIZER : IrDeclarationOriginImpl("CLASS_STATIC_INITIALIZER")
object SECONDARY_CTOR_RECEIVER : IrDeclarationOriginImpl("SECONDARY_CTOR_RECEIVER")
object JS_INTRINSICS_STUB : IrDeclarationOriginImpl("JS_INTRINSICS_STUB")
}
@@ -10,16 +10,23 @@ import org.jetbrains.kotlin.backend.common.ReflectionTypes
import org.jetbrains.kotlin.backend.common.ir.Ir
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.Variance
class JsIrBackendContext(
val module: ModuleDescriptor,
@@ -80,6 +87,41 @@ class JsIrBackendContext(
override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true
}
data class SecondaryCtorPair(val delegate: IrSimpleFunctionSymbol, val stub: IrSimpleFunctionSymbol)
val secondaryConstructorsMap = mutableMapOf<IrConstructorSymbol, SecondaryCtorPair>()
private val stubBuilder = DeclarationStubGenerator(symbolTable, JsLoweredDeclarationOrigin.JS_INTRINSICS_STUB)
val objectCreate: IrSimpleFunction = defineObjectCreateIntrinsic()
private fun defineObjectCreateIntrinsic(): IrSimpleFunction {
val typeParam = TypeParameterDescriptorImpl.createWithDefaultBound(
builtIns.any,
Annotations.EMPTY,
true,
Variance.INVARIANT,
Name.identifier("T"),
0
)
val returnType = KotlinTypeFactory.simpleType(Annotations.EMPTY, typeParam.typeConstructor, emptyList(), false)
val desc = SimpleFunctionDescriptorImpl.create(
module,
Annotations.EMPTY,
Name.identifier("Object\$create"),
CallableMemberDescriptor.Kind.SYNTHESIZED,
SourceElement.NO_SOURCE
).apply {
initialize(null, null, listOf(typeParam), emptyList(), returnType, Modality.FINAL, Visibilities.PUBLIC)
isInline = true
}
return stubBuilder.generateFunctionStub(desc)
}
private fun find(memberScope: MemberScope, className: String): ClassDescriptor {
return find(memberScope, Name.identifier(className))
}
@@ -9,12 +9,10 @@ import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.backend.js.lower.SecondaryCtorLowering
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
@@ -37,21 +35,29 @@ fun compile(
val moduleFragment = psi2IrTranslator.generateModuleFragment(psi2IrContext, files)
val context = JsIrBackendContext(analysisResult.moduleDescriptor, psi2IrContext.irBuiltIns, moduleFragment, psi2IrContext.symbolTable)
val context = JsIrBackendContext(
analysisResult.moduleDescriptor,
psi2IrContext.irBuiltIns,
moduleFragment,
psi2IrContext.symbolTable
)
ExternalDependenciesGenerator(psi2IrContext.symbolTable, psi2IrContext.irBuiltIns).generateUnboundSymbolsAsDependencies(moduleFragment)
moduleFragment.files.forEach { context.lower(it) }
val transformer = SecondaryCtorLowering.CallsiteRedirectionTransformer(context)
moduleFragment.files.forEach { it.accept(transformer, null) }
val program = moduleFragment.accept(IrModuleToJsTransformer(), null)
val program = moduleFragment.accept(IrModuleToJsTransformer(context), null)
return program.toString()
}
fun JsIrBackendContext.lower(file: IrFile) {
LateinitLowering(this, true).lower(file)
PropertiesLowering().lower(file)
LocalFunctionsLowering(this).lower(file)
DefaultArgumentStubGenerator(this).runOnFilePostfix(file)
InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER).runOnFilePostfix(file)
PropertiesLowering().lower(file)
InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).runOnFilePostfix(file)
SecondaryCtorLowering(this).lower(file)
}
@@ -0,0 +1,322 @@
/*
* 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.lower.createIrBuilder
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.LazyClassReceiverParameterDescriptor
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
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.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.backend.js.utils.isPrimary
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
private val oldCtorToNewMap = mutableMapOf<IrConstructorSymbol, JsIrBackendContext.SecondaryCtorPair>()
override fun lower(irFile: IrFile) {
irFile.accept(this, null)
context.secondaryConstructorsMap.putAll(oldCtorToNewMap)
}
override fun visitFile(irFile: IrFile): IrFile {
irFile.declarations.transformFlat { declaration ->
if (declaration is IrClass) {
listOf(declaration) + lowerClass(declaration)
} else null
}
return irFile
}
private fun lowerClass(irClass: IrClass): List<IrSimpleFunction> {
val className = irClass.name.asString()
val oldConstructors = mutableListOf<IrConstructor>()
val newConstructors = mutableListOf<IrSimpleFunction>()
for (declaration in irClass.declarations) {
if (declaration is IrConstructor && !declaration.symbol.isPrimary) {
// TODO delegate name generation
val constructorName = "${className}_init"
// We should split secondary constructor into two functions,
// * Initializer which contains constructor's body and takes just created object as implicit param `$this`
// ** This function is also delegation constructor
// * Creation function which has same signature with original constructor,
// creates new object via `Object.create` builtIn and passes it to corresponding `Init` function
// In other words:
// Foo::constructor(...) {
// body
// }
// =>
// Foo_init_$Init$(..., $this) {
// body[ this = $this ]
// return $this
// }
// Foo_init_$Create$(...) {
// val t = Object.create(Foo.prototype);
// return Foo_init_$Init$(..., t)
// }
val newInitConstructor = createInitConstructor(declaration, constructorName)
val newCreateConstructor = createCreateConstructor(declaration, newInitConstructor, constructorName)
oldCtorToNewMap[declaration.symbol] =
JsIrBackendContext.SecondaryCtorPair(newInitConstructor.symbol, newCreateConstructor.symbol)
oldConstructors += declaration
newConstructors += newInitConstructor
newConstructors += newCreateConstructor
}
}
irClass.declarations.removeAll(oldConstructors)
return newConstructors
}
private class ThisUsageReplaceTransformer(val function: IrFunctionSymbol, val thisSymbol: IrValueParameterSymbol) :
IrElementTransformerVoid() {
override fun visitReturn(expression: IrReturn): IrExpression = IrReturnImpl(
expression.startOffset,
expression.endOffset,
function,
IrGetValueImpl(expression.startOffset, expression.endOffset, thisSymbol)
)
override fun visitGetValue(expression: IrGetValue): IrExpression =
if (expression.descriptor.name.isSpecial && expression.descriptor.name.asString() == Namer.THIS_SPECIAL_NAME) IrGetValueImpl(
expression.startOffset,
expression.endOffset,
thisSymbol,
expression.origin
) else {
expression
}
}
private fun createInitConstructor(declaration: IrConstructor, name: String): IrSimpleFunction {
// TODO delegate name generation
val actualName = "${name}_\$Init\$"
//region TODO: get rid of descriptors and replace them with direct symbol creation
val thisParamDesc = ValueParameterDescriptorImpl(
declaration.descriptor,
null,
declaration.descriptor.valueParameters.size,
Annotations.EMPTY,
Name.identifier("\$this"),
declaration.descriptor.returnType,
false,
false,
false,
null,
SourceElement.NO_SOURCE
)
val functionDescriptor = SimpleFunctionDescriptorImpl.create(
declaration.descriptor.containingDeclaration,
declaration.descriptor.annotations,
Name.identifier(actualName),
declaration.descriptor.kind,
declaration.descriptor.source
).initialize(
null,
declaration.descriptor.dispatchReceiverParameter,
declaration.descriptor.typeParameters,
declaration.descriptor.valueParameters + thisParamDesc,
declaration.returnType,
declaration.descriptor.modality,
declaration.visibility
)
//endregion
val thisSymbol = IrValueParameterSymbolImpl(thisParamDesc)
val functionSymbol = IrSimpleFunctionSymbolImpl(functionDescriptor)
val thisParam = IrValueParameterImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
JsLoweredDeclarationOrigin.SECONDARY_CTOR_RECEIVER,
thisSymbol
)
val constructor = IrFunctionImpl(
declaration.startOffset, declaration.endOffset,
declaration.origin, functionSymbol
)
var statements = (declaration.body as IrStatementContainer).statements.map { it.deepCopyWithSymbols() }
val fixer = ThisUsageReplaceTransformer(functionSymbol, thisSymbol)
for (stmt in statements) {
stmt.transformChildrenVoid(fixer)
}
val retStmt = IrReturnImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
functionSymbol,
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, thisSymbol)
)
statements += retStmt
val newBody = IrBlockBodyImpl(declaration.body!!.startOffset, declaration.body!!.endOffset, statements)
constructor.valueParameters += declaration.valueParameters
constructor.typeParameters += declaration.typeParameters
constructor.parent = declaration.parent
constructor.valueParameters += thisParam
constructor.body = newBody
return constructor
}
private fun createCreateConstructor(ctorOrig: IrConstructor, ctorImpl: IrSimpleFunction, name: String): IrSimpleFunction {
// TODO delegate name generation
val actualName = "${name}_\$Create\$"
//region TODO: descriptor -> symbol
val functionDescriptor = SimpleFunctionDescriptorImpl.create(
ctorOrig.descriptor,
ctorOrig.descriptor.annotations,
Name.identifier(actualName),
ctorOrig.descriptor.kind,
ctorOrig.descriptor.source
).initialize(
null,
ctorOrig.descriptor.dispatchReceiverParameter,
ctorOrig.descriptor.typeParameters,
ctorOrig.descriptor.valueParameters,
ctorOrig.returnType,
ctorOrig.descriptor.modality,
ctorOrig.visibility
)
//endregion
val functionSymbol = IrSimpleFunctionSymbolImpl(functionDescriptor)
val constructor = IrFunctionImpl(
ctorOrig.startOffset, ctorOrig.endOffset,
ctorOrig.origin, functionSymbol
)
val createFunctionIntrinsic = context.objectCreate
val irBuilder = context.createIrBuilder(functionSymbol, ctorOrig.startOffset, ctorOrig.endOffset).irBlockBody {
val thisVar = irTemporaryVar(
IrCallImpl(
startOffset,
endOffset,
ctorOrig.returnType,
createFunctionIntrinsic.symbol,
createFunctionIntrinsic.descriptor,
mapOf(createFunctionIntrinsic.typeParameters[0].descriptor to ctorOrig.returnType)
)
)
+irReturn(
irCall(ctorImpl.symbol).apply {
ctorOrig.valueParameters.forEachIndexed { index, irValueParameter ->
putValueArgument(index, irGet(irValueParameter.symbol))
}
putValueArgument(ctorOrig.valueParameters.size, irGet(thisVar.symbol))
}
)
}
constructor.valueParameters += ctorOrig.valueParameters
constructor.typeParameters += ctorOrig.typeParameters
constructor.parent = ctorOrig.parent
constructor.body = IrBlockBodyImpl(ctorOrig.body?.startOffset!!, ctorOrig.body?.endOffset!!, irBuilder.statements)
return constructor
}
class CallsiteRedirectionTransformer(val context: JsIrBackendContext) : IrElementTransformer<IrFunction?> {
override fun visitFunction(declaration: IrFunction, data: IrFunction?): IrStatement = super.visitFunction(declaration, declaration)
override fun visitCall(expression: IrCall, ownerFunc: IrFunction?): IrElement {
val target = expression.symbol.owner as IrFunction
if (target is IrConstructor) {
if (!target.descriptor.isPrimary) {
return redirectCall(expression, context.secondaryConstructorsMap[target.symbol]!!.stub)
}
}
return expression
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, ownerFunc: IrFunction?): IrElement {
val target = expression.symbol.owner
if (target.symbol.isPrimary) {
// nothing to do here
return expression
}
val fromPrimary = ownerFunc!! is IrConstructor
val newCall = redirectCall(expression, context.secondaryConstructorsMap[target.symbol]!!.delegate)
val readThis = if (fromPrimary) {
IrGetValueImpl(
expression.startOffset,
expression.endOffset,
IrValueParameterSymbolImpl(LazyClassReceiverParameterDescriptor(target.descriptor.containingDeclaration))
)
} else {
IrGetValueImpl(expression.startOffset, expression.endOffset, ownerFunc.valueParameters.last().symbol)
}
newCall.putValueArgument(expression.valueArgumentsCount, readThis)
return newCall
}
private fun redirectCall(
call: IrFunctionAccessExpression,
newTarget: IrSimpleFunctionSymbol
): IrCallImpl {
val newCall = IrCallImpl(call.startOffset, call.endOffset, newTarget)
newCall.copyTypeArgumentsFrom(call)
for (i in 0 until call.valueArgumentsCount) {
newCall.putValueArgument(i, call.getValueArgument(i))
}
return newCall
}
}
}
@@ -7,10 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.isPrimary
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.js.backend.ast.*
class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
@@ -29,4 +26,14 @@ class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatemen
override fun visitClass(declaration: IrClass, context: JsGenerationContext): JsStatement {
return JsClassGenerator(declaration, context).generate()
}
override fun visitField(declaration: IrField, context: JsGenerationContext): JsStatement {
val fieldName = declaration.name.toJsName()
val initExpression =
declaration.initializer?.accept(IrElementToJsExpressionTransformer(), context) ?: JsPrefixOperation(
JsUnaryOperator.VOID,
JsIntLiteral(1)
)
return jsAssignment(JsNameRef(fieldName, JsThisRef()), initExpression).makeStmt()
}
}
@@ -5,8 +5,10 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.builtins.extractParameterNameFromFunctionTypeArgument
import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
@@ -73,8 +75,10 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsExpression {
val classNameRef = expression.symbol.owner.descriptor.constructedClass.name.toJsName().makeRef()
val callFuncRef = JsNameRef(Namer.CALL_FUNCTION, classNameRef)
val fromPrimary = context.currentFunction is IrConstructor
val thisRef = if (fromPrimary) JsThisRef() else JsNameRef("\$this")
val arguments = translateCallArguments(expression, expression.symbol.parameterCount, context)
return JsInvocation(callFuncRef, listOf(JsThisRef()) + arguments)
return JsInvocation(callFuncRef, listOf(thisRef) + arguments)
}
override fun visitCall(expression: IrCall, context: JsGenerationContext): JsExpression {
@@ -86,6 +90,14 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
// * getters and setters
// * binary and unary operations
if (expression.symbol == context.staticContext.backendContext.objectCreate.symbol) {
// TODO: temporary workaround until there is no an intrinsic infrastructure
assert(expression.typeArgumentsCount == 1 && expression.valueArgumentsCount == 0)
val classToCreate = expression.getTypeArgument(0)!!
val prototype = prototypeOf(classToCreate.constructor.declarationDescriptor!!.name.toJsName().makeRef())
return JsInvocation(Namer.JS_OBJECT_CREATE_FUNCTION, prototype)
}
val symbol = expression.symbol
val dispatchReceiver = expression.dispatchReceiver?.accept(this, context)
@@ -94,7 +106,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
val arguments = translateCallArguments(expression, expression.symbol.parameterCount, context)
return if (symbol is IrConstructorSymbol && symbol.isPrimary) {
return if (symbol is IrConstructorSymbol) {
JsNew(JsNameRef((symbol.owner.parent as IrClass).name.asString()), arguments)
} else {
// TODO sanitize name
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.js.backend.ast.*
@@ -15,12 +15,13 @@ import org.jetbrains.kotlin.js.backend.ast.JsStatement
open class IrFunctionToJsTransformer : BaseIrElementToJsNodeTransformer<JsFunction, JsGenerationContext> {
override fun visitSimpleFunction(declaration: IrSimpleFunction, context: JsGenerationContext): JsFunction {
return translateFunction(declaration, declaration.name, context)
val funcName = context.getNameForSymbol(declaration.symbol)
return translateFunction(declaration, funcName, context)
}
override fun visitConstructor(declaration: IrConstructor, context: JsGenerationContext): JsFunction {
assert(declaration.symbol.isPrimary)
return translateFunction(declaration, declaration.symbol.constructedClassName, context)
return translateFunction(declaration, declaration.symbol.constructedClassName.toJsName(), context)
}
@@ -5,16 +5,17 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.js.backend.ast.JsNode
import org.jetbrains.kotlin.js.backend.ast.JsProgram
import org.jetbrains.kotlin.js.backend.ast.JsRootScope
class IrModuleToJsTransformer : BaseIrElementToJsNodeTransformer<JsNode, Nothing?> {
class IrModuleToJsTransformer(val backendContext: JsIrBackendContext) : BaseIrElementToJsNodeTransformer<JsNode, Nothing?> {
override fun visitModuleFragment(declaration: IrModuleFragment, data: Nothing?): JsNode {
val program = JsProgram()
val rootContext = JsGenerationContext(JsRootScope(program))
val rootContext = JsGenerationContext(JsRootScope(program), backendContext)
declaration.files.forEach {
program.globalBlock.statements.add(it.accept(IrFileToJsTransformer(), rootContext))
@@ -38,13 +38,13 @@ fun jsAssignment(left: JsExpression, right: JsExpression) = JsBinaryOperation(Js
fun prototypeOf(classNameRef: JsExpression) = JsNameRef(Namer.PROTOTYPE_NAME, classNameRef)
fun translateFunction(declaration: IrFunction, name: Name?, context: JsGenerationContext): JsFunction {
fun translateFunction(declaration: IrFunction, name: JsName?, context: JsGenerationContext): JsFunction {
val functionScope = JsFunctionScope(context.currentScope, "scope for ${name ?: "annon"}")
val functionContext = context.newDeclaration(functionScope)
val functionContext = context.newDeclaration(functionScope, declaration)
val body = declaration.body?.accept(IrElementToJsStatementTransformer(), functionContext) as? JsBlock ?: JsBlock()
val function = JsFunction(functionScope, body, "member function ${name ?: "annon"}")
function.name = name?.toJsName()
function.name = name
fun JsFunction.addParameter(parameterName: String) {
val parameter = function.scope.declareName(parameterName)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrElementToJsExpressionTransformer
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrElementToJsStatementTransformer
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.toJsName
@@ -16,31 +17,34 @@ import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.name.Name
class JsGenerationContext {
fun newDeclaration(scope: JsScope): JsGenerationContext {
return JsGenerationContext(this, JsBlock(), scope)
fun newDeclaration(scope: JsScope, func: IrFunction? = null): JsGenerationContext {
return JsGenerationContext(this, JsBlock(), scope, func)
}
val currentBlock: JsBlock
val currentScope: JsScope
val currentFunction: IrFunction?
val parent: JsGenerationContext?
val staticContext: JsStaticContext
private val program: JsProgram
constructor(rootScope: JsRootScope) {
constructor(rootScope: JsRootScope, backendContext: JsIrBackendContext) {
this.parent = null
this.program = rootScope.program
this.staticContext = JsStaticContext(rootScope, program.globalBlock, SimpleNameGenerator())
this.staticContext = JsStaticContext(rootScope, program.globalBlock, SimpleNameGenerator(), backendContext)
this.currentScope = rootScope
this.currentBlock = program.globalBlock
this.currentFunction = null
}
constructor(parent: JsGenerationContext, block: JsBlock, scope: JsScope) {
constructor(parent: JsGenerationContext, block: JsBlock, scope: JsScope, func: IrFunction?) {
this.parent = parent
this.program = parent.program
this.staticContext = parent.staticContext
this.currentBlock = block
this.currentScope = scope
this.currentFunction = func
}
fun getNameForSymbol(symbol: IrSymbol): JsName = staticContext.getNameForSymbol(symbol)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.name.Name
@@ -14,7 +15,8 @@ import org.jetbrains.kotlin.name.Name
class JsStaticContext(
private val rootScope: JsRootScope,
private val globalBlock: JsGlobalBlock,
private val nameGenerator: NameGenerator
private val nameGenerator: NameGenerator,
val backendContext: JsIrBackendContext
) {
fun getNameForSymbol(irSymbol: IrSymbol) = nameGenerator.getNameForSymbol(irSymbol, rootScope)
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.naming.isES5IdentifierPart
import org.jetbrains.kotlin.js.naming.isES5IdentifierStart
import org.jetbrains.kotlin.name.Name
class SimpleNameGenerator : NameGenerator {
@@ -48,6 +50,13 @@ class SimpleNameGenerator : NameGenerator {
}
}
return@getOrPut scope.declareName(nameBuilder.toString())
scope.declareName(sanitizeName(nameBuilder.toString()))
})
private fun sanitizeName(name: String): String {
if (name.isEmpty()) return "_"
val first = name.first().let { if (it.isES5IdentifierStart()) it else '_' }
return first.toString() + name.drop(1).map { if (it.isES5IdentifierPart()) it else '_' }.joinToString("")
}
}
@@ -51,7 +51,7 @@ class JvmLower(val context: JvmBackendContext) {
EnumClassLowering(context).runOnFilePostfix(irFile)
//Should be before SyntheticAccessorLowering cause of synthetic accessor for companion constructor
ObjectClassLowering(context).lower(irFile)
InitializersLowering(context, JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER).runOnFilePostfix(irFile)
InitializersLowering(context, JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, true).runOnFilePostfix(irFile)
SingletonReferencesLowering(context).runOnFilePostfix(irFile)
SyntheticAccessorLowering(context).lower(irFile)
BridgeLowering(context).runOnFilePostfix(irFile)
@@ -21287,18 +21287,7 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
@TestMetadata("primCtorDelegation1.kt")
public void testPrimCtorDelegation1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/primCtorDelegation1.kt");
if (KotlinTestUtils.RUN_IGNORED_TESTS_AS_REGULAR) {
doTest(fileName);
return;
}
try {
doTest(fileName);
}
catch (Throwable ignore) {
ignore.printStackTrace();
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive or add it to whitelist for that.");
doTest(fileName);
}
@TestMetadata("propertiesAsParametersInitialized.kt")
@@ -21327,69 +21316,25 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
@TestMetadata("secCtorDelegation1.kt")
public void testSecCtorDelegation1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/secCtorDelegation1.kt");
if (KotlinTestUtils.RUN_IGNORED_TESTS_AS_REGULAR) {
doTest(fileName);
return;
}
try {
doTest(fileName);
}
catch (Throwable ignore) {
ignore.printStackTrace();
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive or add it to whitelist for that.");
doTest(fileName);
}
@TestMetadata("secCtorDelegation2.kt")
public void testSecCtorDelegation2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/secCtorDelegation2.kt");
if (KotlinTestUtils.RUN_IGNORED_TESTS_AS_REGULAR) {
doTest(fileName);
return;
}
try {
doTest(fileName);
}
catch (Throwable ignore) {
ignore.printStackTrace();
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive or add it to whitelist for that.");
doTest(fileName);
}
@TestMetadata("secCtorDelegation3.kt")
public void testSecCtorDelegation3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/secCtorDelegation3.kt");
if (KotlinTestUtils.RUN_IGNORED_TESTS_AS_REGULAR) {
doTest(fileName);
return;
}
try {
doTest(fileName);
}
catch (Throwable ignore) {
ignore.printStackTrace();
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive or add it to whitelist for that.");
doTest(fileName);
}
@TestMetadata("secCtorDelegation4.kt")
public void testSecCtorDelegation4() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/secCtorDelegation4.kt");
if (KotlinTestUtils.RUN_IGNORED_TESTS_AS_REGULAR) {
doTest(fileName);
return;
}
try {
doTest(fileName);
}
catch (Throwable ignore) {
ignore.printStackTrace();
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive or add it to whitelist for that.");
doTest(fileName);
}
@TestMetadata("simpleInitializer.kt")