Implement StringBuilder interpretation

This commit is contained in:
Ivan Kylchik
2020-06-10 19:16:09 +03:00
parent 06ee4ac21f
commit 4fdfdb9b4c
3 changed files with 98 additions and 88 deletions
@@ -21,9 +21,7 @@ import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.statements import org.jetbrains.kotlin.ir.util.statements
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import java.lang.invoke.MethodHandle
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
enum class Code(var info: String = "") { enum class Code(var info: String = "") {
NEXT, RETURN, BREAK_LOOP, BREAK_WHEN, CONTINUE, EXCEPTION NEXT, RETURN, BREAK_LOOP, BREAK_WHEN, CONTINUE, EXCEPTION
@@ -112,6 +110,14 @@ class IrInterpreter(irModule: IrModuleFragment) {
return irFunction.body?.interpret(data) ?: throw AssertionError("Ir function must be with body") return irFunction.body?.interpret(data) ?: throw AssertionError("Ir function must be with body")
} }
private fun MethodHandle.invokeMethod(irFunction: IrFunction, data: Frame): Code {
// TODO try catch
val result = this.invokeWithArguments(irFunction.getArgsForMethodInvocation(data))
data.pushReturnValue(result.toState(result.getType(irFunction.returnType)))
return Code.NEXT
}
private fun calculateAbstract(irFunction: IrFunction, data: Frame): Code { private fun calculateAbstract(irFunction: IrFunction, data: Frame): Code {
if (irFunction.body == null) { if (irFunction.body == null) {
val receiver = data.getVariableState(irFunction.symbol.getReceiver()!!) as Complex val receiver = data.getVariableState(irFunction.symbol.getReceiver()!!) as Complex
@@ -232,8 +238,8 @@ class IrInterpreter(irModule: IrModuleFragment) {
val isWrapper = (dispatchReceiver is Wrapper && rawExtensionReceiver == null) || val isWrapper = (dispatchReceiver is Wrapper && rawExtensionReceiver == null) ||
(extensionReceiver is Wrapper && rawDispatchReceiver == null) (extensionReceiver is Wrapper && rawDispatchReceiver == null)
val code = when { val code = when {
isWrapper -> ((dispatchReceiver ?: extensionReceiver) as Wrapper).invoke(irFunction as IrFunctionImpl, newFrame) isWrapper -> ((dispatchReceiver ?: extensionReceiver) as Wrapper).getMethod(irFunction).invokeMethod(irFunction, newFrame)
irFunction.hasAnnotation(evaluateIntrinsicAnnotation) -> Wrapper.invokeStatic(irFunction as IrFunctionImpl, newFrame) irFunction.hasAnnotation(evaluateIntrinsicAnnotation) -> Wrapper.getStaticMethod(irFunction).invokeMethod(irFunction, newFrame)
irFunction.isAbstract() -> calculateAbstract(irFunction, newFrame) //abstract check must be before fake overridden check irFunction.isAbstract() -> calculateAbstract(irFunction, newFrame) //abstract check must be before fake overridden check
irFunction.isFakeOverridden() -> calculateOverridden(irFunction as IrFunctionImpl, newFrame) irFunction.isFakeOverridden() -> calculateOverridden(irFunction as IrFunctionImpl, newFrame)
irFunction.body == null -> calculateBuiltIns(irFunction, newFrame) irFunction.body == null -> calculateBuiltIns(irFunction, newFrame)
@@ -247,7 +253,14 @@ class IrInterpreter(irModule: IrModuleFragment) {
interpretValueParameters(constructorCall, data).also { if (it != Code.NEXT) return it } interpretValueParameters(constructorCall, data).also { if (it != Code.NEXT) return it }
val valueParameters = constructorCall.symbol.descriptor.valueParameters.map { Variable(it, data.popReturnValue()) }.toMutableList() val valueParameters = constructorCall.symbol.descriptor.valueParameters.map { Variable(it, data.popReturnValue()) }.toMutableList()
val newFrame = InterpreterFrame(valueParameters) val newFrame = InterpreterFrame(valueParameters)
val state = Complex(constructorCall.symbol.owner.parent as IrClass, mutableListOf())
val owner = constructorCall.symbol.owner
val parent = owner.parent as IrClass
if (parent.hasAnnotation(evaluateIntrinsicAnnotation)) {
return Wrapper.getConstructorMethod(owner).invokeMethod(owner, newFrame).apply { data.pushReturnValue(newFrame) }
}
val state = Complex(parent, mutableListOf())
newFrame.addVar(Variable(constructorCall.getThisAsReceiver(), state)) //used to set up fields in body newFrame.addVar(Variable(constructorCall.getThisAsReceiver(), state)) //used to set up fields in body
val code = constructorCall.getBody()?.interpret(newFrame) ?: Code.NEXT val code = constructorCall.getBody()?.interpret(newFrame) ?: Code.NEXT
if (newFrame.hasReturnValue()) { if (newFrame.hasReturnValue()) {
@@ -419,7 +432,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
"kotlin.FloatArray" -> Primitive(args.map { (it as Primitive<Float>).value }.toFloatArray(), type) "kotlin.FloatArray" -> Primitive(args.map { (it as Primitive<Float>).value }.toFloatArray(), type)
"kotlin.DoubleArray" -> Primitive(args.map { (it as Primitive<Double>).value }.toDoubleArray(), type) "kotlin.DoubleArray" -> Primitive(args.map { (it as Primitive<Double>).value }.toDoubleArray(), type)
"kotlin.BooleanArray" -> Primitive(args.map { (it as Primitive<Boolean>).value }.toBooleanArray(), type) "kotlin.BooleanArray" -> Primitive(args.map { (it as Primitive<Boolean>).value }.toBooleanArray(), type)
else -> Primitive<Array<*>>(args.toTypedArray(), type) else -> Primitive<Array<*>>(args.map { (it as? Wrapper)?.value ?: (it as? Primitive<*>)?.value ?: it }.toTypedArray(), type)
} }
data.pushReturnValue(array) data.pushReturnValue(array)
@@ -5,24 +5,23 @@
package org.jetbrains.kotlin.backend.common.interpreter package org.jetbrains.kotlin.backend.common.interpreter
import org.jetbrains.kotlin.backend.common.interpreter.builtins.evaluateIntrinsicAnnotation
import org.jetbrains.kotlin.backend.common.interpreter.stack.Frame
import org.jetbrains.kotlin.backend.common.interpreter.stack.Primitive import org.jetbrains.kotlin.backend.common.interpreter.stack.Primitive
import org.jetbrains.kotlin.backend.common.interpreter.stack.State import org.jetbrains.kotlin.backend.common.interpreter.stack.State
import org.jetbrains.kotlin.backend.common.interpreter.stack.Wrapper import org.jetbrains.kotlin.backend.common.interpreter.stack.Wrapper
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.isArray
import org.jetbrains.kotlin.ir.types.isPrimitiveType
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.ir.util.isFakeOverride import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.ir.util.isPrimitiveArray
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver
@@ -102,7 +101,7 @@ fun Any?.toState(irType: IrType): State {
is State -> this is State -> this
is Boolean, is Char, is Byte, is Short, is Int, is Long, is String, is Float, is Double -> Primitive(this, irType) is Boolean, is Char, is Byte, is Short, is Int, is Long, is String, is Float, is Double -> Primitive(this, irType)
null -> Primitive(this, irType) null -> Primitive(this, irType)
else -> Wrapper(irType.classOrNull!!.owner, this) else -> Wrapper(this, irType.classOrNull!!.owner)
} }
} }
@@ -126,7 +125,8 @@ fun <T> IrConst<T>.toPrimitive(): Primitive<T> {
return Primitive(this.value, this.type) return Primitive(this.value, this.type)
} }
fun IrAnnotationContainer.hasAnnotation(annotation: FqName): Boolean { fun IrAnnotationContainer?.hasAnnotation(annotation: FqName): Boolean {
this ?: return false
if (this.annotations.isNotEmpty()) { if (this.annotations.isNotEmpty()) {
return this.annotations.any { it.symbol.descriptor.containingDeclaration.fqNameSafe == annotation } return this.annotations.any { it.symbol.descriptor.containingDeclaration.fqNameSafe == annotation }
} }
@@ -134,7 +134,13 @@ fun IrAnnotationContainer.hasAnnotation(annotation: FqName): Boolean {
} }
fun IrAnnotationContainer.getAnnotation(annotation: FqName): IrConstructorCall { fun IrAnnotationContainer.getAnnotation(annotation: FqName): IrConstructorCall {
return this.annotations.first { it.symbol.descriptor.containingDeclaration.fqNameSafe == annotation } return this.annotations.firstOrNull { it.symbol.descriptor.containingDeclaration.fqNameSafe == annotation }
?: ((this as IrFunction).parent as IrClass).annotations.first { it.symbol.descriptor.containingDeclaration.fqNameSafe == annotation }
}
fun IrAnnotationContainer.getEvaluateIntrinsicValue(): String? {
if (!this.hasAnnotation(evaluateIntrinsicAnnotation)) return null
return (this.getAnnotation(evaluateIntrinsicAnnotation).getValueArgument(0) as IrConst<*>).value.toString()
} }
fun getPrimitiveClass(fqName: String, asObject: Boolean = false): Class<*>? { fun getPrimitiveClass(fqName: String, asObject: Boolean = false): Class<*>? {
@@ -156,6 +162,17 @@ fun IrType.getFqName(): String? {
return this.classOrNull?.owner?.fqNameForIrSerialization?.asString() return this.classOrNull?.owner?.fqNameForIrSerialization?.asString()
} }
fun IrType?.isPrimitiveState(): Boolean { fun IrFunction.getArgsForMethodInvocation(data: Frame): List<Any?> {
return this?.isPrimitiveType() == true || this?.isArray() == true || this?.isPrimitiveArray() == true val argsValues = data.getAll().map { (it.state as? Wrapper)?.value ?: (it.state as Primitive<*>).value }.toMutableList()
// TODO if vararg isn't last parameter
// must convert vararg array into separated elements for correct invoke
if (this.valueParameters.lastOrNull()?.varargElementType != null) {
argsValues.removeAt(argsValues.size - 1)
val varargState = data.getVariableState(this.valueParameters.last().descriptor)
val varargValue = (varargState as? Wrapper)?.value ?: (varargState as Primitive<*>).value
argsValues.addAll(varargValue as Array<out Any?>)
}
return argsValues
} }
@@ -17,11 +17,13 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.ir.util.isTypeParameter import org.jetbrains.kotlin.ir.util.isTypeParameter
import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.FqNameUnsafe
import java.lang.invoke.MethodHandle
import java.lang.invoke.MethodHandles import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType import java.lang.invoke.MethodType
@@ -37,8 +39,8 @@ interface State {
fun getIrFunction(descriptor: FunctionDescriptor): IrFunction? fun getIrFunction(descriptor: FunctionDescriptor): IrFunction?
} }
open class Primitive<T>(var value: T, val type: IrType) : State { class Primitive<T>(var value: T, val type: IrType) : State {
final override val fields: MutableList<Variable> = mutableListOf() override val fields: MutableList<Variable> = mutableListOf()
init { init {
val properties = type.classOrNull!!.owner.declarations.filterIsInstance<IrProperty>() val properties = type.classOrNull!!.owner.declarations.filterIsInstance<IrProperty>()
@@ -132,79 +134,55 @@ open class Complex(private var type: IrClass, override val fields: MutableList<V
} }
} }
class Wrapper(private val type: IrClass, private val wrapperValue: Any) : Complex(type, mutableListOf()) { class Wrapper(val value: Any, private val type: IrClass) : Complex(type, mutableListOf()) {
private val typeFqName = type.fqNameForIrSerialization.toUnsafe() private val typeFqName = type.fqNameForIrSerialization.toUnsafe()
private val receiverClass = getPrimitiveClass(typeFqName.toString(), asObject = true) private val receiverClass = type.defaultType.getClass(true)
?: JavaToKotlinClassMap.mapKotlinToJava(typeFqName)?.let { Class.forName(it.asSingleFqName().toString()) }
?: Class.forName(typeFqName.toString())
fun invoke(irFunction: IrFunctionImpl, data: Frame): Code { fun getMethod(irFunction: IrFunction): MethodHandle {
val methodName = (irFunction.correspondingPropertySymbol?.owner?.name ?: irFunction.name).toString() // used to get correct java method
// for example, method 'get' in kotlin StringBuilder is actually 'charAt' in java StringBuilder
val intrinsicName = irFunction.getEvaluateIntrinsicValue()
val property = (irFunction as? IrFunctionImpl)?.correspondingPropertySymbol?.owner
val methodName = intrinsicName ?: (property ?: irFunction).name.toString()
val methodType = irFunction.getMethodType() val methodType = irFunction.getMethodType()
val method = MethodHandles.lookup().findVirtual(receiverClass, methodName, methodType) return MethodHandles.lookup().findVirtual(receiverClass, methodName, methodType)
val result = method.invokeWithArguments(irFunction.getArgs(data))
data.pushReturnValue(result.toState(irFunction.returnType))
return Code.NEXT
} }
companion object { companion object {
fun invokeStatic(irFunction: IrFunctionImpl, data: Frame): Code { fun getConstructorMethod(irConstructor: IrFunction): MethodHandle {
val methodType = irConstructor.getMethodType()
return MethodHandles.lookup().findConstructor(irConstructor.returnType.getClass(true), methodType)
}
fun getStaticMethod(irFunction: IrFunction): MethodHandle {
val annotation = irFunction.getAnnotation(evaluateIntrinsicAnnotation) val annotation = irFunction.getAnnotation(evaluateIntrinsicAnnotation)
val jvmFileName = Class.forName((annotation.getValueArgument(0) as IrConst<*>).value.toString()) val jvmFileName = Class.forName((annotation.getValueArgument(0) as IrConst<*>).value.toString())
val methodType = irFunction.getMethodType(withExtensionReceiver = true) val methodType = irFunction.getMethodType()
val method = MethodHandles.lookup().findStatic(jvmFileName, irFunction.name.asString(), methodType) return MethodHandles.lookup().findStatic(jvmFileName, irFunction.name.asString(), methodType)
val result = method.invokeWithArguments(irFunction.getArgs(data))
data.pushReturnValue(result.toState(irFunction.returnType))
return Code.NEXT
} }
private fun IrFunctionImpl.getMethodType(withExtensionReceiver: Boolean = false): MethodType { private fun IrFunction.getMethodType(): MethodType {
val returnClass = this.returnType.getClass(this.isReturnTypePrimitiveAsObject())
val argsClasses = this.valueParameters.map { it.type.getClass(this.isValueParameterPrimitiveAsObject(it.index)) } val argsClasses = this.valueParameters.map { it.type.getClass(this.isValueParameterPrimitiveAsObject(it.index)) }
return if (this is IrFunctionImpl) {
// for regular methods and functions
val returnClass = this.returnType.getClass(this.isReturnTypePrimitiveAsObject())
val extensionClass = this.extensionReceiverParameter?.type?.getClass(this.isExtensionReceiverPrimitive())
val extensionClass = when { MethodType.methodType(returnClass, listOfNotNull(extensionClass) + argsClasses)
withExtensionReceiver -> this.extensionReceiverParameter?.type?.getClass(this.isExtensionReceiverPrimitiveAsObject()) } else {
else -> null // for constructors
MethodType.methodType(Void::class.javaPrimitiveType, argsClasses)
} }
return MethodType.methodType(returnClass, listOfNotNull(extensionClass) + argsClasses)
}
private fun IrFunctionImpl.getArgs(data: Frame): List<Any?> {
val argsValues = data.getAll().map { (it.state as? Wrapper)?.wrapperValue ?: (it.state as? Any) }.toMutableList()
if (this.extensionReceiverParameter?.type.isPrimitiveState()) {
val varargState = data.getVariableState(this.symbol.getExtensionReceiver()!!)
argsValues[0] = (varargState as Primitive<*>).value
}
for ((index, valueParameter) in this.valueParameters.withIndex().reversed()) {
if (valueParameter.type.isPrimitiveState()) {
argsValues[argsValues.size - 1 - index] = (argsValues[argsValues.size - 1 - index] as Primitive<*>).value
}
}
// TODO if vararg isn't last parameter
// must convert vararg array into separated elements for correct invoke
if (this.valueParameters.lastOrNull()?.varargElementType != null) {
argsValues.removeAt(argsValues.size - 1)
val varargState = data.getVariableState(this.valueParameters.last().descriptor)
val varargValue = (varargState as? Wrapper)?.wrapperValue ?: (varargState as Primitive<*>).value
argsValues.addAll(varargValue as Array<out Any?>)
}
return argsValues
} }
private fun IrType.getClass(asObject: Boolean): Class<out Any> { private fun IrType.getClass(asObject: Boolean): Class<out Any> {
val fqName = this.getFqName() val fqName = this.getFqName()
val owner = this.classOrNull?.owner
return when { return when {
owner?.hasAnnotation(evaluateIntrinsicAnnotation) == true -> Class.forName(owner.getEvaluateIntrinsicValue())
this.isPrimitiveType() -> getPrimitiveClass(fqName!!, asObject) this.isPrimitiveType() -> getPrimitiveClass(fqName!!, asObject)
this.isArray() -> if (asObject) Array<Any?>::class.javaObjectType else Array<Any?>::class.java this.isArray() -> if (asObject) Array<Any?>::class.javaObjectType else Array<Any?>::class.java
//TODO primitive array //TODO primitive array
@@ -213,16 +191,18 @@ class Wrapper(private val type: IrClass, private val wrapperValue: Any) : Comple
} ?: Class.forName(fqName) } ?: Class.forName(fqName)
} }
private fun IrFunctionImpl.getOriginalOverriddenSymbols(): MutableList<IrSimpleFunctionSymbol> { private fun IrFunction.getOriginalOverriddenSymbols(): MutableList<IrFunctionSymbol> {
val overriddenSymbols = mutableListOf<IrSimpleFunctionSymbol>() val overriddenSymbols = mutableListOf<IrFunctionSymbol>()
val pool = this.overriddenSymbols.toMutableList() if (this is IrFunctionImpl) {
val iterator = pool.listIterator() val pool = this.overriddenSymbols.toMutableList()
for (symbol in iterator) { val iterator = pool.listIterator()
if (symbol.owner.overriddenSymbols.isEmpty()) { for (symbol in iterator) {
overriddenSymbols += symbol if (symbol.owner.overriddenSymbols.isEmpty()) {
iterator.remove() overriddenSymbols += symbol
} else { iterator.remove()
symbol.owner.overriddenSymbols.forEach { iterator.add(it) } } else {
symbol.owner.overriddenSymbols.forEach { iterator.add(it) }
}
} }
} }
@@ -230,11 +210,11 @@ class Wrapper(private val type: IrClass, private val wrapperValue: Any) : Comple
return overriddenSymbols return overriddenSymbols
} }
private fun IrFunctionImpl.isExtensionReceiverPrimitiveAsObject(): Boolean { private fun IrFunction.isExtensionReceiverPrimitive(): Boolean {
return this.extensionReceiverParameter?.type?.isPrimitiveType() == false return this.extensionReceiverParameter?.type?.isPrimitiveType() == false
} }
private fun IrFunctionImpl.isReturnTypePrimitiveAsObject(): Boolean { private fun IrFunction.isReturnTypePrimitiveAsObject(): Boolean {
for (symbol in getOriginalOverriddenSymbols()) { for (symbol in getOriginalOverriddenSymbols()) {
if (!symbol.owner.returnType.isTypeParameter() && !symbol.owner.returnType.isNullable()) { if (!symbol.owner.returnType.isTypeParameter() && !symbol.owner.returnType.isNullable()) {
return false return false
@@ -243,7 +223,7 @@ class Wrapper(private val type: IrClass, private val wrapperValue: Any) : Comple
return true return true
} }
private fun IrFunctionImpl.isValueParameterPrimitiveAsObject(index: Int): Boolean { private fun IrFunction.isValueParameterPrimitiveAsObject(index: Int): Boolean {
for (symbol in getOriginalOverriddenSymbols()) { for (symbol in getOriginalOverriddenSymbols()) {
if (!symbol.owner.valueParameters[index].type.isTypeParameter() && !symbol.owner.valueParameters[index].type.isNullable()) { if (!symbol.owner.valueParameters[index].type.isTypeParameter() && !symbol.owner.valueParameters[index].type.isNullable()) {
return false return false
@@ -254,10 +234,10 @@ class Wrapper(private val type: IrClass, private val wrapperValue: Any) : Comple
} }
override fun copy(): State { override fun copy(): State {
return Wrapper(type, wrapperValue) return Wrapper(value, type)
} }
override fun toString(): String { override fun toString(): String {
return "Wrapper(obj='$typeFqName', value=$wrapperValue)" return "Wrapper(obj='$typeFqName', value=$value)"
} }
} }