From 2bea8816a6c5a32d41412b1ba44489c01610419f Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Thu, 29 Mar 2018 12:26:14 +0300 Subject: [PATCH] Add Secondary constructor lowering --- .../common/lower/InitializersLowering.kt | 47 +-- .../ir/backend/js/DeclarationOrigins.kt | 2 + .../ir/backend/js/JsIrBackendContext.kt | 42 +++ .../kotlin/ir/backend/js/compiler.kt | 20 +- .../backend/js/lower/SecondaryCtorLowering.kt | 322 ++++++++++++++++++ .../irToJs/IrDeclarationToJsTransformer.kt | 15 +- .../IrElementToJsExpressionTransformer.kt | 16 +- .../IrElementToJsStatementTransformer.kt | 1 + .../irToJs/IrFunctionToJsTransformer.kt | 5 +- .../irToJs/IrModuleToJsTransformer.kt | 5 +- .../js/transformers/irToJs/jsAstUtils.kt | 6 +- .../backend/js/utils/JsGenerationContext.kt | 14 +- .../ir/backend/js/utils/JsStaticContext.kt | 4 +- .../backend/js/utils/SimpleNameGenerator.kt | 11 +- .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 2 +- .../test/semantics/IrBoxJsTestGenerated.java | 65 +--- 16 files changed, 469 insertions(+), 108 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/SecondaryCtorLowering.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InitializersLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InitializersLowering.kt index 167a2cdfaac..4c7199d6836 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InitializersLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InitializersLowering.kt @@ -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() }) + ) ) } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/DeclarationOrigins.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/DeclarationOrigins.kt index a406c8291e6..de52ab160dc 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/DeclarationOrigins.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/DeclarationOrigins.kt @@ -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") } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index 30e5d05c0dc..bc8ea8bc15b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -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() + + 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)) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index 4cb7a73b0c3..7a502f84ad4 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -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) } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/SecondaryCtorLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/SecondaryCtorLowering.kt new file mode 100644 index 00000000000..4b7ae116b4b --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/SecondaryCtorLowering.kt @@ -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() + + 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 { + val className = irClass.name.asString() + val oldConstructors = mutableListOf() + val newConstructors = mutableListOf() + + 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 { + + 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 + } + } +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt index 773bd9aa2d4..d0f24f176a7 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt @@ -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 { @@ -29,4 +26,14 @@ class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer { 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) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt index c133655852f..40c792c30bf 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt @@ -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 { +class IrModuleToJsTransformer(val backendContext: JsIrBackendContext) : BaseIrElementToJsNodeTransformer { 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)) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt index 59a2b60d263..25b2147cf8c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt @@ -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) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt index 88397d0d33b..5c99f93eae7 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt @@ -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) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt index 9486063b2f5..a9308201d16 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt @@ -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) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt index 4c3074b2732..8bcf248cd65 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt @@ -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("") + } } \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 7bd8585e434..318186ab192 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -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) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java index f2e7d82ec51..f9951ae75e6 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java @@ -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")