Create proper constructor in script descriptor

allows to resolve calls to scripts in compiler (but not in the IDE yet)
Some refactorings on the way
This commit is contained in:
Ilya Chernikov
2018-06-22 12:15:14 +02:00
parent b7d3382f13
commit 9453834fb1
10 changed files with 164 additions and 112 deletions
@@ -75,9 +75,7 @@ class ScriptCodegen private constructor(
val jvmSignature = typeMapper.mapScriptSignature(
scriptDescriptor,
scriptContext.earlierScripts,
scriptDefinition.implicitReceivers,
scriptDefinition.environmentVariables
scriptContext.earlierScripts
)
if (state.replSpecific.shouldGenerateScriptResultValue) {
@@ -104,37 +102,6 @@ class ScriptCodegen private constructor(
val superclass = scriptDescriptor.getSuperClassNotAny()
// TODO: throw if class is not found)
if (superclass == null) {
iv.load(0, classType)
iv.invokespecial("java/lang/Object", "<init>", "()V", false)
}
else {
val ctorDesc = superclass.unsubstitutedPrimaryConstructor
?: throw RuntimeException("Primary constructor not found for script template " + superclass.toString())
iv.load(0, classType)
fun Int.incrementIf(cond: Boolean): Int = if (cond) plus(1) else this
val valueParamStart = 1
.incrementIf(scriptContext.earlierScripts.isNotEmpty())
.incrementIf(scriptDefinition.implicitReceivers.isNotEmpty())
.incrementIf(scriptDefinition.environmentVariables.isNotEmpty())
val valueParameters = scriptDescriptor.unsubstitutedPrimaryConstructor.valueParameters
for (superclassParam in ctorDesc.valueParameters) {
val valueParam = valueParameters.first { it.name == superclassParam.name }
iv.load(valueParam!!.index + valueParamStart, typeMapper.mapType(valueParam.type))
}
val ctorMethod = typeMapper.mapToCallableMethod(ctorDesc, false)
val sig = ctorMethod.getAsmMethod().descriptor
iv.invokespecial(
typeMapper.mapSupertype(superclass.defaultType, null).internalName,
"<init>", sig, false)
}
iv.load(0, classType)
val frameMap = FrameMap()
frameMap.enterTemp(OBJECT_TYPE)
@@ -157,6 +124,37 @@ class ScriptCodegen private constructor(
}
}
if (superclass == null) {
iv.load(0, classType)
iv.invokespecial("java/lang/Object", "<init>", "()V", false)
} else {
val ctorDesc = superclass.unsubstitutedPrimaryConstructor
?: throw RuntimeException("Primary constructor not found for script template " + superclass.toString())
iv.load(0, classType)
fun Int.incrementIf(cond: Boolean): Int = if (cond) plus(1) else this
val valueParamStart = 1
.incrementIf(scriptContext.earlierScripts.isNotEmpty())
val valueParameters = scriptDescriptor.unsubstitutedPrimaryConstructor.valueParameters
for (superclassParam in ctorDesc.valueParameters) {
val valueParam = valueParameters.first { it.name == superclassParam.name }
val paramType = typeMapper.mapType(valueParam.type)
iv.load(valueParam!!.index + valueParamStart, paramType)
frameMap.enterTemp(paramType)
}
val ctorMethod = typeMapper.mapToCallableMethod(ctorDesc, false)
val sig = ctorMethod.getAsmMethod().descriptor
iv.invokespecial(
typeMapper.mapSupertype(superclass.defaultType, null).internalName,
"<init>", sig, false
)
}
iv.load(0, classType)
if (scriptDefinition.implicitReceivers.isNotEmpty()) {
val receiversParamIndex = frameMap.enterTemp(AsmUtil.getArrayType(OBJECT_TYPE))
@@ -1607,9 +1607,7 @@ public class KotlinTypeMapper {
@NotNull
public JvmMethodSignature mapScriptSignature(
@NotNull ScriptDescriptor script,
@NotNull List<ScriptDescriptor> importedScripts,
List<? extends KType> implicitReceivers,
List<? extends Pair<String, ? extends KType>> environmentVariables
@NotNull List<ScriptDescriptor> importedScripts
) {
JvmSignatureWriter sw = new BothSignatureWriter(BothSignatureWriter.Mode.METHOD);
@@ -1619,14 +1617,6 @@ public class KotlinTypeMapper {
writeParameter(sw, DescriptorUtilsKt.getModule(script).getBuiltIns().getArray().getDefaultType(), null);
}
if (implicitReceivers.size() > 0) {
writeParameter(sw, DescriptorUtilsKt.getModule(script).getBuiltIns().getArray().getDefaultType(), null);
}
if (environmentVariables.size() > 0) {
writeParameter(sw, DescriptorUtilsKt.getModule(script).getBuiltIns().getMap().getDefaultType(), null);
}
for (ValueParameterDescriptor valueParameter : script.getUnsubstitutedPrimaryConstructor().getValueParameters()) {
writeParameter(sw, valueParameter.getType(), /* callableDescriptor = */ null);
}
@@ -16,14 +16,12 @@
package org.jetbrains.kotlin.script
import org.jetbrains.kotlin.descriptors.NotFoundClasses
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.findNonGenericClassAcrossDependencies
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.lazy.descriptors.script.classId
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.types.*
import kotlin.reflect.KClass
@@ -33,19 +31,27 @@ import kotlin.reflect.KVariance
import kotlin.reflect.full.primaryConstructor
fun KotlinScriptDefinition.getScriptParameters(scriptDescriptor: ScriptDescriptor): List<ScriptParameter> =
template.primaryConstructor?.parameters
?.map { ScriptParameter(Name.identifier(it.name!!), getKotlinTypeByKType(scriptDescriptor, it.type)) }
?: emptyList()
template.primaryConstructor?.parameters
?.map { ScriptParameter(Name.identifier(it.name!!), getKotlinTypeByKType(scriptDescriptor, it.type)) }
?: emptyList()
fun getKotlinTypeByKClass(scriptDescriptor: ScriptDescriptor, kClass: KClass<out Any>): KotlinType =
getKotlinTypeByFqName(scriptDescriptor,
kClass.qualifiedName ?: throw RuntimeException("Cannot get FQN from $kClass"))
getClassDescriptorByKClassOrMock(scriptDescriptor, kClass).defaultType
private fun getKotlinTypeByFqName(scriptDescriptor: ScriptDescriptor, fqName: String): KotlinType =
scriptDescriptor.module.findNonGenericClassAcrossDependencies(
ClassId.topLevel(FqName(fqName)),
NotFoundClasses(LockBasedStorageManager.NO_LOCKS, scriptDescriptor.module)
).defaultType
fun getClassDescriptorByKClass(scriptDescriptor: ScriptDescriptor, kClass: KClass<out Any>): ClassDescriptor? =
scriptDescriptor.module.findClassAcrossModuleDependencies(kClass.classId)
fun getMockClassDescriptor(scriptDescriptor: ScriptDescriptor, kClass: KClass<out Any>): ClassDescriptor {
val classId = kClass.classId
val typeParametersCount = generateSequence(classId, ClassId::getOuterClassId).map { 0 }.toList()
return NotFoundClasses(LockBasedStorageManager.NO_LOCKS, scriptDescriptor.module).getClass(classId, typeParametersCount)
}
fun getClassDescriptorByKClassOrMock(scriptDescriptor: ScriptDescriptor, kClass: KClass<out Any>): ClassDescriptor =
scriptDescriptor.module.findNonGenericClassAcrossDependencies(
kClass.classId,
NotFoundClasses(LockBasedStorageManager.NO_LOCKS, scriptDescriptor.module)
)
// TODO: support star projections
// TODO: support annotations on types and type parameters
@@ -98,6 +98,7 @@ public interface Errors {
DiagnosticFactory2<PsiElement, String, String> API_NOT_AVAILABLE = DiagnosticFactory2.create(ERROR);
DiagnosticFactory1<PsiElement, FqName> MISSING_DEPENDENCY_CLASS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, String> MISSING_SCRIPT_BASE_CLASS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, String> MISSING_SCRIPT_RECEIVER_CLASS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, String> MISSING_SCRIPT_ENVIRONMENT_PROPERTY_CLASS = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, String> PRE_RELEASE_CLASS = DiagnosticFactory1.create(ERROR);
@@ -357,6 +357,7 @@ public class DefaultErrorMessages {
MAP.put(API_NOT_AVAILABLE, "This declaration is only available since Kotlin {0} and cannot be used with the specified API version {1}", STRING, STRING);
MAP.put(MISSING_DEPENDENCY_CLASS, "Cannot access class ''{0}''. Check your module classpath for missing or conflicting dependencies", TO_STRING);
MAP.put(MISSING_SCRIPT_BASE_CLASS, "Cannot access script base class ''{0}''. Check your module classpath for missing or conflicting dependencies", TO_STRING);
MAP.put(MISSING_SCRIPT_RECEIVER_CLASS, "Cannot access implicit script receiver class ''{0}''. Check your module classpath for missing or conflicting dependencies", TO_STRING);
MAP.put(MISSING_SCRIPT_ENVIRONMENT_PROPERTY_CLASS, "Cannot access script environment property class ''{0}''. Check your module classpath for missing or conflicting dependencies", TO_STRING);
MAP.put(PRE_RELEASE_CLASS, "{0} is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler", TO_STRING);
@@ -22,9 +22,11 @@ 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.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider
import org.jetbrains.kotlin.script.ScriptHelper
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
class LazyScriptClassMemberScope(
resolveSession: ResolveSession,
@@ -33,34 +35,67 @@ class LazyScriptClassMemberScope(
trace: BindingTrace
) : LazyClassMemberScope(resolveSession, declarationProvider, scriptDescriptor, trace) {
override fun resolvePrimaryConstructor(): ClassConstructorDescriptor? {
val constructor = ClassConstructorDescriptorImpl.create(
scriptDescriptor,
Annotations.EMPTY,
true,
SourceElement.NO_SOURCE
)
constructor.initialize(
createScriptParameters(constructor),
Visibilities.PUBLIC
)
setDeferredReturnType(constructor)
return constructor
private val scriptPrimaryConstructor: () -> ClassConstructorDescriptorImpl? = resolveSession.storageManager.createNullableLazyValue {
val baseClass = scriptDescriptor.baseClassDescriptor()
val baseConstructorDescriptor = baseClass?.unsubstitutedPrimaryConstructor
if (baseConstructorDescriptor != null) {
val builtIns = scriptDescriptor.builtIns
val implicitReceiversParamType =
if (scriptDescriptor.scriptDefinition().implicitReceivers.isEmpty()) null
else {
"implicitReceivers" to builtIns.array.substitute(builtIns.anyType)!!
}
val environmentVarsParamType =
if (scriptDescriptor.scriptDefinition().environmentVariables.isEmpty()) null
else {
"environmentVariables" to builtIns.map.substitute(builtIns.stringType, builtIns.nullableAnyType)!!
}
val annotations = baseConstructorDescriptor.annotations
val constructorDescriptor = ClassConstructorDescriptorImpl.create(
scriptDescriptor, annotations, baseConstructorDescriptor.isPrimary, scriptDescriptor.source
)
var paramsIndexBase = baseConstructorDescriptor.valueParameters.let { if (it.isEmpty()) 0 else it.last().index + 1 }
val syntheticParameters =
listOf(implicitReceiversParamType, environmentVarsParamType).mapNotNull { param: Pair<String, KotlinType>? ->
if (param == null) null
else 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
}
}
private fun createScriptParameters(constructor: ClassConstructorDescriptorImpl): List<ValueParameterDescriptor> {
return ScriptHelper.getInstance().getScriptParameters(scriptDescriptor.scriptDefinition, scriptDescriptor)
.mapIndexed { index, (name, type) ->
ValueParameterDescriptorImpl(
constructor, null, index, Annotations.EMPTY, name, type,
/* declaresDefaultValue = */ false,
/* isCrossinline = */ false,
/* isNoinline = */ false,
null, SourceElement.NO_SOURCE
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 createPropertiesFromPrimaryConstructorParameters(name: Name, result: MutableSet<PropertyDescriptor>) {
}
}
private fun ClassDescriptor.substitute(vararg types: KotlinType): KotlinType? =
KotlinTypeFactory.simpleType(this.defaultType, arguments = types.map { it.asTypeProjection() })
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
@@ -31,14 +30,15 @@ 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.script.ScriptEnvironmentDescriptor
import org.jetbrains.kotlin.resolve.scopes.*
import org.jetbrains.kotlin.resolve.lazy.descriptors.script.classId
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.ScriptHelper
import org.jetbrains.kotlin.script.ScriptPriorities
import org.jetbrains.kotlin.script.getScriptDefinition
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.utils.ifEmpty
import kotlin.reflect.KClass
import kotlin.reflect.KType
@@ -66,11 +66,10 @@ class LazyScriptDescriptor(
override fun getPriority() = priority
val scriptDefinition: KotlinScriptDefinition
by lazy {
val file = scriptInfo.script.containingKtFile
getScriptDefinition(file) ?: throw RuntimeException("file ${file.name} is not a script")
}
val scriptDefinition: () -> KotlinScriptDefinition = resolveSession.storageManager.createLazyValue {
val file = scriptInfo.script.containingKtFile
getScriptDefinition(file) ?: throw RuntimeException("file ${file.name} is not a script")
}
override fun substitute(substitutor: TypeSubstitutor) = this
@@ -88,28 +87,39 @@ class LazyScriptDescriptor(
override fun getUnsubstitutedPrimaryConstructor() = super.getUnsubstitutedPrimaryConstructor()!!
override fun computeSupertypes() =
listOf(ScriptHelper.getInstance().getKotlinType(this, scriptDefinition.template)).ifEmpty { listOf(builtIns.anyType) }
internal val baseClassDescriptor: () -> ClassDescriptor? = resolveSession.storageManager.createNullableLazyValue {
findTypeDescriptor(scriptDefinition().template, Errors.MISSING_SCRIPT_BASE_CLASS)
}
override fun computeSupertypes() = listOf(baseClassDescriptor()?.defaultType ?: builtIns.anyType)
private val scriptImplicitReceivers: () -> List<ClassDescriptor> = resolveSession.storageManager.createLazyValue {
scriptDefinition.implicitReceivers.mapNotNull { receiver ->
scriptDefinition().implicitReceivers.mapNotNull { receiver ->
findTypeDescriptor(receiver, Errors.MISSING_SCRIPT_RECEIVER_CLASS)
}
}
internal fun findTypeDescriptor(type: KType, errorDiagnostic: DiagnosticFactory1<PsiElement, String>): ClassDescriptor? {
val receiverClassId = type.classId
return receiverClassId?.let {
module.findClassAcrossModuleDependencies(it)
} ?: also {
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) {
// 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,
receiverClassId?.asSingleFqName()?.toString() ?: type.toString()
classId?.asSingleFqName()?.toString() ?: typeName
)
)
}
return typeDescriptor
}
override fun getImplicitReceivers(): List<ClassDescriptor> = scriptImplicitReceivers()
@@ -123,7 +133,7 @@ class LazyScriptDescriptor(
private val scriptOuterScope: () -> LexicalScope = resolveSession.storageManager.createLazyValue {
var outerScope = super.getOuterScope()
val outerScopeReceivers = implicitReceivers.let {
if (scriptDefinition.environmentVariables.isEmpty()) {
if (scriptDefinition().environmentVariables.isEmpty()) {
it
} else {
it + ScriptEnvironmentDescriptor(this)
@@ -143,11 +153,3 @@ class LazyScriptDescriptor(
override fun getOuterScope(): LexicalScope = scriptOuterScope()
}
private val KClass<*>.classId: ClassId
get() = this.java.enclosingClass?.kotlin?.classId?.createNestedClassId(Name.identifier(simpleName!!))
?: ClassId.topLevel(FqName(qualifiedName!!))
private val KType.classId: ClassId?
get() = classifier?.let { it as? KClass<*> }?.classId
@@ -43,7 +43,7 @@ class ScriptEnvironmentDescriptor(script: LazyScriptDescriptor) :
override fun getUnsubstitutedMemberScope(): MemberScope = memberScope()
val properties: () -> List<ScriptEnvironmentPropertyDescriptor> = script.resolveSession.storageManager.createLazyValue {
script.scriptDefinition.environmentVariables.mapNotNull { (name, type) ->
script.scriptDefinition().environmentVariables.mapNotNull { (name, type) ->
script.findTypeDescriptor(type, Errors.MISSING_SCRIPT_ENVIRONMENT_PROPERTY_CLASS)?.let {
name to it
}
@@ -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.resolve.lazy.descriptors.script
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
@@ -84,7 +84,7 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
override fun getPresentableName() = KOTLIN_BUILDER_NAME
override fun getCompilableFileExtensions() = arrayListOf("kt")
override fun getCompilableFileExtensions() = arrayListOf("kt", "kts")
override fun buildStarted(context: CompileContext) {
LOG.debug("==========================================")