diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java index bd98062d1df..ce2caf9a5ed 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java @@ -36,7 +36,7 @@ import org.jetbrains.kotlin.resolve.jvm.AsmTypes; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; -import org.jetbrains.kotlin.resolve.lazy.descriptors.ScriptEnvironmentPropertyDescriptor; +import org.jetbrains.kotlin.resolve.lazy.descriptors.script.ScriptEnvironmentPropertyDescriptor; import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor; import org.jetbrains.kotlin.storage.LockBasedStorageManager; import org.jetbrains.kotlin.types.ErrorUtils; @@ -631,9 +631,9 @@ public class PropertyCodegen { } } - private static class ScriptEnvPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased { - public static final String MAP_IFACE_NAME = "java/util/Map"; - public static final String MAP_FIELD_NAME = "environment"; + static class ScriptEnvPropertyAccessorStrategy extends FunctionGenerationStrategy.CodegenBased { + public static final Type MAP_IFACE_TYPE = Type.getObjectType("java/util/Map"); + public static final String MAP_FIELD_NAME = "$scriptEnvironment"; private final PropertyAccessorDescriptor propertyAccessorDescriptor; public ScriptEnvPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) { @@ -645,20 +645,20 @@ public class PropertyCodegen { public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) { InstructionAdapter v = codegen.v; - String intScriptName = - state.getTypeMapper().mapClass((ClassifierDescriptor)(propertyAccessorDescriptor.getCorrespondingProperty().getContainingDeclaration())).getClassName(); - v.visitVarInsn(Opcodes.ALOAD, 0); - v.visitFieldInsn(Opcodes.GETFIELD, intScriptName, MAP_FIELD_NAME, Type.getObjectType(MAP_IFACE_NAME).getDescriptor()); - v.visitLdcInsn(propertyAccessorDescriptor.getCorrespondingProperty().getName().asString()); + Type scriptType = state.getTypeMapper().mapOwner(propertyAccessorDescriptor); + String scriptInternalName = scriptType.getInternalName(); + v.load(0, scriptType); + v.getfield(scriptInternalName, MAP_FIELD_NAME, MAP_IFACE_TYPE.getDescriptor()); + v.aconst(propertyAccessorDescriptor.getCorrespondingProperty().getName().asString()); if (propertyAccessorDescriptor instanceof PropertyGetterDescriptor) { - v.visitMethodInsn(Opcodes.INVOKEINTERFACE, MAP_IFACE_NAME, "get", - Type.getMethodDescriptor(AsmTypes.OBJECT_TYPE, AsmTypes.OBJECT_TYPE), true); + v.invokeinterface(MAP_IFACE_TYPE.getInternalName(), "get", + Type.getMethodDescriptor(AsmTypes.OBJECT_TYPE, AsmTypes.OBJECT_TYPE)); } else { Type valueType = state.getTypeMapper().mapType(propertyAccessorDescriptor.getCorrespondingProperty()); v.load(1, valueType); StackValue.coerce(valueType, AsmTypes.OBJECT_TYPE, v); - v.visitMethodInsn(Opcodes.INVOKEINTERFACE, MAP_IFACE_NAME, "set", + v.visitMethodInsn(Opcodes.INVOKEINTERFACE, MAP_IFACE_TYPE.getInternalName(), "put", Type.getMethodDescriptor(Type.BOOLEAN_TYPE, AsmTypes.OBJECT_TYPE, AsmTypes.OBJECT_TYPE), true); } Type returnType = state.getTypeMapper().mapReturnType(propertyAccessorDescriptor); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt index f8e255e0ada..75db2fba440 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt @@ -168,10 +168,10 @@ class ScriptCodegen private constructor( if (scriptDefinition.environmentVariables.isNotEmpty()) { val envParamIndex = frameMap.enterTemp(AsmTypes.OBJECT_TYPE) - val mapType = Type.getObjectType("java/util/Map") + val mapType = PropertyCodegen.ScriptEnvPropertyAccessorStrategy.MAP_IFACE_TYPE iv.load(0, classType) iv.load(envParamIndex, mapType) - iv.putfield(classType.internalName, "environment", mapType.descriptor) + iv.putfield(classType.internalName, PropertyCodegen.ScriptEnvPropertyAccessorStrategy.MAP_FIELD_NAME, mapType.descriptor) } val codegen = ExpressionCodegen(mv, frameMap, Type.VOID_TYPE, methodContext, state, this) @@ -210,8 +210,8 @@ class ScriptCodegen private constructor( classBuilder.newField( NO_ORIGIN, ACC_PUBLIC or ACC_FINAL, - "environment", - Type.getObjectType("java/util/Map").descriptor, + PropertyCodegen.ScriptEnvPropertyAccessorStrategy.MAP_FIELD_NAME, + PropertyCodegen.ScriptEnvPropertyAccessorStrategy.MAP_IFACE_TYPE.descriptor, null, null ) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/ScriptContext.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/ScriptContext.kt index 212a7e23043..e953d5cef24 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/ScriptContext.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/ScriptContext.kt @@ -32,7 +32,7 @@ import org.jetbrains.kotlin.psi.KtScript import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.jvm.AsmTypes -import org.jetbrains.kotlin.resolve.lazy.descriptors.ScriptEnvironmentDescriptor +import org.jetbrains.kotlin.resolve.lazy.descriptors.script.ScriptEnvironmentDescriptor import org.jetbrains.org.objectweb.asm.Type import kotlin.reflect.KClass diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt index 7615e2cd8f6..d2cead9445c 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt @@ -412,7 +412,7 @@ class KotlinCoreEnvironment private constructor( setCompatibleBuild() // If not disabled explicitly, we should always support at least the standard script definition if (!configuration.getBoolean(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION) && - !configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS).contains(StandardScriptDefinition) + StandardScriptDefinition !in configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS) ) { configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition) } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.172 b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.172 index 219d2ea5dfc..630f9df6ff7 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.172 +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.172 @@ -205,9 +205,6 @@ class KotlinCoreEnvironment private constructor( }) sourceFiles.sortBy { it.virtualFile.path } - // We should always support at least the standard script definition - configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition) - val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider if (scriptDefinitionProvider != null) { scriptDefinitionProvider.setScriptDefinitionsSources( @@ -411,6 +408,11 @@ class KotlinCoreEnvironment private constructor( parentDisposable: Disposable, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles ): KotlinCoreEnvironment { setCompatibleBuild() + if (!configuration.getBoolean(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION) && + StandardScriptDefinition !in configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS) + ) { + configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition) + } val appEnv = getOrCreateApplicationEnvironmentForProduction(configuration) // Disposing of the environment is unsafe in production then parallel builds are enabled, but turning it off universally // breaks a lot of tests, therefore it is disabled for production and enabled for tests @@ -445,7 +447,10 @@ class KotlinCoreEnvironment private constructor( parentDisposable: Disposable, initialConfiguration: CompilerConfiguration, extensionConfigs: EnvironmentConfigFiles ): KotlinCoreEnvironment { val configuration = initialConfiguration.copy() - if (configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS).isEmpty()) { + // in tests we assume that standard definition should only be added if no other explicit defs are already added + if (!configuration.getBoolean(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION) && + configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS).isEmpty() + ) { configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition) } // Tests are supposed to create a single project and dispose it right after use diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.173 b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.173 index 43ec8562d30..575c7947fa0 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.173 +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt.173 @@ -207,9 +207,6 @@ class KotlinCoreEnvironment private constructor( }) sourceFiles.sortBy { it.virtualFile.path } - // We should always support at least the standard script definition - configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition) - val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? CliScriptDefinitionProvider if (scriptDefinitionProvider != null) { scriptDefinitionProvider.setScriptDefinitionsSources( @@ -413,6 +410,11 @@ class KotlinCoreEnvironment private constructor( parentDisposable: Disposable, configuration: CompilerConfiguration, configFiles: EnvironmentConfigFiles ): KotlinCoreEnvironment { setCompatibleBuild() + if (!configuration.getBoolean(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION) && + StandardScriptDefinition !in configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS) + ) { + configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition) + } val appEnv = getOrCreateApplicationEnvironmentForProduction(configuration) // Disposing of the environment is unsafe in production then parallel builds are enabled, but turning it off universally // breaks a lot of tests, therefore it is disabled for production and enabled for tests @@ -447,7 +449,10 @@ class KotlinCoreEnvironment private constructor( parentDisposable: Disposable, initialConfiguration: CompilerConfiguration, extensionConfigs: EnvironmentConfigFiles ): KotlinCoreEnvironment { val configuration = initialConfiguration.copy() - if (configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS).isEmpty()) { + // in tests we assume that standard definition should only be added if no other explicit defs are already added + if (!configuration.getBoolean(JVMConfigurationKeys.DISABLE_STANDARD_SCRIPT_DEFINITION) && + configuration.getList(JVMConfigurationKeys.SCRIPT_DEFINITIONS).isEmpty() + ) { configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, StandardScriptDefinition) } // Tests are supposed to create a single project and dispose it right after use diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 0bcd0900f9c..9e667b68598 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -98,8 +98,8 @@ public interface Errors { DiagnosticFactory2 API_NOT_AVAILABLE = DiagnosticFactory2.create(ERROR); DiagnosticFactory1 MISSING_DEPENDENCY_CLASS = DiagnosticFactory1.create(ERROR); - DiagnosticFactory1 MISSING_SCRIPT_RECEIVER_CLASS = DiagnosticFactory1.create(ERROR); - DiagnosticFactory1 MISSING_SCRIPT_ENVIRONMENT_PROPERTY_CLASS = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 MISSING_SCRIPT_RECEIVER_CLASS = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 MISSING_SCRIPT_ENVIRONMENT_PROPERTY_CLASS = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 PRE_RELEASE_CLASS = DiagnosticFactory1.create(ERROR); DiagnosticFactory2> INCOMPATIBLE_CLASS = DiagnosticFactory2.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyScriptDescriptor.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyScriptDescriptor.kt index b67df09f5a3..926302e1bcf 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyScriptDescriptor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyScriptDescriptor.kt @@ -18,11 +18,8 @@ package org.jetbrains.kotlin.resolve.lazy.descriptors import com.intellij.psi.PsiElement import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.annotations.Annotations -import org.jetbrains.kotlin.descriptors.impl.* import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1 import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -33,16 +30,14 @@ 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.script.ScriptEnvironmentDescriptor import org.jetbrains.kotlin.resolve.scopes.* 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.storage.LockBasedStorageManager -import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeSubstitutor -import org.jetbrains.kotlin.utils.Printer import org.jetbrains.kotlin.utils.ifEmpty import kotlin.reflect.KClass import kotlin.reflect.KType @@ -102,8 +97,8 @@ class LazyScriptDescriptor( } } - internal fun findTypeDescriptor(type: KType, errorDiagnostic: DiagnosticFactory1): ClassDescriptor? { - val receiverClassId = type.classifier?.let { it as? KClass<*> }?.classId + internal fun findTypeDescriptor(type: KType, errorDiagnostic: DiagnosticFactory1): ClassDescriptor? { + val receiverClassId = type.classId return receiverClassId?.let { module.findClassAcrossModuleDependencies(it) } ?: also { @@ -111,7 +106,7 @@ class LazyScriptDescriptor( resolveSession.trace.report( errorDiagnostic.on( scriptInfo.script, - receiverClassId?.asSingleFqName() ?: FqName(type.toString()) + receiverClassId?.asSingleFqName()?.toString() ?: type.toString() ) ) } @@ -123,7 +118,7 @@ class LazyScriptDescriptor( ScriptEnvironmentDescriptor(this) } - override fun getScriptEnvironmentProperties(): List = scriptEnvironment().properties + override fun getScriptEnvironmentProperties(): List = scriptEnvironment().properties() private val scriptOuterScope: () -> LexicalScope = resolveSession.storageManager.createLazyValue { var outerScope = super.getOuterScope() @@ -156,109 +151,3 @@ private val KClass<*>.classId: ClassId private val KType.classId: ClassId? get() = classifier?.let { it as? KClass<*> }?.classId -class ScriptEnvironmentDescriptor(script: LazyScriptDescriptor) : - MutableClassDescriptor( - script, ClassKind.CLASS, false, false, - Name.special(""), SourceElement.NO_SOURCE, LockBasedStorageManager.NO_LOCKS - ) { - - init { - modality = Modality.FINAL - visibility = Visibilities.PUBLIC - setTypeParameterDescriptors(emptyList()) - createTypeConstructor() - } - - private val memberScope by lazy { - ScriptEnvironmentMemberScope( - script.name.identifier, - properties - ) - } - - override fun getUnsubstitutedMemberScope(): MemberScope = memberScope - - val properties by lazy { - script.scriptDefinition.environmentVariables.mapNotNull { (name, type) -> - script.findTypeDescriptor(type, Errors.MISSING_SCRIPT_ENVIRONMENT_PROPERTY_CLASS)?.let { - name to it - } - }.map { (key, value) -> - ScriptEnvironmentPropertyDescriptor( - Name.identifier(key), - value, - thisAsReceiverParameter, - true, - script - ) - } - } -} - -private class ScriptEnvironmentMemberScope( - private val scriptId: String, - private val environmentProperties: List -) : MemberScopeImpl() { - override fun getContributedDescriptors( - kindFilter: DescriptorKindFilter, - nameFilter: (Name) -> Boolean - ): Collection = - environmentProperties.filter { kindFilter.acceptsKinds(DescriptorKindFilter.VARIABLES_MASK) && nameFilter(it.name) } - - override fun getContributedVariables(name: Name, location: LookupLocation): Collection = - environmentProperties.filter { it.name == name } - - override fun printScopeStructure(p: Printer) { - p.println("Scope of script environment: $scriptId") - } -} - -class ScriptEnvironmentPropertyDescriptor( - name: Name, - typeDescriptor: ClassDescriptor, - receiver: ReceiverParameterDescriptor?, - isVar: Boolean, - script: LazyScriptDescriptor -) : PropertyDescriptorImpl( - script, - null, - Annotations.EMPTY, Modality.OPEN, Visibilities.PUBLIC, - isVar, - name, - CallableMemberDescriptor.Kind.SYNTHESIZED, - SourceElement.NO_SOURCE, - /* lateInit = */ false, /* isConst = */ false, /* isExpect = */ false, /* isActual = */ false, /* isExternal = */ false, - /* isDelegated = */ true -) { - init { - setType(typeDescriptor.defaultType, emptyList(), receiver, null as KotlinType?) - initialize( - makePropertyGetterDescriptor(), - if (!isVar) null else makePropertySetterDescriptor() - ) - } -} - -private fun PropertyDescriptorImpl.makePropertyGetterDescriptor() = - PropertyGetterDescriptorImpl( - this, Annotations.EMPTY, Modality.OPEN, Visibilities.PUBLIC, - /* isDefault = */ false, /* isExternal = */ false, /* isInline = */ false, - CallableMemberDescriptor.Kind.SYNTHESIZED, null, SourceElement.NO_SOURCE - ).also { - it.initialize(returnType) - } - -private fun PropertyDescriptorImpl.makePropertySetterDescriptor() = - PropertySetterDescriptorImpl( - this, Annotations.EMPTY, Modality.OPEN, Visibilities.PUBLIC, - /* isDefault = */ false, /* isExternal = */ false, /* isInline = */ false, - CallableMemberDescriptor.Kind.SYNTHESIZED, null, SourceElement.NO_SOURCE - ).also { - it.initialize( - ValueParameterDescriptorImpl( - this, null, 0, Annotations.EMPTY, Name.special(""), returnType, - /* declaresDefaultValue = */ false, /* isCrossinline = */ false, /* isNoinline = */ false, - null, SourceElement.NO_SOURCE - ) - ) - } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/script/ScriptEnvironmentDescriptor.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/script/ScriptEnvironmentDescriptor.kt new file mode 100644 index 00000000000..3b3b4f9990e --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/script/ScriptEnvironmentDescriptor.kt @@ -0,0 +1,78 @@ +/* + * 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.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.lazy.descriptors.LazyScriptDescriptor +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 ScriptEnvironmentDescriptor(script: LazyScriptDescriptor) : + MutableClassDescriptor( + script, + ClassKind.CLASS, false, false, + Name.special(""), + SourceElement.NO_SOURCE, + LockBasedStorageManager.NO_LOCKS + ) { + + init { + modality = Modality.FINAL + visibility = Visibilities.PUBLIC + setTypeParameterDescriptors(emptyList()) + createTypeConstructor() + } + + private val memberScope: () -> ScriptEnvironmentMemberScope = script.resolveSession.storageManager.createLazyValue { + ScriptEnvironmentMemberScope( + script.name.identifier, + properties() + ) + } + + override fun getUnsubstitutedMemberScope(): MemberScope = memberScope() + + val properties: () -> List = script.resolveSession.storageManager.createLazyValue { + script.scriptDefinition.environmentVariables.mapNotNull { (name, type) -> + script.findTypeDescriptor(type, Errors.MISSING_SCRIPT_ENVIRONMENT_PROPERTY_CLASS)?.let { + name to it + } + }.map { (name, classDescriptor) -> + ScriptEnvironmentPropertyDescriptor( + Name.identifier(name), + classDescriptor, + thisAsReceiverParameter, + true, + script + ) + } + } + + private class ScriptEnvironmentMemberScope( + private val scriptId: String, + private val environmentProperties: List + ) : MemberScopeImpl() { + override fun getContributedDescriptors( + kindFilter: DescriptorKindFilter, + nameFilter: (Name) -> Boolean + ): Collection = + environmentProperties + + override fun getContributedVariables(name: Name, location: LookupLocation): Collection = + environmentProperties.filter { it.name == name } + + override fun printScopeStructure(p: Printer) { + p.println("Scope of script environment: $scriptId") + } + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/script/ScriptEnvironmentPropertyDescriptor.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/script/ScriptEnvironmentPropertyDescriptor.kt new file mode 100644 index 00000000000..d47fa889ee9 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/script/ScriptEnvironmentPropertyDescriptor.kt @@ -0,0 +1,90 @@ +/* + * 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.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor +import org.jetbrains.kotlin.types.KotlinType + +class ScriptEnvironmentPropertyDescriptor( + name: Name, + typeDescriptor: ClassDescriptor, + receiver: ReceiverParameterDescriptor?, + isVar: Boolean, + script: LazyScriptDescriptor +) : PropertyDescriptorImpl( + script, + null, + Annotations.EMPTY, + Modality.OPEN, + Visibilities.PUBLIC, + 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 as KotlinType?) + 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(""), + returnType, + /* declaresDefaultValue = */ + false, /* isCrossinline = */ + false, /* isNoinline = */ + false, + null, + SourceElement.NO_SOURCE + ) + ) + } \ No newline at end of file diff --git a/compiler/psi/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.kt b/compiler/psi/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.kt index d457fd16eb7..02a22242b40 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/script/KotlinScriptDefinitionProvider.kt @@ -16,10 +16,12 @@ package org.jetbrains.kotlin.script +import com.intellij.ide.highlighter.JavaFileType import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.idea.KotlinFileType import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read import kotlin.concurrent.write @@ -34,7 +36,10 @@ interface ScriptDefinitionProvider { } } -fun ScriptDefinitionProvider.findScriptDefinition(file: VirtualFile): KotlinScriptDefinition? = findScriptDefinition(file.name) +fun ScriptDefinitionProvider.findScriptDefinition(file: VirtualFile): KotlinScriptDefinition? { + assert(!file.isDirectory) { "Do not expect directory in findScriptDefinition call: $file" } + return findScriptDefinition(file.name) +} fun getScriptDefinition(file: VirtualFile, project: Project): KotlinScriptDefinition? = ScriptDefinitionProvider.getInstance(project).findScriptDefinition(file) @@ -51,10 +56,9 @@ abstract class LazyScriptDefinitionProvider : ScriptDefinitionProvider { private var _cachedDefinitions: Sequence? = null private val cachedDefinitions: Sequence get() { - // assuming it is always called under read lock - assert(lock.readLockCount > 0) + assert(lock.readLockCount > 0) { "cachedDefinitions should only be used under the read lock" } if (_cachedDefinitions == null) lock.write { - _cachedDefinitions = CashingSequence(currentDefinitions.constrainOnce()) + _cachedDefinitions = CachingSequence(currentDefinitions.constrainOnce()) } return _cachedDefinitions!! } @@ -75,38 +79,31 @@ abstract class LazyScriptDefinitionProvider : ScriptDefinitionProvider { cachedDefinitions.firstOrNull { it.isScript(fileName) } } - override fun isScript(fileName: String) = - if (nonScriptFileName(fileName)) false - else lock.read { - cachedDefinitions.any { it.isScript(fileName) } - } + override fun isScript(fileName: String) = findScriptDefinition(fileName) != null companion object { // TODO: find a common place for storing kotlin-related extensions and reuse values from it everywhere - protected val nonScriptFilenameSuffixes = arrayOf(".kt", ".java") + protected val nonScriptFilenameSuffixes = arrayOf(".${KotlinFileType.EXTENSION}", ".${JavaFileType.DEFAULT_EXTENSION}") } } -private class CashingSequence(from: Sequence) : Sequence { +private class CachingSequence(from: Sequence) : Sequence { private val lock = ReentrantReadWriteLock() private val sequenceIterator = from.iterator() private val cache = arrayListOf() - private inner class CashingIterator : Iterator { + private inner class CachingIterator : Iterator { - private val cacheIterator: Iterator = cache.iterator() - private var cacheRunOut = !cacheIterator.hasNext() + private var cacheCursor = 0 - private fun cacheHasNext() = !cacheRunOut && (cacheIterator.hasNext().also { if (!it) cacheRunOut = true }) + override fun hasNext(): Boolean = lock.read { cacheCursor < cache.size || sequenceIterator.hasNext() } - override fun hasNext(): Boolean = lock.read { cacheHasNext() || sequenceIterator.hasNext() } - - override fun next(): T = lock.read { - if (cacheHasNext()) cacheIterator.next() - else sequenceIterator.next().also { lock.write { cache.add(it) } } + override fun next(): T = lock.write { + if (cacheCursor < cache.size) cache[cacheCursor++] + else sequenceIterator.next().also { cache.add(it) } } } - override fun iterator(): Iterator = CashingIterator() + override fun iterator(): Iterator = CachingIterator() } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt index 60d0f244ba4..dbc938a28b0 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/script/ScriptDefinitionsManager.kt @@ -95,6 +95,7 @@ class ScriptDefinitionsManager(private val project: Project) : LazyScriptDefinit } private fun updateDefinitions() { + assert(lock.isWriteLocked) { "updateDefinitions should only be called under the write lock" } definitions = definitionsByContributor.values.flattenTo(mutableListOf()).asSequence() clearCache() // TODO: clear by script type/definition diff --git a/libraries/scripting/common/src/kotlin/script/experimental/basic/basicScript.kt b/libraries/scripting/common/src/kotlin/script/experimental/basic/basicScript.kt index d0ab36468a4..cadb432ab69 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/basic/basicScript.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/basic/basicScript.kt @@ -5,7 +5,7 @@ package kotlin.script.experimental.basic -import kotlin.reflect.full.primaryConstructor +import kotlin.reflect.full.createInstance import kotlin.script.experimental.annotations.KotlinScriptDefaultCompilationConfiguration import kotlin.script.experimental.api.* import kotlin.script.experimental.util.TypedKey @@ -18,15 +18,15 @@ open class AnnotationsBasedCompilationConfigurator(val environment: ScriptingEnv override val defaultConfiguration = run { val base = environment[ScriptingEnvironmentProperties.baseClass] - val cfg = base.annotations.filterIsInstance(KotlinScriptDefaultCompilationConfiguration::class.java).flatMap { + val cfg = base.annotations.filterIsInstance(KotlinScriptDefaultCompilationConfiguration::class.java).flatMap { ann -> val params = try { - it.compilationConfiguration.objectInstance ?: it.compilationConfiguration.primaryConstructor!!.callBy(emptyMap()) + ann.compilationConfiguration.objectInstance ?: ann.compilationConfiguration.createInstance() } catch (e: Throwable) { throw IllegalArgumentException(ILLEGAL_CONFIG_ANN_ARG, e) } - params.forEach { - if (it !is Pair<*, *> || it.first !is TypedKey<*>) - throw IllegalArgumentException("$ILLEGAL_CONFIG_ANN_ARG: invalid parameter $it") + params.forEach { param -> + if (param !is Pair<*, *> || param.first !is TypedKey<*>) + throw IllegalArgumentException("$ILLEGAL_CONFIG_ANN_ARG: invalid parameter $param") } params as List, Any?>> }