Support distinctBy intermediate call
This commit is contained in:
committed by
Yan Zhulanow
parent
0cc5e924a7
commit
89abcb82e4
+2
-1
@@ -5,6 +5,7 @@ import com.intellij.debugger.streams.kotlin.resolve.ChunkedResolver
|
|||||||
import com.intellij.debugger.streams.kotlin.resolve.FilteredMapResolver
|
import com.intellij.debugger.streams.kotlin.resolve.FilteredMapResolver
|
||||||
import com.intellij.debugger.streams.kotlin.resolve.WindowedResolver
|
import com.intellij.debugger.streams.kotlin.resolve.WindowedResolver
|
||||||
import com.intellij.debugger.streams.kotlin.trace.impl.handler.sequence.FilterIsInstanceHandler
|
import com.intellij.debugger.streams.kotlin.trace.impl.handler.sequence.FilterIsInstanceHandler
|
||||||
|
import com.intellij.debugger.streams.kotlin.trace.impl.handler.sequence.KotlinDistinctByHandler
|
||||||
import com.intellij.debugger.streams.lib.IntermediateOperation
|
import com.intellij.debugger.streams.lib.IntermediateOperation
|
||||||
import com.intellij.debugger.streams.lib.impl.*
|
import com.intellij.debugger.streams.lib.impl.*
|
||||||
import com.intellij.debugger.streams.resolve.AppendResolver
|
import com.intellij.debugger.streams.resolve.AppendResolver
|
||||||
@@ -30,7 +31,7 @@ class KotlinSequencesSupport : LibrarySupportBase() {
|
|||||||
addIntermediateOperationsSupport(*sortedOperations("sorted", "sortedBy", "sortedDescending", "sortedWith"))
|
addIntermediateOperationsSupport(*sortedOperations("sorted", "sortedBy", "sortedDescending", "sortedWith"))
|
||||||
|
|
||||||
addIntermediateOperationsSupport(DistinctOperation("distinct", ::DistinctTraceHandler))
|
addIntermediateOperationsSupport(DistinctOperation("distinct", ::DistinctTraceHandler))
|
||||||
// addIntermediateOperationsSupport(DistinctOperation("distinctBy", ::DistinctByKeyHandler)) waiting for a fix in the platform
|
addIntermediateOperationsSupport(DistinctOperation("distinctBy", ::KotlinDistinctByHandler))
|
||||||
|
|
||||||
addIntermediateOperationsSupport(ConcatOperation("plus", AppendResolver()))
|
addIntermediateOperationsSupport(ConcatOperation("plus", AppendResolver()))
|
||||||
addIntermediateOperationsSupport(ConcatOperation("plusElement", AppendResolver()))
|
addIntermediateOperationsSupport(ConcatOperation("plusElement", AppendResolver()))
|
||||||
|
|||||||
+125
@@ -0,0 +1,125 @@
|
|||||||
|
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||||
|
package com.intellij.debugger.streams.kotlin.trace.impl.handler.sequence
|
||||||
|
|
||||||
|
import com.intellij.debugger.streams.kotlin.trace.dsl.KotlinTypes
|
||||||
|
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 one.util.streamex.StreamEx
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Based on com.intellij.debugger.streams.trace.impl.handler.unified.DistinctByKeyHandler
|
||||||
|
* @author Vitaliy.Bibaev
|
||||||
|
*/
|
||||||
|
class KotlinDistinctByHandler(callNumber: Int, private val call: IntermediateStreamCall, dsl: Dsl)
|
||||||
|
: HandlerBase.Intermediate(dsl) {
|
||||||
|
private companion object {
|
||||||
|
val KEY_EXTRACTOR_VARIABLE_PREFIX = "keyExtractor"
|
||||||
|
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(KotlinTypes.NULLABLE_ANY, call.name + callNumber + "BeforeValues")
|
||||||
|
private val keys = dsl.list(KotlinTypes.NULLABLE_ANY, call.name + callNumber + "Keys")
|
||||||
|
private val time2ValueAfter = dsl.linkedMap(dsl.types.INT, KotlinTypes.NULLABLE_ANY, 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(KotlinTypes.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(KotlinTypes.NULLABLE_ANY, dsl.types.list(dsl.types.INT), "keys2Times")
|
||||||
|
val transitions = dsl.map(dsl.types.INT, dsl.types.INT, "transitionsMap")
|
||||||
|
StreamEx.of(1).distinct().toList()
|
||||||
|
return dsl.block {
|
||||||
|
add(peekHandler.prepareResult())
|
||||||
|
declare(keys2TimesBefore.defaultDeclaration())
|
||||||
|
declare(transitions.defaultDeclaration())
|
||||||
|
|
||||||
|
integerIteration(keys.size(), block@ this) {
|
||||||
|
val key = declare(variable(KotlinTypes.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(KotlinTypes.NULLABLE_ANY, "valueAfter"), time2ValueAfter.get(loopVariable), false)
|
||||||
|
val key = declare(variable(KotlinTypes.NULLABLE_ANY, "key"), nullExpression, true)
|
||||||
|
integerIteration(beforeTimes.size(), forEachLoop@ 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)
|
||||||
|
}
|
||||||
@@ -13,22 +13,22 @@ toList
|
|||||||
after: nothing
|
after: nothing
|
||||||
mappings for distinctBy
|
mappings for distinctBy
|
||||||
direct:
|
direct:
|
||||||
1 -> nothing
|
1 -> 2
|
||||||
3 -> nothing
|
3 -> 4
|
||||||
5 -> nothing
|
5 -> 4
|
||||||
6 -> nothing
|
6 -> 4
|
||||||
7 -> nothing
|
7 -> 4
|
||||||
8 -> nothing
|
8 -> 2
|
||||||
9 -> nothing
|
9 -> 4
|
||||||
10 -> nothing
|
10 -> 2
|
||||||
11 -> nothing
|
11 -> 2
|
||||||
12 -> nothing
|
12 -> 13
|
||||||
14 -> nothing
|
14 -> 15
|
||||||
reverse:
|
reverse:
|
||||||
nothing <- 2
|
1,8,10,11 <- 2
|
||||||
nothing <- 4
|
3,5,6,7,9 <- 4
|
||||||
nothing <- 13
|
12 <- 13
|
||||||
nothing <- 15
|
14 <- 15
|
||||||
mappings for toList
|
mappings for toList
|
||||||
direct:
|
direct:
|
||||||
2 -> nothing
|
2 -> nothing
|
||||||
@@ -37,7 +37,6 @@ mappings for toList
|
|||||||
15 -> nothing
|
15 -> nothing
|
||||||
reverse:
|
reverse:
|
||||||
empty
|
empty
|
||||||
WRONG!
|
|
||||||
Disconnected from the target VM
|
Disconnected from the target VM
|
||||||
|
|
||||||
Process finished with exit code 0
|
Process finished with exit code 0
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
LineBreakpoint created at DistinctByNullableElement.kt:5
|
||||||
|
Run Java
|
||||||
|
Connected to the target VM
|
||||||
|
DistinctByNullableElement.kt:5
|
||||||
|
sequenceOf(null, 1, 2, 3, null)
|
||||||
|
.distinctBy({ it == null })
|
||||||
|
.count()
|
||||||
|
distinctBy
|
||||||
|
before: 1,3,5,6,7
|
||||||
|
after: 2,4
|
||||||
|
count
|
||||||
|
before: 2,4
|
||||||
|
after: nothing
|
||||||
|
mappings for distinctBy
|
||||||
|
direct:
|
||||||
|
1 -> 2
|
||||||
|
3 -> 4
|
||||||
|
5 -> 4
|
||||||
|
6 -> 4
|
||||||
|
7 -> 2
|
||||||
|
reverse:
|
||||||
|
1,7 <- 2
|
||||||
|
3,5,6 <- 4
|
||||||
|
mappings for count
|
||||||
|
direct:
|
||||||
|
2 -> nothing
|
||||||
|
4 -> nothing
|
||||||
|
reverse:
|
||||||
|
empty
|
||||||
|
Disconnected from the target VM
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
LineBreakpoint created at DistinctByNullableKey.kt:5
|
||||||
|
Run Java
|
||||||
|
Connected to the target VM
|
||||||
|
DistinctByNullableKey.kt:5
|
||||||
|
sequenceOf(1, 2, 3, 4, 5)
|
||||||
|
.distinctBy({ if (it % 2 == 0) null else it })
|
||||||
|
.count()
|
||||||
|
distinctBy
|
||||||
|
before: 1,3,5,7,8
|
||||||
|
after: 2,4,6,9
|
||||||
|
count
|
||||||
|
before: 2,4,6,9
|
||||||
|
after: nothing
|
||||||
|
mappings for distinctBy
|
||||||
|
direct:
|
||||||
|
1 -> 2
|
||||||
|
3 -> 4
|
||||||
|
5 -> 6
|
||||||
|
7 -> 4
|
||||||
|
8 -> 9
|
||||||
|
reverse:
|
||||||
|
1 <- 2
|
||||||
|
3,7 <- 4
|
||||||
|
5 <- 6
|
||||||
|
8 <- 9
|
||||||
|
mappings for count
|
||||||
|
direct:
|
||||||
|
2 -> nothing
|
||||||
|
4 -> nothing
|
||||||
|
6 -> nothing
|
||||||
|
9 -> nothing
|
||||||
|
reverse:
|
||||||
|
empty
|
||||||
|
Disconnected from the target VM
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
LineBreakpoint created at DistinctByNullableKeyAndElement.kt:5
|
||||||
|
Run Java
|
||||||
|
Connected to the target VM
|
||||||
|
DistinctByNullableKeyAndElement.kt:5
|
||||||
|
sequenceOf(1, 2, null, null, 3, 1)
|
||||||
|
.distinctBy({ if (it == null) 2 else if (it == 3) null else it })
|
||||||
|
.count()
|
||||||
|
distinctBy
|
||||||
|
before: 1,3,5,6,7,9
|
||||||
|
after: 2,4,8
|
||||||
|
count
|
||||||
|
before: 2,4,8
|
||||||
|
after: nothing
|
||||||
|
mappings for distinctBy
|
||||||
|
direct:
|
||||||
|
1 -> 2
|
||||||
|
3 -> 4
|
||||||
|
5 -> 4
|
||||||
|
6 -> 4
|
||||||
|
7 -> 8
|
||||||
|
9 -> 2
|
||||||
|
reverse:
|
||||||
|
1,9 <- 2
|
||||||
|
3,5,6 <- 4
|
||||||
|
7 <- 8
|
||||||
|
mappings for count
|
||||||
|
direct:
|
||||||
|
2 -> nothing
|
||||||
|
4 -> nothing
|
||||||
|
8 -> nothing
|
||||||
|
reverse:
|
||||||
|
empty
|
||||||
|
Disconnected from the target VM
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
package distinct
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
// Breakpoint!
|
||||||
|
sequenceOf(null, 1, 2, 3, null).distinctBy { it == null }.count()
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
package distinct
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
// Breakpoint!
|
||||||
|
sequenceOf(1, 2, 3, 4, 5).distinctBy { if (it % 2 == 0) null else it }.count()
|
||||||
|
}
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
package distinct
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
// Breakpoint!
|
||||||
|
sequenceOf(1, 2, null, null, 3, 1).distinctBy { if (it == null) 2 else if (it == 3) null else it }.count()
|
||||||
|
}
|
||||||
+4
@@ -7,5 +7,9 @@ package com.intellij.debugger.streams.kotlin.exec.sequence
|
|||||||
class DistinctOperationsTest : OperationsTestCase("distinct") {
|
class DistinctOperationsTest : OperationsTestCase("distinct") {
|
||||||
fun testDistinct() = doTestWithResult()
|
fun testDistinct() = doTestWithResult()
|
||||||
fun testDistinctObjects() = doTestWithResult()
|
fun testDistinctObjects() = doTestWithResult()
|
||||||
|
|
||||||
fun testDistinctBy() = doTestWithResult()
|
fun testDistinctBy() = doTestWithResult()
|
||||||
|
fun testDistinctByNullableElement() = doTestWithResult()
|
||||||
|
fun testDistinctByNullableKey() = doTestWithResult()
|
||||||
|
fun testDistinctByNullableKeyAndElement() = doTestWithResult()
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user