From 77978637a8779738baf5e454d574db1d5fe21229 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Sat, 28 Mar 2020 18:34:22 +0300 Subject: [PATCH] Move all classes from State.kt into separate files --- .../common/interpreter/state/Common.kt | 33 ++ .../common/interpreter/state/Complex.kt | 48 ++ .../interpreter/state/ExceptionState.kt | 157 +++++++ .../common/interpreter/state/Lambda.kt | 37 ++ .../common/interpreter/state/Primitive.kt | 65 +++ .../backend/common/interpreter/state/State.kt | 423 +----------------- .../common/interpreter/state/Wrapper.kt | 166 +++++++ 7 files changed, 507 insertions(+), 422 deletions(-) create mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Common.kt create mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Complex.kt create mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/ExceptionState.kt create mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Lambda.kt create mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Primitive.kt create mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Wrapper.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Common.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Common.kt new file mode 100644 index 00000000000..2ebb1c7f41d --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Common.kt @@ -0,0 +1,33 @@ +/* + * 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.state + +import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization + +class Common private constructor( + override val irClass: IrClass, override val fields: MutableList, superClass: Complex?, subClass: Complex? +) : Complex(irClass, fields, superClass, subClass) { + + constructor(irClass: IrClass) : this(irClass, mutableListOf(), null, null) + + fun getToStringFunction(): IrFunctionImpl { + return irClass.declarations.filterIsInstance() + .filter { it.descriptor.name.asString() == "toString" } + .first { it.descriptor.valueParameters.isEmpty() } as IrFunctionImpl + } + + override fun copy(): State { + return Common(irClass, fields, superClass, subClass ?: this) + } + + override fun toString(): String { + return "Common(obj='${irClass.fqNameForIrSerialization}', super=$superClass, values=$fields)" + } +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Complex.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Complex.kt new file mode 100644 index 00000000000..fb4b41e7f71 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Complex.kt @@ -0,0 +1,48 @@ +/* + * 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.state + +import org.jetbrains.kotlin.backend.common.interpreter.equalTo +import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization + +abstract class Complex( + override val irClass: IrClass, override val fields: MutableList, var superClass: Complex?, var subClass: Complex? +) : State { + fun setSuperClassInstance(superClass: Complex) { + this.superClass = superClass + superClass.subClass = this + } + + fun getOriginal(): Complex { + return subClass?.getOriginal() ?: this + } + + fun irClassFqName(): String { + return irClass.fqNameForIrSerialization.toString() + } + + override fun setState(newVar: Variable) { + when (val oldState = fields.firstOrNull { it.descriptor == newVar.descriptor }) { + null -> fields.add(newVar) // newVar isn't present in value list + else -> fields[fields.indexOf(oldState)] = newVar // newVar already present + } + } + + fun setStatesFrom(state: Complex) = state.fields.forEach { setState(it) } + + override fun getIrFunction(descriptor: FunctionDescriptor): IrFunction? { + if (descriptor is PropertyGetterDescriptor) + return irClass.declarations.filterIsInstance().mapNotNull { it.getter }.single { it.descriptor.equalTo(descriptor) } + + return irClass.declarations.filterIsInstance().singleOrNull { it.descriptor.equalTo(descriptor) } + } +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/ExceptionState.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/ExceptionState.kt new file mode 100644 index 00000000000..a254fb3c4a9 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/ExceptionState.kt @@ -0,0 +1,157 @@ +/* + * 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.state + +import org.jetbrains.kotlin.backend.common.interpreter.equalTo +import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable +import org.jetbrains.kotlin.backend.common.interpreter.toState +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.util.isSubclassOf +import org.jetbrains.kotlin.ir.util.nameForIrSerialization +import kotlin.math.min + +class ExceptionState private constructor( + override val irClass: IrClass, override val fields: MutableList, stackTrace: List, subClass: Complex? = null +) : Complex(irClass, fields, null, subClass) { + + private lateinit var exceptionFqName: String + private val exceptionHierarchy = mutableListOf() + private val messageProperty = irClass.getPropertyByName("message") + private val causeProperty = irClass.getPropertyByName("cause") + + private val stackTrace: List = stackTrace.reversed() + + init { + if (!this::exceptionFqName.isInitialized) this.exceptionFqName = irClassFqName() + + if (fields.none { it.descriptor.equalTo(messageProperty.descriptor) }) { + setMessage() + } + } + + constructor(common: Common, stackTrace: List) : this(common.irClass, common.fields, stackTrace) { + var wrapperSuperType: Complex? = common + while (wrapperSuperType != null && wrapperSuperType !is Wrapper) wrapperSuperType = (wrapperSuperType as Common).superClass + setUpCauseIfNeeded(wrapperSuperType as? Wrapper) + } + + constructor(wrapper: Wrapper, stackTrace: List) : this(wrapper.value as Throwable, wrapper.irClass, stackTrace) { + setUpCauseIfNeeded(wrapper) + } + + constructor( + exception: Throwable, irClass: IrClass, stackTrace: List + ) : this(irClass, evaluateFields(exception, irClass, stackTrace), stackTrace + evaluateAdditionalStackTrace(exception)) { + if (irClass.name.asString() != exception::class.java.simpleName) { + // ir class wasn't found in classpath, a stub was passed => need to save java class hierarchy + this.exceptionFqName = exception::class.java.name + exceptionHierarchy += this.exceptionFqName + generateSequence(exception::class.java.superclass) { it.superclass }.forEach { exceptionHierarchy += it.name } + exceptionHierarchy.removeAt(exceptionHierarchy.lastIndex) // remove unnecessary java.lang.Object + } + } + + data class ExceptionData(val state: ExceptionState) : Throwable() { + override val message: String? = state.getMessage().value + override fun fillInStackTrace() = this + + override fun toString(): String = state.getMessageWithName() + } + + private fun setUpCauseIfNeeded(wrapper: Wrapper?) { + val cause = (wrapper?.value as? Throwable)?.cause as? ExceptionData + setCause(cause?.state) + if (getMessage().value == null && cause != null) { + val causeMessage = cause.state.exceptionFqName + (cause.state.getMessage().value?.let { ": $it" } ?: "") + setMessage(causeMessage) + } + } + + fun isSubtypeOf(ancestor: IrClass): Boolean { + if (exceptionHierarchy.isNotEmpty()) { + return exceptionHierarchy.any { it.contains(ancestor.name.asString()) } + } + return irClass.isSubclassOf(ancestor) + } + + private fun setMessage(messageValue: String? = null) { + setState(Variable(messageProperty.descriptor, Primitive(messageValue, messageProperty.getter!!.returnType))) + } + + private fun setCause(causeValue: State?) { + setState(Variable(causeProperty.descriptor, causeValue ?: Primitive(null, causeProperty.getter!!.returnType))) + } + + fun getMessage(): Primitive = getState(messageProperty.descriptor) as Primitive + private fun getMessageWithName(): String = getMessage().value?.let { "$exceptionFqName: $it" } ?: exceptionFqName + + fun getCause(): ExceptionState? = getState(causeProperty.descriptor)?.let { if (it is ExceptionState) it else null } + + fun getFullDescription(): String { + // TODO remainder of the stack trace with "..." + val message = getMessage().value.let { if (it?.isNotEmpty() == true) ": $it" else "" } + val prefix = if (stackTrace.isNotEmpty()) "\n\t" else "" + val postfix = if (stackTrace.size > 10) "\n\t..." else "" + val causeMessage = getCause()?.getFullDescription()?.replaceFirst("Exception ", "\nCaused by: ") ?: "" + return "Exception $exceptionFqName$message" + + stackTrace.subList(0, min(stackTrace.size, 10)).joinToString(separator = "\n\t", prefix = prefix, postfix = postfix) + + causeMessage + } + + fun getThisAsCauseForException() = ExceptionData(this) + + override fun copy(): State { + return ExceptionState(irClass, fields, stackTrace, subClass ?: this) + } + + companion object { + private fun IrClass.getPropertyByName(name: String): IrProperty { + val getPropertyFun = this.declarations.firstOrNull { it.nameForIrSerialization.asString().contains("get-$name") } + return (getPropertyFun as? IrFunctionImpl)?.correspondingPropertySymbol?.owner + ?: this.declarations.single { it.nameForIrSerialization.asString() == name } as IrProperty + } + + private fun evaluateFields(exception: Throwable, irClass: IrClass, stackTrace: List): MutableList { + val messageProperty = irClass.getPropertyByName("message") + val causeProperty = irClass.getPropertyByName("cause") + + val messageVar = Variable(messageProperty.descriptor, exception.message.toState(messageProperty.getter!!.returnType)) + val causeVar = exception.cause?.let { + Variable(causeProperty.descriptor, ExceptionState(it, irClass, stackTrace + it.stackTrace.reversed().map { "at $it" })) + } + return listOfNotNull(messageVar, causeVar).toMutableList() + } + + private fun evaluateAdditionalStackTrace(e: Throwable): List { + // TODO do we really need this?... It will point to JVM stdlib + val additionalStack = mutableListOf() + if (e.stackTrace.any { it.className == "java.lang.invoke.MethodHandle" }) { + for ((index, stackTraceElement) in e.stackTrace.withIndex()) { + if (stackTraceElement.methodName == "invokeWithArguments") { + additionalStack.addAll(e.stackTrace.slice(0 until index).reversed().map { "at $it" }) + break + } + } + + var cause = e.cause + val lastNeededValue = e.stackTrace.first().let { it.className + "." + it.methodName } + while (cause != null) { + for ((causeStackIndex, causeStackTraceElement) in cause.stackTrace.withIndex()) { + val currentStackTraceValue = causeStackTraceElement.let { it.className + "." + it.methodName } + if (currentStackTraceValue == lastNeededValue) { + cause.stackTrace = cause.stackTrace.sliceArray(0 until causeStackIndex).reversedArray() + break + } + } + cause = cause.cause + } + } + return additionalStack + } + } +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Lambda.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Lambda.kt new file mode 100644 index 00000000000..b9bd9100d3d --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Lambda.kt @@ -0,0 +1,37 @@ +/* + * 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.state + +import org.jetbrains.kotlin.backend.common.interpreter.equalTo +import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization +import org.jetbrains.kotlin.ir.util.nameForIrSerialization + +class Lambda(val irFunction: IrFunction, override val irClass: IrClass) : State { + override val fields: MutableList = mutableListOf() + + // irFunction is anonymous declaration, but irCall will contain descriptor of invoke method from Function interface + private val invokeDescriptor = irClass.declarations.single { it.nameForIrSerialization.asString() == "invoke" }.descriptor + + override fun setState(newVar: Variable) { + throw UnsupportedOperationException("Method setState is not supported in Lambda class") + } + + override fun getIrFunction(descriptor: FunctionDescriptor): IrFunction? { + return if (invokeDescriptor.equalTo(descriptor)) irFunction else null + } + + override fun copy(): State { + return Lambda(irFunction, irClass) + } + + override fun toString(): String { + return "Lambda(${irClass.fqNameForIrSerialization})" + } +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Primitive.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Primitive.kt new file mode 100644 index 00000000000..a92778d9ee9 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Primitive.kt @@ -0,0 +1,65 @@ +/* + * 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.state + +import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrProperty +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.classOrNull + +class Primitive(var value: T, val type: IrType) : State { + override val fields: MutableList = mutableListOf() + override val irClass: IrClass = type.classOrNull!!.owner + + override fun getState(descriptor: DeclarationDescriptor): State { + return super.getState(descriptor) ?: this + } + + override fun setState(newVar: Variable) { + newVar.state as? Primitive ?: throw IllegalArgumentException("Cannot set $newVar in current $this") + value = newVar.state.value + } + + override fun copy(): State { + return Primitive(value, type) + } + + override fun getIrFunction(descriptor: FunctionDescriptor): IrFunction? { + // must add property's getter to declaration's list because they are not present in ir class for primitives + val declarations = irClass.declarations.map { if (it is IrProperty) it.getter else it } + return declarations.filterIsInstance() + .filter { it.descriptor.name == descriptor.name } + .firstOrNull { it.descriptor.valueParameters.map { it.type } == descriptor.valueParameters.map { it.type } } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Primitive<*> + + if (value != other.value) return false + if (type != other.type) return false + if (fields != other.fields) return false + + return true + } + + override fun hashCode(): Int { + var result = value?.hashCode() ?: 0 + result = 31 * result + type.hashCode() + result = 31 * result + fields.hashCode() + return result + } + + override fun toString(): String { + return "Primitive(value=$value, type=${irClass.descriptor.defaultType})" + } +} diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/State.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/State.kt index 94cc0c05c58..626301fea32 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/State.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/State.kt @@ -5,25 +5,12 @@ package org.jetbrains.kotlin.backend.common.interpreter.state -import org.jetbrains.kotlin.backend.common.interpreter.* -import org.jetbrains.kotlin.backend.common.interpreter.builtins.evaluateIntrinsicAnnotation +import org.jetbrains.kotlin.backend.common.interpreter.equalTo import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable -import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrProperty -import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl -import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol -import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.name.FqNameUnsafe -import java.lang.invoke.MethodHandle -import java.lang.invoke.MethodHandles -import java.lang.invoke.MethodType -import kotlin.math.min interface State { val fields: MutableList @@ -46,411 +33,3 @@ interface State { fun getIrFunction(descriptor: FunctionDescriptor): IrFunction? } - -class Primitive(var value: T, val type: IrType) : State { - override val fields: MutableList = mutableListOf() - override val irClass: IrClass = type.classOrNull!!.owner - - override fun getState(descriptor: DeclarationDescriptor): State { - return super.getState(descriptor) ?: this - } - - override fun setState(newVar: Variable) { - newVar.state as? Primitive ?: throw IllegalArgumentException("Cannot set $newVar in current $this") - value = newVar.state.value - } - - override fun copy(): State { - return Primitive(value, type) - } - - override fun getIrFunction(descriptor: FunctionDescriptor): IrFunction? { - // must add property's getter to declaration's list because they are not present in ir class for primitives - val declarations = irClass.declarations.map { if (it is IrProperty) it.getter else it } - return declarations.filterIsInstance() - .filter { it.descriptor.name == descriptor.name } - .firstOrNull { it.descriptor.valueParameters.map { it.type } == descriptor.valueParameters.map { it.type } } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as Primitive<*> - - if (value != other.value) return false - if (type != other.type) return false - if (fields != other.fields) return false - - return true - } - - override fun hashCode(): Int { - var result = value?.hashCode() ?: 0 - result = 31 * result + type.hashCode() - result = 31 * result + fields.hashCode() - return result - } - - override fun toString(): String { - return "Primitive(value=$value, type=${irClass.descriptor.defaultType})" - } -} - -abstract class Complex( - override val irClass: IrClass, override val fields: MutableList, var superClass: Complex?, var subClass: Complex? -) : State { - fun setSuperClassInstance(superClass: Complex) { - this.superClass = superClass - superClass.subClass = this - } - - fun getOriginal(): Complex { - return subClass?.getOriginal() ?: this - } - - fun irClassFqName(): String { - return irClass.fqNameForIrSerialization.toString() - } - - override fun setState(newVar: Variable) { - when (val oldState = fields.firstOrNull { it.descriptor == newVar.descriptor }) { - null -> fields.add(newVar) // newVar isn't present in value list - else -> fields[fields.indexOf(oldState)] = newVar // newVar already present - } - } - - fun setStatesFrom(state: Complex) = state.fields.forEach { setState(it) } - - override fun getIrFunction(descriptor: FunctionDescriptor): IrFunction? { - if (descriptor is PropertyGetterDescriptor) - return irClass.declarations.filterIsInstance().mapNotNull { it.getter }.single { it.descriptor.equalTo(descriptor) } - - return irClass.declarations.filterIsInstance().singleOrNull { it.descriptor.equalTo(descriptor) } - } -} - -class Common private constructor( - override val irClass: IrClass, override val fields: MutableList, superClass: Complex?, subClass: Complex? -) : Complex(irClass, fields, superClass, subClass) { - - constructor(irClass: IrClass) : this(irClass, mutableListOf(), null, null) - - fun getToStringFunction(): IrFunctionImpl { - return irClass.declarations.filterIsInstance() - .filter { it.descriptor.name.asString() == "toString" } - .first { it.descriptor.valueParameters.isEmpty() } as IrFunctionImpl - } - - override fun copy(): State { - return Common(irClass, fields, superClass, subClass ?: this) - } - - override fun toString(): String { - return "Common(obj='${irClass.fqNameForIrSerialization}', super=$superClass, values=$fields)" - } -} - -class Wrapper private constructor( - val value: Any, override val irClass: IrClass, subClass: Complex? -) : Complex(irClass, mutableListOf(), null, subClass) { - - private val typeFqName = irClass.fqNameForIrSerialization.toUnsafe() - private val receiverClass = irClass.defaultType.getClass(true) - - constructor(value: Any, irClass: IrClass) : this(value, irClass, null) - - fun getMethod(irFunction: IrFunction): MethodHandle? { - // if function is actually a getter, then use "get${property.name.capitalize()}" as method name - val propertyName = (irFunction as? IrFunctionImpl)?.correspondingPropertySymbol?.owner?.name?.asString() - val propertyExplicitCall = propertyName?.takeIf { receiverClass.methods.map { it.name }.contains(it) } - val propertyGetCall = "get${propertyName?.capitalize()}".takeIf { receiverClass.methods.map { it.name }.contains(it) } - - // intrinsicName is used to get correct java method - // for example: - method 'get' in kotlin StringBuilder is actually 'charAt' in java StringBuilder - val intrinsicName = irFunction.getEvaluateIntrinsicValue() - if (intrinsicName?.isEmpty() == true) return null - val methodName = intrinsicName ?: propertyExplicitCall ?: propertyGetCall ?: irFunction.name.toString() - - val methodType = irFunction.getMethodType() - return MethodHandles.lookup().findVirtual(receiverClass, methodName, methodType) - } - - override fun setState(newVar: Variable) { - throw UnsupportedOperationException("Method setState is not supported in Wrapper class") - } - - companion object { - private val companionObjectValue = mapOf("kotlin.text.Regex\$Companion" to Regex.Companion) - - fun getCompanionObject(irClass: IrClass): Wrapper { - val objectName = irClass.getEvaluateIntrinsicValue()!! - val objectValue = companionObjectValue[objectName] ?: throw AssertionError("Companion object $objectName cannot be interpreted") - return Wrapper(objectValue, irClass) - } - - fun getConstructorMethod(irConstructor: IrFunction): MethodHandle { - val methodType = irConstructor.getMethodType() - - return MethodHandles.lookup().findConstructor(irConstructor.returnType.getClass(true), methodType) - } - - fun getStaticMethod(irFunction: IrFunction): MethodHandle? { - val intrinsicName = irFunction.getEvaluateIntrinsicValue() - if (intrinsicName?.isEmpty() == true) return null - val jvmClassName = Class.forName(intrinsicName!!) - - val methodType = irFunction.getMethodType() - return MethodHandles.lookup().findStatic(jvmClassName, irFunction.name.asString(), methodType) - } - - fun getEnumEntry(enumClass: IrClass): MethodHandle? { - val intrinsicName = enumClass.getEvaluateIntrinsicValue() - if (intrinsicName?.isEmpty() == true) return null - val enumClassName = Class.forName(intrinsicName!!) - - val methodType = MethodType.methodType(enumClassName, String::class.java) - return MethodHandles.lookup().findStatic(enumClassName, "valueOf", methodType) - } - - private fun IrFunction.getMethodType(): MethodType { - 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()) - - MethodType.methodType(returnClass, listOfNotNull(extensionClass) + argsClasses) - } else { - // for constructors - MethodType.methodType(Void::class.javaPrimitiveType, argsClasses) - } - } - - private fun IrType.getClass(asObject: Boolean): Class { - val fqName = this.getFqName() - val owner = this.classOrNull?.owner - return when { - this.isPrimitiveType() -> getPrimitiveClass(fqName!!, asObject) - this.isArray() -> if (asObject) Array::class.javaObjectType else Array::class.java - owner.hasAnnotation(evaluateIntrinsicAnnotation) -> Class.forName(owner!!.getEvaluateIntrinsicValue()) - //TODO primitive array - this.isTypeParameter() -> Any::class.java - else -> JavaToKotlinClassMap.mapKotlinToJava(FqNameUnsafe(fqName!!))?.let { Class.forName(it.asSingleFqName().toString()) } - } ?: Class.forName(fqName) - } - - private fun IrFunction.getOriginalOverriddenSymbols(): MutableList { - val overriddenSymbols = mutableListOf() - if (this is IrFunctionImpl) { - val pool = this.overriddenSymbols.toMutableList() - val iterator = pool.listIterator() - for (symbol in iterator) { - if (symbol.owner.overriddenSymbols.isEmpty()) { - overriddenSymbols += symbol - iterator.remove() - } else { - symbol.owner.overriddenSymbols.forEach { iterator.add(it) } - } - } - } - - if (overriddenSymbols.isEmpty()) overriddenSymbols.add(this.symbol) - return overriddenSymbols - } - - private fun IrFunction.isExtensionReceiverPrimitive(): Boolean { - return this.extensionReceiverParameter?.type?.isPrimitiveType() == false - } - - private fun IrFunction.isReturnTypePrimitiveAsObject(): Boolean { - for (symbol in getOriginalOverriddenSymbols()) { - if (!symbol.owner.returnType.isTypeParameter() && !symbol.owner.returnType.isNullable()) { - return false - } - } - return true - } - - private fun IrFunction.isValueParameterPrimitiveAsObject(index: Int): Boolean { - for (symbol in getOriginalOverriddenSymbols()) { - if (!symbol.owner.valueParameters[index].type.isTypeParameter() && !symbol.owner.valueParameters[index].type.isNullable()) { - return false - } - } - return true - } - } - - override fun copy(): State { - return Wrapper(value, irClass, subClass ?: this) - } - - override fun toString(): String { - return "Wrapper(obj='$typeFqName', value=$value)" - } -} - -class Lambda(val irFunction: IrFunction, override val irClass: IrClass) : State { - override val fields: MutableList = mutableListOf() - - // irFunction is anonymous declaration, but irCall will contain descriptor of invoke method from Function interface - private val invokeDescriptor = irClass.declarations.single { it.nameForIrSerialization.asString() == "invoke" }.descriptor - - override fun setState(newVar: Variable) { - throw UnsupportedOperationException("Method setState is not supported in Lambda class") - } - - override fun getIrFunction(descriptor: FunctionDescriptor): IrFunction? { - return if (invokeDescriptor.equalTo(descriptor)) irFunction else null - } - - override fun copy(): State { - return Lambda(irFunction, irClass) - } - - override fun toString(): String { - return "Lambda(${irClass.fqNameForIrSerialization})" - } -} - -class ExceptionState private constructor( - override val irClass: IrClass, override val fields: MutableList, stackTrace: List, subClass: Complex? = null -) : Complex(irClass, fields, null, subClass) { - - private lateinit var exceptionFqName: String - private val exceptionHierarchy = mutableListOf() - private val messageProperty = irClass.getPropertyByName("message") - private val causeProperty = irClass.getPropertyByName("cause") - - private val stackTrace: List = stackTrace.reversed() - - init { - if (!this::exceptionFqName.isInitialized) this.exceptionFqName = irClassFqName() - - if (fields.none { it.descriptor.equalTo(messageProperty.descriptor) }) { - setMessage() - } - } - - constructor(common: Common, stackTrace: List) : this(common.irClass, common.fields, stackTrace) { - var wrapperSuperType: Complex? = common - while (wrapperSuperType != null && wrapperSuperType !is Wrapper) wrapperSuperType = (wrapperSuperType as Common).superClass - setUpCauseIfNeeded(wrapperSuperType as? Wrapper) - } - - constructor(wrapper: Wrapper, stackTrace: List) : this(wrapper.value as Throwable, wrapper.irClass, stackTrace) { - setUpCauseIfNeeded(wrapper) - } - - constructor( - exception: Throwable, irClass: IrClass, stackTrace: List - ) : this(irClass, evaluateFields(exception, irClass, stackTrace), stackTrace + evaluateAdditionalStackTrace(exception)) { - if (irClass.name.asString() != exception::class.java.simpleName) { - // ir class wasn't found in classpath, a stub was passed => need to save java class hierarchy - this.exceptionFqName = exception::class.java.name - exceptionHierarchy += this.exceptionFqName - generateSequence(exception::class.java.superclass) { it.superclass }.forEach { exceptionHierarchy += it.name } - exceptionHierarchy.removeAt(exceptionHierarchy.lastIndex) // remove unnecessary java.lang.Object - } - } - - data class ExceptionData(val state: ExceptionState) : Throwable() { - override val message: String? = state.getMessage().value - override fun fillInStackTrace() = this - - override fun toString(): String = state.getMessageWithName() - } - - private fun setUpCauseIfNeeded(wrapper: Wrapper?) { - val cause = (wrapper?.value as? Throwable)?.cause as? ExceptionData - setCause(cause?.state) - if (getMessage().value == null && cause != null) { - val causeMessage = cause.state.exceptionFqName + (cause.state.getMessage().value?.let { ": $it" } ?: "") - setMessage(causeMessage) - } - } - - fun isSubtypeOf(ancestor: IrClass): Boolean { - if (exceptionHierarchy.isNotEmpty()) { - return exceptionHierarchy.any { it.contains(ancestor.name.asString()) } - } - return irClass.isSubclassOf(ancestor) - } - - private fun setMessage(messageValue: String? = null) { - setState(Variable(messageProperty.descriptor, Primitive(messageValue, messageProperty.getter!!.returnType))) - } - - private fun setCause(causeValue: State?) { - setState(Variable(causeProperty.descriptor, causeValue ?: Primitive(null, causeProperty.getter!!.returnType))) - } - - fun getMessage(): Primitive = getState(messageProperty.descriptor) as Primitive - private fun getMessageWithName(): String = getMessage().value?.let { "$exceptionFqName: $it" } ?: exceptionFqName - - fun getCause(): ExceptionState? = getState(causeProperty.descriptor)?.let { if (it is ExceptionState) it else null } - - fun getFullDescription(): String { - // TODO remainder of the stack trace with "..." - val message = getMessage().value.let { if (it?.isNotEmpty() == true) ": $it" else "" } - val prefix = if (stackTrace.isNotEmpty()) "\n\t" else "" - val postfix = if (stackTrace.size > 10) "\n\t..." else "" - val causeMessage = getCause()?.getFullDescription()?.replaceFirst("Exception ", "\nCaused by: ") ?: "" - return "Exception $exceptionFqName$message" + - stackTrace.subList(0, min(stackTrace.size, 10)).joinToString(separator = "\n\t", prefix = prefix, postfix = postfix) + - causeMessage - } - - fun getThisAsCauseForException() = ExceptionData(this) - - override fun copy(): State { - return ExceptionState(irClass, fields, stackTrace, subClass ?: this) - } - - companion object { - private fun IrClass.getPropertyByName(name: String): IrProperty { - val getPropertyFun = this.declarations.firstOrNull { it.nameForIrSerialization.asString().contains("get-$name") } - return (getPropertyFun as? IrFunctionImpl)?.correspondingPropertySymbol?.owner - ?: this.declarations.single { it.nameForIrSerialization.asString() == name } as IrProperty - } - - private fun evaluateFields(exception: Throwable, irClass: IrClass, stackTrace: List): MutableList { - val messageProperty = irClass.getPropertyByName("message") - val causeProperty = irClass.getPropertyByName("cause") - - val messageVar = Variable(messageProperty.descriptor, exception.message.toState(messageProperty.getter!!.returnType)) - val causeVar = exception.cause?.let { - Variable(causeProperty.descriptor, ExceptionState(it, irClass, stackTrace + it.stackTrace.reversed().map { "at $it" })) - } - return listOfNotNull(messageVar, causeVar).toMutableList() - } - - private fun evaluateAdditionalStackTrace(e: Throwable): List { - // TODO do we really need this?... It will point to JVM stdlib - val additionalStack = mutableListOf() - if (e.stackTrace.any { it.className == "java.lang.invoke.MethodHandle" }) { - for ((index, stackTraceElement) in e.stackTrace.withIndex()) { - if (stackTraceElement.methodName == "invokeWithArguments") { - additionalStack.addAll(e.stackTrace.slice(0 until index).reversed().map { "at $it" }) - break - } - } - - var cause = e.cause - val lastNeededValue = e.stackTrace.first().let { it.className + "." + it.methodName } - while (cause != null) { - for ((causeStackIndex, causeStackTraceElement) in cause.stackTrace.withIndex()) { - val currentStackTraceValue = causeStackTraceElement.let { it.className + "." + it.methodName } - if (currentStackTraceValue == lastNeededValue) { - cause.stackTrace = cause.stackTrace.sliceArray(0 until causeStackIndex).reversedArray() - break - } - } - cause = cause.cause - } - } - return additionalStack - } - } -} diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Wrapper.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Wrapper.kt new file mode 100644 index 00000000000..8730bce9915 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Wrapper.kt @@ -0,0 +1,166 @@ +/* + * 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.state + +import org.jetbrains.kotlin.backend.common.interpreter.builtins.evaluateIntrinsicAnnotation +import org.jetbrains.kotlin.backend.common.interpreter.getEvaluateIntrinsicValue +import org.jetbrains.kotlin.backend.common.interpreter.getFqName +import org.jetbrains.kotlin.backend.common.interpreter.getPrimitiveClass +import org.jetbrains.kotlin.backend.common.interpreter.hasAnnotation +import org.jetbrains.kotlin.backend.common.interpreter.stack.Variable +import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +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.isTypeParameter +import org.jetbrains.kotlin.name.FqNameUnsafe +import java.lang.invoke.MethodHandle +import java.lang.invoke.MethodHandles +import java.lang.invoke.MethodType + +class Wrapper private constructor( + val value: Any, override val irClass: IrClass, subClass: Complex? +) : Complex(irClass, mutableListOf(), null, subClass) { + + private val typeFqName = irClass.fqNameForIrSerialization.toUnsafe() + private val receiverClass = irClass.defaultType.getClass(true) + + constructor(value: Any, irClass: IrClass) : this(value, irClass, null) + + fun getMethod(irFunction: IrFunction): MethodHandle? { + // if function is actually a getter, then use "get${property.name.capitalize()}" as method name + val propertyName = (irFunction as? IrFunctionImpl)?.correspondingPropertySymbol?.owner?.name?.asString() + val propertyExplicitCall = propertyName?.takeIf { receiverClass.methods.map { it.name }.contains(it) } + val propertyGetCall = "get${propertyName?.capitalize()}".takeIf { receiverClass.methods.map { it.name }.contains(it) } + + // intrinsicName is used to get correct java method + // for example: - method 'get' in kotlin StringBuilder is actually 'charAt' in java StringBuilder + val intrinsicName = irFunction.getEvaluateIntrinsicValue() + if (intrinsicName?.isEmpty() == true) return null + val methodName = intrinsicName ?: propertyExplicitCall ?: propertyGetCall ?: irFunction.name.toString() + + val methodType = irFunction.getMethodType() + return MethodHandles.lookup().findVirtual(receiverClass, methodName, methodType) + } + + override fun setState(newVar: Variable) { + throw UnsupportedOperationException("Method setState is not supported in Wrapper class") + } + + companion object { + private val companionObjectValue = mapOf("kotlin.text.Regex\$Companion" to Regex.Companion) + + fun getCompanionObject(irClass: IrClass): Wrapper { + val objectName = irClass.getEvaluateIntrinsicValue()!! + val objectValue = companionObjectValue[objectName] ?: throw AssertionError("Companion object $objectName cannot be interpreted") + return Wrapper(objectValue, irClass) + } + + fun getConstructorMethod(irConstructor: IrFunction): MethodHandle { + val methodType = irConstructor.getMethodType() + + return MethodHandles.lookup().findConstructor(irConstructor.returnType.getClass(true), methodType) + } + + fun getStaticMethod(irFunction: IrFunction): MethodHandle? { + val intrinsicName = irFunction.getEvaluateIntrinsicValue() + if (intrinsicName?.isEmpty() == true) return null + val jvmClassName = Class.forName(intrinsicName!!) + + val methodType = irFunction.getMethodType() + return MethodHandles.lookup().findStatic(jvmClassName, irFunction.name.asString(), methodType) + } + + fun getEnumEntry(enumClass: IrClass): MethodHandle? { + val intrinsicName = enumClass.getEvaluateIntrinsicValue() + if (intrinsicName?.isEmpty() == true) return null + val enumClassName = Class.forName(intrinsicName!!) + + val methodType = MethodType.methodType(enumClassName, String::class.java) + return MethodHandles.lookup().findStatic(enumClassName, "valueOf", methodType) + } + + private fun IrFunction.getMethodType(): MethodType { + 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()) + + MethodType.methodType(returnClass, listOfNotNull(extensionClass) + argsClasses) + } else { + // for constructors + MethodType.methodType(Void::class.javaPrimitiveType, argsClasses) + } + } + + private fun IrType.getClass(asObject: Boolean): Class { + val fqName = this.getFqName() + val owner = this.classOrNull?.owner + return when { + this.isPrimitiveType() -> getPrimitiveClass(fqName!!, asObject) + this.isArray() -> if (asObject) Array::class.javaObjectType else Array::class.java + owner.hasAnnotation(evaluateIntrinsicAnnotation) -> Class.forName(owner!!.getEvaluateIntrinsicValue()) + //TODO primitive array + this.isTypeParameter() -> Any::class.java + else -> JavaToKotlinClassMap.mapKotlinToJava(FqNameUnsafe(fqName!!))?.let { Class.forName(it.asSingleFqName().toString()) } + } ?: Class.forName(fqName) + } + + private fun IrFunction.getOriginalOverriddenSymbols(): MutableList { + val overriddenSymbols = mutableListOf() + if (this is IrFunctionImpl) { + val pool = this.overriddenSymbols.toMutableList() + val iterator = pool.listIterator() + for (symbol in iterator) { + if (symbol.owner.overriddenSymbols.isEmpty()) { + overriddenSymbols += symbol + iterator.remove() + } else { + symbol.owner.overriddenSymbols.forEach { iterator.add(it) } + } + } + } + + if (overriddenSymbols.isEmpty()) overriddenSymbols.add(this.symbol) + return overriddenSymbols + } + + private fun IrFunction.isExtensionReceiverPrimitive(): Boolean { + return this.extensionReceiverParameter?.type?.isPrimitiveType() == false + } + + private fun IrFunction.isReturnTypePrimitiveAsObject(): Boolean { + for (symbol in getOriginalOverriddenSymbols()) { + if (!symbol.owner.returnType.isTypeParameter() && !symbol.owner.returnType.isNullable()) { + return false + } + } + return true + } + + private fun IrFunction.isValueParameterPrimitiveAsObject(index: Int): Boolean { + for (symbol in getOriginalOverriddenSymbols()) { + if (!symbol.owner.valueParameters[index].type.isTypeParameter() && !symbol.owner.valueParameters[index].type.isNullable()) { + return false + } + } + return true + } + } + + override fun copy(): State { + return Wrapper(value, irClass, subClass ?: this) + } + + override fun toString(): String { + return "Wrapper(obj='$typeFqName', value=$value)" + } +} \ No newline at end of file