Evaluate Expression: support anonymous objects evaluation

This commit is contained in:
Natalia Ukhorskaya
2015-02-11 15:45:55 +03:00
parent f507af15c0
commit 0391fbd764
23 changed files with 355 additions and 129 deletions
@@ -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
@@ -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))
@@ -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)
} }
} }
@@ -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 })
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,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,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
@@ -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]+)");
@@ -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");
@@ -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.")