Refactoring: extract FrameVisitor class
This commit is contained in:
@@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.debugger.evaluate
|
||||||
|
|
||||||
|
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
|
||||||
|
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||||
|
import com.sun.jdi.ClassType
|
||||||
|
import com.sun.jdi.InvalidStackFrameException
|
||||||
|
import com.sun.jdi.ObjectReference
|
||||||
|
import org.jetbrains.eval4j.Value
|
||||||
|
import org.jetbrains.eval4j.jdi.asJdiValue
|
||||||
|
import org.jetbrains.eval4j.jdi.asValue
|
||||||
|
import org.jetbrains.eval4j.obj
|
||||||
|
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
|
||||||
|
class FrameVisitor(context: EvaluationContextImpl) {
|
||||||
|
private val project = context.getDebugProcess().getProject()
|
||||||
|
private val frame = context.getFrameProxy()?.getStackFrame()
|
||||||
|
|
||||||
|
public fun findValue(name: String, asmType: Type?, checkType: Boolean, failIfNotFound: Boolean): Value? {
|
||||||
|
if (frame == null) return null
|
||||||
|
|
||||||
|
try {
|
||||||
|
when (name) {
|
||||||
|
THIS_NAME -> {
|
||||||
|
val thisValue = findThis(asmType)
|
||||||
|
if (thisValue != null) {
|
||||||
|
return thisValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val localVariable = findLocalVariable(name, asmType, checkType)
|
||||||
|
if (localVariable != null) {
|
||||||
|
return localVariable
|
||||||
|
}
|
||||||
|
|
||||||
|
val capturedValName = getCapturedFieldName(name)
|
||||||
|
val capturedVal = findCapturedLocalVariable(capturedValName, asmType, checkType)
|
||||||
|
if (capturedVal != null) {
|
||||||
|
return capturedVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (!failIfNotFound)
|
||||||
|
null
|
||||||
|
else
|
||||||
|
throw EvaluateExceptionUtil.createEvaluateException("Cannot find local variable: name = $name${if (checkType) ", type = " + asmType.toString() else ""}")
|
||||||
|
}
|
||||||
|
catch(e: InvalidStackFrameException) {
|
||||||
|
throw EvaluateExceptionUtil.createEvaluateException("Local variable $name is unavailable in current frame")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findThis(asmType: Type?): Value? {
|
||||||
|
val thisObject = frame!!.thisObject()
|
||||||
|
if (thisObject != null) {
|
||||||
|
val eval4jValue = thisObject.asValue()
|
||||||
|
if (isValueOfCorrectType(eval4jValue, asmType, true)) return eval4jValue
|
||||||
|
}
|
||||||
|
|
||||||
|
val receiver = findValue(RECEIVER_NAME, asmType, checkType = true, failIfNotFound = false)
|
||||||
|
if (receiver != null) return receiver
|
||||||
|
|
||||||
|
val this0 = findValue(AsmUtil.CAPTURED_THIS_FIELD, asmType, checkType = true, failIfNotFound = false)
|
||||||
|
if (this0 != null) return this0
|
||||||
|
|
||||||
|
val `$this` = findValue("\$this", asmType, checkType = false, failIfNotFound = false)
|
||||||
|
if (`$this` != null) return `$this`
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findLocalVariable(name: String, asmType: Type?, checkType: Boolean): Value? {
|
||||||
|
val localVariable = frame!!.visibleVariableByName(name)
|
||||||
|
if (localVariable == null) return null
|
||||||
|
|
||||||
|
val eval4jValue = frame.getValue(localVariable).asValue()
|
||||||
|
val sharedVarValue = getValueIfSharedVar(eval4jValue, asmType, checkType)
|
||||||
|
if (sharedVarValue != null) {
|
||||||
|
return sharedVarValue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValueOfCorrectType(eval4jValue, asmType, checkType)) {
|
||||||
|
return eval4jValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findCapturedLocalVariable(name: String, asmType: Type?, checkType: Boolean): Value? {
|
||||||
|
val thisObject = frame?.thisObject() ?: return null
|
||||||
|
|
||||||
|
var thisObj: Value? = thisObject.asValue()
|
||||||
|
var capturedVal: Value? = null
|
||||||
|
while (capturedVal == null && thisObj != null) {
|
||||||
|
capturedVal = getField(thisObj, name, asmType, checkType)
|
||||||
|
if (capturedVal == null) {
|
||||||
|
thisObj = getField(thisObj, AsmUtil.CAPTURED_THIS_FIELD, null, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capturedVal != null) {
|
||||||
|
val sharedVarValue = getValueIfSharedVar(capturedVal, asmType, checkType)
|
||||||
|
if (sharedVarValue != null) {
|
||||||
|
return sharedVarValue
|
||||||
|
}
|
||||||
|
return capturedVal
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isValueOfCorrectType(value: Value, asmType: Type?, shouldCheckType: Boolean): Boolean {
|
||||||
|
if (!shouldCheckType || asmType == null || value.asmType == asmType) return true
|
||||||
|
if (project == null) return false
|
||||||
|
|
||||||
|
if ((value.obj() as? com.sun.jdi.ObjectReference)?.referenceType().isSubclass(asmType.getClassName())) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val thisDesc = value.asmType.getClassDescriptor(project)
|
||||||
|
val expDesc = asmType.getClassDescriptor(project)
|
||||||
|
return thisDesc != null && expDesc != null && runReadAction { DescriptorUtils.isSubclass(thisDesc, expDesc) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getField(owner: Value, name: String, asmType: Type?, checkType: Boolean): Value? {
|
||||||
|
try {
|
||||||
|
val obj = owner.asJdiValue(frame!!.virtualMachine(), owner.asmType)
|
||||||
|
if (obj !is ObjectReference) return null
|
||||||
|
|
||||||
|
val _class = obj.referenceType()
|
||||||
|
val field = _class.fieldByName(name)
|
||||||
|
if (field == null) return null
|
||||||
|
|
||||||
|
val fieldValue = obj.getValue(field).asValue()
|
||||||
|
if (isValueOfCorrectType(fieldValue, asmType, checkType)) return fieldValue
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Value.isSharedVar(): Boolean {
|
||||||
|
return this.asmType.getSort() == Type.OBJECT && this.asmType.getInternalName().startsWith(AsmTypes.REF_TYPE_PREFIX)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getValueIfSharedVar(value: Value, expectedType: Type?, checkType: Boolean): Value? {
|
||||||
|
if (!value.isSharedVar()) return null
|
||||||
|
|
||||||
|
val sharedVarValue = getField(value, "element", expectedType, checkType)
|
||||||
|
if (sharedVarValue != null && isValueOfCorrectType(sharedVarValue, expectedType, checkType)) {
|
||||||
|
return sharedVarValue
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCapturedFieldName(name: String) = when (name) {
|
||||||
|
RECEIVER_NAME -> AsmUtil.CAPTURED_RECEIVER_FIELD
|
||||||
|
THIS_NAME -> AsmUtil.CAPTURED_THIS_FIELD
|
||||||
|
AsmUtil.CAPTURED_RECEIVER_FIELD -> name
|
||||||
|
AsmUtil.CAPTURED_THIS_FIELD -> name
|
||||||
|
else -> "$$name"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun com.sun.jdi.Type?.isSubclass(superClassName: String): Boolean {
|
||||||
|
if (this !is ClassType) return false
|
||||||
|
if (allInterfaces().any { it.name() == superClassName }) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var superClass = this.superclass()
|
||||||
|
while (superClass != null) {
|
||||||
|
if (superClass.name() == superClassName) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
superClass = superClass.superclass()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-1
@@ -72,9 +72,10 @@ class KotlinEvaluateExpressionCache(val project: Project) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun canBeEvaluatedInThisContext(compiledData: CompiledDataDescriptor, context: EvaluationContextImpl): Boolean {
|
private fun canBeEvaluatedInThisContext(compiledData: CompiledDataDescriptor, context: EvaluationContextImpl): Boolean {
|
||||||
|
val frameVisitor = FrameVisitor(context)
|
||||||
return compiledData.parameters.all { p ->
|
return compiledData.parameters.all { p ->
|
||||||
val (name, jetType) = p
|
val (name, jetType) = p
|
||||||
val value = context.findLocalVariable(name, asmType = null, checkType = false, failIfNotFound = false)
|
val value = frameVisitor.findValue(name, asmType = null, checkType = false, failIfNotFound = false)
|
||||||
if (value == null) return@all false
|
if (value == null) return@all false
|
||||||
|
|
||||||
val thisDescriptor = value.asmType.getClassDescriptor(project)
|
val thisDescriptor = value.asmType.getClassDescriptor(project)
|
||||||
|
|||||||
@@ -35,15 +35,15 @@ import com.intellij.psi.impl.PsiFileFactoryImpl
|
|||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.testFramework.LightVirtualFile
|
import com.intellij.testFramework.LightVirtualFile
|
||||||
import com.intellij.util.ExceptionUtil
|
import com.intellij.util.ExceptionUtil
|
||||||
import com.sun.jdi.*
|
import com.sun.jdi.InvocationException
|
||||||
|
import com.sun.jdi.ObjectReference
|
||||||
|
import com.sun.jdi.VMDisconnectedException
|
||||||
|
import com.sun.jdi.VirtualMachine
|
||||||
import com.sun.jdi.request.EventRequest
|
import com.sun.jdi.request.EventRequest
|
||||||
import org.jetbrains.eval4j.*
|
import org.jetbrains.eval4j.*
|
||||||
import org.jetbrains.eval4j.Value
|
|
||||||
import org.jetbrains.eval4j.jdi.JDIEval
|
import org.jetbrains.eval4j.jdi.JDIEval
|
||||||
import org.jetbrains.eval4j.jdi.asJdiValue
|
import org.jetbrains.eval4j.jdi.asJdiValue
|
||||||
import org.jetbrains.eval4j.jdi.asValue
|
|
||||||
import org.jetbrains.eval4j.jdi.makeInitialFrame
|
import org.jetbrains.eval4j.jdi.makeInitialFrame
|
||||||
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
|
|
||||||
import org.jetbrains.kotlin.codegen.*
|
import org.jetbrains.kotlin.codegen.*
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
@@ -56,8 +56,6 @@ import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
|||||||
import org.jetbrains.kotlin.idea.JetLanguage
|
import org.jetbrains.kotlin.idea.JetLanguage
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.JavaResolveExtension
|
import org.jetbrains.kotlin.idea.caches.resolve.JavaResolveExtension
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.KotlinCacheService
|
import org.jetbrains.kotlin.idea.caches.resolve.KotlinCacheService
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinEvaluateExpressionCache.CompiledDataDescriptor
|
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinEvaluateExpressionCache.CompiledDataDescriptor
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinEvaluateExpressionCache.ParametersDescriptor
|
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinEvaluateExpressionCache.ParametersDescriptor
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.compilingEvaluator.loadClasses
|
import org.jetbrains.kotlin.idea.debugger.evaluate.compilingEvaluator.loadClasses
|
||||||
@@ -75,15 +73,12 @@ import org.jetbrains.kotlin.psi.codeFragmentUtil.debugTypeInfo
|
|||||||
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
|
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
|
||||||
import org.jetbrains.kotlin.resolve.AnalyzingUtils
|
import org.jetbrains.kotlin.resolve.AnalyzingUtils
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import org.jetbrains.kotlin.types.Flexibility
|
import org.jetbrains.kotlin.types.Flexibility
|
||||||
import org.jetbrains.org.objectweb.asm.*
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes.ASM5
|
import org.jetbrains.org.objectweb.asm.Opcodes.ASM5
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
import java.util.*
|
import java.util.Collections
|
||||||
|
|
||||||
private val RECEIVER_NAME = "\$receiver"
|
private val RECEIVER_NAME = "\$receiver"
|
||||||
private val THIS_NAME = "this"
|
private val THIS_NAME = "this"
|
||||||
@@ -304,7 +299,8 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun EvaluationContextImpl.getArgumentsForEval4j(parameterNames: List<String>, parameterTypes: Array<Type>): List<Value> {
|
private fun EvaluationContextImpl.getArgumentsForEval4j(parameterNames: List<String>, parameterTypes: Array<Type>): List<Value> {
|
||||||
return parameterNames.zip(parameterTypes).map { this.findLocalVariable(it.first, it.second, checkType = false, failIfNotFound = true)!! }
|
val frameVisitor = FrameVisitor(this)
|
||||||
|
return parameterNames.zip(parameterTypes).map { frameVisitor.findValue(it.first, it.second, checkType = false, failIfNotFound = true)!! }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createClassFileFactory(
|
private fun createClassFileFactory(
|
||||||
@@ -338,8 +334,10 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
DiagnosticSink.DO_NOTHING,
|
DiagnosticSink.DO_NOTHING,
|
||||||
null)
|
null)
|
||||||
|
|
||||||
|
val frameVisitor = FrameVisitor(context)
|
||||||
|
|
||||||
extractedFunction.getReceiverTypeReference()?.let {
|
extractedFunction.getReceiverTypeReference()?.let {
|
||||||
state.recordAnonymousType(it, THIS_NAME, context)
|
state.recordAnonymousType(it, THIS_NAME, frameVisitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (param in extractedFunction.getValueParameters()) {
|
for (param in extractedFunction.getValueParameters()) {
|
||||||
@@ -353,7 +351,7 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
exception("An exception occurs during Evaluate Expression Action")
|
exception("An exception occurs during Evaluate Expression Action")
|
||||||
}
|
}
|
||||||
|
|
||||||
state.recordAnonymousType(paramRef, paramName, context)
|
state.recordAnonymousType(paramRef, paramName, frameVisitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION)
|
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION)
|
||||||
@@ -362,12 +360,12 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun GenerationState.recordAnonymousType(typeReference: JetTypeReference, localVariableName: String, context: EvaluationContextImpl) {
|
private fun GenerationState.recordAnonymousType(typeReference: JetTypeReference, localVariableName: String, visitor: FrameVisitor) {
|
||||||
val paramAnonymousType = typeReference.debugTypeInfo
|
val paramAnonymousType = typeReference.debugTypeInfo
|
||||||
if (paramAnonymousType != null) {
|
if (paramAnonymousType != null) {
|
||||||
val declarationDescriptor = paramAnonymousType.getConstructor().getDeclarationDescriptor()
|
val declarationDescriptor = paramAnonymousType.getConstructor().getDeclarationDescriptor()
|
||||||
if (declarationDescriptor is ClassDescriptor) {
|
if (declarationDescriptor is ClassDescriptor) {
|
||||||
val localVariable = context.findLocalVariable(localVariableName, asmType = null, checkType = false, failIfNotFound = false)
|
val localVariable = visitor.findValue(localVariableName, asmType = null, checkType = false, failIfNotFound = false)
|
||||||
if (localVariable == null) {
|
if (localVariable == null) {
|
||||||
exception("Couldn't find local variable this in current frame to get classType for anonymous type ${paramAnonymousType}}")
|
exception("Couldn't find local variable this in current frame to get classType for anonymous type ${paramAnonymousType}}")
|
||||||
}
|
}
|
||||||
@@ -483,142 +481,6 @@ private fun SuspendContext.getInvokePolicy(): Int {
|
|||||||
return if (getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD) ObjectReference.INVOKE_SINGLE_THREADED else 0
|
return if (getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD) ObjectReference.INVOKE_SINGLE_THREADED else 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun com.sun.jdi.Type?.isSubclass(superClassName: String): Boolean {
|
|
||||||
if (this !is ClassType) return false
|
|
||||||
if (allInterfaces().any { it.name() == superClassName }) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
var superClass = this.superclass()
|
|
||||||
while (superClass != null) {
|
|
||||||
if (superClass.name() == superClassName) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
superClass = superClass.superclass()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun EvaluationContextImpl.findLocalVariable(name: String, asmType: Type?, checkType: Boolean, failIfNotFound: Boolean): Value? {
|
|
||||||
val project = getDebugProcess().getProject()
|
|
||||||
val frame = getFrameProxy()?.getStackFrame()
|
|
||||||
if (frame == null) return null
|
|
||||||
|
|
||||||
fun isValueOfCorrectType(value: Value, asmType: Type?, shouldCheckType: Boolean): Boolean {
|
|
||||||
if (!shouldCheckType || asmType == null || value.asmType == asmType) return true
|
|
||||||
if (project == null) return false
|
|
||||||
|
|
||||||
if ((value.obj() as? com.sun.jdi.ObjectReference)?.referenceType().isSubclass(asmType.getClassName())) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
val thisDesc = value.asmType.getClassDescriptor(project)
|
|
||||||
val expDesc = asmType.getClassDescriptor(project)
|
|
||||||
return thisDesc != null && expDesc != null && runReadAction { DescriptorUtils.isSubclass(thisDesc, expDesc) }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
when (name) {
|
|
||||||
THIS_NAME -> {
|
|
||||||
val thisObject = frame.thisObject()
|
|
||||||
if (thisObject != null) {
|
|
||||||
val eval4jValue = thisObject.asValue()
|
|
||||||
if (isValueOfCorrectType(eval4jValue, asmType, true)) return eval4jValue
|
|
||||||
}
|
|
||||||
|
|
||||||
val receiver = findLocalVariable(RECEIVER_NAME, asmType, checkType = true, failIfNotFound = false)
|
|
||||||
if (receiver != null) return receiver
|
|
||||||
|
|
||||||
val this0 = findLocalVariable(AsmUtil.CAPTURED_THIS_FIELD, asmType, checkType = true, failIfNotFound = false)
|
|
||||||
if (this0 != null) return this0
|
|
||||||
|
|
||||||
val `$this` = findLocalVariable("\$this", asmType, checkType = false, failIfNotFound = false)
|
|
||||||
if (`$this` != null) return `$this`
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
|
|
||||||
fun getField(owner: Value, name: String, asmType: Type?, checkType: Boolean): Value? {
|
|
||||||
try {
|
|
||||||
val obj = owner.asJdiValue(frame.virtualMachine(), owner.asmType)
|
|
||||||
if (obj !is ObjectReference) return null
|
|
||||||
|
|
||||||
val _class = obj.referenceType()
|
|
||||||
val field = _class.fieldByName(name)
|
|
||||||
if (field == null) return null
|
|
||||||
|
|
||||||
val fieldValue = obj.getValue(field).asValue()
|
|
||||||
if (isValueOfCorrectType(fieldValue, asmType, checkType)) return fieldValue
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
catch (e: Exception) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Value.isSharedVar(): Boolean {
|
|
||||||
return this.asmType.getSort() == Type.OBJECT && this.asmType.getInternalName().startsWith(AsmTypes.REF_TYPE_PREFIX)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getValueForSharedVar(value: Value, expectedType: Type?, checkType: Boolean): Value? {
|
|
||||||
val sharedVarValue = getField(value, "element", expectedType, checkType)
|
|
||||||
if (sharedVarValue != null && isValueOfCorrectType(sharedVarValue, expectedType, checkType)) {
|
|
||||||
return sharedVarValue
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val localVariable = frame.visibleVariableByName(name)
|
|
||||||
if (localVariable != null) {
|
|
||||||
val eval4jValue = frame.getValue(localVariable).asValue()
|
|
||||||
if (eval4jValue.isSharedVar()) {
|
|
||||||
val sharedVarValue = getValueForSharedVar(eval4jValue, asmType, checkType)
|
|
||||||
if (sharedVarValue != null) {
|
|
||||||
return sharedVarValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValueOfCorrectType(eval4jValue, asmType, checkType)) return eval4jValue
|
|
||||||
}
|
|
||||||
|
|
||||||
fun findCapturedVal(name: String): Value? {
|
|
||||||
var result: Value? = null
|
|
||||||
val thisObject = frame.thisObject() ?: return null
|
|
||||||
var thisObj: Value? = thisObject.asValue()
|
|
||||||
|
|
||||||
while (result == null && thisObj != null) {
|
|
||||||
result = getField(thisObj, name, asmType, checkType)
|
|
||||||
if (result == null) {
|
|
||||||
thisObj = getField(thisObj, AsmUtil.CAPTURED_THIS_FIELD, null, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
val capturedValName = getCapturedFieldName(name)
|
|
||||||
val capturedVal = findCapturedVal(capturedValName)
|
|
||||||
if (capturedVal != null) {
|
|
||||||
if (capturedVal.isSharedVar()) {
|
|
||||||
val sharedVarValue = getValueForSharedVar(capturedVal, asmType, checkType)
|
|
||||||
if (sharedVarValue != null) {
|
|
||||||
return sharedVarValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return capturedVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return if (!failIfNotFound)
|
|
||||||
null
|
|
||||||
else
|
|
||||||
throw EvaluateExceptionUtil.createEvaluateException("Cannot find local variable: name = $name${if (checkType) ", type = " + asmType.toString() else ""}")
|
|
||||||
}
|
|
||||||
catch(e: InvalidStackFrameException) {
|
|
||||||
throw EvaluateExceptionUtil.createEvaluateException("Local variable $name is unavailable in current frame")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Type.getClassDescriptor(project: Project): ClassDescriptor? {
|
fun Type.getClassDescriptor(project: Project): ClassDescriptor? {
|
||||||
if (AsmUtil.isPrimitive(this)) return null
|
if (AsmUtil.isPrimitive(this)) return null
|
||||||
|
|
||||||
@@ -635,13 +497,4 @@ fun Type.getClassDescriptor(project: Project): ClassDescriptor? {
|
|||||||
JavaResolveExtension.getResolver(project, clazz).resolveClass(JavaClassImpl(clazz))
|
JavaResolveExtension.getResolver(project, clazz).resolveClass(JavaClassImpl(clazz))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCapturedFieldName(name: String) = when (name) {
|
|
||||||
RECEIVER_NAME -> AsmUtil.CAPTURED_RECEIVER_FIELD
|
|
||||||
THIS_NAME -> AsmUtil.CAPTURED_THIS_FIELD
|
|
||||||
AsmUtil.CAPTURED_RECEIVER_FIELD -> name
|
|
||||||
AsmUtil.CAPTURED_THIS_FIELD -> name
|
|
||||||
else -> "$$name"
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user