Add possibility to specify supertypes for script, with tests

This commit is contained in:
Ilya Chernikov
2016-05-31 16:24:41 +02:00
parent 670627ac4e
commit 3c1503564e
10 changed files with 130 additions and 15 deletions
@@ -23,10 +23,13 @@ import org.jetbrains.kotlin.codegen.context.CodegenContext;
import org.jetbrains.kotlin.codegen.context.MethodContext;
import org.jetbrains.kotlin.codegen.context.ScriptContext;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor;
import org.jetbrains.kotlin.descriptors.ScriptDescriptor;
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
@@ -91,8 +94,8 @@ public class ScriptCodegen extends MemberCodegen<KtScript> {
ACC_PUBLIC | ACC_SUPER,
classAsmType.getInternalName(),
null,
"java/lang/Object",
ArrayUtil.EMPTY_STRING_ARRAY);
typeMapper.mapSupertype(DescriptorUtilsKt.getSuperClassOrAny(scriptDescriptor).getDefaultType(), null).getInternalName(),
CodegenUtilKt.mapSupertypesNames(typeMapper, DescriptorUtilsKt.getSuperInterfaces(scriptDescriptor), null));
}
@Override
@@ -145,7 +148,11 @@ public class ScriptCodegen extends MemberCodegen<KtScript> {
Type classType = typeMapper.mapType(scriptDescriptor);
iv.load(0, classType);
iv.invokespecial("java/lang/Object", "<init>", "()V", false);
ClassDescriptor superclass = DescriptorUtilsKt.getSuperClassOrAny(scriptDescriptor);
assert hasDefaultConstructor(superclass);
iv.invokespecial(
typeMapper.mapSupertype(superclass.getDefaultType(), null).getInternalName(),
"<init>", "()V", false);
iv.load(0, classType);
@@ -199,6 +206,15 @@ public class ScriptCodegen extends MemberCodegen<KtScript> {
mv.visitEnd();
}
private boolean hasDefaultConstructor(ClassDescriptor superclass) {
for (ConstructorDescriptor ctor : superclass.getConstructors()) {
if (ctor.getValueParameters().isEmpty()) {
return true;
}
}
return false;
}
private void genFieldsForParameters(@NotNull ScriptDescriptor script, @NotNull ClassBuilder classBuilder) {
for (ScriptDescriptor earlierScript : context.getEarlierScripts()) {
Type earlierClassName = typeMapper.mapType(earlierScript);
@@ -20,7 +20,9 @@ package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
import org.jetbrains.kotlin.codegen.context.PackageContext
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo
@@ -133,6 +135,11 @@ fun populateCompanionBackingFieldNamesToOuterContextIfNeeded(companion: KtObject
}
// TODO: inline and remove then ScriptCodegen is converted to Kotlin
fun mapSupertypesNames(typeMapper: KotlinTypeMapper, supertypes: List<ClassDescriptor>, signatureVisitor: JvmSignatureWriter?): Array<String> =
supertypes.map { typeMapper.mapSupertype(it.defaultType, signatureVisitor).internalName }.toTypedArray()
// Top level subclasses of a sealed class should be generated before that sealed class,
// so that we'd generate the necessary accessor for its constructor afterwards
fun sortTopLevelClassesAndPrepareContextForSealedClasses(
@@ -567,6 +567,9 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes
return parameters.invoke();
}
@Nullable
public List<? extends KotlinType> getInjectedSupertypes() { return null; }
private class LazyClassTypeConstructor extends AbstractClassTypeConstructor implements LazyEntity {
private final NotNullLazyValue<List<TypeParameterDescriptor>> parameters = c.getStorageManager().createLazyValue(new Function0<List<TypeParameterDescriptor>>() {
@Override
@@ -595,6 +598,11 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes
return Collections.emptyList();
}
List<? extends KotlinType> injectedSupertypes = getInjectedSupertypes();
if (injectedSupertypes != null) {
return Lists.newArrayList(Collections2.filter(injectedSupertypes, VALID_SUPERTYPE));
}
KtClassOrObject classOrObject = declarationProvider.getOwnerInfo().getCorrespondingClassOrObject();
if (classOrObject == null) {
return Collections.singleton(c.getModuleDescriptor().getBuiltIns().getAnyType());
@@ -26,7 +26,9 @@ 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.source.toSourceElement
import org.jetbrains.kotlin.script.KotlinScriptDefinitionProvider
import org.jetbrains.kotlin.script.ScriptPriorities
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSubstitutor
class LazyScriptDescriptor(
@@ -71,4 +73,11 @@ class LazyScriptDescriptor(
}
override fun getUnsubstitutedPrimaryConstructor() = super.getUnsubstitutedPrimaryConstructor()!!
override fun getInjectedSupertypes(): List<KotlinType>? {
val file = scriptInfo.script.getContainingKtFile()
val scriptDefinition = KotlinScriptDefinitionProvider.getInstance(file.project).findScriptDefinition(file)
val superclasses = scriptDefinition.getScriptSuperclasses(this)
return if (superclasses.isEmpty()) null else superclasses
}
}
@@ -34,6 +34,7 @@ import kotlin.reflect.KClass
interface KotlinScriptDefinition {
fun getScriptParameters(scriptDescriptor: ScriptDescriptor): List<ScriptParameter>
fun getScriptSuperclasses(scriptDescriptor: ScriptDescriptor): List<KotlinType> = emptyList()
fun isScript(file: PsiFile): Boolean
fun getScriptName(script: KtScript): Name
}
+14
View File
@@ -0,0 +1,14 @@
import org.jetbrains.kotlin.scripts.*
// this script expected parameter num : Int
fun fib(n: Int): Int {
val v = fibCombine( { fib(it) }, n)
System.out.println("fib($n)=$v")
return v
}
System.out.println("num: $num")
val result = fib(num)
@@ -20,10 +20,7 @@ import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.name.Name
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.script.*
import org.jetbrains.kotlin.types.KotlinType
import kotlin.reflect.KClass
@@ -32,7 +29,7 @@ abstract class BaseScriptDefinition (val extension: String) : KotlinScriptDefini
override fun getScriptName(script: KtScript): Name = ScriptNameUtil.fileNameWithExtensionStripped(script, extension)
}
class SimpleParamsTestScriptDefinition(extension: String, val parameters: List<ScriptParameter>) : BaseScriptDefinition(extension) {
open class SimpleParamsTestScriptDefinition(extension: String, val parameters: List<ScriptParameter>) : BaseScriptDefinition(extension) {
override fun getScriptParameters(scriptDescriptor: ScriptDescriptor) = parameters
}
@@ -40,3 +37,8 @@ class ReflectedParamClassTestScriptDefinition(extension: String, val name: Strin
override fun getScriptParameters(scriptDescriptor: ScriptDescriptor) =
listOf(makeReflectedClassScriptParameter(scriptDescriptor, Name.identifier(name), parameter))
}
class ReflectedSuperclassTestScriptDefinition(extension: String, parameters: List<ScriptParameter>, val superclass: KClass<out Any>) : SimpleParamsTestScriptDefinition(extension, parameters) {
override fun getScriptSuperclasses(scriptDescriptor: ScriptDescriptor): List<KotlinType> =
listOf(getKotlinType(scriptDescriptor, superclass))
}
@@ -38,6 +38,8 @@ import org.jetbrains.kotlin.utils.PathUtil
import org.junit.Assert
import org.junit.Test
import java.io.File
import java.net.URLClassLoader
import java.util.*
class ScriptTest {
@Test
@@ -66,10 +68,23 @@ class ScriptTest {
@Test
fun testScriptWithClassParameter() {
val cl = TestParamClass::class
val aClass = compileScript("fib_cp.kts", ReflectedParamClassTestScriptDefinition(".kts", "param", cl), runIsolated = false)
val aClass = compileScript("fib_cp.kts", ReflectedParamClassTestScriptDefinition(".kts", "param", TestParamClass::class), runIsolated = false)
Assert.assertNotNull(aClass)
aClass!!.getConstructor(cl.java).newInstance(TestParamClass(4))
aClass!!.getConstructor(TestParamClass::class.java).newInstance(TestParamClass(4))
}
@Test
fun testScriptWithBaseClass() {
val aClass = compileScript("fib_dsl.kts", ReflectedSuperclassTestScriptDefinition(".kts", numIntParam(), TestDSLClass::class), runIsolated = false)
Assert.assertNotNull(aClass)
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
}
@Test
fun testScriptWithInterface() {
val aClass = compileScript("fib_dsl.kts", ReflectedSuperclassTestScriptDefinition(".kts", numIntParam(), TestDSLInterface::class), runIsolated = false)
Assert.assertNotNull(aClass)
aClass!!.getConstructor(Integer.TYPE).newInstance(4)
}
private fun compileScript(
@@ -102,7 +117,7 @@ class ScriptTest {
}
catch (t: Throwable) {
MessageCollectorUtil.reportException(messageCollector, t)
return null
throw t
}
}
@@ -112,10 +127,16 @@ class ScriptTest {
}
private fun CompilerConfiguration.addCurrentClasspathAsRoots() {
System.getProperty("java.class.path")?.let {
val cp: MutableSet<File> = System.getProperty("java.class.path")?.let {
it.split(String.format("\\%s", File.pathSeparatorChar).toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
.forEach { addJvmClasspathRoot(File(it)) }
}
.map { File(it).canonicalFile }.toMutableSet()
} ?: LinkedHashSet<File>()
(this.javaClass.classLoader as? URLClassLoader)?.urLs
?.map { File(it.toURI()).canonicalFile }
?.filter { it.exists() }
?.forEach { cp.add(it) }
cp.forEach { addJvmClasspathRoot(it) }
}
private fun numIntParam(): List<ScriptParameter> {
@@ -0,0 +1,28 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("unused")
package org.jetbrains.kotlin.scripts
open class TestDSLClass
fun TestDSLClass.fibCombine(f: (Int) -> Int, n: Int) = if (n < 2) 1 else f(n - 1) + f(n - 2)
interface TestDSLInterface
fun TestDSLInterface.fibCombine(f: (Int) -> Int, n: Int) = if (n < 2) 1 else f(n - 1) + f(n - 2)
@@ -121,6 +121,15 @@ fun ClassDescriptor.getSuperClassNotAny(): ClassDescriptor? {
fun ClassDescriptor.getSuperClassOrAny(): ClassDescriptor = getSuperClassNotAny() ?: builtIns.any
fun ClassDescriptor.getSuperInterfaces(): List<ClassDescriptor> =
defaultType.constructor.supertypes
.filterNot { KotlinBuiltIns.isAnyOrNullableAny(it) }
.mapNotNull {
val superClassifier = it.constructor.declarationDescriptor
if (DescriptorUtils.isInterface(superClassifier)) superClassifier as ClassDescriptor
else null
}
val ClassDescriptor.secondaryConstructors: List<ConstructorDescriptor>
get() = constructors.filterNot { it.isPrimary }