Implement LambdaProxy class

This commit is contained in:
Ivan Kylchik
2020-08-30 17:03:05 +03:00
committed by TeamCityServer
parent 1d0acedc48
commit 96a090f2ab
12 changed files with 203 additions and 92 deletions
@@ -16,6 +16,10 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl
import org.jetbrains.kotlin.ir.interpreter.builtins.*
import org.jetbrains.kotlin.ir.interpreter.exceptions.*
import org.jetbrains.kotlin.ir.interpreter.intrinsics.IntrinsicEvaluator
import org.jetbrains.kotlin.ir.interpreter.proxy.CommonProxy
import org.jetbrains.kotlin.ir.interpreter.proxy.LambdaProxy.Companion.asProxy
import org.jetbrains.kotlin.ir.interpreter.proxy.Proxy
import org.jetbrains.kotlin.ir.interpreter.proxy.unwrap
import org.jetbrains.kotlin.ir.interpreter.stack.StackImpl
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.*
@@ -89,62 +93,14 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
}
}
/**
* calledFromBuiltIns - used to avoid cyclic calls. For example:
* override fun toString(): String {
* return super.toString()
* }
*/
internal class Proxy(val state: Common, val interpreter: IrInterpreter, private val calledFromBuiltIns: Boolean = false) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Proxy
val valueArguments = mutableListOf<Variable>()
val equalsFun = state.getEqualsFunction()
if (equalsFun.isFakeOverriddenFromAny() || calledFromBuiltIns) return this.state === other.state
equalsFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
valueArguments.add(Variable(equalsFun.valueParameters.single().symbol, other.state))
return with(interpreter) {
stack.newFrame(initPool = valueArguments) {
equalsFun.interpret()
}
(stack.popReturnValue() as Primitive<*>).value as Boolean
} // TODO check
internal fun IrFunction.interpret(valueArguments: List<Variable>): Any? {
val returnLabel = stack.newFrame(initPool = valueArguments) {
this@interpret.interpret()
}
override fun hashCode(): Int {
val valueArguments = mutableListOf<Variable>()
val hashCodeFun = state.getHashCodeFunction()
if (hashCodeFun.isFakeOverriddenFromAny() || calledFromBuiltIns) return System.identityHashCode(state)
hashCodeFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
return with(interpreter) {
stack.newFrame(initPool = valueArguments) {
hashCodeFun.interpret()
}
(stack.popReturnValue() as Primitive<*>).value as Int
}// TODO check
}
override fun toString(): String {
val valueArguments = mutableListOf<Variable>()
val toStringFun = state.getToStringFunction()
if (toStringFun.isFakeOverriddenFromAny() || calledFromBuiltIns) {
return "${state.irClass.internalName()}@" + hashCode().toString(16).padStart(8, '0')
}
toStringFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
return with(interpreter) {
stack.newFrame(initPool = valueArguments) {
toStringFun.interpret()
}
(stack.popReturnValue() as Primitive<*>).value as String
}// TODO check
return when (returnLabel.returnLabel) {
ReturnLabel.REGULAR -> stack.popReturnValue().unwrap(this@IrInterpreter)
ReturnLabel.EXCEPTION -> throw stack.popReturnValue() as ExceptionState
else -> TODO("$returnLabel not supported as result of interpretation")
}
}
@@ -153,8 +109,8 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
is ExceptionState -> this
is Wrapper -> this.value
is Primitive<*> -> this.value
is Common -> Proxy(this, this@IrInterpreter, calledFromBuiltIns)
is Lambda -> this // TODO as Proxy
is Common -> CommonProxy(this, this@IrInterpreter, calledFromBuiltIns)
is Lambda -> this.asProxy(this@IrInterpreter)
else -> throw AssertionError("${this::class} is unsupported as argument for wrap function")
}
}
@@ -350,7 +306,7 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
// dispatch receiver processing
val rawDispatchReceiver = expression.dispatchReceiver
rawDispatchReceiver?.interpret()?.check { return it }
val dispatchReceiver = rawDispatchReceiver?.let { stack.popReturnValue() }?.checkNullability(expression.dispatchReceiver?.type)
var dispatchReceiver = rawDispatchReceiver?.let { stack.popReturnValue() }?.checkNullability(expression.dispatchReceiver?.type)
// extension receiver processing
val rawExtensionReceiver = expression.extensionReceiver
@@ -358,6 +314,10 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
val extensionReceiver = rawExtensionReceiver?.let { stack.popReturnValue() }?.checkNullability(expression.extensionReceiver?.type)
val irFunction = dispatchReceiver?.getIrFunctionByIrCall(expression) ?: expression.symbol.owner
dispatchReceiver = when (irFunction.parent) {
(dispatchReceiver as? Complex)?.superWrapperClass?.irClass -> dispatchReceiver.superWrapperClass
else -> dispatchReceiver
}
// it is important firstly to add receiver, then arguments; this order is used in builtin method call
irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> valueArguments.add(Variable(it, receiver)) } }
@@ -774,7 +734,7 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
is Primitive<*> -> arrayToList(result.value)
is Common -> when {
result.irClass.defaultType.isUnsignedArray() -> arrayToList((result.fields.single().state as Primitive<*>).value)
else -> listOf(Proxy(result, this))
else -> listOf(CommonProxy(result, this))
}
else -> listOf(result)
}
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.interpreter.builtins.evaluateIntrinsicAnnotation
import org.jetbrains.kotlin.ir.interpreter.exceptions.throwAsUserException
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.*
import org.jetbrains.kotlin.ir.interpreter.proxy.Proxy
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
@@ -57,7 +58,7 @@ internal fun State.toIrExpression(expression: IrExpression): IrExpression {
internal fun Any?.toState(irType: IrType): State {
return when (this) {
is IrInterpreter.Proxy -> this.state
is Proxy -> this.state
is State -> this
is Boolean, is Char, is Byte, is Short, is Int, is Long, is String, is Float, is Double, is Array<*>, is ByteArray,
is CharArray, is ShortArray, is IntArray, is LongArray, is FloatArray, is DoubleArray, is BooleanArray -> Primitive(this, irType)
@@ -197,6 +198,7 @@ internal fun IrType.isPrimitiveArray(): Boolean {
}
internal fun IrType.isFunction() = this.getClass()?.fqNameWhenAvailable?.asString()?.startsWith("kotlin.Function") ?: false
internal fun IrType.isKFunction() = this.getClass()?.fqNameWhenAvailable?.asString()?.startsWith("kotlin.reflect.KFunction") ?: false
internal fun IrType.isTypeParameter() = classifierOrNull is IrTypeParameterSymbol
@@ -16,7 +16,7 @@
package org.jetbrains.kotlin.ir.interpreter.builtins
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.proxy.Proxy
/** This file is generated by org.jetbrains.kotlin.backend.common.interpreter.builtins.GenerateBuiltInsMap.generateMap(). DO NOT MODIFY MANUALLY */
@@ -447,7 +447,7 @@ val binaryFunctions = mapOf<CompileTimeFunction, Function2<Any?, Any?, Any?>>(
binaryOperation<Long, Long>("greaterOrEqual", "Long", "Long") { a, b -> a >= b },
binaryOperation<Double, Double>("greaterOrEqual", "Double", "Double") { a, b -> a >= b },
binaryOperation<Any?, Any?>("EQEQ", "Any?", "Any?") { a, b -> a == b },
binaryOperation<Any?, Any?>("EQEQEQ", "Any?", "Any?") { a, b -> if (a is IrInterpreter.Proxy && b is IrInterpreter.Proxy) a.state === b.state else a === b },
binaryOperation<Any?, Any?>("EQEQEQ", "Any?", "Any?") { a, b -> if (a is Proxy && b is Proxy) a.state === b.state else a === b },
binaryOperation<Float?, Float?>("ieee754equals", "Float?", "Float?") { a, b -> a == b },
binaryOperation<Double?, Double?>("ieee754equals", "Double?", "Double?") { a, b -> a == b },
binaryOperation<Boolean, Boolean>("ANDAND", "Boolean", "Boolean") { a, b -> a && b },
@@ -19,7 +19,6 @@ internal class IntrinsicEvaluator {
ArrayOfNulls.equalTo(irFunction) -> ArrayOfNulls.evaluate(irFunction, stack, interpret)
EnumValues.equalTo(irFunction) -> EnumValues.evaluate(irFunction, stack, interpret)
EnumValueOf.equalTo(irFunction) -> EnumValueOf.evaluate(irFunction, stack, interpret)
RegexReplace.equalTo(irFunction) -> RegexReplace.evaluate(irFunction, stack, interpret)
EnumHashCode.equalTo(irFunction) -> EnumHashCode.evaluate(irFunction, stack, interpret)
JsPrimitives.equalTo(irFunction) -> JsPrimitives.evaluate(irFunction, stack, interpret)
ArrayConstructor.equalTo(irFunction) -> ArrayConstructor.evaluate(irFunction, stack, interpret)
@@ -105,28 +105,6 @@ internal object EnumValueOf : IntrinsicBase() {
}
}
internal object RegexReplace : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
return fqName == "kotlin.text.Regex.replace" && irFunction.valueParameters.size == 2
}
override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult {
val states = stack.getAll().map { it.state }
val regex = states.filterIsInstance<Wrapper>().single().value as Regex
val input = states.filterIsInstance<Primitive<*>>().single().asString()
val transform = states.filterIsInstance<Lambda>().single().irFunction
val matchResultParameter = transform.valueParameters.single()
val result = regex.replace(input) {
val itAsState = Variable(matchResultParameter.symbol, Wrapper(it, matchResultParameter.type.classOrNull!!.owner))
stack.newFrame(initPool = listOf(itAsState)) { transform.interpret() }//.check { return it }
stack.popReturnValue().asString()
}
stack.pushReturnValue(result.toState(irFunction.returnType))
return Next
}
}
internal object EnumHashCode : IntrinsicBase() {
override fun equalTo(irFunction: IrFunction): Boolean {
val fqName = irFunction.fqNameWhenAvailable.toString()
@@ -0,0 +1,57 @@
/*
* 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.interpreter.proxy
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.getDispatchReceiver
import org.jetbrains.kotlin.ir.interpreter.internalName
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.Common
import org.jetbrains.kotlin.ir.util.isFakeOverriddenFromAny
/**
* calledFromBuiltIns - used to avoid cyclic calls. For example:
* override fun toString(): String {
* return super.toString()
* }
*/
internal class CommonProxy(override val state: Common, override val interpreter: IrInterpreter, private val calledFromBuiltIns: Boolean = false): Proxy {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as CommonProxy
val valueArguments = mutableListOf<Variable>()
val equalsFun = state.getEqualsFunction()
if (equalsFun.isFakeOverriddenFromAny() || calledFromBuiltIns) return this.state === other.state
equalsFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
valueArguments.add(Variable(equalsFun.valueParameters.single().symbol, other.state))
return with(interpreter) { equalsFun.interpret(valueArguments) } as Boolean
}
override fun hashCode(): Int {
val valueArguments = mutableListOf<Variable>()
val hashCodeFun = state.getHashCodeFunction()
if (hashCodeFun.isFakeOverriddenFromAny() || calledFromBuiltIns) return System.identityHashCode(state)
hashCodeFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
return with(interpreter) { hashCodeFun.interpret(valueArguments) } as Int
}
override fun toString(): String {
val valueArguments = mutableListOf<Variable>()
val toStringFun = state.getToStringFunction()
if (toStringFun.isFakeOverriddenFromAny() || calledFromBuiltIns) {
return "${state.irClass.internalName()}@" + hashCode().toString(16).padStart(8, '0')
}
toStringFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) }
return with(interpreter) { toStringFun.interpret(valueArguments) } as String
}
}
@@ -0,0 +1,60 @@
/*
* 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.interpreter.proxy
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
import org.jetbrains.kotlin.ir.interpreter.state.Lambda
import org.jetbrains.kotlin.ir.interpreter.toState
internal class LambdaProxy(override val state: Lambda, override val interpreter: IrInterpreter): Proxy {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as LambdaProxy
return state == other.state
}
override fun hashCode(): Int {
return state.hashCode()
}
override fun toString(): String {
return state.toString()
}
companion object {
fun Lambda.asProxy(interpreter: IrInterpreter): Any {
val lambdaProxy = LambdaProxy(this, interpreter)
val arity = this.getArity()
val hasBigArity = arity == null || arity >= FunctionInvokeDescriptor.BIG_ARITY
val functionClass = when {
!hasBigArity && this.isFunction -> Class.forName("kotlin.jvm.functions." + this.irClass.name)
hasBigArity && this.isFunction -> Class.forName("kotlin.jvm.functions.FunctionN")
this.isKFunction -> Class.forName("kotlin.reflect.KFunction")
else -> throw InternalError("Cannot handle lambda $this as proxy")
}
return java.lang.reflect.Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), arrayOf(functionClass))
{ proxy, method, args ->
when {
method.name == "invoke" && method.parameterTypes.single().isObject() -> {
val valueArguments = this.irFunction.valueParameters
.mapIndexed { index, parameter -> Variable(parameter.symbol, args[index].toState(parameter.type)) }
with(interpreter) { lambdaProxy.state.irFunction.interpret(valueArguments) }
}
method.name == "equals" && method.parameterTypes.single().isObject() -> lambdaProxy.equals(args.single())
method.name == "hashCode" && method.parameterTypes.isEmpty() -> lambdaProxy.hashCode()
method.name == "toString" && method.parameterTypes.isEmpty() -> lambdaProxy.toString()
else -> throw InternalError("Cannot invoke method ${method.name} from lambda $this")
}
}
}
}
}
@@ -0,0 +1,34 @@
/*
* 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.interpreter.proxy
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.proxy.LambdaProxy.Companion.asProxy
import org.jetbrains.kotlin.ir.interpreter.state.*
internal interface Proxy {
val state: State
val interpreter: IrInterpreter
override fun equals(other: Any?): Boolean
override fun hashCode(): Int
override fun toString(): String
}
internal fun State.unwrap(interpreter: IrInterpreter): Any? {
return when (this) {
is Proxy -> this.state
is Primitive<*> -> this.value
is Common -> CommonProxy(this, interpreter)
is Lambda -> this.asProxy(interpreter)
is Wrapper -> this.value
else -> throw AssertionError("Unsupported state for proxy unwrap: ${this::class.java}")
}
}
internal fun Class<*>.isObject(): Boolean {
return this.name == "java.lang.Object"
}
@@ -50,6 +50,7 @@ internal interface Complex: State {
}
private fun getOverridden(owner: IrSimpleFunction): IrSimpleFunction {
if (owner.parent == superWrapperClass?.irClass) return owner
if (!owner.isFakeOverride || owner.body != null || owner.parentAsClass.defaultType.isAny()) return owner
val overriddenOwner = owner.overriddenSymbols.singleOrNull { !it.owner.parentAsClass.isInterface }?.owner
@@ -11,6 +11,9 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.interpreter.isFunction
import org.jetbrains.kotlin.ir.interpreter.isKFunction
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.utils.addToStdlib.cast
@@ -18,12 +21,18 @@ import org.jetbrains.kotlin.utils.addToStdlib.cast
internal class Lambda(val irFunction: IrFunction, override val irClass: IrClass) : State {
override val fields: MutableList<Variable> = mutableListOf()
override val typeArguments: MutableList<Variable> = mutableListOf()
val isKFunction = irClass.defaultType.isKFunction()
val isFunction = irClass.defaultType.isFunction()
private val invokeSymbol = irClass.declarations
.single { it.nameForIrSerialization.asString() == "invoke" }
.cast<IrSimpleFunction>()
.getLastOverridden().symbol
fun getArity(): Int? {
return irClass.name.asString().removePrefix("Function").removePrefix("KFunction").toIntOrNull()
}
override fun getIrFunctionByIrCall(expression: IrCall): IrFunction? {
return if (invokeSymbol == expression.symbol) irFunction else null
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.ir.interpreter.state
import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFunction
@@ -124,19 +125,28 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex
//TODO check if primitive array is possible here
return when {
notNullType.isPrimitiveType() || notNullType.isString() -> getPrimitiveClass(notNullType, asObject)!!
notNullType.isArray() -> if (asObject) Array<Any?>::class.javaObjectType else Array<Any?>::class.java
notNullType.isArray() -> {
val argumentFqName = (this as IrSimpleType).arguments.single().typeOrNull?.classOrNull?.owner?.fqNameWhenAvailable
argumentFqName?.let { Class.forName("[L$it;") } ?: Array<Any?>::class.java
}
notNullType.isNothing() -> Nothing::class.java
notNullType.isAny() -> Any::class.java
notNullType.isUnit() -> if (asObject) Void::class.javaObjectType else Void::class.javaPrimitiveType!!
notNullType.isNumber() -> Number::class.java
notNullType.isCharSequence() -> CharSequence::class.java
notNullType.isComparable() -> Comparable::class.java
notNullType.isThrowable() -> Throwable::class.java
notNullType.isIterable() -> Iterable::class.java
// TODO implement function mapping; all complexity is to map big arity to FunctionN
//notNullType.isKFunction() -> Class.forName("kotlin.reflect.KFunction")
//notNullType.isFunction() -> Class.forName("kotlin.jvm.functions.Function_TODO")
//notNullType.isSuspendFunction() || notNullType.isKSuspendFunction() -> throw AssertionError()
notNullType.isKFunction() -> Class.forName("kotlin.reflect.KFunction")
notNullType.isFunction() -> {
val arity = fqName?.removePrefix("kotlin.Function")?.toIntOrNull()
return when {
arity == null || arity >= BuiltInFunctionArity.BIG_ARITY -> Class.forName("kotlin.jvm.functions.FunctionN")
else -> Class.forName("kotlin.jvm.functions.${fqName.removePrefix("kotlin.")}")
}
}
//notNullType.isSuspendFunction() || notNullType.isKSuspendFunction() -> throw AssertionError() //TODO
fqName == "kotlin.Enum" -> Enum::class.java
fqName == "kotlin.collections.Collection" || fqName == "kotlin.collections.MutableCollection" -> Collection::class.java
@@ -147,6 +157,7 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex
fqName == "kotlin.collections.Iterator" || fqName == "kotlin.collections.MutableIterator" -> Iterator::class.java
fqName == "kotlin.collections.Map.Entry" || fqName == "kotlin.collections.MutableMap.MutableEntry" -> Map.Entry::class.java
fqName == "kotlin.collections.ListIterator" || fqName == "kotlin.collections.MutableListIterator" -> ListIterator::class.java
fqName == "kotlin.collections.HashMap" -> HashMap::class.java
owner.hasAnnotation(evaluateIntrinsicAnnotation) -> Class.forName(owner!!.getEvaluateIntrinsicValue())
fqName == null -> Any::class.java // null if this.isTypeParameter()
@@ -39,7 +39,7 @@ fun generateMap(): String {
p.println(File("license/COPYRIGHT.txt").readText())
p.println("package org.jetbrains.kotlin.ir.interpreter.builtins")
p.println()
p.println("import org.jetbrains.kotlin.ir.interpreter.IrInterpreter")
p.println("import org.jetbrains.kotlin.ir.interpreter.proxy.Proxy")
p.println()
p.println("/** This file is generated by org.jetbrains.kotlin.backend.common.interpreter.builtins.GenerateBuiltInsMap.generateMap(). DO NOT MODIFY MANUALLY */")
p.println()
@@ -150,7 +150,7 @@ private fun generateBinaryBody(
val methodName = "${function.name}"
val methodSymbol = getIrMethodSymbolByName(methodName)
val body = when (methodName) {
IrBuiltIns.OperatorNames.EQEQEQ -> "if (a is IrInterpreter.Proxy && b is IrInterpreter.Proxy) a.state === b.state else a === b"
IrBuiltIns.OperatorNames.EQEQEQ -> "if (a is Proxy && b is Proxy) a.state === b.state else a === b"
else -> "a $methodSymbol b"
}
" binaryOperation${parameters.first}(\"$methodName\", ${parameters.second}) { a, b -> $body }"