Add Secondary constructor lowering
This commit is contained in:
+27
-20
@@ -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)
|
||||
}
|
||||
+322
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-4
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
+14
-2
@@ -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
|
||||
|
||||
+1
@@ -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.*
|
||||
|
||||
+3
-2
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
+3
-2
@@ -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))
|
||||
|
||||
+3
-3
@@ -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)
|
||||
|
||||
+9
-5
@@ -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)
|
||||
|
||||
+3
-1
@@ -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)
|
||||
|
||||
+10
-1
@@ -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)
|
||||
|
||||
+5
-60
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user