Catch and report exceptions from DependenciesResolver

This commit is contained in:
Pavel V. Talanov
2017-07-18 17:07:50 +03:00
parent eb5be038fa
commit 5faad493b4
6 changed files with 107 additions and 25 deletions
@@ -28,6 +28,7 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.newvfs.BulkFileListener
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
import kotlinx.coroutines.experimental.CoroutineDispatcher
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.asCoroutineDispatcher
import kotlinx.coroutines.experimental.launch
@@ -135,40 +136,63 @@ internal class ScriptDependenciesUpdater(
): TimeStampedJob {
val currentTimeStamp = TimeStamps.next()
fun process(result: DependenciesResolver.ResolveResult) {
val lastRequest = requests[file.path]
val lastTimeStamp = lastRequest?.job?.timeStamp
val isLastSentRequest = lastTimeStamp == null || lastTimeStamp == currentTimeStamp
if (isLastSentRequest) {
if (lastRequest != null) {
// no job running atm unless there is a job started while we process this result
requests.replace(file.path, lastRequest, ModStampedRequest(lastRequest.modificationStamp, job = null))
}
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file, result.reports)
val resultingDependencies = (result.dependencies ?: ScriptDependencies.Empty).adjustByDefinition(scriptDef)
if (cache(resultingDependencies, file)) {
onChange()
}
}
}
val dependenciesResolver = scriptDef.dependencyResolver
val scriptContents = contentLoader.getScriptContents(scriptDef, file)
val environment = contentLoader.getEnvironment(scriptDef)
val newJob = if (dependenciesResolver is AsyncDependenciesResolver) {
launch(asyncUpdatesDispatcher) {
process(dependenciesResolver.resolveAsync(scriptContents, environment))
launchAsyncUpdate(asyncUpdatesDispatcher, file, currentTimeStamp, scriptDef) {
dependenciesResolver.resolveAsync(scriptContents, environment)
}
}
else {
assert(dependenciesResolver is LegacyResolverWrapper)
launch(legacyUpdatesDispatcher) {
process(dependenciesResolver.resolve(scriptContents, environment))
launchAsyncUpdate(legacyUpdatesDispatcher, file, currentTimeStamp, scriptDef) {
dependenciesResolver.resolve(scriptContents, environment)
}
}
return TimeStampedJob(newJob, currentTimeStamp)
}
private fun launchAsyncUpdate(
dispatcher: CoroutineDispatcher,
file: VirtualFile,
currentTimeStamp: TimeStamp,
scriptDef: KotlinScriptDefinition,
doResolve: suspend () -> DependenciesResolver.ResolveResult
) = launch(dispatcher) {
val result = try {
doResolve()
}
catch (t: Throwable) {
t.asResolveFailure(scriptDef)
}
processResult(file, currentTimeStamp, result, scriptDef)
}
private fun processResult(
file: VirtualFile,
currentTimeStamp: TimeStamp,
result: DependenciesResolver.ResolveResult,
scriptDef: KotlinScriptDefinition
) {
val lastRequest = requests[file.path]
val lastTimeStamp = lastRequest?.job?.timeStamp
val isLastSentRequest = lastTimeStamp == null || lastTimeStamp == currentTimeStamp
if (isLastSentRequest) {
if (lastRequest != null) {
// no job running atm unless there is a job started while we process this result
requests.replace(file.path, lastRequest, ModStampedRequest(lastRequest.modificationStamp, job = null))
}
ServiceManager.getService(project, ScriptReportSink::class.java)?.attachReports(file, result.reports)
val resultingDependencies = (result.dependencies ?: ScriptDependencies.Empty).adjustByDefinition(scriptDef)
if (cache(resultingDependencies, file)) {
onChange()
}
}
}
fun updateSync(file: VirtualFile, scriptDef: KotlinScriptDefinition): Boolean {
val newDeps = contentLoader.loadContentsAndResolveDependencies(scriptDef, file) ?: ScriptDependencies.Empty
return cache(newDeps, file)
@@ -0,0 +1,5 @@
<error descr="TestDependenciesResolver threw exception IllegalStateException:
Exception from resolver">
</error>
val s = 3
val g = 4
@@ -0,0 +1,18 @@
package custom.scriptDefinition
import java.io.File
import kotlin.script.dependencies.*
import kotlin.script.dependencies.experimental.*
import kotlin.script.templates.ScriptTemplateDefinition
class TestDependenciesResolver : DependenciesResolver {
override fun resolve(
scriptContents: ScriptContents,
environment: Environment
): DependenciesResolver.ResolveResult {
error("Exception from resolver")
}
}
@ScriptTemplateDefinition(TestDependenciesResolver::class, scriptFilePattern = "script.kts")
open class Template
@@ -95,4 +95,10 @@ public class ScriptConfigurationHighlightingTestGenerated extends AbstractScript
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/script/definition/highlighting/simple/");
doTest(fileName);
}
@TestMetadata("throwingResolver")
public void testThrowingResolver() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/script/definition/highlighting/throwingResolver/");
doTest(fileName);
}
}