diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt index 5d56965aad5..17a36f9e4b2 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt @@ -207,7 +207,11 @@ object KotlinToJVMBytecodeCompiler { } } - fun compileAndExecuteScript(environment: KotlinCoreEnvironment, paths: KotlinPaths, scriptArgs: List): ExitCode { + fun compileAndExecuteScript( + environment: KotlinCoreEnvironment, + paths: KotlinPaths, + scriptArgs: List): ExitCode + { val scriptClass = compileScript(environment, paths) ?: return ExitCode.COMPILATION_ERROR val scriptConstructor = getScriptConstructor(scriptClass) @@ -248,13 +252,24 @@ object KotlinToJVMBytecodeCompiler { private fun getScriptConstructor(scriptClass: Class<*>): Constructor<*> = scriptClass.getConstructor(Array::class.java) - fun compileScript(environment: KotlinCoreEnvironment, paths: KotlinPaths): Class<*>? { + fun compileScript(environment: KotlinCoreEnvironment, paths: KotlinPaths): Class<*>? = + compileScript(environment, + { + val classPaths = arrayListOf(paths.runtimePath.toURI().toURL()) + environment.configuration.jvmClasspathRoots.mapTo(classPaths) { it.toURI().toURL() } + URLClassLoader(classPaths.toTypedArray()) + }) + + fun compileScript(environment: KotlinCoreEnvironment, parentClassLoader: ClassLoader): Class<*>? = compileScript(environment, { parentClassLoader }) + + private inline fun compileScript( + environment: KotlinCoreEnvironment, + makeParentClassLoader: () -> ClassLoader): Class<*>? { val state = analyzeAndGenerate(environment) ?: return null + val classLoader: GeneratedClassLoader try { - val classPaths = arrayListOf(paths.runtimePath.toURI().toURL()) - environment.configuration.jvmClasspathRoots.mapTo(classPaths) { it.toURI().toURL() } - val classLoader = GeneratedClassLoader(state.factory, URLClassLoader(classPaths.toTypedArray(), null)) + classLoader = GeneratedClassLoader(state.factory, makeParentClassLoader(), null) val script = environment.getSourceFiles()[0].script ?: error("Script must be parsed") return classLoader.loadClass(script.fqName.asString()) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinition.kt b/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinition.kt index cf8d8fe9f35..a26523c1f78 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinition.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/script/KotlinScriptDefinition.kt @@ -18,13 +18,19 @@ package org.jetbrains.kotlin.script import com.intellij.psi.PsiFile import com.intellij.util.PathUtil +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ScriptDescriptor +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.parsing.KotlinParserDefinition import org.jetbrains.kotlin.psi.KtScript import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.Variance +import kotlin.reflect.KClass interface KotlinScriptDefinition { fun getScriptParameters(scriptDescriptor: ScriptDescriptor): List @@ -48,8 +54,24 @@ object StandardScriptDefinition : KotlinScriptDefinition { // NOTE: for now we treat .kts files as if they have 'args: Array' parameter // this is not supposed to be final design override fun getScriptParameters(scriptDescriptor: ScriptDescriptor): List { - val builtIns = scriptDescriptor.builtIns - val arrayOfStrings = builtIns.getArrayType(Variance.INVARIANT, builtIns.stringType) - return listOf(ScriptParameter(ARGS_NAME, arrayOfStrings)) + val kc: KClass = StandardScriptDefinition::class + return makeStringListScriptParameters(scriptDescriptor, ARGS_NAME) } } + +fun makeStringListScriptParameters(scriptDescriptor: ScriptDescriptor, propertyName: Name): List { + val builtIns = scriptDescriptor.builtIns + val arrayOfStrings = builtIns.getArrayType(Variance.INVARIANT, builtIns.stringType) + return listOf(ScriptParameter(propertyName, arrayOfStrings)) +} + +fun makeReflectedClassScriptParameter(scriptDescriptor: ScriptDescriptor, propertyName: Name, kClass: KClass): ScriptParameter = + ScriptParameter(propertyName, getKotlinType(scriptDescriptor, kClass)) + +fun getKotlinType(scriptDescriptor: ScriptDescriptor, kClass: KClass): KotlinType { + val module = scriptDescriptor.module + val qualifiedName = kClass.qualifiedName ?: throw RuntimeException("Cannot get FQN from $kClass") + return module.findClassAcrossModuleDependencies(ClassId.topLevel(FqName(qualifiedName)))?.defaultType ?: + throw RuntimeException("Cannot find class $qualifiedName in the dependencies, may be it is missing in the classpath") +} + diff --git a/compiler/testData/script/fib_cp.kts b/compiler/testData/script/fib_cp.kts new file mode 100644 index 00000000000..aa8370a2b18 --- /dev/null +++ b/compiler/testData/script/fib_cp.kts @@ -0,0 +1,11 @@ +// this script expected parameter param : class { val memberNum: Int } + +fun fib(n: Int): Int { + val v = if(n < 2) 1 else fib(n-1) + fib(n-2) + System.out.println("fib($n)=$v") + return v +} + +System.out.println("num: ${param.memberNum}") +val result = fib(param.memberNum) + diff --git a/compiler/tests-common/org/jetbrains/kotlin/codegen/CodegenTestFiles.java b/compiler/tests-common/org/jetbrains/kotlin/codegen/CodegenTestFiles.java index 7d19faab38a..bf1ea058149 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/codegen/CodegenTestFiles.java +++ b/compiler/tests-common/org/jetbrains/kotlin/codegen/CodegenTestFiles.java @@ -29,7 +29,7 @@ import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.resolve.AnalyzingUtils; import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider; import org.jetbrains.kotlin.script.ScriptParameter; -import org.jetbrains.kotlin.scripts.TestScriptDefinition; +import org.jetbrains.kotlin.scripts.SimpleParamsTestScriptDefinition; import org.jetbrains.kotlin.test.KotlinTestUtils; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.Variance; @@ -163,7 +163,7 @@ public class CodegenTestFiles { KotlinScriptDefinitionProvider definitionProvider = KotlinScriptDefinitionProvider.getInstance(project); definitionProvider.addScriptDefinition( - new TestScriptDefinition( + new SimpleParamsTestScriptDefinition( ".kts", scriptParameterTypes ) diff --git a/compiler/tests-common/org/jetbrains/kotlin/scripts/TestScriptDefinition.kt b/compiler/tests-common/org/jetbrains/kotlin/scripts/TestScriptDefinitions.kt similarity index 62% rename from compiler/tests-common/org/jetbrains/kotlin/scripts/TestScriptDefinition.kt rename to compiler/tests-common/org/jetbrains/kotlin/scripts/TestScriptDefinitions.kt index 859e0029c4f..db80b714e5a 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/scripts/TestScriptDefinition.kt +++ b/compiler/tests-common/org/jetbrains/kotlin/scripts/TestScriptDefinitions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,12 +23,20 @@ import org.jetbrains.kotlin.psi.KtScript import org.jetbrains.kotlin.script.KotlinScriptDefinition import org.jetbrains.kotlin.script.ScriptNameUtil import org.jetbrains.kotlin.script.ScriptParameter +import org.jetbrains.kotlin.script.makeReflectedClassScriptParameter +import org.jetbrains.kotlin.types.KotlinType +import kotlin.reflect.KClass -class TestScriptDefinition( - val extension: String, - val parameters: List -) : KotlinScriptDefinition { - override fun getScriptParameters(scriptDescriptor: ScriptDescriptor) = parameters +abstract class BaseScriptDefinition (val extension: String) : KotlinScriptDefinition { override fun isScript(file: PsiFile): Boolean = file.name.endsWith(extension) override fun getScriptName(script: KtScript): Name = ScriptNameUtil.fileNameWithExtensionStripped(script, extension) -} \ No newline at end of file +} + +class SimpleParamsTestScriptDefinition(extension: String, val parameters: List) : BaseScriptDefinition(extension) { + override fun getScriptParameters(scriptDescriptor: ScriptDescriptor) = parameters +} + +class ReflectedParamClassTestScriptDefinition(extension: String, val name: String, val parameter: KClass) : BaseScriptDefinition(extension) { + override fun getScriptParameters(scriptDescriptor: ScriptDescriptor) = + listOf(makeReflectedClassScriptParameter(scriptDescriptor, Name.identifier(name), parameter)) +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ScriptGenTest.java b/compiler/tests/org/jetbrains/kotlin/codegen/ScriptGenTest.java index 4a74bf1e358..4476de02d81 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ScriptGenTest.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ScriptGenTest.java @@ -21,7 +21,7 @@ import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.script.KotlinScriptDefinition; import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider; import org.jetbrains.kotlin.script.ScriptParameter; -import org.jetbrains.kotlin.scripts.TestScriptDefinition; +import org.jetbrains.kotlin.scripts.SimpleParamsTestScriptDefinition; import org.jetbrains.kotlin.test.ConfigurationKind; import org.jetbrains.org.objectweb.asm.Opcodes; @@ -35,12 +35,12 @@ import static java.util.Collections.singletonList; public class ScriptGenTest extends CodegenTestCase { private static final KotlinScriptDefinition FIB_SCRIPT_DEFINITION = - new TestScriptDefinition( + new SimpleParamsTestScriptDefinition( ".lang.kt", singletonList(new ScriptParameter(Name.identifier("num"), DefaultBuiltIns.getInstance().getIntType())) ); private static final KotlinScriptDefinition NO_PARAM_SCRIPT_DEFINITION = - new TestScriptDefinition( + new SimpleParamsTestScriptDefinition( ".kts", Collections.emptyList() ); diff --git a/compiler/tests/org/jetbrains/kotlin/scripts/ScriptTest.kt b/compiler/tests/org/jetbrains/kotlin/scripts/ScriptTest.kt index 70b6ea35c6a..163042bea55 100644 --- a/compiler/tests/org/jetbrains/kotlin/scripts/ScriptTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/scripts/ScriptTest.kt @@ -23,8 +23,10 @@ import org.jetbrains.kotlin.cli.common.messages.* import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler +import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot import org.jetbrains.kotlin.codegen.CompilationException import org.jetbrains.kotlin.config.JVMConfigurationKeys +import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.addKotlinSourceRoot import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.script.KotlinScriptDefinition @@ -35,12 +37,13 @@ import org.jetbrains.kotlin.test.TestJdkKind import org.jetbrains.kotlin.utils.PathUtil import org.junit.Assert import org.junit.Test +import java.io.File class ScriptTest { @Test @Throws(Exception::class) fun testScript() { - val aClass = compileScript("fib.kts", TestScriptDefinition(".kts", numIntParam())) + val aClass = compileScript("fib.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam())) Assert.assertNotNull(aClass) aClass!!.getConstructor(Integer.TYPE).newInstance(4) } @@ -48,7 +51,7 @@ class ScriptTest { @Test @Throws(Exception::class) fun testScriptWithPackage() { - val aClass = compileScript("fib.pkg.kts", TestScriptDefinition(".kts", numIntParam())) + val aClass = compileScript("fib.pkg.kts", SimpleParamsTestScriptDefinition(".kts", numIntParam())) Assert.assertNotNull(aClass) aClass!!.getConstructor(Integer.TYPE).newInstance(4) } @@ -56,14 +59,24 @@ class ScriptTest { @Test @Throws(Exception::class) fun testScriptWithScriptDefinition() { - val aClass = compileScript("fib.fib.kt", TestScriptDefinition(".fib.kt", numIntParam())) + val aClass = compileScript("fib.fib.kt", SimpleParamsTestScriptDefinition(".fib.kt", numIntParam())) Assert.assertNotNull(aClass) aClass!!.getConstructor(Integer.TYPE).newInstance(4) } + @Test + fun testScriptWithClassParameter() { + val cl = TestParamClass::class + val aClass = compileScript("fib_cp.kts", ReflectedParamClassTestScriptDefinition(".kts", "param", cl), runIsolated = false) + Assert.assertNotNull(aClass) + aClass!!.getConstructor(cl.java).newInstance(TestParamClass(4)) + } + private fun compileScript( scriptPath: String, - scriptDefinition: KotlinScriptDefinition): Class<*>? { + scriptDefinition: KotlinScriptDefinition, + runIsolated: Boolean = true): Class<*>? + { val paths = PathUtil.getKotlinPathsForDistDirectory() val messageCollector = PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false) @@ -73,11 +86,14 @@ class ScriptTest { configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, messageCollector) configuration.addKotlinSourceRoot("compiler/testData/script/" + scriptPath) configuration.add(JVMConfigurationKeys.SCRIPT_DEFINITIONS, scriptDefinition) + if (!runIsolated) + configuration.addCurrentClasspathAsRoots() val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) try { - return KotlinToJVMBytecodeCompiler.compileScript(environment, paths) + return if (runIsolated) KotlinToJVMBytecodeCompiler.compileScript(environment, paths) + else KotlinToJVMBytecodeCompiler.compileScript(environment, this.javaClass.classLoader) } catch (e: CompilationException) { messageCollector.report(CompilerMessageSeverity.EXCEPTION, OutputMessageUtil.renderException(e), @@ -95,7 +111,16 @@ class ScriptTest { } } + private fun CompilerConfiguration.addCurrentClasspathAsRoots() { + System.getProperty("java.class.path")?.let { + it.split(String.format("\\%s", File.pathSeparatorChar).toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + .forEach { addJvmClasspathRoot(File(it)) } + } + } + private fun numIntParam(): List { return listOf(ScriptParameter(Name.identifier("num"), DefaultBuiltIns.Instance.intType)) } } + +class TestParamClass(val memberNum: Int) \ No newline at end of file