diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java index 0e1dbcc6795..bd98062d1df 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java @@ -32,9 +32,11 @@ import org.jetbrains.kotlin.resolve.annotations.AnnotationUtilKt; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt; import org.jetbrains.kotlin.resolve.constants.ConstantValue; +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.serialization.deserialization.descriptors.DeserializedPropertyDescriptor; import org.jetbrains.kotlin.storage.LockBasedStorageManager; import org.jetbrains.kotlin.types.ErrorUtils; @@ -486,13 +488,13 @@ public class PropertyCodegen { return false; } - private void generateGetter(@Nullable KtNamedDeclaration p, @NotNull PropertyDescriptor descriptor, @Nullable KtPropertyAccessor getter) { + public void generateGetter(@Nullable KtNamedDeclaration p, @NotNull PropertyDescriptor descriptor, @Nullable KtPropertyAccessor getter) { generateAccessor(p, getter, descriptor.getGetter() != null ? descriptor.getGetter() : DescriptorFactory.createDefaultGetter(descriptor, Annotations.Companion.getEMPTY())); } - private void generateSetter(@Nullable KtNamedDeclaration p, @NotNull PropertyDescriptor descriptor, @Nullable KtPropertyAccessor setter) { + public void generateSetter(@Nullable KtNamedDeclaration p, @NotNull PropertyDescriptor descriptor, @Nullable KtPropertyAccessor setter) { if (!descriptor.isVar()) return; generateAccessor(p, setter, descriptor.getSetter() != null @@ -513,7 +515,10 @@ public class PropertyCodegen { FunctionGenerationStrategy strategy; if (accessor == null || !accessor.hasBody()) { - if (p instanceof KtProperty && ((KtProperty) p).hasDelegate()) { + if (accessorDescriptor.getCorrespondingProperty() instanceof ScriptEnvironmentPropertyDescriptor) { + strategy = new ScriptEnvPropertyAccessorStrategy(state, accessorDescriptor); + } + else if (p instanceof KtProperty && ((KtProperty) p).hasDelegate()) { strategy = new DelegatedPropertyAccessorStrategy(state, accessorDescriptor); } else { @@ -626,6 +631,42 @@ 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"; + private final PropertyAccessorDescriptor propertyAccessorDescriptor; + + public ScriptEnvPropertyAccessorStrategy(@NotNull GenerationState state, @NotNull PropertyAccessorDescriptor descriptor) { + super(state); + this.propertyAccessorDescriptor = descriptor; + } + + @Override + 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()); + if (propertyAccessorDescriptor instanceof PropertyGetterDescriptor) { + v.visitMethodInsn(Opcodes.INVOKEINTERFACE, MAP_IFACE_NAME, "get", + Type.getMethodDescriptor(AsmTypes.OBJECT_TYPE, AsmTypes.OBJECT_TYPE), true); + } + 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", + Type.getMethodDescriptor(Type.BOOLEAN_TYPE, AsmTypes.OBJECT_TYPE, AsmTypes.OBJECT_TYPE), true); + } + Type returnType = state.getTypeMapper().mapReturnType(propertyAccessorDescriptor); + StackValue.coerce(AsmTypes.OBJECT_TYPE, returnType, v); + v.areturn(returnType); + } + } + public void genDelegate(@NotNull PropertyDescriptor delegate, @NotNull PropertyDescriptor delegateTo, @NotNull StackValue field) { ClassDescriptor toClass = (ClassDescriptor) delegateTo.getContainingDeclaration(); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt index 8866a59354b..f8e255e0ada 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ScriptCodegen.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.descriptors.ScriptDescriptor import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.* +import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.* import org.jetbrains.org.objectweb.asm.Type @@ -53,6 +54,10 @@ class ScriptCodegen private constructor( override fun generateSyntheticPartsBeforeBody() { generatePropertyMetadataArrayFieldIfNeeded(classAsmType) + scriptContext.scriptDescriptor.scriptEnvironmentProperties.forEach { + propertyCodegen.generateGetter(null, it, null) + propertyCodegen.generateSetter(null, it, null) + } } override fun generateSyntheticPartsAfterBody() {} @@ -68,7 +73,12 @@ class ScriptCodegen private constructor( ) { val scriptDefinition = scriptContext.script.kotlinScriptDefinition.value - val jvmSignature = typeMapper.mapScriptSignature(scriptDescriptor, scriptContext.earlierScripts, scriptDefinition.implicitReceivers) + val jvmSignature = typeMapper.mapScriptSignature( + scriptDescriptor, + scriptContext.earlierScripts, + scriptDefinition.implicitReceivers, + scriptDefinition.environmentVariables + ) if (state.replSpecific.shouldGenerateScriptResultValue) { val resultFieldInfo = scriptContext.resultFieldInfo @@ -108,6 +118,7 @@ class ScriptCodegen private constructor( 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) { @@ -155,6 +166,14 @@ class ScriptCodegen private constructor( } } + if (scriptDefinition.environmentVariables.isNotEmpty()) { + val envParamIndex = frameMap.enterTemp(AsmTypes.OBJECT_TYPE) + val mapType = Type.getObjectType("java/util/Map") + iv.load(0, classType) + iv.load(envParamIndex, mapType) + iv.putfield(classType.internalName, "environment", mapType.descriptor) + } + val codegen = ExpressionCodegen(mv, frameMap, Type.VOID_TYPE, methodContext, state, this) generateInitializers { codegen } @@ -187,6 +206,16 @@ class ScriptCodegen private constructor( null ) } + if (scriptContext.scriptDescriptor.scriptEnvironmentProperties.isNotEmpty()) { + classBuilder.newField( + NO_ORIGIN, + ACC_PUBLIC or ACC_FINAL, + "environment", + Type.getObjectType("java/util/Map").descriptor, + null, + null + ) + } } private fun genMembers() { 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 7513da3a99a..212a7e23043 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/ScriptContext.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/ScriptContext.kt @@ -23,7 +23,6 @@ import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ScriptDescriptor -import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -32,9 +31,8 @@ import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtScript import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.jvm.AsmTypes -import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyScriptDescriptor +import org.jetbrains.kotlin.resolve.lazy.descriptors.ScriptEnvironmentDescriptor import org.jetbrains.org.objectweb.asm.Type import kotlin.reflect.KClass @@ -75,6 +73,9 @@ class ScriptContext( } fun getOuterReceiverExpression(prefix: StackValue?, thisOrOuterClass: ClassDescriptor): StackValue { + if (thisOrOuterClass is ScriptEnvironmentDescriptor) { + return prefix ?: StackValue.LOCAL_0 + } receiverDescriptors.forEachIndexed { index, outerReceiver -> if (outerReceiver == thisOrOuterClass) { return getImplicitReceiverType(index)?.let { type -> diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java index 4e035c7dd1d..8e03a95c0e6 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java @@ -1586,7 +1586,8 @@ public class KotlinTypeMapper { public JvmMethodSignature mapScriptSignature( @NotNull ScriptDescriptor script, @NotNull List importedScripts, - List implicitReceivers + List implicitReceivers, + List> environmentVariables ) { JvmSignatureWriter sw = new BothSignatureWriter(BothSignatureWriter.Mode.METHOD); @@ -1600,6 +1601,10 @@ public class KotlinTypeMapper { 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); } 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 b4baf75f4fd..b67df09f5a3 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 @@ -102,7 +102,7 @@ class LazyScriptDescriptor( } } - private fun findTypeDescriptor(type: KType, errorDiagnostic: DiagnosticFactory1): ClassDescriptor? { + internal fun findTypeDescriptor(type: KType, errorDiagnostic: DiagnosticFactory1): ClassDescriptor? { val receiverClassId = type.classifier?.let { it as? KClass<*> }?.classId return receiverClassId?.let { module.findClassAcrossModuleDependencies(it) @@ -119,15 +119,11 @@ class LazyScriptDescriptor( override fun getImplicitReceivers(): List = scriptImplicitReceivers() - private val scriptEnvironmentProperties: () -> List> = resolveSession.storageManager.createLazyValue { - scriptDefinition.environmentVariables.mapNotNull { (name, type) -> - findTypeDescriptor(type, Errors.MISSING_SCRIPT_ENVIRONMENT_PROPERTY_CLASS)?.let { - name to it - } - } + private val scriptEnvironment: () -> ScriptEnvironmentDescriptor = resolveSession.storageManager.createLazyValue { + ScriptEnvironmentDescriptor(this) } - fun getScriptEnvironmentProperties(): List> = scriptEnvironmentProperties() + override fun getScriptEnvironmentProperties(): List = scriptEnvironment().properties private val scriptOuterScope: () -> LexicalScope = resolveSession.storageManager.createLazyValue { var outerScope = super.getOuterScope() @@ -160,7 +156,7 @@ private val KClass<*>.classId: ClassId private val KType.classId: ClassId? get() = classifier?.let { it as? KClass<*> }?.classId -private class ScriptEnvironmentDescriptor(script: LazyScriptDescriptor) : +class ScriptEnvironmentDescriptor(script: LazyScriptDescriptor) : MutableClassDescriptor( script, ClassKind.CLASS, false, false, Name.special(""), SourceElement.NO_SOURCE, LockBasedStorageManager.NO_LOCKS @@ -176,23 +172,32 @@ private class ScriptEnvironmentDescriptor(script: LazyScriptDescriptor) : private val memberScope by lazy { ScriptEnvironmentMemberScope( script.name.identifier, - script.getScriptEnvironmentProperties().map { - makeEnvironmentPropertyDescriptor( - Name.identifier(it.first), - it.second, - true, - script - ) - } + 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 + private val environmentProperties: List ) : MemberScopeImpl() { override fun getContributedDescriptors( kindFilter: DescriptorKindFilter, @@ -208,27 +213,35 @@ private class ScriptEnvironmentMemberScope( } } -private fun ScriptEnvironmentDescriptor.makeEnvironmentPropertyDescriptor(name: Name, typeDescriptor: ClassDescriptor, isVar: Boolean, script: LazyScriptDescriptor) = - PropertyDescriptorImpl.create( - script.containingDeclaration, - Annotations.EMPTY, Modality.FINAL, Visibilities.PUBLIC, - isVar, - name, - CallableMemberDescriptor.Kind.SYNTHESIZED, - SourceElement.NO_SOURCE, - /* lateInit = */ false, /* isConst = */ false, /* isExpect = */ false, /* isActual = */ false, /* isExternal = */ false, - /* isDelegated = */ true - ).also { - it.setType(typeDescriptor.defaultType, emptyList(), thisAsReceiverParameter, null as KotlinType?) - it.initialize( - it.makePropertyGetterDescriptor(), - if (!isVar) null else it.makePropertySetterDescriptor() +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.FINAL, Visibilities.PUBLIC, + this, Annotations.EMPTY, Modality.OPEN, Visibilities.PUBLIC, /* isDefault = */ false, /* isExternal = */ false, /* isInline = */ false, CallableMemberDescriptor.Kind.SYNTHESIZED, null, SourceElement.NO_SOURCE ).also { @@ -237,7 +250,7 @@ private fun PropertyDescriptorImpl.makePropertyGetterDescriptor() = private fun PropertyDescriptorImpl.makePropertySetterDescriptor() = PropertySetterDescriptorImpl( - this, Annotations.EMPTY, Modality.FINAL, Visibilities.PUBLIC, + this, Annotations.EMPTY, Modality.OPEN, Visibilities.PUBLIC, /* isDefault = */ false, /* isExternal = */ false, /* isInline = */ false, CallableMemberDescriptor.Kind.SYNTHESIZED, null, SourceElement.NO_SOURCE ).also { diff --git a/compiler/testData/codegen/customScript/simpleEnvVars.kts b/compiler/testData/codegen/customScript/simpleEnvVars.kts new file mode 100644 index 00000000000..c1508062fc1 --- /dev/null +++ b/compiler/testData/codegen/customScript/simpleEnvVars.kts @@ -0,0 +1,8 @@ + +// KOTLIN_SCRIPT_DEFINITION: org.jetbrains.kotlin.codegen.TestScriptWithSimpleEnvVars + +// envVar: stringVar1 = abracadabra + +val res = stringVar1.drop(4) + +// expected: res = cadabra diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCustomScriptCodegenTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCustomScriptCodegenTest.kt index 4e4fb95d27d..554255ecb13 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCustomScriptCodegenTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractCustomScriptCodegenTest.kt @@ -212,3 +212,13 @@ object TestScriptWithReceiversConfiguration : ArrayList, Any?>> @KotlinScriptDefaultCompilationConfiguration(TestScriptWithReceiversConfiguration::class) abstract class TestScriptWithReceivers +object TestScriptWithSimpleEnvVarsConfiguration : ArrayList, Any?>>( + listOf( + ScriptCompileConfigurationProperties.contextVariables("stringVar1" to String::class.starProjectedType) + ) +) + +@Suppress("unused") +@KotlinScript +@KotlinScriptDefaultCompilationConfiguration(TestScriptWithSimpleEnvVarsConfiguration::class) +abstract class TestScriptWithSimpleEnvVars diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CustomScriptCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CustomScriptCodegenTestGenerated.java index 2ac780f31d6..c381c528316 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CustomScriptCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CustomScriptCodegenTestGenerated.java @@ -29,6 +29,11 @@ public class CustomScriptCodegenTestGenerated extends AbstractCustomScriptCodege KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/customScript"), Pattern.compile("^(.+)\\.kts$"), TargetBackend.ANY, true); } + @TestMetadata("simpleEnvVars.kts") + public void testSimpleEnvVars() throws Exception { + runTest("compiler/testData/codegen/customScript/simpleEnvVars.kts"); + } + @TestMetadata("stringReceiver.kts") public void testStringReceiver() throws Exception { runTest("compiler/testData/codegen/customScript/stringReceiver.kts"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/ScriptDescriptor.java b/core/descriptors/src/org/jetbrains/kotlin/descriptors/ScriptDescriptor.java index 1d618c09ba3..9da0192066c 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/ScriptDescriptor.java +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/ScriptDescriptor.java @@ -29,4 +29,7 @@ public interface ScriptDescriptor extends ClassDescriptor { @NotNull List getImplicitReceivers(); + + @NotNull + List getScriptEnvironmentProperties(); }