Move the rest of script descriptor implementation details to plugin, cleanup

This commit is contained in:
Ilya Chernikov
2019-02-12 17:45:45 +01:00
parent 29fa0f5dff
commit cc4aedb807
9 changed files with 15 additions and 106 deletions

View File

@@ -0,0 +1,104 @@
/*
* 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.compiler.plugin.resolve
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassMemberScope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
class LazyScriptClassMemberScope(
resolveSession: ResolveSession,
declarationProvider: ClassMemberDeclarationProvider,
private val scriptDescriptor: LazyScriptDescriptor,
trace: BindingTrace
) : LazyClassMemberScope(resolveSession, declarationProvider, scriptDescriptor, trace) {
private val scriptPrimaryConstructor: () -> ClassConstructorDescriptorImpl? = resolveSession.storageManager.createNullableLazyValue {
val baseClass = scriptDescriptor.baseClassDescriptor()
val baseConstructorDescriptor = baseClass?.unsubstitutedPrimaryConstructor
if (baseConstructorDescriptor != null) {
val implicitReceiversParamTypes =
scriptDescriptor.implicitReceivers.mapIndexed { idx, receiver ->
val name =
if (receiver is ScriptDescriptor) "$IMPORTED_SCRIPT_PARAM_NAME_PREFIX${receiver.name}"
else "$IMPLICIT_RECEIVER_PARAM_NAME_PREFIX$idx"
name to receiver.defaultType
}
val providedPropertiesParamTypes =
scriptDescriptor.scriptProvidedProperties.map {
it.name.identifier to it.type
}
val annotations = baseConstructorDescriptor.annotations
val constructorDescriptor = ClassConstructorDescriptorImpl.create(
scriptDescriptor, annotations, baseConstructorDescriptor.isPrimary, scriptDescriptor.source
)
var paramsIndexBase = baseConstructorDescriptor.valueParameters.lastIndex + 1
val syntheticParameters =
(implicitReceiversParamTypes + providedPropertiesParamTypes).map { param: Pair<String, KotlinType> ->
ValueParameterDescriptorImpl(
constructorDescriptor,
null,
paramsIndexBase++,
Annotations.EMPTY,
Name.identifier(param.first),
param.second,
false, false, false, null, SourceElement.NO_SOURCE
)
}
val parameters = baseConstructorDescriptor.valueParameters.map { it.copy(constructorDescriptor, it.name, it.index) } +
syntheticParameters
constructorDescriptor.initialize(parameters, baseConstructorDescriptor.visibility)
constructorDescriptor.returnType = scriptDescriptor.defaultType
constructorDescriptor
} else {
null
}
}
override fun resolvePrimaryConstructor(): ClassConstructorDescriptor? {
val constructor = scriptPrimaryConstructor()
?: ClassConstructorDescriptorImpl.create(
scriptDescriptor,
Annotations.EMPTY,
true,
SourceElement.NO_SOURCE
).initialize(
emptyList(),
Visibilities.PUBLIC
)
setDeferredReturnType(constructor)
return constructor
}
override fun getNonDeclaredProperties(name: Name, result: MutableSet<PropertyDescriptor>) {
super.getNonDeclaredProperties(name, result)
if (scriptDescriptor.resultFieldName() == name.asString()) {
scriptDescriptor.resultValue?.let {
result.add(it)
}
}
}
override fun createPropertiesFromPrimaryConstructorParameters(name: Name, result: MutableSet<PropertyDescriptor>) {
}
companion object {
const val IMPLICIT_RECEIVER_PARAM_NAME_PREFIX = "\$\$implicitReceiver"
const val IMPORTED_SCRIPT_PARAM_NAME_PREFIX = "\$\$importedScript"
}
}
private fun ClassDescriptor.substitute(vararg types: KotlinType): KotlinType? =
KotlinTypeFactory.simpleType(this.defaultType, arguments = types.map { it.asTypeProjection() })

View File

@@ -0,0 +1,253 @@
/*
* 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.compiler.plugin.resolve
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Errors.MISSING_IMPORTED_SCRIPT_FILE
import org.jetbrains.kotlin.diagnostics.Errors.MISSING_IMPORTED_SCRIPT_PSI
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.lazy.LazyClassContext
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.data.KtScriptInfo
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeImpl
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind
import org.jetbrains.kotlin.resolve.source.toSourceElement
import org.jetbrains.kotlin.script.KotlinScriptDefinition
import org.jetbrains.kotlin.script.ScriptDependenciesProvider
import org.jetbrains.kotlin.script.ScriptPriorities
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.io.File
import kotlin.reflect.KClass
import kotlin.reflect.KType
class LazyScriptDescriptor(
val resolveSession: ResolveSession,
containingDeclaration: DeclarationDescriptor,
name: Name,
internal val scriptInfo: KtScriptInfo
) : ScriptDescriptor, LazyClassDescriptor(
resolveSession,
containingDeclaration,
name,
scriptInfo,
/* isExternal = */ false
) {
init {
resolveSession.trace.record(BindingContext.SCRIPT, scriptInfo.script, this)
}
override fun getResultValue(): ReplResultPropertyDescriptor? {
val expression = scriptInfo.script
.getChildOfType<KtBlockExpression>()
?.getChildrenOfType<KtScriptInitializer>()?.lastOrNull()
?.getChildOfType<KtExpression>()
val type = expression?.let {
resolveSession.trace.bindingContext.getType(it)
}
return if (type != null && !type.isUnit() && !type.isNothing()) {
resultFieldName()?.let {
ReplResultPropertyDescriptor(
Name.identifier(it),
type,
this.thisAsReceiverParameter,
this,
expression.toSourceElement()
)
}
} else null
}
fun resultFieldName(): String? {
val scriptPriority = scriptInfo.script.getUserData(ScriptPriorities.PRIORITY_KEY)
if (scriptPriority != null) {
return "res$scriptPriority"
}
val scriptName = name.asString()
return if (scriptName.startsWith("Line_")) {
"res${scriptName.split("_")[1]}"
} else "\$\$result"
}
private val sourceElement = scriptInfo.script.toSourceElement()
override fun getSource() = sourceElement
private val priority: Int = ScriptPriorities.getScriptPriority(scriptInfo.script)
override fun getPriority() = priority
val scriptDefinition: () -> KotlinScriptDefinition = resolveSession.storageManager.createLazyValue {
val file = scriptInfo.script.containingKtFile
scriptInfo.script.kotlinScriptDefinition ?: throw RuntimeException("file ${file.name} is not a script")
}
override fun substitute(substitutor: TypeSubstitutor) = this
override fun <R, D> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D): R =
visitor.visitScriptDescriptor(this, data)
override fun createMemberScope(c: LazyClassContext, declarationProvider: ClassMemberDeclarationProvider): LazyScriptClassMemberScope =
LazyScriptClassMemberScope(
// Must be a ResolveSession for scripts
c as ResolveSession,
declarationProvider,
this,
c.trace
)
override fun getUnsubstitutedPrimaryConstructor() = super.getUnsubstitutedPrimaryConstructor()!!
internal val baseClassDescriptor: () -> ClassDescriptor? = resolveSession.storageManager.createNullableLazyValue {
val template = scriptDefinition().template
findTypeDescriptor(
template,
if (template.qualifiedName?.startsWith("kotlin.script.templates.standard") == true) Errors.MISSING_SCRIPT_STANDARD_TEMPLATE
else Errors.MISSING_SCRIPT_BASE_CLASS
)
}
override fun computeSupertypes() = listOf(baseClassDescriptor()?.defaultType ?: builtIns.anyType)
private inner class ImportedScriptDescriptorsFinder {
val fileManager = VirtualFileManager.getInstance()
val localFS = fileManager.getFileSystem(StandardFileSystems.FILE_PROTOCOL)
val psiManager = PsiManager.getInstance(scriptInfo.script.project)
operator fun invoke(importedScriptFile: File): ScriptDescriptor? {
fun errorDescriptor(errorDiagnostic: DiagnosticFactory1<PsiElement, String>?): ScriptDescriptor? {
reportErrorString1(errorDiagnostic, importedScriptFile.path)
return null
}
val vfile = localFS.findFileByPath(importedScriptFile.path)
?: return errorDescriptor(MISSING_IMPORTED_SCRIPT_FILE)
val psiFile = psiManager.findFile(vfile)
?: return errorDescriptor(MISSING_IMPORTED_SCRIPT_PSI)
// Note: is not an error now - if import references other valid source file, it is simply compiled along with script
// TODO: check if this is the behavior we want to have - see #KT-28916
val ktScript = (psiFile as? KtFile)?.declarations?.firstIsInstanceOrNull<KtScript>()
?: return null
return resolveSession.getScriptDescriptor(ktScript) as ScriptDescriptor
}
}
private val scriptImplicitReceivers: () -> List<ClassDescriptor> = resolveSession.storageManager.createLazyValue {
val res = ArrayList<ClassDescriptor>()
val importedScriptsFiles = ScriptDependenciesProvider.getInstance(scriptInfo.script.project)
?.getScriptDependencies(scriptInfo.script.containingKtFile)?.scripts
if (importedScriptsFiles != null) {
val findImportedScriptDescriptor = ImportedScriptDescriptorsFinder()
importedScriptsFiles.mapNotNullTo(res) {
findImportedScriptDescriptor(it)
}
}
scriptDefinition().implicitReceivers.mapNotNullTo(res) { receiver ->
findTypeDescriptor(receiver, Errors.MISSING_SCRIPT_RECEIVER_CLASS)
}
res
}
internal fun findTypeDescriptor(kClass: KClass<*>, errorDiagnostic: DiagnosticFactory1<PsiElement, String>?): ClassDescriptor? =
findTypeDescriptor(kClass.classId, kClass.toString(), errorDiagnostic)
internal fun findTypeDescriptor(type: KType, errorDiagnostic: DiagnosticFactory1<PsiElement, String>?): ClassDescriptor? =
findTypeDescriptor(type.classId, type.toString(), errorDiagnostic)
internal fun findTypeDescriptor(
classId: ClassId?, typeName: String,
errorDiagnostic: DiagnosticFactory1<PsiElement, String>?
): ClassDescriptor? {
val typeDescriptor = classId?.let { module.findClassAcrossModuleDependencies(it) }
if (typeDescriptor == null) {
reportErrorString1(errorDiagnostic, classId?.asSingleFqName()?.toString() ?: typeName)
}
return typeDescriptor
}
private fun reportErrorString1(errorDiagnostic: DiagnosticFactory1<PsiElement, String>?, arg: String) {
if (errorDiagnostic != null) {
// TODO: use PositioningStrategies to highlight some specific place in case of error, instead of treating the whole file as invalid
resolveSession.trace.report(
errorDiagnostic.on(
scriptInfo.script,
arg
)
)
}
}
override fun getImplicitReceivers(): List<ClassDescriptor> = scriptImplicitReceivers()
private val scriptProvidedProperties: () -> ScriptProvidedPropertiesDescriptor = resolveSession.storageManager.createLazyValue {
ScriptProvidedPropertiesDescriptor(this)
}
override fun getScriptProvidedProperties(): List<PropertyDescriptor> = scriptProvidedProperties().properties()
private val scriptOuterScope: () -> LexicalScope = resolveSession.storageManager.createLazyValue {
var outerScope = super.getOuterScope()
val outerScopeReceivers = implicitReceivers.let {
if (scriptDefinition().providedProperties.isEmpty()) {
it
} else {
it + ScriptProvidedPropertiesDescriptor(this)
}
}
for (receiverClassDescriptor in outerScopeReceivers.asReversed()) {
outerScope = LexicalScopeImpl(
outerScope,
receiverClassDescriptor,
true,
receiverClassDescriptor.thisAsReceiverParameter,
LexicalScopeKind.CLASS_MEMBER_SCOPE
)
}
outerScope
}
override fun getOuterScope(): LexicalScope = scriptOuterScope()
private val scriptClassAnnotations: () -> Annotations = resolveSession.storageManager.createLazyValue {
baseClassDescriptor()?.annotations?.let { ann ->
FilteredAnnotations(ann) { fqname ->
val shortName = fqname.shortName().identifier
// TODO: consider more precise annotation filtering
!shortName.startsWith("KotlinScript") && !shortName.startsWith("ScriptTemplate")
}
} ?: super.annotations
}
override val annotations: Annotations
get() = scriptClassAnnotations()
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2010-2018 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.compiler.plugin.resolve
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
class ReplResultPropertyDescriptor(
name: Name,
kotlinType: KotlinType,
receiver: ReceiverParameterDescriptor?,
script: ScriptDescriptor,
source: SourceElement
) : PropertyDescriptorImpl(
script,
null,
Annotations.EMPTY,
Modality.FINAL,
Visibilities.PUBLIC,
false,
name,
CallableMemberDescriptor.Kind.SYNTHESIZED,
source,
/* lateInit = */ false, /* isConst = */ false, /* isExpect = */ false, /* isActual = */ false, /* isExternal = */ false,
/* isDelegated = */ false
) {
init {
setType(kotlinType, emptyList(), receiver, null)
initialize(
null, null
)
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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.compiler.plugin.resolve
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.MutableClassDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.Printer
class ScriptProvidedPropertiesDescriptor(script: LazyScriptDescriptor) :
MutableClassDescriptor(
script,
ClassKind.CLASS, false, false,
Name.special("<synthetic script provided properties for ${script.name}>"),
SourceElement.NO_SOURCE,
LockBasedStorageManager.NO_LOCKS
) {
init {
modality = Modality.FINAL
visibility = Visibilities.PUBLIC
setTypeParameterDescriptors(emptyList())
createTypeConstructor()
}
private val memberScope: () -> ScriptProvidedPropertiesMemberScope = script.resolveSession.storageManager.createLazyValue {
ScriptProvidedPropertiesMemberScope(
script.name.identifier,
properties()
)
}
override fun getUnsubstitutedMemberScope(): MemberScope = memberScope()
val properties: () -> List<ScriptProvidedPropertyDescriptor> = script.resolveSession.storageManager.createLazyValue {
script.scriptDefinition().providedProperties.mapNotNull { (name, type) ->
script.findTypeDescriptor(type, Errors.MISSING_SCRIPT_PROVIDED_PROPERTY_CLASS)?.let {
name to it
}
}.map { (name, classDescriptor) ->
ScriptProvidedPropertyDescriptor(
Name.identifier(name),
classDescriptor,
thisAsReceiverParameter,
true,
script
)
}
}
private class ScriptProvidedPropertiesMemberScope(
private val scriptId: String,
private val providedProperties: List<PropertyDescriptor>
) : MemberScopeImpl() {
override fun getContributedDescriptors(
kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean
): Collection<DeclarationDescriptor> =
providedProperties
override fun getContributedVariables(name: Name, location: LookupLocation): Collection<PropertyDescriptor> =
providedProperties.filter { it.name == name }
override fun printScopeStructure(p: Printer) {
p.println("Scope of script provided properties: $scriptId")
}
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright 2010-2018 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.compiler.plugin.resolve
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.name.Name
class ScriptProvidedPropertyDescriptor(
name: Name,
typeDescriptor: ClassDescriptor,
receiver: ReceiverParameterDescriptor?,
isVar: Boolean,
script: ScriptDescriptor
) : PropertyDescriptorImpl(
script,
null,
Annotations.EMPTY,
Modality.FINAL,
Visibilities.PRIVATE,
isVar,
name,
CallableMemberDescriptor.Kind.SYNTHESIZED,
SourceElement.NO_SOURCE,
/* lateInit = */ false, /* isConst = */ false, /* isExpect = */ false, /* isActual = */ false, /* isExternal = */ false,
/* isDelegated = */ false
) {
init {
setType(typeDescriptor.defaultType, emptyList(), receiver, null)
initialize(
makePropertyGetterDescriptor(),
if (!isVar) null else makePropertySetterDescriptor()
)
}
}
private fun PropertyDescriptorImpl.makePropertyGetterDescriptor() =
PropertyGetterDescriptorImpl(
this,
Annotations.EMPTY,
this.modality,
this.visibility,
/* isDefault = */
false, /* isExternal = */
false, /* isInline = */
false,
this.kind,
null,
SourceElement.NO_SOURCE
).also {
it.initialize(returnType)
}
private fun PropertyDescriptorImpl.makePropertySetterDescriptor() =
PropertySetterDescriptorImpl(
this,
Annotations.EMPTY,
this.modality,
this.visibility,
/* isDefault = */
false, /* isExternal = */
false, /* isInline = */
false,
this.kind,
null,
SourceElement.NO_SOURCE
).also {
it.initialize(
ValueParameterDescriptorImpl(
this,
null,
0,
Annotations.EMPTY,
Name.special("<set-?>"),
returnType,
/* declaresDefaultValue = */
false, /* isCrossinline = */
false, /* isNoinline = */
false,
null,
SourceElement.NO_SOURCE
)
)
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright 2010-2018 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.compiler.plugin.resolve
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import kotlin.reflect.KClass
import kotlin.reflect.KType
val KClass<*>.classId: ClassId
get() = this.java.enclosingClass?.kotlin?.classId?.createNestedClassId(Name.identifier(simpleName!!))
?: ClassId.topLevel(FqName(qualifiedName!!))
val KType.classId: ClassId?
get() = classifier?.let { it as? KClass<*> }?.classId

View File

@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
import org.jetbrains.kotlin.resolve.lazy.LazyClassContext
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.declarations.PackageMemberDeclarationProvider
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor
import org.jetbrains.kotlin.scripting.compiler.plugin.resolve.LazyScriptDescriptor
class ScriptingResolveExtension : SyntheticResolveExtension {
override fun generateSyntheticClasses(