diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt index 5f3ef20e285..e606437899c 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt @@ -229,6 +229,14 @@ abstract class Symbols(val context: T, private val open fun functionN(n: Int): IrClassSymbol = symbolTable.referenceClass(builtIns.getFunction(n)) open fun suspendFunctionN(n: Int): IrClassSymbol = symbolTable.referenceClass(builtIns.getSuspendFunction(n)) + fun kproperty0(): IrClassSymbol = symbolTable.referenceClass(builtIns.kProperty0) + fun kproperty1(): IrClassSymbol = symbolTable.referenceClass(builtIns.kProperty1) + fun kproperty2(): IrClassSymbol = symbolTable.referenceClass(builtIns.kProperty2) + + fun kmutableproperty0(): IrClassSymbol = symbolTable.referenceClass(builtIns.kMutableProperty0) + fun kmutableproperty1(): IrClassSymbol = symbolTable.referenceClass(builtIns.kMutableProperty1) + fun kmutableproperty2(): IrClassSymbol = symbolTable.referenceClass(builtIns.kMutableProperty2) + val extensionToString = getSimpleFunction(Name.identifier("toString")) { it.dispatchReceiverParameter == null && it.extensionReceiverParameter != null && KotlinBuiltIns.isNullableAny(it.extensionReceiverParameter!!.type) && it.valueParameters.size == 0 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 b779fbade36..6dd58663940 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 @@ -377,12 +377,14 @@ private val objectUsageLoweringPhase = makeCustomJsModulePhase( val jsPhases = namedIrModulePhase( name = "IrModuleLowering", description = "IR module lowering", - lower = validateIrBeforeLowering then + lower = scriptRemoveReceiverLowering then + validateIrBeforeLowering then testGenerationPhase then expectDeclarationsRemovingPhase then stripTypeAliasDeclarationsPhase then arrayConstructorPhase then functionInliningPhase then + createScriptFunctionsPhase then provisionalFunctionExpressionPhase then lateinitLoweringPhase then tailrecLoweringPhase then diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CreateScriptFunctionsPhase.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CreateScriptFunctionsPhase.kt new file mode 100644 index 00000000000..3a0941bd2b9 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CreateScriptFunctionsPhase.kt @@ -0,0 +1,141 @@ +/* + * 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.ir.backend.js.lower + +import org.jetbrains.kotlin.backend.common.CommonBackendContext +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor +import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrSetField +import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl +import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.util.transformFlat +import org.jetbrains.kotlin.name.Name + +val createScriptFunctionsPhase = makeIrModulePhase( + ::CreateScriptFunctionsPhase, + name = "CreateScriptFunctionsPhase", + description = "Create functions for initialize and evaluate script" +) + +private object SCRIPT_FUNCTION : IrDeclarationOriginImpl("SCRIPT_FUNCTION") + +class CreateScriptFunctionsPhase(val context: CommonBackendContext) : FileLoweringPass { + override fun lower(irFile: IrFile) { + irFile.declarations.transformFlat { declaration -> + if (declaration is IrScript) lower(declaration) + else null + } + } + + private fun lower(irScript: IrScript): List { + val (startOffset, endOffset) = getFunctionBodyOffsets(irScript) + + val initializeStatements = irScript.declarations + .filterIsInstance() + .mapNotNull { it.backingField } + .filter { it.initializer != null } + .map { Pair(it, it.initializer!!.expression) } + + initializeStatements.forEach { it.first.initializer = null } + + val initializeScriptFunction = createFunction(irScript, "\$initializeScript\$", context.irBuiltIns.unitType).also { + it.body = IrBlockBodyImpl( + startOffset, + endOffset, + initializeStatements.map { (field, expression) -> createIrSetField(field, expression) } + ) + } + + val evaluateScriptFunction = createFunction(irScript, "\$evaluateScript\$", getReturnType(irScript)).also { + it.body = IrBlockBodyImpl( + startOffset, + endOffset, + irScript.statements.prepareForEvaluateScriptFunction(it) + ) + } + + with(irScript) { + declarations += initializeScriptFunction + declarations += evaluateScriptFunction + statements.clear() + statements += createCall(initializeScriptFunction) + statements += createCall(evaluateScriptFunction) + } + + return listOf(irScript) + } + + private fun getFunctionBodyOffsets(irScript: IrScript): Pair { + return with(irScript.statements) { + if (isEmpty()) { + Pair(UNDEFINED_OFFSET, UNDEFINED_OFFSET) + } else { + Pair(irScript.statements.first().startOffset, irScript.statements.last().endOffset) + } + } + } + + private fun getReturnType(irScript: IrScript): IrType { + return (irScript.statements.lastOrNull() as? IrExpression)?.type ?: context.irBuiltIns.unitType + } + + private fun createFunction(irScript: IrScript, name: String, returnType: IrType): IrFunctionImpl { + val (startOffset, endOffset) = getFunctionBodyOffsets(irScript) + val descriptor = WrappedSimpleFunctionDescriptor() + + return IrFunctionImpl( + startOffset, endOffset, SCRIPT_FUNCTION, + IrSimpleFunctionSymbolImpl(descriptor), + Name.identifier(name), + Visibilities.PRIVATE, Modality.FINAL, returnType, + isInline = false, isExternal = false, isTailrec = false, isSuspend = false + ).also { + descriptor.bind(it) + it.parent = irScript + } + } + + private fun List.prepareForEvaluateScriptFunction(evaluateScriptFunction: IrFunction): List { + return if (isNotEmpty()) { + val returnStatement = IrReturnImpl( + last().startOffset, + last().endOffset, + context.irBuiltIns.nothingType, + evaluateScriptFunction.symbol, + last() as IrExpression + ) + dropLast(1) + returnStatement + } else emptyList() + } + + private fun createIrSetField(field: IrField, expression: IrExpression): IrSetField { + return IrSetFieldImpl( + field.startOffset, + field.endOffset, + field.symbol, + null, + expression, + expression.type + ) + } + + private fun createCall(function: IrFunction): IrCall { + return IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, function.returnType, function.symbol) + } +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ScriptRemoveReceiverLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ScriptRemoveReceiverLowering.kt new file mode 100644 index 00000000000..3af60c01490 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ScriptRemoveReceiverLowering.kt @@ -0,0 +1,164 @@ +/* + * 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.ir.backend.js.lower + +import org.jetbrains.kotlin.backend.common.CommonBackendContext +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase +import org.jetbrains.kotlin.descriptors.ScriptDescriptor +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrPropertyReferenceImpl +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.types.IrSimpleType +import org.jetbrains.kotlin.ir.types.IrTypeProjection +import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl +import org.jetbrains.kotlin.ir.util.transformFlat +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import java.lang.IllegalArgumentException + +val scriptRemoveReceiverLowering = makeIrModulePhase( + ::ScriptRemoveReceiverLowering, + name = "ScriptRemoveReceiver", + description = "Remove receivers for declarations in script" +) + +private class ScriptRemoveReceiverLowering(val context: CommonBackendContext) : FileLoweringPass { + override fun lower(irFile: IrFile) { + if (context.scriptMode) { + irFile.declarations.transformFlat { + if (it is IrScript) { + lower(it) + } else null + } + } + } + + fun lower(script: IrScript): List { + val transformer: IrElementTransformerVoid = object : IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression { + if (expression.symbol.owner.parent is IrScript) { + expression.dispatchReceiver = null + } + return super.visitCall(expression) + } + + override fun visitFieldAccess(expression: IrFieldAccessExpression): IrExpression { + if (expression.symbol.owner.parent is IrScript) { + expression.receiver = null + } + return super.visitFieldAccess(expression) + } + + override fun visitFunctionReference(expression: IrFunctionReference): IrExpression { + expression.transformChildrenVoid(this) + + if (expression.symbol.owner.parent is IrScript) { + expression.dispatchReceiver = null + + val result = with(super.visitFunctionReference(expression) as IrFunctionReference) { + val arguments = (type as IrSimpleType).arguments.filter { + !(it is IrTypeProjection && it.type is IrSimpleType && (it.type as IrSimpleType).classifier.descriptor is ScriptDescriptor) + } + IrFunctionReferenceImpl( + startOffset, + endOffset, + IrSimpleTypeImpl( + context.ir.symbols.functionN(arguments.size), + (type as IrSimpleType).hasQuestionMark, + arguments, + type.annotations + ), + symbol, + descriptor, + typeArgumentsCount, + valueArgumentsCount, + origin + ).also { + it.dispatchReceiver = dispatchReceiver + it.extensionReceiver = extensionReceiver + } + } + + return result + } + return expression + } + + override fun visitPropertyReference(expression: IrPropertyReference): IrExpression { + expression.transformChildrenVoid(this) + + + if (expression.symbol.owner.parent is IrScript) { + expression.dispatchReceiver = null + + val result = with(super.visitPropertyReference(expression) as IrPropertyReference) { + val arguments = (type as IrSimpleType).arguments.filter { + !(it is IrTypeProjection && it.type is IrSimpleType && (it.type as IrSimpleType).classifier.descriptor is ScriptDescriptor) + } + IrPropertyReferenceImpl( + startOffset, + endOffset, + IrSimpleTypeImpl( + (if (setter == null) getPropertyN(arguments.size) else getMutablePropertyN(arguments.size)), + (type as IrSimpleType).hasQuestionMark, + arguments, + type.annotations + ), + symbol, + descriptor, + typeArgumentsCount, + field, + getter, + setter, + origin + ).also { + it.dispatchReceiver = dispatchReceiver + it.extensionReceiver = extensionReceiver + } + } + + return result + } + return expression + } + + private fun getPropertyN(n: Int): IrClassSymbol { + return when (n) { + 2 -> context.ir.symbols.kproperty2() + 1 -> context.ir.symbols.kproperty1() + 0 -> context.ir.symbols.kproperty0() + else -> throw IllegalArgumentException() + } + } + + private fun getMutablePropertyN(n: Int): IrClassSymbol { + return when (n) { + 2 -> context.ir.symbols.kmutableproperty2() + 1 -> context.ir.symbols.kmutableproperty1() + 0 -> context.ir.symbols.kmutableproperty0() + else -> throw IllegalArgumentException() + } + } + } + + script.transformChildrenVoid(transformer) + + script.declarations.forEach { + when (it) { + is IrSimpleFunction -> it.dispatchReceiverParameter = null + is IrProperty -> { + it.getter?.dispatchReceiverParameter = null + it.setter?.dispatchReceiverParameter = null + } + } + } + + return listOf(script) + } +}