Evaluate Expression: support anonymous objects evaluation
This commit is contained in:
@@ -17,8 +17,10 @@
|
|||||||
package org.jetbrains.kotlin.psi.codeFragmentUtil
|
package org.jetbrains.kotlin.psi.codeFragmentUtil
|
||||||
|
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import org.jetbrains.kotlin.psi.JetFile
|
|
||||||
import org.jetbrains.kotlin.psi.JetCodeFragment
|
import org.jetbrains.kotlin.psi.JetCodeFragment
|
||||||
|
import org.jetbrains.kotlin.psi.JetFile
|
||||||
|
import org.jetbrains.kotlin.psi.JetTypeReference
|
||||||
|
import org.jetbrains.kotlin.types.JetType
|
||||||
|
|
||||||
public val SUPPRESS_DIAGNOSTICS_IN_DEBUG_MODE: Key<Boolean> = Key.create<Boolean>("SUPPRESS_DIAGNOSTICS_IN_DEBUG_MODE")
|
public val SUPPRESS_DIAGNOSTICS_IN_DEBUG_MODE: Key<Boolean> = Key.create<Boolean>("SUPPRESS_DIAGNOSTICS_IN_DEBUG_MODE")
|
||||||
|
|
||||||
@@ -31,3 +33,14 @@ public var JetFile.suppressDiagnosticsInDebugMode: Boolean
|
|||||||
set(skip: Boolean) {
|
set(skip: Boolean) {
|
||||||
putUserData(SUPPRESS_DIAGNOSTICS_IN_DEBUG_MODE, skip)
|
putUserData(SUPPRESS_DIAGNOSTICS_IN_DEBUG_MODE, skip)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public val DEBUG_TYPE_REFERENCE_STRING: String = "DebugTypeKotlinRulezzzz"
|
||||||
|
|
||||||
|
public val DEBUG_TYPE_INFO: Key<JetType> = Key.create<JetType>("DEBUG_TYPE_INFO")
|
||||||
|
public var JetTypeReference.debugTypeInfo: JetType?
|
||||||
|
get() = getUserData(DEBUG_TYPE_INFO)
|
||||||
|
set(type: JetType?) {
|
||||||
|
if (type != null && this.getText() == DEBUG_TYPE_REFERENCE_STRING) {
|
||||||
|
putUserData(DEBUG_TYPE_INFO, type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.resolve.lazy.LazyEntity
|
|||||||
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
|
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
import org.jetbrains.kotlin.psi.debugText.getDebugText
|
import org.jetbrains.kotlin.psi.debugText.getDebugText
|
||||||
|
import org.jetbrains.kotlin.psi.codeFragmentUtil.debugTypeInfo
|
||||||
|
|
||||||
public class TypeResolver(
|
public class TypeResolver(
|
||||||
private val annotationResolver: AnnotationResolver,
|
private val annotationResolver: AnnotationResolver,
|
||||||
@@ -67,6 +68,12 @@ public class TypeResolver(
|
|||||||
val cachedType = c.trace.getBindingContext().get(BindingContext.TYPE, typeReference)
|
val cachedType = c.trace.getBindingContext().get(BindingContext.TYPE, typeReference)
|
||||||
if (cachedType != null) return type(cachedType)
|
if (cachedType != null) return type(cachedType)
|
||||||
|
|
||||||
|
if (typeReference.debugTypeInfo != null) {
|
||||||
|
val debugType = typeReference.debugTypeInfo
|
||||||
|
c.trace.record(BindingContext.TYPE, typeReference, debugType)
|
||||||
|
return type(debugType)
|
||||||
|
}
|
||||||
|
|
||||||
if (!c.allowBareTypes && lazinessToken.isLazy()) {
|
if (!c.allowBareTypes && lazinessToken.isLazy()) {
|
||||||
// Bare types can be allowed only inside expressions; lazy type resolution is only relevant for declarations
|
// Bare types can be allowed only inside expressions; lazy type resolution is only relevant for declarations
|
||||||
class LazyKotlinType : DelegatingType(), LazyEntity {
|
class LazyKotlinType : DelegatingType(), LazyEntity {
|
||||||
|
|||||||
@@ -16,69 +16,63 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.debugger.evaluate
|
package org.jetbrains.kotlin.idea.debugger.evaluate
|
||||||
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.*
|
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.debugger.SourcePosition
|
import com.intellij.debugger.SourcePosition
|
||||||
import com.intellij.debugger.engine.evaluation.*
|
import com.intellij.debugger.engine.SuspendContext
|
||||||
|
import com.intellij.debugger.engine.evaluation.EvaluateException
|
||||||
|
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil
|
||||||
|
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||||
import com.intellij.debugger.engine.evaluation.expression.*
|
import com.intellij.debugger.engine.evaluation.expression.*
|
||||||
import org.jetbrains.kotlin.resolve.AnalyzingUtils
|
import com.intellij.openapi.diagnostic.Attachment
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
import com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
|
||||||
import com.intellij.testFramework.LightVirtualFile
|
|
||||||
import org.jetbrains.kotlin.idea.JetLanguage
|
|
||||||
import org.jetbrains.kotlin.psi.JetFile
|
|
||||||
import com.intellij.psi.impl.PsiFileFactoryImpl
|
|
||||||
import com.intellij.psi.PsiFileFactory
|
|
||||||
import com.intellij.openapi.vfs.CharsetToolkit
|
import com.intellij.openapi.vfs.CharsetToolkit
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
import com.intellij.psi.JavaPsiFacade
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes.ASM5
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.org.objectweb.asm.*
|
import com.intellij.psi.PsiFileFactory
|
||||||
|
import com.intellij.psi.impl.PsiFileFactoryImpl
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
|
import com.intellij.testFramework.LightVirtualFile
|
||||||
|
import com.sun.jdi.*
|
||||||
|
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.makeInitialFrame
|
|
||||||
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.eval4j.jdi.asValue
|
import org.jetbrains.eval4j.jdi.asValue
|
||||||
import org.jetbrains.kotlin.psi.JetNamedFunction
|
import org.jetbrains.eval4j.jdi.makeInitialFrame
|
||||||
import org.jetbrains.kotlin.codegen.ClassFileFactory
|
|
||||||
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
|
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
|
import org.jetbrains.kotlin.codegen.*
|
||||||
import org.jetbrains.kotlin.psi.JetCodeFragment
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||||
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import com.intellij.openapi.diagnostic.Logger
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
|
|
||||||
import org.jetbrains.kotlin.diagnostics.Severity
|
import org.jetbrains.kotlin.diagnostics.Severity
|
||||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||||
import com.sun.jdi.request.EventRequest
|
import org.jetbrains.kotlin.idea.JetLanguage
|
||||||
import com.sun.jdi.ObjectReference
|
|
||||||
import com.intellij.debugger.engine.SuspendContext
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinEvaluateExpressionCache.*
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import com.sun.jdi.VirtualMachine
|
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
|
||||||
import com.sun.jdi.InvalidStackFrameException
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
|
||||||
import org.jetbrains.kotlin.psi.analysisContext
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
|
||||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
|
||||||
import com.intellij.psi.JavaPsiFacade
|
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.JavaResolveExtension
|
import org.jetbrains.kotlin.idea.caches.resolve.JavaResolveExtension
|
||||||
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
|
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
|
||||||
import com.intellij.openapi.project.Project
|
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinEvaluateExpressionCache.CompiledDataDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinEvaluateExpressionCache.ParametersDescriptor
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
|
||||||
import org.jetbrains.kotlin.types.Flexibility
|
|
||||||
import org.jetbrains.kotlin.psi.JetElement
|
|
||||||
import org.jetbrains.kotlin.idea.util.attachment.attachmentByPsiFile
|
|
||||||
import com.intellij.openapi.diagnostic.Attachment
|
|
||||||
import org.jetbrains.kotlin.idea.util.attachment.mergeAttachments
|
|
||||||
import com.sun.jdi.ClassType
|
|
||||||
import com.sun.jdi.InvocationException
|
|
||||||
import org.jetbrains.kotlin.idea.debugger.evaluate.compilingEvaluator.loadClasses
|
import org.jetbrains.kotlin.idea.debugger.evaluate.compilingEvaluator.loadClasses
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
|
import org.jetbrains.kotlin.idea.util.attachment.attachmentByPsiFile
|
||||||
|
import org.jetbrains.kotlin.idea.util.attachment.mergeAttachments
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.codeFragmentUtil.debugTypeInfo
|
||||||
|
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
|
||||||
|
import org.jetbrains.kotlin.resolve.AnalyzingUtils
|
||||||
|
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.types.Flexibility
|
||||||
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
|
import org.jetbrains.org.objectweb.asm.Opcodes.ASM5
|
||||||
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
|
||||||
private val RECEIVER_NAME = "\$receiver"
|
private val RECEIVER_NAME = "\$receiver"
|
||||||
private val THIS_NAME = "this"
|
private val THIS_NAME = "this"
|
||||||
@@ -126,7 +120,7 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
val compiledData = KotlinEvaluateExpressionCache.getOrCreateCompiledData(codeFragment, sourcePosition, context) {
|
val compiledData = KotlinEvaluateExpressionCache.getOrCreateCompiledData(codeFragment, sourcePosition, context) {
|
||||||
fragment, position ->
|
fragment, position ->
|
||||||
isCompiledDataFromCache = false
|
isCompiledDataFromCache = false
|
||||||
extractAndCompile(fragment, position)
|
extractAndCompile(fragment, position, context)
|
||||||
}
|
}
|
||||||
val result = runEval4j(context, compiledData)
|
val result = runEval4j(context, compiledData)
|
||||||
|
|
||||||
@@ -134,7 +128,7 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
|
|
||||||
// If bytecode was taken from cache and exception was thrown - recompile bytecode and run eval4j again
|
// If bytecode was taken from cache and exception was thrown - recompile bytecode and run eval4j again
|
||||||
if (isCompiledDataFromCache && result is ExceptionThrown && result.kind == ExceptionThrown.ExceptionKind.BROKEN_CODE) {
|
if (isCompiledDataFromCache && result is ExceptionThrown && result.kind == ExceptionThrown.ExceptionKind.BROKEN_CODE) {
|
||||||
return runEval4j(context, extractAndCompile(codeFragment, sourcePosition)).toJdiValue(virtualMachine)
|
return runEval4j(context, extractAndCompile(codeFragment, sourcePosition, context)).toJdiValue(virtualMachine)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.toJdiValue(virtualMachine)
|
return result.toJdiValue(virtualMachine)
|
||||||
@@ -159,7 +153,7 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
}
|
}
|
||||||
|
|
||||||
class object {
|
class object {
|
||||||
private fun extractAndCompile(codeFragment: JetCodeFragment, sourcePosition: SourcePosition): CompiledDataDescriptor {
|
private fun extractAndCompile(codeFragment: JetCodeFragment, sourcePosition: SourcePosition, context: EvaluationContextImpl): CompiledDataDescriptor {
|
||||||
codeFragment.checkForErrors()
|
codeFragment.checkForErrors()
|
||||||
|
|
||||||
val extractedFunction = getFunctionForExtractedFragment(codeFragment, sourcePosition.getFile(), sourcePosition.getLine())
|
val extractedFunction = getFunctionForExtractedFragment(codeFragment, sourcePosition.getFile(), sourcePosition.getLine())
|
||||||
@@ -167,7 +161,7 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
throw IllegalStateException("Code fragment cannot be extracted to function")
|
throw IllegalStateException("Code fragment cannot be extracted to function")
|
||||||
}
|
}
|
||||||
|
|
||||||
val classFileFactory = createClassFileFactory(codeFragment, extractedFunction)
|
val classFileFactory = createClassFileFactory(codeFragment, extractedFunction, context)
|
||||||
|
|
||||||
// KT-4509
|
// KT-4509
|
||||||
val outputFiles = (classFileFactory : OutputFileCollection).asList()
|
val outputFiles = (classFileFactory : OutputFileCollection).asList()
|
||||||
@@ -273,7 +267,7 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
return parameterNames.zip(parameterTypes).map { this.findLocalVariable(it.first, it.second, checkType = false, failIfNotFound = true)!! }
|
return parameterNames.zip(parameterTypes).map { this.findLocalVariable(it.first, it.second, checkType = false, failIfNotFound = true)!! }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createClassFileFactory(codeFragment: JetCodeFragment, extractedFunction: JetNamedFunction): ClassFileFactory {
|
private fun createClassFileFactory(codeFragment: JetCodeFragment, extractedFunction: JetNamedFunction, context: EvaluationContextImpl): ClassFileFactory {
|
||||||
return runReadAction {
|
return runReadAction {
|
||||||
val file = createFileForDebugger(codeFragment, extractedFunction)
|
val file = createFileForDebugger(codeFragment, extractedFunction)
|
||||||
|
|
||||||
@@ -287,12 +281,44 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
|
|||||||
listOf(file)
|
listOf(file)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
extractedFunction.getReceiverTypeReference()?.let {
|
||||||
|
state.recordAnonymousType(it, THIS_NAME, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (param in extractedFunction.getValueParameters()) {
|
||||||
|
val paramRef = param.getTypeReference()
|
||||||
|
val paramName = param.getName()
|
||||||
|
if (paramRef == null || paramName == null) {
|
||||||
|
logger.error("Each parameter for extracted function should have a name and a type reference",
|
||||||
|
Attachment("codeFragment.txt", codeFragment.getText()),
|
||||||
|
Attachment("extractedFunction.txt", extractedFunction.getText()))
|
||||||
|
|
||||||
|
exception("An exception occurs during Evaluate Expression Action")
|
||||||
|
}
|
||||||
|
|
||||||
|
state.recordAnonymousType(paramRef, paramName, context)
|
||||||
|
}
|
||||||
|
|
||||||
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION)
|
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION)
|
||||||
|
|
||||||
state.getFactory()
|
state.getFactory()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun GenerationState.recordAnonymousType(typeReference: JetTypeReference, localVariableName: String, context: EvaluationContextImpl) {
|
||||||
|
val paramAnonymousType = typeReference.debugTypeInfo
|
||||||
|
if (paramAnonymousType != null) {
|
||||||
|
val declarationDescriptor = paramAnonymousType.getConstructor().getDeclarationDescriptor()
|
||||||
|
if (declarationDescriptor is ClassDescriptor) {
|
||||||
|
val localVariable = context.findLocalVariable(localVariableName, asmType = null, checkType = false, failIfNotFound = false)
|
||||||
|
if (localVariable == null) {
|
||||||
|
exception("Couldn't find local variable this in current frame to get classType for anonymous type ${paramAnonymousType}}")
|
||||||
|
}
|
||||||
|
getBindingTrace().record(CodegenBinding.ASM_TYPE, declarationDescriptor, localVariable.asmType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun exception(msg: String) = throw EvaluateExceptionUtil.createEvaluateException(msg)
|
private fun exception(msg: String) = throw EvaluateExceptionUtil.createEvaluateException(msg)
|
||||||
|
|
||||||
private fun exception(e: Throwable): Nothing {
|
private fun exception(e: Throwable): Nothing {
|
||||||
@@ -351,6 +377,18 @@ private fun createFileForDebugger(codeFragment: JetCodeFragment,
|
|||||||
|
|
||||||
val jetFile = codeFragment.createJetFile("debugFile.kt", fileText)
|
val jetFile = codeFragment.createJetFile("debugFile.kt", fileText)
|
||||||
jetFile.suppressDiagnosticsInDebugMode = true
|
jetFile.suppressDiagnosticsInDebugMode = true
|
||||||
|
|
||||||
|
val list = jetFile.getDeclarations()
|
||||||
|
val function = list.get(0) as JetNamedFunction
|
||||||
|
|
||||||
|
function.getReceiverTypeReference()?.debugTypeInfo = extractedFunction.getReceiverTypeReference()?.debugTypeInfo
|
||||||
|
|
||||||
|
for ((newParam, oldParam) in function.getValueParameters().zip(extractedFunction.getValueParameters())) {
|
||||||
|
newParam.getTypeReference()?.debugTypeInfo = oldParam.getTypeReference()?.debugTypeInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
function.getTypeReference()?.debugTypeInfo = extractedFunction.getTypeReference()?.debugTypeInfo
|
||||||
|
|
||||||
return jetFile
|
return jetFile
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -451,8 +489,8 @@ fun EvaluationContextImpl.findLocalVariable(name: String, asmType: Type?, checkT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Value.isSharedVar(expectedType: Type?): Boolean {
|
fun Value.isSharedVar(): Boolean {
|
||||||
return expectedType != null && this.asmType == StackValue.sharedTypeForType(expectedType)
|
return this.asmType.getSort() == Type.OBJECT && this.asmType.getInternalName().startsWith(AsmTypes.REF_TYPE_PREFIX)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getValueForSharedVar(value: Value, expectedType: Type?, checkType: Boolean): Value? {
|
fun getValueForSharedVar(value: Value, expectedType: Type?, checkType: Boolean): Value? {
|
||||||
@@ -466,7 +504,7 @@ fun EvaluationContextImpl.findLocalVariable(name: String, asmType: Type?, checkT
|
|||||||
val localVariable = frame.visibleVariableByName(name)
|
val localVariable = frame.visibleVariableByName(name)
|
||||||
if (localVariable != null) {
|
if (localVariable != null) {
|
||||||
val eval4jValue = frame.getValue(localVariable).asValue()
|
val eval4jValue = frame.getValue(localVariable).asValue()
|
||||||
if (eval4jValue.isSharedVar(asmType)) {
|
if (eval4jValue.isSharedVar()) {
|
||||||
val sharedVarValue = getValueForSharedVar(eval4jValue, asmType, checkType)
|
val sharedVarValue = getValueForSharedVar(eval4jValue, asmType, checkType)
|
||||||
if (sharedVarValue != null) {
|
if (sharedVarValue != null) {
|
||||||
return sharedVarValue
|
return sharedVarValue
|
||||||
@@ -493,7 +531,7 @@ fun EvaluationContextImpl.findLocalVariable(name: String, asmType: Type?, checkT
|
|||||||
val capturedValName = getCapturedFieldName(name)
|
val capturedValName = getCapturedFieldName(name)
|
||||||
val capturedVal = findCapturedVal(capturedValName)
|
val capturedVal = findCapturedVal(capturedValName)
|
||||||
if (capturedVal != null) {
|
if (capturedVal != null) {
|
||||||
if (capturedVal.isSharedVar(asmType)) {
|
if (capturedVal.isSharedVar()) {
|
||||||
val sharedVarValue = getValueForSharedVar(capturedVal, asmType, checkType)
|
val sharedVarValue = getValueForSharedVar(capturedVal, asmType, checkType)
|
||||||
if (sharedVarValue != null) {
|
if (sharedVarValue != null) {
|
||||||
return sharedVarValue
|
return sharedVarValue
|
||||||
|
|||||||
+1
-1
@@ -95,7 +95,7 @@ fun getFunctionForExtractedFragment(
|
|||||||
if (targetSibling == null) return null
|
if (targetSibling == null) return null
|
||||||
|
|
||||||
val analysisResult = ExtractionData(
|
val analysisResult = ExtractionData(
|
||||||
tmpFile, newDebugExpression.toRange(), targetSibling, ExtractionOptions(false, true)
|
tmpFile, newDebugExpression.toRange(), targetSibling, ExtractionOptions(false, true, true)
|
||||||
).performAnalysis()
|
).performAnalysis()
|
||||||
if (analysisResult.status != Status.SUCCESS) {
|
if (analysisResult.status != Status.SUCCESS) {
|
||||||
throw EvaluateExceptionUtil.createEvaluateException(getErrorMessageForExtractFunctionResult(analysisResult))
|
throw EvaluateExceptionUtil.createEvaluateException(getErrorMessageForExtractFunctionResult(analysisResult))
|
||||||
|
|||||||
+5
-3
@@ -63,10 +63,12 @@ trait Parameter {
|
|||||||
val originalDescriptor: DeclarationDescriptor
|
val originalDescriptor: DeclarationDescriptor
|
||||||
val name: String
|
val name: String
|
||||||
val mirrorVarName: String?
|
val mirrorVarName: String?
|
||||||
val parameterType: JetType
|
|
||||||
val parameterTypeCandidates: List<JetType>
|
|
||||||
val receiverCandidate: Boolean
|
val receiverCandidate: Boolean
|
||||||
|
|
||||||
|
fun getParameterType(allowSpecialClassNames: Boolean): JetType
|
||||||
|
|
||||||
|
fun getParameterTypeCandidates(allowSpecialClassNames: Boolean): List<JetType>
|
||||||
|
|
||||||
fun copy(name: String, parameterType: JetType): Parameter
|
fun copy(name: String, parameterType: JetType): Parameter
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +145,7 @@ trait OutputValue {
|
|||||||
val parameter: Parameter,
|
val parameter: Parameter,
|
||||||
override val originalExpressions: List<JetExpression>
|
override val originalExpressions: List<JetExpression>
|
||||||
): OutputValue {
|
): OutputValue {
|
||||||
override val valueType: JetType get() = parameter.parameterType
|
override val valueType: JetType get() = parameter.getParameterType(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Initializer(
|
class Initializer(
|
||||||
|
|||||||
@@ -55,10 +55,11 @@ import org.jetbrains.kotlin.idea.refactoring.compareDescriptors
|
|||||||
|
|
||||||
data class ExtractionOptions(
|
data class ExtractionOptions(
|
||||||
val inferUnitTypeForUnusedValues: Boolean,
|
val inferUnitTypeForUnusedValues: Boolean,
|
||||||
val enableListBoxing: Boolean
|
val enableListBoxing: Boolean,
|
||||||
|
val allowSpecialClassNames: Boolean
|
||||||
) {
|
) {
|
||||||
class object {
|
class object {
|
||||||
val DEFAULT = ExtractionOptions(true, false)
|
val DEFAULT = ExtractionOptions(true, false, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-9
@@ -441,6 +441,7 @@ private fun JetType.isExtractable(): Boolean {
|
|||||||
private fun JetType.processTypeIfExtractable(
|
private fun JetType.processTypeIfExtractable(
|
||||||
typeParameters: MutableSet<TypeParameter>,
|
typeParameters: MutableSet<TypeParameter>,
|
||||||
nonDenotableTypes: MutableSet<JetType>,
|
nonDenotableTypes: MutableSet<JetType>,
|
||||||
|
options: ExtractionOptions,
|
||||||
processTypeArguments: Boolean = true
|
processTypeArguments: Boolean = true
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return collectReferencedTypes(processTypeArguments).fold(true) { (extractable, typeToCheck) ->
|
return collectReferencedTypes(processTypeArguments).fold(true) { (extractable, typeToCheck) ->
|
||||||
@@ -458,6 +459,9 @@ private fun JetType.processTypeIfExtractable(
|
|||||||
typeToCheck.canBeReferencedViaImport() ->
|
typeToCheck.canBeReferencedViaImport() ->
|
||||||
extractable
|
extractable
|
||||||
|
|
||||||
|
options.allowSpecialClassNames && typeToCheck.isSpecial() ->
|
||||||
|
extractable
|
||||||
|
|
||||||
typeToCheck.isError() ->
|
typeToCheck.isError() ->
|
||||||
false
|
false
|
||||||
|
|
||||||
@@ -501,7 +505,7 @@ private class MutableParameter(
|
|||||||
CommonSupertypes.commonSupertype(defaultTypes)
|
CommonSupertypes.commonSupertype(defaultTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val parameterTypeCandidates: List<JetType> by Delegates.lazy {
|
private val parameterTypeCandidates: List<JetType> by Delegates.lazy {
|
||||||
writable = false
|
writable = false
|
||||||
|
|
||||||
val typePredicate = and(typePredicates)
|
val typePredicate = and(typePredicates)
|
||||||
@@ -522,10 +526,20 @@ private class MutableParameter(
|
|||||||
typeList.add(superType)
|
typeList.add(superType)
|
||||||
}
|
}
|
||||||
|
|
||||||
typeList.filter { it.isExtractable() }
|
typeList
|
||||||
}
|
}
|
||||||
|
|
||||||
override val parameterType: JetType by Delegates.lazy { parameterTypeCandidates.firstOrNull() ?: defaultType }
|
override fun getParameterTypeCandidates(allowSpecialClassNames: Boolean): List<JetType> {
|
||||||
|
return if (!allowSpecialClassNames) {
|
||||||
|
parameterTypeCandidates.filter { it.isExtractable() }
|
||||||
|
} else {
|
||||||
|
parameterTypeCandidates
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getParameterType(allowSpecialClassNames: Boolean): JetType {
|
||||||
|
return getParameterTypeCandidates(allowSpecialClassNames).firstOrNull() ?: defaultType
|
||||||
|
}
|
||||||
|
|
||||||
override fun copy(name: String, parameterType: JetType): Parameter = DelegatingParameter(this, name, parameterType)
|
override fun copy(name: String, parameterType: JetType): Parameter = DelegatingParameter(this, name, parameterType)
|
||||||
}
|
}
|
||||||
@@ -533,9 +547,10 @@ private class MutableParameter(
|
|||||||
private class DelegatingParameter(
|
private class DelegatingParameter(
|
||||||
val original: Parameter,
|
val original: Parameter,
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val parameterType: JetType
|
val parameterType: JetType
|
||||||
): Parameter by original {
|
): Parameter by original {
|
||||||
override fun copy(name: String, parameterType: JetType): Parameter = DelegatingParameter(original, name, parameterType)
|
override fun copy(name: String, parameterType: JetType): Parameter = DelegatingParameter(original, name, parameterType)
|
||||||
|
override fun getParameterType(allowSpecialClassNames: Boolean) = parameterType
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ParametersInfo {
|
private class ParametersInfo {
|
||||||
@@ -596,7 +611,7 @@ private fun ExtractionData.inferParametersInfo(
|
|||||||
|
|
||||||
if (referencedClassDescriptor != null) {
|
if (referencedClassDescriptor != null) {
|
||||||
if (!referencedClassDescriptor.getDefaultType().processTypeIfExtractable(
|
if (!referencedClassDescriptor.getDefaultType().processTypeIfExtractable(
|
||||||
info.typeParameters, info.nonDenotableTypes, false
|
info.typeParameters, info.nonDenotableTypes, options, false
|
||||||
)) continue
|
)) continue
|
||||||
|
|
||||||
info.replacementMap[refInfo.offsetInBody] = FqNameReplacement(originalDescriptor.importableFqNameSafe)
|
info.replacementMap[refInfo.offsetInBody] = FqNameReplacement(originalDescriptor.importableFqNameSafe)
|
||||||
@@ -657,11 +672,11 @@ private fun ExtractionData.inferParametersInfo(
|
|||||||
)
|
)
|
||||||
|
|
||||||
for ((descriptorToExtract, parameter) in extractedDescriptorToParameter) {
|
for ((descriptorToExtract, parameter) in extractedDescriptorToParameter) {
|
||||||
if (!parameter.parameterType.processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes)) continue
|
if (!parameter.getParameterType(options.allowSpecialClassNames).processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes, options)) continue
|
||||||
|
|
||||||
with (parameter) {
|
with (parameter) {
|
||||||
if (currentName == null) {
|
if (currentName == null) {
|
||||||
currentName = JetNameSuggester.suggestNames(parameterType, varNameValidator, "p").first()
|
currentName = JetNameSuggester.suggestNames(getParameterType(options.allowSpecialClassNames), varNameValidator, "p").first()
|
||||||
}
|
}
|
||||||
mirrorVarName = if (descriptorToExtract in modifiedVarDescriptors) varNameValidator.validateName(name) else null
|
mirrorVarName = if (descriptorToExtract in modifiedVarDescriptors) varNameValidator.validateName(name) else null
|
||||||
info.parameters.add(this)
|
info.parameters.add(this)
|
||||||
@@ -669,7 +684,7 @@ private fun ExtractionData.inferParametersInfo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (typeToCheck in info.typeParameters.flatMapTo(HashSet<JetType>()) { it.collectReferencedTypes(bindingContext) }) {
|
for (typeToCheck in info.typeParameters.flatMapTo(HashSet<JetType>()) { it.collectReferencedTypes(bindingContext) }) {
|
||||||
typeToCheck.processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes)
|
typeToCheck.processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -763,7 +778,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
|||||||
controlFlowMessage?.let { messages.add(it) }
|
controlFlowMessage?.let { messages.add(it) }
|
||||||
|
|
||||||
val returnType = controlFlow.outputValueBoxer.returnType
|
val returnType = controlFlow.outputValueBoxer.returnType
|
||||||
returnType.processTypeIfExtractable(paramsInfo.typeParameters, paramsInfo.nonDenotableTypes)
|
returnType.processTypeIfExtractable(paramsInfo.typeParameters, paramsInfo.nonDenotableTypes, options)
|
||||||
|
|
||||||
if (paramsInfo.nonDenotableTypes.isNotEmpty()) {
|
if (paramsInfo.nonDenotableTypes.isNotEmpty()) {
|
||||||
val typeStr = paramsInfo.nonDenotableTypes.map {it.renderForMessage()}.sort()
|
val typeStr = paramsInfo.nonDenotableTypes.map {it.renderForMessage()}.sort()
|
||||||
|
|||||||
@@ -16,49 +16,39 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.refactoring.extractFunction
|
package org.jetbrains.kotlin.idea.refactoring.extractFunction
|
||||||
|
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
|
||||||
import org.jetbrains.kotlin.psi.JetElement
|
|
||||||
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
|
|
||||||
import java.util.HashMap
|
|
||||||
import org.jetbrains.kotlin.psi.JetTreeVisitorVoid
|
|
||||||
import org.jetbrains.kotlin.psi.JetNamedFunction
|
|
||||||
import org.jetbrains.kotlin.psi.JetExpression
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.psi.JetDeclaration
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.psi.JetProperty
|
|
||||||
import org.jetbrains.kotlin.psi.JetPsiFactory.CallableBuilder
|
|
||||||
import org.jetbrains.kotlin.psi.JetPsiFactory.CallableBuilder.Target
|
|
||||||
import org.jetbrains.kotlin.psi.JetNamedDeclaration
|
|
||||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
|
||||||
import java.util.LinkedHashMap
|
|
||||||
import java.util.Collections
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.isFunctionLiteralOutsideParentheses
|
|
||||||
import org.jetbrains.kotlin.psi.JetFunctionLiteralArgument
|
|
||||||
import org.jetbrains.kotlin.idea.util.psiModificationUtil.moveInsideParenthesesAndReplaceWith
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.appendElement
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.replaced
|
|
||||||
import org.jetbrains.kotlin.psi.JetBlockExpression
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.extractFunction.OutputValue.ParameterUpdate
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.extractFunction.OutputValue.Jump
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.extractFunction.OutputValue.Initializer
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.extractFunction.OutputValue.ExpressionValue
|
|
||||||
import org.jetbrains.kotlin.psi.JetReturnExpression
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.JetNameValidatorImpl
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.JetNameSuggester
|
import org.jetbrains.kotlin.idea.refactoring.JetNameSuggester
|
||||||
import org.jetbrains.kotlin.idea.refactoring.isMultiLine
|
import org.jetbrains.kotlin.idea.refactoring.JetNameValidatorImpl
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.extractFunction.OutputValue.ExpressionValue
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.extractFunction.OutputValue.Initializer
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.extractFunction.OutputValue.Jump
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.extractFunction.OutputValue.ParameterUpdate
|
||||||
import org.jetbrains.kotlin.idea.refactoring.extractFunction.OutputValueBoxer.AsTuple
|
import org.jetbrains.kotlin.idea.refactoring.extractFunction.OutputValueBoxer.AsTuple
|
||||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.JetPsiUnifier
|
import org.jetbrains.kotlin.idea.refactoring.isMultiLine
|
||||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.UnifierParameter
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.JetPsiRange
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.JetPsiRange
|
||||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.JetPsiRange.Match
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.JetPsiRange.Match
|
||||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.UnificationResult.WeaklyMatched
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.JetPsiUnifier
|
||||||
import org.jetbrains.kotlin.idea.util.psi.patternMatching.UnificationResult.StronglyMatched
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.UnificationResult.StronglyMatched
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.UnificationResult.WeaklyMatched
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
import org.jetbrains.kotlin.idea.util.psi.patternMatching.UnifierParameter
|
||||||
|
import org.jetbrains.kotlin.idea.util.psiModificationUtil.moveInsideParenthesesAndReplaceWith
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.JetPsiFactory.CallableBuilder
|
||||||
|
import org.jetbrains.kotlin.psi.JetPsiFactory.CallableBuilder.Target
|
||||||
|
import org.jetbrains.kotlin.psi.codeFragmentUtil.DEBUG_TYPE_REFERENCE_STRING
|
||||||
|
import org.jetbrains.kotlin.psi.codeFragmentUtil.debugTypeInfo
|
||||||
|
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||||
|
import org.jetbrains.kotlin.types.JetType
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import java.util.Collections
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
import java.util.HashMap
|
||||||
|
import java.util.LinkedHashMap
|
||||||
|
|
||||||
fun ExtractableCodeDescriptor.getDeclarationText(
|
fun ExtractableCodeDescriptor.getDeclarationText(
|
||||||
options: ExtractionGeneratorOptions = ExtractionGeneratorOptions.DEFAULT,
|
options: ExtractionGeneratorOptions = ExtractionGeneratorOptions.DEFAULT,
|
||||||
@@ -77,16 +67,20 @@ fun ExtractableCodeDescriptor.getDeclarationText(
|
|||||||
|
|
||||||
builder.typeParams(typeParameters.map { it.originalDeclaration.getText()!! })
|
builder.typeParams(typeParameters.map { it.originalDeclaration.getText()!! })
|
||||||
|
|
||||||
receiverParameter?.let { builder.receiver(descriptorRenderer.renderType(it.parameterType)) }
|
fun JetType.typeAsString(): String {
|
||||||
|
return if (isSpecial()) DEBUG_TYPE_REFERENCE_STRING else descriptorRenderer.renderType(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
receiverParameter?.let { builder.receiver(it.getParameterType(extractionData.options.allowSpecialClassNames).typeAsString()) }
|
||||||
|
|
||||||
builder.name(if (name != "") name else DEFAULT_FUNCTION_NAME)
|
builder.name(if (name != "") name else DEFAULT_FUNCTION_NAME)
|
||||||
|
|
||||||
parameters.forEach { parameter ->
|
parameters.forEach { parameter ->
|
||||||
builder.param(parameter.name, descriptorRenderer.renderType(parameter.parameterType))
|
builder.param(parameter.name, parameter.getParameterType(extractionData.options.allowSpecialClassNames).typeAsString())
|
||||||
}
|
}
|
||||||
|
|
||||||
with(controlFlow.outputValueBoxer.returnType) {
|
with(controlFlow.outputValueBoxer.returnType) {
|
||||||
if (isDefault() || isError()) builder.noReturnType() else builder.returnType(descriptorRenderer.renderType(this))
|
if (isDefault() || isError()) builder.noReturnType() else builder.returnType(this.typeAsString())
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.typeConstraints(typeParameters.flatMap { it.originalConstraints }.map { it.getText()!! })
|
builder.typeConstraints(typeParameters.flatMap { it.originalConstraints }.map { it.getText()!! })
|
||||||
@@ -99,6 +93,10 @@ fun ExtractableCodeDescriptor.getDeclarationText(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun JetType.isSpecial(): Boolean {
|
||||||
|
return this.getConstructor().getDeclarationDescriptor()?.getName()?.isSpecial() ?: false
|
||||||
|
}
|
||||||
|
|
||||||
fun createNameCounterpartMap(from: JetElement, to: JetElement): Map<JetSimpleNameExpression, JetSimpleNameExpression> {
|
fun createNameCounterpartMap(from: JetElement, to: JetElement): Map<JetSimpleNameExpression, JetSimpleNameExpression> {
|
||||||
val map = HashMap<JetSimpleNameExpression, JetSimpleNameExpression>()
|
val map = HashMap<JetSimpleNameExpression, JetSimpleNameExpression>()
|
||||||
|
|
||||||
@@ -172,7 +170,7 @@ fun ExtractableCodeDescriptor.findDuplicates(): List<DuplicateInfo> {
|
|||||||
return if (matched) newControlFlow else null
|
return if (matched) newControlFlow else null
|
||||||
}
|
}
|
||||||
|
|
||||||
val unifierParameters = parameters.map { UnifierParameter(it.originalDescriptor, it.parameterType) }
|
val unifierParameters = parameters.map { UnifierParameter(it.originalDescriptor, it.getParameterType(extractionData.options.allowSpecialClassNames)) }
|
||||||
|
|
||||||
val unifier = JetPsiUnifier(unifierParameters, true)
|
val unifier = JetPsiUnifier(unifierParameters, true)
|
||||||
|
|
||||||
@@ -527,6 +525,18 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
|
|||||||
val declaration = createDeclaration().let { if (options.inTempFile) it else insertDeclaration(it, anchor) }
|
val declaration = createDeclaration().let { if (options.inTempFile) it else insertDeclaration(it, anchor) }
|
||||||
adjustDeclarationBody(declaration)
|
adjustDeclarationBody(declaration)
|
||||||
|
|
||||||
|
if (declaration is JetNamedFunction && declaration.getContainingJetFile().suppressDiagnosticsInDebugMode) {
|
||||||
|
declaration.getReceiverTypeReference()?.debugTypeInfo = receiverParameter?.getParameterType(true)
|
||||||
|
|
||||||
|
for ((i, param) in declaration.getValueParameters().withIndex()) {
|
||||||
|
param.getTypeReference()?.debugTypeInfo = parameters[i].getParameterType(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (declaration.getTypeReference() != null) {
|
||||||
|
declaration.getTypeReference()?.debugTypeInfo = KotlinBuiltIns.getInstance().getAnyType()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options.inTempFile) return ExtractionResult(declaration, Collections.emptyMap(), nameByOffset)
|
if (options.inTempFile) return ExtractionResult(declaration, Collections.emptyMap(), nameByOffset)
|
||||||
|
|
||||||
makeCall(this, declaration, controlFlow, extractionData.originalRange, parameters.map { it.argumentText })
|
makeCall(this, declaration, controlFlow, extractionData.originalRange, parameters.map { it.argumentText })
|
||||||
|
|||||||
+3
-3
@@ -55,7 +55,7 @@ public class KotlinParameterTablePanel extends JPanel {
|
|||||||
public ParameterInfo(Parameter originalParameter) {
|
public ParameterInfo(Parameter originalParameter) {
|
||||||
this.originalParameter = originalParameter;
|
this.originalParameter = originalParameter;
|
||||||
this.name = originalParameter.getName();
|
this.name = originalParameter.getName();
|
||||||
this.type = originalParameter.getParameterType();
|
this.type = originalParameter.getParameterType(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Parameter getOriginalParameter() {
|
public Parameter getOriginalParameter() {
|
||||||
@@ -176,7 +176,7 @@ public class KotlinParameterTablePanel extends JPanel {
|
|||||||
ParameterInfo info = parameterInfos.get(row);
|
ParameterInfo info = parameterInfos.get(row);
|
||||||
|
|
||||||
myEditorComponent.setCell(table, row, column);
|
myEditorComponent.setCell(table, row, column);
|
||||||
myEditorComponent.setOptions(info.getOriginalParameter().getParameterTypeCandidates().toArray());
|
myEditorComponent.setOptions(info.getOriginalParameter().getParameterTypeCandidates(false).toArray());
|
||||||
myEditorComponent.setDefaultValue(info.getType());
|
myEditorComponent.setDefaultValue(info.getType());
|
||||||
myEditorComponent.setToString(new Function<Object, String>() {
|
myEditorComponent.setToString(new Function<Object, String>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -360,7 +360,7 @@ public class KotlinParameterTablePanel extends JPanel {
|
|||||||
case PARAMETER_NAME_COLUMN:
|
case PARAMETER_NAME_COLUMN:
|
||||||
return isEnabled() && info.isEnabled();
|
return isEnabled() && info.isEnabled();
|
||||||
case PARAMETER_TYPE_COLUMN:
|
case PARAMETER_TYPE_COLUMN:
|
||||||
return isEnabled() && info.isEnabled() && info.getOriginalParameter().getParameterTypeCandidates().size() > 1;
|
return isEnabled() && info.isEnabled() && info.getOriginalParameter().getParameterTypeCandidates(false).size() > 1;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
LineBreakpoint created at ceAnonymousObject.kt:18
|
||||||
|
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! ceAnonymousObject.CeAnonymousObjectPackage
|
||||||
|
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
ceAnonymousObject.kt:18
|
||||||
|
Compile bytecode for publicTopLevelObject
|
||||||
|
Compile bytecode for privateTopLevelObject
|
||||||
|
Compile bytecode for publicObject
|
||||||
|
Compile bytecode for protectedObject
|
||||||
|
Compile bytecode for localObject
|
||||||
|
Compile bytecode for localObject.test()
|
||||||
|
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
LineBreakpoint created at ceAnonymousObjectCapturedInClosure.kt:7
|
||||||
|
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! ceAnonymousObjectCapturedInClosure.CeAnonymousObjectCapturedInClosurePackage
|
||||||
|
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
ceAnonymousObjectCapturedInClosure.kt:7
|
||||||
|
Compile bytecode for lambda { localObject.test() }
|
||||||
|
Compile bytecode for lambda { localObjectVar.test() }
|
||||||
|
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
LineBreakpoint created at ceAnonymousObjectThisAsReceiver.kt:9
|
||||||
|
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! ceAnonymousObjectThisAsReceiver.CeAnonymousObjectThisAsReceiverPackage
|
||||||
|
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
ceAnonymousObjectThisAsReceiver.kt:9
|
||||||
|
Compile bytecode for test()
|
||||||
|
Compile bytecode for this.test()
|
||||||
|
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
@@ -2,6 +2,8 @@ LineBreakpoint created at ceObject.kt:5
|
|||||||
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! ceObject.CeObjectPackage
|
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! ceObject.CeObjectPackage
|
||||||
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
ceObject.kt:5
|
ceObject.kt:5
|
||||||
|
Compile bytecode for (object: T {}).test()
|
||||||
|
Compile bytecode for object: T {}
|
||||||
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
|
||||||
|
|
||||||
Process finished with exit code 0
|
Process finished with exit code 0
|
||||||
|
|||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
package ceAnonymousObject
|
||||||
|
|
||||||
|
public val publicTopLevelObject: Any = object { fun test() = 1 }
|
||||||
|
private val privateTopLevelObject = object { fun test() = 1 }
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
MyClass().foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyClass {
|
||||||
|
public val publicObject: Any = object { fun test() = 1 }
|
||||||
|
protected val protectedObject: Any = object { fun test() = 1 }
|
||||||
|
private val privateObject = object { fun test() = 1 }
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val localObject = object { fun test() = 1 }
|
||||||
|
//Breakpoint!
|
||||||
|
val a = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPRESSION: publicTopLevelObject
|
||||||
|
// RESULT: instance of ceAnonymousObject.CeAnonymousObjectPackage$ceAnonymousObject$@packagePartHASH$publicTopLevelObject$1(id=ID): LceAnonymousObject/CeAnonymousObjectPackage$ceAnonymousObject$@packagePartHASH$publicTopLevelObject$1;
|
||||||
|
|
||||||
|
// EXPRESSION: privateTopLevelObject
|
||||||
|
// RESULT: instance of ceAnonymousObject.CeAnonymousObjectPackage$ceAnonymousObject$@packagePartHASH$privateTopLevelObject$1(id=ID): LceAnonymousObject/CeAnonymousObjectPackage$ceAnonymousObject$@packagePartHASH$privateTopLevelObject$1;
|
||||||
|
|
||||||
|
// EXPRESSION: publicObject
|
||||||
|
// RESULT: instance of ceAnonymousObject.MyClass$publicObject$1(id=ID): LceAnonymousObject/MyClass$publicObject$1;
|
||||||
|
|
||||||
|
// EXPRESSION: protectedObject
|
||||||
|
// RESULT: instance of ceAnonymousObject.MyClass$protectedObject$1(id=ID): LceAnonymousObject/MyClass$protectedObject$1;
|
||||||
|
|
||||||
|
// -EXPRESSION: privateObject
|
||||||
|
// -RESULT: 1
|
||||||
|
|
||||||
|
// -EXPRESSION: privateObject.test()
|
||||||
|
// -RESULT: 1: I
|
||||||
|
|
||||||
|
// EXPRESSION: localObject
|
||||||
|
// RESULT: instance of ceAnonymousObject.MyClass$foo$localObject$1(id=ID): LceAnonymousObject/MyClass$foo$localObject$1;
|
||||||
|
|
||||||
|
// EXPRESSION: localObject.test()
|
||||||
|
// RESULT: 1: I
|
||||||
|
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
package ceAnonymousObjectCapturedInClosure
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val localObject = object { fun test() = 1 }
|
||||||
|
var localObjectVar = object { fun test() = 1 }
|
||||||
|
//Breakpoint!
|
||||||
|
lambda { localObjectVar.test() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lambda(f: () -> Int) = f()
|
||||||
|
|
||||||
|
// EXPRESSION: lambda { localObject.test() }
|
||||||
|
// RESULT: 1: I
|
||||||
|
|
||||||
|
// EXPRESSION: lambda { localObjectVar.test() }
|
||||||
|
// RESULT: 1: I
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package ceAnonymousObjectThisAsReceiver
|
||||||
|
|
||||||
|
fun main(args : Array<String>) {
|
||||||
|
val localObject = object {
|
||||||
|
fun test() = 1
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
//Breakpoint!
|
||||||
|
val a = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
localObject.foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPRESSION: test()
|
||||||
|
// RESULT: 1: I
|
||||||
|
|
||||||
|
// EXPRESSION: this.test()
|
||||||
|
// RESULT: 1: I
|
||||||
+5
-2
@@ -9,5 +9,8 @@ trait T {
|
|||||||
fun test() = 1
|
fun test() = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
//- EXPRESSION: (object: T {}).test()
|
// EXPRESSION: (object: T {}).test()
|
||||||
//- RESULT: 1: I
|
// RESULT: 1: I
|
||||||
|
|
||||||
|
// EXPRESSION: object: T {}
|
||||||
|
// RESULT: instance of packageForDebugger.PackageForDebuggerPackage$debugFile$@packagePartHASH$myFun$1(id=ID): LpackageForDebugger/PackageForDebuggerPackage$debugFile$@packagePartHASH$myFun$1;
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
object {}
|
|
||||||
|
|
||||||
// RESULT: Cannot perform an action because following types are unavailable from debugger scope: <no name provided>
|
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// OPTIONS: true, true
|
// OPTIONS: true, true, false
|
||||||
// PARAM_TYPES: kotlin.Int
|
// PARAM_TYPES: kotlin.Int
|
||||||
// PARAM_TYPES: kotlin.Int
|
// PARAM_TYPES: kotlin.Int
|
||||||
// PARAM_TYPES: kotlin.Int
|
// PARAM_TYPES: kotlin.Int
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// OPTIONS: true, true
|
// OPTIONS: true, true, false
|
||||||
// PARAM_TYPES: kotlin.Int
|
// PARAM_TYPES: kotlin.Int
|
||||||
// PARAM_TYPES: kotlin.Int
|
// PARAM_TYPES: kotlin.Int
|
||||||
// PARAM_TYPES: kotlin.Int
|
// PARAM_TYPES: kotlin.Int
|
||||||
|
|||||||
+6
-1
@@ -63,6 +63,8 @@ import com.intellij.debugger.SourcePosition
|
|||||||
import com.intellij.debugger.engine.SourcePositionProvider
|
import com.intellij.debugger.engine.SourcePositionProvider
|
||||||
import com.intellij.debugger.engine.evaluation.TextWithImports
|
import com.intellij.debugger.engine.evaluation.TextWithImports
|
||||||
import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor
|
import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor
|
||||||
|
import java.util.regex.Matcher
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestBase() {
|
public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestBase() {
|
||||||
private val logger = Logger.getLogger(javaClass<KotlinEvaluateExpressionCache>())!!
|
private val logger = Logger.getLogger(javaClass<KotlinEvaluateExpressionCache>())!!
|
||||||
@@ -353,8 +355,11 @@ public abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestB
|
|||||||
|
|
||||||
private fun Value.asString(): String {
|
private fun Value.asString(): String {
|
||||||
if (this is ObjectValue && this.value is ObjectReference) {
|
if (this is ObjectValue && this.value is ObjectReference) {
|
||||||
return this.toString().replaceFirst("id=[0-9]*", "id=ID")
|
val regexMatcher = PACKAGE_PART_PATTERN.matcher(this.toString())
|
||||||
|
return regexMatcher.replaceAll("$1@packagePartHASH").replaceFirst("id=[0-9]*", "id=ID")
|
||||||
}
|
}
|
||||||
return this.toString()
|
return this.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val PACKAGE_PART_PATTERN = Pattern.compile("(Package\\$[\\w]*\\$)([0-9a-f]+)");
|
||||||
|
|||||||
+18
@@ -243,6 +243,24 @@ public class KotlinEvaluateExpressionTestGenerated extends AbstractKotlinEvaluat
|
|||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/compilingEvaluator"), Pattern.compile("^(.+)\\.kt$"), true);
|
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/compilingEvaluator"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ceAnonymousObject.kt")
|
||||||
|
public void testCeAnonymousObject() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/compilingEvaluator/ceAnonymousObject.kt");
|
||||||
|
doSingleBreakpointTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ceAnonymousObjectCapturedInClosure.kt")
|
||||||
|
public void testCeAnonymousObjectCapturedInClosure() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/compilingEvaluator/ceAnonymousObjectCapturedInClosure.kt");
|
||||||
|
doSingleBreakpointTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ceAnonymousObjectThisAsReceiver.kt")
|
||||||
|
public void testCeAnonymousObjectThisAsReceiver() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/compilingEvaluator/ceAnonymousObjectThisAsReceiver.kt");
|
||||||
|
doSingleBreakpointTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ceLambda.kt")
|
@TestMetadata("ceLambda.kt")
|
||||||
public void testCeLambda() throws Exception {
|
public void testCeLambda() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/compilingEvaluator/ceLambda.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/evaluate/singleBreakpoint/compilingEvaluator/ceLambda.kt");
|
||||||
|
|||||||
+5
-2
@@ -44,6 +44,7 @@ import com.intellij.psi.util.PsiTreeUtil
|
|||||||
import com.intellij.psi.PsiWhiteSpace
|
import com.intellij.psi.PsiWhiteSpace
|
||||||
import org.jetbrains.kotlin.psi.JetPackageDirective
|
import org.jetbrains.kotlin.psi.JetPackageDirective
|
||||||
import org.jetbrains.kotlin.utils.emptyOrSingletonList
|
import org.jetbrains.kotlin.utils.emptyOrSingletonList
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTestCase() {
|
public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTestCase() {
|
||||||
override fun getProjectDescriptor() = LightCodeInsightFixtureTestCase.JAVA_LATEST
|
override fun getProjectDescriptor() = LightCodeInsightFixtureTestCase.JAVA_LATEST
|
||||||
@@ -96,7 +97,9 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
|
|||||||
if (it.isNotEmpty()) {
|
if (it.isNotEmpty()) {
|
||||||
[suppress("CAST_NEVER_SUCCEEDS")]
|
[suppress("CAST_NEVER_SUCCEEDS")]
|
||||||
val args = it.map { it.toBoolean() }.copyToArray() as Array<Any?>
|
val args = it.map { it.toBoolean() }.copyToArray() as Array<Any?>
|
||||||
javaClass<ExtractionOptions>().getConstructors()[0].newInstance(*args) as ExtractionOptions
|
val constructor = javaClass<ExtractionOptions>().getConstructors()[0]
|
||||||
|
assertTrue(constructor.getParameterTypes().size() == args.size(), "Wrong number of parameters was passed for ExtractOptions constructor: expected = ${constructor.getParameterTypes().size()}, actual = ${args.size()}. \nTest directive: // OPTIONS: $it")
|
||||||
|
constructor.newInstance(*args) as ExtractionOptions
|
||||||
} else ExtractionOptions.DEFAULT
|
} else ExtractionOptions.DEFAULT
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +121,7 @@ public abstract class AbstractJetExtractionTest() : JetLightCodeInsightFixtureTe
|
|||||||
val allParameters = emptyOrSingletonList(descriptor.receiverParameter) + descriptor.parameters
|
val allParameters = emptyOrSingletonList(descriptor.receiverParameter) + descriptor.parameters
|
||||||
val actualDescriptors = allParameters.map { renderer.render(it.originalDescriptor) }.joinToString()
|
val actualDescriptors = allParameters.map { renderer.render(it.originalDescriptor) }.joinToString()
|
||||||
val actualTypes = allParameters.map {
|
val actualTypes = allParameters.map {
|
||||||
it.parameterTypeCandidates.map { renderer.renderType(it) }.joinToString(", ", "[", "]")
|
it.getParameterTypeCandidates(extractionOptions.allowSpecialClassNames).map { renderer.renderType(it) }.joinToString(", ", "[", "]")
|
||||||
}.joinToString()
|
}.joinToString()
|
||||||
|
|
||||||
assertEquals(expectedDescriptors, actualDescriptors, "Expected descriptors mismatch.")
|
assertEquals(expectedDescriptors, actualDescriptors, "Expected descriptors mismatch.")
|
||||||
|
|||||||
Reference in New Issue
Block a user