[K/JS] Use only single variant of default arguments function wrapper for exported and not-exported functions

This commit is contained in:
Artem Kobzar
2022-09-19 17:49:04 +00:00
committed by Space
parent ea7ce55082
commit 73e7053c35
59 changed files with 1109 additions and 597 deletions
@@ -16,6 +16,7 @@ object JsLoweredDeclarationOrigin : IrDeclarationOrigin {
object BRIDGE_PROPERTY_ACCESSOR : IrDeclarationOriginImpl("BRIDGE_PROPERTY_ACCESSOR")
object OBJECT_GET_INSTANCE_FUNCTION : IrDeclarationOriginImpl("OBJECT_GET_INSTANCE_FUNCTION")
object JS_SHADOWED_EXPORT : IrDeclarationOriginImpl("JS_SHADOWED_EXPORT")
object JS_SUPER_CONTEXT_PARAMETER : IrDeclarationOriginImpl("JS_SUPER_CONTEXT_PARAMETER")
object JS_SHADOWED_DEFAULT_PARAMETER : IrDeclarationOriginImpl("JS_SHADOWED_DEFAULT_PARAMETER")
object ENUM_GET_INSTANCE_FUNCTION : IrDeclarationOriginImpl("ENUM_GET_INSTANCE_FUNCTION")
}
@@ -29,6 +29,7 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
// TODO: Should we drop operator intrinsics in favor of IrDynamicOperatorExpression?
// Global variables
val void = getInternalProperty("VOID")
val globalThis = getInternalProperty("globalThis")
// Equality operations:
@@ -140,7 +141,6 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
// Other:
val jsObjectCreate = getInternalFunction("objectCreate") // Object.create
val jsCode = getInternalFunction("js") // js("<code>")
val jsHashCode = getInternalFunction("hashCode")
val jsGetNumberHashCode = getInternalFunction("getNumberHashCode")
@@ -313,11 +313,13 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
val jsArraySlice = getInternalFunction("slice")
val jsCall = getInternalFunction("jsCall")
val jsBind = getInternalFunction("jsBind")
// TODO move to IntrinsifyCallsLowering
val doNotIntrinsifyAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("DoNotIntrinsify"))
val jsFunAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("JsFun"))
val jsNameAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("JsName"))
val jsImplicitExportAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("JsImplicitExport"))
@@ -341,6 +343,7 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
val jsCharSequenceLength = getInternalFunction("charSequenceLength")
val jsCharSequenceSubSequence = getInternalFunction("charSequenceSubSequence")
val jsContexfulRef = getInternalFunction("jsContextfulRef")
val jsBoxIntrinsic = getInternalFunction("boxIntrinsic")
val jsUnboxIntrinsic = getInternalFunction("unboxIntrinsic")
@@ -350,10 +353,12 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
val readSharedBox = getInternalFunction("sharedBoxRead")
val writeSharedBox = getInternalFunction("sharedBoxWrite")
val jsUndefined = getInternalFunction("jsUndefined")
val linkageErrorSymbol = getInternalFunction("throwLinkageError")
val jsPrototypeOfSymbol = getInternalFunction("protoOf")
val jsDefinePropertySymbol = getInternalFunction("defineProp")
val jsObjectCreateSymbol = getInternalFunction("objectCreate") // Object.create
// Helpers:
private fun getInternalFunction(name: String) =
@@ -555,7 +555,7 @@ private val defaultArgumentPatchOverridesPhase = makeDeclarationTransformerPhase
)
private val defaultParameterInjectorPhase = makeBodyLoweringPhase(
{ context -> DefaultParameterInjector(context, skipExternalMethods = true, forceSetOverrideSymbols = false) },
::JsDefaultParameterInjector,
name = "DefaultParameterInjector",
description = "Replace callsite with default parameters with corresponding stub function",
prerequisite = setOf(interopCallableReferenceLoweringPhase, innerClassesLoweringPhase)
@@ -567,19 +567,6 @@ private val defaultParameterCleanerPhase = makeDeclarationTransformerPhase(
description = "Clean default parameters up"
)
private val exportedDefaultParameterStubPhase = makeDeclarationTransformerPhase(
::ExportedDefaultParameterStub,
name = "ExportedDefaultParameterStub",
description = "Generates default stub for exported entity and renames the non-default counterpart"
)
private val jsDefaultCallbackGeneratorPhase = makeBodyLoweringPhase(
::JsDefaultCallbackGenerator,
name = "JsDefaultCallbackGenerator",
description = "Build binding for super calls with default parameters"
)
private val varargLoweringPhase = makeBodyLoweringPhase(
::VarargLowering,
name = "VarargLowering",
@@ -811,6 +798,7 @@ private val cleanupLoweringPhase = makeBodyLoweringPhase(
name = "CleanupLowering",
description = "Clean up IR before codegen"
)
private val moveOpenClassesToSeparatePlaceLowering = makeCustomJsModulePhase(
{ context, module ->
if (context.granularity == JsGenerationGranularity.PER_FILE)
@@ -899,12 +887,10 @@ val loweringList = listOf<Lowering>(
computeStringTrimPhase,
privateMembersLoweringPhase,
privateMemberUsagesLoweringPhase,
exportedDefaultParameterStubPhase,
defaultArgumentStubGeneratorPhase,
defaultArgumentPatchOverridesPhase,
defaultParameterInjectorPhase,
defaultParameterCleanerPhase,
jsDefaultCallbackGeneratorPhase,
throwableSuccessorsLoweringPhase,
es6AddInternalParametersToConstructorPhase,
es6ConstructorLowering,
@@ -239,7 +239,7 @@ class IrToJs(
val globalNames = NameTable<String>(nameGenerator.staticNames)
val exporter = ExportModelToJsStatements(
nameGenerator,
staticContext,
declareNewNamespace = { globalNames.declareFreshName(it, it) }
)
exportedDeclarations.forEach {
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.RuntimeDiagnostic
import org.jetbrains.kotlin.utils.addIfNotNull
fun eliminateDeadDeclarations(
modules: Iterable<IrModuleFragment>,
@@ -116,6 +117,7 @@ private fun buildRoots(modules: Iterable<IrModuleFragment>, context: JsIrBackend
}
}
addIfNotNull(context.intrinsics.void.owner.backingField)
addAll(context.testFunsPerFile.values)
addAll(context.additionalExportedDeclarations)
}
@@ -67,9 +67,9 @@ internal class JsUsefulDeclarationProcessor(
val ref = expression.getTypeArgument(0)?.classOrNull ?: context.irBuiltIns.anyClass
referencedJsClassesFromExpressions += ref.owner
}
context.intrinsics.jsObjectCreate -> {
context.intrinsics.jsObjectCreateSymbol -> {
val classToCreate = expression.getTypeArgument(0)!!.classifierOrFail.owner as IrClass
classToCreate.enqueue(data, "intrinsic: jsObjectCreate")
classToCreate.enqueue(data, "intrinsic: jsObjectCreateSymbol")
constructedClasses += classToCreate
}
context.intrinsics.jsEquals -> {
@@ -129,11 +129,29 @@ internal class JsUsefulDeclarationProcessor(
if (irClass.containsMetadata()) {
when {
irClass.isObject -> context.intrinsics.metadataObjectConstructorSymbol.owner.enqueue(irClass, "object metadata")
irClass.isInterface -> {
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")
else -> {
context.intrinsics.metadataClassConstructorSymbol.owner.enqueue(irClass, "class metadata")
}
}
}
if (!irClass.isExpect && !irClass.isExternal && !irClass.defaultType.isAny()) {
if (!irClass.isInterface) {
context.intrinsics.jsPrototypeOfSymbol.owner.enqueue(irClass, "class metadata")
}
if (irClass.superTypes.any { !it.isInterface() }) {
context.intrinsics.jsObjectCreateSymbol.owner.enqueue(irClass, "class metadata")
}
if (irClass.isInner || irClass.isObject) {
context.intrinsics.jsDefinePropertySymbol.owner.enqueue(irClass, "class metadata")
}
context.intrinsics.setMetadataForSymbol.owner.enqueue(irClass, "metadata")
@@ -146,6 +164,12 @@ internal class JsUsefulDeclarationProcessor(
if (irFunction.isReal && irFunction.body != null) {
irFunction.parentClassOrNull?.takeIf { it.isInterface }?.enqueue(irFunction, "interface default method is used")
}
val property = irFunction.correspondingPropertySymbol?.owner ?: return
if (property.isExported(context) || property.isOverriddenExternal()) {
context.intrinsics.jsDefinePropertySymbol.owner.enqueue(irFunction, "property for export")
}
}
private fun IrClass.containsMetadata(): Boolean =
@@ -218,14 +218,17 @@ abstract class UsefulDeclarationProcessor(
}
}
private fun IrSimpleFunction.isAccessorForOverriddenExternalField(): Boolean {
protected fun IrSimpleFunction.isAccessorForOverriddenExternalField(): Boolean {
return correspondingPropertySymbol?.owner?.isExternalOrOverriddenExternal() ?: false
}
private fun IrProperty.isExternalOrOverriddenExternal(): Boolean {
return isEffectivelyExternal() || overriddenSymbols.any { it.owner.isExternalOrOverriddenExternal() }
protected fun IrProperty.isExternalOrOverriddenExternal(): Boolean {
return isEffectivelyExternal() || isOverriddenExternal()
}
protected fun IrProperty.isOverriddenExternal(): Boolean =
overriddenSymbols.any { it.owner.isExternalOrOverriddenExternal() }
protected open fun handleAssociatedObjects(): Unit = Unit
fun collectDeclarations(rootDeclarations: Iterable<IrDeclaration>): Set<IrDeclaration> {
@@ -86,13 +86,15 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac
ExportedFunction(
function.getExportedIdentifier(),
returnType = exportType(function.returnType),
parameters = (listOfNotNull(function.extensionReceiverParameter) + function.valueParameters).map { exportParameter(it) },
typeParameters = function.typeParameters.map(::exportTypeParameter),
isMember = parent is IrClass,
isStatic = function.isStaticMethodOfClass,
isAbstract = parent is IrClass && !parent.isInterface && function.modality == Modality.ABSTRACT,
isProtected = function.visibility == DescriptorVisibilities.PROTECTED,
ir = function
ir = function,
parameters = (listOfNotNull(function.extensionReceiverParameter) + function.valueParameters)
.filter { it.shouldBeExported() }
.map { exportParameter(it) },
)
}
}
@@ -362,6 +364,10 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac
return isInterface && !isExternal || isJsImplicitExport()
}
private fun IrValueParameter.shouldBeExported(): Boolean {
return origin != JsLoweredDeclarationOrigin.JS_SUPER_CONTEXT_PARAMETER
}
private fun IrClass.shouldContainImplementationOfMagicProperty(superTypes: Iterable<IrType>): Boolean {
return !isExternal && superTypes.any {
val superClass = it.classOrNull?.owner ?: return@any false
@@ -640,7 +646,6 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac
if (function.origin == JsLoweredDeclarationOrigin.BRIDGE_WITHOUT_STABLE_NAME ||
function.origin == JsLoweredDeclarationOrigin.BRIDGE_PROPERTY_ACCESSOR ||
function.origin == JsLoweredDeclarationOrigin.BRIDGE_WITH_STABLE_NAME ||
function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER ||
function.origin == JsLoweredDeclarationOrigin.OBJECT_GET_INSTANCE_FUNCTION ||
function.origin == JsLoweredDeclarationOrigin.JS_SHADOWED_EXPORT ||
function.origin == JsLoweredDeclarationOrigin.ENUM_GET_INSTANCE_FUNCTION
@@ -5,21 +5,14 @@
package org.jetbrains.kotlin.ir.backend.js.export
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.jsAssignment
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.prototypeOf
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsElementAccess
import org.jetbrains.kotlin.ir.backend.js.utils.IrNamer
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.getJsNameOrKotlinName
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.*
import org.jetbrains.kotlin.ir.backend.js.utils.*
import org.jetbrains.kotlin.ir.util.companionObject
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd
class ExportModelToJsStatements(
private val namer: IrNamer,
private val namer: JsStaticContext,
private val declareNewNamespace: (String) -> String
) {
private val namespaceToRefMap = mutableMapOf<String, JsNameRef>()
@@ -88,7 +81,7 @@ class ExportModelToJsStatements(
require(namespace != null) { "Only namespaced properties are allowed" }
val getter = declaration.irGetter?.let { JsNameRef(namer.getNameForStaticDeclaration(it)) }
val setter = declaration.irSetter?.let { JsNameRef(namer.getNameForStaticDeclaration(it)) }
listOf(defineProperty(namespace, declaration.name, getter, setter).makeStmt())
listOf(defineProperty(namespace, declaration.name, getter, setter, namer).makeStmt())
}
is ErrorDeclaration -> emptyList()
@@ -98,7 +91,7 @@ class ExportModelToJsStatements(
val newNameSpace = jsElementAccess(declaration.name, namespace)
val getter = JsNameRef(namer.getNameForStaticDeclaration(declaration.irGetter))
val staticsExport = declaration.nestedClasses.flatMap { generateDeclarationExport(it, newNameSpace, esModules) }
listOf(defineProperty(namespace, declaration.name, getter, null).makeStmt()) + staticsExport
listOf(defineProperty(namespace, declaration.name, getter, null, namer).makeStmt()) + staticsExport
}
is ExportedRegularClass -> {
@@ -106,7 +99,7 @@ class ExportModelToJsStatements(
val newNameSpace = if (namespace != null)
jsElementAccess(declaration.name, namespace)
else
JsNameRef(Namer.PROTOTYPE_NAME, namer.getNameForClass(declaration.ir).makeRef())
prototypeOf(namer.getNameForClass(declaration.ir).makeRef(), namer)
val name = namer.getNameForStaticDeclaration(declaration.ir)
val klassExport =
if (esModules) {
@@ -173,14 +166,15 @@ class ExportModelToJsStatements(
blockStatements.add(JsReturn(bindConstructor.makeRef()))
return defineProperty(
prototypeOf(outerClassRef),
prototypeOf(outerClassRef, namer),
name,
JsFunction(
emptyScope,
JsBlock(*blockStatements.toTypedArray()),
"inner class '$name' getter"
),
null
null,
namer
).makeStmt()
}
@@ -64,6 +64,9 @@ object JsIrBuilder {
fun buildGetValue(symbol: IrValueSymbol) =
IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol.owner.type, symbol, JsStatementOrigins.SYNTHESIZED_STATEMENT)
fun buildSetValue(symbol: IrValueSymbol, value: IrExpression) =
IrSetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol.owner.type, symbol, value, JsStatementOrigins.SYNTHESIZED_STATEMENT)
fun buildSetVariable(symbol: IrVariableSymbol, value: IrExpression, type: IrType) =
IrSetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, symbol, value, JsStatementOrigins.SYNTHESIZED_STATEMENT)
@@ -227,10 +227,14 @@ class AutoboxingTransformer(context: JsCommonBackendContext) : AbstractValueUsag
}
override fun visitCall(expression: IrCall): IrExpression {
if (expression.symbol == irBuiltIns.eqeqeqSymbol && expression.allArgumentsHaveType(irBuiltIns.charType)) {
return expression.apply { transformChildrenVoid() }
return if (
expression.symbol != irBuiltIns.eqeqeqSymbol ||
!expression.allArgumentsHaveType(irBuiltIns.charType) &&
expression.origin != IrStatementOrigin.SYNTHETIC_NOT_AUTOBOXED_CHECK
) {
super.visitCall(expression)
} else {
return super.visitCall(expression)
expression.apply { transformChildrenVoid() }
}
}
@@ -1,154 +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.DeclarationTransformer
import org.jetbrains.kotlin.backend.common.lower.VariableRemapper
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.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.hasStableJsName
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
private fun IrConstructorCall.isAnnotation(name: FqName): Boolean {
return symbol.owner.parentAsClass.fqNameWhenAvailable == name
}
class ExportedDefaultParameterStub(val context: JsIrBackendContext) : DeclarationTransformer {
private fun IrBuilderWithScope.createDefaultResolutionExpression(
fromParameter: IrValueParameter,
toParameter: IrValueParameter,
): IrExpression? {
return fromParameter.defaultValue?.let { defaultValue ->
irIfThenElse(
toParameter.type,
irEqeqeq(
irGet(toParameter, context.irBuiltIns.anyNType),
irCall(this@ExportedDefaultParameterStub.context.intrinsics.jsUndefined)
),
defaultValue.expression,
irGet(toParameter)
)
}
}
private fun IrConstructor.introduceDefaultResolution(): IrConstructor {
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
val variables = mutableMapOf<IrValueParameter, IrValueDeclaration>()
val defaultResolutionStatements = valueParameters.mapNotNull { valueParameter ->
irBuilder.createDefaultResolutionExpression(valueParameter, valueParameter)?.let { initializer ->
JsIrBuilder.buildVar(
valueParameter.type,
this@introduceDefaultResolution,
name = valueParameter.name.asString(),
initializer = initializer
).also {
variables[valueParameter] = it
}
}
}
if (variables.isNotEmpty()) {
body?.transformChildren(VariableRemapper(variables), null)
body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += defaultResolutionStatements
statements += body?.statements ?: emptyList()
}
}
return also {
valueParameters.forEach {
if (it.defaultValue != null) {
it.origin = JsLoweredDeclarationOrigin.JS_SHADOWED_DEFAULT_PARAMETER
}
}
}
}
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration !is IrFunction) {
return null
}
if (!declaration.hasStableJsName(context)) {
return null
}
if (!declaration.valueParameters.any { it.defaultValue != null }) {
return null
}
if (declaration is IrConstructor) {
return listOf(declaration.introduceDefaultResolution())
}
val exportedDefaultStubFun = context.irFactory.buildFun {
updateFrom(declaration)
name = declaration.name
origin = JsIrBuilder.SYNTHESIZED_DECLARATION
}
if (declaration.isExported(context)) {
context.additionalExportedDeclarations.add(exportedDefaultStubFun)
}
exportedDefaultStubFun.parent = declaration.parent
exportedDefaultStubFun.copyParameterDeclarationsFrom(declaration)
exportedDefaultStubFun.returnType = declaration.returnType.remapTypeParameters(declaration, exportedDefaultStubFun)
exportedDefaultStubFun.valueParameters.forEach {
if (it.defaultValue != null) {
it.origin = JsLoweredDeclarationOrigin.JS_SHADOWED_DEFAULT_PARAMETER
}
it.defaultValue = null
}
declaration.origin = JsLoweredDeclarationOrigin.JS_SHADOWED_EXPORT
val irBuilder = context.createIrBuilder(exportedDefaultStubFun.symbol, exportedDefaultStubFun.startOffset, exportedDefaultStubFun.endOffset)
exportedDefaultStubFun.body = irBuilder.irBlockBody(exportedDefaultStubFun) {
+irReturn(irCall(declaration).apply {
passTypeArgumentsFrom(declaration)
dispatchReceiver = exportedDefaultStubFun.dispatchReceiverParameter?.let { irGet(it) }
extensionReceiver = exportedDefaultStubFun.extensionReceiverParameter?.let { irGet(it) }
declaration.valueParameters.forEachIndexed { index, irValueParameter ->
val exportedParameter = exportedDefaultStubFun.valueParameters[index]
val value = createDefaultResolutionExpression(irValueParameter, exportedParameter) ?: irGet(exportedParameter)
putValueArgument(index, value)
}
})
}
val (exportAnnotations, irrelevantAnnotations) = declaration.annotations.map { it.deepCopyWithSymbols(declaration as? IrDeclarationParent) }
.partition {
it.isAnnotation(JsAnnotations.jsExportFqn) || (it.isAnnotation(JsAnnotations.jsNameFqn))
}
declaration.annotations = irrelevantAnnotations
exportedDefaultStubFun.annotations = exportAnnotations
return listOf(exportedDefaultStubFun, declaration)
}
}
@@ -0,0 +1,38 @@
/*
* 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.lower.DefaultArgumentFunctionFactory
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.util.copyTypeParametersFrom
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isTopLevel
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.name.Name
class JsDefaultArgumentFunctionFactory(override val context: JsIrBackendContext) : DefaultArgumentFunctionFactory(context) {
override fun IrFunction.generateDefaultArgumentStubFrom(original: IrFunction, useConstructorMarker: Boolean) {
copyAttributesFrom(original)
copyTypeParametersFrom(original)
copyReturnTypeFrom(original)
copyReceiversFrom(original)
copyValueParametersFrom(original, wrapWithNullable = false)
if (!original.isTopLevel) {
introduceContextParam()
}
}
private fun IrFunction.introduceContextParam() = addValueParameter {
name = Name.identifier("\$super")
type = parentAsClass.defaultType
origin = JsLoweredDeclarationOrigin.JS_SUPER_CONTEXT_PARAMETER
}
}
@@ -1,117 +1,247 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.ir.deepCopyWithVariables
import org.jetbrains.kotlin.backend.common.lower.DefaultArgumentStubGenerator
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
import org.jetbrains.kotlin.backend.common.lower.LoweredStatementOrigins
import org.jetbrains.kotlin.backend.common.lower.*
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.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
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.builders.IrBlockBodyBuilder
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irImplicitCast
import org.jetbrains.kotlin.ir.backend.js.utils.getVoid
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
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.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.runIf
class JsDefaultArgumentStubGenerator(override val context: JsIrBackendContext) : DefaultArgumentStubGenerator(context, true, true, false) {
class JsDefaultArgumentStubGenerator(override val context: JsIrBackendContext) :
DefaultArgumentStubGenerator(
context,
skipExternalMethods = true,
forceSetOverrideSymbols = false,
factory = JsDefaultArgumentFunctionFactory(context)
) {
override fun needSpecialDispatch(irFunction: IrSimpleFunction) = irFunction.isOverridableOrOverrides
override fun IrFunction.resolveAnnotations(): List<IrConstructorCall> = copyAnnotationsWhen {
!(isAnnotation(JsAnnotations.jsExportFqn) || isAnnotation(JsAnnotations.jsNameFqn))
}
override fun IrBlockBodyBuilder.generateHandleCall(
handlerDeclaration: IrValueParameter,
oldIrFunction: IrFunction,
newIrFunction: IrFunction,
params: MutableList<IrValueDeclaration>
): IrExpression {
val paramCount = oldIrFunction.valueParameters.size
val invokeFunctionN = resolveInvoke(paramCount)
return irCall(invokeFunctionN, IrStatementOrigin.INVOKE).apply {
dispatchReceiver = irImplicitCast(irGet(handlerDeclaration), invokeFunctionN.dispatchReceiverParameter!!.type)
// NOTE: currently we do not have a syntax to perform super extension call
// that's why we've used to just fail with an exception in case we have extension function in for JS IR compilation
// TODO: that was overkill, however, we still need to revisit this issue later on
params.forEachIndexed { i, variable -> putValueArgument(i, irGet(variable)) }
private fun IrBuilderWithScope.createDefaultResolutionExpression(
defaultExpression: IrExpression?,
toParameter: IrValueParameter,
): IrExpression? {
return defaultExpression?.let {
irIfThenElse(
toParameter.type,
irEqeqeqWithoutBox(
irGet(toParameter, toParameter.type),
this@JsDefaultArgumentStubGenerator.context.getVoid()
),
it,
irGet(toParameter)
)
}
}
override fun IrExpression.prepareToBeUsedIn(function: IrFunction): IrExpression {
return deepCopyWithVariables().also {
it.patchDeclarationParents(function)
private fun IrBuilderWithScope.createResolutionStatement(
parameter: IrValueParameter,
defaultExpression: IrExpression?,
): IrSetValue? {
return createDefaultResolutionExpression(defaultExpression, parameter)?.let {
JsIrBuilder.buildSetValue(parameter.symbol, it)
}
}
private fun resolveInvoke(paramCount: Int): IrSimpleFunction {
assert(paramCount > 0)
val functionKlass = context.ir.symbols.functionN(paramCount).owner
return functionKlass.declarations.filterIsInstance<IrSimpleFunction>().first { it.name == Name.identifier("invoke") }
}
}
private fun IrFunction.introduceDefaultResolution(): IrFunction {
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
class JsDefaultCallbackGenerator(val context: JsIrBackendContext): BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression {
super.visitCall(expression)
if (expression.origin != LoweredStatementOrigins.DEFAULT_DISPATCH_CALL || expression.superQualifierSymbol == null) return expression
val variables = mutableMapOf<IrValueParameter, IrValueParameter>()
val binding = buildBoundSuperCall(expression)
valueParameters = valueParameters.map { param ->
param.takeIf { it.defaultValue != null }
?.copyTo(this, isAssignable = true, origin = JsLoweredDeclarationOrigin.JS_SHADOWED_DEFAULT_PARAMETER)
?.also { new -> variables[param] = new } ?: param
}
expression.putValueArgument(expression.valueArgumentsCount - 1, binding)
val defaultResolutionStatements = valueParameters.mapNotNull {
irBuilder.createResolutionStatement(it, it.defaultValue?.expression)
}
return expression
if (variables.isNotEmpty()) {
body?.transformChildren(VariableRemapper(variables), null)
body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += defaultResolutionStatements
statements += body?.statements ?: emptyList()
}
})
}
private fun buildBoundSuperCall(irCall: IrCall): IrExpression {
val originalFunction = context.mapping.defaultArgumentsOriginalFunction[irCall.symbol.owner]!!
val reference = irCall.run {
IrFunctionReferenceImpl(
startOffset,
endOffset,
context.irBuiltIns.anyType,
originalFunction.symbol,
typeArgumentsCount = 0,
valueArgumentsCount = originalFunction.valueParameters.size,
reflectionTarget = originalFunction.symbol,
origin = JsStatementOrigins.BIND_CALL
)
}
return irCall.run {
IrCallImpl(
startOffset,
endOffset,
context.irBuiltIns.anyType,
context.intrinsics.jsBind,
valueArgumentsCount = 2,
typeArgumentsCount = 0,
origin = JsStatementOrigins.BIND_CALL,
superQualifierSymbol = superQualifierSymbol
)
}.apply {
putValueArgument(0, irCall.dispatchReceiver?.deepCopyWithSymbols())
putValueArgument(1, reference)
return also {
context.mapping.defaultArgumentsDispatchFunction[it] = it
}
}
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration !is IrFunction || declaration.isExternalOrInheritedFromExternal()) {
return null
}
if (declaration.hasDefaultArgs() && (declaration is IrConstructor || declaration.isTopLevel)) {
return listOf(declaration.introduceDefaultResolution())
}
val (originalFun, defaultFunStub) = super.transformFlat(declaration) ?: return null
if (originalFun !is IrFunction || defaultFunStub !is IrFunction) {
return listOf(originalFun, defaultFunStub)
}
if (!defaultFunStub.isFakeOverride) {
with(defaultFunStub) {
valueParameters.forEach {
if (it.defaultValue != null) {
it.origin = JsLoweredDeclarationOrigin.JS_SHADOWED_DEFAULT_PARAMETER
}
it.defaultValue = null
}
if (originalFun.isExported(context)) {
context.additionalExportedDeclarations.add(defaultFunStub)
if (!originalFun.hasAnnotation(JsAnnotations.jsNameFqn)) {
annotations += originalFun.generateJsNameAnnotationCall()
}
}
}
}
val (exportAnnotations, irrelevantAnnotations) = originalFun.annotations
.map { it.deepCopyWithSymbols(originalFun as? IrDeclarationParent) }
.partition {
it.isAnnotation(JsAnnotations.jsExportFqn) || (it.isAnnotation(JsAnnotations.jsNameFqn))
}
originalFun.annotations = irrelevantAnnotations
defaultFunStub.annotations += exportAnnotations
originalFun.origin = JsLoweredDeclarationOrigin.JS_SHADOWED_EXPORT
return listOf(originalFun, defaultFunStub)
}
override fun IrFunction.generateDefaultStubBody(originalDeclaration: IrFunction): IrBody {
val ctx = context
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
val variables = mutableMapOf<IrValueParameter, IrValueDeclaration>().apply {
originalDeclaration.dispatchReceiverParameter?.let {
set(it, dispatchReceiverParameter!!)
}
originalDeclaration.extensionReceiverParameter?.let {
set(it, extensionReceiverParameter!!)
}
originalDeclaration.valueParameters.forEachIndexed { index, param ->
set(param, valueParameters[index])
}
}
return irBuilder.irBlockBody(this) {
+valueParameters.zip(originalDeclaration.valueParameters)
.mapNotNull { (new, original) ->
createResolutionStatement(
new,
original.defaultValue?.expression?.transform(VariableRemapper(variables), null),
)
}
val wrappedFunctionCall = irCall(originalDeclaration, JsStatementOrigins.IMPLEMENTATION_DELEGATION_CALL).apply {
passTypeArgumentsFrom(originalDeclaration)
dispatchReceiver = dispatchReceiverParameter?.let { irGet(it) }
extensionReceiver = extensionReceiverParameter?.let { irGet(it) }
originalDeclaration.valueParameters.forEachIndexed { index, irValueParameter ->
putValueArgument(index, irGet(variables[irValueParameter] ?: valueParameters[index]))
}
}
var superContextValueParam: IrValueParameter? = null
val superFunCall = runIf(wrappedFunctionCall.dispatchReceiver != null && !originalDeclaration.isExported(ctx)) {
val superContext = valueParameters.last().also {
superContextValueParam = it
}
val realOverrideTarget = originalDeclaration.realOverrideTarget.takeIf {
it !is IrOverridableMember || it.modality !== Modality.ABSTRACT
}
if (realOverrideTarget?.parentClassOrNull?.isInterface == true) {
irCall(realOverrideTarget).apply {
extensionReceiver = wrappedFunctionCall.extensionReceiver?.deepCopyWithSymbols()
(0 until wrappedFunctionCall.valueArgumentsCount).forEach {
putValueArgument(it, wrappedFunctionCall.getValueArgument(it)?.deepCopyWithSymbols())
}
}
} else {
irCall(ctx.intrinsics.jsCall).apply {
putValueArgument(0, wrappedFunctionCall.dispatchReceiver!!.deepCopyWithSymbols())
putValueArgument(
1,
irCall(ctx.intrinsics.jsContexfulRef).apply {
putValueArgument(0, irGet(superContext))
putValueArgument(1, irRawFunctionReference(ctx.dynamicType, originalDeclaration.symbol))
}
)
putValueArgument(2, irVararg(ctx.dynamicType, buildList {
addIfNotNull(wrappedFunctionCall.extensionReceiver?.deepCopyWithSymbols())
(0 until wrappedFunctionCall.valueArgumentsCount).forEach {
addIfNotNull(wrappedFunctionCall.getValueArgument(it)?.deepCopyWithSymbols())
}
}))
}
}
}
+irReturn(
if (superFunCall == null) {
wrappedFunctionCall
} else {
irIfThenElse(
originalDeclaration.returnType,
irEqeqeqWithoutBox(irGet(superContextValueParam!!), ctx.getVoid()),
wrappedFunctionCall,
superFunCall
)
}
)
}
}
private fun IrFunction.generateJsNameAnnotationCall(): IrConstructorCall {
val builder = context.createIrBuilder(symbol, startOffset, endOffset)
return with(context) {
builder.irCall(intrinsics.jsNameAnnotationSymbol.constructors.single())
.apply {
putValueArgument(
0,
IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, irBuiltIns.stringType, name.identifier)
)
}
}
}
private fun IrConstructorCall.isAnnotation(name: FqName): Boolean {
return symbol.owner.parentAsClass.fqNameWhenAvailable == name
}
private fun IrFunction.hasDefaultArgs(): Boolean =
valueParameters.any { it.defaultValue != null }
}
@@ -0,0 +1,113 @@
/*
* 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.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.backend.common.lower.DefaultParameterInjector
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.backend.js.utils.getVoid
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.util.copyAnnotations
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isTopLevel
import org.jetbrains.kotlin.ir.util.isVararg
class JsDefaultParameterInjector(override val context: JsIrBackendContext) :
DefaultParameterInjector(
context,
skipExternalMethods = true,
forceSetOverrideSymbols = false,
factory = JsDefaultArgumentFunctionFactory(context)
) {
override fun nullConst(startOffset: Int, endOffset: Int, irParameter: IrValueParameter): IrExpression? =
if (irParameter.isVararg && !irParameter.hasDefaultValue()) {
null
} else {
context.getVoid()
}
override fun shouldReplaceWithSyntheticFunction(functionAccess: IrFunctionAccessExpression): Boolean {
return super.shouldReplaceWithSyntheticFunction(functionAccess) || functionAccess.symbol.owner.run {
origin == JsLoweredDeclarationOrigin.JS_SHADOWED_EXPORT &&
!isTopLevel &&
functionAccess.origin != JsStatementOrigins.IMPLEMENTATION_DELEGATION_CALL &&
isExported(context)
}
}
override fun parametersForCall(expression: IrFunctionAccessExpression): Pair<IrFunctionSymbol, List<IrExpression?>>? {
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val declaration = expression.symbol.owner
val stubFunction = factory.findBaseFunctionWithDefaultArgumentsFor(declaration, skipInline, skipExternalMethods)?.let {
factory.generateDefaultsFunction(
it,
skipInline,
skipExternalMethods,
forceSetOverrideSymbols,
defaultArgumentStubVisibility(declaration),
useConstructorMarker(declaration),
it.copyAnnotations(),
)
} ?: return null
return stubFunction.symbol to buildList {
for (i in 0 until expression.valueArgumentsCount) {
val declaredParameter = stubFunction.valueParameters[i]
val actualParameter = expression.getValueArgument(i)
add(actualParameter ?: nullConst(startOffset, endOffset, declaredParameter))
}
if (expression is IrCall && stubFunction.hasSuperContextParameter()) {
add(expression.superQualifierSymbol?.prototypeOf() ?: context.getVoid())
}
}
}
private fun IrFunction.hasSuperContextParameter(): Boolean {
return valueParameters.lastOrNull()?.origin == JsLoweredDeclarationOrigin.JS_SUPER_CONTEXT_PARAMETER
}
private fun IrClassSymbol.prototypeOf(): IrExpression {
return IrCallImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
context.dynamicType,
context.intrinsics.jsPrototypeOfSymbol,
0,
1
).apply {
putValueArgument(
0,
IrCallImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
context.dynamicType,
context.intrinsics.jsClass,
1,
0
).apply {
putTypeArgument(0, owner.defaultType)
}
)
}
}
private fun IrValueParameter.hasDefaultValue(): Boolean =
origin == JsLoweredDeclarationOrigin.JS_SHADOWED_DEFAULT_PARAMETER
}
@@ -24,6 +24,11 @@ class JsPropertyAccessorInlineLowering(
if (!isTopLevel && !context.icCompatibleIr2Js.incrementalCacheEnabled)
return true
// Just undefined value
if (symbol == context.intrinsics.void) {
return true
}
// TODO: teach the deserializer to load constant property initializers
if (context.icCompatibleIr2Js.isCompatible) {
val accessFile = accessContainer.fileOrNull ?: return false
@@ -52,8 +52,11 @@ private val JsPackage = FqName("kotlin.js")
private val JsIntrinsicFqName = FqName("kotlin.js.JsIntrinsic")
private fun IrDeclaration.isPlacedInsideInternalPackage() =
(parent as? IrPackageFragment)?.fqName == JsPackage
private fun isIntrinsic(declaration: IrDeclaration): Boolean =
declaration is IrSimpleFunction && (declaration.parent as? IrPackageFragment)?.fqName == JsPackage &&
declaration is IrSimpleFunction && declaration.isPlacedInsideInternalPackage() &&
declaration.annotations.any { it.symbol.owner.constructedClass.fqNameWhenAvailable == JsIntrinsicFqName }
fun moveBodilessDeclarationsToSeparatePlace(context: JsIrBackendContext, moduleFragment: IrModuleFragment) {
@@ -86,7 +86,7 @@ class SecondaryConstructorLowering(val context: JsIrBackendContext) : Declaratio
private fun generateFactoryBody(constructor: IrConstructor, irClass: IrClass, stub: IrSimpleFunction, delegate: IrSimpleFunction) {
stub.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
val type = irClass.defaultType
val createFunctionIntrinsic = context.intrinsics.jsObjectCreate
val createFunctionIntrinsic = context.intrinsics.jsObjectCreateSymbol
val irCreateCall = JsIrBuilder.buildCall(createFunctionIntrinsic, type, listOf(type))
val irDelegateCall = JsIrBuilder.buildCall(delegate.symbol, type).also { call ->
for (i in 0 until stub.typeParameters.size) {
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.lower
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.utils.getVoid
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent
@@ -28,10 +29,8 @@ class ThrowableLowering(
private val throwableConstructors = context.throwableConstructors
private val newThrowableFunction = context.newThrowableSymbol
private val jsUndefined = context.intrinsics.jsUndefined
fun nullValue(): IrExpression = IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, nothingNType)
fun undefinedValue(): IrExpression = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, nothingNType, jsUndefined, 0, 0)
private fun undefinedValue(): IrExpression = context.getVoid()
data class ThrowableArguments(
val message: IrExpression,
@@ -158,7 +158,7 @@ class IrModuleToJsTransformer(
val moduleBody = generateModuleBody(modules, staticContext)
val internalModuleName = ReservedJsNames.makeInternalModuleName()
val globalNames = NameTable<String>(namer.globalNames)
val exportStatements = ExportModelToJsStatements(nameGenerator) { globalNames.declareFreshName(it, it) }
val exportStatements = ExportModelToJsStatements(staticContext) { globalNames.declareFreshName(it, it) }
.generateModuleExport(exportedModule, internalModuleName)
val (crossModuleImports, importedKotlinModules) = generateCrossModuleImports(nameGenerator, modules, dependencies, { JsName(sanitizeName(it), false) })
@@ -38,7 +38,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
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
}
private val classPrototypeRef = prototypeOf(classNameRef)
private val classPrototypeRef = prototypeOf(classNameRef, context.staticContext)
private val classBlock = JsCompositeBlock()
private val classModel = JsIrClassModel(irClass)
@@ -202,7 +202,8 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
classPrototypeRef,
context.getNameForProperty(property).ident,
getter = getterForwarder,
setter = setterForwarder
setter = setterForwarder,
context.staticContext
)
)
}
@@ -328,11 +329,13 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
val associatedObjects = generateAssociatedObjects()
val suspendArity = generateSuspendArity()
val undefined = context.staticContext.backendContext.getVoid().accept(IrElementToJsExpressionTransformer(), context)
return JsInvocation(
JsNameRef(context.getNameForStaticFunction(setMetadataFor)),
listOf(ctor, name, metadataConstructor, parent, interfaces, associatedObjectKey, associatedObjects, suspendArity)
.dropLastWhile { it == null }
.map { it ?: Namer.JS_UNDEFINED }
.map { it ?: undefined }
).makeStmt()
}
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.expressions.IrRawFunctionReference
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.classifierOrFail
@@ -86,11 +87,10 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
prefixOp(intrinsics.jsTypeOf, JsUnaryOperator.TYPEOF)
add(intrinsics.jsObjectCreate) { call, context ->
add(intrinsics.jsObjectCreateSymbol) { call, context ->
val classToCreate = call.getTypeArgument(0)!!.classifierOrFail.owner as IrClass
val className = context.getNameForClass(classToCreate)
val prototype = prototypeOf(className.makeRef())
JsInvocation(Namer.JS_OBJECT_CREATE_FUNCTION, prototype)
objectCreate(prototypeOf(className.makeRef(), context.staticContext), context.staticContext)
}
add(intrinsics.jsClass) { call, context ->
@@ -194,6 +194,16 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
JsNameRef(fieldName, arg)
}
add(intrinsics.jsCall) { call, context: JsGenerationContext ->
val args = translateCallArguments(call, context)
val receiver = args[0]
val target = args[1]
val varargs = args[2] as? JsArrayLiteral ?: error("Expect to have JsArrayLiteral, because of vararg with dynamic element type")
val callRef = JsNameRef(Namer.CALL_FUNCTION, target)
JsInvocation(callRef, receiver, *varargs.expressions.toTypedArray())
}
add(intrinsics.jsBind) { call, context: JsGenerationContext ->
val receiver = call.getValueArgument(0)!!
val jsReceiver = receiver.accept(IrElementToJsExpressionTransformer(), context)
@@ -202,7 +212,7 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
val superClass = call.superQualifierSymbol!!
val functionName = context.getNameForMemberFunction(target.symbol.owner as IrSimpleFunction)
val superName = context.getNameForClass(superClass.owner).makeRef()
JsNameRef(functionName, prototypeOf(superName))
JsNameRef(functionName, prototypeOf(superName, context.staticContext))
}
is IrFunctionExpression -> target.accept(IrElementToJsExpressionTransformer(), context)
else -> compilationException(
@@ -214,6 +224,15 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
JsInvocation(bindRef, jsReceiver)
}
add(intrinsics.jsContexfulRef) { call, context: JsGenerationContext ->
val receiver = call.getValueArgument(0)!!
val jsReceiver = receiver.accept(IrElementToJsExpressionTransformer(), context)
val target = call.getValueArgument(1) as IrRawFunctionReference
val jsTarget = context.getNameForMemberFunction(target.symbol.owner as IrSimpleFunction)
JsNameRef(jsTarget, jsReceiver)
}
add(intrinsics.unreachable) { _, _ ->
JsInvocation(JsNameRef(Namer.UNREACHABLE_NAME))
}
@@ -234,9 +253,6 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
val value = args[1]
jsAssignment(JsNameRef(Namer.SHARED_BOX_V, box), value)
}
add(intrinsics.jsUndefined) { _, _ ->
JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1))
}
val suspendInvokeTransform: (IrCall, JsGenerationContext) -> JsExpression = { call, context: JsGenerationContext ->
// Because it is intrinsic, we know everything about this function
@@ -263,7 +279,7 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
}
private fun translateCallArguments(expression: IrCall, context: JsGenerationContext): List<JsExpression> {
return translateCallArguments(expression, context, IrElementToJsExpressionTransformer())
return translateCallArguments(expression, context, IrElementToJsExpressionTransformer(), false)
}
private fun MutableMap<IrSymbol, IrCallTransformer>.add(functionSymbol: IrSymbol, t: IrCallTransformer) {
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.compilationException
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrFileEntry
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
import org.jetbrains.kotlin.ir.backend.js.sourceMapsInfo
import org.jetbrains.kotlin.ir.backend.js.utils.*
@@ -31,6 +32,14 @@ import java.io.IOException
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
fun jsUndefined(context: IrNamer, backendContext: JsIrBackendContext): JsExpression {
return when (val void = backendContext.getVoid()) {
is IrGetField -> context.getNameForField(void.symbol.owner).makeRef()
else -> JsNullLiteral()
}
}
fun jsVar(name: JsName, initializer: IrExpression?, context: JsGenerationContext): JsVars {
val jsInitializer = initializer?.accept(IrElementToJsExpressionTransformer(), context)
return JsVars(JsVars.JsVar(name, jsInitializer))
@@ -63,7 +72,34 @@ fun jsElementAccess(name: JsName, receiver: JsExpression?): JsExpression =
fun jsAssignment(left: JsExpression, right: JsExpression) = JsBinaryOperation(JsBinaryOperator.ASG, left, right)
fun prototypeOf(classNameRef: JsExpression) = JsNameRef(Namer.PROTOTYPE_NAME, classNameRef)
fun prototypeOf(classNameRef: JsExpression, context: JsStaticContext) =
JsInvocation(
context
.getNameForStaticFunction(context.backendContext.intrinsics.jsPrototypeOfSymbol.owner)
.makeRef(),
classNameRef
)
fun objectCreate(prototype: JsExpression, context: JsStaticContext) =
JsInvocation(
context
.getNameForStaticFunction(context.backendContext.intrinsics.jsObjectCreateSymbol.owner)
.makeRef(),
prototype
)
fun defineProperty(obj: JsExpression, name: String, getter: JsExpression?, setter: JsExpression?, context: JsStaticContext) =
JsInvocation(
context
.getNameForStaticFunction(context.backendContext.intrinsics.jsDefinePropertySymbol.owner)
.makeRef(),
obj,
JsStringLiteral(name),
*listOf(getter, setter)
.dropLastWhile { it == null }
.map { it ?: jsUndefined(context, context.backendContext) }
.toTypedArray()
)
fun translateFunction(declaration: IrFunction, name: JsName?, context: JsGenerationContext): JsFunction {
context.staticContext.backendContext.getJsCodeForFunction(declaration.symbol)?.let { function ->
@@ -167,7 +203,7 @@ fun translateCall(
} else {
val qualifierName = context.getNameForClass(klass).makeRef()
val targetName = context.getNameForMemberFunction(target)
val qPrototype = JsNameRef(targetName, prototypeOf(qualifierName))
val qPrototype = JsNameRef(targetName, prototypeOf(qualifierName, context.staticContext))
JsNameRef(Namer.CALL_FUNCTION, qPrototype)
}
@@ -336,6 +372,7 @@ fun translateCallArguments(
expression: IrMemberAccessExpression<IrFunctionSymbol>,
context: JsGenerationContext,
transformer: IrElementToJsExpressionTransformer,
allowDropTailVoids: Boolean = true
): List<JsExpression> {
val size = expression.valueArgumentsCount
@@ -344,13 +381,15 @@ fun translateCallArguments(
val validWithNullArgs = expression.validWithNullArgs()
val arguments = (0 until size)
.mapTo(ArrayList(size)) { index ->
val argument = expression.getValueArgument(index)
argument?.accept(transformer, context)
expression.getValueArgument(index).checkOnNullability(validWithNullArgs)
}
.onEach { result ->
if (result == null) {
assert(validWithNullArgs)
}
.dropLastWhile {
allowDropTailVoids &&
it is IrGetField &&
it.symbol.owner.correspondingPropertySymbol == context.staticContext.backendContext.intrinsics.void
}
.map {
it?.accept(transformer, context)
}
.mapIndexed { index, result ->
val isEmptyExternalVararg = validWithNullArgs &&
@@ -363,34 +402,24 @@ fun translateCallArguments(
} else result
}
.dropLastWhile { it == null }
.map { it ?: JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1)) }
.map { it ?: jsUndefined(context, context.staticContext.backendContext) }
check(!expression.symbol.isSuspend) { "Suspend functions should be lowered" }
return arguments
}
private fun IrExpression?.checkOnNullability(validWithNullArgs: Boolean) =
also {
if (it == null) {
assert(validWithNullArgs)
}
}
private fun IrMemberAccessExpression<*>.validWithNullArgs() =
this is IrFunctionAccessExpression && symbol.owner.isExternalOrInheritedFromExternal()
fun JsStatement.asBlock() = this as? JsBlock ?: JsBlock(this)
fun defineProperty(receiver: JsExpression, name: String, value: () -> JsExpression): JsInvocation {
val objectDefineProperty = JsNameRef("defineProperty", Namer.JS_OBJECT)
return JsInvocation(objectDefineProperty, receiver, JsStringLiteral(name), value())
}
fun defineProperty(receiver: JsExpression, name: String, getter: JsExpression?, setter: JsExpression? = null) =
defineProperty(receiver, name) {
JsObjectLiteral(true).apply {
propertyInitializers += JsPropertyInitializer(JsStringLiteral("configurable"), JsBooleanLiteral(true))
if (getter != null)
propertyInitializers += JsPropertyInitializer(JsStringLiteral("get"), getter)
if (setter != null)
propertyInitializers += JsPropertyInitializer(JsStringLiteral("set"), setter)
}
}
// Partially copied from org.jetbrains.kotlin.js.translate.utils.JsAstUtils
object JsAstUtils {
private fun deBlockIfPossible(statement: JsStatement): JsStatement {
@@ -7,10 +7,14 @@ package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.descriptors.isClass
import org.jetbrains.kotlin.descriptors.isInterface
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrReturn
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
import org.jetbrains.kotlin.ir.util.parentClassOrNull
import org.jetbrains.kotlin.name.FqName
@@ -41,3 +45,18 @@ fun IrDeclarationWithName.getFqNameWithJsNameWhenAvailable(shouldIncludePackage:
private fun getKotlinOrJsQualifier(parent: IrPackageFragment, shouldIncludePackage: Boolean): FqName? {
return (parent as? IrFile)?.getJsQualifier()?.let { FqName(it) } ?: parent.fqName.takeIf { shouldIncludePackage }
}
// 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 =
intrinsics.void.owner.backingField?.let {
IrGetFieldImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
it.symbol,
irBuiltIns.nothingNType
)
} ?: IrConstImpl.constNull(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
irBuiltIns.nothingNType
)
@@ -136,7 +136,10 @@ fun calculateJsFunctionSignature(declaration: IrFunction, context: JsIrBackendCo
nameBuilder.append("_r$${it.type.asString()}")
}
declaration.valueParameters.ifNotEmpty {
joinTo(nameBuilder, "") { "_${it.type.asString()}" }
joinTo(nameBuilder, "") {
val defaultValueSign = if (it.origin == JsLoweredDeclarationOrigin.JS_SHADOWED_DEFAULT_PARAMETER) "?" else ""
"_${it.type.asString()}$defaultValueSign"
}
}
declaration.returnType.let {
// Return type is only used in signature for inline class and Unit types because
@@ -27,12 +27,7 @@ object Namer {
val JS_ERROR = JsNameRef("Error")
val JS_OBJECT = JsNameRef("Object")
val JS_UNDEFINED = JsNameRef("undefined")
val JS_OBJECT_CREATE_FUNCTION = JsNameRef("create", JS_OBJECT)
val METADATA = "\$metadata\$"
val INTERFACES_MASK = "\$imask\$"
val KCALLABLE_GET_NAME = "<get-name>"
val KCALLABLE_NAME = "callableName"
@@ -38,7 +38,6 @@ fun IrFunction.hasStableJsName(context: JsIrBackendContext): Boolean {
if (
origin == JsLoweredDeclarationOrigin.JS_SHADOWED_EXPORT ||
origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER ||
origin == JsLoweredDeclarationOrigin.BRIDGE_WITHOUT_STABLE_NAME ||
origin == JsLoweredDeclarationOrigin.BRIDGE_PROPERTY_ACCESSOR
) {