From c6a24c03e462a43d0ccf86595f7052b1e75e8767 Mon Sep 17 00:00:00 2001 From: Ilya Chernikov Date: Wed, 6 Feb 2019 16:05:15 +0100 Subject: [PATCH] Move CliScriptDefinition provider to the plugin too, fix tests --- .../builder/AbstractRawFirBuilderTestCase.kt | 7 -- .../script/KotlinScriptDefinitionProvider.kt | 77 ---------------- .../kotlin/parsing/AbstractParsingTest.java | 10 +-- .../kotlin/parsing/ParsingTestGenerated.java | 9 ++ .../kotlin/scripts/ScriptProviderTest.kt | 2 +- .../core/script/ScriptDefinitionsManager.kt | 6 +- ...ScriptingCompilerConfigurationExtension.kt | 10 ++- .../legacy}/CliScriptDefinitionProvider.kt | 18 +--- .../legacy}/CliScriptDependenciesProvider.kt | 2 +- .../scripting/legacy}/CliScriptReportSink.kt | 19 +--- .../legacy/LazyScriptDefinitionProvider.kt | 88 +++++++++++++++++++ 11 files changed, 118 insertions(+), 130 deletions(-) rename {compiler/cli/src/org/jetbrains/kotlin/cli/common/script => plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy}/CliScriptDefinitionProvider.kt (64%) rename {compiler/cli/src/org/jetbrains/kotlin/cli/common/script => plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy}/CliScriptDependenciesProvider.kt (98%) rename {compiler/cli/src/org/jetbrains/kotlin/cli/common/script => plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy}/CliScriptReportSink.kt (65%) create mode 100644 plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy/LazyScriptDefinitionProvider.kt diff --git a/compiler/fir/psi2fir/tests/org/jetbrains/kotlin/fir/builder/AbstractRawFirBuilderTestCase.kt b/compiler/fir/psi2fir/tests/org/jetbrains/kotlin/fir/builder/AbstractRawFirBuilderTestCase.kt index a946c2e01db..07ca74a42f2 100644 --- a/compiler/fir/psi2fir/tests/org/jetbrains/kotlin/fir/builder/AbstractRawFirBuilderTestCase.kt +++ b/compiler/fir/psi2fir/tests/org/jetbrains/kotlin/fir/builder/AbstractRawFirBuilderTestCase.kt @@ -14,7 +14,6 @@ import com.intellij.psi.PsiFile import com.intellij.psi.tree.IElementType import com.intellij.util.PathUtil import org.jetbrains.kotlin.KtNodeTypes -import org.jetbrains.kotlin.cli.common.script.CliScriptDefinitionProvider import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirRenderer import org.jetbrains.kotlin.fir.FirSessionBase @@ -27,7 +26,6 @@ import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid import org.jetbrains.kotlin.parsing.KotlinParserDefinition import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.script.ScriptDefinitionProvider import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.testFramework.KtParsingTestCase import java.io.File @@ -39,11 +37,6 @@ abstract class AbstractRawFirBuilderTestCase : KtParsingTestCase( "kt", KotlinParserDefinition() ) { - override fun setUp() { - super.setUp() - project.registerService(ScriptDefinitionProvider::class.java, CliScriptDefinitionProvider::class.java) - } - override fun getTestDataPath() = KotlinTestUtils.getHomeDirectory() private fun createFile(filePath: String, fileType: IElementType): PsiFile { diff --git a/compiler/psi/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.kt b/compiler/psi/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.kt index 0a84c71deab..cd3ce4c0450 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.kt @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.script import com.intellij.ide.highlighter.JavaClassFileType -import com.intellij.ide.highlighter.JavaFileType import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.fileTypes.FileTypeRegistry import com.intellij.openapi.project.Project @@ -25,9 +24,6 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiManager import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.psi.KtFile -import java.util.concurrent.locks.ReentrantReadWriteLock -import kotlin.concurrent.read -import kotlin.concurrent.write interface ScriptDefinitionProvider { fun findScriptDefinition(fileName: String): KotlinScriptDefinition? @@ -73,76 +69,3 @@ private fun isKotlinFileType(file: VirtualFile): Boolean { typeRegistry.getFileTypeByFileName(file.name) == KotlinFileType.INSTANCE } -abstract class LazyScriptDefinitionProvider : ScriptDefinitionProvider { - - protected val lock = ReentrantReadWriteLock() - - protected abstract val currentDefinitions: Sequence - - private var _cachedDefinitions: Sequence? = null - private val cachedDefinitions: Sequence - get() { - assert(lock.readLockCount > 0) { "cachedDefinitions should only be used under the read lock" } - if (_cachedDefinitions == null) lock.write { - _cachedDefinitions = CachingSequence(currentDefinitions.constrainOnce()) - } - return _cachedDefinitions!! - } - - protected fun clearCache() { - lock.write { - _cachedDefinitions = null - } - } - - protected open fun nonScriptFileName(fileName: String) = nonScriptFilenameSuffixes.any { - fileName.endsWith(it, ignoreCase = true) - } - - override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? = - if (nonScriptFileName(fileName)) null - else lock.read { - cachedDefinitions.firstOrNull { it.isScript(fileName) } - } - - override fun isScript(fileName: String) = findScriptDefinition(fileName) != null - - override fun getKnownFilenameExtensions(): Sequence = lock.read { - cachedDefinitions.map { it.fileExtension } - } - - companion object { - // TODO: find a common place for storing kotlin-related extensions and reuse values from it everywhere - protected val nonScriptFilenameSuffixes = arrayOf(".${KotlinFileType.EXTENSION}", ".${JavaFileType.DEFAULT_EXTENSION}") - } -} - -private class CachingSequence(from: Sequence) : Sequence { - - private val lock = ReentrantReadWriteLock() - private val sequenceIterator = from.iterator() - private val cache = arrayListOf() - - private inner class CachingIterator : Iterator { - - private var cacheCursor = 0 - - override fun hasNext(): Boolean = - lock.read { cacheCursor < cache.size } - // iterator's hasNext can mutate the iterator's state, therefore write lock is needed - || lock.write { cacheCursor < cache.size || sequenceIterator.hasNext() } - - override fun next(): T { - lock.read { - if (cacheCursor < cache.size) return cache[cacheCursor++] - } - // lock.write is not an upgrade but retake, therefore - one more check needed - lock.write { - return if (cacheCursor < cache.size) cache[cacheCursor++] - else sequenceIterator.next().also { cache.add(it) } - } - } - } - - override fun iterator(): Iterator = CachingIterator() -} diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/AbstractParsingTest.java b/compiler/tests/org/jetbrains/kotlin/parsing/AbstractParsingTest.java index 8bccb905328..7a3548a21eb 100644 --- a/compiler/tests/org/jetbrains/kotlin/parsing/AbstractParsingTest.java +++ b/compiler/tests/org/jetbrains/kotlin/parsing/AbstractParsingTest.java @@ -26,12 +26,11 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.PathUtil; +import kotlin.jvm.functions.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.KtNodeTypes; import org.jetbrains.kotlin.TestsCompilerError; -import org.jetbrains.kotlin.cli.common.script.CliScriptDefinitionProvider; import org.jetbrains.kotlin.psi.*; -import org.jetbrains.kotlin.script.ScriptDefinitionProvider; import org.jetbrains.kotlin.test.KotlinTestUtils; import org.jetbrains.kotlin.test.testFramework.KtParsingTestCase; @@ -39,14 +38,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import kotlin.jvm.functions.Function1; - public abstract class AbstractParsingTest extends KtParsingTestCase { - @Override - protected void setUp() throws Exception { - super.setUp(); - getProject().registerService(ScriptDefinitionProvider.class, CliScriptDefinitionProvider.class); - } @Override protected String getTestDataPath() { diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java index 29b3a2fa71f..3d33bad1734 100644 --- a/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/parsing/ParsingTestGenerated.java @@ -6,6 +6,8 @@ package org.jetbrains.kotlin.parsing; import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.script.ScriptDefinitionProvider; +import org.jetbrains.kotlin.scripting.legacy.CliScriptDefinitionProvider; import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; import org.jetbrains.kotlin.test.KotlinTestUtils; import org.jetbrains.kotlin.test.TargetBackend; @@ -2473,6 +2475,13 @@ public class ParsingTestGenerated extends AbstractParsingTest { @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class Script extends AbstractParsingTest { + + @Override + protected void setUp() throws Exception { + super.setUp(); + getProject().registerService(ScriptDefinitionProvider.class, CliScriptDefinitionProvider.class); + } + private void runTest(String testDataFilePath) throws Exception { KotlinTestUtils.runTest(this::doParsingTest, TargetBackend.ANY, testDataFilePath); } diff --git a/compiler/tests/org/jetbrains/kotlin/scripts/ScriptProviderTest.kt b/compiler/tests/org/jetbrains/kotlin/scripts/ScriptProviderTest.kt index 6e2bd430745..efafcf026f2 100644 --- a/compiler/tests/org/jetbrains/kotlin/scripts/ScriptProviderTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/scripts/ScriptProviderTest.kt @@ -5,9 +5,9 @@ package org.jetbrains.kotlin.scripts -import org.jetbrains.kotlin.cli.common.script.CliScriptDefinitionProvider import org.jetbrains.kotlin.script.KotlinScriptDefinition import org.jetbrains.kotlin.script.ScriptDefinitionsSource +import org.jetbrains.kotlin.scripting.legacy.CliScriptDefinitionProvider import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase import org.junit.Assert import org.junit.Test diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt index 171cfff183c..cdd6e0ec0fa 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt @@ -38,8 +38,12 @@ import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.caches.project.SdkInfo import org.jetbrains.kotlin.idea.caches.project.getScriptRelatedModuleInfo import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings -import org.jetbrains.kotlin.script.* +import org.jetbrains.kotlin.script.KotlinScriptDefinition +import org.jetbrains.kotlin.script.KotlinScriptDefinitionFromAnnotatedTemplate +import org.jetbrains.kotlin.script.ScriptDefinitionProvider +import org.jetbrains.kotlin.script.ScriptTemplatesProvider import org.jetbrains.kotlin.scripting.compiler.plugin.KotlinScriptDefinitionAdapterFromNewAPI +import org.jetbrains.kotlin.scripting.legacy.LazyScriptDefinitionProvider import org.jetbrains.kotlin.utils.PathUtil import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.flattenTo diff --git a/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerConfigurationExtension.kt b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerConfigurationExtension.kt index 5fdb1c78f6c..15f784e2529 100644 --- a/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerConfigurationExtension.kt +++ b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerConfigurationExtension.kt @@ -10,9 +10,8 @@ import com.intellij.mock.MockProject import com.intellij.openapi.fileTypes.FileTypeRegistry import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.cli.common.script.CliScriptDefinitionProvider -import org.jetbrains.kotlin.cli.common.script.CliScriptDependenciesProvider -import org.jetbrains.kotlin.cli.common.script.CliScriptReportSink +import org.jetbrains.kotlin.scripting.legacy.CliScriptDependenciesProvider +import org.jetbrains.kotlin.scripting.legacy.CliScriptReportSink import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.config.CompilerConfiguration @@ -23,6 +22,7 @@ import org.jetbrains.kotlin.script.ScriptDefinitionProvider import org.jetbrains.kotlin.script.ScriptDependenciesProvider import org.jetbrains.kotlin.script.ScriptReportSink import org.jetbrains.kotlin.script.StandardScriptDefinition +import org.jetbrains.kotlin.scripting.legacy.CliScriptDefinitionProvider import java.io.File class ScriptingCompilerConfigurationExtension(val project: MockProject) : CompilerConfigurationExtension { @@ -108,7 +108,9 @@ class ScriptingCompilerConfigurationComponentRegistrar : ComponentRegistrar { val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) if (messageCollector != null) { - project.registerService(ScriptReportSink::class.java, CliScriptReportSink(messageCollector)) + project.registerService(ScriptReportSink::class.java, + CliScriptReportSink(messageCollector) + ) } } } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/script/CliScriptDefinitionProvider.kt b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy/CliScriptDefinitionProvider.kt similarity index 64% rename from compiler/cli/src/org/jetbrains/kotlin/cli/common/script/CliScriptDefinitionProvider.kt rename to plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy/CliScriptDefinitionProvider.kt index fe9a5d25000..78f5ed1a74f 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/script/CliScriptDefinitionProvider.kt +++ b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy/CliScriptDefinitionProvider.kt @@ -1,23 +1,11 @@ /* - * Copyright 2010-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2010-2019 JetBrains s.r.o. 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.cli.common.script +package org.jetbrains.kotlin.scripting.legacy import org.jetbrains.kotlin.script.KotlinScriptDefinition -import org.jetbrains.kotlin.script.LazyScriptDefinitionProvider import org.jetbrains.kotlin.script.ScriptDefinitionsSource import org.jetbrains.kotlin.script.StandardScriptDefinition import kotlin.concurrent.write diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/script/CliScriptDependenciesProvider.kt b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy/CliScriptDependenciesProvider.kt similarity index 98% rename from compiler/cli/src/org/jetbrains/kotlin/cli/common/script/CliScriptDependenciesProvider.kt rename to plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy/CliScriptDependenciesProvider.kt index f83c77270fd..723811d8e51 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/script/CliScriptDependenciesProvider.kt +++ b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy/CliScriptDependenciesProvider.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.jetbrains.kotlin.cli.common.script +package org.jetbrains.kotlin.scripting.legacy import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.diagnostic.Logger diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/script/CliScriptReportSink.kt b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy/CliScriptReportSink.kt similarity index 65% rename from compiler/cli/src/org/jetbrains/kotlin/cli/common/script/CliScriptReportSink.kt rename to plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy/CliScriptReportSink.kt index 17e95bdf67c..4fb6b470cf2 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/script/CliScriptReportSink.kt +++ b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy/CliScriptReportSink.kt @@ -1,20 +1,9 @@ /* - * Copyright 2010-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2010-2019 JetBrains s.r.o. 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.cli.common.script +package org.jetbrains.kotlin.scripting.legacy import com.intellij.openapi.vfs.VirtualFile import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation @@ -23,7 +12,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.script.ScriptReportSink import kotlin.script.experimental.dependencies.ScriptReport -class CliScriptReportSink(private val messageCollector: MessageCollector) : ScriptReportSink { +internal class CliScriptReportSink(private val messageCollector: MessageCollector) : ScriptReportSink { override fun attachReports(scriptFile: VirtualFile, reports: List) { reports.forEach { messageCollector.report(it.severity.convertSeverity(), it.message, location(scriptFile, it.position)) diff --git a/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy/LazyScriptDefinitionProvider.kt b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy/LazyScriptDefinitionProvider.kt new file mode 100644 index 00000000000..8487c792ee4 --- /dev/null +++ b/plugins/scripting/scripting-cli/src/org/jetbrains/kotlin/scripting/legacy/LazyScriptDefinitionProvider.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. 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.scripting.legacy + +import com.intellij.ide.highlighter.JavaFileType +import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.script.KotlinScriptDefinition +import org.jetbrains.kotlin.script.ScriptDefinitionProvider +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.read +import kotlin.concurrent.write + +abstract class LazyScriptDefinitionProvider : ScriptDefinitionProvider { + + protected val lock = ReentrantReadWriteLock() + + protected abstract val currentDefinitions: Sequence + + private var _cachedDefinitions: Sequence? = null + private val cachedDefinitions: Sequence + get() { + assert(lock.readLockCount > 0) { "cachedDefinitions should only be used under the read lock" } + if (_cachedDefinitions == null) lock.write { + _cachedDefinitions = CachingSequence(currentDefinitions.constrainOnce()) + } + return _cachedDefinitions!! + } + + protected fun clearCache() { + lock.write { + _cachedDefinitions = null + } + } + + protected open fun nonScriptFileName(fileName: String) = nonScriptFilenameSuffixes.any { + fileName.endsWith(it, ignoreCase = true) + } + + override fun findScriptDefinition(fileName: String): KotlinScriptDefinition? = + if (nonScriptFileName(fileName)) null + else lock.read { + cachedDefinitions.firstOrNull { it.isScript(fileName) } + } + + override fun isScript(fileName: String) = findScriptDefinition(fileName) != null + + override fun getKnownFilenameExtensions(): Sequence = lock.read { + cachedDefinitions.map { it.fileExtension } + } + + companion object { + // TODO: find a common place for storing kotlin-related extensions and reuse values from it everywhere + protected val nonScriptFilenameSuffixes = arrayOf(".${KotlinFileType.EXTENSION}", ".${JavaFileType.DEFAULT_EXTENSION}") + } +} + +private class CachingSequence(from: Sequence) : Sequence { + + private val lock = ReentrantReadWriteLock() + private val sequenceIterator = from.iterator() + private val cache = arrayListOf() + + private inner class CachingIterator : Iterator { + + private var cacheCursor = 0 + + override fun hasNext(): Boolean = + lock.read { cacheCursor < cache.size } + // iterator's hasNext can mutate the iterator's state, therefore write lock is needed + || lock.write { cacheCursor < cache.size || sequenceIterator.hasNext() } + + override fun next(): T { + lock.read { + if (cacheCursor < cache.size) return cache[cacheCursor++] + } + // lock.write is not an upgrade but retake, therefore - one more check needed + lock.write { + return if (cacheCursor < cache.size) cache[cacheCursor++] + else sequenceIterator.next().also { cache.add(it) } + } + } + } + + override fun iterator(): Iterator = CachingIterator() +}