Move CliScriptDefinition provider to the plugin too, fix tests

This commit is contained in:
Ilya Chernikov
2019-02-06 16:05:15 +01:00
parent 731df8d3cc
commit c6a24c03e4
11 changed files with 118 additions and 130 deletions
@@ -1,51 +0,0 @@
/*
* 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.
*/
package org.jetbrains.kotlin.cli.common.script
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
class CliScriptDefinitionProvider : LazyScriptDefinitionProvider() {
private val definitionsFromSources: MutableList<Sequence<KotlinScriptDefinition>> = arrayListOf()
private val definitions: MutableList<KotlinScriptDefinition> = arrayListOf(StandardScriptDefinition)
override val currentDefinitions: Sequence<KotlinScriptDefinition> =
definitionsFromSources.asSequence().flatMap { it } + definitions.asSequence()
override fun getDefaultScriptDefinition(): KotlinScriptDefinition {
return StandardScriptDefinition
}
fun setScriptDefinitions(newDefinitions: List<KotlinScriptDefinition>) {
lock.write {
definitions.clear()
definitions.addAll(newDefinitions)
}
}
fun setScriptDefinitionsSources(newSources: List<ScriptDefinitionsSource>) {
lock.write {
definitionsFromSources.clear()
for (it in newSources) {
definitionsFromSources.add(it.definitions.constrainOnce())
}
}
}
}
@@ -1,64 +0,0 @@
/*
* 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.
*/
package org.jetbrains.kotlin.cli.common.script
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.script.*
import java.io.File
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
import kotlin.script.experimental.dependencies.ScriptDependencies
class CliScriptDependenciesProvider(private val project: Project) : ScriptDependenciesProvider {
private val cacheLock = ReentrantReadWriteLock()
private val cache = hashMapOf<String, ScriptDependencies?>()
private val scriptContentLoader = ScriptContentLoader(project)
override fun getScriptDependencies(file: VirtualFile): ScriptDependencies? = cacheLock.read {
calculateExternalDependencies(file)
}
private fun calculateExternalDependencies(file: VirtualFile): ScriptDependencies? {
val path = file.path
val cached = cache[path]
return if (cached != null) cached
else {
val scriptDef = findScriptDefinition(file, project)
if (scriptDef != null) {
val result = scriptContentLoader.loadContentsAndResolveDependencies(scriptDef, file)
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file, result.reports)
val deps = result.dependencies?.adjustByDefinition(scriptDef)
if (deps != null) {
log.info("[kts] new cached deps for $path: ${deps.classpath.joinToString(File.pathSeparator)}")
}
cacheLock.write {
cache.put(path, deps)
}
deps
} else null
}
}
}
private val log = Logger.getInstance(ScriptDependenciesProvider::class.java)
@@ -1,46 +0,0 @@
/*
* 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.
*/
package org.jetbrains.kotlin.cli.common.script
import com.intellij.openapi.vfs.VirtualFile
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
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 {
override fun attachReports(scriptFile: VirtualFile, reports: List<ScriptReport>) {
reports.forEach {
messageCollector.report(it.severity.convertSeverity(), it.message, location(scriptFile, it.position))
}
}
private fun location(scriptFile: VirtualFile, position: ScriptReport.Position?): CompilerMessageLocation? {
if (position == null) return CompilerMessageLocation.create(scriptFile.path)
return CompilerMessageLocation.create(scriptFile.path, position.startLine, position.startColumn, null)
}
private fun ScriptReport.Severity.convertSeverity(): CompilerMessageSeverity = when (this) {
ScriptReport.Severity.FATAL -> CompilerMessageSeverity.ERROR
ScriptReport.Severity.ERROR -> CompilerMessageSeverity.ERROR
ScriptReport.Severity.WARNING -> CompilerMessageSeverity.WARNING
ScriptReport.Severity.INFO -> CompilerMessageSeverity.INFO
ScriptReport.Severity.DEBUG -> CompilerMessageSeverity.LOGGING
}
}
@@ -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 {
@@ -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<KotlinScriptDefinition>
private var _cachedDefinitions: Sequence<KotlinScriptDefinition>? = null
private val cachedDefinitions: Sequence<KotlinScriptDefinition>
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<String> = 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<T>(from: Sequence<T>) : Sequence<T> {
private val lock = ReentrantReadWriteLock()
private val sequenceIterator = from.iterator()
private val cache = arrayListOf<T>()
private inner class CachingIterator : Iterator<T> {
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<T> = CachingIterator()
}
@@ -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() {
@@ -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);
}
@@ -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