Move out JVM debugger functionality

This commit is contained in:
Yan Zhulanow
2019-04-05 15:10:14 +03:00
parent 5843336d42
commit ae7550c5af
318 changed files with 1068 additions and 723 deletions
@@ -0,0 +1,20 @@
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compile(project(":compiler:backend"))
compile(project(":idea:ide-common"))
compile(files("${System.getProperty("java.home")}/../lib/tools.jar"))
compileOnly(intellijPluginDep("stream-debugger"))
testCompile(project(":kotlin-test:kotlin-test-junit"))
testCompile(commonDep("junit:junit"))
}
sourceSets {
"main" { projectDefault() }
"test" { none() }
}
@@ -0,0 +1,54 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.lib.collections
import com.intellij.debugger.streams.lib.IntermediateOperation
import com.intellij.debugger.streams.lib.TerminalOperation
import com.intellij.debugger.streams.lib.impl.LibrarySupportBase
import com.intellij.debugger.streams.resolve.FilterResolver
import com.intellij.debugger.streams.resolve.ValuesOrderResolver
import com.intellij.debugger.streams.trace.CallTraceInterpreter
import com.intellij.debugger.streams.trace.IntermediateCallHandler
import com.intellij.debugger.streams.trace.TerminatorCallHandler
import com.intellij.debugger.streams.trace.dsl.Dsl
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
import com.intellij.debugger.streams.wrapper.TerminatorStreamCall
import org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.collections.BothSemanticHandlerWrapper
import org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.collections.BothSemanticsHandler
import org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.collections.FilterCallHandler
import org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.interpret.FilterTraceInterpreter
class KotlinCollectionLibrarySupport : LibrarySupportBase() {
init {
addOperation(FilterOperation("filter", FilterCallHandler(), true))
addOperation(FilterOperation("filterNot", FilterCallHandler(), false))
}
private fun addOperation(operation: CollectionOperation) {
addIntermediateOperationsSupport(operation)
addTerminationOperationsSupport(operation)
}
private abstract class CollectionOperation(
override val name: String,
handler: BothSemanticsHandler
) : IntermediateOperation, TerminalOperation {
private val wrapper = BothSemanticHandlerWrapper(handler)
override fun getTraceHandler(callOrder: Int, call: IntermediateStreamCall, dsl: Dsl): IntermediateCallHandler =
wrapper.createIntermediateHandler(callOrder, call, dsl)
override fun getTraceHandler(call: TerminatorStreamCall, resultExpression: String, dsl: Dsl): TerminatorCallHandler =
wrapper.createTerminatorHandler(call, resultExpression, dsl)
}
private class FilterOperation(name: String, handler: BothSemanticsHandler, valueToAccept: Boolean) :
CollectionOperation(name, handler) {
override val traceInterpreter: CallTraceInterpreter = FilterTraceInterpreter(valueToAccept)
override val valuesOrderResolver: ValuesOrderResolver = FilterResolver()
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.lib.collections
import com.intellij.debugger.streams.lib.LibrarySupport
import com.intellij.debugger.streams.lib.LibrarySupportProvider
import com.intellij.debugger.streams.trace.TraceExpressionBuilder
import com.intellij.debugger.streams.trace.dsl.impl.DslImpl
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.debugger.sequence.psi.collections.KotlinCollectionChainBuilder
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinCollectionsPeekCallFactory
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinStatementFactory
import org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.KotlinTraceExpressionBuilder
class KotlinCollectionSupportProvider : LibrarySupportProvider {
private companion object {
val builder: StreamChainBuilder = KotlinCollectionChainBuilder()
val support: LibrarySupport = KotlinCollectionLibrarySupport()
val dsl = DslImpl(KotlinStatementFactory(KotlinCollectionsPeekCallFactory()))
}
override fun getLanguageId(): String = KotlinLanguage.INSTANCE.id
override fun getChainBuilder(): StreamChainBuilder = builder
override fun getLibrarySupport(): LibrarySupport = support
override fun getExpressionBuilder(project: Project): TraceExpressionBuilder =
KotlinTraceExpressionBuilder(dsl, support.createHandlerFactory(dsl))
}
@@ -0,0 +1,43 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.lib.java
import com.intellij.debugger.streams.lib.LibrarySupport
import com.intellij.debugger.streams.lib.LibrarySupportProvider
import com.intellij.debugger.streams.lib.impl.StandardLibrarySupport
import com.intellij.debugger.streams.trace.TraceExpressionBuilder
import com.intellij.debugger.streams.trace.dsl.impl.DslImpl
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.debugger.sequence.psi.impl.KotlinChainTransformerImpl
import org.jetbrains.kotlin.idea.debugger.sequence.psi.impl.PackageBasedCallChecker
import org.jetbrains.kotlin.idea.debugger.sequence.psi.impl.TerminatedChainBuilder
import org.jetbrains.kotlin.idea.debugger.sequence.psi.java.JavaStreamChainTypeExtractor
import org.jetbrains.kotlin.idea.debugger.sequence.psi.java.StandardLibraryCallChecker
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.JavaPeekCallFactory
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinStatementFactory
import org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.KotlinTraceExpressionBuilder
class JavaStandardLibrarySupportProvider : LibrarySupportProvider {
private companion object {
val builder = TerminatedChainBuilder(
KotlinChainTransformerImpl(JavaStreamChainTypeExtractor()),
StandardLibraryCallChecker(PackageBasedCallChecker("java.util.stream"))
)
val support = StandardLibrarySupport()
val dsl = DslImpl(KotlinStatementFactory(JavaPeekCallFactory()))
}
override fun getLanguageId(): String = KotlinLanguage.INSTANCE.id
override fun getChainBuilder(): StreamChainBuilder = builder
override fun getLibrarySupport(): LibrarySupport = support
override fun getExpressionBuilder(project: Project): TraceExpressionBuilder =
KotlinTraceExpressionBuilder(dsl, support.createHandlerFactory(dsl))
}
@@ -0,0 +1,43 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.lib.java
import com.intellij.debugger.streams.lib.LibrarySupport
import com.intellij.debugger.streams.lib.LibrarySupportProvider
import com.intellij.debugger.streams.lib.impl.StreamExLibrarySupport
import com.intellij.debugger.streams.trace.TraceExpressionBuilder
import com.intellij.debugger.streams.trace.dsl.impl.DslImpl
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.debugger.sequence.psi.impl.KotlinChainTransformerImpl
import org.jetbrains.kotlin.idea.debugger.sequence.psi.impl.PackageBasedCallChecker
import org.jetbrains.kotlin.idea.debugger.sequence.psi.impl.TerminatedChainBuilder
import org.jetbrains.kotlin.idea.debugger.sequence.psi.java.JavaStreamChainTypeExtractor
import org.jetbrains.kotlin.idea.debugger.sequence.psi.java.StreamExCallChecker
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.JavaPeekCallFactory
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinStatementFactory
import org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.KotlinTraceExpressionBuilder
class StreamExLibrarySupportProvider : LibrarySupportProvider {
private companion object {
val streamChainBuilder = TerminatedChainBuilder(
KotlinChainTransformerImpl(JavaStreamChainTypeExtractor()),
StreamExCallChecker(PackageBasedCallChecker("one.util.streamex"))
)
val support = StreamExLibrarySupport()
val dsl = DslImpl(KotlinStatementFactory(JavaPeekCallFactory()))
val expressionBuilder = KotlinTraceExpressionBuilder(dsl, support.createHandlerFactory(dsl))
}
override fun getLanguageId(): String = KotlinLanguage.INSTANCE.id
override fun getChainBuilder(): StreamChainBuilder = streamChainBuilder
override fun getLibrarySupport(): LibrarySupport = support
override fun getExpressionBuilder(project: Project): TraceExpressionBuilder = expressionBuilder
}
@@ -0,0 +1,43 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.lib.sequence
import com.intellij.debugger.streams.lib.LibrarySupport
import com.intellij.debugger.streams.lib.LibrarySupportProvider
import com.intellij.debugger.streams.trace.TraceExpressionBuilder
import com.intellij.debugger.streams.trace.dsl.impl.DslImpl
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.idea.debugger.sequence.psi.impl.KotlinChainTransformerImpl
import org.jetbrains.kotlin.idea.debugger.sequence.psi.impl.TerminatedChainBuilder
import org.jetbrains.kotlin.idea.debugger.sequence.psi.sequence.SequenceCallChecker
import org.jetbrains.kotlin.idea.debugger.sequence.psi.sequence.SequenceCallCheckerWithNameHeuristics
import org.jetbrains.kotlin.idea.debugger.sequence.psi.sequence.SequenceTypeExtractor
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinCollectionsPeekCallFactory
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinStatementFactory
import org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.KotlinTraceExpressionBuilder
class KotlinSequenceSupportProvider : LibrarySupportProvider {
override fun getLanguageId(): String = KotlinLanguage.INSTANCE.id
private companion object {
val builder: StreamChainBuilder = TerminatedChainBuilder(
KotlinChainTransformerImpl(SequenceTypeExtractor()),
SequenceCallCheckerWithNameHeuristics(SequenceCallChecker())
)
val support = KotlinSequencesSupport()
val dsl = DslImpl(KotlinStatementFactory(KotlinCollectionsPeekCallFactory()))
val expressionBuilder = KotlinTraceExpressionBuilder(dsl, support.createHandlerFactory(dsl))
}
override fun getChainBuilder(): StreamChainBuilder = builder
override fun getLibrarySupport(): LibrarySupport = support
override fun getExpressionBuilder(project: Project): TraceExpressionBuilder = expressionBuilder
}
@@ -0,0 +1,71 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.lib.sequence
import com.intellij.debugger.streams.lib.IntermediateOperation
import com.intellij.debugger.streams.lib.impl.*
import com.intellij.debugger.streams.resolve.AppendResolver
import com.intellij.debugger.streams.resolve.PairMapResolver
import com.intellij.debugger.streams.trace.impl.handler.unified.DistinctTraceHandler
import com.intellij.debugger.streams.trace.impl.interpret.SimplePeekCallTraceInterpreter
import org.jetbrains.kotlin.idea.debugger.sequence.resolve.ChunkedResolver
import org.jetbrains.kotlin.idea.debugger.sequence.resolve.FilteredMapResolver
import org.jetbrains.kotlin.idea.debugger.sequence.resolve.WindowedResolver
import org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.sequence.FilterIsInstanceHandler
import org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.sequence.KotlinDistinctByHandler
class KotlinSequencesSupport : LibrarySupportBase() {
init {
addIntermediateOperationsSupport(
*filterOperations(
"filter", "filterNot", "filterIndexed",
"drop", "dropWhile", "minus", "minusElement", "take", "takeWhile", "onEach", "asSequence"
)
)
addIntermediateOperationsSupport(FilterIsInstanceOperationHandler())
addIntermediateOperationsSupport(
*mapOperations(
"map", "mapIndexed", "requireNoNulls", "withIndex",
"zip", "constrainOnce"
)
)
addIntermediateOperationsSupport(*flatMapOperations("flatMap", "flatten"))
addIntermediateOperationsSupport(*sortedOperations("sorted", "sortedBy", "sortedDescending", "sortedWith"))
addIntermediateOperationsSupport(DistinctOperation("distinct", ::DistinctTraceHandler))
addIntermediateOperationsSupport(DistinctOperation("distinctBy", ::KotlinDistinctByHandler))
addIntermediateOperationsSupport(ConcatOperation("plus", AppendResolver()))
addIntermediateOperationsSupport(ConcatOperation("plusElement", AppendResolver()))
addIntermediateOperationsSupport(OrderBasedOperation("zipWithNext", PairMapResolver()))
addIntermediateOperationsSupport(OrderBasedOperation("mapNotNull", FilteredMapResolver()))
addIntermediateOperationsSupport(OrderBasedOperation("chunked", ChunkedResolver()))
addIntermediateOperationsSupport(OrderBasedOperation("windowed", WindowedResolver()))
}
private fun filterOperations(vararg names: String): Array<IntermediateOperation> =
names.map { FilterOperation(it) }.toTypedArray()
private fun mapOperations(vararg names: String): Array<IntermediateOperation> =
names.map { MappingOperation(it) }.toTypedArray()
private fun flatMapOperations(vararg names: String): Array<IntermediateOperation> =
names.map { FlatMappingOperation(it) }.toTypedArray()
private fun sortedOperations(vararg names: String): Array<IntermediateOperation> =
names.map { SortedOperation(it) }.toTypedArray()
private class FilterIsInstanceOperationHandler : IntermediateOperationBase(
"filterIsInstance", ::FilterIsInstanceHandler,
SimplePeekCallTraceInterpreter(), FilteredMapResolver()
)
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi
import org.jetbrains.kotlin.idea.debugger.sequence.psi.sequence.SequenceCallCheckerWithNameHeuristics
import org.jetbrains.kotlin.psi.KtCallExpression
abstract class CallCheckerWithNameHeuristics(private val nestedChecker: StreamCallChecker) : StreamCallChecker {
override fun isIntermediateCall(expression: KtCallExpression): Boolean = nestedChecker.isIntermediateCall(expression)
override fun isTerminationCall(expression: KtCallExpression): Boolean {
val name = expression.calleeExpression?.text
if (name != null) {
return isTerminalCallName(name) && nestedChecker.isTerminationCall(expression)
}
return nestedChecker.isTerminationCall(expression)
}
abstract fun isTerminalCallName(callName: String): Boolean
}
@@ -0,0 +1,32 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi
import com.intellij.debugger.streams.trace.impl.handler.type.GenericType
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.types.KotlinType
interface CallTypeExtractor {
fun extractIntermediateCallTypes(call: KtCallExpression): IntermediateCallTypes
fun extractTerminalCallTypes(call: KtCallExpression): TerminatorCallTypes
data class IntermediateCallTypes(val typeBefore: GenericType, val typeAfter: GenericType)
data class TerminatorCallTypes(val typeBefore: GenericType, val resultType: GenericType)
abstract class Base : CallTypeExtractor {
override fun extractIntermediateCallTypes(call: KtCallExpression): IntermediateCallTypes =
IntermediateCallTypes(extractItemsType(call.receiverType()), extractItemsType(call.resolveType()))
override fun extractTerminalCallTypes(call: KtCallExpression): TerminatorCallTypes =
TerminatorCallTypes(extractItemsType(call.receiverType()), getResultType(call.resolveType()))
protected abstract fun extractItemsType(type: KotlinType?): GenericType
protected abstract fun getResultType(type: KotlinType): GenericType
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.util.approximateFlexibleTypes
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.FlexibleType
import org.jetbrains.kotlin.types.KotlinType
object KotlinPsiUtil {
fun getTypeName(type: KotlinType): String {
if (type is FlexibleType) {
return DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type.approximateFlexibleTypes())
}
return DescriptorRenderer.FQ_NAMES_IN_TYPES.renderType(type)
}
fun getTypeWithoutTypeParameters(type: KotlinType): String {
val descriptor = type.constructor.declarationDescriptor ?: return getTypeName(type)
return descriptor.fqNameSafe.asString()
}
}
fun KtExpression.resolveType(): KotlinType =
this.analyze(BodyResolveMode.PARTIAL).getType(this)!!
fun KtCallExpression.callName(): String = this.calleeExpression!!.text
fun KtCallExpression.receiverValue(): ReceiverValue? {
val resolvedCall = getResolvedCall(analyze(BodyResolveMode.PARTIAL)) ?: return null
return resolvedCall.dispatchReceiver ?: resolvedCall.extensionReceiver
}
fun KtCallExpression.previousCall(): KtCallExpression? {
val parent = this.parent as? KtDotQualifiedExpression ?: return null
val receiverExpression = parent.receiverExpression
if (receiverExpression is KtCallExpression) return receiverExpression
if (receiverExpression is KtDotQualifiedExpression) return receiverExpression.selectorExpression as? KtCallExpression
return null
}
fun KtCallExpression.receiverType(): KotlinType? = receiverValue()?.type
@@ -0,0 +1,15 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi
import org.jetbrains.kotlin.psi.KtCallExpression
interface StreamCallChecker {
fun isIntermediateCall(expression: KtCallExpression): Boolean
fun isTerminationCall(expression: KtCallExpression): Boolean
fun isStreamCall(expression: KtCallExpression): Boolean = isIntermediateCall(expression) || isTerminationCall(expression)
}
@@ -0,0 +1,45 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi.collections
import com.intellij.debugger.streams.psi.ChainTransformer
import com.intellij.debugger.streams.wrapper.QualifierExpression
import com.intellij.debugger.streams.wrapper.StreamChain
import com.intellij.debugger.streams.wrapper.impl.StreamChainImpl
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.idea.debugger.sequence.psi.impl.KotlinChainTransformerImpl
import org.jetbrains.kotlin.idea.debugger.sequence.psi.resolveType
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.types.KotlinType
class CollectionChainTransformer : ChainTransformer<KtCallExpression> {
private val transformer = KotlinChainTransformerImpl(KotlinCollectionsTypeExtractor())
override fun transform(chainCalls: List<KtCallExpression>, context: PsiElement): StreamChain {
val chain = transformer.transform(chainCalls, context)
if (chainCalls.first().resolveType().isArray) {
val qualifier = WrappedQualifier(chain.qualifierExpression)
return StreamChainImpl(qualifier, chain.intermediateCalls, chain.terminationCall, chain.context)
}
return chain
}
/**
* Kotlin arrays have not {@code onEach} extension. But current implementation uses onEach to increment a time counter.
* We use asIterable to avoid further issues with the transformed expression evaluation
* TODO: Avoid showing "asIterable()" in the tab name in trace window
*/
private class WrappedQualifier(private val qualifierExpression: QualifierExpression) : QualifierExpression by qualifierExpression {
override val text: String
get() = qualifierExpression.text + ".asIterable()"
}
private val KotlinType.isArray: Boolean
get() = KotlinBuiltIns.isArray(this) || KotlinBuiltIns.isPrimitiveArray(this)
}
@@ -0,0 +1,79 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi.collections
import org.jetbrains.kotlin.idea.debugger.sequence.psi.KotlinPsiUtil
import org.jetbrains.kotlin.idea.debugger.sequence.psi.impl.KotlinChainBuilderBase
import org.jetbrains.kotlin.idea.debugger.sequence.psi.previousCall
import org.jetbrains.kotlin.idea.debugger.sequence.psi.receiverType
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.supertypes
class KotlinCollectionChainBuilder
: KotlinChainBuilderBase(CollectionChainTransformer()) {
private companion object {
// TODO: Avoid enumeration of all available types
val SUPPORTED_RECEIVERS = setOf(
"kotlin.collections.Iterable", "kotlin.CharSequence", "kotlin.Array",
"kotlin.BooleanArray", "kotlin.ByteArray", "kotlin.ShortArray", "kotlin.CharArray", "kotlin.IntArray",
"kotlin.LongArray", "kotlin.DoubleArray", "kotlin.FloatArray"
)
}
private fun isCollectionTransformationCall(expression: KtCallExpression): Boolean {
val receiverType = expression.receiverType() ?: return false
if (isTypeSuitable(receiverType)) return true
return receiverType.supertypes().any { isTypeSuitable(it) }
}
override val existenceChecker: ExistenceChecker = object : ExistenceChecker() {
override fun visitCallExpression(expression: KtCallExpression) {
if (isFound()) return
if (isCollectionTransformationCall(expression)) {
fireElementFound()
} else {
super.visitCallExpression(expression)
}
}
}
override fun createChainsBuilder(): ChainBuilder = object : ChainBuilder() {
private val previousCalls: MutableMap<KtCallExpression, KtCallExpression> = mutableMapOf()
private val visitedCalls: MutableSet<KtCallExpression> = mutableSetOf()
override fun visitCallExpression(expression: KtCallExpression) {
super.visitCallExpression(expression)
if (isCollectionTransformationCall(expression)) {
visitedCalls.add(expression)
val previous = expression.previousCall()
if (previous != null && isCollectionTransformationCall(previous)) {
previousCalls[expression] = previous
}
}
}
override fun chains(): List<List<KtCallExpression>> {
val notLastCalls: Set<KtCallExpression> = previousCalls.values.toSet()
return visitedCalls.filter { it !in notLastCalls }.map { buildPsiChain(it) }
}
private fun buildPsiChain(expression: KtCallExpression): List<KtCallExpression> {
val result = mutableListOf<KtCallExpression>()
var current: KtCallExpression? = expression
while (current != null) {
result.add(current)
current = previousCalls[current]
}
result.reverse()
return result
}
}
private fun isTypeSuitable(type: KotlinType): Boolean =
SUPPORTED_RECEIVERS.contains(KotlinPsiUtil.getTypeWithoutTypeParameters(type))
}
@@ -0,0 +1,60 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi.collections
import com.intellij.debugger.streams.trace.impl.handler.type.GenericType
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.kotlin.idea.debugger.sequence.psi.CallTypeExtractor
import org.jetbrains.kotlin.idea.debugger.sequence.psi.KotlinPsiUtil
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes.ANY
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes.NULLABLE_ANY
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.supertypes
class KotlinCollectionsTypeExtractor : CallTypeExtractor.Base() {
private companion object {
val LOG = Logger.getInstance(KotlinCollectionsTypeExtractor::class.java)
}
override fun extractItemsType(type: KotlinType?): GenericType {
if (type == null) return NULLABLE_ANY
return tryToFindElementType(type) ?: defaultType(type)
}
override fun getResultType(type: KotlinType): GenericType {
val typeName = KotlinPsiUtil.getTypeWithoutTypeParameters(type)
return KotlinSequenceTypes.primitiveTypeByName(typeName) ?: KotlinSequenceTypes.primitiveArrayByName(typeName) ?: getAny(type)
}
private fun tryToFindElementType(type: KotlinType): GenericType? {
val typeName = KotlinPsiUtil.getTypeWithoutTypeParameters(type)
if (typeName == "kotlin.collections.Iterable" || typeName == "kotlin.Array") {
if (type.arguments.isEmpty()) return NULLABLE_ANY
val itemsType = type.arguments.first().type
if (itemsType.isMarkedNullable) return NULLABLE_ANY
val primitiveType = KotlinSequenceTypes.primitiveTypeByName(KotlinPsiUtil.getTypeWithoutTypeParameters(itemsType))
return primitiveType ?: ANY
}
if (typeName == "kotlin.String" || typeName == "kotlin.CharSequence") return KotlinSequenceTypes.CHAR
val primitiveArray = KotlinSequenceTypes.primitiveArrayByName(typeName)
if (primitiveArray != null) return primitiveArray.elementType
return type.supertypes().asSequence()
.map(this::tryToFindElementType)
.firstOrNull()
}
private fun defaultType(type: KotlinType): GenericType {
LOG.warn("Could not find type of items for type ${KotlinPsiUtil.getTypeName(type)}")
return getAny(type)
}
private fun getAny(type: KotlinType): GenericType = if (type.isMarkedNullable) NULLABLE_ANY else ANY
}
@@ -0,0 +1,94 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi.impl
import com.intellij.debugger.streams.psi.ChainTransformer
import com.intellij.debugger.streams.psi.PsiUtil
import com.intellij.debugger.streams.wrapper.StreamChain
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.psi.*
abstract class KotlinChainBuilderBase(private val transformer: ChainTransformer<KtCallExpression>) : StreamChainBuilder {
protected abstract val existenceChecker: ExistenceChecker
override fun isChainExists(startElement: PsiElement): Boolean {
val start = if (startElement is PsiWhiteSpace) PsiUtil.ignoreWhiteSpaces(startElement) else startElement
var element = getLatestElementInScope(start)
existenceChecker.reset()
while (element != null && !existenceChecker.isFound()) {
existenceChecker.reset()
element.accept(existenceChecker)
element = toUpperLevel(element)
}
return existenceChecker.isFound()
}
override fun build(startElement: PsiElement): List<StreamChain> {
val visitor = createChainsBuilder()
val start = if (startElement is PsiWhiteSpace) PsiUtil.ignoreWhiteSpaces(startElement) else startElement
var element = getLatestElementInScope(start)
while (element != null) {
element.accept(visitor)
element = toUpperLevel(element)
}
return visitor.chains().map { transformer.transform(it, startElement) }
}
private fun toUpperLevel(element: PsiElement): PsiElement? {
var current = element.parent
while (current != null && !(current is KtLambdaExpression || current is KtAnonymousInitializer || current is KtObjectDeclaration)) {
current = current.parent
}
return getLatestElementInScope(current)
}
protected abstract fun createChainsBuilder(): ChainBuilder
private fun getLatestElementInScope(element: PsiElement?): PsiElement? {
var current = element
while (current != null) {
if (current is KtNamedFunction && current.hasInitializer()) {
break
}
val parent = current.parent
if (parent is KtBlockExpression || parent is KtLambdaExpression) {
break
}
current = parent
}
return current
}
protected abstract class ExistenceChecker : MyTreeVisitor() {
private var myIsFound: Boolean = false
fun isFound(): Boolean = myIsFound
fun reset() = setFound(false)
protected fun fireElementFound() = setFound(true)
private fun setFound(value: Boolean) {
myIsFound = value
}
}
protected abstract class ChainBuilder : MyTreeVisitor() {
abstract fun chains(): List<List<KtCallExpression>>
}
protected abstract class MyTreeVisitor : KtTreeVisitorVoid() {
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {}
override fun visitBlockExpression(expression: KtBlockExpression) {}
}
}
@@ -0,0 +1,75 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi.impl
import com.intellij.debugger.streams.psi.ChainTransformer
import com.intellij.debugger.streams.trace.impl.handler.type.GenericType
import com.intellij.debugger.streams.wrapper.CallArgument
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
import com.intellij.debugger.streams.wrapper.QualifierExpression
import com.intellij.debugger.streams.wrapper.StreamChain
import com.intellij.debugger.streams.wrapper.impl.*
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.debugger.sequence.psi.CallTypeExtractor
import org.jetbrains.kotlin.idea.debugger.sequence.psi.KotlinPsiUtil
import org.jetbrains.kotlin.idea.debugger.sequence.psi.callName
import org.jetbrains.kotlin.idea.debugger.sequence.psi.resolveType
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
import org.jetbrains.kotlin.psi.KtValueArgument
import org.jetbrains.kotlin.resolve.calls.callUtil.getParameterForArgument
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
class KotlinChainTransformerImpl(private val typeExtractor: CallTypeExtractor) : ChainTransformer<KtCallExpression> {
override fun transform(callChain: List<KtCallExpression>, context: PsiElement): StreamChain {
val intermediateCalls = mutableListOf<IntermediateStreamCall>()
for (call in callChain.subList(0, callChain.size - 1)) {
val (typeBefore, typeAfter) = typeExtractor.extractIntermediateCallTypes(call)
intermediateCalls += IntermediateStreamCallImpl(
call.callName(), call.valueArguments.map { createCallArgument(call, it) },
typeBefore, typeAfter,
call.textRange
)
}
val terminationsPsiCall = callChain.last()
val (typeBeforeTerminator, resultType) = typeExtractor.extractTerminalCallTypes(terminationsPsiCall)
val terminationCall = TerminatorStreamCallImpl(
terminationsPsiCall.callName(),
terminationsPsiCall.valueArguments.map { createCallArgument(terminationsPsiCall, it) },
typeBeforeTerminator, resultType, terminationsPsiCall.textRange
)
val typeAfterQualifier =
if (intermediateCalls.isEmpty()) typeBeforeTerminator else intermediateCalls.first().typeBefore
val qualifier = createQualifier(callChain.first(), typeAfterQualifier)
return StreamChainImpl(qualifier, intermediateCalls, terminationCall, context)
}
private fun createCallArgument(callExpression: KtCallExpression, arg: KtValueArgument): CallArgument {
fun KtValueArgument.toCallArgument(): CallArgument {
val argExpression = getArgumentExpression()!!
return CallArgumentImpl(KotlinPsiUtil.getTypeName(argExpression.resolveType()), this.text)
}
val bindingContext = callExpression.getResolutionFacade().analyzeWithAllCompilerChecks(listOf(callExpression)).bindingContext
val resolvedCall = callExpression.getResolvedCall(bindingContext) ?: return arg.toCallArgument()
val parameter = resolvedCall.getParameterForArgument(arg) ?: return arg.toCallArgument()
return CallArgumentImpl(KotlinPsiUtil.getTypeName(parameter.type), arg.text)
}
private fun createQualifier(expression: PsiElement, typeAfter: GenericType): QualifierExpression {
val parent = expression.parent as? KtDotQualifiedExpression
?: return QualifierExpressionImpl("", TextRange.EMPTY_RANGE, typeAfter)
val receiver = parent.receiverExpression
return QualifierExpressionImpl(receiver.text, receiver.textRange, typeAfter)
}
}
@@ -0,0 +1,42 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi.impl
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.idea.debugger.sequence.psi.KotlinPsiUtil
import org.jetbrains.kotlin.idea.debugger.sequence.psi.StreamCallChecker
import org.jetbrains.kotlin.idea.debugger.sequence.psi.receiverType
import org.jetbrains.kotlin.idea.debugger.sequence.psi.resolveType
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.types.KotlinType
class PackageBasedCallChecker(private val supportedPackage: String) : StreamCallChecker {
override fun isIntermediateCall(expression: KtCallExpression): Boolean {
return checkReceiverSupported(expression) && checkResultSupported(expression, true)
}
override fun isTerminationCall(expression: KtCallExpression): Boolean {
return checkReceiverSupported(expression) && checkResultSupported(expression, false)
}
private fun checkResultSupported(
expression: KtCallExpression,
shouldSupportResult: Boolean
): Boolean {
val resultType = expression.resolveType()
return shouldSupportResult == isSupportedType(resultType)
}
private fun checkReceiverSupported(expression: KtCallExpression): Boolean {
val receiverType = expression.receiverType()
return receiverType != null && isSupportedType(receiverType)
}
private fun isSupportedType(type: KotlinType): Boolean {
val typeName = KotlinPsiUtil.getTypeWithoutTypeParameters(type)
return StringUtil.getPackageName(typeName).startsWith(supportedPackage)
}
}
@@ -0,0 +1,75 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi.impl
import com.intellij.debugger.streams.psi.ChainTransformer
import org.jetbrains.kotlin.idea.debugger.sequence.psi.StreamCallChecker
import org.jetbrains.kotlin.idea.debugger.sequence.psi.previousCall
import org.jetbrains.kotlin.psi.KtCallExpression
import java.util.*
open class TerminatedChainBuilder(
transformer: ChainTransformer<KtCallExpression>,
private val callChecker: StreamCallChecker
) : KotlinChainBuilderBase(transformer) {
override val existenceChecker: ExistenceChecker = MyExistenceChecker()
override fun createChainsBuilder(): ChainBuilder = MyBuilderVisitor()
private inner class MyExistenceChecker : ExistenceChecker() {
override fun visitCallExpression(expression: KtCallExpression) {
if (callChecker.isTerminationCall(expression)) {
fireElementFound()
} else {
super.visitCallExpression(expression)
}
}
}
private inner class MyBuilderVisitor : ChainBuilder() {
private val myTerminationCalls = mutableSetOf<KtCallExpression>()
private val myPreviousCalls = mutableMapOf<KtCallExpression, KtCallExpression>()
override fun visitCallExpression(expression: KtCallExpression) {
super.visitCallExpression(expression)
if (!myPreviousCalls.containsKey(expression) && callChecker.isStreamCall(expression)) {
updateCallTree(expression)
}
}
private fun updateCallTree(expression: KtCallExpression) {
if (callChecker.isTerminationCall(expression)) {
myTerminationCalls.add(expression)
}
val parentCall = expression.previousCall()
if (parentCall is KtCallExpression && callChecker.isStreamCall(parentCall)) {
myPreviousCalls[expression] = parentCall
updateCallTree(parentCall)
}
}
override fun chains(): List<List<KtCallExpression>> {
val chains = ArrayList<List<KtCallExpression>>()
for (terminationCall in myTerminationCalls) {
val chain = ArrayList<KtCallExpression>()
var current: KtCallExpression? = terminationCall
while (current != null) {
if (!callChecker.isStreamCall(current)) {
break
}
chain.add(current)
current = myPreviousCalls[current]
}
chain.reverse()
chains.add(chain)
}
return chains
}
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi.java
import com.intellij.debugger.streams.trace.impl.handler.type.ClassTypeImpl
import com.intellij.debugger.streams.trace.impl.handler.type.GenericType
import com.intellij.psi.CommonClassNames
import org.jetbrains.kotlin.idea.debugger.sequence.psi.CallTypeExtractor
import org.jetbrains.kotlin.idea.debugger.sequence.psi.KotlinPsiUtil
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.getImmediateSuperclassNotAny
class JavaStreamChainTypeExtractor : CallTypeExtractor.Base() {
override fun extractItemsType(type: KotlinType?): GenericType {
if (type == null) {
return KotlinSequenceTypes.NULLABLE_ANY
}
return when (KotlinPsiUtil.getTypeWithoutTypeParameters(type)) {
CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM -> KotlinSequenceTypes.INT
CommonClassNames.JAVA_UTIL_STREAM_DOUBLE_STREAM -> KotlinSequenceTypes.DOUBLE
CommonClassNames.JAVA_UTIL_STREAM_LONG_STREAM -> KotlinSequenceTypes.LONG
CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM -> KotlinSequenceTypes.NULLABLE_ANY
else -> extractItemsType(type.getImmediateSuperclassNotAny())
}
}
override fun getResultType(type: KotlinType): GenericType = ClassTypeImpl(KotlinPsiUtil.getTypeName(type))
}
@@ -0,0 +1,20 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi.java
import org.jetbrains.kotlin.idea.debugger.sequence.psi.CallCheckerWithNameHeuristics
import org.jetbrains.kotlin.idea.debugger.sequence.psi.StreamCallChecker
class StandardLibraryCallChecker(nestedChecker: StreamCallChecker) : CallCheckerWithNameHeuristics(nestedChecker) {
private companion object {
val TERMINATION_CALLS: Set<String> = setOf(
"forEach", "toArray", "reduce", "collect", "min", "max", "count", "sum", "anyMatch", "allMatch", "noneMatch", "findFirst",
"findAny", "forEachOrdered", "average", "summaryStatistics"
)
}
override fun isTerminalCallName(callName: String): Boolean = TERMINATION_CALLS.contains(callName)
}
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi.java
import org.jetbrains.kotlin.idea.debugger.sequence.psi.CallCheckerWithNameHeuristics
import org.jetbrains.kotlin.idea.debugger.sequence.psi.StreamCallChecker
class StreamExCallChecker(nestedChecker: StreamCallChecker): CallCheckerWithNameHeuristics(nestedChecker) {
private companion object {
val TERMINATION_CALLS: Set<String> = setOf(
"forEach", "toArray", "reduce", "collect", "min", "max", "count", "sum", "anyMatch", "allMatch", "noneMatch", "findFirst",
"findAny", "forEachOrdered", "average", "summaryStatistics", "toList", "toSet", "toCollection", "toListAndThen", "toSetAndThen",
"toImmutableList", "toImmutableSet", "toMap", "toSortedMap", "toNavigableMap", "toImmutableMap", "toMapAndThen", "toCustomMap",
"partitioningBy", "partitioningTo", "groupingBy", "groupingTo", "grouping", "joining", "toFlatList", "toFlatCollection",
"maxBy", "maxByInt", "maxByLong", "maxByDouble", "minBy", "minByInt", "minByLong", "minByDouble", "has", "indexOf", "foldLeft",
"foldRight", "scanLeft", "scanRight", "toByteArray", "toCharArray", "toShortArray", "toBitSet", "toFloatArray", "charsToString",
"codePointsToString", "forPairs", "forKeyValue", "asByteInputStream", "into"
)
}
override fun isTerminalCallName(callName: String): Boolean = TERMINATION_CALLS.contains(callName)
}
@@ -0,0 +1,32 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi.sequence
import org.jetbrains.kotlin.idea.debugger.sequence.psi.KotlinPsiUtil
import org.jetbrains.kotlin.idea.debugger.sequence.psi.StreamCallChecker
import org.jetbrains.kotlin.idea.debugger.sequence.psi.receiverType
import org.jetbrains.kotlin.idea.debugger.sequence.psi.resolveType
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.supertypes
class SequenceCallChecker : StreamCallChecker {
override fun isIntermediateCall(expression: KtCallExpression): Boolean {
val receiverType = expression.receiverType() ?: return false
return isSequenceInheritor(receiverType) && isSequenceInheritor(expression.resolveType())
}
override fun isTerminationCall(expression: KtCallExpression): Boolean {
val receiverType = expression.receiverType() ?: return false
return isSequenceInheritor(receiverType) && !isSequenceInheritor(expression.resolveType())
}
private fun isSequenceInheritor(type: KotlinType): Boolean =
isSequenceType(type) || type.supertypes().any(this::isSequenceType)
private fun isSequenceType(type: KotlinType): Boolean =
"kotlin.sequences.Sequence" == KotlinPsiUtil.getTypeWithoutTypeParameters(type)
}
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi.sequence
import org.jetbrains.kotlin.idea.debugger.sequence.psi.CallCheckerWithNameHeuristics
import org.jetbrains.kotlin.idea.debugger.sequence.psi.StreamCallChecker
class SequenceCallCheckerWithNameHeuristics(nestedChecker: StreamCallChecker) : CallCheckerWithNameHeuristics(nestedChecker) {
private companion object {
val TERMINATION_CALLS: Set<String> = setOf(
"all", "any", "associate", "associateBy", "associateByTo", "associateTo", "average", "chunked", "contains", "count", "distinct",
"distinctBy", "elementAt", "elementAtOrElse", "elementAtOrNull", "find", "findLast", "first", "firstOrNull", "fold",
"foldIndexed", "forEach", "forEachIndexed", "groupBy", "groupByTo", "indexOf", "indexOfFirst", "indexOfLast", "joinToString",
"joinTo", "last", "lastIndexOf", "lastOrNull", "max", "maxBy", "maxWith", "min", "minBy", "minWith", "none", "partition",
"reduce", "reduceIndexed", "single", "singleOrNull", "sum", "sumBy", "sumByDouble", "toCollection", "toHashSet", "toList",
"toMutableList", "toMutableSet", "toSet", "toSortedSet", "unzip"
)
}
override fun isTerminalCallName(callName: String): Boolean = TERMINATION_CALLS.contains(callName)
}
@@ -0,0 +1,54 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.psi.sequence
import com.intellij.debugger.streams.trace.impl.handler.type.ClassTypeImpl
import com.intellij.debugger.streams.trace.impl.handler.type.GenericType
import com.intellij.openapi.diagnostic.Logger
import org.jetbrains.kotlin.idea.debugger.sequence.psi.CallTypeExtractor
import org.jetbrains.kotlin.idea.debugger.sequence.psi.KotlinPsiUtil
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.supertypes
class SequenceTypeExtractor : CallTypeExtractor.Base() {
private companion object {
val LOG = Logger.getInstance(SequenceTypeExtractor::class.java)
}
override fun extractItemsType(type: KotlinType?): GenericType {
if (type == null) return KotlinSequenceTypes.NULLABLE_ANY
return tryToFindElementType(type) ?: defaultType(type)
}
override fun getResultType(type: KotlinType): GenericType {
val typeName = KotlinPsiUtil.getTypeWithoutTypeParameters(type)
return KotlinSequenceTypes.primitiveTypeByName(typeName)
?: KotlinSequenceTypes.primitiveArrayByName(typeName)
?: ClassTypeImpl(KotlinPsiUtil.getTypeName(type))
}
private fun tryToFindElementType(type: KotlinType): GenericType? {
val typeName = KotlinPsiUtil.getTypeWithoutTypeParameters(type)
if (typeName == "kotlin.sequences.Sequence") {
if (type.arguments.isEmpty()) return KotlinSequenceTypes.NULLABLE_ANY
val itemsType = type.arguments.single().type
if (itemsType.isMarkedNullable) return KotlinSequenceTypes.NULLABLE_ANY
val primitiveType = KotlinSequenceTypes.primitiveTypeByName(KotlinPsiUtil.getTypeWithoutTypeParameters(itemsType))
return primitiveType ?: KotlinSequenceTypes.ANY
}
return type.supertypes().asSequence()
.map(this::tryToFindElementType)
.firstOrNull()
}
private fun defaultType(type: KotlinType): GenericType {
LOG.warn("Could not find type of items for type ${KotlinPsiUtil.getTypeName(type)}")
return if (type.isMarkedNullable) KotlinSequenceTypes.NULLABLE_ANY else KotlinSequenceTypes.ANY
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.resolve
import com.intellij.debugger.streams.resolve.ValuesOrderResolver
import com.intellij.debugger.streams.trace.TraceElement
import com.intellij.debugger.streams.trace.TraceInfo
class ChunkedResolver : ValuesOrderResolver {
override fun resolve(info: TraceInfo): ValuesOrderResolver.Result {
val beforeIndex = info.valuesOrderBefore
val afterIndex = info.valuesOrderAfter
val invertedOrder = mutableMapOf<Int, MutableList<Int>>()
val beforeTimes = beforeIndex.keys.sorted().toTypedArray()
val afterTimes = afterIndex.keys.sorted().toTypedArray()
var beforeIx = 0
for (afterTime in afterTimes) {
while (beforeIx < beforeTimes.size && beforeTimes[beforeIx] < afterTime) {
invertedOrder.computeIfAbsent(afterTime, { _ -> mutableListOf() }).add(beforeTimes[beforeIx])
beforeIx += 1
}
}
val direct = mutableMapOf<TraceElement, List<TraceElement>>()
val reverse = mutableMapOf<TraceElement, List<TraceElement>>()
for ((timeAfter, elementAfter) in afterIndex) {
val before: List<Int> = invertedOrder[timeAfter] ?: emptyList()
val beforeElements = before.map { beforeIndex[it]!! }
beforeElements.forEach { direct[it] = listOf(elementAfter) }
reverse[elementAfter] = beforeElements
}
return ValuesOrderResolver.Result.of(direct, reverse)
}
}
@@ -0,0 +1,41 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.resolve
import com.intellij.debugger.streams.resolve.ValuesOrderResolver
import com.intellij.debugger.streams.trace.TraceElement
import com.intellij.debugger.streams.trace.TraceInfo
class FilteredMapResolver : ValuesOrderResolver {
override fun resolve(info: TraceInfo): ValuesOrderResolver.Result {
val before = info.valuesOrderBefore
val after = info.valuesOrderAfter
val invertedOrder = mutableMapOf<Int, Int>()
val beforeTimes = before.keys.sorted().toIntArray()
val afterTimes = after.keys.sorted().toIntArray()
var beforeIndex = 0
for (afterTime in afterTimes) {
while (beforeIndex < beforeTimes.size && afterTime > beforeTimes[beforeIndex]) beforeIndex += 1
val beforeTime = beforeTimes[beforeIndex - 1]
if (beforeTime < afterTime) {
invertedOrder[afterTime] = beforeTime
}
}
val direct = mutableMapOf<TraceElement, List<TraceElement>>()
val reverse = mutableMapOf<TraceElement, List<TraceElement>>()
for ((afterTime, beforeTime) in invertedOrder) {
val beforeElement = before.getValue(beforeTime)
val afterElement = after.getValue(afterTime)
direct[beforeElement] = listOf(afterElement)
reverse[afterElement] = listOf(beforeElement)
}
return ValuesOrderResolver.Result.of(direct, reverse)
}
}
@@ -0,0 +1,58 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.resolve
import com.intellij.debugger.streams.resolve.ValuesOrderResolver
import com.intellij.debugger.streams.trace.TraceElement
import com.intellij.debugger.streams.trace.TraceInfo
class WindowedResolver : ValuesOrderResolver {
override fun resolve(info: TraceInfo): ValuesOrderResolver.Result {
val indexBefore = info.valuesOrderBefore
val indexAfter = info.valuesOrderAfter
val timesBefore = indexBefore.keys.sorted().toIntArray()
val timesAfter = indexAfter.keys.sorted().toIntArray()
if (timesAfter.isEmpty()) return emptyTransitions(indexBefore)
val direct = mutableMapOf<TraceElement, MutableList<TraceElement>>()
val reverse = mutableMapOf<TraceElement, List<TraceElement>>()
var windowStartIndex = 0
var windowEndIndex = calcWindowSize(timesBefore, timesAfter)
for (timeAfter in timesAfter) {
if (windowEndIndex == timesAfter.size) {
windowStartIndex += 1
} else {
while (windowEndIndex < timesBefore.size && timesBefore[windowEndIndex] < timeAfter) {
windowStartIndex += 1
windowEndIndex += 1
}
}
val window = (windowStartIndex until windowEndIndex).asSequence()
.map { indexBefore[timesBefore[it]]!! }
.toList()
val mappedElement = indexAfter[timeAfter]!!
window.forEach { direct.computeIfAbsent(it, { mutableListOf() }).add(mappedElement) }
reverse[mappedElement] = window
}
return ValuesOrderResolver.Result.of(direct, reverse)
}
private fun calcWindowSize(before: IntArray, after: IntArray): Int {
var size = 0
while (size < before.size && before[size] < after[0]) size += 1
return size
}
private fun emptyTransitions(indexBefore: MutableMap<Int, TraceElement>): ValuesOrderResolver.Result {
val direct = indexBefore.asSequence().sortedBy { it.key }.associate { it.value to emptyList<TraceElement>() }
return ValuesOrderResolver.Result.of(direct, emptyMap())
}
}
@@ -0,0 +1,15 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.impl.handler.PeekCall
import com.intellij.debugger.streams.trace.impl.handler.type.GenericType
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
class JavaPeekCallFactory : PeekCallFactory {
override fun createPeekCall(elementsType: GenericType, lambda: String): IntermediateStreamCall =
PeekCall(lambda, elementsType)
}
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.ArrayVariable
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.trace.dsl.VariableDeclaration
import com.intellij.debugger.streams.trace.dsl.impl.TextExpression
import com.intellij.debugger.streams.trace.dsl.impl.VariableImpl
import com.intellij.debugger.streams.trace.impl.handler.type.ArrayType
class KotlinArrayVariable(override val type: ArrayType, override val name: String) : VariableImpl(type, name), ArrayVariable {
override fun get(index: Expression): Expression = TextExpression("$name[${index.toCode()}]!!")
override fun set(index: Expression, value: Expression): Expression = TextExpression("$name[${index.toCode()}] = ${value.toCode()}")
override fun defaultDeclaration(size: Expression): VariableDeclaration =
KotlinVariableDeclaration(this, false, type.sizedDeclaration(size.toCode()))
}
@@ -0,0 +1,14 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.trace.dsl.Variable
import com.intellij.debugger.streams.trace.dsl.impl.AssignmentStatement
class KotlinAssignmentStatement(override val variable: Variable, override val expression: Expression) : AssignmentStatement {
override fun toCode(indent: Int): String = "${variable.toCode()} = ${expression.toCode()}".withIndent(indent)
}
@@ -0,0 +1,14 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.trace.dsl.StatementFactory
import com.intellij.debugger.streams.trace.dsl.impl.LineSeparatedCodeBlock
open class KotlinCodeBlock(statementFactory: StatementFactory) : LineSeparatedCodeBlock(statementFactory) {
override fun doReturn(expression: Expression) = addStatement(expression)
}
@@ -0,0 +1,15 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.impl.handler.type.GenericType
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
import org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.OnEachCall
class KotlinCollectionsPeekCallFactory : PeekCallFactory {
override fun createPeekCall(elementsType: GenericType, lambda: String): IntermediateStreamCall =
OnEachCall(elementsType, lambda)
}
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.Convertable
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.trace.dsl.ForLoopBody
import com.intellij.debugger.streams.trace.dsl.Variable
class KotlinForEachLoop(
private val iterateVariable: Variable,
private val collection: Expression,
private val loopBody: ForLoopBody
) : Convertable {
override fun toCode(indent: Int): String =
"for (${iterateVariable.name} in ${collection.toCode()}) {\n".withIndent(indent) +
loopBody.toCode(indent + 1) +
"}".withIndent(indent)
}
@@ -0,0 +1,25 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.Convertable
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.trace.dsl.ForLoopBody
import com.intellij.debugger.streams.trace.dsl.VariableDeclaration
class KotlinForLoop(
private val initialization: VariableDeclaration,
private val condition: Expression,
private val afterThought: Expression,
private val loopBody: ForLoopBody
) : Convertable {
override fun toCode(indent: Int): String =
initialization.toCode(indent) + "\n" +
"while (${condition.toCode()}) {\n".withIndent(indent) +
loopBody.toCode(indent + 1) +
afterThought.toCode(indent + 1) + "\n" +
"}".withIndent(indent)
}
@@ -0,0 +1,23 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.trace.dsl.ForLoopBody
import com.intellij.debugger.streams.trace.dsl.StatementFactory
import com.intellij.debugger.streams.trace.dsl.Variable
import com.intellij.debugger.streams.trace.dsl.impl.TextExpression
class KotlinForLoopBody(
override val loopVariable: Variable,
statementFactory: StatementFactory
) : KotlinCodeBlock(statementFactory), ForLoopBody {
private companion object {
val BREAK = TextExpression("break")
}
override fun breakIteration(): Expression = BREAK
}
@@ -0,0 +1,28 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.CodeBlock
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.trace.dsl.StatementFactory
import com.intellij.debugger.streams.trace.dsl.impl.common.IfBranchBase
class KotlinIfBranch(condition: Expression, thenBlock: CodeBlock, statementFactory: StatementFactory) :
IfBranchBase(condition, thenBlock, statementFactory) {
override fun toCode(indent: Int): String {
val elseBlockVar = elseBlock
val ifThen = "if (${condition.toCode(0)}) {\n".withIndent(indent) +
thenBlock.toCode(indent + 1) +
"}".withIndent(indent)
if (elseBlockVar != null) {
return ifThen + " else { \n" +
elseBlockVar.toCode(indent + 1) +
"}".withIndent(indent)
}
return ifThen
}
}
@@ -0,0 +1,20 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.CodeBlock
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.trace.dsl.Lambda
import com.intellij.debugger.streams.trace.dsl.impl.TextExpression
class KotlinLambda(override val variableName: String, override val body: CodeBlock) : Lambda {
override fun call(callName: String, vararg args: Expression): Expression = TextExpression("(${toCode()})").call(callName, *args)
override fun toCode(indent: Int): String =
"{ $variableName ->\n".withIndent(indent) +
body.toCode(indent + 1) +
"}"
}
@@ -0,0 +1,13 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.trace.dsl.LambdaBody
import com.intellij.debugger.streams.trace.dsl.StatementFactory
class KotlinLambdaBody(override val lambdaArg: Expression, statementFactory: StatementFactory) : KotlinCodeBlock(statementFactory),
LambdaBody
@@ -0,0 +1,25 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.trace.dsl.ListVariable
import com.intellij.debugger.streams.trace.dsl.VariableDeclaration
import com.intellij.debugger.streams.trace.dsl.impl.VariableImpl
import com.intellij.debugger.streams.trace.impl.handler.type.ListType
class KotlinListVariable(override val type: ListType, name: String) : VariableImpl(type, name), ListVariable {
override fun get(index: Expression): Expression = call("get", index)
override fun set(index: Expression, newValue: Expression): Expression = call("set", index, newValue)
override fun add(element: Expression): Expression = call("add", element)
override fun contains(element: Expression): Expression = call("contains", element)
override fun size(): Expression = property("size")
override fun defaultDeclaration(): VariableDeclaration =
KotlinVariableDeclaration(this, false, type.defaultValue)
}
@@ -0,0 +1,32 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.trace.dsl.Lambda
import com.intellij.debugger.streams.trace.dsl.VariableDeclaration
import com.intellij.debugger.streams.trace.dsl.impl.TextExpression
import com.intellij.debugger.streams.trace.dsl.impl.common.MapVariableBase
import com.intellij.debugger.streams.trace.impl.handler.type.MapType
class KotlinMapVariable(type: MapType, name: String) : MapVariableBase(type, name) {
override fun get(key: Expression): Expression = this.call("getValue", key)
override fun set(key: Expression, newValue: Expression): Expression =
TextExpression("${toCode()}[${key.toCode()}] = ${newValue.toCode()}")
override fun contains(key: Expression): Expression = TextExpression("(${key.toCode()} in ${toCode()})")
override fun size(): Expression = TextExpression("${toCode()}.size")
override fun keys(): Expression = TextExpression("${toCode()}.keys")
override fun computeIfAbsent(key: Expression, supplier: Lambda): Expression =
TextExpression("${toCode()}.computeIfAbsent(${key.toCode()}, ${supplier.toCode()})")
override fun defaultDeclaration(isMutable: Boolean): VariableDeclaration =
KotlinVariableDeclaration(this, false, type.defaultValue)
}
@@ -0,0 +1,93 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.Types
import com.intellij.debugger.streams.trace.impl.handler.type.*
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES
object KotlinSequenceTypes : Types {
override val ANY: GenericType = ClassTypeImpl(FQ_NAMES.any.asString(), "kotlin.Any()")
override val BOOLEAN: GenericType = ClassTypeImpl(FQ_NAMES._boolean.asString(), "false")
val BYTE: GenericType = ClassTypeImpl(FQ_NAMES._byte.asString(), "0")
val SHORT: GenericType = ClassTypeImpl(FQ_NAMES._short.asString(), "0")
val CHAR: GenericType = ClassTypeImpl(FQ_NAMES._char.asString(), "0.toChar()")
override val INT: GenericType = ClassTypeImpl(FQ_NAMES._int.asString(), "0")
override val LONG: GenericType = ClassTypeImpl(FQ_NAMES._long.asString(), "0L")
val FLOAT: GenericType = ClassTypeImpl(FQ_NAMES._float.asString(), "0.0f")
override val DOUBLE: GenericType = ClassTypeImpl(FQ_NAMES._double.asString(), "0.0")
override val STRING: GenericType = ClassTypeImpl(FQ_NAMES.string.asString(), "\"\"")
override val EXCEPTION: GenericType = ClassTypeImpl(FQ_NAMES.throwable.asString(), "kotlin.Throwable()")
override val VOID: GenericType = ClassTypeImpl(FQ_NAMES.unit.asString(), "Unit")
val NULLABLE_ANY: GenericType = nullable { ANY }
override val TIME: GenericType = ClassTypeImpl(
"java.util.concurrent.atomic.AtomicInteger",
"java.util.concurrent.atomic.AtomicInteger()"
)
override fun list(elementsType: GenericType): ListType =
ListTypeImpl(elementsType, { "kotlin.collections.MutableList<$it>" }, "kotlin.collections.mutableListOf()")
override fun array(elementType: GenericType): ArrayType = when (elementType) {
BOOLEAN -> ArrayTypeImpl(BOOLEAN, { "kotlin.BooleanArray" }, { "kotlin.BooleanArray($it)" })
BYTE -> ArrayTypeImpl(BYTE, { "kotlin.ByteArray" }, { "kotlin.ByteArray($it)" })
SHORT -> ArrayTypeImpl(SHORT, { "kotlin.ShortArray" }, { "kotlin.ShortArray($it)" })
CHAR -> ArrayTypeImpl(CHAR, { "kotlin.CharArray" }, { "kotlin.CharArray($it)" })
INT -> ArrayTypeImpl(INT, { "kotlin.IntArray" }, { "kotlin.IntArray($it)" })
LONG -> ArrayTypeImpl(LONG, { "kotlin.LongArray" }, { "kotlin.LongArray($it)" })
FLOAT -> ArrayTypeImpl(FLOAT, { "kotlin.FloatArray" }, { "kotlin.FloatArray($it)" })
DOUBLE -> ArrayTypeImpl(DOUBLE, { "kotlin.DoubleArray" }, { "kotlin.DoubleArray($it)" })
else -> ArrayTypeImpl(nullable { elementType }, { "kotlin.Array<$it>" },
{ "kotlin.arrayOfNulls<${elementType.genericTypeName}>($it)" })
}
override fun map(keyType: GenericType, valueType: GenericType): MapType =
MapTypeImpl(
keyType, valueType,
{ keys, values -> "kotlin.collections.MutableMap<$keys, $values>" },
"kotlin.collections.mutableMapOf()"
)
override fun linkedMap(keyType: GenericType, valueType: GenericType): MapType =
MapTypeImpl(
keyType, valueType,
{ keys, values -> "kotlin.collections.MutableMap<$keys, $values>" },
"kotlin.collections.linkedMapOf()"
)
override fun nullable(typeSelector: Types.() -> GenericType): GenericType {
val type = this.typeSelector()
if (type.genericTypeName.last() == '?') return type
return when (type) {
is ArrayType -> ArrayTypeImpl(type.elementType, { "kotlin.Array<$it>?" }, { type.sizedDeclaration(it) })
is ListType -> ListTypeImpl(type.elementType, { "kotlin.collections.MutableList<$it>?" }, type.defaultValue)
is MapType -> MapTypeImpl(
type.keyType, type.valueType, { keys, values -> "kotlin.collections.MutableMap<$keys, $values>?" },
type.defaultValue
)
else -> ClassTypeImpl(type.genericTypeName + '?', type.defaultValue)
}
}
private val primitiveTypesIndex: Map<String, GenericType> =
listOf(
BOOLEAN, BYTE, INT, SHORT,
CHAR, LONG, FLOAT, DOUBLE
)
.associate { it.genericTypeName to it }
private val primitiveArraysIndex: Map<String, ArrayType> = primitiveTypesIndex.asSequence()
.map { array(it.value) }
.associate { it.genericTypeName to it }
fun primitiveTypeByName(typeName: String): GenericType? = primitiveTypesIndex[typeName]
fun primitiveArrayByName(typeName: String): ArrayType? = primitiveArraysIndex[typeName]
}
@@ -0,0 +1,111 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.*
import com.intellij.debugger.streams.trace.dsl.impl.AssignmentStatement
import com.intellij.debugger.streams.trace.dsl.impl.TextExpression
import com.intellij.debugger.streams.trace.dsl.impl.VariableImpl
import com.intellij.debugger.streams.trace.impl.handler.type.GenericType
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
class KotlinStatementFactory(private val peekCallFactory: PeekCallFactory) : StatementFactory {
override fun createNewListExpression(elementType: GenericType, vararg args: Expression): Expression =
TextExpression("kotlin.collections.mutableListOf<${elementType.genericTypeName}>(${StatementFactory.commaSeparate(*args)})")
override fun createListVariable(elementType: GenericType, name: String): ListVariable =
KotlinListVariable(types.list(elementType), name)
override fun not(expression: Expression): Expression = TextExpression("!${expression.toCode()}")
override val types: Types = KotlinSequenceTypes
override fun createEmptyCompositeCodeBlock(): CompositeCodeBlock = KotlinCodeBlock(this)
override fun createEmptyCodeBlock(): CodeBlock = KotlinCodeBlock(this)
override fun createVariableDeclaration(variable: Variable, isMutable: Boolean): VariableDeclaration =
KotlinVariableDeclaration(variable, isMutable)
override fun createVariableDeclaration(variable: Variable, init: Expression, isMutable: Boolean): VariableDeclaration =
KotlinVariableDeclaration(variable, isMutable, init.toCode())
override fun createEmptyForLoopBody(iterateVariable: Variable): ForLoopBody = KotlinForLoopBody(iterateVariable, this)
override fun createForEachLoop(iterateVariable: Variable, collection: Expression, loopBody: ForLoopBody): Convertable =
KotlinForEachLoop(iterateVariable, collection, loopBody)
override fun createForLoop(
initialization: VariableDeclaration, condition: Expression,
afterThought: Expression, loopBody: ForLoopBody
): Convertable =
KotlinForLoop(initialization, condition, afterThought, loopBody)
override fun createEmptyLambdaBody(argName: String): LambdaBody = KotlinLambdaBody(TextExpression(argName), this)
override fun createLambda(argName: String, lambdaBody: LambdaBody): Lambda = KotlinLambda(argName, lambdaBody)
override fun createVariable(type: GenericType, name: String): Variable = VariableImpl(type, name)
override fun and(left: Expression, right: Expression): Expression = TextExpression("${left.toCode()} && ${right.toCode()}")
override fun equals(left: Expression, right: Expression): Expression = TextExpression("${left.toCode()} == ${right.toCode()}")
override fun same(left: Expression, right: Expression): Expression = TextExpression("${left.toCode()} === ${right.toCode()}")
override fun createIfBranch(condition: Expression, thenBlock: CodeBlock): IfBranch =
KotlinIfBranch(condition, thenBlock, this)
override fun createAssignmentStatement(variable: Variable, expression: Expression): AssignmentStatement =
KotlinAssignmentStatement(variable, expression)
override fun createMapVariable(keyType: GenericType, valueType: GenericType, name: String, linked: Boolean): MapVariable =
KotlinMapVariable(if (linked) types.linkedMap(keyType, valueType) else types.map(keyType, valueType), name)
override fun createArrayVariable(elementType: GenericType, name: String): ArrayVariable =
KotlinArrayVariable(types.array(elementType), name)
override fun createScope(codeBlock: CodeBlock): Convertable =
object : Convertable {
override fun toCode(indent: Int): String =
"run {\n".withIndent(indent) +
codeBlock.toCode(indent + 1) +
"}".withIndent(indent)
}
override fun createTryBlock(block: CodeBlock): TryBlock = KotlinTryBlock(block, this)
override fun createTimeVariableDeclaration(): VariableDeclaration =
KotlinVariableDeclaration(createVariable(types.TIME, "time"), false, types.TIME.defaultValue)
override fun currentTimeExpression(): Expression = TextExpression("time.get()")
override fun updateCurrentTimeExpression(): Expression = TextExpression("time.incrementAndGet()")
override fun createNewArrayExpression(elementType: GenericType, vararg args: Expression): Expression {
val arguments = args.joinToString { it.toCode() }
val text = when (elementType) {
types.BOOLEAN -> "kotlin.booleanArrayOf($arguments)"
KotlinSequenceTypes.BYTE -> "kotlin.byteArrayOf($arguments)"
KotlinSequenceTypes.SHORT -> "kotlin.shortArrayOf($arguments)"
KotlinSequenceTypes.CHAR -> "kotlin.charArrayOf($arguments)"
types.INT -> "kotlin.intArrayOf($arguments)"
types.LONG -> "kotlin.longArrayOf($arguments)"
KotlinSequenceTypes.FLOAT -> "kotlin.floatArrayOf($arguments)"
types.DOUBLE -> "kotlin.doubleArrayOf($arguments)"
else -> "kotlin.arrayOf<${types.nullable { elementType }.genericTypeName}>($arguments)"
}
return TextExpression(text)
}
override fun createNewSizedArray(elementType: GenericType, size: Expression): Expression =
TextExpression(types.array(elementType).sizedDeclaration(size.toCode()))
override fun createPeekCall(elementsType: GenericType, lambda: String): IntermediateStreamCall =
peekCallFactory.createPeekCall(elementsType, lambda)
}
@@ -0,0 +1,21 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.CodeBlock
import com.intellij.debugger.streams.trace.dsl.StatementFactory
import com.intellij.debugger.streams.trace.dsl.impl.common.TryBlockBase
class KotlinTryBlock(private val block: CodeBlock, statementFactory: StatementFactory) : TryBlockBase(statementFactory) {
override fun toCode(indent: Int): String {
val descriptor = myCatchDescriptor ?: error("catch block must be specified")
return "try {\n".withIndent(indent) +
block.toCode(indent + 1) +
"} catch(${descriptor.variable.name} : ${descriptor.variable.type.variableTypeName}) {\n" +
descriptor.block.toCode(indent + 1) +
"}".withIndent(indent)
}
}
@@ -0,0 +1,21 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.dsl.Variable
import com.intellij.debugger.streams.trace.dsl.VariableDeclaration
class KotlinVariableDeclaration(
override val variable: Variable,
override val isMutable: Boolean,
private val init: String = ""
) : VariableDeclaration {
override fun toCode(indent: Int): String {
val prefix = if (isMutable) "var" else "val"
val suffix = if (init.trim().isEmpty()) "" else " = $init"
return "$prefix ${variable.name}: ${variable.type.variableTypeName}$suffix".withIndent(indent)
}
}
@@ -0,0 +1,13 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl
import com.intellij.debugger.streams.trace.impl.handler.type.GenericType
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
interface PeekCallFactory {
fun createPeekCall(elementsType: GenericType, lambda: String): IntermediateStreamCall
}
@@ -0,0 +1,30 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.impl
import com.intellij.debugger.streams.lib.HandlerFactory
import com.intellij.debugger.streams.trace.dsl.Dsl
import com.intellij.debugger.streams.trace.impl.TraceExpressionBuilderBase
import com.intellij.debugger.streams.wrapper.StreamChain
import com.intellij.openapi.diagnostic.Logger
class KotlinTraceExpressionBuilder(dsl: Dsl, handlerFactory: HandlerFactory) : TraceExpressionBuilderBase(dsl, handlerFactory) {
private companion object {
private val LOG = Logger.getInstance(KotlinTraceExpressionBuilder::class.java)
}
override fun createTraceExpression(chain: StreamChain): String {
val expression = super.createTraceExpression(chain)
val resultDeclaration = dsl.declaration(dsl.variable(dsl.types.nullable { ANY }, resultVariableName), dsl.nullExpression, true)
val result = "${resultDeclaration.toCode()}\n " +
"$expression\n" +
resultVariableName
LOG.info("trace expression: \n$result")
return result
}
}
@@ -0,0 +1,21 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler
import com.intellij.debugger.streams.wrapper.*
import com.intellij.debugger.streams.wrapper.impl.IntermediateStreamCallImpl
import com.intellij.debugger.streams.wrapper.impl.TerminatorStreamCallImpl
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes
fun IntermediateStreamCall.withArgs(args: List<CallArgument>) =
IntermediateStreamCallImpl(name, args, typeBefore, typeAfter, textRange)
fun TerminatorStreamCall.withArgs(args: List<CallArgument>) =
TerminatorStreamCallImpl(name, args, typeBefore, resultType, textRange)
fun StreamCall.typeBefore() = if (this is TypeBeforeAware) this.typeBefore else KotlinSequenceTypes.ANY
fun StreamCall.typeAfter() = if (this is TypeAfterAware) this.typeAfter else KotlinSequenceTypes.ANY
@@ -0,0 +1,34 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler
import com.intellij.debugger.streams.trace.impl.handler.type.GenericType
import com.intellij.debugger.streams.wrapper.CallArgument
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
import com.intellij.debugger.streams.wrapper.StreamCallType
import com.intellij.debugger.streams.wrapper.impl.CallArgumentImpl
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes
class OnEachCall(private val elementsType: GenericType, lambda: String) : IntermediateStreamCall {
private val args: List<CallArgument>
init {
args = listOf(CallArgumentImpl(KotlinSequenceTypes.ANY.genericTypeName, lambda))
}
override fun getArguments(): List<CallArgument> = args
override fun getName(): String = "onEach"
override fun getType(): StreamCallType = StreamCallType.INTERMEDIATE
override fun getTextRange(): TextRange = TextRange.EMPTY_RANGE
override fun getTypeBefore(): GenericType = elementsType
override fun getTypeAfter(): GenericType = elementsType
}
@@ -0,0 +1,24 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.collections
import com.intellij.debugger.streams.trace.IntermediateCallHandler
import com.intellij.debugger.streams.trace.TerminatorCallHandler
import com.intellij.debugger.streams.trace.dsl.Dsl
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
import com.intellij.debugger.streams.wrapper.TerminatorStreamCall
/**
* Unlike java streams, most operations in kotlin collections are intermediate and terminal simultaneously.
* To avoid using of the same code in two places we will implement common logic in the {@link BothSemanticsHandler}.
*/
class BothSemanticHandlerWrapper(private val handler: BothSemanticsHandler) {
fun createIntermediateHandler(order: Int, call: IntermediateStreamCall, dsl: Dsl): IntermediateCallHandler =
CollectionIntermediateHandler(order, call, dsl, handler)
fun createTerminatorHandler(call: TerminatorStreamCall, resultExpression: String, dsl: Dsl): TerminatorCallHandler =
CollectionTerminatorHandler(call, resultExpression, dsl, handler)
}
@@ -0,0 +1,27 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.collections
import com.intellij.debugger.streams.trace.dsl.*
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
import com.intellij.debugger.streams.wrapper.StreamCall
import com.intellij.debugger.streams.wrapper.TerminatorStreamCall
interface BothSemanticsHandler {
fun variablesDeclaration(call: StreamCall, order: Int, dsl: Dsl): List<VariableDeclaration>
fun prepareResult(dsl: Dsl, variables: List<Variable>): CodeBlock
fun additionalCallsBefore(call: StreamCall, dsl: Dsl): List<IntermediateStreamCall>
fun additionalCallsAfter(call: StreamCall, dsl: Dsl): List<IntermediateStreamCall>
fun transformAsIntermediateCall(call: IntermediateStreamCall, variables: List<Variable>, dsl: Dsl): IntermediateStreamCall
fun transformAsTerminalCall(call: TerminatorStreamCall, variables: List<Variable>, dsl: Dsl): TerminatorStreamCall
fun getResultExpression(call: StreamCall, dsl: Dsl, variables: List<Variable>): Expression
}
@@ -0,0 +1,25 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.collections
import com.intellij.debugger.streams.trace.TraceHandler
import com.intellij.debugger.streams.trace.dsl.Dsl
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.trace.dsl.Variable
import com.intellij.debugger.streams.trace.dsl.VariableDeclaration
import com.intellij.debugger.streams.wrapper.StreamCall
abstract class CollectionHandlerBase(
order: Int, private val dsl: Dsl,
private val call: StreamCall, private val internalHandler: BothSemanticsHandler
) : TraceHandler {
private val declarations: List<VariableDeclaration> = internalHandler.variablesDeclaration(call, order, dsl)
protected val variables: List<Variable> = declarations.map(VariableDeclaration::variable)
override fun additionalVariablesDeclaration(): List<VariableDeclaration> = declarations
override fun getResultExpression(): Expression = internalHandler.getResultExpression(call, dsl, variables)
}
@@ -0,0 +1,40 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.collections
import com.intellij.debugger.streams.trace.IntermediateCallHandler
import com.intellij.debugger.streams.trace.dsl.CodeBlock
import com.intellij.debugger.streams.trace.dsl.Dsl
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
class CollectionIntermediateHandler(
order: Int,
private val call: IntermediateStreamCall,
private val dsl: Dsl,
private val internalHandler: BothSemanticsHandler
) : IntermediateCallHandler, CollectionHandlerBase(order, dsl, call, internalHandler) {
override fun prepareResult(): CodeBlock {
return internalHandler.prepareResult(dsl, variables)
}
override fun additionalCallsBefore(): List<IntermediateStreamCall> {
return internalHandler.additionalCallsBefore(call, dsl)
}
override fun additionalCallsAfter(): List<IntermediateStreamCall> {
return internalHandler.additionalCallsAfter(call, dsl)
}
override fun transformCall(call: IntermediateStreamCall): IntermediateStreamCall {
return internalHandler.transformAsIntermediateCall(call, variables, dsl)
}
override fun getResultExpression(): Expression {
return internalHandler.getResultExpression(call, dsl, variables)
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.collections
import com.intellij.debugger.streams.trace.TerminatorCallHandler
import com.intellij.debugger.streams.trace.dsl.CodeBlock
import com.intellij.debugger.streams.trace.dsl.Dsl
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.trace.dsl.impl.TextExpression
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
import com.intellij.debugger.streams.wrapper.TerminatorStreamCall
class CollectionTerminatorHandler(
private val call: TerminatorStreamCall,
private val resultExpression: String,
private val dsl: Dsl,
private val internalHandler: BothSemanticsHandler
) : TerminatorCallHandler, CollectionHandlerBase(Int.MAX_VALUE, dsl, call, internalHandler) {
override fun prepareResult(): CodeBlock {
val prepareResult = internalHandler.prepareResult(dsl, variables)
val additionalCallsAfter = internalHandler.additionalCallsAfter(call, dsl)
if (additionalCallsAfter.isEmpty()) {
return prepareResult
}
return dsl.block {
+createCallAfterExpression(additionalCallsAfter)
add(prepareResult)
}
}
override fun additionalCallsBefore(): List<IntermediateStreamCall> =
internalHandler.additionalCallsBefore(call, dsl)
override fun transformCall(call: TerminatorStreamCall): TerminatorStreamCall {
return internalHandler.transformAsTerminalCall(call, variables, dsl)
}
private fun createCallAfterExpression(additionalCallsAfter: List<IntermediateStreamCall>): Expression {
var result: Expression = TextExpression(resultExpression)
for (call in additionalCallsAfter) {
val args = call.arguments.map { TextExpression(it.text) }.toTypedArray()
result = result.call(call.name, *args)
}
return result
}
}
@@ -0,0 +1,83 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.collections
import com.intellij.debugger.streams.trace.dsl.*
import com.intellij.debugger.streams.trace.dsl.impl.TextExpression
import com.intellij.debugger.streams.trace.impl.handler.type.ClassTypeImpl
import com.intellij.debugger.streams.wrapper.CallArgument
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
import com.intellij.debugger.streams.wrapper.StreamCall
import com.intellij.debugger.streams.wrapper.TerminatorStreamCall
import com.intellij.debugger.streams.wrapper.impl.CallArgumentImpl
import org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.typeBefore
import org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.withArgs
class FilterCallHandler : BothSemanticsHandler {
private companion object {
const val VALUES_ARRAY_NAME = "objectsInPredicate"
const val PREDICATE_RESULT_ARRAY_NAME = "filteringResults"
fun oldPredicateVariableName(order: Int): String = "filterPredicate$order"
}
override fun variablesDeclaration(call: StreamCall, order: Int, dsl: Dsl): List<VariableDeclaration> {
val types = dsl.types
val timeToObjectMap = dsl.linkedMap(types.INT, call.typeBefore(), call.name + "Values" + order)
val predicateResultMap = dsl.linkedMap(types.INT, types.BOOLEAN, call.name + "PredicateValue" + order)
val predicate = call.arguments[0]
// TODO: use generic types in CallArgument
val oldFilterPredicate = dsl.variable(ClassTypeImpl(predicate.type), oldPredicateVariableName(order))
return listOf(
timeToObjectMap.defaultDeclaration(), predicateResultMap.defaultDeclaration(),
dsl.declaration(oldFilterPredicate, TextExpression(predicate.text), false)
)
}
override fun prepareResult(dsl: Dsl, variables: List<Variable>): CodeBlock {
val values = variables[0] as MapVariable
val filterResult = variables[1] as MapVariable
return dsl.block {
add(values.convertToArray(dsl, VALUES_ARRAY_NAME))
add(filterResult.convertToArray(dsl, PREDICATE_RESULT_ARRAY_NAME))
}
}
override fun additionalCallsBefore(call: StreamCall, dsl: Dsl): List<IntermediateStreamCall> = emptyList()
override fun additionalCallsAfter(call: StreamCall, dsl: Dsl): List<IntermediateStreamCall> = emptyList()
override fun getResultExpression(call: StreamCall, dsl: Dsl, variables: List<Variable>): Expression {
return dsl.newArray(dsl.types.ANY, TextExpression(VALUES_ARRAY_NAME), TextExpression(PREDICATE_RESULT_ARRAY_NAME))
}
override fun transformAsIntermediateCall(call: IntermediateStreamCall, variables: List<Variable>, dsl: Dsl): IntermediateStreamCall {
return call.withArgs(listOf(createNewPredicate(variables, dsl)))
}
override fun transformAsTerminalCall(call: TerminatorStreamCall, variables: List<Variable>, dsl: Dsl): TerminatorStreamCall {
return call.withArgs(listOf(createNewPredicate(variables, dsl)))
}
private fun createNewPredicate(variables: List<Variable>, dsl: Dsl): CallArgument {
val valuesMap = variables[0] as MapVariable
val filteringMap = variables[1] as MapVariable
val oldPredicate = variables[2]
val newPredicate = dsl.lambda("value") {
+dsl.updateTime()
+valuesMap.set(dsl.currentTime(), lambdaArg)
val filterResult = dsl.variable(dsl.types.BOOLEAN, "result")
declare(filterResult, oldPredicate.call("invoke", lambdaArg), false)
+filteringMap.set(dsl.currentTime(), filterResult)
+dsl.updateTime() // reserve unique time for mapped value
doReturn(filterResult)
}.toCode()
return CallArgumentImpl(oldPredicate.type.genericTypeName, newPredicate)
}
}
@@ -0,0 +1,78 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.sequence
import com.intellij.debugger.streams.trace.dsl.CodeBlock
import com.intellij.debugger.streams.trace.dsl.Dsl
import com.intellij.debugger.streams.trace.dsl.Expression
import com.intellij.debugger.streams.trace.dsl.VariableDeclaration
import com.intellij.debugger.streams.trace.impl.handler.unified.HandlerBase
import com.intellij.debugger.streams.trace.impl.handler.unified.PeekTraceHandler
import com.intellij.debugger.streams.wrapper.CallArgument
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
import com.intellij.debugger.streams.wrapper.impl.CallArgumentImpl
import com.intellij.debugger.streams.wrapper.impl.IntermediateStreamCallImpl
import com.intellij.openapi.util.TextRange.EMPTY_RANGE
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes
class FilterIsInstanceHandler(num: Int, call: IntermediateStreamCall, dsl: Dsl) : HandlerBase.Intermediate(dsl) {
private companion object {
fun createHandler(num: Int, call: IntermediateStreamCall, dsl: Dsl): HandlerBase.Intermediate =
if (call.arguments.isEmpty()) MyWithGenericsHandler(num, call, dsl)
else PeekTraceHandler(num, call.name, call.typeBefore, call.typeAfter, dsl)
}
private val filterHandler = createHandler(num, call, dsl)
// use explicit delegation to avoid issues with navigation
override fun additionalCallsAfter(): MutableList<IntermediateStreamCall> = filterHandler.additionalCallsAfter()
override fun additionalCallsBefore(): List<IntermediateStreamCall> = filterHandler.additionalCallsBefore()
override fun additionalVariablesDeclaration(): List<VariableDeclaration> = filterHandler.additionalVariablesDeclaration()
override fun getResultExpression(): Expression = filterHandler.resultExpression
override fun prepareResult(): CodeBlock = filterHandler.prepareResult()
override fun transformCall(call: IntermediateStreamCall): IntermediateStreamCall = filterHandler.transformCall(call)
/*
* Transforms filterIsInstance<ClassName> -> filter { it is ClassName }.map { it as ClassName }
*/
private class MyWithGenericsHandler(num: Int, private val call: IntermediateStreamCall, dsl: Dsl) : HandlerBase.Intermediate(dsl) {
private val peekHandler = PeekTraceHandler(num, "filterIsInstance", call.typeBefore, call.typeAfter, dsl)
override fun additionalCallsAfter(): List<IntermediateStreamCall> {
val mapperType = functionalType(call.typeBefore.genericTypeName, call.typeAfter.genericTypeName)
val mapper = CallArgumentImpl(mapperType, "{ x -> x as ${call.typeAfter.genericTypeName} }")
val result: MutableList<IntermediateStreamCall> = mutableListOf(syntheticMapCall(mapper))
result.addAll(peekHandler.additionalCallsAfter())
return result
}
override fun additionalCallsBefore(): List<IntermediateStreamCall> = peekHandler.additionalCallsBefore()
override fun additionalVariablesDeclaration(): List<VariableDeclaration> =
peekHandler.additionalVariablesDeclaration()
override fun getResultExpression(): Expression = peekHandler.resultExpression
override fun prepareResult(): CodeBlock = peekHandler.prepareResult()
override fun transformCall(call: IntermediateStreamCall): IntermediateStreamCall {
val typeAfter = call.typeAfter.genericTypeName
val predicateType = functionalType(typeAfter, KotlinSequenceTypes.BOOLEAN.genericTypeName)
val predicate = CallArgumentImpl(predicateType, " { x -> x is $typeAfter} ")
return syntheticFilterCall(predicate)
}
private fun syntheticMapCall(mapper: CallArgument): IntermediateStreamCall =
IntermediateStreamCallImpl("map", listOf(mapper), call.typeBefore, call.typeAfter, EMPTY_RANGE)
private fun syntheticFilterCall(predicate: CallArgument): IntermediateStreamCall =
IntermediateStreamCallImpl("filter", listOf(predicate), call.typeBefore, call.typeBefore, EMPTY_RANGE)
private fun functionalType(argType: String, resultType: String): String {
return "($argType) -> $resultType"
}
}
}
@@ -0,0 +1,129 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.handler.sequence
import com.intellij.debugger.streams.trace.dsl.*
import com.intellij.debugger.streams.trace.dsl.impl.TextExpression
import com.intellij.debugger.streams.trace.impl.handler.type.ClassTypeImpl
import com.intellij.debugger.streams.trace.impl.handler.unified.HandlerBase
import com.intellij.debugger.streams.trace.impl.handler.unified.PeekTraceHandler
import com.intellij.debugger.streams.wrapper.CallArgument
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
import com.intellij.debugger.streams.wrapper.impl.CallArgumentImpl
import com.intellij.debugger.streams.wrapper.impl.IntermediateStreamCallImpl
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes
/**
* Based on com.intellij.debugger.streams.trace.impl.handler.unified.DistinctByKeyHandler
*/
class KotlinDistinctByHandler(callNumber: Int, private val call: IntermediateStreamCall, dsl: Dsl) : HandlerBase.Intermediate(dsl) {
private companion object {
const val KEY_EXTRACTOR_VARIABLE_PREFIX = "keyExtractor"
const val TRANSITIONS_ARRAY_NAME = "transitionsArray"
}
private val peekHandler = PeekTraceHandler(callNumber, "distinctBy", call.typeBefore, call.typeAfter, dsl)
private val keyExtractor: CallArgument
private val extractorVariable: Variable
private val beforeTimes = dsl.list(dsl.types.INT, call.name + callNumber + "BeforeTimes")
private val beforeValues = dsl.list(call.typeBefore, call.name + callNumber + "BeforeValues")
private val keys = dsl.list(KotlinSequenceTypes.NULLABLE_ANY, call.name + callNumber + "Keys")
private val time2ValueAfter = dsl.linkedMap(dsl.types.INT, call.typeAfter, call.name + callNumber + "after")
init {
val arguments = call.arguments
assert(arguments.isNotEmpty(), { "Key extractor is not specified" })
keyExtractor = arguments.first()
extractorVariable = dsl.variable(ClassTypeImpl(keyExtractor.type), KEY_EXTRACTOR_VARIABLE_PREFIX + callNumber)
}
override fun additionalVariablesDeclaration(): List<VariableDeclaration> {
val extractor = dsl.declaration(extractorVariable, TextExpression(keyExtractor.text), false)
val variables =
mutableListOf(
extractor, beforeTimes.defaultDeclaration(), beforeValues.defaultDeclaration(),
time2ValueAfter.defaultDeclaration(), keys.defaultDeclaration()
)
variables.addAll(peekHandler.additionalVariablesDeclaration())
return variables
}
override fun transformCall(call: IntermediateStreamCall): IntermediateStreamCall {
val newKeyExtractor = dsl.lambda("x") {
val key = dsl.variable(KotlinSequenceTypes.NULLABLE_ANY, "key")
declare(key, extractorVariable.call("invoke", lambdaArg), false)
statement { beforeTimes.add(dsl.currentTime()) }
statement { beforeValues.add(lambdaArg) }
statement { keys.add(key) }
doReturn(key)
}.toCode()
return call.updateArguments(listOf(CallArgumentImpl(keyExtractor.type, newKeyExtractor)))
}
override fun prepareResult(): CodeBlock {
val keys2TimesBefore = dsl.map(KotlinSequenceTypes.NULLABLE_ANY, dsl.types.list(dsl.types.INT), "keys2Times")
val transitions = dsl.map(dsl.types.INT, dsl.types.INT, "transitionsMap")
return dsl.block {
add(peekHandler.prepareResult())
declare(keys2TimesBefore.defaultDeclaration())
declare(transitions.defaultDeclaration())
integerIteration(keys.size(), this) {
val key = declare(variable(KotlinSequenceTypes.NULLABLE_ANY, "key"), keys.get(loopVariable), false)
val lst = list(dsl.types.INT, "lst")
declare(lst, keys2TimesBefore.computeIfAbsent(key, lambda("k") {
doReturn(newList(types.INT))
}), false)
statement { lst.add(beforeTimes.get(loopVariable)) }
}
forEachLoop(variable(types.INT, "afterTime"), time2ValueAfter.keys()) {
val afterTime = loopVariable
val valueAfter = declare(variable(call.typeAfter, "valueAfter"), time2ValueAfter.get(loopVariable), false)
val key = declare(variable(KotlinSequenceTypes.NULLABLE_ANY, "key"), nullExpression, true)
integerIteration(beforeTimes.size(), this) {
ifBranch((valueAfter same beforeValues.get(loopVariable)) and !transitions.contains(beforeTimes.get(loopVariable))) {
key assign keys.get(loopVariable)
statement { breakIteration() }
}
}
forEachLoop(variable(types.INT, "beforeTime"), keys2TimesBefore.get(key)) {
statement { transitions.set(loopVariable, afterTime) }
}
}
add(transitions.convertToArray(this, "transitionsArray"))
}
}
override fun getResultExpression(): Expression =
dsl.newArray(dsl.types.ANY, peekHandler.resultExpression, TextExpression(TRANSITIONS_ARRAY_NAME))
override fun additionalCallsBefore(): List<IntermediateStreamCall> = peekHandler.additionalCallsBefore()
override fun additionalCallsAfter(): List<IntermediateStreamCall> {
val callsAfter = ArrayList(peekHandler.additionalCallsAfter())
val lambda = dsl.lambda("x") {
doReturn(time2ValueAfter.set(dsl.currentTime(), lambdaArg))
}
callsAfter.add(dsl.createPeekCall(call.typeAfter, lambda.toCode()))
return callsAfter
}
private fun CodeContext.integerIteration(border: Expression, block: CodeBlock, init: ForLoopBody.() -> Unit) {
block.forLoop(
declaration(variable(types.INT, "i"), TextExpression("0"), true),
TextExpression("i < ${border.toCode()}"),
TextExpression("i = i + 1"), init
)
}
private fun IntermediateStreamCall.updateArguments(args: List<CallArgument>): IntermediateStreamCall =
IntermediateStreamCallImpl("distinctBy", args, typeBefore, typeAfter, textRange)
}
@@ -0,0 +1,68 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.interpret
import com.intellij.debugger.streams.trace.CallTraceInterpreter
import com.intellij.debugger.streams.trace.TraceElement
import com.intellij.debugger.streams.trace.TraceInfo
import com.intellij.debugger.streams.trace.impl.TraceElementImpl
import com.intellij.debugger.streams.trace.impl.interpret.ex.UnexpectedValueTypeException
import com.intellij.debugger.streams.wrapper.StreamCall
import com.sun.jdi.ArrayReference
import com.sun.jdi.BooleanValue
import com.sun.jdi.IntegerValue
import com.sun.jdi.Value
class FilterTraceInterpreter(private val predicateValueToAccept: Boolean) : CallTraceInterpreter {
override fun resolve(call: StreamCall, value: Value): TraceInfo {
if (value !is ArrayReference) throw UnexpectedValueTypeException("array reference excepted, but actual: ${value.type().name()}")
val before = resolveValuesBefore(value.getValue(0))
val filteringMap = value.getValue(1)
val after = resolveValuesAfter(before, filteringMap)
return ValuesOrder(call, before, after)
}
private fun resolveValuesBefore(map: Value): Map<Int, TraceElement> {
val (keys, objects) = InterpreterUtil.extractMap(map)
val result: MutableList<TraceElement> = mutableListOf()
for (i in 0.until(keys.length())) {
val time = keys.getValue(i)
val value = objects.getValue(i)
if (time !is IntegerValue) throw UnexpectedValueTypeException("time should be represented by integer value")
result.add(TraceElementImpl(time.value(), value))
}
return InterpreterUtil.createIndexByTime(result)
}
private fun resolveValuesAfter(before: Map<Int, TraceElement>, filteringMap: Value): Map<Int, TraceElement> {
val predicateValues = extractPredicateValues(filteringMap)
val result = linkedMapOf<Int, TraceElement>()
for ((beforeTime, element) in before) {
val predicateValue = predicateValues[beforeTime]
if (predicateValue == predicateValueToAccept) {
result[beforeTime + 1] = TraceElementImpl(beforeTime + 1, element.value)
}
}
return result
}
private fun extractPredicateValues(filteringMap: Value): Map<Int, Boolean> {
val (keys, values) = InterpreterUtil.extractMap(filteringMap)
val result = mutableMapOf<Int, Boolean>()
for (i in 0.until(keys.length())) {
val time = keys.getValue(i)
val value = values.getValue(i)
if (time !is IntegerValue) throw UnexpectedValueTypeException("time should be represented by integer value")
if (value !is BooleanValue) throw UnexpectedValueTypeException("predicate value should be represented by boolean value")
result[time.value()] = value.value()
}
return result
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.interpret
import com.intellij.debugger.streams.trace.TraceElement
import com.intellij.debugger.streams.trace.impl.interpret.ex.UnexpectedValueException
import com.sun.jdi.ArrayReference
import com.sun.jdi.Value
object InterpreterUtil {
fun extractMap(value: Value): MapRepresentation {
if (value !is ArrayReference || value.length() != 2) {
throw UnexpectedValueException("Map should be represented by array with two nested arrays: keys and values")
}
val keys = value.getValue(0)
val values = value.getValue(1)
if (keys !is ArrayReference || values !is ArrayReference || keys.length() != values.length()) {
throw UnexpectedValueException("Keys and values should be arrays with equal counts of elements")
}
return MapRepresentation(keys, values)
}
fun createIndexByTime(elements: List<TraceElement>): Map<Int, TraceElement> =
elements.associate { elem -> elem.time to elem }
data class MapRepresentation(val keys: ArrayReference, val values: ArrayReference)
}
@@ -0,0 +1,26 @@
/*
* Copyright 2000-2017 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.interpret
import com.intellij.debugger.streams.trace.TraceElement
import com.intellij.debugger.streams.trace.TraceInfo
import com.intellij.debugger.streams.wrapper.StreamCall
class ValuesOrder(
private val call: StreamCall,
private val before: Map<Int, TraceElement>,
private val after: Map<Int, TraceElement>
) : TraceInfo {
override fun getValuesOrderBefore(): Map<Int, TraceElement> = before
override fun getCall(): StreamCall = call
override fun getValuesOrderAfter(): Map<Int, TraceElement> = after
override fun getDirectTrace(): MutableMap<TraceElement, MutableList<TraceElement>>? = null
override fun getReverseTrace(): MutableMap<TraceElement, MutableList<TraceElement>>? = null
}