Make possible to create arrays with their constructors

This commit is contained in:
Ivan Kylchik
2020-06-10 19:28:25 +03:00
parent aca889a95b
commit 18d29f7d98
3 changed files with 186 additions and 13 deletions
@@ -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<Int>).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<IrVariable>().zip(inlineFunOwner.valueParameters)
val args = mutableListOf<Variable>()
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<IrVariable>().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)
@@ -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<Unit, IrElement> {
val originalVarToInline = mutableMapOf<DeclarationDescriptor, DeclarationDescriptor>()
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 <T> visitConst(expression: IrConst<T>, 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)
}
}
@@ -9,4 +9,6 @@ package org.jetbrains.kotlin.backend.common.interpreter.intrinsic
public fun emptyArray(): Array<Any?> = kotlin.emptyArray()
public fun arrayOf(vararg elements: Any?): Array<Any?> = elements as Array<Any?>
public fun arrayOf(vararg elements: Any?): Array<Any?> = elements as Array<Any?>
public fun arrayOfNulls(size: Int): Array<Any?> = arrayOfNulls<Any?>(size)