[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
@@ -0,0 +1,209 @@
/*
* Copyright 2010-2019 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.backend.common.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
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.impl.IrErrorExpressionImpl
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
abstract class DefaultArgumentFunctionFactory(open val context: CommonBackendContext) {
protected fun IrFunction.generateDefaultArgumentsFunctionName() =
Name.identifier("${name}\$default")
protected abstract fun IrFunction.generateDefaultArgumentStubFrom(original: IrFunction, useConstructorMarker: Boolean)
protected fun IrFunction.copyAttributesFrom(original: IrFunction) {
(this as? IrAttributeContainer)?.copyAttributes(original as? IrAttributeContainer)
}
protected fun IrFunction.copyReturnTypeFrom(original: IrFunction) {
returnType = original.returnType.remapTypeParameters(original.classIfConstructor, classIfConstructor)
}
protected fun IrFunction.copyReceiversFrom(original: IrFunction) {
dispatchReceiverParameter = original.dispatchReceiverParameter?.copyTo(this)
extensionReceiverParameter = original.extensionReceiverParameter?.copyTo(this)
contextReceiverParametersCount = original.contextReceiverParametersCount
}
protected fun IrFunction.copyValueParametersFrom(original: IrFunction, wrapWithNullable: Boolean = true) {
valueParameters = original.valueParameters.map {
val newType = it.type.remapTypeParameters(original.classIfConstructor, classIfConstructor)
val makeNullable = wrapWithNullable && it.defaultValue != null &&
(context.ir.unfoldInlineClassType(it.type) ?: it.type) !in context.irBuiltIns.primitiveIrTypes
it.copyTo(
this,
type = if (makeNullable) newType.makeNullable() else newType,
defaultValue = if (it.defaultValue != null) {
original.factory.createExpressionBody(
IrErrorExpressionImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
it.type,
"Default Stub"
)
)
} else null,
isAssignable = it.defaultValue != null
)
}
}
fun findBaseFunctionWithDefaultArgumentsFor(
declaration: IrFunction,
skipInlineMethods: Boolean,
skipExternalMethods: Boolean
): IrFunction? {
val visited = mutableSetOf<IrFunction>()
fun IrFunction.dfsImpl(): IrFunction? {
visited += this
if (isInline && skipInlineMethods) return null
if (skipExternalMethods && isExternalOrInheritedFromExternal()) return null
if (this is IrSimpleFunction) {
overriddenSymbols.forEach { overridden ->
val base = overridden.owner
if (base !in visited) base.dfsImpl()?.let { return it }
}
}
if (valueParameters.any { it.defaultValue != null }) return this
return null
}
return declaration.dfsImpl()
}
fun generateDefaultsFunction(
declaration: IrFunction,
skipInlineMethods: Boolean,
skipExternalMethods: Boolean,
forceSetOverrideSymbols: Boolean,
visibility: DescriptorVisibility,
useConstructorMarker: Boolean,
copiedAnnotations: List<IrConstructorCall>,
): IrFunction? {
if (skipInlineMethods && declaration.isInline) return null
if (skipExternalMethods && declaration.isExternalOrInheritedFromExternal()) return null
if (context.mapping.defaultArgumentsOriginalFunction[declaration] != null) return null
context.mapping.defaultArgumentsDispatchFunction[declaration]?.let { return it }
if (declaration is IrSimpleFunction) {
// If this is an override of a function with default arguments, produce a fake override of a default stub.
if (declaration.overriddenSymbols.any {
findBaseFunctionWithDefaultArgumentsFor(
it.owner,
skipInlineMethods,
skipExternalMethods
) != null
})
return generateDefaultsFunctionImpl(
declaration,
IrDeclarationOrigin.FAKE_OVERRIDE,
visibility,
copiedAnnotations,
true,
useConstructorMarker,
).also { defaultsFunction ->
context.mapping.defaultArgumentsDispatchFunction[declaration] = defaultsFunction
context.mapping.defaultArgumentsOriginalFunction[defaultsFunction] = declaration
if (forceSetOverrideSymbols) {
(defaultsFunction as IrSimpleFunction).overriddenSymbols += declaration.overriddenSymbols.mapNotNull {
generateDefaultsFunction(
it.owner,
skipInlineMethods,
skipExternalMethods,
forceSetOverrideSymbols,
visibility,
useConstructorMarker,
it.owner.copyAnnotations(),
)?.symbol as IrSimpleFunctionSymbol?
}
}
}
}
// Note: this is intentionally done *after* checking for overrides. While normally `override fun`s
// have no default parameters, there is an exception in case of interface delegation:
// interface I {
// fun f(x: Int = 1)
// }
// class C(val y: I) : I by y {
// // implicit `override fun f(x: Int) = y.f(x)` has a default value for `x`
// }
// Since this bug causes the metadata serializer to write the "has default value" flag into compiled
// binaries, it's way too late to fix it. Hence the workaround.
if (declaration.valueParameters.any { it.defaultValue != null }) {
return generateDefaultsFunctionImpl(
declaration,
IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER,
visibility,
copiedAnnotations,
false,
useConstructorMarker,
).also {
context.mapping.defaultArgumentsDispatchFunction[declaration] = it
context.mapping.defaultArgumentsOriginalFunction[it] = declaration
}
}
return null
}
private fun generateDefaultsFunctionImpl(
declaration: IrFunction,
newOrigin: IrDeclarationOrigin,
newVisibility: DescriptorVisibility,
copiedAnnotations: List<IrConstructorCall>,
isFakeOverride: Boolean,
useConstructorMarker: Boolean,
): IrFunction {
val newFunction = when (declaration) {
is IrConstructor ->
declaration.factory.buildConstructor {
updateFrom(declaration)
origin = newOrigin
isExternal = false
isPrimary = false
isExpect = false
visibility = newVisibility
}
is IrSimpleFunction ->
declaration.factory.buildFun {
updateFrom(declaration)
name = declaration.generateDefaultArgumentsFunctionName()
origin = newOrigin
this.isFakeOverride = isFakeOverride
modality = Modality.FINAL
isExternal = false
isTailrec = false
visibility = newVisibility
}
else -> throw IllegalStateException("Unknown function type")
}
return newFunction.apply {
parent = declaration.parent
generateDefaultArgumentStubFrom(declaration, useConstructorMarker)
// TODO some annotations are needed (e.g. @JvmStatic), others need different values (e.g. @JvmName), the rest are redundant.
annotations += copiedAnnotations
}
}
}
@@ -41,7 +41,8 @@ open class DefaultArgumentStubGenerator(
open val context: CommonBackendContext,
private val skipInlineMethods: Boolean = true,
private val skipExternalMethods: Boolean = false,
private val forceSetOverrideSymbols: Boolean = true
private val forceSetOverrideSymbols: Boolean = true,
private val factory: DefaultArgumentFunctionFactory = MaskedDefaultArgumentFunctionFactory(context)
) : DeclarationTransformer {
override val withLocalDeclarations: Boolean get() = true
@@ -55,34 +56,21 @@ open class DefaultArgumentStubGenerator(
protected open fun IrFunction.resolveAnnotations(): List<IrConstructorCall> = copyAnnotations()
private fun lower(irFunction: IrFunction): List<IrFunction>? {
val newIrFunction =
irFunction.generateDefaultsFunction(
context,
skipInlineMethods,
skipExternalMethods,
forceSetOverrideSymbols,
defaultArgumentStubVisibility(irFunction),
useConstructorMarker(irFunction),
irFunction.resolveAnnotations()
) ?: return null
if (newIrFunction.isFakeOverride) {
return listOf(irFunction, newIrFunction)
}
log { "$irFunction -> $newIrFunction" }
protected open fun IrFunction.generateDefaultStubBody(originalDeclaration: IrFunction): IrBody {
val newIrFunction = this
val builder = context.createIrBuilder(newIrFunction.symbol)
log { "$originalDeclaration -> $newIrFunction" }
newIrFunction.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
return context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
statements += builder.irBlockBody(newIrFunction) {
val params = mutableListOf<IrValueDeclaration>()
val variables = mutableMapOf<IrValueSymbol, IrValueSymbol>()
irFunction.dispatchReceiverParameter?.let {
originalDeclaration.dispatchReceiverParameter?.let {
variables[it.symbol] = newIrFunction.dispatchReceiverParameter?.symbol!!
}
irFunction.extensionReceiverParameter?.let {
originalDeclaration.extensionReceiverParameter?.let {
variables[it.symbol] = newIrFunction.extensionReceiverParameter?.symbol!!
}
@@ -96,23 +84,23 @@ open class DefaultArgumentStubGenerator(
//
// works correctly so that `f() { "OK" }` returns "OK" and
// `f()` throws a NullPointerException.
irFunction.valueParameters.forEach {
originalDeclaration.valueParameters.forEach {
variables[it.symbol] = newIrFunction.valueParameters[it.index].symbol
}
generateSuperCallHandlerCheckIfNeeded(irFunction, newIrFunction)
generateSuperCallHandlerCheckIfNeeded(originalDeclaration, newIrFunction)
val intAnd = this@DefaultArgumentStubGenerator.context.ir.symbols.getBinaryOperator(
OperatorNameConventions.AND, context.irBuiltIns.intType, context.irBuiltIns.intType
)
var sourceParameterIndex = -1
for (valueParameter in irFunction.valueParameters) {
for (valueParameter in originalDeclaration.valueParameters) {
if (!valueParameter.isMovedReceiver()) {
++sourceParameterIndex
}
val parameter = newIrFunction.valueParameters[valueParameter.index]
val remapped = valueParameter.defaultValue?.let { defaultValue ->
val mask = irGet(newIrFunction.valueParameters[irFunction.valueParameters.size + valueParameter.index / 32])
val mask = irGet(newIrFunction.valueParameters[originalDeclaration.valueParameters.size + valueParameter.index / 32])
val bit = irInt(1 shl (sourceParameterIndex % 32))
val defaultFlag =
irCallOp(intAnd, context.irBuiltIns.intType, mask, bit)
@@ -128,8 +116,8 @@ open class DefaultArgumentStubGenerator(
variables[valueParameter.symbol] = remapped.symbol
}
when (irFunction) {
is IrConstructor -> +irDelegatingConstructorCall(irFunction).apply {
when (originalDeclaration) {
is IrConstructor -> +irDelegatingConstructorCall(originalDeclaration).apply {
passTypeArgumentsFrom(newIrFunction.parentAsClass)
// This is for Kotlin/Native, which differs from the other backends in that constructors
// apparently do have dispatch receivers (though *probably* not type arguments, but copy
@@ -138,12 +126,30 @@ open class DefaultArgumentStubGenerator(
dispatchReceiver = newIrFunction.dispatchReceiverParameter?.let { irGet(it) }
params.forEachIndexed { i, variable -> putValueArgument(i, irGet(variable)) }
}
is IrSimpleFunction -> +irReturn(dispatchToImplementation(irFunction, newIrFunction, params))
is IrSimpleFunction -> +irReturn(dispatchToImplementation(originalDeclaration, newIrFunction, params))
else -> error("Unknown function declaration")
}
}.statements
}
return listOf(irFunction, newIrFunction)
}
private fun lower(irFunction: IrFunction): List<IrFunction>? {
val newIrFunction =
factory.generateDefaultsFunction(
irFunction,
skipInlineMethods,
skipExternalMethods,
forceSetOverrideSymbols,
defaultArgumentStubVisibility(irFunction),
useConstructorMarker(irFunction),
irFunction.resolveAnnotations(),
) ?: return null
return listOf(irFunction, newIrFunction).also {
if (!newIrFunction.isFakeOverride) {
newIrFunction.body = newIrFunction.generateDefaultStubBody(irFunction)
}
}
}
/**
@@ -253,45 +259,24 @@ open class DefaultArgumentStubGenerator(
private fun log(msg: () -> String) = context.log { "DEFAULT-REPLACER: ${msg()}" }
}
private fun IrFunction.findBaseFunctionWithDefaultArguments(skipInlineMethods: Boolean, skipExternalMethods: Boolean): IrFunction? {
val visited = mutableSetOf<IrFunction>()
fun IrFunction.dfsImpl(): IrFunction? {
visited += this
if (isInline && skipInlineMethods) return null
if (skipExternalMethods && isExternalOrInheritedFromExternal()) return null
if (this is IrSimpleFunction) {
overriddenSymbols.forEach { overridden ->
val base = overridden.owner
if (base !in visited) base.dfsImpl()?.let { return it }
}
}
if (valueParameters.any { it.defaultValue != null }) return this
return null
}
return dfsImpl()
}
open class DefaultParameterInjector(
open val context: CommonBackendContext,
private val skipInline: Boolean = true,
private val skipExternalMethods: Boolean = false,
private val forceSetOverrideSymbols: Boolean = true
protected val skipInline: Boolean = true,
protected val skipExternalMethods: Boolean = false,
protected val forceSetOverrideSymbols: Boolean = true,
protected val factory: DefaultArgumentFunctionFactory = MaskedDefaultArgumentFunctionFactory(context),
) : IrElementTransformerVoid(), BodyLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) {
irBody.transformChildrenVoid(this)
}
protected open fun shouldReplaceWithSyntheticFunction(functionAccess: IrFunctionAccessExpression): Boolean {
return (0 until functionAccess.valueArgumentsCount).count { functionAccess.getValueArgument(it) != null } != functionAccess.symbol.owner.valueParameters.size
}
private fun <T : IrFunctionAccessExpression> visitFunctionAccessExpression(expression: T, builder: (IrFunctionSymbol) -> T): T {
val argumentsCount = (0 until expression.valueArgumentsCount).count { expression.getValueArgument(it) != null }
if (argumentsCount == expression.symbol.owner.valueParameters.size)
if (!shouldReplaceWithSyntheticFunction(expression))
return expression
val (symbol, params) = parametersForCall(expression) ?: return expression
@@ -346,7 +331,13 @@ open class DefaultParameterInjector(
expression.transformChildrenVoid()
return visitFunctionAccessExpression(expression) {
with(expression) {
IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, type, it as IrConstructorSymbol, LoweredStatementOrigins.DEFAULT_DISPATCH_CALL)
IrConstructorCallImpl.fromSymbolOwner(
startOffset,
endOffset,
type,
it as IrConstructorSymbol,
LoweredStatementOrigins.DEFAULT_DISPATCH_CALL
)
}
}
}
@@ -379,7 +370,7 @@ open class DefaultParameterInjector(
}
}
private fun parametersForCall(expression: IrFunctionAccessExpression): Pair<IrFunctionSymbol, List<IrExpression?>>? {
open protected fun parametersForCall(expression: IrFunctionAccessExpression): Pair<IrFunctionSymbol, List<IrExpression?>>? {
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val declaration = expression.symbol.owner
@@ -388,21 +379,24 @@ open class DefaultParameterInjector(
// in an interface does not leave an abstract method after being moved to DefaultImpls (see InterfaceLowering).
// Calling the fake override on an implementation of that interface would then result in a call to a method
// that does not actually exist as DefaultImpls is not part of the inheritance hierarchy.
val baseFunction = declaration.findBaseFunctionWithDefaultArguments(skipInline, skipExternalMethods)
val stubFunction = baseFunction?.generateDefaultsFunction(
context,
skipInline,
skipExternalMethods,
forceSetOverrideSymbols,
defaultArgumentStubVisibility(declaration),
useConstructorMarker(declaration),
baseFunction.copyAnnotations()
) ?: return null
val baseFunction = factory.findBaseFunctionWithDefaultArgumentsFor(declaration, skipInline, skipExternalMethods)
val stubFunction = baseFunction?.let {
factory.generateDefaultsFunction(
it,
skipInline,
skipExternalMethods,
forceSetOverrideSymbols,
defaultArgumentStubVisibility(declaration),
useConstructorMarker(declaration),
baseFunction.copyAnnotations(),
)
} ?: return null
log { "$declaration -> $stubFunction" }
val realArgumentsNumber = declaration.valueParameters.size
val maskValues = IntArray((declaration.valueParameters.size + 31) / 32)
assert(
((if (isStatic(expression.symbol.owner) && stubFunction.extensionReceiverParameter != null) 1 else 0) +
stubFunction.valueParameters.size - realArgumentsNumber - maskValues.size) in listOf(0, 1)
@@ -410,6 +404,7 @@ open class DefaultParameterInjector(
"argument count mismatch: expected $realArgumentsNumber arguments + ${maskValues.size} masks + optional handler/marker, " +
"got ${stubFunction.valueParameters.size} total in ${stubFunction.render()}"
}
var sourceParameterIndex = -1
val valueParametersPrefix =
if (isStatic(expression.symbol.owner))
@@ -500,134 +495,34 @@ class DefaultParameterPatchOverridenSymbolsLowering(
}
}
private fun IrFunction.generateDefaultsFunction(
context: CommonBackendContext,
skipInlineMethods: Boolean,
skipExternalMethods: Boolean,
forceSetOverrideSymbols: Boolean,
visibility: DescriptorVisibility,
useConstructorMarker: Boolean,
copiedAnnotations: List<IrConstructorCall>
): IrFunction? {
if (skipInlineMethods && isInline) return null
if (skipExternalMethods && isExternalOrInheritedFromExternal()) return null
if (context.mapping.defaultArgumentsOriginalFunction[this] != null) return null
context.mapping.defaultArgumentsDispatchFunction[this]?.let { return it }
if (this is IrSimpleFunction) {
// If this is an override of a function with default arguments, produce a fake override of a default stub.
if (overriddenSymbols.any { it.owner.findBaseFunctionWithDefaultArguments(skipInlineMethods, skipExternalMethods) != null })
return generateDefaultsFunctionImpl(
context, IrDeclarationOrigin.FAKE_OVERRIDE, visibility, copiedAnnotations, true, useConstructorMarker
).also { defaultsFunction ->
context.mapping.defaultArgumentsDispatchFunction[this] = defaultsFunction
context.mapping.defaultArgumentsOriginalFunction[defaultsFunction] = this
private class MaskedDefaultArgumentFunctionFactory(context: CommonBackendContext) : DefaultArgumentFunctionFactory(context) {
override fun IrFunction.generateDefaultArgumentStubFrom(original: IrFunction, useConstructorMarker: Boolean) {
copyAttributesFrom(original)
copyTypeParametersFrom(original)
copyReturnTypeFrom(original)
copyReceiversFrom(original)
copyValueParametersFrom(original)
if (forceSetOverrideSymbols) {
(defaultsFunction as IrSimpleFunction).overriddenSymbols += overriddenSymbols.mapNotNull {
it.owner.generateDefaultsFunction(
context,
skipInlineMethods,
skipExternalMethods,
forceSetOverrideSymbols,
visibility,
useConstructorMarker,
it.owner.copyAnnotations()
)?.symbol as IrSimpleFunctionSymbol?
}
}
}
}
// Note: this is intentionally done *after* checking for overrides. While normally `override fun`s
// have no default parameters, there is an exception in case of interface delegation:
// interface I {
// fun f(x: Int = 1)
// }
// class C(val y: I) : I by y {
// // implicit `override fun f(x: Int) = y.f(x)` has a default value for `x`
// }
// Since this bug causes the metadata serializer to write the "has default value" flag into compiled
// binaries, it's way too late to fix it. Hence the workaround.
if (valueParameters.any { it.defaultValue != null }) {
return generateDefaultsFunctionImpl(
context, IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER, visibility, copiedAnnotations, false, useConstructorMarker
).also {
context.mapping.defaultArgumentsDispatchFunction[this] = it
context.mapping.defaultArgumentsOriginalFunction[it] = this
for (i in 0 until (original.valueParameters.size + 31) / 32) {
addValueParameter(
"mask$i".synthesizedString,
context.irBuiltIns.intType,
IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION
)
}
}
return null
}
private fun IrFunction.generateDefaultsFunctionImpl(
context: CommonBackendContext,
newOrigin: IrDeclarationOrigin,
newVisibility: DescriptorVisibility,
copiedAnnotations: List<IrConstructorCall>,
isFakeOverride: Boolean,
useConstructorMarker: Boolean
): IrFunction {
val newFunction = when (this) {
is IrConstructor ->
factory.buildConstructor {
updateFrom(this@generateDefaultsFunctionImpl)
origin = newOrigin
isExternal = false
isPrimary = false
isExpect = false
visibility = newVisibility
}
is IrSimpleFunction ->
factory.buildFun {
updateFrom(this@generateDefaultsFunctionImpl)
name = Name.identifier("${this@generateDefaultsFunctionImpl.name}\$default")
origin = newOrigin
this.isFakeOverride = isFakeOverride
modality = Modality.FINAL
isExternal = false
isTailrec = false
visibility = newVisibility
}
else -> throw IllegalStateException("Unknown function type")
}
(newFunction as? IrAttributeContainer)?.copyAttributes(this@generateDefaultsFunctionImpl as? IrAttributeContainer)
newFunction.copyTypeParametersFrom(this)
newFunction.parent = parent
newFunction.returnType = returnType.remapTypeParameters(classIfConstructor, newFunction.classIfConstructor)
newFunction.dispatchReceiverParameter = dispatchReceiverParameter?.copyTo(newFunction)
newFunction.extensionReceiverParameter = extensionReceiverParameter?.copyTo(newFunction)
newFunction.contextReceiverParametersCount = contextReceiverParametersCount
if (useConstructorMarker) {
val markerType = context.ir.symbols.defaultConstructorMarker.defaultType.makeNullable()
addValueParameter("marker".synthesizedString, markerType, IrDeclarationOrigin.DEFAULT_CONSTRUCTOR_MARKER)
} else if (context.ir.shouldGenerateHandlerParameterForDefaultBodyFun()) {
addValueParameter(
"handler".synthesizedString,
context.irBuiltIns.anyNType,
IrDeclarationOrigin.METHOD_HANDLER_IN_DEFAULT_FUNCTION
)
}
newFunction.valueParameters = valueParameters.map {
val newType = it.type.remapTypeParameters(classIfConstructor, newFunction.classIfConstructor)
val makeNullable = it.defaultValue != null &&
(context.ir.unfoldInlineClassType(it.type) ?: it.type) !in context.irBuiltIns.primitiveIrTypes
it.copyTo(
newFunction,
type = if (makeNullable) newType.makeNullable() else newType,
defaultValue = if (it.defaultValue != null) {
factory.createExpressionBody(IrErrorExpressionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it.type, "Default Stub"))
} else null,
isAssignable = it.defaultValue != null
)
}
for (i in 0 until (valueParameters.size + 31) / 32) {
newFunction.addValueParameter("mask$i".synthesizedString, context.irBuiltIns.intType, IrDeclarationOrigin.MASK_FOR_DEFAULT_FUNCTION)
}
if (useConstructorMarker) {
val markerType = context.ir.symbols.defaultConstructorMarker.defaultType.makeNullable()
newFunction.addValueParameter("marker".synthesizedString, markerType, IrDeclarationOrigin.DEFAULT_CONSTRUCTOR_MARKER)
} else if (context.ir.shouldGenerateHandlerParameterForDefaultBodyFun()) {
newFunction.addValueParameter(
"handler".synthesizedString,
context.irBuiltIns.anyNType,
IrDeclarationOrigin.METHOD_HANDLER_IN_DEFAULT_FUNCTION
)
}
// TODO some annotations are needed (e.g. @JvmStatic), others need different values (e.g. @JvmName), the rest are redundant.
newFunction.annotations += copiedAnnotations
return newFunction
}
private fun IrValueParameter.isMovedReceiver() =
@@ -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
) {
@@ -149,8 +149,8 @@ fun IrBuilderWithScope.irSet(variable: IrValueSymbol, value: IrExpression, origi
fun IrBuilderWithScope.irSet(variable: IrValueDeclaration, value: IrExpression, origin: IrStatementOrigin = IrStatementOrigin.EQ) =
irSet(variable.symbol, value, origin)
fun IrBuilderWithScope.irGetField(receiver: IrExpression?, field: IrField) =
IrGetFieldImpl(startOffset, endOffset, field.symbol, field.type, receiver)
fun IrBuilderWithScope.irGetField(receiver: IrExpression?, field: IrField, type: IrType = field.type) =
IrGetFieldImpl(startOffset, endOffset, field.symbol, type, receiver)
fun IrBuilderWithScope.irSetField(receiver: IrExpression?, field: IrField, value: IrExpression, origin: IrStatementOrigin? = null) =
IrSetFieldImpl(startOffset, endOffset, field.symbol, receiver, value, context.irBuiltIns.unitType, origin = origin)
@@ -161,6 +161,17 @@ fun IrBuilderWithScope.irGetObjectValue(type: IrType, classSymbol: IrClassSymbol
fun IrBuilderWithScope.irEqeqeq(arg1: IrExpression, arg2: IrExpression) =
context.eqeqeq(startOffset, endOffset, arg1, arg2)
fun IrBuilderWithScope.irEqeqeqWithoutBox(arg1: IrExpression, arg2: IrExpression) =
primitiveOp2(
startOffset,
endOffset,
context.irBuiltIns.eqeqeqSymbol,
context.irBuiltIns.booleanType,
IrStatementOrigin.SYNTHETIC_NOT_AUTOBOXED_CHECK,
arg1,
arg2
)
fun IrBuilderWithScope.irNull() =
irNull(context.irBuiltIns.nothingNType)
@@ -54,6 +54,10 @@ abstract class IrStatementsBuilder<out T : IrElement>(
addStatement(this)
}
operator fun List<IrStatement>.unaryPlus() {
forEach(::addStatement)
}
protected abstract fun addStatement(irStatement: IrStatement)
abstract fun doBuild(): T
}
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.ir.expressions
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
abstract class IrStatementOriginImpl(val debugName: String) : IrStatementOrigin {
override fun toString(): String = debugName
}
@@ -90,6 +92,8 @@ interface IrStatementOrigin {
object BRIDGE_DELEGATION : IrStatementOriginImpl("BRIDGE_DELEGATION")
object SYNTHETIC_NOT_AUTOBOXED_CHECK : IrStatementOriginImpl("SYNTHETIC_NOT_AUTOBOXED_CHECK")
data class COMPONENT_N private constructor(val index: Int) : IrStatementOriginImpl("COMPONENT_$index") {
companion object {
private val precreatedComponents = Array(32) { i -> COMPONENT_N(i + 1) }
@@ -19,4 +19,5 @@ interface JsStatementOrigins {
object COROUTINE_ROOT_LOOP : IrStatementOriginImpl("COROUTINE_ROOT_LOOP")
object COROUTINE_SWITCH : IrStatementOriginImpl("COROUTINE_SWITCH")
object CLASS_REFERENCE : IrStatementOriginImpl("CLASS_REFERENCE")
object IMPLEMENTATION_DELEGATION_CALL : IrStatementOriginImpl("IMPLEMENTATION_DELEGATION_CALL")
}
+4 -2
View File
@@ -90,7 +90,8 @@ fun trimMarginConstantCustomPrefixInterpolatedUsingConstant(): String {
""".trimMargin(marginPrefix = "$OCTOTHORPE@$OCTOTHORPE")
}
// CHECK_CALLED_IN_SCOPE: function=trimMargin$default scope=trimMarginNotConstant
// CHECK_CALLED_IN_SCOPE: function=trimMargin$default scope=trimMarginNotConstant IGNORED_BACKENDS=JS_IR
// CHECK_CALLED_IN_SCOPE: function=trimMargin scope=trimMarginNotConstant TARGET_BACKENDS=JS_IR
fun trimMarginNotConstant(arg: String): String {
return arg.trimMargin()
}
@@ -100,7 +101,8 @@ fun trimMarginNotConstantCustomPrefix(arg: String): String {
return arg.trimMargin("###")
}
// CHECK_CALLED_IN_SCOPE: function=trimMargin$default scope=trimMarginInterpolated
// CHECK_CALLED_IN_SCOPE: function=trimMargin$default scope=trimMarginInterpolated IGNORED_BACKENDS=JS_IR
// CHECK_CALLED_IN_SCOPE: function=trimMargin scope=trimMarginInterpolated TARGET_BACKENDS=JS_IR
fun trimMarginInterpolated(arg: Int): String {
return """
|Hello,
+2 -2
View File
@@ -30,5 +30,5 @@ fun box() {
// EXPECTATIONS JS_IR
// test.kt:9 box
// test.kt:11 box
// test.kt:4 A.foo_26di_k$
// test.kt:5 box
// test.kt:4 protoOf.foo_26di_k$
// test.kt:5 box
+2 -2
View File
@@ -32,9 +32,9 @@ fun box() {
// EXPECTATIONS JS_IR
// test.kt:9 box
// test.kt:10 box
// test.kt:4 A.foo_26di_k$
// test.kt:4 protoOf.foo_26di_k$
// test.kt:11 box
// test.kt:4 A.foo_26di_k$
// test.kt:4 protoOf.foo_26di_k$
// test.kt:5 box
// test.kt:13 box
// test.kt:5 box
+1 -1
View File
@@ -45,4 +45,4 @@ fun box() {
// test.kt:15 box
// test.kt:16 box
// test.kt:16 box
// test.kt:8 Companion_19.foo_26di_k$
// test.kt:8 protoOf.foo_26di_k$
+21 -23
View File
@@ -68,29 +68,27 @@ fun box() {
// test.kt:3 D
// test.kt:3 D
// test.kt:14 box
// test.kt:1 D.equals
// test.kt:1 D.equals
// test.kt:1 D.equals
// test.kt:1 D.equals
// test.kt:1 D.equals
// test.kt:1 D.equals
// test.kt:1 protoOf.equals
// test.kt:1 protoOf.equals
// test.kt:1 protoOf.equals
// test.kt:1 protoOf.equals
// test.kt:1 protoOf.equals
// test.kt:1 protoOf.equals
// test.kt:15 box
// test.kt:1 D.hashCode
// test.kt:1 D.hashCode
// test.kt:1 protoOf.hashCode
// test.kt:1 protoOf.hashCode
// test.kt:16 box
// test.kt:1 D.toString
// test.kt:1 protoOf.toString
// test.kt:17 box
// test.kt:17 box
// test.kt:1 D.component1_7eebsc_k$
// test.kt:1 protoOf.component1_7eebsc_k$
// test.kt:17 box
// test.kt:1 D.component2_7eebsb_k$
// test.kt:1 protoOf.component2_7eebsb_k$
// test.kt:18 box
// test.kt:1 D.copy$default_cbhffz_k$
// test.kt:1 D.copy$default_cbhffz_k$
// test.kt:1 D.copy$default_cbhffz_k$
// test.kt:1 D.copy$default_cbhffz_k$
// test.kt:1 D.copy$default_cbhffz_k$
// test.kt:1 D.copy_t8q04r_k$
// test.kt:1 protoOf.copy$default_roih85_k$
// test.kt:1 protoOf.copy$default_roih85_k$
// test.kt:1 protoOf.copy$default_roih85_k$
// test.kt:1 protoOf.copy_t8q04r_k$
// test.kt:3 D
// test.kt:3 D
// test.kt:19 box
@@ -100,17 +98,17 @@ fun box() {
// test.kt:5 E
// test.kt:5 E
// test.kt:20 box
// test.kt:7 E.equals
// test.kt:7 protoOf.equals
// test.kt:21 box
// test.kt:8 E.hashCode
// test.kt:8 protoOf.hashCode
// test.kt:22 box
// test.kt:6 E.toString
// test.kt:6 protoOf.toString
// test.kt:23 box
// test.kt:23 box
// test.kt:1 E.component1_7eebsc_k$
// test.kt:1 protoOf.component1_7eebsc_k$
// test.kt:23 box
// test.kt:1 E.component2_7eebsb_k$
// test.kt:1 protoOf.component2_7eebsb_k$
// test.kt:24 box
// test.kt:9 E.copy_1tks5_k$
// test.kt:9 protoOf.copy_1tks5_k$
// test.kt:5 E
// test.kt:5 E
+5 -4
View File
@@ -26,7 +26,8 @@ fun box() {
// EXPECTATIONS JS_IR
// test.kt:11 box
// test.kt:11 box
// test.kt:6 A.foo$default_dec9f7_k$
// test.kt:6 A.foo$default_dec9f7_k$
// test.kt:4 A.computeParam_vubdyi_k$
// test.kt:6 A.foo$default_dec9f7_k$
// test.kt:6 protoOf.foo$default_ourpn2_k$
// test.kt:4 protoOf.computeParam_vubdyi_k$
// test.kt:6 protoOf.foo$default_ourpn2_k$
// test.kt:6 protoOf.foo$default_ourpn2_k$
// test.kt:6 protoOf.foo$default_ourpn2_k$
+1 -1
View File
@@ -57,7 +57,7 @@ fun box() {
// test.kt:11 E
// test.kt:11 E
// test.kt:22 box
// test.kt:9 E.foo_26di_k$
// test.kt:9 protoOf.foo_26di_k$
// test.kt:15 E2_initEntries
// test.kt:14 E2
// test.kt:17 E2_initEntries
@@ -24,6 +24,4 @@ inline fun bar(i: Int = 1) {
// EXPECTATIONS JS_IR
// test.kt:4 box
// test.kt:8 foo$default
// test.kt:8 foo$default
// test.kt:8 foo$default
// test.kt:8 foo
+1 -1
View File
@@ -22,4 +22,4 @@ fun box() {
// EXPECTATIONS JS_IR
// test.kt:11 box
// test.kt:11 box
// test.kt:6 A.get_prop_wosl9o_k$
// test.kt:6 protoOf.get_prop_wosl9o_k$
+1 -4
View File
@@ -18,7 +18,4 @@ fun box(): String {
// EXPECTATIONS JS_IR
// test.kt:8 box
// test.kt:3 ifoo$default
// test.kt:3 ifoo$default
// test.kt:3 ifoo$default
// test.kt:4 ifoo
// test.kt:3 ifoo
@@ -19,7 +19,7 @@ suspend fun box() {
// test.kt:11 box
// EXPECTATIONS JS_IR
// test.kt:8 $boxCOROUTINE$0.doResume_5yljmg_k$
// test.kt:8 protoOf.doResume_5yljmg_k$
// test.kt:4 foo
// test.kt:4 foo
// test.kt:9 box$lambda
@@ -1220,6 +1220,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
runTest("js/js.translator/testData/box/defaultArguments/inheritViaAnotherInterfaceIndirectly.kt");
}
@Test
@TestMetadata("interfaceSuperCall.kt")
public void testInterfaceSuperCall() throws Exception {
runTest("js/js.translator/testData/box/defaultArguments/interfaceSuperCall.kt");
}
@Test
@TestMetadata("overloadFunWithDefArg.kt")
public void testOverloadFunWithDefArg() throws Exception {
@@ -2130,6 +2136,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
runTest("js/js.translator/testData/box/export/exportClassWithInternalOneFile.kt");
}
@Test
@TestMetadata("exportDefaultParameterAndOverrideIt.kt")
public void testExportDefaultParameterAndOverrideIt() throws Exception {
runTest("js/js.translator/testData/box/export/exportDefaultParameterAndOverrideIt.kt");
}
@Test
@TestMetadata("exportEnumClass.kt")
public void testExportEnumClass() throws Exception {
@@ -1284,6 +1284,12 @@ public class FirJsTestGenerated extends AbstractFirJsTest {
runTest("js/js.translator/testData/box/defaultArguments/inheritViaAnotherInterfaceIndirectly.kt");
}
@Test
@TestMetadata("interfaceSuperCall.kt")
public void testInterfaceSuperCall() throws Exception {
runTest("js/js.translator/testData/box/defaultArguments/interfaceSuperCall.kt");
}
@Test
@TestMetadata("overloadFunWithDefArg.kt")
public void testOverloadFunWithDefArg() throws Exception {
@@ -2602,6 +2608,12 @@ public class FirJsTestGenerated extends AbstractFirJsTest {
runTest("js/js.translator/testData/box/export/exportClassWithInternalOneFile.kt");
}
@Test
@TestMetadata("exportDefaultParameterAndOverrideIt.kt")
public void testExportDefaultParameterAndOverrideIt() throws Exception {
runTest("js/js.translator/testData/box/export/exportDefaultParameterAndOverrideIt.kt");
}
@Test
@TestMetadata("exportEnumClass.kt")
public void testExportEnumClass() throws Exception {
@@ -1284,6 +1284,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
runTest("js/js.translator/testData/box/defaultArguments/inheritViaAnotherInterfaceIndirectly.kt");
}
@Test
@TestMetadata("interfaceSuperCall.kt")
public void testInterfaceSuperCall() throws Exception {
runTest("js/js.translator/testData/box/defaultArguments/interfaceSuperCall.kt");
}
@Test
@TestMetadata("overloadFunWithDefArg.kt")
public void testOverloadFunWithDefArg() throws Exception {
@@ -2602,6 +2608,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
runTest("js/js.translator/testData/box/export/exportClassWithInternalOneFile.kt");
}
@Test
@TestMetadata("exportDefaultParameterAndOverrideIt.kt")
public void testExportDefaultParameterAndOverrideIt() throws Exception {
runTest("js/js.translator/testData/box/export/exportDefaultParameterAndOverrideIt.kt");
}
@Test
@TestMetadata("exportEnumClass.kt")
public void testExportEnumClass() throws Exception {
@@ -0,0 +1,28 @@
// EXPECTED_REACHABLE_NODES: 1299
package foo
interface A {
open fun foo(x: Int = 20, y: Int = 3) = x + y
}
open class B : A {
override fun foo(x: Int, y: Int) = 0
fun bar1() = super.foo()
fun bar2() = super.foo(30)
fun bar3() = super.foo(y = 4)
}
fun box(): String {
val v1 = B().bar1()
if (v1 != 23) return "fail1: $v1"
val v2 = B().bar2()
if (v2 != 33) return "fail2: $v2"
val v3 = B().bar3()
if (v3 != 24) return "fail3: $v3"
return "OK"
}
@@ -0,0 +1,82 @@
// EXPECTED_REACHABLE_NODES: 1252
// RUN_PLAIN_BOX_FUNCTION
// INFER_MAIN_MODULE
// SKIP_DCE_DRIVEN
// IGNORE_BACKEND: JS
// MODULE: export_default_method
// FILE: lib.kt
@JsExport
abstract class Adapter {
abstract fun method(o: String = "O", k: String = "K")
}
@JsExport
class TwoArgumentsWrapper(val adapter: Adapter) {
fun method(a: String, b: String) {
adapter.method(a, b)
}
}
@JsExport
class OneArgumentWrapper(val adapter: Adapter) {
fun method(param: String) {
adapter.method(param)
}
}
@JsExport
class NoArgumentsWrapper(val adapter: Adapter) {
fun method() {
adapter.method()
}
}
// FILE: test.js
function box() {
const Adapter = this.export_default_method.Adapter
const Wrapper2 = this.export_default_method.TwoArgumentsWrapper
const Wrapper1 = this.export_default_method.OneArgumentWrapper
const Wrapper0 = this.export_default_method.NoArgumentsWrapper
class WebAdapter extends Adapter {
constructor(name) {
super()
this.resetError(name)
}
method(o = "O", k = "K") {
this.result = o + k
}
resetError(name) {
this.result = `Error: ${name}'s overridden method was not called`
}
}
const webAdapter = new WebAdapter("webAdapter2")
const wrapper2 = new Wrapper2(webAdapter)
wrapper2.method("O", "K")
if (webAdapter.result !== "OK") return webAdapter.result
webAdapter.resetError("webAdapter1")
const wrapper1 = new Wrapper1(webAdapter)
wrapper1.method("O")
if (webAdapter.result !== "OK") return webAdapter.result
webAdapter.resetError("webAdapter0")
const wrapper0 = new Wrapper0(webAdapter)
wrapper0.method()
if (webAdapter.result !== "OK") return webAdapter.result
return "OK"
}
@@ -15,8 +15,7 @@ inline fun <reified T> boo() = "boo"
// MODULE: main(#my_libr@ry)
// MODULE_KIND: PLAIN
// FILE: box.kt
// CHECK_CONTAINS_NO_CALLS: box except=assertEquals TARGET_BACKENDS=JS
// CHECK_CONTAINS_NO_CALLS: box except=assertEquals$default IGNORED_BACKENDS=JS
// CHECK_CONTAINS_NO_CALLS: box except=assertEquals
fun box(): String {
assertEquals("foo", foo())
@@ -12,4 +12,4 @@ fun baz() = 1
fun bar() = 2
// LINES(JS): 1 3 3 2 2 4 3 4 4 4 5 5 2 8 8 10 10 10 12 12 12
// LINES(JS_IR): 8 8 * 1 3 3 1 4 5 1 1 * 10 10 * 12 12
// LINES(JS_IR): 1 3 3 4 5 1 * 8 8 * 10 10 * 12 12
+1 -1
View File
@@ -6,4 +6,4 @@ data class A(
// LINES(JS): 1 2 3 * 1 2 2 1 3 3 1 1 1 2 3 1 1 1 2 3 1 1 1 2 3 1 1 1 1 1 2 3
// FIXME: componentN function body should point to the corresponding property.
// LINES(JS_IR): 2 2 3 3 * 2 2 * 3 3 * 1 1 * 1 1 * 1 1 * 1 1 1 1 1 1 1 1 * 1 1 * 1 1 1 1 1 1 * 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
// LINES(JS_IR): 2 2 3 3 * 2 2 * 3 3 * 1 1 * 1 1 * 1 1 * 1 1 1 1 * 1 1 * 1 1 1 1 1 1 * 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
@@ -11,4 +11,4 @@ class B : A() {
}
// LINES(JS): 1 2 2 2 2 2 2 2 2 2 4 4 4 * 7 7 8 10 9 9
// LINES(JS_IR): 2 2 * 2 2 2 2 2 * 4 4 * 7 * 9 9
// LINES(JS_IR): 2 2 * 2 2 2 * 4 4 * 7 * 9 9
+1 -1
View File
@@ -8,4 +8,4 @@ fun box(
}
// LINES(JS): 1 8 2 2 2 2 3 3 3 4 6 6 7 7
// LINES(JS_IR): 1 1 2 1 4 1 * 6 6 7 7 * 1 1 2 1 1 4 1 1
// LINES(JS_IR): 1 2 1 1 4 1 6 6 7 7
+17 -5
View File
@@ -90,8 +90,8 @@ internal fun captureStack(instance: Throwable, constructorFunction: Any) {
internal fun newThrowable(message: String?, cause: Throwable?): Throwable {
val throwable = js("new Error()")
throwable.message = if (isUndefined(message)) {
if (isUndefined(cause)) message else cause?.toString() ?: undefined
} else message ?: undefined
if (isUndefined(cause)) message else cause?.toString() ?: VOID
} else message ?: VOID
throwable.cause = cause
throwable.name = "Throwable"
return throwable.unsafeCast<Throwable>()
@@ -109,10 +109,10 @@ internal fun setPropertiesToThrowableInstance(this_: dynamic, message: String?,
@Suppress("SENSELESS_COMPARISON")
if (message !== null) {
// undefined
cause?.toString() ?: undefined
cause?.toString() ?: VOID
} else {
// real null
undefined
VOID
}
} else message
}
@@ -135,7 +135,19 @@ internal fun errorCode(description: String): Nothing {
}
@Suppress("SENSELESS_COMPARISON")
internal fun isUndefined(value: dynamic): Boolean = value === undefined
internal fun isUndefined(value: dynamic): Boolean = value === VOID
internal fun <T, R> boxIntrinsic(@Suppress("UNUSED_PARAMETER") x: T): R = error("Should be lowered")
internal fun <T, R> unboxIntrinsic(@Suppress("UNUSED_PARAMETER") x: T): R = error("Should be lowered")
@Suppress("UNUSED_PARAMETER")
internal fun protoOf(constructor: Any) =
js("constructor.prototype")
@Suppress("UNUSED_PARAMETER")
internal fun <T> objectCreate(proto: T?) =
js("Object.create(proto)")
@Suppress("UNUSED_PARAMETER")
internal fun defineProp(obj: Any, name: String, getter: Any?, setter: Any?) =
js("Object.defineProperty(obj, name, { configurable: true, get: getter, set: setter })")
+10 -7
View File
@@ -18,6 +18,9 @@ internal fun jsEqeq(a: Any?, b: Any?): Boolean
@JsIntrinsic
internal fun jsNotEq(a: Any?, b: Any?): Boolean
@JsIntrinsic
internal fun jsUndefined(): Nothing?
@JsIntrinsic
internal fun jsEqeqeq(a: Any?, b: Any?): Boolean
@@ -172,9 +175,6 @@ internal fun float32ArrayOf(a: Any?): Any?
@JsIntrinsic
internal fun float64ArrayOf(a: Any?): Any?
@JsIntrinsic
internal fun <T> objectCreate(): T
@JsIntrinsic
internal fun <T> sharedBoxCreate(v: T?): dynamic
@@ -184,15 +184,15 @@ internal fun <T> sharedBoxRead(box: dynamic): T?
@JsIntrinsic
internal fun <T> sharedBoxWrite(box: dynamic, nv: T?)
@JsIntrinsic
internal fun jsUndefined(): Nothing?
@JsIntrinsic
internal fun <T> DefaultType(): T
@JsIntrinsic
internal fun jsBind(receiver: Any?, target: Any?): Any?
@JsIntrinsic
internal fun jsCall(receiver: Any?, target: Any?, vararg args: Any?): Any?
@JsIntrinsic
internal fun <A> slice(a: A): A
@@ -220,4 +220,7 @@ internal fun <reified T : Any> jsClassIntrinsic(): JsClass<T>
internal fun jsInIntrinsic(lhs: Any?, rhs: Any): Boolean
@JsIntrinsic
internal fun jsDelete(e: Any?)
internal fun jsDelete(e: Any?)
@JsIntrinsic
internal fun jsContextfulRef(context: dynamic, fn: dynamic): dynamic
@@ -44,8 +44,7 @@ private fun getKPropMetadata(paramCount: Int, setter: Any?): dynamic {
}
private fun metadataObject(): Metadata {
val undef = js("undefined")
return classMeta(undef, undef, undef, undef)
return classMeta(VOID, VOID, VOID, VOID)
}
private val propertyRefClassMetadataCache: Array<Array<dynamic>> = arrayOf<Array<dynamic>>(
@@ -44,7 +44,7 @@ internal fun classMeta(name: String?, associatedObjectKey: Number?, associatedOb
}
// Seems like we need to disable this check if variables are used inside js annotation
@Suppress("UNUSED_PARAMETER")
@Suppress("UNUSED_PARAMETER", "UNUSED_VARIABLE")
private fun createMetadata(
kind: String,
name: String?,
@@ -53,13 +53,14 @@ private fun createMetadata(
suspendArity: Array<Int>?,
iid: Int?
): Metadata {
val undef = VOID
return js("""({
kind: kind,
simpleName: name,
associatedObjectKey: associatedObjectKey,
associatedObjects: associatedObjects,
suspendArity: suspendArity,
${'$'}kClass$: undefined,
${'$'}kClass$: undef,
iid: iid
})""")
}
+7
View File
@@ -0,0 +1,7 @@
/*
* 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 kotlin.js
internal val VOID: Nothing? = js("void 0")