[K/JS] Add support of compilation with ES-classes

This commit is contained in:
Artem Kobzar
2023-01-17 18:14:17 +00:00
committed by Space Team
parent 8d728d4f53
commit 71486a321c
239 changed files with 57567 additions and 1658 deletions
@@ -65,7 +65,7 @@ public final class DebugKlibMetadataProtoBuf {
* <code>optional int32 flags = 2;</code> * <code>optional int32 flags = 2;</code>
* *
* <pre> * <pre>
*0x1 = preRelease *Possible values are listed in KlibMetadataHeaderFlags class.
* </pre> * </pre>
*/ */
boolean hasFlags(); boolean hasFlags();
@@ -73,7 +73,7 @@ public final class DebugKlibMetadataProtoBuf {
* <code>optional int32 flags = 2;</code> * <code>optional int32 flags = 2;</code>
* *
* <pre> * <pre>
*0x1 = preRelease *Possible values are listed in KlibMetadataHeaderFlags class.
* </pre> * </pre>
*/ */
int getFlags(); int getFlags();
@@ -433,7 +433,7 @@ public final class DebugKlibMetadataProtoBuf {
* <code>optional int32 flags = 2;</code> * <code>optional int32 flags = 2;</code>
* *
* <pre> * <pre>
*0x1 = preRelease *Possible values are listed in KlibMetadataHeaderFlags class.
* </pre> * </pre>
*/ */
public boolean hasFlags() { public boolean hasFlags() {
@@ -443,7 +443,7 @@ public final class DebugKlibMetadataProtoBuf {
* <code>optional int32 flags = 2;</code> * <code>optional int32 flags = 2;</code>
* *
* <pre> * <pre>
*0x1 = preRelease *Possible values are listed in KlibMetadataHeaderFlags class.
* </pre> * </pre>
*/ */
public int getFlags() { public int getFlags() {
@@ -1225,7 +1225,7 @@ public final class DebugKlibMetadataProtoBuf {
* <code>optional int32 flags = 2;</code> * <code>optional int32 flags = 2;</code>
* *
* <pre> * <pre>
*0x1 = preRelease *Possible values are listed in KlibMetadataHeaderFlags class.
* </pre> * </pre>
*/ */
public boolean hasFlags() { public boolean hasFlags() {
@@ -1235,7 +1235,7 @@ public final class DebugKlibMetadataProtoBuf {
* <code>optional int32 flags = 2;</code> * <code>optional int32 flags = 2;</code>
* *
* <pre> * <pre>
*0x1 = preRelease *Possible values are listed in KlibMetadataHeaderFlags class.
* </pre> * </pre>
*/ */
public int getFlags() { public int getFlags() {
@@ -1245,7 +1245,7 @@ public final class DebugKlibMetadataProtoBuf {
* <code>optional int32 flags = 2;</code> * <code>optional int32 flags = 2;</code>
* *
* <pre> * <pre>
*0x1 = preRelease *Possible values are listed in KlibMetadataHeaderFlags class.
* </pre> * </pre>
*/ */
public Builder setFlags(int value) { public Builder setFlags(int value) {
@@ -1258,7 +1258,7 @@ public final class DebugKlibMetadataProtoBuf {
* <code>optional int32 flags = 2;</code> * <code>optional int32 flags = 2;</code>
* *
* <pre> * <pre>
*0x1 = preRelease *Possible values are listed in KlibMetadataHeaderFlags class.
* </pre> * </pre>
*/ */
public Builder clearFlags() { public Builder clearFlags() {
@@ -291,6 +291,17 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
) )
var strictImplicitExportType: Boolean by FreezableVar(false) var strictImplicitExportType: Boolean by FreezableVar(false)
@GradleOption(
value = DefaultValue.BOOLEAN_FALSE_DEFAULT,
gradleInputType = GradleInputTypes.INPUT,
shouldGenerateDeprecatedKotlinOptions = true,
)
@Argument(
value = "-Xes-classes",
description = "Generated JavaScript will use ES2015 classes."
)
var useEsClasses: Boolean by FreezableVar(false)
@GradleOption( @GradleOption(
value = DefaultValue.BOOLEAN_TRUE_DEFAULT, value = DefaultValue.BOOLEAN_TRUE_DEFAULT,
gradleInputType = GradleInputTypes.INPUT, gradleInputType = GradleInputTypes.INPUT,
@@ -144,7 +144,8 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
arguments.irSafeExternalBooleanDiagnostic, arguments.irSafeExternalBooleanDiagnostic,
messageCollector messageCollector
), ),
granularity = arguments.granularity granularity = arguments.granularity,
es6mode = arguments.useEsClasses
) )
} }
@@ -668,7 +669,14 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
compilerConfiguration = configurationJs, compilerConfiguration = configurationJs,
irFactory = { IrFactoryImplForJsIC(WholeWorldStageController()) }, irFactory = { IrFactoryImplForJsIC(WholeWorldStageController()) },
mainArguments = mainCallArguments, mainArguments = mainCallArguments,
compilerInterfaceFactory = { mainModule, cfg -> JsIrCompilerWithIC(mainModule, cfg, arguments.granularity) } compilerInterfaceFactory = { mainModule, cfg ->
JsIrCompilerWithIC(
mainModule,
cfg,
arguments.granularity,
es6mode = arguments.useEsClasses
)
}
) )
val artifacts = cacheUpdater.actualizeCaches() val artifacts = cacheUpdater.actualizeCaches()
@@ -30,6 +30,6 @@ abstract class AbstractValueRemapper : IrElementTransformerVoid() {
} }
} }
open class ValueRemapper(private val map: Map<IrValueSymbol, IrValueSymbol>) : AbstractValueRemapper() { open class ValueRemapper(protected open val map: Map<IrValueSymbol, IrValueSymbol>) : AbstractValueRemapper() {
override fun remapValue(oldValue: IrValueSymbol): IrValueSymbol? = map[oldValue] override fun remapValue(oldValue: IrValueSymbol): IrValueSymbol? = map[oldValue]
} }
@@ -125,6 +125,13 @@ class InlineClassLowering(val context: CommonBackendContext) {
return builder.irGet(initFunction.valueParameters.single()) return builder.irGet(initFunction.valueParameters.single())
return expression return expression
} }
override fun visitSetValue(expression: IrSetValue): IrExpression {
expression.transformChildrenVoid()
if (expression.symbol == origParameterSymbol)
return builder.irSet(initFunction.valueParameters.single(), expression.value)
return expression
}
}) })
} }
+irReturn(unboxedInlineClassValue()) +irReturn(unboxedInlineClassValue())
@@ -152,11 +152,9 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
val jsAnyToString = getInternalFunction("anyToString") val jsAnyToString = getInternalFunction("anyToString")
val jsCompareTo = getInternalFunction("compareTo") val jsCompareTo = getInternalFunction("compareTo")
val jsEquals = getInternalFunction("equals") val jsEquals = getInternalFunction("equals")
val jsConstruct = getInternalFunction("construct")
val jsNewTarget = getInternalFunction("jsNewTarget") val jsNewTarget = getInternalFunction("jsNewTarget")
val jsEmptyObject = getInternalFunction("emptyObject") val jsEmptyObject = getInternalFunction("emptyObject")
val jsOpenInitializerBox = getInternalFunction("openInitializerBox") val jsOpenInitializerBox = getInternalFunction("openInitializerBox")
val es6DefaultType = getInternalFunction("DefaultType")
val jsImul = getInternalFunction("imul") val jsImul = getInternalFunction("imul")
@@ -167,6 +165,8 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
val jsBooleanInExternalLog = getInternalFunction("booleanInExternalLog") val jsBooleanInExternalLog = getInternalFunction("booleanInExternalLog")
val jsBooleanInExternalException = getInternalFunction("booleanInExternalException") val jsBooleanInExternalException = getInternalFunction("booleanInExternalException")
val jsNewAnonymousClass = getInternalFunction("jsNewAnonymousClass")
// Coroutines // Coroutines
val jsCoroutineContext val jsCoroutineContext
@@ -369,7 +369,10 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
val jsPrototypeOfSymbol = getInternalFunction("protoOf") val jsPrototypeOfSymbol = getInternalFunction("protoOf")
val jsDefinePropertySymbol = getInternalFunction("defineProp") val jsDefinePropertySymbol = getInternalFunction("defineProp")
val jsObjectCreateSymbol = getInternalFunction("objectCreate") // Object.create val jsObjectCreateSymbol = getInternalFunction("objectCreate") // Object.create(x)
val jsCreateThisSymbol = getInternalFunction("createThis") // Object.create(x.prototype)
val jsBoxApplySymbol = getInternalFunction("boxApply")
val jsCreateExternalThisSymbol = getInternalFunction("createExternalThis")
// Helpers: // Helpers:
@@ -29,6 +29,12 @@ private fun DeclarationContainerLoweringPass.runOnFilesPostfix(files: Iterable<I
private fun ClassLoweringPass.runOnFilesPostfix(moduleFragment: IrModuleFragment) = moduleFragment.files.forEach { runOnFilePostfix(it) } private fun ClassLoweringPass.runOnFilesPostfix(moduleFragment: IrModuleFragment) = moduleFragment.files.forEach { runOnFilePostfix(it) }
private fun List<Lowering>.toCompilerPhase() =
map {
@Suppress("USELESS_CAST")
it.modulePhase as CompilerPhase<JsIrBackendContext, Iterable<IrModuleFragment>, Iterable<IrModuleFragment>>
}.reduce { acc, lowering -> acc.then(lowering) }
private fun makeJsModulePhase( private fun makeJsModulePhase(
lowering: (JsIrBackendContext) -> FileLoweringPass, lowering: (JsIrBackendContext) -> FileLoweringPass,
name: String, name: String,
@@ -683,19 +689,6 @@ private val typeOperatorLoweringPhase = makeBodyLoweringPhase(
) )
) )
private val es6AddInternalParametersToConstructorPhase = makeBodyLoweringPhase(
::ES6AddInternalParametersToConstructorPhase,
name = "ES6CreateInitFunctionPhase",
description = "Add `box` and `resultType` params, create init functions for constructors"
)
private val es6ConstructorLowering = makeBodyLoweringPhase(
::ES6ConstructorLowering,
name = "ES6ConstructorLoweringPhase",
description = "Lower constructors",
prerequisite = setOf(es6AddInternalParametersToConstructorPhase)
)
private val secondaryConstructorLoweringPhase = makeDeclarationTransformerPhase( private val secondaryConstructorLoweringPhase = makeDeclarationTransformerPhase(
::SecondaryConstructorLowering, ::SecondaryConstructorLowering,
name = "SecondaryConstructorLoweringPhase", name = "SecondaryConstructorLoweringPhase",
@@ -791,10 +784,31 @@ private val invokeStaticInitializersPhase = makeBodyLoweringPhase(
prerequisite = setOf(objectDeclarationLoweringPhase) prerequisite = setOf(objectDeclarationLoweringPhase)
) )
private val es6AddBoxParameterToConstructorsLowering = makeDeclarationTransformerPhase(
::ES6AddBoxParameterToConstructorsLowering,
name = "ES6AddBoxParameterToConstructorsLowering",
description = "Add box parameter to a constructor if needed",
)
private val es6ConstructorLowering = makeDeclarationTransformerPhase(
::ES6ConstructorLowering,
name = "ES6ConstructorLowering",
description = "Lower constructors declarations to support ES classes",
prerequisite = setOf(es6AddBoxParameterToConstructorsLowering)
)
private val es6ConstructorUsageLowering = makeBodyLoweringPhase(
::ES6ConstructorCallLowering,
name = "ES6ConstructorCallLowering",
description = "Lower constructor usages to support ES classes",
prerequisite = setOf(es6ConstructorLowering)
)
private val objectUsageLoweringPhase = makeBodyLoweringPhase( private val objectUsageLoweringPhase = makeBodyLoweringPhase(
::ObjectUsageLowering, ::ObjectUsageLowering,
name = "ObjectUsageLowering", name = "ObjectUsageLowering",
description = "Transform IrGetObjectValue into instance generator call" description = "Transform IrGetObjectValue into instance generator call",
prerequisite = setOf(primaryConstructorLoweringPhase)
) )
private val escapedIdentifiersLowering = makeBodyLoweringPhase( private val escapedIdentifiersLowering = makeBodyLoweringPhase(
@@ -809,7 +823,6 @@ private val implicitlyExportedDeclarationsMarkingLowering = makeDeclarationTrans
description = "Add @JsImplicitExport annotation to declarations which are not exported but are used inside other exported declarations as a type" description = "Add @JsImplicitExport annotation to declarations which are not exported but are used inside other exported declarations as a type"
) )
private val cleanupLoweringPhase = makeBodyLoweringPhase( private val cleanupLoweringPhase = makeBodyLoweringPhase(
{ CleanupLowering() }, { CleanupLowering() },
name = "CleanupLowering", name = "CleanupLowering",
@@ -912,8 +925,6 @@ val loweringList = listOf<Lowering>(
defaultParameterCleanerPhase, defaultParameterCleanerPhase,
captureStackTraceInThrowablesPhase, captureStackTraceInThrowablesPhase,
throwableSuccessorsLoweringPhase, throwableSuccessorsLoweringPhase,
es6AddInternalParametersToConstructorPhase,
es6ConstructorLowering,
varargLoweringPhase, varargLoweringPhase,
multipleCatchesLoweringPhase, multipleCatchesLoweringPhase,
errorExpressionLoweringPhase, errorExpressionLoweringPhase,
@@ -927,10 +938,13 @@ val loweringList = listOf<Lowering>(
inlineClassDeclarationLoweringPhase, inlineClassDeclarationLoweringPhase,
inlineClassUsageLoweringPhase, inlineClassUsageLoweringPhase,
autoboxingTransformerPhase, autoboxingTransformerPhase,
blockDecomposerLoweringPhase,
objectDeclarationLoweringPhase, objectDeclarationLoweringPhase,
blockDecomposerLoweringPhase,
invokeStaticInitializersPhase, invokeStaticInitializersPhase,
objectUsageLoweringPhase, objectUsageLoweringPhase,
es6AddBoxParameterToConstructorsLowering,
es6ConstructorLowering,
es6ConstructorUsageLowering,
callsLoweringPhase, callsLoweringPhase,
escapedIdentifiersLowering, escapedIdentifiersLowering,
implicitlyExportedDeclarationsMarkingLowering, implicitlyExportedDeclarationsMarkingLowering,
@@ -947,10 +961,56 @@ val pirLowerings = loweringList.filter { it is DeclarationLowering || it is Body
val jsPhases = SameTypeNamedCompilerPhase( val jsPhases = SameTypeNamedCompilerPhase(
name = "IrModuleLowering", name = "IrModuleLowering",
description = "IR module lowering", description = "IR module lowering",
lower = loweringList.map { lower = loweringList.toCompilerPhase(),
@Suppress("USELESS_CAST") actions = setOf(defaultDumper.toMultiModuleAction(), validationAction.toMultiModuleAction()),
it.modulePhase as CompilerPhase<JsIrBackendContext, Iterable<IrModuleFragment>, Iterable<IrModuleFragment>> nlevels = 1
}.reduce { acc, lowering -> acc.then(lowering) }, )
private val es6CollectConstructorsWhichNeedBoxParameterLowering = makeDeclarationTransformerPhase(
::ES6CollectConstructorsWhichNeedBoxParameters,
name = "ES6CollectConstructorsWhichNeedBoxParameters",
description = "[Optimization] Collect all of the constructors which requires box parameter",
)
private val es6BoxParameterOptimization = makeBodyLoweringPhase(
::ES6ConstructorBoxParameterOptimizationLowering,
name = "ES6ConstructorBoxParameterOptimizationLowering",
description = "[Optimization] Remove box parameter from the constructors which don't require box parameter",
prerequisite = setOf(es6CollectConstructorsWhichNeedBoxParameterLowering)
)
private val es6CollectPrimaryConstructorsWhichCouldBeOptimizedLowering = makeDeclarationTransformerPhase(
::ES6CollectPrimaryConstructorsWhichCouldBeOptimizedLowering,
name = "ES6CollectPrimaryConstructorsWhichCouldBeOptimizedLowering",
description = "[Optimization] Collect all of the constructors which could be translated into a regular constructor",
)
private val es6PrimaryConstructorOptimizationLowering = makeDeclarationTransformerPhase(
::ES6PrimaryConstructorOptimizationLowering,
name = "ES6PrimaryConstructorOptimizationLowering",
description = "[Optimization] Replace synthetically generated static fabric method with a plain old ES6 constructors whenever it's possible",
prerequisite = setOf(es6CollectPrimaryConstructorsWhichCouldBeOptimizedLowering)
)
private val es6PrimaryConstructorUsageOptimizationLowering = makeBodyLoweringPhase(
::ES6PrimaryConstructorUsageOptimizationLowering,
name = "ES6PrimaryConstructorUsageOptimizationLowering",
description = "[Optimization] Replace usage of synthetically generated static fabric method with a plain old ES6 constructors whenever it's possible",
prerequisite = setOf(es6BoxParameterOptimization, es6PrimaryConstructorOptimizationLowering)
)
val optimizationLoweringList = listOf<Lowering>(
es6CollectConstructorsWhichNeedBoxParameterLowering,
es6CollectPrimaryConstructorsWhichCouldBeOptimizedLowering,
es6BoxParameterOptimization,
es6PrimaryConstructorOptimizationLowering,
es6PrimaryConstructorUsageOptimizationLowering,
)
val jsOptimizationPhases = SameTypeNamedCompilerPhase(
name = "IrModuleOptimizationLowering",
description = "IR module optimization lowering",
lower = optimizationLoweringList.toCompilerPhase(),
actions = setOf(defaultDumper.toMultiModuleAction(), validationAction.toMultiModuleAction()), actions = setOf(defaultDumper.toMultiModuleAction(), validationAction.toMultiModuleAction()),
nlevels = 1 nlevels = 1
) )
@@ -7,9 +7,13 @@ package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.backend.common.DefaultDelegateFactory import org.jetbrains.kotlin.backend.common.DefaultDelegateFactory
import org.jetbrains.kotlin.backend.common.DefaultMapping import org.jetbrains.kotlin.backend.common.DefaultMapping
import org.jetbrains.kotlin.ir.backend.js.utils.MutableReference
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
class JsMapping : DefaultMapping() { class JsMapping : DefaultMapping() {
val esClassWhichNeedBoxParameters = mutableSetOf<IrClass>()
val esClassToPossibilityForOptimization = mutableMapOf<IrClass, MutableReference<Boolean>>()
val outerThisFieldSymbols = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrField>() val outerThisFieldSymbols = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrField>()
val innerClassConstructors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrConstructor, IrConstructor>() val innerClassConstructors = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrConstructor, IrConstructor>()
val originalInnerClassPrimaryConstructorByClass = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrConstructor>() val originalInnerClassPrimaryConstructorByClass = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrConstructor>()
@@ -20,8 +24,6 @@ class JsMapping : DefaultMapping() {
val classToSyntheticPrimaryConstructor = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrConstructor>() val classToSyntheticPrimaryConstructor = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrClass, IrConstructor>()
val privateMemberToCorrespondingStatic = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrFunction, IrSimpleFunction>() val privateMemberToCorrespondingStatic = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrFunction, IrSimpleFunction>()
val constructorToInitFunction = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrConstructor, IrSimpleFunction>()
val enumEntryToGetInstanceFun = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrEnumEntry, IrSimpleFunction>() val enumEntryToGetInstanceFun = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrEnumEntry, IrSimpleFunction>()
val enumEntryToInstanceField = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrEnumEntry, IrField>() val enumEntryToInstanceField = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrEnumEntry, IrField>()
val enumConstructorToNewConstructor = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrConstructor, IrConstructor>() val enumConstructorToNewConstructor = DefaultDelegateFactory.newDeclarationToDeclarationMapping<IrConstructor, IrConstructor>()
@@ -9,26 +9,35 @@ import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
import org.jetbrains.kotlin.ir.backend.js.export.isExported import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.backend.js.lower.isBuiltInClass import org.jetbrains.kotlin.ir.backend.js.lower.isBuiltInClass
import org.jetbrains.kotlin.ir.backend.js.lower.isEs6ConstructorReplacement
import org.jetbrains.kotlin.ir.backend.js.utils.* import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.serialization.js.ModuleKind
internal class JsUsefulDeclarationProcessor( internal class JsUsefulDeclarationProcessor(
override val context: JsIrBackendContext, override val context: JsIrBackendContext,
printReachabilityInfo: Boolean, printReachabilityInfo: Boolean,
removeUnusedAssociatedObjects: Boolean removeUnusedAssociatedObjects: Boolean
) : UsefulDeclarationProcessor(printReachabilityInfo, removeUnusedAssociatedObjects) { ) : UsefulDeclarationProcessor(printReachabilityInfo, removeUnusedAssociatedObjects) {
private val equalsMethod = getMethodOfAny("equals") private val equalsMethod = getMethodOfAny("equals")
private val hashCodeMethod = getMethodOfAny("hashCode") private val hashCodeMethod = getMethodOfAny("hashCode")
private val isEsModules = context.configuration[JSConfigurationKeys.MODULE_KIND] == ModuleKind.ES
override val bodyVisitor: BodyVisitorBase = object : BodyVisitorBase() { override val bodyVisitor: BodyVisitorBase = object : BodyVisitorBase() {
override fun visitCall(expression: IrCall, data: IrDeclaration) { override fun visitCall(expression: IrCall, data: IrDeclaration) {
super.visitCall(expression, data) super.visitCall(expression, data)
if (expression.usePrototype(data)) {
context.intrinsics.jsPrototypeOfSymbol.owner.enqueue(expression.symbol.owner, "access to super type")
}
when (expression.symbol) { when (expression.symbol) {
context.intrinsics.jsBoxIntrinsic -> { context.intrinsics.jsBoxIntrinsic -> {
val inlineClass = context.inlineClassesUtils.getInlinedClass(expression.getTypeArgument(0)!!)!! val inlineClass = context.inlineClassesUtils.getInlinedClass(expression.getTypeArgument(0)!!)!!
@@ -68,6 +77,21 @@ internal class JsUsefulDeclarationProcessor(
constructedClasses += classToCreate constructedClasses += classToCreate
} }
context.intrinsics.jsCreateThisSymbol -> {
val jsClassOrThis = expression.getValueArgument(0)
val classTypeToCreate = when (jsClassOrThis) {
is IrCall -> jsClassOrThis.getTypeArgument(0)!!
is IrGetValue -> jsClassOrThis.type
else -> error("Unexpected first argument of createThis function call")
}
val classToCreate = classTypeToCreate.classifierOrFail.owner as IrClass
classToCreate.enqueue(data, "intrinsic: jsCreateThis")
constructedClasses += classToCreate
}
context.intrinsics.jsEquals -> { context.intrinsics.jsEquals -> {
equalsMethod.enqueue(data, "intrinsic: jsEquals") equalsMethod.enqueue(data, "intrinsic: jsEquals")
} }
@@ -86,26 +110,6 @@ internal class JsUsefulDeclarationProcessor(
} }
} }
context.intrinsics.jsConstruct -> {
val callType = expression.getTypeArgument(0)!!
val constructor = callType.getClass()!!.primaryConstructor
constructor!!.enqueue(data, "ctor call from jsConstruct-intrinsic")
}
context.intrinsics.es6DefaultType -> {
//same as jsClass
val ref = expression.getTypeArgument(0)!!.classifierOrFail.owner as IrDeclaration
ref.enqueue(data, "intrinsic: jsClass")
referencedJsClasses += ref
//Generate klass in `val currResultType = resultType || klass`
val arg = expression.getTypeArgument(0)!!
val klass = arg.getClass()
if (klass != null) {
constructedClasses.add(klass)
}
}
context.intrinsics.jsInvokeSuspendSuperType, context.intrinsics.jsInvokeSuspendSuperType,
context.intrinsics.jsInvokeSuspendSuperTypeWithReceiver, context.intrinsics.jsInvokeSuspendSuperTypeWithReceiver,
context.intrinsics.jsInvokeSuspendSuperTypeWithReceiverAndParam -> { context.intrinsics.jsInvokeSuspendSuperTypeWithReceiverAndParam -> {
@@ -137,48 +141,57 @@ internal class JsUsefulDeclarationProcessor(
} }
} }
if (irClass.containsMetadata()) { if (!irClass.containsMetadata()) return
when {
irClass.isObject -> context.intrinsics.metadataObjectConstructorSymbol.owner.enqueue(irClass, "object metadata")
irClass.isInterface -> { when {
context.intrinsics.implementSymbol.owner.enqueue(irClass, "interface metadata") irClass.isObject -> context.intrinsics.metadataObjectConstructorSymbol.owner.enqueue(irClass, "object metadata")
context.intrinsics.metadataInterfaceConstructorSymbol.owner.enqueue(irClass, "interface metadata")
}
else -> { irClass.isInterface -> {
context.intrinsics.metadataClassConstructorSymbol.owner.enqueue(irClass, "class metadata") context.intrinsics.implementSymbol.owner.enqueue(irClass, "interface metadata")
} context.intrinsics.metadataInterfaceConstructorSymbol.owner.enqueue(irClass, "interface metadata")
} }
else -> context.intrinsics.metadataClassConstructorSymbol.owner.enqueue(irClass, "class metadata")
} }
if (!irClass.isExpect && !irClass.isExternal && !irClass.defaultType.isAny()) { context.intrinsics.setMetadataForSymbol.owner.enqueue(irClass, "metadata")
if (!irClass.isInterface) {
context.intrinsics.jsPrototypeOfSymbol.owner.enqueue(irClass, "class metadata")
}
if (irClass.superTypes.any { !it.isInterface() }) { if (irClass.containsInterfaceDefaultImplementation()) {
context.intrinsics.jsObjectCreateSymbol.owner.enqueue(irClass, "class metadata") context.intrinsics.jsPrototypeOfSymbol.owner.enqueue(irClass, "interface default implementation")
} }
if (irClass.isInner || irClass.isObject) { if ((!context.es6mode || !isEsModules) && (irClass.isInner || irClass.isObject)) {
context.intrinsics.jsDefinePropertySymbol.owner.enqueue(irClass, "class metadata") context.intrinsics.jsDefinePropertySymbol.owner.enqueue(irClass, "object lazy initialization")
} }
context.intrinsics.setMetadataForSymbol.owner.enqueue(irClass, "metadata") if (context.es6mode) return
if (!irClass.isInterface) {
context.intrinsics.jsPrototypeOfSymbol.owner.enqueue(irClass, "class prototype access")
}
if (irClass.superTypes.any { !it.isInterface() }) {
context.intrinsics.jsObjectCreateSymbol.owner.enqueue(irClass, "class inheritance code")
} }
} }
override fun processSimpleFunction(irFunction: IrSimpleFunction) { override fun processSimpleFunction(irFunction: IrSimpleFunction) {
super.processSimpleFunction(irFunction) super.processSimpleFunction(irFunction)
if (irFunction.isEs6ConstructorReplacement) {
constructedClasses += irFunction.dispatchReceiverParameter?.type?.classOrNull?.owner!!
}
if (irFunction.isReal && irFunction.body != null) { if (irFunction.isReal && irFunction.body != null) {
irFunction.parentClassOrNull?.takeIf { it.isInterface }?.enqueue(irFunction, "interface default method is used") irFunction.parentClassOrNull?.takeIf { it.isInterface }?.enqueue(irFunction, "interface default method is used")
} }
if (context.es6mode && isEsModules) return
val property = irFunction.correspondingPropertySymbol?.owner ?: return val property = irFunction.correspondingPropertySymbol?.owner ?: return
if (property.isExported(context) || property.isOverriddenExternal()) { if (property.isExported(context) || property.getJsName() != null || property.isOverriddenExternal()) {
context.intrinsics.jsPrototypeOfSymbol.owner.enqueue(irFunction, "property for export")
context.intrinsics.jsDefinePropertySymbol.owner.enqueue(irFunction, "property for export") context.intrinsics.jsDefinePropertySymbol.owner.enqueue(irFunction, "property for export")
} }
} }
@@ -225,8 +238,24 @@ internal class JsUsefulDeclarationProcessor(
} }
override fun isExported(declaration: IrDeclaration): Boolean = declaration.isExported(context) override fun isExported(declaration: IrDeclaration): Boolean = declaration.isExported(context)
}
private fun IrCall.usePrototype(container: IrDeclaration?): Boolean {
if (superQualifierSymbol == null) return false
val currentFun = (container as? IrSimpleFunction)
val currentClass = currentFun?.parentClassOrNull
return !context.es6mode ||
currentFun?.dispatchReceiverParameter == null ||
currentClass != null && (currentClass.isInner || currentClass.isLocal)
}
private fun IrClass.containsInterfaceDefaultImplementation(): Boolean {
return superTypes.any { it.classOrNull?.owner?.isExternal == true } ||
isExported(context) ||
isInterface && declarations.any { it is IrFunction && it.body != null }
}
}
private fun Collection<IrClass>.filterDescendantsOf(bases: Collection<IrClass>): Collection<IrClass> { private fun Collection<IrClass>.filterDescendantsOf(bases: Collection<IrClass>): Collection<IrClass> {
val visited = hashSetOf<IrClass>() val visited = hashSetOf<IrClass>()
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.ir.backend.js.utils.isAssociatedObjectAnnotatedAnnot
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
@@ -13,8 +13,8 @@ import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.lower.ES6AddInternalParametersToConstructorPhase.ES6_INIT_BOX_PARAMETER import org.jetbrains.kotlin.ir.backend.js.lower.isBoxParameter
import org.jetbrains.kotlin.ir.backend.js.lower.ES6AddInternalParametersToConstructorPhase.ES6_RESULT_TYPE_PARAMETER import org.jetbrains.kotlin.ir.backend.js.lower.isEs6ConstructorReplacement
import org.jetbrains.kotlin.ir.backend.js.utils.* import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
@@ -86,7 +86,7 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac
returnType = exportType(function.returnType), returnType = exportType(function.returnType),
typeParameters = function.typeParameters.map(::exportTypeParameter), typeParameters = function.typeParameters.map(::exportTypeParameter),
isMember = parent is IrClass, isMember = parent is IrClass,
isStatic = function.isStaticMethodOfClass, isStatic = function.isStaticMethod,
isAbstract = parent is IrClass && !parent.isInterface && function.modality == Modality.ABSTRACT, isAbstract = parent is IrClass && !parent.isInterface && function.modality == Modality.ABSTRACT,
isProtected = function.visibility == DescriptorVisibilities.PROTECTED, isProtected = function.visibility == DescriptorVisibilities.PROTECTED,
ir = function, ir = function,
@@ -100,10 +100,9 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac
private fun exportConstructor(constructor: IrConstructor): ExportedDeclaration? { private fun exportConstructor(constructor: IrConstructor): ExportedDeclaration? {
if (!constructor.isPrimary) return null if (!constructor.isPrimary) return null
val allValueParameters = listOfNotNull(constructor.extensionReceiverParameter) + val allValueParameters = listOfNotNull(constructor.extensionReceiverParameter) + constructor.valueParameters
constructor.valueParameters.filterNot { it.origin === ES6_RESULT_TYPE_PARAMETER || it.origin === ES6_INIT_BOX_PARAMETER }
return ExportedConstructor( return ExportedConstructor(
parameters = allValueParameters.map { exportParameter(it) }, parameters = allValueParameters.filterNot { it.isBoxParameter }.map { exportParameter(it) },
visibility = constructor.visibility.toExportedVisibility() visibility = constructor.visibility.toExportedVisibility()
) )
} }
@@ -696,6 +695,9 @@ private class ExportedClassDeclarationsInfo(
private val IrClassifierSymbol.isInterface private val IrClassifierSymbol.isInterface
get() = (owner as? IrClass)?.isInterface == true get() = (owner as? IrClass)?.isInterface == true
private val IrFunction.isStaticMethod: Boolean
get() = isEs6ConstructorReplacement || isStaticMethodOfClass
private fun getExportCandidate(declaration: IrDeclaration): IrDeclarationWithName? { private fun getExportCandidate(declaration: IrDeclaration): IrDeclarationWithName? {
// Only actual public declarations with name can be exported // Only actual public declarations with name can be exported
if (declaration !is IrDeclarationWithVisibility || if (declaration !is IrDeclarationWithVisibility ||
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.export
import org.jetbrains.kotlin.ir.backend.js.utils.* import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.lower.isEs6ConstructorReplacement
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsAstUtils import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsAstUtils
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.defineProperty import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.defineProperty
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsAssignment import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsAssignment
@@ -15,13 +16,16 @@ import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsElementAccess
import org.jetbrains.kotlin.ir.backend.js.utils.Namer import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.util.companionObject import org.jetbrains.kotlin.ir.util.companionObject
import org.jetbrains.kotlin.ir.util.isObject
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd
class ExportModelToJsStatements( class ExportModelToJsStatements(
private val namer: JsStaticContext, private val staticContext: JsStaticContext,
private val declareNewNamespace: (String) -> String private val es6mode: Boolean,
private val declareNewNamespace: (String) -> String,
) { ) {
private val namespaceToRefMap = mutableMapOf<String, JsNameRef>() private val namespaceToRefMap = mutableMapOf<String, JsNameRef>()
@@ -38,7 +42,8 @@ class ExportModelToJsStatements(
fun generateDeclarationExport( fun generateDeclarationExport(
declaration: ExportedDeclaration, declaration: ExportedDeclaration,
namespace: JsExpression?, namespace: JsExpression?,
esModules: Boolean esModules: Boolean,
parentClass: IrClass? = null
): List<JsStatement> { ): List<JsStatement> {
return when (declaration) { return when (declaration) {
is ExportedNamespace -> { is ExportedNamespace -> {
@@ -73,7 +78,7 @@ class ExportModelToJsStatements(
} }
is ExportedFunction -> { is ExportedFunction -> {
val name = namer.getNameForStaticDeclaration(declaration.ir) val name = staticContext.getNameForStaticDeclaration(declaration.ir)
when { when {
namespace != null -> namespace != null ->
listOf(jsAssignment(jsElementAccess(declaration.name, namespace), JsNameRef(name)).makeStmt()) listOf(jsAssignment(jsElementAccess(declaration.name, namespace), JsNameRef(name)).makeStmt())
@@ -88,28 +93,29 @@ class ExportModelToJsStatements(
is ExportedProperty -> { is ExportedProperty -> {
require(namespace != null || esModules) { "Only namespaced properties are allowed" } require(namespace != null || esModules) { "Only namespaced properties are allowed" }
val getter = declaration.irGetter?.let { namer.getNameForStaticDeclaration(it) } when {
val setter = declaration.irSetter?.let { namer.getNameForStaticDeclaration(it) } namespace == null -> {
if (namespace == null) { val property = declaration.generateTopLevelGetters()
val property = JsVars.JsVar( listOf(JsVars(property), JsExport(property.name, JsName(declaration.name, false)))
JsName(declaration.name, false), }
JsObjectLiteral(false).apply { es6mode && declaration.isMember -> {
getter?.let { val jsClass = parentClass?.getCorrespondingJsClass() ?: error("Expect to have parentClass at this point")
val fieldName = when (declaration.irGetter.origin) { jsClass.members += declaration.generateClassMembers()
JsLoweredDeclarationOrigin.OBJECT_GET_INSTANCE_FUNCTION -> "getInstance" listOf(JsEmpty)
else -> "get" }
} else -> {
propertyInitializers += JsPropertyInitializer(JsStringLiteral(fieldName), it.makeRef()) val getter = declaration.irGetter?.let { staticContext.getNameForStaticDeclaration(it) }
} val setter = declaration.irSetter?.let { staticContext.getNameForStaticDeclaration(it) }
setter?.let { propertyInitializers += JsPropertyInitializer(JsStringLiteral("set"), it.makeRef()) } listOf(
} defineProperty(
) namespace,
listOf( declaration.name,
JsVars(property), getter?.makeRef(),
JsExport(property.name, JsName(declaration.name, false)) setter?.makeRef(),
) staticContext
} else { ).makeStmt()
listOf(defineProperty(namespace, declaration.name, getter?.makeRef(), setter?.makeRef(), namer).makeStmt()) )
}
} }
} }
@@ -120,24 +126,31 @@ class ExportModelToJsStatements(
val newNameSpace = when { val newNameSpace = when {
namespace != null -> jsElementAccess(declaration.name, namespace) namespace != null -> jsElementAccess(declaration.name, namespace)
else -> else ->
jsElementAccess(Namer.PROTOTYPE_NAME, namer.getNameForClass(declaration.ir).makeRef()) jsElementAccess(Namer.PROTOTYPE_NAME, staticContext.getNameForClass(declaration.ir).makeRef())
} }
val staticsExport = declaration.nestedClasses.flatMap { generateDeclarationExport(it, newNameSpace, esModules) } val staticsExport =
declaration.nestedClasses.flatMap { generateDeclarationExport(it, newNameSpace, esModules, declaration.ir) }
val objectExport = when (namespace) { val objectExport = when {
null -> generateDeclarationExport( es6mode || namespace == null -> generateDeclarationExport(
ExportedProperty(declaration.name, ExportedType.Primitive.Any, irGetter = declaration.irGetter), ExportedProperty(
declaration.name,
ExportedType.Primitive.Any,
isStatic = parentClass?.isObject != true,
irGetter = declaration.irGetter,
isMember = parentClass != null
),
namespace, namespace,
esModules esModules,
parentClass
) )
else -> listOf( else -> listOf(
defineProperty( defineProperty(
namespace, namespace,
declaration.name, declaration.name,
namer.getNameForStaticDeclaration(declaration.irGetter).makeRef(), staticContext.getNameForStaticDeclaration(declaration.irGetter).makeRef(),
null, null,
namer staticContext
).makeStmt() ).makeStmt()
) )
} }
@@ -147,11 +160,11 @@ class ExportModelToJsStatements(
is ExportedRegularClass -> { is ExportedRegularClass -> {
if (declaration.isInterface) return emptyList() if (declaration.isInterface) return emptyList()
val name = namer.getNameForStaticDeclaration(declaration.ir) val name = staticContext.getNameForStaticDeclaration(declaration.ir)
val newNameSpace = when { val newNameSpace = when {
namespace != null -> jsElementAccess(declaration.name, namespace) namespace != null -> jsElementAccess(declaration.name, namespace)
esModules -> name.makeRef() esModules -> name.makeRef()
else -> prototypeOf(namer.getNameForClass(declaration.ir).makeRef(), namer) else -> prototypeOf(staticContext.getNameForClass(declaration.ir).makeRef(), staticContext)
} }
val klassExport = when { val klassExport = when {
namespace != null -> jsAssignment(newNameSpace, JsNameRef(name)).makeStmt() namespace != null -> jsAssignment(newNameSpace, JsNameRef(name)).makeStmt()
@@ -161,7 +174,7 @@ class ExportModelToJsStatements(
// These are only used when exporting secondary constructors annotated with @JsName // These are only used when exporting secondary constructors annotated with @JsName
val staticFunctions = declaration.members val staticFunctions = declaration.members
.filter { it is ExportedFunction && it.isStatic } .filter { it is ExportedFunction && it.isStatic && !it.ir.isEs6ConstructorReplacement }
.takeIf { !declaration.ir.isInner }.orEmpty() .takeIf { !declaration.ir.isInner }.orEmpty()
val enumEntries = declaration.members.filter { it is ExportedProperty && it.isStatic } val enumEntries = declaration.members.filter { it is ExportedProperty && it.isStatic }
@@ -171,22 +184,75 @@ class ExportModelToJsStatements(
.map { it.generateInnerClassAssignment(declaration) } .map { it.generateInnerClassAssignment(declaration) }
val staticsExport = (staticFunctions + enumEntries + declaration.nestedClasses) val staticsExport = (staticFunctions + enumEntries + declaration.nestedClasses)
.flatMap { generateDeclarationExport(it, newNameSpace, esModules) } .flatMap { generateDeclarationExport(it, newNameSpace, esModules, declaration.ir) }
listOfNotNull(klassExport) + staticsExport + innerClassesAssignments listOfNotNull(klassExport) + staticsExport + innerClassesAssignments
} }
} }
} }
private fun ExportedProperty.generateTopLevelGetters(): JsVars.JsVar {
val getter = irGetter?.let { staticContext.getNameForStaticDeclaration(it) }
val setter = irSetter?.let { staticContext.getNameForStaticDeclaration(it) }
return JsVars.JsVar(
JsName(name, false),
JsObjectLiteral(false).apply {
getter?.let {
val fieldName = when (irGetter?.origin) {
JsLoweredDeclarationOrigin.OBJECT_GET_INSTANCE_FUNCTION -> "getInstance"
else -> "get"
}
propertyInitializers += JsPropertyInitializer(JsStringLiteral(fieldName), it.makeRef())
}
setter?.let { propertyInitializers += JsPropertyInitializer(JsStringLiteral("set"), it.makeRef()) }
}
)
}
private fun ExportedProperty.generateClassMembers(): List<JsFunction> {
val getter = irGetter?.let { staticContext.getNameForStaticDeclaration(it) }
val setter = irSetter?.let { staticContext.getNameForStaticDeclaration(it) }
return buildList {
if (getter != null) {
add(JsFunction(emptyScope, "").also {
it.name = JsName(name, false)
if (isStatic) {
it.modifiers.add(JsFunction.Modifier.STATIC)
}
it.modifiers.add(JsFunction.Modifier.GET)
it.body = JsBlock().apply {
statements.add(JsReturn(JsInvocation(getter.makeRef())))
}
})
}
if (setter != null) {
add(JsFunction(emptyScope, "").also {
val value = JsName("value", true)
it.name = JsName(name, false)
it.parameters.add(JsParameter(value))
if (isStatic) {
it.modifiers.add(JsFunction.Modifier.STATIC)
}
it.modifiers.add(JsFunction.Modifier.SET)
it.body = JsBlock().apply {
statements.add(JsExpressionStatement(JsInvocation(setter.makeRef(), value.makeRef())))
}
})
}
}
}
private fun ExportedClass.generateInnerClassAssignment(outerClass: ExportedClass): JsStatement { private fun ExportedClass.generateInnerClassAssignment(outerClass: ExportedClass): JsStatement {
val innerClassRef = namer.getNameForStaticDeclaration(ir).makeRef() val innerClassRef = staticContext.getNameForStaticDeclaration(ir).makeRef()
val outerClassRef = namer.getNameForStaticDeclaration(outerClass.ir).makeRef() val outerClassRef = staticContext.getNameForStaticDeclaration(outerClass.ir).makeRef()
val companionObject = ir.companionObject() val companionObject = ir.companionObject()
val secondaryConstructors = members.filterIsInstanceAnd<ExportedFunction> { it.isStatic } val secondaryConstructors = members.filterIsInstanceAnd<ExportedFunction> { it.isStatic }
val bindConstructor = JsName("__bind_constructor_", false) val bindConstructor = JsName("__bind_constructor_", false)
val blockStatements = mutableListOf<JsStatement>( val blockStatements = mutableListOf<JsStatement>(
JsVars(JsVars.JsVar(bindConstructor, innerClassRef.bindToThis())) JsVars(JsVars.JsVar(bindConstructor, innerClassRef.bindToThis(innerClassRef)))
) )
if (companionObject != null) { if (companionObject != null) {
@@ -200,35 +266,47 @@ class ExportModelToJsStatements(
} }
secondaryConstructors.forEach { secondaryConstructors.forEach {
val currentFunRef = namer.getNameForStaticDeclaration(it.ir).makeRef() val currentFunRef = if (it.ir.isEs6ConstructorReplacement) {
JsNameRef(staticContext.getNameForMemberFunction(it.ir), innerClassRef)
} else {
staticContext.getNameForStaticDeclaration(it.ir).makeRef()
}
val assignment = jsAssignment( val assignment = jsAssignment(
JsNameRef(it.name, bindConstructor.makeRef()), JsNameRef(it.name, bindConstructor.makeRef()),
currentFunRef.bindToThis() currentFunRef.bindToThis(innerClassRef)
).makeStmt() ).makeStmt()
blockStatements.add(assignment) blockStatements.add(assignment)
} }
blockStatements.add(JsReturn(bindConstructor.makeRef())) blockStatements.add(JsReturn(bindConstructor.makeRef()))
val innerClassGetter = JsFunction(emptyScope, JsBlock(*blockStatements.toTypedArray()), "inner class '$name' getter")
return defineProperty( return if (es6mode) {
prototypeOf(outerClassRef, namer), outerClass.ir.getCorrespondingJsClass().members += innerClassGetter.also {
name, it.name = JsName(name, false)
JsFunction( it.modifiers.add(JsFunction.Modifier.GET)
emptyScope, }
JsBlock(*blockStatements.toTypedArray()), JsEmpty
"inner class '$name' getter" } else {
), defineProperty(
null, prototypeOf(outerClassRef, staticContext),
namer name,
).makeStmt() innerClassGetter,
null,
staticContext
).makeStmt()
}
} }
private fun JsNameRef.bindToThis(): JsInvocation { private fun IrClass.getCorrespondingJsClass(): JsClass {
return JsInvocation( val jsClassModel = staticContext.classModels[symbol] ?: error("Class with name '$name' was not found")
JsNameRef("bind", this), return (jsClassModel.preDeclarationBlock.statements.first() as? JsExpressionStatement)?.expression as? JsClass
JsNullLiteral(), ?: error("Expect to have JsClass as a first statement inside JsIrClassModel")
JsThisRef() }
)
private fun JsNameRef.bindToThis(bindTo: JsExpression): JsInvocation {
return JsInvocation(JsNameRef("bind", this), bindTo, JsThisRef())
} }
} }
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.backend.js.utils.isJsImplicitExport import org.jetbrains.kotlin.ir.backend.js.utils.isJsImplicitExport
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.superTypes import org.jetbrains.kotlin.ir.types.superTypes
@@ -734,6 +734,7 @@ fun rebuildCacheForDirtyFiles(
irFactory: IrFactory, irFactory: IrFactory,
exportedDeclarations: Set<FqName>, exportedDeclarations: Set<FqName>,
mainArguments: List<String>?, mainArguments: List<String>?,
es6mode: Boolean
): Pair<IrModuleFragment, List<Pair<IrFile, JsIrProgramFragment>>> { ): Pair<IrModuleFragment, List<Pair<IrFile, JsIrProgramFragment>>> {
val emptyMetadata = object : KotlinSourceFileExports() { val emptyMetadata = object : KotlinSourceFileExports() {
override val inverseDependencies = KotlinSourceFileMap<Set<IdSignature>>(emptyMap()) override val inverseDependencies = KotlinSourceFileMap<Set<IdSignature>>(emptyMap())
@@ -753,7 +754,13 @@ fun rebuildCacheForDirtyFiles(
currentIrModule.files.filter { irFile -> irFile.fileEntry.name in files } currentIrModule.files.filter { irFile -> irFile.fileEntry.name in files }
} ?: currentIrModule.files } ?: currentIrModule.files
val compilerWithIC = JsIrCompilerWithIC(currentIrModule, configuration, JsGenerationGranularity.PER_MODULE, exportedDeclarations) val compilerWithIC = JsIrCompilerWithIC(
currentIrModule,
configuration,
JsGenerationGranularity.PER_MODULE,
exportedDeclarations,
es6mode
)
// Load declarations referenced during `context` initialization // Load declarations referenced during `context` initialization
jsIrLinker.loadUnboundSymbols() jsIrLinker.loadUnboundSymbols()
@@ -15,19 +15,19 @@ import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
object JsIrBuilder { object JsIrBuilder {
object SYNTHESIZED_DECLARATION : IrDeclarationOriginImpl("SYNTHESIZED_DECLARATION") object SYNTHESIZED_DECLARATION : IrDeclarationOriginImpl("SYNTHESIZED_DECLARATION")
fun buildCall( fun buildCall(
target: IrSimpleFunctionSymbol, target: IrSimpleFunctionSymbol,
type: IrType? = null, type: IrType? = null,
typeArguments: List<IrType>? = null, typeArguments: List<IrType>? = null,
origin: IrStatementOrigin = JsStatementOrigins.SYNTHESIZED_STATEMENT origin: IrStatementOrigin = JsStatementOrigins.SYNTHESIZED_STATEMENT,
superQualifierSymbol: IrClassSymbol? = null,
): IrCall { ): IrCall {
val owner = target.owner val owner = target.owner
return IrCallImpl( return IrCallImpl(
@@ -35,6 +35,7 @@ object JsIrBuilder {
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
type ?: owner.returnType, type ?: owner.returnType,
target, target,
superQualifierSymbol = superQualifierSymbol,
typeArgumentsCount = owner.typeParameters.size, typeArgumentsCount = owner.typeParameters.size,
valueArgumentsCount = owner.valueParameters.size, valueArgumentsCount = owner.valueParameters.size,
origin = origin origin = origin
@@ -46,54 +47,95 @@ object JsIrBuilder {
} }
} }
fun buildConstructorCall( fun buildArray(elements: List<IrExpression>, type: IrType, elementType: IrType): IrExpression {
target: IrConstructorSymbol, return IrVarargImpl(
type: IrType? = null, UNDEFINED_OFFSET,
typeArguments: List<IrType>? = null, UNDEFINED_OFFSET,
constructorTypeArguments: List<IrType>? = null, type,
origin: IrStatementOrigin = JsStatementOrigins.SYNTHESIZED_STATEMENT elementType,
): IrConstructorCall { elements
)
}
fun buildDelegatingConstructorCall(target: IrConstructorSymbol, typeArguments: List<IrType?>? = null): IrDelegatingConstructorCall {
val owner = target.owner val owner = target.owner
val parent = owner.parentAsClass val irClass = owner.parentAsClass
return IrConstructorCallImpl(
return IrDelegatingConstructorCallImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
type ?: owner.returnType, owner.returnType,
target, target,
typeArgumentsCount = parent.typeParameters.size, typeArgumentsCount = irClass.typeParameters.size,
valueArgumentsCount = owner.valueParameters.size, valueArgumentsCount = owner.valueParameters.size,
constructorTypeArgumentsCount = owner.typeParameters.size,
origin = origin
).apply { ).apply {
typeArguments?.let { typeArguments?.let {
assert(typeArguments.size == typeArgumentsCount) assert(it.size == typeArgumentsCount)
it.withIndex().forEach { (i, t) -> putTypeArgument(i, t) }
}
constructorTypeArguments?.let {
assert(constructorTypeArguments.size == constructorTypeArgumentsCount)
it.withIndex().forEach { (i, t) -> putTypeArgument(i, t) } it.withIndex().forEach { (i, t) -> putTypeArgument(i, t) }
} }
} }
} }
fun buildConstructorCall(
target: IrConstructorSymbol,
typeArguments: List<IrType?>? = null,
constructorTypeArguments: List<IrType?>? = null,
origin: IrStatementOrigin = JsStatementOrigins.SYNTHESIZED_STATEMENT
): IrConstructorCall {
val owner = target.owner
val irClass = owner.parentAsClass
return IrConstructorCallImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
owner.returnType,
target,
typeArgumentsCount = irClass.typeParameters.size,
constructorTypeArgumentsCount = owner.typeParameters.size,
valueArgumentsCount = owner.valueParameters.size,
origin = origin
).apply {
typeArguments?.let {
assert(it.size == typeArgumentsCount)
it.withIndex().forEach { (i, t) -> putTypeArgument(i, t) }
}
constructorTypeArguments?.let {
assert(it.size == typeArgumentsCount)
it.withIndex().forEach { (i, t) -> putTypeArgument(i, t) }
}
}
}
fun buildRawReference(targetSymbol: IrFunctionSymbol, type: IrType): IrRawFunctionReference =
IrRawFunctionReferenceImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, targetSymbol)
fun buildReturn(targetSymbol: IrFunctionSymbol, value: IrExpression, type: IrType) = fun buildReturn(targetSymbol: IrFunctionSymbol, value: IrExpression, type: IrType) =
IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, targetSymbol, value) IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, targetSymbol, value)
fun buildThrow(type: IrType, value: IrExpression) = IrThrowImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, value) fun buildThrow(type: IrType, value: IrExpression) = IrThrowImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, value)
fun buildValueParameter(parent: IrFunction, name: String, index: Int, type: IrType): IrValueParameter = fun buildValueParameter(
parent: IrFunction,
name: String,
index: Int,
type: IrType,
isAssignable: Boolean = false,
origin: IrDeclarationOrigin = SYNTHESIZED_DECLARATION
): IrValueParameter =
buildValueParameter(parent) { buildValueParameter(parent) {
this.origin = SYNTHESIZED_DECLARATION this.origin = origin
this.name = Name.identifier(name) this.name = Name.identifier(name)
this.index = index this.index = index
this.type = type this.type = type
this.isAssignable = isAssignable
} }
fun buildGetObjectValue(type: IrType, classSymbol: IrClassSymbol) = fun buildGetObjectValue(type: IrType, classSymbol: IrClassSymbol) =
IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, classSymbol) IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, classSymbol)
fun buildGetValue(symbol: IrValueSymbol) = fun buildGetValue(symbol: IrValueSymbol, origin: IrStatementOrigin = JsStatementOrigins.SYNTHESIZED_STATEMENT) =
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol.owner.type, symbol, JsStatementOrigins.SYNTHESIZED_STATEMENT) IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol.owner.type, symbol, origin)
fun buildSetValue(symbol: IrValueSymbol, value: IrExpression) = fun buildSetValue(symbol: IrValueSymbol, value: IrExpression) =
IrSetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol.owner.type, symbol, value, JsStatementOrigins.SYNTHESIZED_STATEMENT) IrSetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol.owner.type, symbol, value, JsStatementOrigins.SYNTHESIZED_STATEMENT)
@@ -5,17 +5,20 @@
package org.jetbrains.kotlin.ir.backend.js.lower package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.DeclarationTransformer import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.util.parentAsClass
class AnnotationConstructorLowering(context: CommonBackendContext) : DeclarationTransformer { class AnnotationConstructorLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
private val unitType = context.irBuiltIns.unitType private val unitType = context.irBuiltIns.unitType
private val anyConstructor = context.irBuiltIns.anyClass.constructors.first()
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? { override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration !is IrConstructor || !declaration.isPrimary) return null if (declaration !is IrConstructor || !declaration.isPrimary) return null
@@ -27,6 +30,9 @@ class AnnotationConstructorLowering(context: CommonBackendContext) : Declaration
// put empty body to make sure proper initializer is generated // put empty body to make sure proper initializer is generated
// TODO what about its previous body? // TODO what about its previous body?
declaration.body = declaration.factory.createBlockBody(declaration.startOffset, declaration.endOffset) { declaration.body = declaration.factory.createBlockBody(declaration.startOffset, declaration.endOffset) {
if (context.es6mode) {
statements += IrDelegatingConstructorCallImpl(startOffset, endOffset, context.irBuiltIns.anyType, anyConstructor, 0, 0)
}
statements += IrInstanceInitializerCallImpl(startOffset, endOffset, irClass.symbol, unitType) statements += IrInstanceInitializerCallImpl(startOffset, endOffset, irClass.symbol, unitType)
} }
@@ -36,9 +36,7 @@ class JsBlockDecomposerLowering(val context: JsIrBackendContext) : AbstractBlock
JsIrBuilder.buildCall(context.intrinsics.unreachable, context.irBuiltIns.nothingType) JsIrBuilder.buildCall(context.intrinsics.unreachable, context.irBuiltIns.nothingType)
} }
abstract class AbstractBlockDecomposerLowering( abstract class AbstractBlockDecomposerLowering(private val context: JsCommonBackendContext) : BodyLoweringPass {
private val context: JsCommonBackendContext
) : BodyLoweringPass {
private val nothingType = context.irBuiltIns.nothingType private val nothingType = context.irBuiltIns.nothingType
@@ -179,7 +179,11 @@ class CallableReferenceLowering(private val context: CommonBackendContext) : Bod
origin = if (isKReference || !isLambda) FUNCTION_REFERENCE_IMPL else LAMBDA_IMPL origin = if (isKReference || !isLambda) FUNCTION_REFERENCE_IMPL else LAMBDA_IMPL
name = makeContextDependentName() name = makeContextDependentName()
}.apply { }.apply {
superTypes = listOfNotNull(superClass, referenceType, secondFunctionInterface?.symbol?.typeWithArguments(referenceType.arguments)) superTypes = listOfNotNull(
this@CallableReferenceBuilder.superClass,
referenceType,
secondFunctionInterface?.symbol?.typeWithArguments(referenceType.arguments)
)
// if (samSuperType == null) // if (samSuperType == null)
// superTypes += functionSuperClass.typeWith(parameterTypes) // superTypes += functionSuperClass.typeWith(parameterTypes)
// if (irFunctionReference.isSuspend) superTypes += context.ir.symbols.suspendFunctionInterface.defaultType // if (irFunctionReference.isSuspend) superTypes += context.ir.symbols.suspendFunctionInterface.defaultType
@@ -6,18 +6,23 @@
package org.jetbrains.kotlin.ir.backend.js.lower package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBlockBody import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrBody import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall
import org.jetbrains.kotlin.ir.expressions.impl.IrRawFunctionReferenceImpl import org.jetbrains.kotlin.ir.expressions.impl.IrRawFunctionReferenceImpl
import org.jetbrains.kotlin.ir.util.isSubclassOf import org.jetbrains.kotlin.ir.util.isSubclassOf
import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.name.Name
object ES6_THROWABLE_CONSTRUCTOR_SLOT : IrDeclarationOriginImpl("ES6_THROWABLE_CONSTRUCTOR_SLOT")
/** /**
* Capture stack trace in primary constructors of Throwable * Capture stack trace in primary constructors of Throwable
*/ */
@@ -31,15 +36,36 @@ class CaptureStackTraceInThrowables(val context: JsIrBackendContext) : BodyLower
if (!klass.isSubclassOf(context.irBuiltIns.throwableClass.owner)) if (!klass.isSubclassOf(context.irBuiltIns.throwableClass.owner))
return return
val statements = (irBody as IrBlockBody).statements val statements = (irBody as? IrBlockBody)?.statements ?: return
val delegatingConstructorCallIndex = statements.indexOfLast { it is IrDelegatingConstructorCall } val delegatingConstructorCallIndex = statements.indexOfLast { it is IrDelegatingConstructorCall }
statements.add(delegatingConstructorCallIndex + 1, JsIrBuilder.buildCall(context.intrinsics.captureStack).also { call -> statements.add(delegatingConstructorCallIndex + 1, JsIrBuilder.buildCall(context.intrinsics.captureStack).also { call ->
call.putValueArgument(0, JsIrBuilder.buildGetValue(klass.thisReceiver!!.symbol)) val self = klass.thisReceiver!!.symbol
call.putValueArgument(
1, val constructorRef = if (context.es6mode) {
IrRawFunctionReferenceImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.anyType, container.symbol) JsIrBuilder.buildGetField(klass.addThrowableConstructorSlot().symbol, JsIrBuilder.buildGetValue(self))
) } else {
JsIrBuilder.buildRawReference(container.symbol, context.irBuiltIns.anyType)
}
call.putValueArgument(0, JsIrBuilder.buildGetValue(self))
call.putValueArgument(1, constructorRef)
}) })
} }
private fun IrClass.addThrowableConstructorSlot(): IrField {
return factory.buildField {
type = context.dynamicType
isFinal = false
isExternal = false
isStatic = false
metadata = null
name = Name.identifier(Namer.THROWABLE_CONSTRUCTOR)
visibility = DescriptorVisibilities.PRIVATE
origin = ES6_THROWABLE_CONSTRUCTOR_SLOT
}.apply {
parent = this@addThrowableConstructorSlot
declarations.add(this)
}
}
} }
@@ -8,14 +8,20 @@ package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.compilationException import org.jetbrains.kotlin.backend.common.compilationException
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.getVoid
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.expressions.IrBody import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstKind import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
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.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.defaultType import org.jetbrains.kotlin.ir.types.defaultType
@@ -33,7 +39,13 @@ class ConstTransformer(private val context: JsIrBackendContext) : IrElementTrans
): IrExpression { ): IrExpression {
val constructor = irClass.constructors.single { it.owner.isPrimary } val constructor = irClass.constructors.single { it.owner.isPrimary }
val argType = constructor.owner.valueParameters.first().type val argType = constructor.owner.valueParameters.first().type
return IrConstructorCallImpl.fromSymbolOwner(expression.startOffset, expression.endOffset, irClass.defaultType, constructor).apply {
return IrConstructorCallImpl.fromSymbolOwner(
expression.startOffset,
expression.endOffset,
irClass.defaultType,
constructor
).apply {
for (i in args.indices) { for (i in args.indices) {
putValueArgument(i, carrierFactory(startOffset, endOffset, argType, args[i])) putValueArgument(i, carrierFactory(startOffset, endOffset, argType, args[i]))
} }
@@ -0,0 +1,182 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.ir.ValueRemapper
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.getVoid
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.utils.hasStrictSignature
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
object ES6_BOX_PARAMETER : IrDeclarationOriginImpl("ES6_BOX_PARAMETER")
object ES6_BOX_PARAMETER_DEFAULT_RESOLUTION : IrStatementOriginImpl("ES6_BOX_PARAMETER_DEFAULT_RESOLUTION")
val IrValueParameter.isBoxParameter: Boolean
get() = origin == ES6_BOX_PARAMETER
val IrWhen.isBoxParameterDefaultResolution: Boolean
get() = origin == ES6_BOX_PARAMETER_DEFAULT_RESOLUTION
val IrFunction.boxParameter: IrValueParameter?
get() = valueParameters.lastOrNull()?.takeIf { it.isBoxParameter }
class ES6AddBoxParameterToConstructorsLowering(val context: JsIrBackendContext) : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (!context.es6mode || declaration !is IrConstructor || declaration.hasStrictSignature(context)) return null
hackEnums(declaration)
hackExceptions(declaration)
hackSimpleClassWithCapturing(declaration)
if (!declaration.isSyntheticPrimaryConstructor) {
declaration.addBoxParameter()
}
return null
}
private fun IrConstructor.addBoxParameter() {
val irClass = parentAsClass
val boxParameter = generateBoxParameter(irClass).also { valueParameters += it }
val body = body as? IrBlockBody ?: return
val isBoxUsed = body.replaceThisWithBoxBeforeSuperCall(irClass, boxParameter.symbol)
if (isBoxUsed) {
body.statements.add(0, boxParameter.generateDefaultResolution())
}
}
private fun createJsObjectLiteral(): IrExpression {
return JsIrBuilder.buildCall(context.intrinsics.jsEmptyObject)
}
private fun IrConstructor.generateBoxParameter(irClass: IrClass): IrValueParameter {
return JsIrBuilder.buildValueParameter(
parent = this,
name = Namer.ES6_BOX_PARAMETER_NAME,
index = valueParameters.size,
type = irClass.defaultType.makeNullable(),
origin = ES6_BOX_PARAMETER,
isAssignable = true
)
}
private fun IrValueParameter.generateDefaultResolution(): IrExpression {
return with(context.createIrBuilder(symbol, UNDEFINED_OFFSET, UNDEFINED_OFFSET)) {
irIfThen(
context.irBuiltIns.unitType,
irEqeqeqWithoutBox(irGet(type, symbol), this@ES6AddBoxParameterToConstructorsLowering.context.getVoid()),
irSet(symbol, createJsObjectLiteral()),
ES6_BOX_PARAMETER_DEFAULT_RESOLUTION
)
}
}
private fun IrBody.replaceThisWithBoxBeforeSuperCall(irClass: IrClass, boxParameterSymbol: IrValueSymbol): Boolean {
var meetCapturing = false
var meetDelegatingConstructor = false
val selfParameterSymbol = irClass.thisReceiver!!.symbol
transformChildrenVoid(object : ValueRemapper(mapOf(selfParameterSymbol to boxParameterSymbol)) {
override fun visitGetValue(expression: IrGetValue): IrExpression {
return if (meetDelegatingConstructor) {
expression
} else {
super.visitGetValue(expression)
}
}
override fun visitSetField(expression: IrSetField): IrExpression {
if (meetDelegatingConstructor) return expression
val newExpression = super.visitSetField(expression)
val receiver = expression.receiver as? IrGetValue
if (receiver?.symbol == boxParameterSymbol) {
meetCapturing = true
}
return newExpression
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
meetDelegatingConstructor = true
return super.visitDelegatingConstructorCall(expression)
}
})
return meetCapturing
}
private fun hackEnums(constructor: IrConstructor) {
constructor.transformChildren(object : IrElementTransformerVoid() {
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
return (expression.argument as? IrDelegatingConstructorCall) ?: expression
}
}, null)
}
private fun hackSimpleClassWithCapturing(constructor: IrConstructor) {
val irClass = constructor.parentAsClass
if (irClass.superClass != null || (!irClass.isInner && !irClass.isLocal)) return
val statements = (constructor.body as? IrBlockBody)?.statements ?: return
val delegationConstructorIndex = statements.indexOfFirst { it is IrDelegatingConstructorCall }
if (delegationConstructorIndex == -1) return
val firstClassFieldAssignment = statements.indexOfFirst { statement ->
statement is IrSetField && statement.receiver?.let { it is IrGetValue && it.symbol == irClass.thisReceiver?.symbol } == true
}
if (firstClassFieldAssignment == -1 || firstClassFieldAssignment > delegationConstructorIndex) return
statements.add(firstClassFieldAssignment, statements[delegationConstructorIndex])
statements.removeAt(delegationConstructorIndex + 1)
}
/**
* Swap call synthetic primary ctor and call extendThrowable
*/
private fun hackExceptions(constructor: IrConstructor) {
val setPropertiesSymbol = context.setPropertiesToThrowableInstanceSymbol
val statements = (constructor.body as? IrBlockBody)?.statements ?: return
var callIndex = -1
var superCallIndex = -1
for (i in statements.indices) {
val s = statements[i]
if (s is IrCall && s.symbol === setPropertiesSymbol) {
callIndex = i
}
if (s is IrDelegatingConstructorCall && s.symbol.owner.origin === PrimaryConstructorLowering.SYNTHETIC_PRIMARY_CONSTRUCTOR) {
superCallIndex = i
}
}
if (callIndex != -1 && superCallIndex != -1) {
val tmp = statements[callIndex]
statements[callIndex] = statements[superCallIndex]
statements[superCallIndex] = tmp
}
}
}
@@ -1,216 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.BodyLoweringPass
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
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.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.lower.PrimaryConstructorLowering.SYNTHETIC_PRIMARY_CONSTRUCTOR
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.types.isAny
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.statements
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.name.Name
/**
* Add ES6_INIT_BOX_PARAMETER for each constructor (except constructors
* of inline, external and primitive (string, arrays) classes).
* Uses: Inline class's constructor initialize field of outer class before
* call `super`, but its impossible in ES6. ES6_INIT_BOX_PARAMETER - object,
* that collect these values and pass to super constructor. In the last
* constructor of delegation chain ES6_INIT_BOX_PARAMETER will be open
* with `Object.assign(this, box)`
*
* Add ES6_RESULT_TYPE PARAMETER for each secondary ctor (except
* constructors of external and primitive (string, arrays) classes).
* Uses: Pass information about type of new object to `Reflect.construct()`
*
* Transform SYNTHETIC_PRIMARY_CONSTRUCTOR:
* constructor() {
* //statements
* }
* ==>
* constructor() {
* init(this)
* }
* init($this$) {
* //statements from ctor
* }
*/
class ES6AddInternalParametersToConstructorPhase(val context: JsIrBackendContext) : BodyLoweringPass {
object ES6_SYNTHETIC_PRIMARY_INIT_FUNCTION : IrDeclarationOriginImpl("ES6_SYNTHETIC_PRIMARY_INIT_FUNCTION")
object ES6_INIT_BOX_PARAMETER : IrDeclarationOriginImpl("ES6_INIT_BOX_PARAMETER")
object ES6_RESULT_TYPE_PARAMETER : IrDeclarationOriginImpl("ES6_RESULT_TYPE_PARAMETER")
override fun lower(irBody: IrBody, container: IrDeclaration) {
if (!context.es6mode) return
container.transform(CallSiteTransformer(), null)
if (container !is IrConstructor) return
if (!container.hasStrictSignature()) container.addInternalValueParameters()
if (container.origin === SYNTHETIC_PRIMARY_CONSTRUCTOR) {
createInitFunction(container)
openInitializerBox(container)
}
}
private fun IrConstructor.addInternalValueParameters() {
addValueParameter("box", context.dynamicType, ES6_INIT_BOX_PARAMETER)
if (!isPrimary) {
addValueParameter("resultType", context.dynamicType, ES6_RESULT_TYPE_PARAMETER)
}
}
private fun createInitFunction(constructor: IrConstructor) {
val irClass = constructor.parentAsClass
val initFunction = buildInitFunction(constructor, irClass)
irClass.declarations += initFunction
context.mapping.constructorToInitFunction[constructor] = initFunction
initFunction.transformChildren(object : IrElementTransformerVoid() {
override fun visitDeclaration(declaration: IrDeclarationBase): IrStatement {
declaration.parent = initFunction
return declaration
}
}, null)
}
private fun openInitializerBox(constructor: IrConstructor) = with(constructor.body as IrBlockBody) {
statements.clear()
statements += JsIrBuilder.buildCall(context.intrinsics.jsOpenInitializerBox).also {
it.putValueArgument(0, JsIrBuilder.buildGetValue(constructor.parentAsClass.thisReceiver!!.symbol))
it.putValueArgument(1, JsIrBuilder.buildGetValue(constructor.valueParameters.last().symbol))
}
}
private fun buildInitFunction(constructor: IrConstructor, irClass: IrClass): IrSimpleFunction {
val functionName = "${irClass.name}_init"
return context.irFactory.buildFun {
name = Name.identifier(functionName)
returnType = context.irBuiltIns.unitType
visibility = DescriptorVisibilities.PROTECTED
modality = Modality.FINAL
isInline = constructor.isInline
isExternal = constructor.isExternal
origin = ES6_SYNTHETIC_PRIMARY_INIT_FUNCTION
}.apply {
parent = irClass
addValueParameter("\$this\$", context.dynamicType)
body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET, constructor.body?.statements.orEmpty())
transformChildren(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
return if (expression.symbol.owner === constructor.parentAsClass.thisReceiver!!) {
with(expression) { IrGetValueImpl(startOffset, endOffset, type, valueParameters.single().symbol) }
} else {
expression
}
}
}, null)
}
}
/**
* Pass `null` for ES6_INIT_BOX_PARAMETER and ES6_RESULT_TYPE_PARAMETER
*/
inner class CallSiteTransformer : IrElementTransformerVoid() {
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
val constructor = expression.symbol.owner
val parent = constructor.parentAsClass
if (constructor.hasStrictSignature() || parent.defaultType.isAny()) return expression
val newArgsCount = if (constructor.isPrimary) 1 else 2
return expression.run {
IrConstructorCallImpl(
startOffset,
endOffset,
type,
symbol,
typeArgumentsCount,
constructorTypeArgumentsCount,
valueArgumentsCount + newArgsCount
).also {
for (i in 0 until valueArgumentsCount) {
it.putValueArgument(i, getValueArgument(i))
}
it.putValueArgument(
valueArgumentsCount,
JsIrBuilder.buildNull(context.dynamicType)
)
if (!constructor.isPrimary) {
it.putValueArgument(
valueArgumentsCount + 1,
JsIrBuilder.buildNull(context.dynamicType)
)
}
}
}
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
val constructor = expression.symbol.owner
val parent = constructor.parentAsClass
if (constructor.hasStrictSignature() || parent.defaultType.isAny()) return expression
val newArgsCount = if (constructor.isPrimary) 1 else 2
return expression.run {
IrDelegatingConstructorCallImpl(
startOffset,
endOffset,
type,
symbol,
typeArgumentsCount,
valueArgumentsCount + newArgsCount
).also {
for (i in 0 until valueArgumentsCount) {
it.putValueArgument(i, getValueArgument(i))
}
it.putValueArgument(
valueArgumentsCount,
JsIrBuilder.buildNull(context.dynamicType)
)
if (!constructor.isPrimary) {
it.putValueArgument(
valueArgumentsCount + 1,
JsIrBuilder.buildNull(context.dynamicType)
)
}
}
}
}
}
private fun IrConstructor.hasStrictSignature(): Boolean {
val primitives = with(context.irBuiltIns) { primitiveTypesToPrimitiveArrays.values + stringClass }
return with(parentAsClass) { isExternal || context.inlineClassesUtils.isClassInlineLike(this) || symbol in primitives }
}
}
@@ -0,0 +1,140 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.getVoid
import org.jetbrains.kotlin.ir.backend.js.utils.irEmpty
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.util.isLocal
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.superClass
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd
class ES6ConstructorBoxParameterOptimizationLowering(private val context: JsIrBackendContext) : BodyLoweringPass {
private val esClassWhichNeedBoxParameters = context.mapping.esClassWhichNeedBoxParameters
override fun lower(irBody: IrBody, container: IrDeclaration) {
if (!context.es6mode) return
val containerFunction = container as? IrFunction
val shouldRemoveBoxRelatedDeclarationsAndStatements =
containerFunction?.isEs6ConstructorReplacement == true && !containerFunction.parentAsClass.requiredToHaveBoxParameter()
if (containerFunction != null && shouldRemoveBoxRelatedDeclarationsAndStatements && irBody is IrBlockBody) {
containerFunction.valueParameters = containerFunction.valueParameters.filter { !it.isBoxParameter }
}
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitWhen(expression: IrWhen): IrExpression {
return if (shouldRemoveBoxRelatedDeclarationsAndStatements && expression.isBoxParameterDefaultResolution) {
irEmpty(context)
} else {
super.visitWhen(expression)
}
}
override fun visitCall(expression: IrCall): IrExpression {
val callee = expression.symbol.owner
return when {
shouldRemoveBoxRelatedDeclarationsAndStatements && (callee.symbol == context.intrinsics.jsCreateThisSymbol || callee.symbol == context.intrinsics.jsCreateExternalThisSymbol) -> {
expression.putValueArgument(expression.valueArgumentsCount - 1, context.getVoid())
super.visitCall(expression)
}
callee.isEs6ConstructorReplacement && (!callee.parentAsClass.requiredToHaveBoxParameter() || shouldRemoveBoxRelatedDeclarationsAndStatements) -> {
val newArgumentsSize = expression.valueArgumentsCount - 1
super.visitCall(IrCallImpl(
expression.startOffset,
expression.endOffset,
expression.type,
expression.symbol,
expression.typeArgumentsCount,
newArgumentsSize,
expression.origin,
superQualifierSymbol = expression.superQualifierSymbol
).apply {
copyTypeArgumentsFrom(expression)
dispatchReceiver = expression.dispatchReceiver
extensionReceiver = expression.extensionReceiver
for (i in 0 until newArgumentsSize) {
putValueArgument(i, expression.getValueArgument(i))
}
})
}
else -> super.visitCall(expression)
}
}
})
}
private fun IrClass.requiredToHaveBoxParameter(): Boolean {
return esClassWhichNeedBoxParameters.contains(this)
}
}
class ES6CollectConstructorsWhichNeedBoxParameters(private val context: JsIrBackendContext) : DeclarationTransformer {
private val esClassWhichNeedBoxParameters = context.mapping.esClassWhichNeedBoxParameters
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (!context.es6mode || declaration !is IrClass) return null
val hasSuperClass = declaration.superClass != null
if (hasSuperClass && declaration.isInner) {
declaration.addToClassListWhichNeedBoxParameter()
}
if (hasSuperClass && declaration.isLocal && declaration.containsCapturedValues()) {
declaration.addToClassListWhichNeedBoxParameter()
}
return null
}
private fun IrClass.containsCapturedValues(): Boolean {
if (superClass == null) return false
declarations
.filterIsInstanceAnd<IrFunction> { it.isEs6ConstructorReplacement }
.forEach {
var meetCapturing = false
val boxParameter = it.boxParameter
it.body?.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitSetField(expression: IrSetField) {
val receiver = expression.receiver as? IrGetValue
if (receiver != null && receiver.symbol == boxParameter?.symbol) {
meetCapturing = true
}
super.visitSetField(expression)
}
})
if (meetCapturing) return true
}
return false
}
private fun IrClass.addToClassListWhichNeedBoxParameter() {
if (isExternal) return
esClassWhichNeedBoxParameters.add(this)
superClass?.addToClassListWhichNeedBoxParameter()
}
}
@@ -0,0 +1,73 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.hasStrictSignature
import org.jetbrains.kotlin.ir.backend.js.utils.jsConstructorReference
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class ES6ConstructorCallLowering(val context: JsIrBackendContext) : BodyLoweringPass {
private var IrConstructor.constructorFactory by context.mapping.secondaryConstructorToFactory
override fun lower(irBody: IrBody, container: IrDeclaration) {
if (!context.es6mode) return
val containerFunction = container as? IrFunction
irBody.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
val currentConstructor = expression.symbol.owner
val irClass = currentConstructor.parentAsClass
val currentFunction = currentFunction?.irElement as? IrFunction ?: containerFunction
if (irClass.symbol == context.irBuiltIns.anyClass || currentConstructor.hasStrictSignature(context)) {
return super.visitConstructorCall(expression)
}
val factoryFunction = currentConstructor.constructorFactory ?: error("Replacement for the constructor is not found")
if (expression.isInitCall) {
assert(factoryFunction.isInitFunction) { "Expect to have init function replacement" }
return JsIrBuilder.buildCall(factoryFunction.symbol).apply {
copyValueArgumentsFrom(expression, factoryFunction)
}
}
val isDelegatingCall =
expression.isSyntheticDelegatingReplacement && currentFunction != null && currentFunction.parentAsClass != irClass
val factoryFunctionCall = JsIrBuilder.buildCall(
factoryFunction.symbol,
superQualifierSymbol = irClass.symbol.takeIf { isDelegatingCall },
origin = if (isDelegatingCall) ES6_DELEGATING_CONSTRUCTOR_REPLACEMENT else JsStatementOrigins.SYNTHESIZED_STATEMENT
).apply {
copyValueArgumentsFrom(expression, factoryFunction)
if (expression.isSyntheticDelegatingReplacement) {
currentFunction?.boxParameter?.let {
putValueArgument(valueArgumentsCount - 1, JsIrBuilder.buildGetValue(it.symbol))
}
if (superQualifierSymbol == null) {
dispatchReceiver = JsIrBuilder.buildGetValue(factoryFunction.dispatchReceiverParameter!!.symbol)
}
} else {
dispatchReceiver = irClass.jsConstructorReference(context)
}
}
return super.visitCall(factoryFunctionCall)
}
})
}
}
@@ -5,568 +5,253 @@
package org.jetbrains.kotlin.ir.backend.js.lower package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.backend.common.ir.ValueRemapper
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.lower.ES6AddInternalParametersToConstructorPhase.ES6_INIT_BOX_PARAMETER import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.backend.js.lower.ES6AddInternalParametersToConstructorPhase.ES6_RESULT_TYPE_PARAMETER
import org.jetbrains.kotlin.ir.backend.js.lower.PrimaryConstructorLowering.SYNTHETIC_PRIMARY_CONSTRUCTOR
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.types.isAny import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.statements
import org.jetbrains.kotlin.ir.util.transformInPlace
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.runIf
object ES6_THIS_VARIABLE_ORIGIN : IrDeclarationOriginImpl("ES6_THIS_VARIABLE_ORIGIN") object ES6_INIT_CALL : IrStatementOriginImpl("ES6_INIT_CALL")
object ES6_CONSTRUCTOR_REPLACEMENT : IrDeclarationOriginImpl("ES6_CONSTRUCTOR_REPLACEMENT")
object ES6_PRIMARY_CONSTRUCTOR_REPLACEMENT : IrDeclarationOriginImpl("ES6_PRIMARY_CONSTRUCTOR_REPLACEMENT")
object ES6_INIT_FUNCTION : IrDeclarationOriginImpl("ES6_INIT_FUNCTION")
object ES6_DELEGATING_CONSTRUCTOR_REPLACEMENT : IrStatementOriginImpl("ES6_DELEGATING_CONSTRUCTOR_REPLACEMENT")
class ES6ConstructorLowering(val context: JsIrBackendContext) : BodyLoweringPass { val IrDeclaration.isEs6ConstructorReplacement: Boolean
override fun lower(irBody: IrBody, container: IrDeclaration) { get() = origin == ES6_CONSTRUCTOR_REPLACEMENT || origin == ES6_PRIMARY_CONSTRUCTOR_REPLACEMENT
if (!context.es6mode) return
if (container !is IrConstructor) return val IrDeclaration.isEs6PrimaryConstructorReplacement: Boolean
get() = origin == ES6_PRIMARY_CONSTRUCTOR_REPLACEMENT
if (container.hasStrictSignature()) return val IrFunctionAccessExpression.isSyntheticDelegatingReplacement: Boolean
get() = origin == ES6_DELEGATING_CONSTRUCTOR_REPLACEMENT
hackEnums(container) val IrDeclaration.isInitFunction: Boolean
hackExceptions(context, container) get() = origin == ES6_INIT_FUNCTION
val superCall = getSuperCall(container) ?: return val IrFunctionAccessExpression.isInitCall: Boolean
val superCtor = superCall.symbol.owner get() = origin == ES6_INIT_CALL
val helper = LowerCtorHelper(context, container, superCtor)
if (container.isPrimary) { class ES6ConstructorLowering(val context: JsIrBackendContext) : DeclarationTransformer {
if (superCtor.isPrimary) primaryToPrimary(helper) private var IrConstructor.constructorFactory by context.mapping.secondaryConstructorToFactory
else primaryToSecondary(helper)
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (!context.es6mode || declaration !is IrConstructor || declaration.hasStrictSignature(context)) return null
return if (declaration.isSyntheticPrimaryConstructor) {
listOf(declaration.generateInitFunction())
} else { } else {
if (superCtor.isPrimary) secondaryToPrimary(helper) val factoryFunction = declaration.generateCreateFunction()
else secondaryToSecondary(helper) listOfNotNull(factoryFunction, declaration.generateExportedConstructorIfNeed(factoryFunction))
replaceCallToDefaultPrimary(context, container)
changeIrConstructorToIrFunction(context, container)
} }
} }
/** private fun IrConstructor.generateExportedConstructorIfNeed(factoryFunction: IrSimpleFunction): IrConstructor? {
* constructor(args, box) { return runIf(isExported(context) && isPrimary) {
* var currBox = box || {} apply {
* valueParameters = valueParameters.filterNot { it.isBoxParameter }
* //1. Superclass is Any (body as? IrBlockBody)?.let {
* Object.assign(this, currBox) val selfReplacedConstructorCall = JsIrBuilder.buildCall(factoryFunction.symbol).apply {
* //body valueParameters.forEachIndexed { i, it -> putValueArgument(i, JsIrBuilder.buildGetValue(it.symbol)) }
* dispatchReceiver = JsIrBuilder.buildCall(context.intrinsics.jsNewTarget)
* //2. Base class isInline or isExternal or array/string }
* super(args) it.statements.add(JsIrBuilder.buildReturn(symbol, selfReplacedConstructorCall, returnType))
* Object.assign(this, currBox)
* //body
*
* //3. Base class !isInline and !isExternal and !(array/string)
* //fill initialization box
* super(args, currBox)
* //body
* }
*/
private fun primaryToPrimary(helper: LowerCtorHelper) = with(helper) {
statements.add(0, boxOrEmptyObject)
if (superCtor.parentAsClass.defaultType.isAny()) {
//after superCall
statements.add(1, openBoxStatement(thisSymbol, boxSymbol))
return
}
if (superCtor.hasStrictSignature()) {
//after superCall
statements.add(2, openBoxStatement(thisSymbol, boxSymbol))
} else {
fillInitializerBox(boxSymbol, constructor)
putBoxToSuperCall(boxSymbol, constructor)
}
}
/**
* constructor(args, box) {
* var currBox = box || {}
*
* //1. Base class isExternal or array/string
* var $this$ = Base_constructor(.., currBox)
* Object.assign(this, currBox)
* return $this$
*
* //2. Base class !isInline and !isExternal and !(array/string)
* //fill initialization box
* var $this$ = Base_constructor(.., currBox, new.target)
* //body
* return $this$
* }
*/
private fun primaryToSecondary(helper: LowerCtorHelper) = with(helper) {
statements.add(0, boxOrEmptyObject)
if (superCtor.hasStrictSignature()) {
fun createNewSuperCall(): IrCall {
val callType = JsIrBuilder.buildCall(context.intrinsics.jsClass, context.dynamicType, listOf(superCtor.returnType))
val newTarget = JsIrBuilder.buildCall(context.intrinsics.jsNewTarget)
val args = IrVarargImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
context.dynamicType,
context.dynamicType,
getElements(constructor)
)
return JsIrBuilder.buildCall(context.intrinsics.jsConstruct).apply {
putValueArgument(0, callType)
putValueArgument(1, newTarget)
putValueArgument(2, args)
putTypeArgument(0, superCtor.returnType)
} }
} }
val newThis = createThisVariable(createNewSuperCall())
changeReturnUnitToReturnInstance(newThis)
statements.add(2, openBoxStatement(newThis.symbol, boxSymbol))
statements += returnThis(constructor, newThis)
redirectOldThisToNewOne(constructor, newThis)
} else {
putBoxToSuperCall(boxSymbol, constructor)
fillInitializerBox(boxSymbol, constructor)
val newTarget = JsIrBuilder.buildCall(context.intrinsics.jsNewTarget)
val newThis = createThisVariable(createNewSuperCallPrimaryToSecondary(superCall, newTarget))
changeReturnUnitToReturnInstance(newThis)
statements += returnThis(constructor, newThis)
redirectOldThisToNewOne(constructor, newThis)
} }
} }
/** private fun IrConstructor.generateInitFunction(): IrSimpleFunction {
* Derived_constructor(.., box, resultType) { val constructor = this
* var currBox = box || {} val irClass = parentAsClass
* var currResultType = resultType || Derived val constructorName = "init_${irClass.constructorPostfix}"
* return context.irFactory.buildFun {
* //1. Base class isInline or isExternal or array/string name = Name.identifier(constructorName)
* var $this$ = construct(Base, currResultType, [..]) returnType = context.irBuiltIns.unitType
* Object.assign(this, currBox) visibility = DescriptorVisibilities.PRIVATE
* //body modality = Modality.FINAL
* return $this$ isInline = constructor.isInline
* isExternal = constructor.isExternal
* //2. Base class !isInline and !isExternal and !(array/string) origin = ES6_INIT_FUNCTION
* //fill initialization box }.also { factory ->
* var $this$ = construct(Base, currResultType, [.., currBox]) factory.parent = irClass
* //body factory.copyTypeParametersFrom(irClass)
* return $this$ factory.annotations = annotations
* } factory.extensionReceiverParameter = irClass.thisReceiver?.copyTo(factory)
*/
private fun secondaryToPrimary(helper: LowerCtorHelper) = with(helper) {
statements.add(0, boxOrEmptyObject)
statements.add(1, resultTypeOrDefaultType())
val resultTypeSymbol = (statements[1] as IrVariable).symbol
if (superCtor.hasStrictSignature()) { factory.body = constructor.body?.deepCopyWithSymbols(factory)?.apply {
val newThis = createThisVariable(createNewSuperCallSecondaryToPrimary(constructor, superCtor, resultTypeSymbol)) transformChildrenVoid(ValueRemapper(mapOf(irClass.thisReceiver!!.symbol to factory.extensionReceiverParameter!!.symbol)))
changeReturnUnitToReturnInstance(newThis)
statements.add(3, openBoxStatement(newThis.symbol, boxSymbol))
statements.add(returnThis(constructor, newThis))
redirectOldThisToNewOne(constructor, newThis)
} else {
fillInitializerBox(boxSymbol, constructor)
val newThis = createThisVariable(createNewSuperCallSecondaryToPrimary(constructor, superCtor, resultTypeSymbol, boxSymbol))
changeReturnUnitToReturnInstance(newThis)
statements.add(returnThis(constructor, newThis))
redirectOldThisToNewOne(constructor, newThis)
}
}
/**
* Derived_constructor(.., box, resultType) {
* var currBox = box || {}
* var currResultType = resultType || Derived
* //fill initialization box
*
* //1. Base class isInline or isExternal or array/string
* var $this$ = Base_constructor(..)
* Object.assign(this, currBox)
* //body
* return $this$
*
* //2. Base class !isInline and !isExternal and !(array/string)
* var $this$ = Base_constructor(.., currBox, currResultType)
* //body
* return $this$
* }
*/
private fun secondaryToSecondary(helper: LowerCtorHelper) = with(helper) {
statements.add(0, boxOrEmptyObject)
statements.add(1, resultTypeOrDefaultType())
val resultTypeSymbol = (statements[1] as IrVariable).symbol
putBoxToSuperCall(boxSymbol, constructor)
fillInitializerBox(boxSymbol, constructor)
if (superCtor.hasStrictSignature()) {
val newThis = createThisVariable(superCall)
changeReturnUnitToReturnInstance(newThis)
statements.add(3, openBoxStatement(newThis.symbol, boxSymbol))
statements += returnThis(constructor, newThis)
redirectOldThisToNewOne(constructor, newThis)
} else {
val newThis = createThisVariable(createNewSuperCallSecondaryToSecondary(superCall, boxSymbol, resultTypeSymbol))
changeReturnUnitToReturnInstance(newThis)
statements += returnThis(constructor, newThis)
redirectOldThisToNewOne(constructor, newThis)
}
}
//superCallBuilder
private fun createNewSuperCallSecondaryToSecondary(
superCall: IrDelegatingConstructorCall,
boxSymbol: IrValueSymbol,
resultTypeSymbol: IrVariableSymbol
) = superCall.apply {
putValueArgument(ES6_INIT_BOX_PARAMETER, JsIrBuilder.buildGetValue(boxSymbol))
putValueArgument(ES6_RESULT_TYPE_PARAMETER, JsIrBuilder.buildGetValue(resultTypeSymbol))
}
//superCallBuilder
private fun createNewSuperCallPrimaryToSecondary(
superCall: IrDelegatingConstructorCall,
newTarget: IrCall? = null
) = superCall.apply { putValueArgument(ES6_RESULT_TYPE_PARAMETER, newTarget) }
private fun IrDelegatingConstructorCall.putValueArgument(origin: IrDeclarationOrigin, value: IrExpression?) {
val valueParameters = symbol.owner.valueParameters
for (i in valueParameters.indices) {
if (valueParameters[i].origin === origin) {
putValueArgument(i, value)
} }
constructorFactory = factory
} }
} }
//superCallBuilder private fun IrConstructor.generateCreateFunction(): IrSimpleFunction {
private fun createNewSuperCallSecondaryToPrimary( val constructor = this
constructor: IrConstructor, val irClass = parentAsClass
superCtor: IrConstructor, val type = irClass.defaultType
resultTypeSymbol: IrVariableSymbol, val constructorName = "new_${irClass.constructorPostfix}"
boxSymbol: IrVariableSymbol? = null
): IrCall { return context.irFactory.buildFun {
val callType = JsIrBuilder.buildCall(context.intrinsics.jsClass, context.dynamicType, listOf(superCtor.returnType)) name = Name.identifier(constructorName)
val resultType = JsIrBuilder.buildGetValue(resultTypeSymbol) returnType = type
val arguments = IrVarargImpl( visibility = constructor.visibility
UNDEFINED_OFFSET, UNDEFINED_OFFSET, modality = Modality.FINAL
context.irBuiltIns.anyNType, isInline = constructor.isInline
context.irBuiltIns.anyNType, isExternal = constructor.isExternal
getElements(constructor, boxSymbol) origin = when {
constructor.isPrimary -> ES6_PRIMARY_CONSTRUCTOR_REPLACEMENT
else -> ES6_CONSTRUCTOR_REPLACEMENT
}
}.also { factory ->
factory.parent = irClass
factory.copyTypeParametersFrom(irClass)
factory.copyValueParametersFrom(constructor)
factory.annotations = annotations
factory.dispatchReceiverParameter = irClass.thisReceiver?.copyTo(factory)
if (irClass.isExported(context) && constructor.isPrimary) {
factory.excludeFromExport()
}
factory.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
val bodyCopy = constructor.body?.deepCopyWithSymbols(factory) ?: return@createBlockBody
val self = bodyCopy.replaceSuperCallsAndThisUsages(irClass, factory, constructor)
statements.addAll(bodyCopy.statements)
statements.add(JsIrBuilder.buildReturn(factory.symbol, JsIrBuilder.buildGetValue(self), irClass.defaultType))
}
constructorFactory = factory
}
}
private fun IrFunction.generateThisVariable(irClass: IrClass, initializer: IrExpression): IrVariable {
return JsIrBuilder.buildVar(
type = irClass.defaultType,
parent = this,
name = Namer.SYNTHETIC_RECEIVER_NAME,
initializer = initializer
) )
if (superCtor.parentAsClass.defaultType.isAny()) {
//superType is Any, and we have default primary --> create call to default primary
return JsIrBuilder.buildCall(context.intrinsics.jsConstruct, context.irBuiltIns.nothingType).apply {
arguments.elements.clear()
arguments.addElement(JsIrBuilder.buildGetValue(boxSymbol!!))
putValueArgument(0, JsIrBuilder.buildCall(context.intrinsics.jsClass, context.dynamicType, listOf(constructor.returnType)))
putValueArgument(1, resultType)
putValueArgument(2, arguments)
putTypeArgument(0, constructor.returnType)
}
}
return JsIrBuilder.buildCall(context.intrinsics.jsConstruct, context.irBuiltIns.nothingType).apply {
putValueArgument(0, callType)
putValueArgument(1, resultType)
putValueArgument(2, arguments)
putTypeArgument(0, superCtor.returnType)
}
} }
/** private val IrClass.constructorPostfix: String
* Copy arguments from superCall get() = fqNameWhenAvailable?.asString()?.replace('.', '_') ?: name.toString()
*/
private fun getElements(constructor: IrConstructor, boxSymbol: IrValueSymbol? = null): List<IrVarargElement> {
val result = mutableListOf<IrVarargElement>()
val superCall = constructor.body!!.statements.filterIsInstance<IrDelegatingConstructorCall>().first() private fun irAnyArray(elements: List<IrExpression>): IrExpression {
repeat(superCall.valueArgumentsCount) { i -> return JsIrBuilder.buildArray(
val arg = superCall.getValueArgument(i) ?: return@repeat elements,
if (superCall.symbol.owner.valueParameters[i].origin === ES6_INIT_BOX_PARAMETER) { context.irBuiltIns.arrayClass.typeWith(context.irBuiltIns.anyNType),
result += JsIrBuilder.buildGetValue(boxSymbol!!) context.irBuiltIns.anyNType,
} else { )
if (context.inlineClassesUtils.getInlinedClass(arg.type) != null) { }
val any = context.irBuiltIns.anyNType
result += JsIrBuilder.buildTypeOperator(any, IrTypeOperator.REINTERPRET_CAST, arg, any) private fun IrBody.replaceSuperCallsAndThisUsages(
irClass: IrClass,
constructorReplacement: IrSimpleFunction,
currentConstructor: IrConstructor,
): IrValueSymbol {
var generatedThisValueSymbol: IrValueSymbol? = null
val selfParameterSymbol = irClass.thisReceiver!!.symbol
val boxParameterSymbol = constructorReplacement.boxParameter
transformChildrenVoid(object : ValueRemapper(emptyMap()) {
override val map: MutableMap<IrValueSymbol, IrValueSymbol> = currentConstructor.valueParameters
.zip(constructorReplacement.valueParameters)
.associate { it.first.symbol to it.second.symbol }
.toMutableMap()
override fun visitReturn(expression: IrReturn): IrExpression {
return if (expression.returnTargetSymbol == currentConstructor.symbol) {
super.visitReturn(
JsIrBuilder.buildReturn(
constructorReplacement.symbol,
JsIrBuilder.buildGetValue(selfParameterSymbol),
irClass.defaultType
)
)
} else { } else {
result += arg super.visitReturn(expression)
}
}
}
return result
}
//builder
private fun returnThis(constructor: IrConstructor, newThis: IrVariable): IrReturn {
return JsIrBuilder.buildReturn(
constructor.symbol,
JsIrBuilder.buildGetValue(newThis.symbol),
context.irBuiltIns.nothingType
)
}
/**
* Transform statements like `this.x = y` to `box.x = y`
*/
private fun fillInitializerBox(boxSymbol: IrValueSymbol, constructor: IrConstructor) {
val statements = (constructor.body as IrBlockBody).statements
for (i in statements.indices) {
val current = statements[i]
if (current is IrSetField) {
if ((current.receiver as? IrGetValue)?.symbol?.owner === constructor.parentAsClass.thisReceiver) {
current.receiver = JsIrBuilder.buildGetValue(boxSymbol)
} }
} }
if (statements[i] is IrDelegatingConstructorCall) {
break
}
}
}
//transformer
private fun putBoxToSuperCall(boxSymbol: IrValueSymbol, constructor: IrConstructor) {
constructor.transformChildren(object : IrElementTransformerVoid() {
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression { override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
return expression.also { it.putValueArgument(ES6_INIT_BOX_PARAMETER, JsIrBuilder.buildGetValue(boxSymbol)) } val constructor = expression.symbol.owner
if (constructor.isSyntheticPrimaryConstructor) {
return JsIrBuilder.buildConstructorCall(expression.symbol, origin = ES6_INIT_CALL)
.apply {
copyValueArgumentsFrom(expression, constructor)
extensionReceiver = JsIrBuilder.buildGetValue(selfParameterSymbol)
}
.run { visitConstructorCall(this) }
}
val boxParameterGetter = boxParameterSymbol?.let { JsIrBuilder.buildGetValue(it.symbol) } ?: context.getVoid()
val newThisValue = when {
constructor.isEffectivelyExternal() ->
JsIrBuilder.buildCall(context.intrinsics.jsCreateExternalThisSymbol)
.apply {
putValueArgument(0, irClass.getCurrentConstructorReference(constructorReplacement))
putValueArgument(1, expression.symbol.owner.parentAsClass.jsConstructorReference(context))
putValueArgument(2, irAnyArray(expression.valueArguments.map { it ?: context.getVoid() }))
putValueArgument(3, boxParameterGetter)
}
constructor.parentAsClass.symbol == context.irBuiltIns.anyClass ->
JsIrBuilder.buildCall(context.intrinsics.jsCreateThisSymbol)
.apply {
putValueArgument(0, irClass.getCurrentConstructorReference(constructorReplacement))
putValueArgument(1, boxParameterGetter)
}
else ->
JsIrBuilder.buildConstructorCall(
expression.symbol,
null,
expression.typeArguments,
ES6_DELEGATING_CONSTRUCTOR_REPLACEMENT
).apply {
copyValueArgumentsFrom(expression, constructor)
}
}
val newThisVariable = constructorReplacement.generateThisVariable(irClass, newThisValue)
.also {
generatedThisValueSymbol = it.symbol
map[selfParameterSymbol] = it.symbol
}
return super.visitComposite(JsIrBuilder.buildComposite(context.irBuiltIns.unitType, listOf(newThisVariable)))
} }
}, null) })
return generatedThisValueSymbol!!
} }
//builder private fun IrClass.getCurrentConstructorReference(currentFactoryFunction: IrSimpleFunction): IrExpression {
private fun openBoxStatement(thisSymbol: IrValueSymbol, boxSymbol: IrValueSymbol): IrCall { return if (isFinalClass) {
return JsIrBuilder.buildCall(context.intrinsics.jsOpenInitializerBox).also { jsConstructorReference(context)
it.putValueArgument(0, JsIrBuilder.buildGetValue(thisSymbol)) } else {
it.putValueArgument(1, JsIrBuilder.buildGetValue(boxSymbol)) JsIrBuilder.buildGetValue(currentFactoryFunction.dispatchReceiverParameter!!.symbol)
} }
} }
//util private fun IrDeclaration.excludeFromExport() {
private fun IrConstructor.hasStrictSignature(): Boolean { val jsExportIgnoreClass = context.intrinsics.jsExportIgnoreAnnotationSymbol.owner
val primitives = with(context.irBuiltIns) { primitiveTypesToPrimitiveArrays.values + stringClass } val jsExportIgnoreCtor = jsExportIgnoreClass.primaryConstructor ?: return
return with(parentAsClass) { isExternal || context.inlineClassesUtils.isClassInlineLike(this) || symbol in primitives } annotations += JsIrBuilder.buildConstructorCall(jsExportIgnoreCtor.symbol)
} }
} }
//transformer
private fun replaceCallToDefaultPrimary(context: JsIrBackendContext, constructor: IrConstructor) {
val thisSymbol = (((constructor.body as IrBlockBody).statements
.find { it is IrVariable && it.origin === ES6_THIS_VARIABLE_ORIGIN }) as IrVariable?)?.symbol ?: return
(constructor.body as IrBlockBody).statements.transformInPlace {
if (it is IrDelegatingConstructorCall) {
val superCtor = it.symbol.owner
val initFunc = context.mapping.constructorToInitFunction[superCtor]!!
JsIrBuilder.buildCall(initFunc.symbol).apply {
putValueArgument(0, JsIrBuilder.buildGetValue(thisSymbol))
}
} else it
}
}
//transformer
private fun redirectOldThisToNewOne(constructor: IrConstructor, newThis: IrVariable) {
constructor.transformChildren(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
return if (expression.symbol.owner === constructor.parentAsClass.thisReceiver!!) {
with(expression) { IrGetValueImpl(startOffset, endOffset, type, newThis.symbol) }
} else {
expression
}
}
}, null)
}
/**
* Change `return Unit` to `return $this$`
*/
private fun LowerCtorHelper.changeReturnUnitToReturnInstance(newThis: IrVariable) {
constructor.transformChildren(object : IrElementTransformerVoid() {
override fun visitReturn(expression: IrReturn): IrExpression {
return JsIrBuilder.buildReturn(
constructor.symbol,
JsIrBuilder.buildGetValue(newThis.symbol),
expression.type
)
}
}, null)
}
private fun hackEnums(constructor: IrConstructor) {
constructor.transformChildren(object : IrElementTransformerVoid() {
override fun visitTypeOperator(expression: IrTypeOperatorCall): IrExpression {
return (expression.argument as? IrDelegatingConstructorCall) ?: expression
}
}, null)
}
/**
* Swap call synthetic primary ctor and call extendThrowable
*/
private fun hackExceptions(context: JsIrBackendContext, constructor: IrConstructor) {
val setPropertiesSymbol = context.setPropertiesToThrowableInstanceSymbol
val statements = (constructor.body as IrBlockBody).statements
var callIndex = -1
var superCallIndex = -1
for (i in statements.indices) {
val s = statements[i]
if (s is IrCall && s.symbol === setPropertiesSymbol) callIndex = i
if (s is IrDelegatingConstructorCall && s.symbol.owner.origin === SYNTHETIC_PRIMARY_CONSTRUCTOR) superCallIndex = i
}
if (callIndex != -1 && superCallIndex != -1) {
val tmp = statements[callIndex]
statements[callIndex] = statements[superCallIndex]
statements[superCallIndex] = tmp
}
}
private class LowerCtorHelper(
private val context: JsIrBackendContext,
val constructor: IrConstructor,
val superCtor: IrConstructor
) {
private val boxParameterSymbol = constructor.valueParameters.find { it.origin === ES6_INIT_BOX_PARAMETER }!!.symbol
val resultTypeParameterSymbol by lazy {
constructor.valueParameters.find { it.origin === ES6_RESULT_TYPE_PARAMETER }!!.symbol
}
val statements = (constructor.body as IrBlockBody).statements
/**
* var currBox = box || {}
*/
val boxOrEmptyObject = boxOrEmptyObject(boxParameterSymbol, constructor)
private fun boxOrEmptyObject(boxSymbol: IrValueSymbol, parent: IrConstructor): IrVariable {
val emptyObject = JsIrBuilder.buildCall(context.intrinsics.jsEmptyObject)
val or = JsIrBuilder.buildCall(context.intrinsics.jsOr, context.dynamicType).apply {
putValueArgument(0, JsIrBuilder.buildGetValue(boxSymbol))
putValueArgument(1, emptyObject)
}
return JsIrBuilder.buildVar(context.dynamicType, parent, "currBox", initializer = or)
}
val boxSymbol = boxOrEmptyObject.symbol
val thisSymbol = constructor.parentAsClass.thisReceiver!!.symbol
val superCall: IrDelegatingConstructorCall by lazy { getSuperCall(constructor)!! }
/**
* var $this$ = `newSuperCall`
*/
fun createThisVariable(newSuperCall: IrExpression): IrVariable {
val newThis = JsIrBuilder.buildVar(
context.dynamicType,
constructor,
"\$this\$",
initializer = newSuperCall
).apply {
origin = ES6_THIS_VARIABLE_ORIGIN
}
(constructor.body as IrBlockBody).statements.transformInPlace {
if (it === superCall) newThis
else it
}
return newThis
}
/**
* var currResultType = resultType || D
*/
fun resultTypeOrDefaultType(): IrStatement {
val defaultType = JsIrBuilder.buildCall(context.intrinsics.es6DefaultType).apply {
putTypeArgument(0, constructor.parentAsClass.defaultType)
}
val or = JsIrBuilder.buildCall(context.intrinsics.jsOr, context.dynamicType).apply {
putValueArgument(0, JsIrBuilder.buildGetValue(resultTypeParameterSymbol))
putValueArgument(1, defaultType)
}
return JsIrBuilder.buildVar(context.dynamicType, constructor, "currResultType", initializer = or)
}
}
private fun getSuperCall(constructor: IrConstructor): IrDelegatingConstructorCall? {
var result: IrDelegatingConstructorCall? = null
(constructor.body as IrBlockBody).acceptChildren(object : IrElementVisitor<Unit, Any?> {
override fun visitElement(element: IrElement, data: Any?) { }
override fun visitBlock(expression: IrBlock, data: Any?) {
expression.statements.forEach { it.accept(this, data) }
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: Any?) {
result = result ?: expression
}
}, null)
return result
}
private fun changeIrConstructorToIrFunction(context: JsIrBackendContext, container: IrConstructor) {
val newConstructor = context.irFactory.buildFun {
name = Name.identifier("${container.parentAsClass.name}_constructor")
returnType = container.returnType
origin = JsIrBuilder.SYNTHESIZED_DECLARATION
}.apply {
parent = container.parent
container.valueParameters.forEach { param ->
addValueParameter(param.name.asString(), param.type, param.origin)
}
val parametersMap = container.valueParameters.zip(valueParameters).toMap()
body = container.body
transformChildren(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
val newParam = parametersMap[expression.symbol.owner]
return if (newParam != null) JsIrBuilder.buildGetValue(newParam.symbol) else expression
}
}, null)
}
container.parentAsClass.declarations.transformInPlace {
if (it === container) newConstructor else it
}
context.mapping.secondaryConstructorToDelegate[container] = newConstructor
}
@@ -0,0 +1,258 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.ir.ValueRemapper
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.JsAnnotations
import org.jetbrains.kotlin.ir.backend.js.utils.MutableReference
import org.jetbrains.kotlin.ir.backend.js.utils.irEmpty
import org.jetbrains.kotlin.ir.backend.js.utils.mutableReferenceOf
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd
const val CREATE_EXTERNAL_THIS_CONSTRUCTOR_PARAMETERS = 2
class ES6PrimaryConstructorOptimizationLowering(private val context: JsIrBackendContext) : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (!context.es6mode || declaration !is IrFunction || !declaration.shouldBeConvertedToPlainConstructor(context)) {
return null
}
val irClass = declaration.parentAsClass
if (irClass.isExported(context)) {
irClass.removeConstructorForExport()
}
return listOf(declaration.convertToRegularConstructor(irClass))
}
private fun IrFunction.convertToRegularConstructor(irClass: IrClass): IrConstructor {
val original = this
val superClass = irClass.superClass
val classThisSymbol = irClass.thisReceiver!!.symbol
return factory.buildConstructor {
updateFrom(original)
isPrimary = true
returnType = original.returnType
origin = IrDeclarationOrigin.DEFINED
}.also { constructor ->
constructor.copyAnnotationsFrom(original)
constructor.copyParameterDeclarationsFrom(original)
constructor.parent = irClass
if (irClass.isExported(context)) {
constructor.annotations = original.annotations.withoutFirst { it.isAnnotation(JsAnnotations.jsExportIgnoreFqn) }
}
val boxParameter = constructor.boxParameter
val body = (original.body?.deepCopyWithSymbols(constructor) as IrBlockBody)
.also { constructor.body = it }
body.transformChildrenVoid(object : ValueRemapper(emptyMap()) {
override val map = original.valueParameters.zip(constructor.valueParameters)
.associate { it.first.symbol to it.second.symbol }
.toMutableMap<IrValueSymbol, IrValueSymbol>()
override fun visitReturn(expression: IrReturn): IrExpression {
return if (expression.returnTargetSymbol == original.symbol) {
return irEmpty(context)
} else {
super.visitReturn(expression)
}
}
override fun visitCall(expression: IrCall): IrExpression {
return if (expression.symbol == context.intrinsics.jsBoxApplySymbol) {
irEmpty(context)
} else {
super.visitCall(expression)
}
}
override fun visitVariable(declaration: IrVariable): IrStatement {
val initializer = declaration.initializer
if (initializer is IrCall) {
when {
initializer.isSyntheticDelegatingReplacement -> {
map[declaration.symbol] = classThisSymbol
return super.visitCall(initializer)
}
initializer.symbol == context.intrinsics.jsCreateThisSymbol -> {
map[declaration.symbol] = classThisSymbol
return if (boxParameter != null && superClass == null) {
super.visitCall(JsIrBuilder.buildCall(context.intrinsics.jsBoxApplySymbol).apply {
putValueArgument(0, JsIrBuilder.buildGetValue(irClass.thisReceiver!!.symbol))
putValueArgument(1, JsIrBuilder.buildGetValue(boxParameter.symbol))
})
} else {
irEmpty(context)
}
}
initializer.symbol == context.intrinsics.jsCreateExternalThisSymbol -> {
map[declaration.symbol] = classThisSymbol
val externalConstructor =
superClass?.primaryConstructor?.symbol ?: error("Expect to have external constructor here")
val parameters = initializer.getValueArgument(CREATE_EXTERNAL_THIS_CONSTRUCTOR_PARAMETERS) as? IrVararg
?: error("Wrong type of argument was provided")
return JsIrBuilder.buildDelegatingConstructorCall(externalConstructor).apply {
parameters.elements.forEachIndexed { i, it -> putValueArgument(i, it as IrExpression) }
}
}
}
}
return super.visitVariable(declaration)
}
})
}
}
private fun IrClass.removeConstructorForExport() {
declarations.removeIf { it is IrConstructor }
}
private inline fun <T> Iterable<T>.withoutFirst(predicate: (T) -> Boolean): List<T> {
val original = this
return buildList {
var isFirstMatch = true
for (element in original) {
if (!isFirstMatch || !predicate(element)) {
add(element)
} else {
isFirstMatch = false
}
}
}
}
}
class ES6PrimaryConstructorUsageOptimizationLowering(private val context: JsIrBackendContext) : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression {
val callee = expression.symbol.owner
return when {
!callee.shouldBeConvertedToPlainConstructor(context) -> super.visitCall(expression)
expression.isSyntheticDelegatingReplacement -> {
super.visitDelegatingConstructorCall(JsIrBuilder.buildDelegatingConstructorCall(callee.parentAsClass.primaryConstructor!!.symbol)
.apply { copyTypeAndValueArgumentsFrom(expression) })
}
else -> {
super.visitConstructorCall(JsIrBuilder.buildConstructorCall(callee.parentAsClass.primaryConstructor!!.symbol)
.apply { copyTypeAndValueArgumentsFrom(expression) })
}
}
}
})
}
}
/**
* When we can't optimize class hierarchy chain if there is a class inside which:
* 1. Has primary constructor which delegates to a secondary
* 2. Has secondary constructor which delegates to a primary
* 3. Has a constructor with a box parameter, and it has an external superclass
* 4. Is a subtype for Throwable, because we replace the super call inside constructors with `setPropertiesToThrowableInstance` call
* Otherwise, we can generate a simple ES-class constructor in each class of the hierarchy
*/
class ES6CollectPrimaryConstructorsWhichCouldBeOptimizedLowering(private val context: JsIrBackendContext) : DeclarationTransformer {
private val esClassWhichNeedBoxParameters = context.mapping.esClassWhichNeedBoxParameters
private val esClassToPossibilityForOptimization = context.mapping.esClassToPossibilityForOptimization
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (
context.es6mode &&
declaration is IrClass &&
!declaration.isExternal &&
!context.inlineClassesUtils.isClassInlineLike(declaration) &&
!esClassToPossibilityForOptimization.contains(declaration)
) {
declaration.checkIfCanBeOptimized()
}
return null
}
private fun IrClass.checkIfCanBeOptimized() {
var currentClass: IrClass? = this
var nearestOptimizationDecision: MutableReference<Boolean>? = null
while (currentClass != null && !currentClass.isExternal) {
val currentClassOptimizationDecision = esClassToPossibilityForOptimization[currentClass]
if (currentClassOptimizationDecision != null) {
nearestOptimizationDecision = currentClassOptimizationDecision
break
}
currentClass = currentClass.superClass
}
if (nearestOptimizationDecision == null) {
nearestOptimizationDecision = mutableReferenceOf(true)
}
currentClass = this
while (currentClass != null && !currentClass.isExternal && !esClassToPossibilityForOptimization.contains(currentClass)) {
esClassToPossibilityForOptimization[currentClass] = nearestOptimizationDecision
if (nearestOptimizationDecision.value && !currentClass.canBeOptimized()) {
nearestOptimizationDecision.value = false
}
currentClass = currentClass.superClass
}
}
private fun IrClass.canBeOptimized(): Boolean {
return superClass?.symbol != context.throwableClass && !isSubclassOfExternalClassWithRequiredBoxParameter() && !hasPrimaryDelegatedToSecondaryOrSecondaryToPrimary()
}
private fun IrClass.hasPrimaryDelegatedToSecondaryOrSecondaryToPrimary(): Boolean {
declarations
.filterIsInstanceAnd<IrFunction> { it.isEs6ConstructorReplacement }
.forEach {
var meetUnoptimizedDelegation = false
it.body?.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression {
if (expression.isSyntheticDelegatingReplacement && expression.symbol.owner.origin != it.origin) {
meetUnoptimizedDelegation = true
}
return super.visitCall(expression)
}
})
if (meetUnoptimizedDelegation) return true
}
return false
}
private fun IrClass.isSubclassOfExternalClassWithRequiredBoxParameter(): Boolean {
return superClass?.isExternal == true && esClassWhichNeedBoxParameters.contains(this)
}
}
private fun IrFunction.shouldBeConvertedToPlainConstructor(context: JsIrBackendContext): Boolean {
return isEs6PrimaryConstructorReplacement && context.mapping.esClassToPossibilityForOptimization[parentAsClass]?.value == true
}
@@ -6,11 +6,10 @@
package org.jetbrains.kotlin.ir.backend.js.lower package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.DeclarationTransformer import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.export.isExported import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.isJsImplicitExport import org.jetbrains.kotlin.ir.backend.js.utils.isJsImplicitExport
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
@@ -86,7 +85,7 @@ class ImplicitlyExportedDeclarationsMarkingLowering(private val context: JsIrBac
private fun IrDeclaration.markWithJsImplicitExport() { private fun IrDeclaration.markWithJsImplicitExport() {
val jsImplicitExportCtor = context.intrinsics.jsImplicitExportAnnotationSymbol.constructors.single() val jsImplicitExportCtor = context.intrinsics.jsImplicitExportAnnotationSymbol.constructors.single()
annotations += context.createIrBuilder(symbol).irCall(jsImplicitExportCtor) annotations += JsIrBuilder.buildConstructorCall(jsImplicitExportCtor)
parentClassOrNull?.takeIf { it.shouldBeMarkedWithImplicitExport() }?.markWithJsImplicitExport() parentClassOrNull?.takeIf { it.shouldBeMarkedWithImplicitExport() }?.markWithJsImplicitExport()
} }
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.jsConstructorReference
import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
@@ -44,7 +45,7 @@ class JsClassUsageInReflectionLowering(val backendContext: JsIrBackendContext) :
private fun IrClassReference.generateDirectValueUsage(): IrExpression? { private fun IrClassReference.generateDirectValueUsage(): IrExpression? {
return with(backendContext) { return with(backendContext) {
when(val classSymbol = symbol as? IrClassSymbol ?: return null) { when (val classSymbol = symbol as? IrClassSymbol ?: return null) {
irBuiltIns.nothingClass -> null irBuiltIns.nothingClass -> null
irBuiltIns.anyClass -> irBuiltIns.anyClass ->
JsIrBuilder.buildCall(intrinsics.jsCode).apply { JsIrBuilder.buildCall(intrinsics.jsCode).apply {
@@ -59,10 +60,7 @@ class JsClassUsageInReflectionLowering(val backendContext: JsIrBackendContext) :
) )
} }
else -> else -> classSymbol.owner.jsConstructorReference(backendContext)
JsIrBuilder.buildCall(intrinsics.jsClass, origin = JsStatementOrigins.CLASS_REFERENCE).apply {
putTypeArgument(0, classSymbol.owner.defaultType)
}
} }
} }
} }
@@ -17,13 +17,11 @@ import org.jetbrains.kotlin.ir.backend.js.utils.JsAnnotations
import org.jetbrains.kotlin.ir.backend.js.utils.getVoid import org.jetbrains.kotlin.ir.backend.js.utils.getVoid
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.runIf import org.jetbrains.kotlin.utils.addToStdlib.runIf
@@ -72,18 +70,13 @@ class JsDefaultArgumentStubGenerator(override val context: JsIrBackendContext) :
?.also { new -> variables[param] = new } ?: param ?.also { new -> variables[param] = new } ?: param
} }
val defaultResolutionStatements = valueParameters.mapNotNull { val blockBody = body as? IrBlockBody
irBuilder.createResolutionStatement(it, it.defaultValue?.expression)
}
if (variables.isNotEmpty()) {
body?.transformChildren(VariableRemapper(variables), null)
body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += defaultResolutionStatements
statements += body?.statements ?: emptyList()
}
if (blockBody != null && variables.isNotEmpty()) {
blockBody.transformChildren(VariableRemapper(variables), null)
blockBody.statements.addAll(0, valueParameters.mapNotNull {
irBuilder.createResolutionStatement(it, it.defaultValue?.expression)
})
} }
return also { return also {
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
import org.jetbrains.kotlin.ir.backend.js.export.isExported import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.backend.js.utils.getVoid import org.jetbrains.kotlin.ir.backend.js.utils.getVoid
import org.jetbrains.kotlin.ir.backend.js.utils.jsConstructorReference
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
@@ -91,19 +92,7 @@ class JsDefaultParameterInjector(override val context: JsIrBackendContext) :
0, 0,
1 1
).apply { ).apply {
putValueArgument( putValueArgument(0, owner.jsConstructorReference(context))
0,
IrCallImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
context.dynamicType,
context.intrinsics.jsClass,
1,
0
).apply {
putTypeArgument(0, owner.defaultType)
}
)
} }
} }
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.lower.InnerClassesSupport
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.backend.js.JsMapping import org.jetbrains.kotlin.ir.backend.js.JsMapping
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder.SYNTHESIZED_DECLARATION import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder.SYNTHESIZED_DECLARATION
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.Namer import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.builders.declarations.buildField import org.jetbrains.kotlin.ir.builders.declarations.buildField
@@ -41,7 +42,7 @@ class JsInnerClassesSupport(mapping: JsMapping, private val irFactory: IrFactory
irFactory.buildField { irFactory.buildField {
origin = IrDeclarationOrigin.FIELD_FOR_OUTER_THIS origin = IrDeclarationOrigin.FIELD_FOR_OUTER_THIS
name = Name.identifier("\$this") name = Name.identifier(Namer.SYNTHETIC_RECEIVER_NAME)
type = outerClass.defaultType type = outerClass.defaultType
visibility = DescriptorVisibilities.PROTECTED visibility = DescriptorVisibilities.PROTECTED
isFinal = true isFinal = true
@@ -12,15 +12,19 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.backend.common.lower.irIfThen import org.jetbrains.kotlin.backend.common.lower.irIfThen
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.getVoid
import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.buildField import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.types.makeNullable import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
@@ -28,9 +32,7 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class ObjectDeclarationLowering( class ObjectDeclarationLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
val context: JsCommonBackendContext
) : DeclarationTransformer {
private var IrClass.instanceField by context.mapping.objectToInstanceField private var IrClass.instanceField by context.mapping.objectToInstanceField
private var IrClass.syntheticPrimaryConstructor by context.mapping.classToSyntheticPrimaryConstructor private var IrClass.syntheticPrimaryConstructor by context.mapping.classToSyntheticPrimaryConstructor
@@ -39,7 +41,6 @@ class ObjectDeclarationLowering(
* If the object being lowered is nested inside an enum class, we want to also initialize the enum entries when initializing the object. * If the object being lowered is nested inside an enum class, we want to also initialize the enum entries when initializing the object.
*/ */
private var IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun private var IrClass.initEntryInstancesFun: IrSimpleFunction? by context.mapping.enumClassToInitEntryInstancesFun
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? { override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration !is IrClass || declaration.kind != ClassKind.OBJECT || declaration.isEffectivelyExternal()) if (declaration !is IrClass || declaration.kind != ClassKind.OBJECT || declaration.isEffectivelyExternal())
return null return null
@@ -67,7 +68,7 @@ class ObjectDeclarationLowering(
if (initEntryInstancesFun != null) if (initEntryInstancesFun != null)
+irCall(initEntryInstancesFun) +irCall(initEntryInstancesFun)
+irIfThen( +irIfThen(
irEqualsNull(irGetField(null, instanceField)), irNullabilityCheck(instanceField),
// Instance field initialized inside constructor // Instance field initialized inside constructor
irCallConstructor(primaryConstructor.symbol, emptyList()) irCallConstructor(primaryConstructor.symbol, emptyList())
) )
@@ -77,35 +78,30 @@ class ObjectDeclarationLowering(
return listOf(declaration, instanceField, getInstanceFun) return listOf(declaration, instanceField, getInstanceFun)
} }
private fun IrBuilderWithScope.irNullabilityCheck(instanceField: IrField): IrExpression {
val context = this@ObjectDeclarationLowering.context
return if (context is JsIrBackendContext && context.es6mode) {
irEqeqeqWithoutBox(irGetField(null, instanceField), context.getVoid())
} else {
irEqualsNull(irGetField(null, instanceField))
}
}
} }
class ObjectUsageLowering( class ObjectUsageLowering(val context: JsCommonBackendContext) : BodyLoweringPass {
val context: JsCommonBackendContext
) : BodyLoweringPass {
private var IrClass.instanceField by context.mapping.objectToInstanceField private var IrClass.instanceField by context.mapping.objectToInstanceField
override fun lower(irBody: IrBody, container: IrDeclaration) { override fun lower(irBody: IrBody, container: IrDeclaration) {
if (container is IrConstructor && container.isPrimary) { val functionContainer = container.takeIf { it is IrConstructor && it.isPrimary }
val irClass = container.parentAsClass val irClass = functionContainer?.parentAsClass
irClass.instanceField?.let { instanceField ->
// Initialize instance field in the beginning of the constructor because it can be used inside the constructor later irClass?.instanceField?.let {
val initInstanceField = context.createIrBuilder(container.symbol).buildStatement(UNDEFINED_OFFSET, UNDEFINED_OFFSET) { if (context.es6mode && irClass.superClass == null) return@let
irSetField(null, instanceField, irGet(irClass.thisReceiver!!)) // Initialize instance field in the beginning of the constructor because it can be used inside the constructor later
} val initInstanceField = generateInitInstanceField(it, irClass.getValueForInstanceFieldForTheFirstTime())
if (context.es6mode) { (irBody as IrBlockBody).statements.add(0, initInstanceField)
//find `superCall` and put after
(irBody as IrBlockBody).statements.transformFlat {
if (it is IrDelegatingConstructorCall) listOf(it, initInstanceField)
else if (it is IrVariable && it.origin === ES6_THIS_VARIABLE_ORIGIN) {
initInstanceField.value = JsIrBuilder.buildGetValue(it.symbol)
listOf(it, initInstanceField)
} else null
}
} else {
(irBody as IrBlockBody).statements.add(0, initInstanceField)
}
}
} }
irBody.transformChildrenVoid(object : IrElementTransformerVoid() { irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
@@ -114,8 +110,35 @@ class ObjectUsageLowering(
if (obj.isEffectivelyExternal()) return expression if (obj.isEffectivelyExternal()) return expression
return JsIrBuilder.buildCall(context.getOrCreateGetInstanceFunction(obj).symbol) return JsIrBuilder.buildCall(context.getOrCreateGetInstanceFunction(obj).symbol)
} }
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression {
val instanceField = irClass?.instanceField
return if (!context.es6mode || instanceField == null) {
super.visitDelegatingConstructorCall(expression)
} else {
JsIrBuilder.buildComposite(
context.irBuiltIns.unitType,
listOf(
super.visitDelegatingConstructorCall(expression),
generateInitInstanceField(instanceField, JsIrBuilder.buildGetValue(irClass.thisReceiver!!.symbol))
)
)
}
}
}) })
} }
private fun IrClass.getValueForInstanceFieldForTheFirstTime(): IrExpression {
return if (context.es6mode && context is JsIrBackendContext) {
JsIrBuilder.buildNull(thisReceiver!!.type)
} else {
JsIrBuilder.buildGetValue(thisReceiver!!.symbol)
}
}
private fun generateInitInstanceField(instanceField: IrField, value: IrExpression): IrStatement {
return JsIrBuilder.buildSetField(instanceField.symbol, null, value, context.irBuiltIns.unitType)
}
} }
private fun JsCommonBackendContext.getOrCreateGetInstanceFunction(obj: IrClass) = private fun JsCommonBackendContext.getOrCreateGetInstanceFunction(obj: IrClass) =
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationTransformer import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
@@ -17,18 +18,20 @@ import org.jetbrains.kotlin.ir.expressions.IrInstanceInitializerCall
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
val IrDeclaration.isSyntheticPrimaryConstructor: Boolean
get() = origin == PrimaryConstructorLowering.SYNTHETIC_PRIMARY_CONSTRUCTOR
// Create primary constructor if it doesn't exist // Create primary constructor if it doesn't exist
class PrimaryConstructorLowering(val context: JsCommonBackendContext) : DeclarationTransformer { class PrimaryConstructorLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
private var IrClass.syntheticPrimaryConstructor by context.mapping.classToSyntheticPrimaryConstructor private var IrClass.syntheticPrimaryConstructor by context.mapping.classToSyntheticPrimaryConstructor
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? { override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration is IrClass && !declaration.isInterface) { if (declaration is IrClass && declaration.kind != ClassKind.INTERFACE) {
val constructors = declaration.constructors val constructors = declaration.constructors
if (constructors.any { it.isPrimary }) return null if (constructors.any { it.isPrimary }) return null
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.DeclarationTransformer import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.compilationException
import org.jetbrains.kotlin.backend.common.getOrPut import org.jetbrains.kotlin.backend.common.getOrPut
import org.jetbrains.kotlin.backend.common.ir.ValueRemapper import org.jetbrains.kotlin.backend.common.ir.ValueRemapper
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
@@ -33,6 +32,7 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.runIf
class SecondaryConstructorLowering(val context: JsIrBackendContext) : DeclarationTransformer { class SecondaryConstructorLowering(val context: JsIrBackendContext) : DeclarationTransformer {
@@ -234,6 +234,7 @@ private fun JsIrBackendContext.buildConstructorFactory(constructor: IrConstructo
class SecondaryFactoryInjectorLowering(val context: JsIrBackendContext) : BodyLoweringPass { class SecondaryFactoryInjectorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) { override fun lower(irBody: IrBody, container: IrDeclaration) {
if (context.es6mode) return
// TODO Simplify? Is this needed at all? // TODO Simplify? Is this needed at all?
var parentFunction: IrFunction? = container as? IrFunction var parentFunction: IrFunction? = container as? IrFunction
var declaration = container var declaration = container
@@ -266,14 +267,7 @@ private class CallsiteRedirectionTransformer(private val context: JsIrBackendCon
val target = expression.symbol.owner val target = expression.symbol.owner
return if (target.isSecondaryConstructorCall) { return if (target.isSecondaryConstructorCall) {
val factory = with(context) { val factory = context.buildConstructorFactory(target, target.parentAsClass)
if (es6mode) mapping.secondaryConstructorToDelegate[target]
?: compilationException(
"Not found IrFunction for secondary ctor",
expression
)
else buildConstructorFactory(target, target.parentAsClass)
}
replaceSecondaryConstructorWithFactoryFunction(expression, factory.symbol) replaceSecondaryConstructorWithFactoryFunction(expression, factory.symbol)
} else expression } else expression
} }
@@ -285,18 +279,8 @@ private class CallsiteRedirectionTransformer(private val context: JsIrBackendCon
return if (target.isSecondaryConstructorCall) { return if (target.isSecondaryConstructorCall) {
val klass = target.parentAsClass val klass = target.parentAsClass
val delegate = with(context) { val delegate = context.buildConstructorDelegate(target, klass)
if (es6mode) mapping.secondaryConstructorToDelegate[target]
?: compilationException(
"Not found IrFunction for secondary ctor",
expression
)
else buildConstructorDelegate(target, klass)
}
val newCall = replaceSecondaryConstructorWithFactoryFunction(expression, delegate.symbol) val newCall = replaceSecondaryConstructorWithFactoryFunction(expression, delegate.symbol)
if (context.es6mode) {
return newCall
}
val readThis = expression.run { val readThis = expression.run {
if (data is IrConstructor) { if (data is IrConstructor) {
@@ -315,16 +299,19 @@ private class CallsiteRedirectionTransformer(private val context: JsIrBackendCon
private fun replaceSecondaryConstructorWithFactoryFunction( private fun replaceSecondaryConstructorWithFactoryFunction(
call: IrFunctionAccessExpression, call: IrFunctionAccessExpression,
newTarget: IrSimpleFunctionSymbol newTarget: IrSimpleFunctionSymbol
) = IrCallImpl( ): IrCall {
call.startOffset, call.endOffset, call.type, newTarget, val irClass = call.symbol.owner.parentAsClass
typeArgumentsCount = call.typeArgumentsCount, return IrCallImpl(
valueArgumentsCount = newTarget.owner.valueParameters.size call.startOffset, call.endOffset, call.type, newTarget,
).apply { typeArgumentsCount = call.typeArgumentsCount,
valueArgumentsCount = newTarget.owner.valueParameters.size,
superQualifierSymbol = irClass.symbol.takeIf { context.es6mode && call.isSyntheticDelegatingReplacement }
).apply {
copyTypeArgumentsFrom(call)
copyTypeArgumentsFrom(call) for (i in 0 until call.valueArgumentsCount) {
putValueArgument(i, call.getValueArgument(i))
for (i in 0 until call.valueArgumentsCount) { }
putValueArgument(i, call.getValueArgument(i))
} }
} }
} }
@@ -6,8 +6,8 @@
package org.jetbrains.kotlin.ir.backend.js.lower package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.utils.getVoid import org.jetbrains.kotlin.ir.backend.js.utils.getVoid
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrDeclaration
@@ -21,12 +21,7 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
class ThrowableLowering( class ThrowableLowering(val context: JsIrBackendContext, val extendThrowableFunction: IrSimpleFunctionSymbol) : BodyLoweringPass {
val context: JsIrBackendContext,
val extendThrowableFunction: IrSimpleFunctionSymbol
) : BodyLoweringPass {
private val nothingNType = context.irBuiltIns.nothingNType
private val throwableConstructors = context.throwableConstructors private val throwableConstructors = context.throwableConstructors
private val newThrowableFunction = context.newThrowableSymbol private val newThrowableFunction = context.newThrowableSymbol
@@ -64,6 +59,8 @@ class ThrowableLowering(
} }
inner class Transformer : IrElementTransformer<IrDeclarationParent> { inner class Transformer : IrElementTransformer<IrDeclarationParent> {
private val anyConstructor = context.irBuiltIns.anyClass.constructors.first()
override fun visitClass(declaration: IrClass, data: IrDeclarationParent) = super.visitClass(declaration, declaration) override fun visitClass(declaration: IrClass, data: IrDeclarationParent) = super.visitClass(declaration, declaration)
override fun visitConstructorCall(expression: IrConstructorCall, data: IrDeclarationParent): IrExpression { override fun visitConstructorCall(expression: IrConstructorCall, data: IrDeclarationParent): IrExpression {
@@ -93,7 +90,7 @@ class ThrowableLowering(
val klass = data as IrClass val klass = data as IrClass
val thisReceiver = IrGetValueImpl(expression.startOffset, expression.endOffset, klass.thisReceiver!!.symbol) val thisReceiver = IrGetValueImpl(expression.startOffset, expression.endOffset, klass.thisReceiver!!.symbol)
return expression.run { val expressionReplacement = expression.run {
IrCallImpl( IrCallImpl(
startOffset, endOffset, type, extendThrowableFunction, startOffset, endOffset, type, extendThrowableFunction,
valueArgumentsCount = 3, valueArgumentsCount = 3,
@@ -104,6 +101,25 @@ class ThrowableLowering(
it.putValueArgument(2, causeArg) it.putValueArgument(2, causeArg)
} }
} }
return if (!context.es6mode) {
expressionReplacement
} else {
JsIrBuilder.buildComposite(
context.irBuiltIns.unitType,
listOf(
IrDelegatingConstructorCallImpl(
expression.startOffset,
expression.endOffset,
context.irBuiltIns.anyType,
anyConstructor,
0,
0
),
expressionReplacement
)
)
}
} }
} }
} }
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
import org.jetbrains.kotlin.ir.backend.js.dce.eliminateDeadDeclarations
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
fun optimizeProgramByIr(
modules: Iterable<IrModuleFragment>,
context: JsIrBackendContext,
removeUnusedAssociatedObjects: Boolean
) {
eliminateDeadDeclarations(modules, context, removeUnusedAssociatedObjects)
jsOptimizationPhases.invokeToplevel(PhaseConfig(jsOptimizationPhases), context, modules)
}
@@ -166,7 +166,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
} }
return if (context.staticContext.backendContext.es6mode) { return if (context.staticContext.backendContext.es6mode) {
JsInvocation(JsNameRef("super"), arguments) JsInvocation(JsSuperRef(), arguments)
} else { } else {
JsInvocation(callFuncRef, listOf(thisRef) + arguments) JsInvocation(callFuncRef, listOf(thisRef) + arguments)
}.withSource(expression, context) }.withSource(expression, context)
@@ -65,7 +65,11 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
} }
override fun visitComposite(expression: IrComposite, context: JsGenerationContext): JsStatement { override fun visitComposite(expression: IrComposite, context: JsGenerationContext): JsStatement {
return JsBlock(expression.statements.map { it.accept(this, context) }).withSource(expression, context) return if (expression.statements.isEmpty()) {
JsEmpty
} else {
JsBlock(expression.statements.map { it.accept(this, context) }).withSource(expression, context)
}
} }
override fun visitExpression(expression: IrExpression, context: JsGenerationContext): JsStatement { override fun visitExpression(expression: IrExpression, context: JsGenerationContext): JsStatement {
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.backend.common.serialization.checkIsFunctionInterface import org.jetbrains.kotlin.backend.common.serialization.checkIsFunctionInterface
import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.ir.backend.js.* import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.ir.backend.js.dce.eliminateDeadDeclarations
import org.jetbrains.kotlin.ir.backend.js.export.* import org.jetbrains.kotlin.ir.backend.js.export.*
import org.jetbrains.kotlin.ir.backend.js.lower.StaticMembersLowering import org.jetbrains.kotlin.ir.backend.js.lower.StaticMembersLowering
import org.jetbrains.kotlin.ir.backend.js.lower.isBuiltInClass import org.jetbrains.kotlin.ir.backend.js.lower.isBuiltInClass
@@ -46,29 +45,33 @@ val IrModuleFragment.safeName: String
get() = name.asString().safeModuleName get() = name.asString().safeModuleName
enum class TranslationMode( enum class TranslationMode(
val dce: Boolean, val production: Boolean,
val perModule: Boolean, val perModule: Boolean,
val minimizedMemberNames: Boolean, val minimizedMemberNames: Boolean,
) { ) {
FULL(dce = false, perModule = false, minimizedMemberNames = false), FULL_DEV(production = false, perModule = false, minimizedMemberNames = false),
FULL_DCE(dce = true, perModule = false, minimizedMemberNames = false), FULL_PROD(production = true, perModule = false, minimizedMemberNames = false),
FULL_DCE_MINIMIZED_NAMES(dce = true, perModule = false, minimizedMemberNames = true), FULL_PROD_MINIMIZED_NAMES(production = true, perModule = false, minimizedMemberNames = true),
PER_MODULE(dce = false, perModule = true, minimizedMemberNames = false), PER_MODULE_DEV(production = false, perModule = true, minimizedMemberNames = false),
PER_MODULE_DCE(dce = true, perModule = true, minimizedMemberNames = false), PER_MODULE_PROD(production = true, perModule = true, minimizedMemberNames = false),
PER_MODULE_DCE_MINIMIZED_NAMES(dce = true, perModule = true, minimizedMemberNames = true); PER_MODULE_PROD_MINIMIZED_NAMES(production = true, perModule = true, minimizedMemberNames = true);
companion object { companion object {
fun fromFlags(dce: Boolean, perModule: Boolean, minimizedMemberNames: Boolean): TranslationMode { fun fromFlags(
production: Boolean,
perModule: Boolean,
minimizedMemberNames: Boolean
): TranslationMode {
return if (perModule) { return if (perModule) {
if (dce) { if (production) {
if (minimizedMemberNames) PER_MODULE_DCE_MINIMIZED_NAMES if (minimizedMemberNames) PER_MODULE_PROD_MINIMIZED_NAMES
else PER_MODULE_DCE else PER_MODULE_PROD
} else PER_MODULE } else PER_MODULE_DEV
} else { } else {
if (dce) { if (production) {
if (minimizedMemberNames) FULL_DCE_MINIMIZED_NAMES if (minimizedMemberNames) FULL_PROD_MINIMIZED_NAMES
else FULL_DCE else FULL_PROD
} else FULL } else FULL_DEV
} }
} }
} }
@@ -146,15 +149,15 @@ class IrModuleToJsTransformer(
val result = EnumMap<TranslationMode, CompilationOutputs>(TranslationMode::class.java) val result = EnumMap<TranslationMode, CompilationOutputs>(TranslationMode::class.java)
modes.filter { !it.dce }.forEach { modes.filter { !it.production }.forEach {
result[it] = makeJsCodeGeneratorFromIr(exportData, it).generateJsCode(relativeRequirePath, true) result[it] = makeJsCodeGeneratorFromIr(exportData, it).generateJsCode(relativeRequirePath, true)
} }
if (modes.any { it.dce }) { if (modes.any { it.production }) {
eliminateDeadDeclarations(modules, backendContext, removeUnusedAssociatedObjects) optimizeProgramByIr(modules, backendContext, removeUnusedAssociatedObjects)
} }
modes.filter { it.dce }.forEach { modes.filter { it.production }.forEach {
result[it] = makeJsCodeGeneratorFromIr(exportData, it).generateJsCode(relativeRequirePath, true) result[it] = makeJsCodeGeneratorFromIr(exportData, it).generateJsCode(relativeRequirePath, true)
} }
@@ -165,8 +168,8 @@ class IrModuleToJsTransformer(
val exportData = associateIrAndExport(modules) val exportData = associateIrAndExport(modules)
doStaticMembersLowering(modules) doStaticMembersLowering(modules)
if (mode.dce) { if (mode.production) {
eliminateDeadDeclarations(modules, backendContext, removeUnusedAssociatedObjects) optimizeProgramByIr(modules, backendContext, removeUnusedAssociatedObjects)
} }
return makeJsCodeGeneratorFromIr(exportData, mode) return makeJsCodeGeneratorFromIr(exportData, mode)
@@ -242,8 +245,12 @@ class IrModuleToJsTransformer(
val internalModuleName = ReservedJsNames.makeInternalModuleName().takeIf { !isEsModules } val internalModuleName = ReservedJsNames.makeInternalModuleName().takeIf { !isEsModules }
val globalNames = NameTable<String>(globalNameScope) val globalNames = NameTable<String>(globalNameScope)
val statements = result.declarations.statements
val fileStatements = fileExports.file.accept(IrFileToJsTransformer(useBareParameterNames = true), staticContext).statements
val exportStatements = val exportStatements =
ExportModelToJsStatements(staticContext, { globalNames.declareFreshName(it, it) }).generateModuleExport( ExportModelToJsStatements(staticContext, backendContext.es6mode, { globalNames.declareFreshName(it, it) }).generateModuleExport(
ExportedModule(mainModuleName, moduleKind, fileExports.exports), ExportedModule(mainModuleName, moduleKind, fileExports.exports),
internalModuleName, internalModuleName,
isEsModules isEsModules
@@ -252,9 +259,6 @@ class IrModuleToJsTransformer(
result.exports.statements += exportStatements result.exports.statements += exportStatements
result.dts = fileExports.tsDeclarations result.dts = fileExports.tsDeclarations
val statements = result.declarations.statements
val fileStatements = fileExports.file.accept(IrFileToJsTransformer(useBareParameterNames = true), staticContext).statements
if (fileStatements.isNotEmpty()) { if (fileStatements.isNotEmpty()) {
var startComment = "" var startComment = ""
@@ -6,39 +6,32 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.backend.common.compilationException import org.jetbrains.kotlin.backend.common.compilationException
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.export.isAllowedFakeOverriddenDeclaration import org.jetbrains.kotlin.ir.backend.js.export.isAllowedFakeOverriddenDeclaration
import org.jetbrains.kotlin.ir.backend.js.export.isExported import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.backend.js.export.isOverriddenExported import org.jetbrains.kotlin.ir.backend.js.export.isOverriddenExported
import org.jetbrains.kotlin.ir.backend.js.lower.isEs6ConstructorReplacement
import org.jetbrains.kotlin.ir.backend.js.utils.* import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.common.isValidES5Identifier import org.jetbrains.kotlin.js.common.isValidES5Identifier
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.runIf
class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) { class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) {
private val className = context.getNameForClass(irClass) private val className = context.getNameForClass(irClass)
private val classNameRef = className.makeRef() private val classNameRef = className.makeRef()
private val baseClass: IrType? = irClass.superTypes.firstOrNull { !it.classifierOrFail.isInterface } private val baseClass: IrType? = irClass.superTypes.firstOrNull { !it.classifierOrFail.isInterface }
private val classPrototypeRef by lazy { prototypeOf(classNameRef, context.staticContext) }
private val baseClassRef by lazy { // Lazy in case was not collected by namer during JsClassGenerator construction private val baseClassRef by lazy { // Lazy in case was not collected by namer during JsClassGenerator construction
if (baseClass != null && !baseClass.isAny()) baseClass.getClassRef(context) else null if (baseClass != null && !baseClass.isAny()) baseClass.getClassRef(context) else null
} }
private val classPrototypeRef = prototypeOf(classNameRef, context.staticContext)
private val classBlock = JsCompositeBlock() private val classBlock = JsCompositeBlock()
private val classModel = JsIrClassModel(irClass) private val classModel = JsIrClassModel(irClass)
@@ -67,14 +60,9 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
when (declaration) { when (declaration) {
is IrConstructor -> { is IrConstructor -> {
if (es6mode) { if (es6mode) {
declaration.accept(IrFunctionToJsTransformer(), context).let { declaration.accept(IrFunctionToJsTransformer(), context).let { fn ->
//HACK: add superCall to Error if (fn.body.statements.any { it !is JsEmpty && !it.isSimpleSuperCall(fn) }) {
if ((baseClass?.classifierOrNull?.owner as? IrClass)?.symbol === context.staticContext.backendContext.throwableClass) { jsClass.constructor = fn
it.body.statements.add(0, JsInvocation(JsNameRef("super")).makeStmt())
}
if (it.body.statements.any { it !is JsEmpty }) {
jsClass.constructor = it
} }
} }
} else { } else {
@@ -85,13 +73,15 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
properties.addIfNotNull(declaration.correspondingPropertySymbol?.owner) properties.addIfNotNull(declaration.correspondingPropertySymbol?.owner)
if (es6mode) { if (es6mode) {
val (memberRef, function) = generateMemberFunction(declaration) if (declaration.isEs6ConstructorReplacement && irClass.isInterface) continue
function?.let { jsClass.members += it } val (memberName, function) = generateMemberFunction(declaration)
declaration.generateAssignmentIfMangled(memberRef) function?.let { jsClass.members += it.escapedIfNeed() }
declaration.generateAssignmentIfMangled(memberName)
} else { } else {
val (memberRef, function) = generateMemberFunction(declaration) val (memberName, function) = generateMemberFunction(declaration)
val memberRef = jsElementAccess(memberName, classPrototypeRef)
function?.let { classBlock.statements += jsAssignment(memberRef, it.apply { name = null }).makeStmt() } function?.let { classBlock.statements += jsAssignment(memberRef, it.apply { name = null }).makeStmt() }
declaration.generateAssignmentIfMangled(memberRef) declaration.generateAssignmentIfMangled(memberName)
} }
} }
is IrClass -> { is IrClass -> {
@@ -164,6 +154,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
getterOverridesExternal || getterOverridesExternal ||
property.getJsName() != null property.getJsName() != null
) { ) {
val propertyName = context.getNameForProperty(property)
// Use "direct dispatch" for final properties, i. e. instead of this: // Use "direct dispatch" for final properties, i. e. instead of this:
// Object.defineProperty(Foo.prototype, 'prop', { // Object.defineProperty(Foo.prototype, 'prop', {
@@ -180,7 +171,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
val getterForwarder = property.getter val getterForwarder = property.getter
.takeIf { it.shouldExportAccessor(context.staticContext.backendContext) } .takeIf { it.shouldExportAccessor(context.staticContext.backendContext) }
.getOrGenerateIfFinal { .getOrGenerateIfFinalOrEs6Mode {
propertyAccessorForwarder("getter forwarder") { propertyAccessorForwarder("getter forwarder") {
JsReturn(JsInvocation(it)) JsReturn(JsInvocation(it))
} }
@@ -188,7 +179,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
val setterForwarder = property.setter val setterForwarder = property.setter
.takeIf { it.shouldExportAccessor(context.staticContext.backendContext) } .takeIf { it.shouldExportAccessor(context.staticContext.backendContext) }
.getOrGenerateIfFinal { .getOrGenerateIfFinalOrEs6Mode {
val setterArgName = JsName("value", false) val setterArgName = JsName("value", false)
propertyAccessorForwarder("setter forwarder") { propertyAccessorForwarder("setter forwarder") {
JsInvocation(it, JsNameRef(setterArgName)).makeStmt() JsInvocation(it, JsNameRef(setterArgName)).makeStmt()
@@ -197,29 +188,37 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
} }
} }
classBlock.statements += JsExpressionStatement( if (es6mode) {
defineProperty( jsClass.members += listOfNotNull(
classPrototypeRef, (getterForwarder as? JsFunction)?.apply {
context.getNameForProperty(property).ident, name = propertyName
getter = getterForwarder, modifiers.add(JsFunction.Modifier.GET)
setter = setterForwarder, },
context.staticContext (setterForwarder as? JsFunction)?.apply {
name = propertyName
modifiers.add(JsFunction.Modifier.SET)
}
) )
) } else {
classBlock.statements += JsExpressionStatement(
defineProperty(classPrototypeRef, propertyName.ident, getterForwarder, setterForwarder, context.staticContext)
)
}
} }
} }
} }
classModel.preDeclarationBlock.statements += generateSetMetadataCall() val metadataPlace = if (es6mode) classModel.postDeclarationBlock else classModel.preDeclarationBlock
metadataPlace.statements += generateSetMetadataCall()
context.staticContext.classModels[irClass.symbol] = classModel context.staticContext.classModels[irClass.symbol] = classModel
return classBlock return classBlock
} }
private inline fun IrSimpleFunction?.getOrGenerateIfFinal(generateFunc: IrSimpleFunction.() -> JsFunction?): JsExpression? { private inline fun IrSimpleFunction?.getOrGenerateIfFinalOrEs6Mode(generateFunc: IrSimpleFunction.() -> JsFunction?): JsExpression? {
if (this == null) return null if (this == null) return null
return if (modality == Modality.FINAL) accessorRef() else generateFunc() return if (!es6mode && modality == Modality.FINAL) accessorRef() else generateFunc()
} }
private fun IrSimpleFunction.isDefinedInsideExportedInterface(): Boolean { private fun IrSimpleFunction.isDefinedInsideExportedInterface(): Boolean {
@@ -237,13 +236,13 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
) )
} }
private fun IrSimpleFunction.generateAssignmentIfMangled(memberRef: JsExpression) { private fun IrSimpleFunction.generateAssignmentIfMangled(memberName: JsName) {
if ( if (
irClass.isExported(context.staticContext.backendContext) && irClass.isExported(context.staticContext.backendContext) &&
visibility.isPublicAPI && hasMangledName() && visibility.isPublicAPI && hasMangledName() &&
correspondingPropertySymbol == null correspondingPropertySymbol == null
) { ) {
classBlock.statements += jsAssignment(prototypeAccessRef(), memberRef).makeStmt() classBlock.statements += jsAssignment(prototypeAccessRef(), jsElementAccess(memberName, classPrototypeRef)).makeStmt()
} }
} }
@@ -259,9 +258,8 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
return isInterface && !isEffectivelyExternal() return isInterface && !isEffectivelyExternal()
} }
private fun generateMemberFunction(declaration: IrSimpleFunction): Pair<JsExpression, JsFunction?> { private fun generateMemberFunction(declaration: IrSimpleFunction): Pair<JsName, JsFunction?> {
val memberName = context.getNameForMemberFunction(declaration.realOverrideTarget) val memberName = context.getNameForMemberFunction(declaration.realOverrideTarget)
val memberRef = jsElementAccess(memberName.ident, classPrototypeRef)
if (declaration.isReal && declaration.body != null) { if (declaration.isReal && declaration.body != null) {
val translatedFunction: JsFunction = declaration.accept(IrFunctionToJsTransformer(), context) val translatedFunction: JsFunction = declaration.accept(IrFunctionToJsTransformer(), context)
@@ -269,10 +267,10 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
if (irClass.isInterface) { if (irClass.isInterface) {
classModel.preDeclarationBlock.statements += translatedFunction.makeStmt() classModel.preDeclarationBlock.statements += translatedFunction.makeStmt()
return Pair(memberRef, null) return Pair(memberName, null)
} }
return Pair(memberRef, translatedFunction) return Pair(memberName, translatedFunction)
} }
// do not generate code like // do not generate code like
@@ -294,7 +292,10 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
if (implClassDeclaration.shouldCopyFrom()) { if (implClassDeclaration.shouldCopyFrom()) {
val reference = context.getNameForStaticDeclaration(it).makeRef() val reference = context.getNameForStaticDeclaration(it).makeRef()
classModel.postDeclarationBlock.statements += jsAssignment(memberRef, reference).makeStmt() classModel.postDeclarationBlock.statements += jsAssignment(
jsElementAccess(memberName, classPrototypeRef),
reference
).makeStmt()
if (isFakeOverride) { if (isFakeOverride) {
classModel.postDeclarationBlock.statements += missedOverrides classModel.postDeclarationBlock.statements += missedOverrides
.map { missedOverride -> .map { missedOverride ->
@@ -307,7 +308,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
} }
} }
return Pair(memberRef, null) return Pair(memberName, null)
} }
private fun maybeGeneratePrimaryConstructor() { private fun maybeGeneratePrimaryConstructor() {
@@ -322,7 +323,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
val setMetadataFor = context.staticContext.backendContext.intrinsics.setMetadataForSymbol.owner val setMetadataFor = context.staticContext.backendContext.intrinsics.setMetadataForSymbol.owner
val ctor = classNameRef val ctor = classNameRef
val parent = baseClassRef val parent = baseClassRef?.takeIf { !es6mode }
val name = generateSimpleName() val name = generateSimpleName()
val interfaces = generateInterfacesList() val interfaces = generateInterfacesList()
val metadataConstructor = getMetadataConstructor() val metadataConstructor = getMetadataConstructor()
@@ -352,8 +353,6 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
private fun IrType.isFunctionType() = isFunctionOrKFunction() || isSuspendFunctionOrKFunction() private fun IrType.isFunctionType() = isFunctionOrKFunction() || isSuspendFunctionOrKFunction()
private fun isCoroutineClass(): Boolean = irClass.superTypes.any { it.isSuspendFunctionTypeOrSubtype() }
private fun generateSimpleName(): JsStringLiteral? { private fun generateSimpleName(): JsStringLiteral? {
return irClass.name.takeIf { !it.isSpecial }?.let { JsStringLiteral(it.identifier) } return irClass.name.takeIf { !it.isSpecial }?.let { JsStringLiteral(it.identifier) }
} }
@@ -386,7 +385,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
.distinct() .distinct()
.map { JsIntLiteral(it) } .map { JsIntLiteral(it) }
return JsArrayLiteral(arity) return JsArrayLiteral(arity).takeIf { arity.isNotEmpty() }
} }
private fun generateAssociatedObjectKey(): JsIntLiteral? { private fun generateAssociatedObjectKey(): JsIntLiteral? {
@@ -411,6 +410,31 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
} }
} }
fun JsFunction.escapedIfNeed(): JsFunction {
if (name?.ident?.isValidES5Identifier() == false) {
name = JsName("'${name.ident}'", name.isTemporary)
}
return this
}
fun JsStatement.isSimpleSuperCall(container: JsFunction): Boolean {
if (this !is JsExpressionStatement) return false
val invocation = expression as? JsInvocation ?: return false
if (invocation.qualifier !is JsSuperRef || container.parameters.size != invocation.arguments.size) return false
for (i in 0..container.parameters.lastIndex) {
val declaredParameter = container.parameters[i]
val providedParameter = (invocation.arguments[i] as? JsNameRef)?.takeIf { it.qualifier == null } ?: return false
if (declaredParameter.name != providedParameter.name) {
return false
}
}
return true
}
fun IrSimpleFunction?.shouldExportAccessor(context: JsIrBackendContext): Boolean { fun IrSimpleFunction?.shouldExportAccessor(context: JsIrBackendContext): Boolean {
if (this == null) return false if (this == null) return false
@@ -119,11 +119,6 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
JsObjectLiteral() JsObjectLiteral()
} }
add(intrinsics.es6DefaultType) { call, context ->
val typeArgument = call.getTypeArgument(0)!!
typeArgument.getClassRef(context)
}
addIfNotNull(intrinsics.jsCode) { call, _ -> addIfNotNull(intrinsics.jsCode) { call, _ ->
compilationException( compilationException(
"Should not be called", "Should not be called",
@@ -272,6 +267,16 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
add(intrinsics.jsInvokeSuspendSuperTypeWithReceiverAndParam, suspendInvokeTransform) add(intrinsics.jsInvokeSuspendSuperTypeWithReceiverAndParam, suspendInvokeTransform)
add(intrinsics.jsArguments) { _, _ -> Namer.ARGUMENTS } add(intrinsics.jsArguments) { _, _ -> Namer.ARGUMENTS }
add(intrinsics.jsNewAnonymousClass) { call, context ->
val baseClass = translateCallArguments(call, context).single() as JsNameRef
JsClass(baseClass = baseClass)
}
add(intrinsics.void.owner.getter!!.symbol) { _, context ->
val backingField = context.getNameForField(intrinsics.void.owner.backingField!!)
JsNameRef(backingField)
}
} }
} }
@@ -39,8 +39,9 @@ class Merger(
importStatements.putIfAbsent(declaration, JsVars(JsVars.JsVar(importName, rename(importExpression)))) importStatements.putIfAbsent(declaration, JsVars(JsVars.JsVar(importName, rename(importExpression))))
} }
val classModels = mutableMapOf<JsName, JsIrIcClassModel>() + f.classes val classModels = (mutableMapOf<JsName, JsIrIcClassModel>() + f.classes)
f.classes.clear() .also { f.classes.clear() }
classModels.entries.forEach { (name, model) -> classModels.entries.forEach { (name, model) ->
f.classes[rename(name)] = JsIrIcClassModel(model.superClasses.map { rename(it) }).also { f.classes[rename(name)] = JsIrIcClassModel(model.superClasses.map { rename(it) }).also {
it.preDeclarationBlock.statements += model.preDeclarationBlock.statements it.preDeclarationBlock.statements += model.preDeclarationBlock.statements
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
import org.jetbrains.kotlin.ir.backend.js.lower.*
import org.jetbrains.kotlin.ir.backend.js.sourceMapsInfo import org.jetbrains.kotlin.ir.backend.js.sourceMapsInfo
import org.jetbrains.kotlin.ir.backend.js.utils.* import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
@@ -32,7 +33,6 @@ import java.io.IOException
import java.io.InputStreamReader import java.io.InputStreamReader
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
fun jsUndefined(context: IrNamer, backendContext: JsIrBackendContext): JsExpression { fun jsUndefined(context: IrNamer, backendContext: JsIrBackendContext): JsExpression {
return when (val void = backendContext.getVoid()) { return when (val void = backendContext.getVoid()) {
is IrGetField -> context.getNameForField(void.symbol.owner).makeRef() is IrGetField -> context.getNameForField(void.symbol.owner).makeRef()
@@ -114,6 +114,7 @@ fun translateFunction(declaration: IrFunction, name: JsName?, context: JsGenerat
val body = declaration.body?.accept(IrElementToJsStatementTransformer(), functionContext) as? JsBlock ?: JsBlock() val body = declaration.body?.accept(IrElementToJsStatementTransformer(), functionContext) as? JsBlock ?: JsBlock()
val function = JsFunction(emptyScope, body, "member function ${name ?: "annon"}") val function = JsFunction(emptyScope, body, "member function ${name ?: "annon"}")
.apply { if (declaration.isEs6ConstructorReplacement) modifiers.add(JsFunction.Modifier.STATIC) }
.withSource(declaration, context, useNameOf = declaration) .withSource(declaration, context, useNameOf = declaration)
function.name = name function.name = name
@@ -147,6 +148,7 @@ fun translateCall(
transformer: IrElementToJsExpressionTransformer transformer: IrElementToJsExpressionTransformer
): JsExpression { ): JsExpression {
val function = expression.symbol.owner.realOverrideTarget val function = expression.symbol.owner.realOverrideTarget
val currentDispatchReceiver = context.currentFunction?.parentClassOrNull
context.staticContext.intrinsics[function.symbol]?.let { context.staticContext.intrinsics[function.symbol]?.let {
return it(expression, context) return it(expression, context)
@@ -194,6 +196,10 @@ fun translateCall(
Pair(function, superQualifier.owner) Pair(function, superQualifier.owner)
} }
if (expression.isSyntheticDelegatingReplacement || currentDispatchReceiver.canUseSuperRef(function, context, klass)) {
return JsInvocation(JsNameRef(context.getNameForMemberFunction(target), JsSuperRef()), arguments)
}
val callRef = if (klass.isInterface) { val callRef = if (klass.isInterface) {
val nameForStaticDeclaration = context.getNameForStaticDeclaration(target) val nameForStaticDeclaration = context.getNameForStaticDeclaration(target)
JsNameRef(Namer.CALL_FUNCTION, JsNameRef(nameForStaticDeclaration)) JsNameRef(Namer.CALL_FUNCTION, JsNameRef(nameForStaticDeclaration))
@@ -373,12 +379,15 @@ fun translateCallArguments(
): List<JsExpression> { ): List<JsExpression> {
val size = expression.valueArgumentsCount val size = expression.valueArgumentsCount
val varargParameterIndex = expression.symbol.owner.realOverrideTarget.varargParameterIndex() val function = expression.symbol.owner
val varargParameterIndex = function.realOverrideTarget.varargParameterIndex()
val validWithNullArgs = expression.validWithNullArgs() val validWithNullArgs = expression.validWithNullArgs()
val arguments = (0 until size) val arguments = (0 until size)
.mapTo(ArrayList(size)) { index -> .mapTo(ArrayList(size)) { index ->
expression.getValueArgument(index).checkOnNullability(validWithNullArgs) expression.getValueArgument(index).checkOnNullability(
validWithNullArgs || function.valueParameters[index].isBoxParameter
)
} }
.dropLastWhile { .dropLastWhile {
allowDropTailVoids && allowDropTailVoids &&
@@ -635,3 +644,11 @@ private val nameMappingOriginAllowList = setOf(
BOUND_VALUE_PARAMETER, BOUND_VALUE_PARAMETER,
JsLoweredDeclarationOrigin.JS_SHADOWED_DEFAULT_PARAMETER, JsLoweredDeclarationOrigin.JS_SHADOWED_DEFAULT_PARAMETER,
) )
private fun IrClass?.canUseSuperRef(function: IrFunction, context: JsGenerationContext, superClass: IrClass): Boolean {
return this != null &&
function.origin != IrDeclarationOrigin.LOWERED_SUSPEND_FUNCTION &&
context.staticContext.backendContext.es6mode &&
!superClass.isInterface && !isInner && !isLocal &&
context.currentFunction?.isEs6ConstructorReplacement != true
}
@@ -74,8 +74,20 @@ private fun JsNode.computeScopes(): Scope {
accept(object : RecursiveJsVisitor() { accept(object : RecursiveJsVisitor() {
var currentScope: Scope = rootScope var currentScope: Scope = rootScope
override fun visitFunction(x: JsFunction) { override fun visitClass(x: JsClass) {
x.name?.let { currentScope.declaredNames += it } x.name?.let { currentScope.declaredNames += it }
// We need it to not rename methods and fields inside class body
// Because if they are in clash with something, it means overriding
x.constructor?.accept(this)
x.members.forEach { visitFunction(it, shouldReserveName = false) }
}
override fun visitFunction(x: JsFunction) {
visitFunction(x, shouldReserveName = true)
}
fun visitFunction(x: JsFunction, shouldReserveName: Boolean) {
x.name?.takeIf { shouldReserveName }?.let { currentScope.declaredNames += it }
val oldScope = currentScope val oldScope = currentScope
currentScope = Scope().apply { currentScope = Scope().apply {
currentScope.children += this currentScope.children += this
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.ir.declarations.IrOverridableDeclaration
import org.jetbrains.kotlin.ir.expressions.IrClassReference import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrVararg
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -37,10 +36,6 @@ object JsAnnotations {
fun IrConstructorCall.getSingleConstStringArgument() = fun IrConstructorCall.getSingleConstStringArgument() =
(getValueArgument(0) as IrConst<String>).value (getValueArgument(0) as IrConst<String>).value
@Suppress("UNCHECKED_CAST")
fun IrConstructorCall.getClassReferencVarargArguments() =
(getValueArgument(0) as? IrVararg)?.elements as? List<IrClassReference>
fun IrAnnotationContainer.getJsModule(): String? = fun IrAnnotationContainer.getJsModule(): String? =
getAnnotation(JsAnnotations.jsModuleFqn)?.getSingleConstStringArgument() getAnnotation(JsAnnotations.jsModuleFqn)?.getSingleConstStringArgument()
@@ -9,16 +9,28 @@ import org.jetbrains.kotlin.descriptors.isClass
import org.jetbrains.kotlin.descriptors.isInterface import org.jetbrains.kotlin.descriptors.isInterface
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
import org.jetbrains.kotlin.ir.backend.js.export.isExported import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrReturn import org.jetbrains.kotlin.ir.expressions.IrReturn
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isAnnotationClass
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.parentClassOrNull import org.jetbrains.kotlin.ir.util.parentClassOrNull
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
fun IrClass.jsConstructorReference(context: JsIrBackendContext): IrExpression {
return JsIrBuilder.buildCall(context.intrinsics.jsClass, origin = JsStatementOrigins.CLASS_REFERENCE)
.apply { putTypeArgument(0, defaultType) }
}
fun IrDeclaration.isExportedMember(context: JsIrBackendContext) = fun IrDeclaration.isExportedMember(context: JsIrBackendContext) =
(this is IrDeclarationWithVisibility && visibility.isPublicAPI) && (this is IrDeclarationWithVisibility && visibility.isPublicAPI) &&
parentClassOrNull?.isExported(context) == true parentClassOrNull?.isExported(context) == true
@@ -42,10 +54,23 @@ fun IrDeclarationWithName.getFqNameWithJsNameWhenAvailable(shouldIncludePackage:
} }
} }
fun IrConstructor.hasStrictSignature(context: JsIrBackendContext): Boolean {
val primitives = with(context.irBuiltIns) { primitiveTypesToPrimitiveArrays.values + stringClass }
return with(parentAsClass) {
isExternal || isExpect || isAnnotationClass || context.inlineClassesUtils.isClassInlineLike(this) || symbol in primitives
}
}
private fun getKotlinOrJsQualifier(parent: IrPackageFragment, shouldIncludePackage: Boolean): FqName? { private fun getKotlinOrJsQualifier(parent: IrPackageFragment, shouldIncludePackage: Boolean): FqName? {
return (parent as? IrFile)?.getJsQualifier()?.let { FqName(it) } ?: parent.fqName.takeIf { shouldIncludePackage } return (parent as? IrFile)?.getJsQualifier()?.let { FqName(it) } ?: parent.fqName.takeIf { shouldIncludePackage }
} }
val IrFunctionAccessExpression.typeArguments: List<IrType?>
get() = List(typeArgumentsCount) { getTypeArgument(it) }
val IrFunctionAccessExpression.valueArguments: List<IrExpression?>
get() = List(valueArgumentsCount) { getValueArgument(it) }
// TODO: the code is written to pass Repl tests, so we should understand. why in Repl tests we don't have backingField // TODO: the code is written to pass Repl tests, so we should understand. why in Repl tests we don't have backingField
fun JsIrBackendContext.getVoid(): IrExpression = fun JsIrBackendContext.getVoid(): IrExpression =
intrinsics.void.owner.backingField?.let { intrinsics.void.owner.backingField?.let {
@@ -60,3 +85,8 @@ fun JsIrBackendContext.getVoid(): IrExpression =
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
irBuiltIns.nothingNType irBuiltIns.nothingNType
) )
fun irEmpty(context: JsIrBackendContext): IrExpression {
return JsIrBuilder.buildComposite(context.dynamicType, emptyList())
}
@@ -15,10 +15,13 @@ object Namer {
val OUTER_NAME = "\$outer" val OUTER_NAME = "\$outer"
val UNREACHABLE_NAME = "\$unreachable" val UNREACHABLE_NAME = "\$unreachable"
val THROWABLE_CONSTRUCTOR = "\$throwableCtor"
val DELEGATE = "\$delegate" val DELEGATE = "\$delegate"
val IMPLICIT_RECEIVER_NAME = "this" val IMPLICIT_RECEIVER_NAME = "this"
val SYNTHETIC_RECEIVER_NAME = "\$this"
val ES6_BOX_PARAMETER_NAME = "\$box"
val ARGUMENTS = JsNameRef("arguments") val ARGUMENTS = JsNameRef("arguments")
@@ -0,0 +1,10 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.utils
class MutableReference<T>(var value: T)
fun <T> mutableReferenceOf(value: T): MutableReference<T> = MutableReference(value)
@@ -54,4 +54,6 @@ object ExpressionIds {
const val PROPERTY_REFERENCE = 19 const val PROPERTY_REFERENCE = 19
const val INVOCATION = 20 const val INVOCATION = 20
const val NEW = 21 const val NEW = 21
const val CLASS = 22
const val SUPER_REF = 23
} }
@@ -240,6 +240,7 @@ private class JsIrAstDeserializer(private val source: ByteArray) {
private val sideEffectKindValues = SideEffectKind.values() private val sideEffectKindValues = SideEffectKind.values()
private val jsBinaryOperatorValues = JsBinaryOperator.values() private val jsBinaryOperatorValues = JsBinaryOperator.values()
private val jsUnaryOperatorValues = JsUnaryOperator.values() private val jsUnaryOperatorValues = JsUnaryOperator.values()
private val jsFunctionModifiersValues = JsFunction.Modifier.values()
private fun readExpression(): JsExpression { private fun readExpression(): JsExpression {
return withComments { return withComments {
@@ -249,6 +250,9 @@ private class JsIrAstDeserializer(private val source: ByteArray) {
THIS_REF -> { THIS_REF -> {
JsThisRef() JsThisRef()
} }
SUPER_REF -> {
JsSuperRef()
}
NULL -> { NULL -> {
JsNullLiteral() JsNullLiteral()
} }
@@ -283,10 +287,15 @@ private class JsIrAstDeserializer(private val source: ByteArray) {
) )
} }
FUNCTION -> { FUNCTION -> {
JsFunction(scope, readBlock(), "").apply { readFunction()
readRepeated { parameters += readParameter() } }
ifTrue { name = nameTable[readInt()] } CLASS -> {
isLocal = readBoolean() JsClass(
ifTrue { nameTable[readInt()] },
ifTrue { nameTable[readInt()].makeRef() },
ifTrue { readFunction() },
).apply {
readRepeated { members += readFunction() }
} }
} }
DOC_COMMENT -> { DOC_COMMENT -> {
@@ -353,6 +362,15 @@ private class JsIrAstDeserializer(private val source: ByteArray) {
} }
} }
private fun readFunction(): JsFunction {
return JsFunction(scope, readBlock(), "").apply {
readRepeated { parameters += readParameter() }
readRepeated { modifiers += jsFunctionModifiersValues[readInt()] }
ifTrue { name = nameTable[readInt()] }
isLocal = readBoolean()
}
}
private fun readJsImportedModule(): JsImportedModule { private fun readJsImportedModule(): JsImportedModule {
return JsImportedModule( return JsImportedModule(
stringTable[readInt()], stringTable[readInt()],
@@ -447,4 +465,4 @@ private class JsIrAstDeserializer(private val source: ByteArray) {
ifTrue { this.commentsAfterNode = readArray { readComment() }.toList() } ifTrue { this.commentsAfterNode = readArray { readComment() }.toList() }
} }
} }
} }
@@ -336,6 +336,10 @@ private class JsIrAstSerializer {
writeByte(ExpressionIds.THIS_REF) writeByte(ExpressionIds.THIS_REF)
} }
override fun visitSuper(x: JsSuperRef) {
writeByte(ExpressionIds.SUPER_REF)
}
override fun visitNull(x: JsNullLiteral) { override fun visitNull(x: JsNullLiteral) {
writeByte(ExpressionIds.NULL) writeByte(ExpressionIds.NULL)
} }
@@ -387,12 +391,24 @@ private class JsIrAstSerializer {
override fun visitFunction(x: JsFunction) { override fun visitFunction(x: JsFunction) {
writeByte(ExpressionIds.FUNCTION) writeByte(ExpressionIds.FUNCTION)
writeBlock(x.body) writeFunction(x)
writeCollection(x.parameters) { writeParameter(it) } }
override fun visitClass(x: JsClass) {
writeByte(ExpressionIds.CLASS)
ifNotNull(x.name) { ifNotNull(x.name) {
writeInt(internalizeName(it)) writeInt(internalizeName(it))
} }
writeBoolean(x.isLocal) // TODO: add more complex JsNameRef parsing in future when we will support `class` expressions inside a `js` call
ifNotNull(x.baseClass?.name) {
writeInt(internalizeName(it))
}
ifNotNull(x.constructor) {
writeFunction(it)
}
writeCollection(x.members) {
writeFunction(it)
}
} }
override fun visitDocComment(comment: JsDocComment) { override fun visitDocComment(comment: JsDocComment) {
@@ -494,6 +510,16 @@ private class JsIrAstSerializer {
ifNotNull(module.plainReference) { writeExpression(it) } ifNotNull(module.plainReference) { writeExpression(it) }
} }
private fun DataWriter.writeFunction(function: JsFunction) {
writeBlock(function.body)
writeCollection(function.parameters) { writeParameter(it) }
writeCollection(function.modifiers) { writeInt(it.ordinal) }
ifNotNull(function.name) {
writeInt(internalizeName(it))
}
writeBoolean(function.isLocal)
}
private fun DataWriter.writeParameter(parameter: JsParameter) { private fun DataWriter.writeParameter(parameter: JsParameter) {
writeInt(internalizeName(parameter.name)) writeInt(internalizeName(parameter.name))
writeBoolean(parameter.hasDefaultValue) writeBoolean(parameter.hasDefaultValue)
@@ -5,8 +5,6 @@
package org.jetbrains.kotlin.ir.expressions package org.jetbrains.kotlin.ir.expressions
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
abstract class IrStatementOriginImpl(val debugName: String) : IrStatementOrigin { abstract class IrStatementOriginImpl(val debugName: String) : IrStatementOrigin {
override fun toString(): String = debugName override fun toString(): String = debugName
} }
@@ -205,6 +205,12 @@ val IrProperty.isSimpleProperty: Boolean
val IrClass.functions: Sequence<IrSimpleFunction> val IrClass.functions: Sequence<IrSimpleFunction>
get() = declarations.asSequence().filterIsInstance<IrSimpleFunction>() get() = declarations.asSequence().filterIsInstance<IrSimpleFunction>()
val IrClass.superClass: IrClass?
get() = superTypes
.firstOrNull { !it.isInterface() && !it.isAny() }
?.classOrNull
?.owner
val IrClassSymbol.functions: Sequence<IrSimpleFunctionSymbol> val IrClassSymbol.functions: Sequence<IrSimpleFunctionSymbol>
get() = owner.functions.map { it.symbol } get() = owner.functions.map { it.symbol }
@@ -823,8 +829,11 @@ fun IrFunction.copyValueParametersFrom(from: IrFunction, substitutionMap: Map<Ir
fun IrFunction.copyParameterDeclarationsFrom(from: IrFunction) { fun IrFunction.copyParameterDeclarationsFrom(from: IrFunction) {
assert(typeParameters.isEmpty()) assert(typeParameters.isEmpty())
copyTypeParametersFrom(from) copyTypeParametersFrom(from)
val substitutionMap = makeTypeParameterSubstitutionMap(from, this) copyValueParametersFrom(from)
copyValueParametersFrom(from, substitutionMap) }
fun IrFunction.copyValueParametersFrom(from: IrFunction) {
copyValueParametersFrom(from, makeTypeParameterSubstitutionMap(from, this))
} }
fun IrTypeParametersContainer.copyTypeParameters( fun IrTypeParametersContainer.copyTypeParameters(
+1
View File
@@ -45,6 +45,7 @@ where advanced options include:
-Xtyped-arrays Translate primitive arrays to JS typed arrays -Xtyped-arrays Translate primitive arrays to JS typed arrays
-Xuse-deprecated-legacy-compiler -Xuse-deprecated-legacy-compiler
Use deprecated legacy compiler without error Use deprecated legacy compiler without error
-Xes-classes Generated JavaScript will use ES2015 classes.
-Xwasm Use experimental WebAssembly compiler backend -Xwasm Use experimental WebAssembly compiler backend
-Xwasm-debug-info Add debug info to WebAssembly compiled module -Xwasm-debug-info Add debug info to WebAssembly compiled module
-Xwasm-enable-array-range-checks -Xwasm-enable-array-range-checks
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS, JS_IR, WASM // IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, WASM
var result = "" var result = ""
fun sideEffecting(): Int { fun sideEffecting(): Int {
+1 -1
View File
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS, JS_IR // IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6
// The code in this test should be prohibited in the frontend, see KT-36188. // The code in this test should be prohibited in the frontend, see KT-36188.
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS, JS_IR // IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6
// !JVM_DEFAULT_MODE: all // !JVM_DEFAULT_MODE: all
// JVM_TARGET: 1.8 // JVM_TARGET: 1.8
@@ -7,7 +7,7 @@
// ^ wasm-function[1893]:0x1cf8a: RuntimeError: dereferencing a null pointer // ^ wasm-function[1893]:0x1cf8a: RuntimeError: dereferencing a null pointer
// IGNORE_BACKEND: JS // IGNORE_BACKEND: JS
// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JS_IR, JS_IR_ES6
// ^ TypeError: tmp is not a function // ^ TypeError: tmp is not a function
// FILE: funInterfaceConstructedObjectsEquality.kt // FILE: funInterfaceConstructedObjectsEquality.kt
@@ -7,7 +7,7 @@
// ^ Failed: ks1 != ks2 (same file, same SAM type) // ^ Failed: ks1 != ks2 (same file, same SAM type)
// IGNORE_BACKEND: JS // IGNORE_BACKEND: JS
// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JS_IR, JS_IR_ES6
// ^ Failed: ks1 != ks2 (same file, same SAM type) // ^ Failed: ks1 != ks2 (same file, same SAM type)
// FILE: funInterfaceConstructorEquality.kt // FILE: funInterfaceConstructorEquality.kt
@@ -1,6 +1,6 @@
// !LANGUAGE: +DefinitelyNonNullableTypes // !LANGUAGE: +DefinitelyNonNullableTypes
// IGNORE_BACKEND: JS // IGNORE_BACKEND: JS
// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JS_IR, JS_IR_ES6
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
fun <T> test(t: T) = t as (T & Any) fun <T> test(t: T) = t as (T & Any)
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS, JS_IR, WASM // IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, WASM
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <T> f() = 1L as T fun <T> f() = 1L as T
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS, JS_IR, WASM // IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, WASM
// boxed primitive comparisons // boxed primitive comparisons
fun isBoolean(a: Any) = a::class == true::class fun isBoolean(a: Any) = a::class == true::class
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JS_IR_ES6 // IGNORE_BACKEND_K2: JS_IR, JS_IR_ES6
// IGNORE_BACKEND_K2: JS_IR
class Outer(val x: Any) { class Outer(val x: Any) {
inner class Inner( inner class Inner(
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JS_IR_ES6 // IGNORE_BACKEND_K2: JS_IR, JS_IR_ES6
// IGNORE_BACKEND_K2: JS_IR
class Outer(val x: Any) { class Outer(val x: Any) {
inner class Inner( inner class Inner(
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR_ES6
// WITH_STDLIB // WITH_STDLIB
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR_ES6
// WITH_STDLIB // WITH_STDLIB
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR_ES6
// WITH_STDLIB // WITH_STDLIB
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR_ES6
// WITH_STDLIB // WITH_STDLIB
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR_ES6
// WITH_STDLIB // WITH_STDLIB
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR_ES6
// WITH_STDLIB // WITH_STDLIB
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JS_IR, JS_IR_ES6
// WITH_COROUTINES // WITH_COROUTINES
// WITH_STDLIB // WITH_STDLIB
// MODULE: lib(support) // MODULE: lib(support)
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JS_IR, JS_IR_ES6
// WITH_COROUTINES // WITH_COROUTINES
// WITH_STDLIB // WITH_STDLIB
// MODULE: lib(support) // MODULE: lib(support)
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JS_IR, JS_IR_ES6
// KT-55464 // KT-55464
// IGNORE_BACKEND_K2: NATIVE // IGNORE_BACKEND_K2: NATIVE
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JS_IR, JS_IR_ES6
// WITH_COROUTINES // WITH_COROUTINES
// WITH_STDLIB // WITH_STDLIB
// MODULE: lib(support) // MODULE: lib(support)
@@ -1,7 +1,6 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: NESTED_OBJECT_INIT // WASM_MUTE_REASON: NESTED_OBJECT_INIT
// IGNORE_BACKEND: NATIVE // IGNORE_BACKEND: NATIVE
// IGNORE_BACKEND: JS_IR_ES6
// WITH_STDLIB // WITH_STDLIB
// WITH_COROUTINES // WITH_COROUTINES
import helpers.* import helpers.*
@@ -1,6 +1,6 @@
// WITH_STDLIB // WITH_STDLIB
// FULL_JDK // FULL_JDK
// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JS_IR, JS_IR_ES6
import kotlin.coroutines.* import kotlin.coroutines.*
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JVM, JS, JS_IR // IGNORE_BACKEND: JVM, JS, JS_IR, JS_IR_ES6
// KT-30080 // KT-30080
data class A( data class A(
@@ -1,7 +1,7 @@
// WITH_STDLIB // WITH_STDLIB
// CHECK_BYTECODE_LISTING // CHECK_BYTECODE_LISTING
// FIR_IDENTICAL // FIR_IDENTICAL
// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JS_IR, JS_IR_ES6
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
// IGNORE_FIR_DIAGNOSTICS_DIFF // IGNORE_FIR_DIAGNOSTICS_DIFF
open class A { open class A {
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
<!NO_TAIL_CALLS_FOUND!>tailrec<!> fun noTails() { <!NO_TAIL_CALLS_FOUND!>tailrec<!> fun noTails() {
// nothing here // nothing here
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
// TODO: muted automatically, investigate should it be ran for JS or not // TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS // IGNORE_BACKEND: JS
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
// TODO: muted automatically, investigate should it be ran for JS or not // TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS // IGNORE_BACKEND: JS
// IGNORE_FIR_DIAGNOSTICS_DIFF // IGNORE_FIR_DIAGNOSTICS_DIFF
@@ -1,7 +1,6 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
// IGNORE_BACKEND: JS_IR_ES6
// TODO: muted automatically, investigate should it be ran for JS or not // TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS // IGNORE_BACKEND: JS
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
tailrec fun test(x : Int) : Int { tailrec fun test(x : Int) : Int {
var z = if (x > 3) 3 else x var z = if (x > 3) 3 else x
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
// TODO: muted automatically, investigate should it be ran for JS or not // TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS // IGNORE_BACKEND: JS
@@ -1,7 +1,6 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// !DIAGNOSTICS: -UNUSED_PARAMETER // !DIAGNOSTICS: -UNUSED_PARAMETER
// IGNORE_BACKEND: JS_IR_ES6
<!NO_TAIL_CALLS_FOUND!>tailrec<!> fun foo() { <!NO_TAIL_CALLS_FOUND!>tailrec<!> fun foo() {
bar { bar {
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
<!NO_TAIL_CALLS_FOUND!>tailrec<!> fun foo() { <!NO_TAIL_CALLS_FOUND!>tailrec<!> fun foo() {
fun bar() { fun bar() {
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
// TODO: muted automatically, investigate should it be ran for JS or not // TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS // IGNORE_BACKEND: JS
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
<!NO_TAIL_CALLS_FOUND!>tailrec<!> fun test(counter : Int) : Int { <!NO_TAIL_CALLS_FOUND!>tailrec<!> fun test(counter : Int) : Int {
if (counter == 0) return 0 if (counter == 0) return 0
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
<!NO_TAIL_CALLS_FOUND!>tailrec<!> fun test(counter : Int) : Int { <!NO_TAIL_CALLS_FOUND!>tailrec<!> fun test(counter : Int) : Int {
if (counter == 0) return 0 if (counter == 0) return 0
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
<!NO_TAIL_CALLS_FOUND!>tailrec<!> fun test(counter : Int) : Int { <!NO_TAIL_CALLS_FOUND!>tailrec<!> fun test(counter : Int) : Int {
if (counter == 0) return 0 if (counter == 0) return 0
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
<!NO_TAIL_CALLS_FOUND!>tailrec<!> fun test(counter : Int) : Int { <!NO_TAIL_CALLS_FOUND!>tailrec<!> fun test(counter : Int) : Int {
if (counter == 0) return 0 if (counter == 0) return 0
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
// TODO: muted automatically, investigate should it be ran for JS or not // TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS // IGNORE_BACKEND: JS
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
// TODO: muted automatically, investigate should it be ran for JS or not // TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS // IGNORE_BACKEND: JS
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
// TODO: muted automatically, investigate should it be ran for JS or not // TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS // IGNORE_BACKEND: JS
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
<!NO_TAIL_CALLS_FOUND!>tailrec<!> fun test(go: Boolean) : Unit { <!NO_TAIL_CALLS_FOUND!>tailrec<!> fun test(go: Boolean) : Unit {
if (!go) return if (!go) return
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
// DONT_RUN_GENERATED_CODE: JS // DONT_RUN_GENERATED_CODE: JS
// IGNORE_BACKEND: JVM // IGNORE_BACKEND: JVM
// IGNORE_FIR_DIAGNOSTICS_DIFF // IGNORE_FIR_DIAGNOSTICS_DIFF
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR_ES6
// DONT_RUN_GENERATED_CODE: JS // DONT_RUN_GENERATED_CODE: JS
object O { object O {
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
// TODO: muted automatically, investigate should it be ran for JS or not // TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS // IGNORE_BACKEND: JS
@@ -1,6 +1,5 @@
// IGNORE_BACKEND: WASM // IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS // WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR_ES6
// TODO: muted automatically, investigate should it be ran for JS or not // TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS // IGNORE_BACKEND: JS

Some files were not shown because too many files have changed in this diff Show More