diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt index 7b643930b90..df82b3e4040 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classOrNull +import org.jetbrains.kotlin.ir.types.isArray import org.jetbrains.kotlin.ir.types.isSubtypeOf import org.jetbrains.kotlin.ir.util.* import java.lang.invoke.MethodHandle @@ -323,6 +324,30 @@ class IrInterpreter(irModule: IrModuleFragment) { return Wrapper.getConstructorMethod(owner).invokeMethod(owner, newFrame).apply { data.pushReturnValue(newFrame) } } + if (parent.defaultType.isArray() || parent.defaultType.isPrimitiveArray()) { + // array constructor doesn't have body so must be treated separately + // here we must except only one possible constructor, with one value parameter size + // the other one, with lambda, is transforming into ir returnable block + val arrayConstructor = irBuiltIns.primitiveArrays.first().constructors.single { it.owner.valueParameters.size == 2 } + val sizeDescriptor = arrayConstructor.owner.valueParameters.single { it.name.asString() == "size" }.descriptor + val size = (newFrame.getVariableState(sizeDescriptor) as Primitive).value + + val array = when (parent.defaultType.getFqName()) { + "kotlin.ByteArray" -> Primitive(ByteArray(size), parent.defaultType) + "kotlin.CharArray" -> Primitive(CharArray(size), parent.defaultType) + "kotlin.ShortArray" -> Primitive(ShortArray(size), parent.defaultType) + "kotlin.IntArray" -> Primitive(IntArray(size), parent.defaultType) + "kotlin.LongArray" -> Primitive(LongArray(size), parent.defaultType) + "kotlin.FloatArray" -> Primitive(FloatArray(size), parent.defaultType) + "kotlin.DoubleArray" -> Primitive(DoubleArray(size), parent.defaultType) + "kotlin.BooleanArray" -> Primitive(BooleanArray(size), parent.defaultType) + else -> throw AssertionError("Unsupported array class ${parent.defaultType.getFqName()}") + } + + data.pushReturnValue(array) + return Code.NEXT + } + val state = Common(parent, mutableListOf()) newFrame.addVar(Variable(constructorCall.getThisAsReceiver(), state)) //used to set up fields in body val code = constructorCall.getBody()?.interpret(newFrame) ?: Code.NEXT @@ -333,8 +358,8 @@ class IrInterpreter(irModule: IrModuleFragment) { private suspend fun interpretConstructorCall(constructorCall: IrConstructorCall, data: Frame): Code { return interpretConstructor(constructorCall, data).apply { - val instance = data.peekReturnValue() as Complex - instance.setInstanceRecursive(instance) + // constructor can return primitive object; fot example, when create array by constructor + (data.peekReturnValue() as? Complex)?.let { it.setInstanceRecursive(it) } } } @@ -372,17 +397,26 @@ class IrInterpreter(irModule: IrModuleFragment) { return Code.NEXT } - val inlineFunOwner = (block as? IrReturnableBlock)?.inlineFunctionSymbol?.owner - if (inlineFunOwner?.hasAnnotation(evaluateIntrinsicAnnotation) == true) { - val rawArgs = block.statements.filterIsInstance().zip(inlineFunOwner.valueParameters) - val args = mutableListOf() - for ((variable, valueParameter) in rawArgs) { - val tempFrame = data.copy().apply { variable.initializer!!.interpret(this).also { if (it != Code.NEXT) return it } } - // must set variable descriptor here because temp variables inside ir returnable block have different from methods args names - args += Variable(valueParameter.descriptor, tempFrame.popReturnValue()) + val inlineFun = (block as? IrReturnableBlock)?.inlineFunctionSymbol?.owner + if (inlineFun?.hasAnnotation(evaluateIntrinsicAnnotation) == true) { + val newFrame = data.copy() + block.statements.filterIsInstance().forEach { variable -> + variable.initializer!!.interpret(newFrame).also { if (it != Code.NEXT) return it } + newFrame.addVar(Variable(variable.descriptor, newFrame.popReturnValue())) } - val newFrame = data.copy().apply { addAll(args) } - return Wrapper.getStaticMethod(inlineFunOwner).invokeMethod(inlineFunOwner, newFrame).apply { data.pushReturnValue(newFrame) } + + val originalVarToInline = ParallelVisitorForInlineFun() + .apply { this.visitElement(inlineFun.body!!.statements.first(), block.statements.last()) } + .originalVarToInline + + val frameForWrapper = InterpreterFrame() + (listOfNotNull(inlineFun.extensionReceiverParameter, inlineFun.dispatchReceiverParameter) + inlineFun.valueParameters) + .map { it.descriptor } + .forEach { frameForWrapper.addVar(Variable(it, newFrame.getVariableState(originalVarToInline[it]!!))) } + + return Wrapper.getStaticMethod(inlineFun) + .invokeMethod(inlineFun, frameForWrapper) + .apply { data.pushReturnValue(frameForWrapper) } } return interpretStatements(block.statements, data) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/ParallelVisitorForInlineFun.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/ParallelVisitorForInlineFun.kt new file mode 100644 index 00000000000..11d952ddccc --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/ParallelVisitorForInlineFun.kt @@ -0,0 +1,137 @@ +/* + * 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.backend.common.interpreter + +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrVariable +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.util.statements +import org.jetbrains.kotlin.ir.visitors.IrElementVisitor + +/** + * This class is used for parallel processing two ir elements and gathering information about ir temporary variables from inline block + */ +internal class ParallelVisitorForInlineFun : IrElementVisitor { + val originalVarToInline = mutableMapOf() + + override fun visitElement(element: IrElement, data: IrElement) { + element.accept(this, data) + } + + override fun visitGetValue(expression: IrGetValue, data: IrElement) { + val original = expression.symbol.descriptor + val inline = (data as IrGetValue).symbol.descriptor + if (!original.equalTo(inline)) { + originalVarToInline[original] = inline + } + } + + override fun visitConst(expression: IrConst, data: IrElement) {} + + override fun visitVariable(declaration: IrVariable, data: IrElement) { + declaration.initializer?.accept(this, (data as IrVariable).initializer!!) + } + + override fun visitBody(body: IrBody, data: IrElement) { + for ((index, statement) in body.statements.withIndex()) { + statement.accept(this, (data as IrBody).statements[index]) + } + } + + override fun visitBlock(expression: IrBlock, data: IrElement) { + for ((index, statement) in expression.statements.withIndex()) { + statement.accept(this, (data as IrBlock).statements[index]) + } + } + + override fun visitStringConcatenation(expression: IrStringConcatenation, data: IrElement) { + for ((index, argument) in expression.arguments.withIndex()) { + argument.accept(this, (data as IrStringConcatenation).arguments[index]) + } + } + + override fun visitSetVariable(expression: IrSetVariable, data: IrElement) { + expression.value.accept(this, (data as IrSetVariable).value) + } + + override fun visitGetField(expression: IrGetField, data: IrElement) { + expression.receiver?.accept(this, (data as IrGetField).receiver!!) + } + + override fun visitSetField(expression: IrSetField, data: IrElement) { + expression.value.accept(this, (data as IrSetField).value) + expression.receiver?.accept(this, data.receiver!!) + } + + override fun visitCall(expression: IrCall, data: IrElement) { + expression.dispatchReceiver?.accept(this, (data as IrCall).dispatchReceiver!!) + expression.extensionReceiver?.accept(this, (data as IrCall).extensionReceiver!!) + for (i in 0 until expression.valueArgumentsCount) { + expression.getValueArgument(i)?.accept(this, (data as IrCall).getValueArgument(i)!!) + } + expression.getBody()?.accept(this, (data as IrCall).getBody()!!) + } + + override fun visitConstructorCall(expression: IrConstructorCall, data: IrElement) { + expression.dispatchReceiver?.accept(this, (data as IrConstructorCall).dispatchReceiver!!) + expression.extensionReceiver?.accept(this, (data as IrConstructorCall).extensionReceiver!!) + for (i in 0 until expression.valueArgumentsCount) { + expression.getValueArgument(i)?.accept(this, (data as IrConstructorCall).getValueArgument(i)!!) + } + expression.getBody()?.accept(this, (data as IrConstructorCall).getBody()!!) + } + + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: IrElement) { + expression.dispatchReceiver?.accept(this, (data as IrDelegatingConstructorCall).dispatchReceiver!!) + expression.extensionReceiver?.accept(this, (data as IrDelegatingConstructorCall).extensionReceiver!!) + for (i in 0 until expression.valueArgumentsCount) { + expression.getValueArgument(i)?.accept(this, (data as IrDelegatingConstructorCall).getValueArgument(i)!!) + } + expression.getBody()?.accept(this, (data as IrDelegatingConstructorCall).getBody()!!) + } + + override fun visitWhen(expression: IrWhen, data: IrElement) { + for ((index, branch) in expression.branches.withIndex()) { + branch.accept(this, (data as IrWhen).branches[index]) + } + } + + override fun visitBranch(branch: IrBranch, data: IrElement) { + branch.condition.accept(this, (data as IrBranch).condition) + branch.result.accept(this, data.result) + } + + override fun visitLoop(loop: IrLoop, data: IrElement) { + loop.condition.accept(this, (data as IrLoop).condition) + loop.body?.accept(this, data.body!!) + } + + override fun visitTry(aTry: IrTry, data: IrElement) { + aTry.tryResult.accept(this, (data as IrTry).tryResult) + for ((index, catch) in aTry.catches.withIndex()) { + catch.accept(this, data.catches[index]) + } + aTry.finallyExpression?.accept(this, data.finallyExpression!!) + } + + override fun visitCatch(aCatch: IrCatch, data: IrElement) { + aCatch.catchParameter.accept(this, (data as IrCatch).catchParameter) + aCatch.result.accept(this, data.result) + } + + override fun visitReturn(expression: IrReturn, data: IrElement) { + expression.value.accept(this, (data as IrReturn).value) + } + + override fun visitThrow(expression: IrThrow, data: IrElement) { + expression.value.accept(this, (data as IrThrow).value) + } + + override fun visitTypeOperator(expression: IrTypeOperatorCall, data: IrElement) { + expression.argument.accept(this, (data as IrTypeOperatorCall).argument) + } +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/intrinsic/InterpreterIntrinsics.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/intrinsic/InterpreterIntrinsics.kt index e075de4f351..c157dc1dd45 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/intrinsic/InterpreterIntrinsics.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/intrinsic/InterpreterIntrinsics.kt @@ -9,4 +9,6 @@ package org.jetbrains.kotlin.backend.common.interpreter.intrinsic public fun emptyArray(): Array = kotlin.emptyArray() -public fun arrayOf(vararg elements: Any?): Array = elements as Array \ No newline at end of file +public fun arrayOf(vararg elements: Any?): Array = elements as Array + +public fun arrayOfNulls(size: Int): Array = arrayOfNulls(size) \ No newline at end of file