Refactor definition keys

This commit is contained in:
Ilya Chernikov
2018-08-17 13:15:41 +02:00
parent dbee426647
commit ab63d59a56
11 changed files with 100 additions and 126 deletions
@@ -21,8 +21,8 @@ import org.junit.Assert
import java.io.File
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.ScriptDefinition
import kotlin.script.experimental.api.contextVariables
import kotlin.script.experimental.api.scriptImplicitReceivers
import kotlin.script.experimental.api.implicitReceivers
import kotlin.script.experimental.api.providedProperties
abstract class AbstractCustomScriptCodegenTest : CodegenTestCase() {
private lateinit var scriptDefinitions: List<String>
@@ -121,21 +121,19 @@ abstract class AbstractCustomScriptCodegenTest : CodegenTestCase() {
}
}
object TestScriptWithReceiversConfiguration : ScriptDefinition {
override val properties = properties {
scriptImplicitReceivers<String>()
}
}
object TestScriptWithReceiversConfiguration : ScriptDefinition(
{
implicitReceivers<String>()
})
@Suppress("unused")
@KotlinScript(definition = TestScriptWithReceiversConfiguration::class)
abstract class TestScriptWithReceivers
object TestScriptWithSimpleEnvVarsConfiguration : ScriptDefinition {
override val properties = properties {
contextVariables("stringVar1" to String::class)
}
}
object TestScriptWithSimpleEnvVarsConfiguration : ScriptDefinition(
{
providedProperties("stringVar1" to String::class)
})
@Suppress("unused")
@KotlinScript(definition = TestScriptWithSimpleEnvVarsConfiguration::class)
@@ -40,46 +40,37 @@ object MyScriptDefinition : ScriptDefinition(
// variant: dependencies(collectDependenciesFromCurrentContext(...
refineConfiguration {
// variant ^: dynamicConfiguration
handler(MyConfigurator())
triggerOnAnnotations(DependsOn::class, Repository::class)
onAnnotations(DependsOn::class, Repository::class, handler = ::myConfigureOnAnnotations)
// variants: onAnnotations, refineOnAnnotations (esp. for dynamicConfiguration), updateOnAnnotations
// other triggers: beforeParsing, onSections
}
}
)
class MyConfigurator : RefineScriptCompilationConfigurationHandler {
private val resolver = FilesAndMavenResolver()
private val resolver = FilesAndMavenResolver()
override suspend operator fun invoke(
scriptSource: ScriptSource,
scriptDefinition: ScriptDefinition,
configuration: ScriptCompileConfiguration?,
processedScriptData: ProcessedScriptData?
): ResultWithDiagnostics<ScriptCompileConfiguration?> {
val annotations = processedScriptData?.get(ProcessedScriptData.foundAnnotations)?.takeIf { it.isNotEmpty() }
?: return configuration.asSuccess()
val scriptContents = object : ScriptContents {
override val annotations: Iterable<Annotation> = annotations
override val file: File? = null
override val text: CharSequence? = null
}
val diagnostics = arrayListOf<ScriptDiagnostic>()
fun report(severity: ScriptDependenciesResolver.ReportSeverity, message: String, position: ScriptContents.Position?) {
diagnostics.add(ScriptDiagnostic(message, mapLegacyDiagnosticSeverity(severity), mapLegacyScriptPosition(position)))
}
return try {
val newDepsFromResolver = resolver.resolve(scriptContents, emptyMap(), ::report, null).get()
?: return configuration.asSuccess(diagnostics)
val resolvedClasspath = newDepsFromResolver.classpath.toList().takeIf { it.isNotEmpty() }
?: return configuration.asSuccess(diagnostics)
ScriptCompileConfiguration {
dependencies(JvmDependency(resolvedClasspath))
}.asSuccess(diagnostics)
} catch (e: Throwable) {
ResultWithDiagnostics.Failure(*diagnostics.toTypedArray(), e.asDiagnostics())
}
fun myConfigureOnAnnotations(script: ScriptDataFacade): ResultWithDiagnostics<ScriptCompileConfiguration?> {
val annotations = script.collectedData?.get(ScriptCollectedData.foundAnnotations)?.takeIf { it.isNotEmpty() }
?: return script.configuration.asSuccess()
val scriptContents = object : ScriptContents {
override val annotations: Iterable<Annotation> = annotations
override val file: File? = null
override val text: CharSequence? = null
}
val diagnostics = arrayListOf<ScriptDiagnostic>()
fun report(severity: ScriptDependenciesResolver.ReportSeverity, message: String, position: ScriptContents.Position?) {
diagnostics.add(ScriptDiagnostic(message, mapLegacyDiagnosticSeverity(severity), mapLegacyScriptPosition(position)))
}
return try {
val newDepsFromResolver = resolver.resolve(scriptContents, emptyMap(), ::report, null).get()
?: return script.configuration.asSuccess(diagnostics)
val resolvedClasspath = newDepsFromResolver.classpath.toList().takeIf { it.isNotEmpty() }
?: return script.configuration.asSuccess(diagnostics)
ScriptCompileConfiguration {
dependencies(JvmDependency(resolvedClasspath))
}.asSuccess(diagnostics)
} catch (e: Throwable) {
ResultWithDiagnostics.Failure(*diagnostics.toTypedArray(), e.asDiagnostics())
}
}
@@ -30,38 +30,43 @@ val ScriptCompileConfigurationKeys.sourceFragments by PropertiesCollection.key<L
val ScriptCompileConfigurationKeys.scriptBodyTarget by PropertiesCollection.keyCopy(ScriptDefinition.scriptBodyTarget)
val ScriptCompileConfigurationKeys.scriptImplicitReceivers by PropertiesCollection.keyCopy(ScriptDefinition.scriptImplicitReceivers)
val ScriptCompileConfigurationKeys.scriptImplicitReceivers by PropertiesCollection.keyCopy(ScriptDefinition.implicitReceivers)
val ScriptCompileConfigurationKeys.contextVariables by PropertiesCollection.keyCopy(ScriptDefinition.contextVariables)
val ScriptCompileConfigurationKeys.providedProperties by PropertiesCollection.keyCopy(ScriptDefinition.providedProperties)
val ScriptCompileConfigurationKeys.defaultImports by PropertiesCollection.keyCopy(ScriptDefinition.defaultImports)
val ScriptCompileConfigurationKeys.restrictions by PropertiesCollection.keyCopy(ScriptDefinition.restrictions)
val ScriptCompileConfigurationKeys.importedScripts by PropertiesCollection.keyCopy(ScriptDefinition.importedScripts)
val ScriptCompileConfigurationKeys.dependencies by PropertiesCollection.keyCopy(ScriptDefinition.dependencies)
val ScriptCompileConfigurationKeys.compilerOptions by PropertiesCollection.keyCopy(ScriptDefinition.compilerOptions)
interface ProcessedScriptDataKeys
interface ScriptCollectedDataKeys
class ProcessedScriptData(properties: Map<PropertiesCollection.Key<*>, Any>) : PropertiesCollection(properties) {
class ScriptCollectedData(properties: Map<PropertiesCollection.Key<*>, Any>) : PropertiesCollection(properties) {
companion object : ProcessedScriptDataKeys
companion object : ScriptCollectedDataKeys
}
val ProcessedScriptDataKeys.foundAnnotations by PropertiesCollection.key<List<Annotation>>()
val ScriptCollectedDataKeys.foundAnnotations by PropertiesCollection.key<List<Annotation>>()
val ProcessedScriptDataKeys.foundFragments by PropertiesCollection.key<List<ScriptSourceNamedFragment>>()
val ScriptCollectedDataKeys.foundFragments by PropertiesCollection.key<List<ScriptSourceNamedFragment>>()
class ScriptDataFacade(
val source: ScriptSource,
val definition: ScriptDefinition,
val configuration: ScriptCompileConfiguration?,
val collectedData: ScriptCollectedData? = null
)
interface RefineScriptCompilationConfigurationHandler {
suspend operator fun invoke(
scriptSource: ScriptSource,
scriptDefinition: ScriptDefinition,
configuration: ScriptCompileConfiguration?,
processedScriptData: ProcessedScriptData? = null
): ResultWithDiagnostics<ScriptCompileConfiguration?>
}
typealias RefineScriptCompilationConfigurationHandler = (ScriptDataFacade) -> ResultWithDiagnostics<ScriptCompileConfiguration?>
class RefineConfigurationOnAnnotationsData(
val annotations: List<KotlinType>,
val handler: RefineScriptCompilationConfigurationHandler
)
class RefineConfigurationOnSectionsData(
val sections: List<String>,
val handler: RefineScriptCompilationConfigurationHandler
)
@@ -22,7 +22,7 @@ data class ScriptSourceNamedFragment(val name: String?, val range: ScriptSource.
enum class ScriptBodyTarget {
Constructor,
SingleAbstractMethod
SAMFunction
}
data class ResolvingRestrictionRule(
@@ -29,7 +29,7 @@ open class ScriptDefinition(baseDefinitions: Iterable<ScriptDefinition>, body: B
}
}
val ScriptDefinitionKeys.name by PropertiesCollection.key<String>("Kotlin script") // Name of the script type
val ScriptDefinitionKeys.displayName by PropertiesCollection.key<String>("Kotlin script") // Name of the script type
val ScriptDefinitionKeys.fileExtension by PropertiesCollection.key<String>("kts") // file extension
@@ -37,31 +37,25 @@ val ScriptDefinitionKeys.baseClass by PropertiesCollection.key<KotlinType>() //
val ScriptDefinitionKeys.scriptBodyTarget by PropertiesCollection.key<ScriptBodyTarget>(ScriptBodyTarget.Constructor)
val ScriptDefinitionKeys.scriptImplicitReceivers by PropertiesCollection.key<List<KotlinType>>() // in the order from outer to inner scope
val ScriptDefinitionKeys.implicitReceivers by PropertiesCollection.key<List<KotlinType>>() // in the order from outer to inner scope
val ScriptDefinitionKeys.contextVariables by PropertiesCollection.key<Map<String, KotlinType>>() // external variables
val ScriptDefinitionKeys.providedProperties by PropertiesCollection.key<Map<String, KotlinType>>() // external variables
val ScriptDefinitionKeys.defaultImports by PropertiesCollection.key<List<String>>()
val ScriptDefinitionKeys.restrictions by PropertiesCollection.key<List<ResolvingRestrictionRule>>()
val ScriptDefinitionKeys.importedScripts by PropertiesCollection.key<List<ScriptSource>>()
val ScriptDefinitionKeys.dependencies by PropertiesCollection.key<List<ScriptDependency>>()
val ScriptDefinitionKeys.generatedClassAnnotations by PropertiesCollection.key<List<Annotation>>()
val ScriptDefinitionKeys.generatedMethodAnnotations by PropertiesCollection.key<List<Annotation>>()
val ScriptDefinitionKeys.copyAnnotationsFrom by PropertiesCollection.key<List<KotlinType>>()
val ScriptDefinitionKeys.compilerOptions by PropertiesCollection.key<List<String>>() // Q: CommonCompilerOptions instead?
val ScriptDefinitionKeys.refineConfigurationHandler by PropertiesCollection.key<RefineScriptCompilationConfigurationHandler>() // dynamic configurator
val ScriptDefinitionKeys.refineConfigurationBeforeParsing by PropertiesCollection.key<RefineScriptCompilationConfigurationHandler>() // default: false
val ScriptDefinitionKeys.refineConfigurationBeforeParsing by PropertiesCollection.key<Boolean>() // default: false
val ScriptDefinitionKeys.refineConfigurationOnAnnotations by PropertiesCollection.key<RefineConfigurationOnAnnotationsData>()
val ScriptDefinitionKeys.refineConfigurationOnAnnotations by PropertiesCollection.key<List<KotlinType>>()
val ScriptDefinitionKeys.refineConfigurationOnSections by PropertiesCollection.key<List<String>>()
val ScriptDefinitionKeys.refineConfigurationOnSections by PropertiesCollection.key<RefineConfigurationOnSectionsData>()
// DSL:
@@ -70,36 +64,36 @@ val ScriptDefinition.Builder.refineConfiguration get() = RefineConfigurationBuil
class RefineConfigurationBuilder : PropertiesCollection.Builder() {
fun handler(fn: RefineScriptCompilationConfigurationHandler) {
set(ScriptDefinition.refineConfigurationHandler, fn)
fun beforeParsing(handler: RefineScriptCompilationConfigurationHandler) {
set(ScriptDefinition.refineConfigurationBeforeParsing, handler)
}
fun triggerBeforeParsing(value: Boolean = true) {
set(ScriptDefinition.refineConfigurationBeforeParsing, value)
fun onAnnotations(annotations: List<KotlinType>, handler: RefineScriptCompilationConfigurationHandler) {
set(ScriptDefinition.refineConfigurationOnAnnotations, RefineConfigurationOnAnnotationsData(annotations, handler))
}
fun triggerOnAnnotations(annotations: Iterable<KotlinType>) {
ScriptDefinition.refineConfigurationOnAnnotations.append(annotations)
fun onAnnotations(vararg annotations: KotlinType, handler: RefineScriptCompilationConfigurationHandler) {
onAnnotations(annotations.asList(), handler)
}
fun triggerOnAnnotations(vararg annotations: KotlinType) {
triggerOnAnnotations(annotations.asIterable())
inline fun <reified T : Annotation> onAnnotations(noinline handler: RefineScriptCompilationConfigurationHandler) {
onAnnotations(listOf(KotlinType(T::class)), handler)
}
inline fun <reified T : Annotation> triggerOnAnnotations() {
triggerOnAnnotations(KotlinType(T::class))
fun onAnnotations(vararg annotations: KClass<out Annotation>, handler: RefineScriptCompilationConfigurationHandler) {
onAnnotations(annotations.map { KotlinType(it) }, handler)
}
fun triggerOnAnnotations(vararg annotations: KClass<out Annotation>) {
triggerOnAnnotations(annotations.map { KotlinType(it) })
fun onAnnotations(annotations: Iterable<KClass<out Annotation>>, handler: RefineScriptCompilationConfigurationHandler) {
onAnnotations(annotations.map { KotlinType(it) }, handler)
}
fun triggerOnSections(sections: Iterable<String>) {
ScriptDefinition.refineConfigurationOnSections.append(sections)
fun onSections(sections: List<String>, handler: RefineScriptCompilationConfigurationHandler) {
set(ScriptDefinition.refineConfigurationOnSections, RefineConfigurationOnSectionsData(sections, handler))
}
fun triggerOnSections(vararg sections: String) {
ScriptDefinition.refineConfigurationOnSections.append(*sections)
fun onSections(vararg sections: String, handler: RefineScriptCompilationConfigurationHandler) {
onSections(sections.asList(), handler)
}
}
@@ -44,7 +44,7 @@ fun createScriptDefinitionFromAnnotatedBaseClass(
return ScriptDefinition {
baseClass(baseClassType)
fileExtension(baseClass.findAnnotation<KotlinScriptFileExtension>()?.extension ?: mainAnnotation.extension)
name(mainAnnotation.name)
displayName(mainAnnotation.name)
include(scriptingPropsInstance(mainAnnotation.definition))
@@ -163,7 +163,7 @@ open class PropertiesCollection(val properties: Map<Key<*>, Any> = emptyMap()) {
@JvmName("appendToMap")
fun <K, V> PropertiesCollection.Key<in Map<K, V>>.append(values: Iterable<Pair<K, V>>) {
val newValues = get(this)?.let { it + values } ?: values
val newValues = get(this)?.let { it + values } ?: values.toMap()
data[this] = newValues
}
@@ -253,7 +253,8 @@ internal class BridgeScriptDefinition(
) {
override val acceptedAnnotations = run {
val cl = this::class.java.classLoader
getMergedFromChainOrNull(ScriptDefinition.refineConfigurationOnAnnotations, scriptDefinition, additionalCompilationConfiguration)
getFirstFromChainOrNull(ScriptDefinition.refineConfigurationOnAnnotations, scriptDefinition, additionalCompilationConfiguration)
?.annotations
?.map { (cl.loadClass(it.typeName) as Class<out Annotation>).kotlin }
?: emptyList()
}
@@ -40,20 +40,13 @@ open class JvmScriptCompiler(
scriptDefinition: ScriptDefinition,
additionalConfiguration: ScriptCompileConfiguration?
): ResultWithDiagnostics<CompiledScript<*>> {
val refineConfigurationFn = scriptDefinition[ScriptDefinition.refineConfigurationHandler]
val refineConfigurationFn = scriptDefinition[ScriptDefinition.refineConfigurationBeforeParsing]
val refinedConfiguration =
if (scriptDefinition[ScriptDefinition.refineConfigurationBeforeParsing] == true) {
if (refineConfigurationFn == null) {
return ResultWithDiagnostics.Failure("Non-null configurator expected".asErrorDiagnostics())
refineConfigurationFn?.invoke(ScriptDataFacade(script, scriptDefinition, additionalConfiguration))?.let {
when (it) {
is ResultWithDiagnostics.Failure -> return it
is ResultWithDiagnostics.Success -> it.value
}
refineConfigurationFn(script, scriptDefinition, additionalConfiguration).let {
when (it) {
is ResultWithDiagnostics.Failure -> return it
is ResultWithDiagnostics.Success -> it.value
}
}
} else {
null
}
val actualConfiguration = mergeConfigurations(additionalConfiguration, refinedConfiguration)
val cached = cache.get(script, scriptDefinition, actualConfiguration)
@@ -35,18 +35,18 @@ class BridgeDependenciesResolver(
return try {
val diagnostics = arrayListOf<ScriptReport>()
val processedScriptData = ProcessedScriptData(
val processedScriptData = ScriptCollectedData(
mapOf(
ProcessedScriptData.foundAnnotations to scriptContents.annotations
ScriptCollectedData.foundAnnotations to scriptContents.annotations
)
)
val refineFn = scriptDefinition[ScriptDefinition.refineConfigurationHandler]
val refineFn = scriptDefinition[ScriptDefinition.refineConfigurationOnAnnotations]?.handler
val refinedConfiguration =
if (refineFn == null) null
else {
val res = refineFn(
scriptContents.toScriptSource(), scriptDefinition, additionalConfiguration, processedScriptData
ScriptDataFacade(scriptContents.toScriptSource(), scriptDefinition, additionalConfiguration, processedScriptData)
)
when (res) {
is ResultWithDiagnostics.Failure ->
@@ -36,7 +36,7 @@ abstract class KotlinScriptDefinitionAdapterFromNewAPIBase : KotlinScriptDefinit
override val template: KClass<*> get() = baseClass
override val name: String
get() = scriptDefinition[ScriptDefinition.name] ?: "Kotlin Script"
get() = scriptDefinition[ScriptDefinition.displayName] ?: "Kotlin Script"
override val fileType: LanguageFileType = KotlinFileType.INSTANCE
@@ -56,19 +56,19 @@ abstract class KotlinScriptDefinitionAdapterFromNewAPIBase : KotlinScriptDefinit
}
override val acceptedAnnotations: List<KClass<out Annotation>> by lazy(LazyThreadSafetyMode.PUBLICATION) {
scriptDefinition[ScriptDefinition.refineConfigurationOnAnnotations]
scriptDefinition[ScriptDefinition.refineConfigurationOnAnnotations]?.annotations
.orEmpty()
.map { getScriptingClass(it) as KClass<out Annotation> }
}
override val implicitReceivers: List<KType> by lazy(LazyThreadSafetyMode.PUBLICATION) {
scriptDefinition[ScriptDefinition.scriptImplicitReceivers]
scriptDefinition[ScriptDefinition.implicitReceivers]
.orEmpty()
.map { getScriptingClass(it).starProjectedType }
}
override val environmentVariables: List<Pair<String, KType>> by lazy(LazyThreadSafetyMode.PUBLICATION) {
scriptDefinition[ScriptDefinition.contextVariables]
scriptDefinition[ScriptDefinition.providedProperties]
?.map { (k, v) -> k to getScriptingClass(v).starProjectedType }.orEmpty()
}
@@ -82,14 +82,6 @@ abstract class KotlinScriptDefinitionAdapterFromNewAPIBase : KotlinScriptDefinit
ScriptExpectedLocation.TestsOnly
)
override val targetClassAnnotations: List<Annotation>
get() = scriptDefinition[ScriptDefinition.generatedClassAnnotations]
.orEmpty()
override val targetMethodAnnotations: List<Annotation>
get() = scriptDefinition[ScriptDefinition.generatedMethodAnnotations]
.orEmpty()
private val scriptingClassGetter by lazy(LazyThreadSafetyMode.PUBLICATION) {
hostEnvironment[ScriptingEnvironment.getScriptingClass]
?: throw IllegalArgumentException("Expecting 'getScriptingClass' property in the scripting environment")
@@ -109,7 +101,7 @@ class KotlinScriptDefinitionAdapterFromNewAPI(
override val hostEnvironment: ScriptingEnvironment
) : KotlinScriptDefinitionAdapterFromNewAPIBase() {
override val name: String get() = scriptDefinition[ScriptDefinition.name] ?: super.name
override val name: String get() = scriptDefinition[ScriptDefinition.displayName] ?: super.name
override val scriptFileExtensionWithDot =
"." + (scriptDefinition[ScriptDefinition.fileExtension] ?: "kts")