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.* 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) { override fun lower(irClass: IrClass) {
val classInitializersBuilder = ClassInitializersBuilder(irClass) val classInitializersBuilder = ClassInitializersBuilder(irClass)
irClass.acceptChildrenVoid(classInitializersBuilder) irClass.acceptChildrenVoid(classInitializersBuilder)
classInitializersBuilder.transformInstanceInitializerCallsInConstructors(irClass) classInitializersBuilder.transformInstanceInitializerCallsInConstructors(irClass)
classInitializersBuilder.createStaticInitializationMethod(irClass) if (clinitNeeded) classInitializersBuilder.createStaticInitializationMethod(irClass)
} }
private inner class ClassInitializersBuilder(val irClass: IrClass) : IrElementVisitorVoid { 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 irFieldInitializer = declaration.initializer?.expression ?: return
val receiver = val receiver =
if (declaration.descriptor.dispatchReceiverParameter != null) // TODO isStaticField if (declaration.descriptor.dispatchReceiverParameter != null) // TODO isStaticField
IrGetValueImpl(irFieldInitializer.startOffset, irFieldInitializer.endOffset, IrGetValueImpl(
irClass.descriptor.thisAsReceiverParameter) irFieldInitializer.startOffset, irFieldInitializer.endOffset,
else null irClass.descriptor.thisAsReceiverParameter
)
else null
val irSetField = IrSetFieldImpl( val irSetField = IrSetFieldImpl(
irFieldInitializer.startOffset, irFieldInitializer.endOffset, irFieldInitializer.startOffset, irFieldInitializer.endOffset,
declaration.descriptor, declaration.descriptor,
receiver, receiver,
irFieldInitializer, irFieldInitializer,
null, null null, null
) )
if (DescriptorUtils.isStaticDeclaration(declaration.descriptor)) { if (DescriptorUtils.isStaticDeclaration(declaration.descriptor)) {
staticInitializerStatements.add(irSetField) staticInitializerStatements.add(irSetField)
} } else {
else {
instanceInitializerStatements.add(irSetField) instanceInitializerStatements.add(irSetField)
} }
} }
@@ -100,15 +105,17 @@ class InitializersLowering(val context: CommonBackendContext, val declarationOri
SourceElement.NO_SOURCE SourceElement.NO_SOURCE
) )
staticInitializerDescriptor.initialize( staticInitializerDescriptor.initialize(
null, null, emptyList(), emptyList(), null, null, emptyList(), emptyList(),
irClass.descriptor.builtIns.unitType, irClass.descriptor.builtIns.unitType,
Modality.FINAL, Visibilities.PUBLIC Modality.FINAL, Visibilities.PUBLIC
) )
irClass.declarations.add( irClass.declarations.add(
IrFunctionImpl(irClass.startOffset, irClass.endOffset, declarationOrigin, IrFunctionImpl(
staticInitializerDescriptor, irClass.startOffset, irClass.endOffset, declarationOrigin,
IrBlockBodyImpl(irClass.startOffset, irClass.endOffset, staticInitializerDescriptor,
staticInitializerStatements.map { it.copy() })) IrBlockBodyImpl(irClass.startOffset, irClass.endOffset,
staticInitializerStatements.map { it.copy() })
)
) )
} }
} }
@@ -10,4 +10,6 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
object JsLoweredDeclarationOrigin : IrDeclarationOrigin { object JsLoweredDeclarationOrigin : IrDeclarationOrigin {
object CLASS_STATIC_INITIALIZER : IrDeclarationOriginImpl("CLASS_STATIC_INITIALIZER") 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.Ir
import org.jetbrains.kotlin.backend.common.ir.Symbols import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.descriptors.* 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.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol 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.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator
import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.Variance
class JsIrBackendContext( class JsIrBackendContext(
val module: ModuleDescriptor, val module: ModuleDescriptor,
@@ -80,6 +87,41 @@ class JsIrBackendContext(
override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true 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 { private fun find(memberScope: MemberScope, className: String): ClassDescriptor {
return find(memberScope, Name.identifier(className)) 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.lower.*
import org.jetbrains.kotlin.backend.common.runOnFilePostfix import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.config.CompilerConfiguration 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.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.declarations.IrFile
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator 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.js.analyze.TopDownAnalyzerFacadeForJS
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
@@ -37,21 +35,29 @@ fun compile(
val moduleFragment = psi2IrTranslator.generateModuleFragment(psi2IrContext, files) 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) ExternalDependenciesGenerator(psi2IrContext.symbolTable, psi2IrContext.irBuiltIns).generateUnboundSymbolsAsDependencies(moduleFragment)
moduleFragment.files.forEach { context.lower(it) } 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() return program.toString()
} }
fun JsIrBackendContext.lower(file: IrFile) { fun JsIrBackendContext.lower(file: IrFile) {
LateinitLowering(this, true).lower(file) LateinitLowering(this, true).lower(file)
PropertiesLowering().lower(file)
LocalFunctionsLowering(this).lower(file) LocalFunctionsLowering(this).lower(file)
DefaultArgumentStubGenerator(this).runOnFilePostfix(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.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.isPrimary import org.jetbrains.kotlin.ir.backend.js.utils.isPrimary
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.*
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.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> { class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
@@ -29,4 +26,14 @@ class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatemen
override fun visitClass(declaration: IrClass, context: JsGenerationContext): JsStatement { override fun visitClass(declaration: IrClass, context: JsGenerationContext): JsStatement {
return JsClassGenerator(declaration, context).generate() 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 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.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.IrClass 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.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
@@ -73,8 +75,10 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsExpression { override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, context: JsGenerationContext): JsExpression {
val classNameRef = expression.symbol.owner.descriptor.constructedClass.name.toJsName().makeRef() val classNameRef = expression.symbol.owner.descriptor.constructedClass.name.toJsName().makeRef()
val callFuncRef = JsNameRef(Namer.CALL_FUNCTION, classNameRef) 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) 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 { override fun visitCall(expression: IrCall, context: JsGenerationContext): JsExpression {
@@ -86,6 +90,14 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
// * getters and setters // * getters and setters
// * binary and unary operations // * 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 symbol = expression.symbol
val dispatchReceiver = expression.dispatchReceiver?.accept(this, context) val dispatchReceiver = expression.dispatchReceiver?.accept(this, context)
@@ -94,7 +106,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
val arguments = translateCallArguments(expression, expression.symbol.parameterCount, context) 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) JsNew(JsNameRef((symbol.owner.parent as IrClass).name.asString()), arguments)
} else { } else {
// TODO sanitize name // 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.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.ir.backend.js.utils.* 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.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.js.backend.ast.* 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> { open class IrFunctionToJsTransformer : BaseIrElementToJsNodeTransformer<JsFunction, JsGenerationContext> {
override fun visitSimpleFunction(declaration: IrSimpleFunction, context: JsGenerationContext): JsFunction { 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 { override fun visitConstructor(declaration: IrConstructor, context: JsGenerationContext): JsFunction {
assert(declaration.symbol.isPrimary) 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 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.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.js.backend.ast.JsNode import org.jetbrains.kotlin.js.backend.ast.JsNode
import org.jetbrains.kotlin.js.backend.ast.JsProgram import org.jetbrains.kotlin.js.backend.ast.JsProgram
import org.jetbrains.kotlin.js.backend.ast.JsRootScope 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 { override fun visitModuleFragment(declaration: IrModuleFragment, data: Nothing?): JsNode {
val program = JsProgram() val program = JsProgram()
val rootContext = JsGenerationContext(JsRootScope(program)) val rootContext = JsGenerationContext(JsRootScope(program), backendContext)
declaration.files.forEach { declaration.files.forEach {
program.globalBlock.statements.add(it.accept(IrFileToJsTransformer(), rootContext)) 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 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 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 body = declaration.body?.accept(IrElementToJsStatementTransformer(), functionContext) as? JsBlock ?: JsBlock()
val function = JsFunction(functionScope, body, "member function ${name ?: "annon"}") val function = JsFunction(functionScope, body, "member function ${name ?: "annon"}")
function.name = name?.toJsName() function.name = name
fun JsFunction.addParameter(parameterName: String) { fun JsFunction.addParameter(parameterName: String) {
val parameter = function.scope.declareName(parameterName) val parameter = function.scope.declareName(parameterName)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.utils package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor 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.IrElementToJsExpressionTransformer
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrElementToJsStatementTransformer import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrElementToJsStatementTransformer
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.toJsName 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 import org.jetbrains.kotlin.name.Name
class JsGenerationContext { class JsGenerationContext {
fun newDeclaration(scope: JsScope): JsGenerationContext { fun newDeclaration(scope: JsScope, func: IrFunction? = null): JsGenerationContext {
return JsGenerationContext(this, JsBlock(), scope) return JsGenerationContext(this, JsBlock(), scope, func)
} }
val currentBlock: JsBlock val currentBlock: JsBlock
val currentScope: JsScope val currentScope: JsScope
val currentFunction: IrFunction?
val parent: JsGenerationContext? val parent: JsGenerationContext?
val staticContext: JsStaticContext val staticContext: JsStaticContext
private val program: JsProgram private val program: JsProgram
constructor(rootScope: JsRootScope) { constructor(rootScope: JsRootScope, backendContext: JsIrBackendContext) {
this.parent = null this.parent = null
this.program = rootScope.program this.program = rootScope.program
this.staticContext = JsStaticContext(rootScope, program.globalBlock, SimpleNameGenerator()) this.staticContext = JsStaticContext(rootScope, program.globalBlock, SimpleNameGenerator(), backendContext)
this.currentScope = rootScope this.currentScope = rootScope
this.currentBlock = program.globalBlock 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.parent = parent
this.program = parent.program this.program = parent.program
this.staticContext = parent.staticContext this.staticContext = parent.staticContext
this.currentBlock = block this.currentBlock = block
this.currentScope = scope this.currentScope = scope
this.currentFunction = func
} }
fun getNameForSymbol(symbol: IrSymbol): JsName = staticContext.getNameForSymbol(symbol) fun getNameForSymbol(symbol: IrSymbol): JsName = staticContext.getNameForSymbol(symbol)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.utils package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor 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.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -14,7 +15,8 @@ import org.jetbrains.kotlin.name.Name
class JsStaticContext( class JsStaticContext(
private val rootScope: JsRootScope, private val rootScope: JsRootScope,
private val globalBlock: JsGlobalBlock, private val globalBlock: JsGlobalBlock,
private val nameGenerator: NameGenerator private val nameGenerator: NameGenerator,
val backendContext: JsIrBackendContext
) { ) {
fun getNameForSymbol(irSymbol: IrSymbol) = nameGenerator.getNameForSymbol(irSymbol, rootScope) 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.descriptors.*
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.backend.ast.* 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 import org.jetbrains.kotlin.name.Name
class SimpleNameGenerator : NameGenerator { 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) EnumClassLowering(context).runOnFilePostfix(irFile)
//Should be before SyntheticAccessorLowering cause of synthetic accessor for companion constructor //Should be before SyntheticAccessorLowering cause of synthetic accessor for companion constructor
ObjectClassLowering(context).lower(irFile) 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) SingletonReferencesLowering(context).runOnFilePostfix(irFile)
SyntheticAccessorLowering(context).lower(irFile) SyntheticAccessorLowering(context).lower(irFile)
BridgeLowering(context).runOnFilePostfix(irFile) BridgeLowering(context).runOnFilePostfix(irFile)
@@ -21287,18 +21287,7 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
@TestMetadata("primCtorDelegation1.kt") @TestMetadata("primCtorDelegation1.kt")
public void testPrimCtorDelegation1() throws Exception { public void testPrimCtorDelegation1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/primCtorDelegation1.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/primCtorDelegation1.kt");
if (KotlinTestUtils.RUN_IGNORED_TESTS_AS_REGULAR) { doTest(fileName);
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.");
} }
@TestMetadata("propertiesAsParametersInitialized.kt") @TestMetadata("propertiesAsParametersInitialized.kt")
@@ -21327,69 +21316,25 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
@TestMetadata("secCtorDelegation1.kt") @TestMetadata("secCtorDelegation1.kt")
public void testSecCtorDelegation1() throws Exception { public void testSecCtorDelegation1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/secCtorDelegation1.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/secCtorDelegation1.kt");
if (KotlinTestUtils.RUN_IGNORED_TESTS_AS_REGULAR) { doTest(fileName);
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.");
} }
@TestMetadata("secCtorDelegation2.kt") @TestMetadata("secCtorDelegation2.kt")
public void testSecCtorDelegation2() throws Exception { public void testSecCtorDelegation2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/secCtorDelegation2.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/secCtorDelegation2.kt");
if (KotlinTestUtils.RUN_IGNORED_TESTS_AS_REGULAR) { doTest(fileName);
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.");
} }
@TestMetadata("secCtorDelegation3.kt") @TestMetadata("secCtorDelegation3.kt")
public void testSecCtorDelegation3() throws Exception { public void testSecCtorDelegation3() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/secCtorDelegation3.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/secCtorDelegation3.kt");
if (KotlinTestUtils.RUN_IGNORED_TESTS_AS_REGULAR) { doTest(fileName);
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.");
} }
@TestMetadata("secCtorDelegation4.kt") @TestMetadata("secCtorDelegation4.kt")
public void testSecCtorDelegation4() throws Exception { public void testSecCtorDelegation4() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/secCtorDelegation4.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/simple/secCtorDelegation4.kt");
if (KotlinTestUtils.RUN_IGNORED_TESTS_AS_REGULAR) { doTest(fileName);
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.");
} }
@TestMetadata("simpleInitializer.kt") @TestMetadata("simpleInitializer.kt")