From 73e7053c3562f2e8dc0aeac608d1b00b2988ff43 Mon Sep 17 00:00:00 2001 From: Artem Kobzar Date: Mon, 19 Sep 2022 17:49:04 +0000 Subject: [PATCH] [K/JS] Use only single variant of default arguments function wrapper for exported and not-exported functions --- .../lower/DefaultArgumentFunctionFactory.kt | 209 ++++++++++++ .../lower/DefaultArgumentStubGenerator.kt | 281 +++++----------- .../ir/backend/js/DeclarationOrigins.kt | 1 + .../kotlin/ir/backend/js/JsIntrinsics.kt | 11 +- .../kotlin/ir/backend/js/JsLoweringPhases.kt | 18 +- .../kotlin/ir/backend/js/codegen/IrToJs.kt | 2 +- .../jetbrains/kotlin/ir/backend/js/dce/Dce.kt | 2 + .../js/dce/JsUsefulDeclarationProcessor.kt | 30 +- .../js/dce/UsefulDeclarationProcessor.kt | 9 +- .../backend/js/export/ExportModelGenerator.kt | 11 +- .../js/export/ExportModelToJsStatements.kt | 24 +- .../kotlin/ir/backend/js/ir/IrBuilder.kt | 3 + .../backend/js/lower/AutoboxingTransformer.kt | 10 +- .../js/lower/ExportedDefaultParameterStub.kt | 154 --------- .../lower/JsDefaultArgumentFunctionFactory.kt | 38 +++ .../lower/JsDefaultArgumentStubGenerator.kt | 300 +++++++++++++----- .../js/lower/JsDefaultParameterInjector.kt | 113 +++++++ .../lower/JsPropertyAccessorInlineLowering.kt | 5 + ...MoveBodilessDeclarationsToSeparatePlace.kt | 5 +- .../backend/js/lower/SecondaryCtorLowering.kt | 2 +- .../ir/backend/js/lower/ThrowableLowering.kt | 5 +- .../irToJs/IrModuleToJsTransformer.kt | 2 +- .../transformers/irToJs/JsClassGenerator.kt | 9 +- .../irToJs/JsIntrinsicTransformers.kt | 32 +- .../js/transformers/irToJs/jsAstUtils.kt | 81 +++-- .../kotlin/ir/backend/js/utils/IrJsUtils.kt | 19 ++ .../kotlin/ir/backend/js/utils/NameTables.kt | 5 +- .../kotlin/ir/backend/js/utils/Namer.kt | 5 - .../kotlin/ir/backend/js/utils/misc.kt | 1 - .../kotlin/ir/builders/ExpressionHelpers.kt | 15 +- .../jetbrains/kotlin/ir/builders/IrBuilder.kt | 4 + .../ir/expressions/IrStatementOrigin.kt | 4 + .../kotlin/ir/backend/js/StatementOrigins.kt | 1 + .../codegen/box/strings/trimOptimization.kt | 6 +- .../debug/stepping/callWithReceiver.kt | 4 +- compiler/testData/debug/stepping/chainCall.kt | 4 +- .../testData/debug/stepping/classObject.kt | 2 +- compiler/testData/debug/stepping/dataClass.kt | 44 ++- .../debug/stepping/defaultParameter.kt | 9 +- compiler/testData/debug/stepping/enum.kt | 2 +- .../debug/stepping/functionCallWithDefault.kt | 4 +- .../debug/stepping/propertyAccessor.kt | 2 +- .../debug/stepping/simpleDefaultArg.kt | 5 +- .../stepping/suspendFunWithLambdaParameter.kt | 2 +- .../kotlin/js/test/BoxJsTestGenerated.java | 12 + .../kotlin/js/test/ir/FirJsTestGenerated.java | 12 + .../js/test/ir/IrBoxJsTestGenerated.java | 12 + .../defaultArguments/interfaceSuperCall.kt | 28 ++ .../exportDefaultParameterAndOverrideIt.kt | 82 +++++ .../inlineFromModuleWithNonIdentifierName.kt | 3 +- .../complexExpressionAsDefaultArgument.kt | 2 +- .../testData/lineNumbers/dataClass.kt | 2 +- .../lineNumbers/memberFunWithDefaultParam.kt | 2 +- .../testData/lineNumbers/optionalArgs.kt | 2 +- libraries/stdlib/js-ir/runtime/coreRuntime.kt | 22 +- .../stdlib/js-ir/runtime/jsIntrinsics.kt | 17 +- .../stdlib/js-ir/runtime/reflectRuntime.kt | 3 +- .../stdlib/js-ir/runtime/typeCheckUtils.kt | 5 +- libraries/stdlib/js-ir/runtime/void.kt | 7 + 59 files changed, 1109 insertions(+), 597 deletions(-) create mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentFunctionFactory.kt delete mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ExportedDefaultParameterStub.kt create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultArgumentFunctionFactory.kt create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultParameterInjector.kt create mode 100644 js/js.translator/testData/box/defaultArguments/interfaceSuperCall.kt create mode 100644 js/js.translator/testData/box/export/exportDefaultParameterAndOverrideIt.kt create mode 100644 libraries/stdlib/js-ir/runtime/void.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentFunctionFactory.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentFunctionFactory.kt new file mode 100644 index 00000000000..1cb98be05fa --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentFunctionFactory.kt @@ -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() + + 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, + ): 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, + 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 + } + } +} diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt index 8c60e0e3081..b28fd2eab59 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/DefaultArgumentStubGenerator.kt @@ -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 = copyAnnotations() - private fun lower(irFunction: IrFunction): List? { - 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() val variables = mutableMapOf() - 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? { + 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() - - 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 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>? { + open protected fun parametersForCall(expression: IrFunctionAccessExpression): Pair>? { 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 -): 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, - 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() = diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/DeclarationOrigins.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/DeclarationOrigins.kt index 52461ff417e..c73718c9549 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/DeclarationOrigins.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/DeclarationOrigins.kt @@ -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") } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt index fba07727ac5..2a3695f8db9 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -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("") 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) = diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 2b22aa49d89..09f1ead9517 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -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( computeStringTrimPhase, privateMembersLoweringPhase, privateMemberUsagesLoweringPhase, - exportedDefaultParameterStubPhase, defaultArgumentStubGeneratorPhase, defaultArgumentPatchOverridesPhase, defaultParameterInjectorPhase, defaultParameterCleanerPhase, - jsDefaultCallbackGeneratorPhase, throwableSuccessorsLoweringPhase, es6AddInternalParametersToConstructorPhase, es6ConstructorLowering, diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/codegen/IrToJs.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/codegen/IrToJs.kt index 63695556ea3..04b074d1a6b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/codegen/IrToJs.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/codegen/IrToJs.kt @@ -239,7 +239,7 @@ class IrToJs( val globalNames = NameTable(nameGenerator.staticNames) val exporter = ExportModelToJsStatements( - nameGenerator, + staticContext, declareNewNamespace = { globalNames.declareFreshName(it, it) } ) exportedDeclarations.forEach { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/Dce.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/Dce.kt index fe8022c5516..302175746bb 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/Dce.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/Dce.kt @@ -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, @@ -116,6 +117,7 @@ private fun buildRoots(modules: Iterable, context: JsIrBackend } } + addIfNotNull(context.intrinsics.void.owner.backingField) addAll(context.testFunsPerFile.values) addAll(context.additionalExportedDeclarations) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt index 73c15b0fe9d..c7c0b9f3793 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/JsUsefulDeclarationProcessor.kt @@ -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 = diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt index 3636b7be166..fda87c49c34 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/dce/UsefulDeclarationProcessor.kt @@ -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): Set { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt index 5d9bcb55d7f..f444d95176d 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt @@ -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): 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 diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt index a0e1bc227bf..61bdd779b6a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt @@ -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() @@ -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() } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt index be0eedac161..daae0641d0c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt @@ -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) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt index ad192a5e840..f8362f027fd 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/AutoboxingTransformer.kt @@ -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() } } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ExportedDefaultParameterStub.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ExportedDefaultParameterStub.kt deleted file mode 100644 index b84312bb1e0..00000000000 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ExportedDefaultParameterStub.kt +++ /dev/null @@ -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() - - 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? { - 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) - } -} - diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultArgumentFunctionFactory.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultArgumentFunctionFactory.kt new file mode 100644 index 00000000000..36f278669b7 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultArgumentFunctionFactory.kt @@ -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 + } + +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultArgumentStubGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultArgumentStubGenerator.kt index 7532c628cbb..ea5a8955df4 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultArgumentStubGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultArgumentStubGenerator.kt @@ -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 = copyAnnotationsWhen { - !(isAnnotation(JsAnnotations.jsExportFqn) || isAnnotation(JsAnnotations.jsNameFqn)) - } - - override fun IrBlockBodyBuilder.generateHandleCall( - handlerDeclaration: IrValueParameter, - oldIrFunction: IrFunction, - newIrFunction: IrFunction, - params: MutableList - ): 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().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() - 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? { + 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().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 } } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultParameterInjector.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultParameterInjector.kt new file mode 100644 index 00000000000..32e2de67061 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsDefaultParameterInjector.kt @@ -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>? { + 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 +} + diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsPropertyAccessorInlineLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsPropertyAccessorInlineLowering.kt index 165d6628d86..b229bfb22ea 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsPropertyAccessorInlineLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsPropertyAccessorInlineLowering.kt @@ -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 diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MoveBodilessDeclarationsToSeparatePlace.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MoveBodilessDeclarationsToSeparatePlace.kt index 39aded1478d..2a6e7ef615d 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MoveBodilessDeclarationsToSeparatePlace.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MoveBodilessDeclarationsToSeparatePlace.kt @@ -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) { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/SecondaryCtorLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/SecondaryCtorLowering.kt index e76c08a57a1..b4cae0aa581 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/SecondaryCtorLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/SecondaryCtorLowering.kt @@ -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) { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableLowering.kt index 189894afe56..a8898947e26 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ThrowableLowering.kt @@ -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, diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt index 86103e872b4..4d42c329696 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt @@ -158,7 +158,7 @@ class IrModuleToJsTransformer( val moduleBody = generateModuleBody(modules, staticContext) val internalModuleName = ReservedJsNames.makeInternalModuleName() val globalNames = NameTable(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) }) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt index 89f9908d609..f225dd0810f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt @@ -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() } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt index 8479106fba0..5fa616c2e8d 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsIntrinsicTransformers.kt @@ -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 { - return translateCallArguments(expression, context, IrElementToJsExpressionTransformer()) + return translateCallArguments(expression, context, IrElementToJsExpressionTransformer(), false) } private fun MutableMap.add(functionSymbol: IrSymbol, t: IrCallTransformer) { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt index ff158d58c24..772a0963554 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt @@ -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, context: JsGenerationContext, transformer: IrElementToJsExpressionTransformer, + allowDropTailVoids: Boolean = true ): List { 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 { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrJsUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrJsUtils.kt index 1c8b7ed579b..597950a5219 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrJsUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/IrJsUtils.kt @@ -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 + ) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt index 5e9cb1b396d..6cd9d7e5614 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt @@ -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 diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt index ab5e228e1e9..b34353986d1 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt @@ -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 = "" val KCALLABLE_NAME = "callableName" diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/misc.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/misc.kt index 56f586607f3..16549151f83 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/misc.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/misc.kt @@ -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 ) { diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt index 2d9a28ed9a8..f3f13fb9785 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt @@ -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) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/IrBuilder.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/IrBuilder.kt index e5b71ff03d2..cd81a792fde 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/IrBuilder.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/IrBuilder.kt @@ -54,6 +54,10 @@ abstract class IrStatementsBuilder( addStatement(this) } + operator fun List.unaryPlus() { + forEach(::addStatement) + } + protected abstract fun addStatement(irStatement: IrStatement) abstract fun doBuild(): T } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt index d69ec7fc4d9..e2e7536d285 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt @@ -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) } diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/StatementOrigins.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/StatementOrigins.kt index 2aa19dcb08e..213090af6a8 100644 --- a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/StatementOrigins.kt +++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/StatementOrigins.kt @@ -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") } \ No newline at end of file diff --git a/compiler/testData/codegen/box/strings/trimOptimization.kt b/compiler/testData/codegen/box/strings/trimOptimization.kt index 6aa5540115d..d9232e00cf5 100644 --- a/compiler/testData/codegen/box/strings/trimOptimization.kt +++ b/compiler/testData/codegen/box/strings/trimOptimization.kt @@ -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, diff --git a/compiler/testData/debug/stepping/callWithReceiver.kt b/compiler/testData/debug/stepping/callWithReceiver.kt index d6f117af1d1..bc7e2f01d2b 100644 --- a/compiler/testData/debug/stepping/callWithReceiver.kt +++ b/compiler/testData/debug/stepping/callWithReceiver.kt @@ -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 \ No newline at end of file diff --git a/compiler/testData/debug/stepping/chainCall.kt b/compiler/testData/debug/stepping/chainCall.kt index fdd5ab778c0..cf06021120c 100644 --- a/compiler/testData/debug/stepping/chainCall.kt +++ b/compiler/testData/debug/stepping/chainCall.kt @@ -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 diff --git a/compiler/testData/debug/stepping/classObject.kt b/compiler/testData/debug/stepping/classObject.kt index 9d81af6854e..bbb0295d934 100644 --- a/compiler/testData/debug/stepping/classObject.kt +++ b/compiler/testData/debug/stepping/classObject.kt @@ -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$ \ No newline at end of file diff --git a/compiler/testData/debug/stepping/dataClass.kt b/compiler/testData/debug/stepping/dataClass.kt index cafc7cd6120..ebe9643f749 100644 --- a/compiler/testData/debug/stepping/dataClass.kt +++ b/compiler/testData/debug/stepping/dataClass.kt @@ -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 diff --git a/compiler/testData/debug/stepping/defaultParameter.kt b/compiler/testData/debug/stepping/defaultParameter.kt index 512f0c8b0e2..ec28930b762 100644 --- a/compiler/testData/debug/stepping/defaultParameter.kt +++ b/compiler/testData/debug/stepping/defaultParameter.kt @@ -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$ \ No newline at end of file diff --git a/compiler/testData/debug/stepping/enum.kt b/compiler/testData/debug/stepping/enum.kt index bb8934c046b..b0958718d3e 100644 --- a/compiler/testData/debug/stepping/enum.kt +++ b/compiler/testData/debug/stepping/enum.kt @@ -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 diff --git a/compiler/testData/debug/stepping/functionCallWithDefault.kt b/compiler/testData/debug/stepping/functionCallWithDefault.kt index ed5667b2e7b..95641fe6310 100644 --- a/compiler/testData/debug/stepping/functionCallWithDefault.kt +++ b/compiler/testData/debug/stepping/functionCallWithDefault.kt @@ -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 \ No newline at end of file diff --git a/compiler/testData/debug/stepping/propertyAccessor.kt b/compiler/testData/debug/stepping/propertyAccessor.kt index f08a3da2fa9..27937b4de41 100644 --- a/compiler/testData/debug/stepping/propertyAccessor.kt +++ b/compiler/testData/debug/stepping/propertyAccessor.kt @@ -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$ \ No newline at end of file diff --git a/compiler/testData/debug/stepping/simpleDefaultArg.kt b/compiler/testData/debug/stepping/simpleDefaultArg.kt index 59815af1241..ef86affd641 100644 --- a/compiler/testData/debug/stepping/simpleDefaultArg.kt +++ b/compiler/testData/debug/stepping/simpleDefaultArg.kt @@ -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 \ No newline at end of file diff --git a/compiler/testData/debug/stepping/suspendFunWithLambdaParameter.kt b/compiler/testData/debug/stepping/suspendFunWithLambdaParameter.kt index bea9ad99ffb..bad906369f6 100644 --- a/compiler/testData/debug/stepping/suspendFunWithLambdaParameter.kt +++ b/compiler/testData/debug/stepping/suspendFunWithLambdaParameter.kt @@ -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 diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java index dec9edc11fd..099cc5c62ab 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java @@ -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 { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsTestGenerated.java index 7402db3f8c3..d8388a77aa0 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/FirJsTestGenerated.java @@ -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 { diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java index b0081e47b14..3e474f90a51 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java @@ -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 { diff --git a/js/js.translator/testData/box/defaultArguments/interfaceSuperCall.kt b/js/js.translator/testData/box/defaultArguments/interfaceSuperCall.kt new file mode 100644 index 00000000000..ba6ae1bca2a --- /dev/null +++ b/js/js.translator/testData/box/defaultArguments/interfaceSuperCall.kt @@ -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" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/export/exportDefaultParameterAndOverrideIt.kt b/js/js.translator/testData/box/export/exportDefaultParameterAndOverrideIt.kt new file mode 100644 index 00000000000..c6123b14df3 --- /dev/null +++ b/js/js.translator/testData/box/export/exportDefaultParameterAndOverrideIt.kt @@ -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" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/multiModuleWrappers/plain/inlineFromModuleWithNonIdentifierName.kt b/js/js.translator/testData/box/multiModuleWrappers/plain/inlineFromModuleWithNonIdentifierName.kt index f2195461b5f..ad20e788c17 100644 --- a/js/js.translator/testData/box/multiModuleWrappers/plain/inlineFromModuleWithNonIdentifierName.kt +++ b/js/js.translator/testData/box/multiModuleWrappers/plain/inlineFromModuleWithNonIdentifierName.kt @@ -15,8 +15,7 @@ inline fun 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()) diff --git a/js/js.translator/testData/lineNumbers/complexExpressionAsDefaultArgument.kt b/js/js.translator/testData/lineNumbers/complexExpressionAsDefaultArgument.kt index d00283717c1..3f8b40f3ce5 100644 --- a/js/js.translator/testData/lineNumbers/complexExpressionAsDefaultArgument.kt +++ b/js/js.translator/testData/lineNumbers/complexExpressionAsDefaultArgument.kt @@ -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 diff --git a/js/js.translator/testData/lineNumbers/dataClass.kt b/js/js.translator/testData/lineNumbers/dataClass.kt index 5664598c477..11e57507183 100644 --- a/js/js.translator/testData/lineNumbers/dataClass.kt +++ b/js/js.translator/testData/lineNumbers/dataClass.kt @@ -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 diff --git a/js/js.translator/testData/lineNumbers/memberFunWithDefaultParam.kt b/js/js.translator/testData/lineNumbers/memberFunWithDefaultParam.kt index b52a3dc8c50..1f9a95c55a9 100644 --- a/js/js.translator/testData/lineNumbers/memberFunWithDefaultParam.kt +++ b/js/js.translator/testData/lineNumbers/memberFunWithDefaultParam.kt @@ -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 diff --git a/js/js.translator/testData/lineNumbers/optionalArgs.kt b/js/js.translator/testData/lineNumbers/optionalArgs.kt index 5a29b3e15b0..2401964d7b5 100644 --- a/js/js.translator/testData/lineNumbers/optionalArgs.kt +++ b/js/js.translator/testData/lineNumbers/optionalArgs.kt @@ -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 diff --git a/libraries/stdlib/js-ir/runtime/coreRuntime.kt b/libraries/stdlib/js-ir/runtime/coreRuntime.kt index e9bd9d36d0c..e2e43d251b4 100644 --- a/libraries/stdlib/js-ir/runtime/coreRuntime.kt +++ b/libraries/stdlib/js-ir/runtime/coreRuntime.kt @@ -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() @@ -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 boxIntrinsic(@Suppress("UNUSED_PARAMETER") x: T): R = error("Should be lowered") internal fun 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 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 })") diff --git a/libraries/stdlib/js-ir/runtime/jsIntrinsics.kt b/libraries/stdlib/js-ir/runtime/jsIntrinsics.kt index adb027e3c60..78dff7c23e4 100644 --- a/libraries/stdlib/js-ir/runtime/jsIntrinsics.kt +++ b/libraries/stdlib/js-ir/runtime/jsIntrinsics.kt @@ -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 objectCreate(): T - @JsIntrinsic internal fun sharedBoxCreate(v: T?): dynamic @@ -184,15 +184,15 @@ internal fun sharedBoxRead(box: dynamic): T? @JsIntrinsic internal fun sharedBoxWrite(box: dynamic, nv: T?) -@JsIntrinsic -internal fun jsUndefined(): Nothing? - @JsIntrinsic internal fun 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 slice(a: A): A @@ -220,4 +220,7 @@ internal fun jsClassIntrinsic(): JsClass internal fun jsInIntrinsic(lhs: Any?, rhs: Any): Boolean @JsIntrinsic -internal fun jsDelete(e: Any?) \ No newline at end of file +internal fun jsDelete(e: Any?) + +@JsIntrinsic +internal fun jsContextfulRef(context: dynamic, fn: dynamic): dynamic \ No newline at end of file diff --git a/libraries/stdlib/js-ir/runtime/reflectRuntime.kt b/libraries/stdlib/js-ir/runtime/reflectRuntime.kt index a74d3b8b1cc..fce9cdd6497 100644 --- a/libraries/stdlib/js-ir/runtime/reflectRuntime.kt +++ b/libraries/stdlib/js-ir/runtime/reflectRuntime.kt @@ -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> = arrayOf>( diff --git a/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt b/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt index 01695a315e1..3f79d64e1b3 100644 --- a/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt +++ b/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt @@ -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?, iid: Int? ): Metadata { + val undef = VOID return js("""({ kind: kind, simpleName: name, associatedObjectKey: associatedObjectKey, associatedObjects: associatedObjects, suspendArity: suspendArity, - ${'$'}kClass$: undefined, + ${'$'}kClass$: undef, iid: iid })""") } diff --git a/libraries/stdlib/js-ir/runtime/void.kt b/libraries/stdlib/js-ir/runtime/void.kt new file mode 100644 index 00000000000..710658e9465 --- /dev/null +++ b/libraries/stdlib/js-ir/runtime/void.kt @@ -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") \ No newline at end of file