Refactor CodegenTestCase

- prohibit main(Array<String>) in favor of box(): String
- move all script-related code to ScriptGenTest
- remove unused environment-creating methods
- inline trivial methods & other minor stuff
This commit is contained in:
Alexander Udalov
2013-01-25 00:55:52 +04:00
parent 74467df3ef
commit df5b809b46
21 changed files with 184 additions and 197 deletions
@@ -20,6 +20,7 @@ class X {
fun foo(x: Int) {} fun foo(x: Int) {}
fun main(args: Array<String>) { fun box(): String {
X().test() X().test()
return "OK"
} }
@@ -20,6 +20,7 @@ class X {
fun foo(x: Int) {} fun foo(x: Int) {}
fun main(args: Array<String>) { fun box(): String {
X().test() X().test()
return "OK"
} }
@@ -20,6 +20,7 @@ class X {
fun foo(x: Int) {} fun foo(x: Int) {}
fun main(args: Array<String>) { fun box(): String {
X().test() X().test()
return "OK"
} }
@@ -1,5 +1,6 @@
fun main(args : Array<String>) { fun box(): String {
if (1 != 0) { if (1 != 0) {
1 1
} }
return "OK"
} }
@@ -9,10 +9,12 @@ class Broken() : jet.Function0<String>() {
} }
} }
fun main(args: Array<String>) { fun box(): String {
val works1: ()->Object = Works(); val works1: ()->Object = Works();
works1() works1()
val broken1: ()->String = Broken(); val broken1: ()->String = Broken();
broken1() broken1()
return "OK"
} }
@@ -1,5 +1,6 @@
public var v1: String = "V1" public var v1: String = "V1"
fun main(args: Array<String>) { fun box(): String {
System.out?.println("v1: $v1, v2: $v2") val s = "v1: $v1, v2: $v2"
return "OK"
} }
@@ -2,6 +2,7 @@ class A<T>(t: Array<Array<T>>) {
val a:Array<Array<T>> = t val a:Array<Array<T>> = t
} }
fun main(args : Array<String>) { fun box(): String {
A<Int>(array()) // <- java.lang.VerifyError: (class: A, method: getA signature: ()[[Ljava/lang/Object;) Wrong return type in function A<Int>(array()) // <- java.lang.VerifyError: (class: A, method: getA signature: ()[[Ljava/lang/Object;) Wrong return type in function
return "OK"
} }
@@ -1,3 +1,4 @@
fun main(args: Array<String>) { fun box(): String {
kotlin.assert(true) kotlin.assert(true)
return "OK"
} }
@@ -1,6 +1,5 @@
import java.util.HashMap fun box(): String {
fun main(args: Array<String>) {
(0.toLong() as Number?)?.toByte() (0.toLong() as Number?)?.toByte()
(0 as Int?)?.toDouble() (0 as Int?)?.toDouble()
return "OK"
} }
@@ -1,5 +1,6 @@
fun main(args : Array<String>) { fun box(): String {
1 in 1.rangeTo(10) 1 in 1.rangeTo(10)
1..10 1..10
'h' in 'A'.rangeTo('Z') 'h' in 'A'.rangeTo('Z')
return "OK"
} }
@@ -1,4 +1,4 @@
fun main(args: Array<String>) { fun box(): String {
9 in 0..9 9 in 0..9
val intRange = 0..9 val intRange = 0..9
9 in intRange 9 in intRange
@@ -10,4 +10,6 @@ fun main(args: Array<String>) {
9.toLong() in longRange 9.toLong() in longRange
val shortRange = 0.toShort()..9.toShort() val shortRange = 0.toShort()..9.toShort()
9.toShort() in shortRange 9.toShort() in shortRange
return "OK"
} }
@@ -1,5 +1,6 @@
fun main(args: Array<String>) { fun box(): String {
A() A()
return "OK"
} }
class A: B() { class A: B() {
@@ -7,8 +7,9 @@ class C() {
} }
fun main(args: Array<String>) { fun box(): String {
if (2 in C()..2) { if (2 in C()..2) {
System.out?.println(2) 2 == 2
} }
return "OK"
} }
@@ -16,10 +16,12 @@
package org.jetbrains.jet.codegen; package org.jetbrains.jet.codegen;
import org.jetbrains.jet.ConfigurationKind;
public abstract class AbstractDataClassCodegenTest extends CodegenTestCase { public abstract class AbstractDataClassCodegenTest extends CodegenTestCase {
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
createEnvironmentWithFullJdk(); createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL);
} }
} }
@@ -18,14 +18,12 @@ package org.jetbrains.jet.codegen;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtil;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
import com.intellij.testFramework.UsefulTestCase; import com.intellij.testFramework.UsefulTestCase;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.Type;
import org.jetbrains.jet.ConfigurationKind; import org.jetbrains.jet.ConfigurationKind;
import org.jetbrains.jet.JetTestUtils; import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.TestJdkKind; import org.jetbrains.jet.TestJdkKind;
@@ -35,17 +33,15 @@ import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment;
import org.jetbrains.jet.codegen.state.GenerationState; import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.codegen.state.Progress; import org.jetbrains.jet.codegen.state.Progress;
import org.jetbrains.jet.config.CompilerConfiguration; import org.jetbrains.jet.config.CompilerConfiguration;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetPsiUtil; import org.jetbrains.jet.lang.psi.JetPsiUtil;
import org.jetbrains.jet.lang.resolve.AnalyzingUtils; import org.jetbrains.jet.lang.resolve.AnalyzingUtils;
import org.jetbrains.jet.lang.resolve.ScriptNameUtil;
import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM; import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM;
import org.jetbrains.jet.parsing.JetParsingTest; import org.jetbrains.jet.parsing.JetParsingTest;
import org.jetbrains.jet.utils.ExceptionUtils; import org.jetbrains.jet.utils.ExceptionUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.MalformedURLException; import java.net.MalformedURLException;
@@ -61,31 +57,16 @@ public abstract class CodegenTestCase extends UsefulTestCase {
protected JetCoreEnvironment myEnvironment; protected JetCoreEnvironment myEnvironment;
protected CodegenTestFiles myFiles; protected CodegenTestFiles myFiles;
protected Object scriptInstance;
private GenerationState alreadyGenerated; private GenerationState alreadyGenerated;
private GeneratedClassLoader initializedClassLoader; private GeneratedClassLoader initializedClassLoader;
protected void createEnvironmentWithMockJdkAndIdeaAnnotations() {
if (myEnvironment != null) {
throw new IllegalStateException("must not set up myEnvironemnt twice");
}
myEnvironment = JetTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(getTestRootDisposable());
}
protected void createEnvironmentWithMockJdkAndIdeaAnnotations(@NotNull ConfigurationKind configurationKind) { protected void createEnvironmentWithMockJdkAndIdeaAnnotations(@NotNull ConfigurationKind configurationKind) {
if (myEnvironment != null) { if (myEnvironment != null) {
throw new IllegalStateException("must not set up myEnvironemnt twice"); throw new IllegalStateException("must not set up myEnvironment twice");
} }
myEnvironment = JetTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(getTestRootDisposable(), configurationKind); myEnvironment = JetTestUtils.createEnvironmentWithMockJdkAndIdeaAnnotations(getTestRootDisposable(), configurationKind);
} }
protected void createEnvironmentWithFullJdk() {
if (myEnvironment != null) {
throw new IllegalStateException("must not set up myEnvironemnt twice");
}
myEnvironment = JetTestUtils.createEnvironmentWithFullJdk(getTestRootDisposable());
}
protected static void assertThrows(Method foo, Class<? extends Throwable> exceptionClass, Object instance, Object... args) throws IllegalAccessException { protected static void assertThrows(Method foo, Class<? extends Throwable> exceptionClass, Object instance, Object... args) throws IllegalAccessException {
boolean caught = false; boolean caught = false;
try { try {
@@ -97,16 +78,10 @@ public abstract class CodegenTestCase extends UsefulTestCase {
assertTrue(caught); assertTrue(caught);
} }
@Override
protected void setUp() throws Exception {
super.setUp();
}
@Override @Override
protected void tearDown() throws Exception { protected void tearDown() throws Exception {
myFiles = null; myFiles = null;
myEnvironment = null; myEnvironment = null;
scriptInstance = null;
alreadyGenerated = null; alreadyGenerated = null;
if (initializedClassLoader != null) { if (initializedClassLoader != null) {
@@ -118,17 +93,11 @@ public abstract class CodegenTestCase extends UsefulTestCase {
} }
protected void loadText(final String text) { protected void loadText(final String text) {
myFiles = CodegenTestFiles.create("a.jet", text, myEnvironment.getProject()); myFiles = CodegenTestFiles.create("a.kt", text, myEnvironment.getProject());
} }
protected String loadFile(final String name) { protected String loadFile(final String name) {
try { return loadFileByFullPath(JetParsingTest.getTestDataDir() + "/codegen/" + name);
final String content = JetTestUtils.doLoadFile(JetParsingTest.getTestDataDir() + "/codegen/", name);
myFiles = CodegenTestFiles.create(name, content, myEnvironment.getProject());
return content;
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
protected String loadFileByFullPath(final String fullPath) { protected String loadFileByFullPath(final String fullPath) {
@@ -173,101 +142,22 @@ public abstract class CodegenTestCase extends UsefulTestCase {
blackBox(false); blackBox(false);
} }
@NotNull
private Class<?> loadClassFromType(@NotNull Type type) {
try {
switch (type.getSort()) {
case Type.OBJECT:
return Class.forName(type.getClassName());
case Type.INT:
return int.class;
case Type.LONG:
return long.class;
default:
// AFAIK there is no way to create array class from class
if (type.getDescriptor().equals("[Ljava/lang/String;")) {
return String[].class;
}
throw new IllegalStateException("not implemented: " + type.getDescriptor());
}
}
catch (Exception e) {
throw ExceptionUtils.rethrow(e);
}
}
private Constructor getConstructor(@NotNull Class<?> clazz, org.jetbrains.asm4.commons.Method method) {
if (!method.getName().equals("<init>")) {
throw new IllegalArgumentException("not constructor: " + method);
}
Class[] classes = new Class[method.getArgumentTypes().length];
for (int i = 0; i < classes.length; ++i) {
classes[i] = loadClassFromType(method.getArgumentTypes()[i]);
}
try {
return clazz.getConstructor(classes);
}
catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
private void blackBox(boolean classPathInTheSameClassLoader) { private void blackBox(boolean classPathInTheSameClassLoader) {
GenerationState state = generateClassesInFileGetState(); ClassFileFactory factory = generateClassesInFile();
GeneratedClassLoader loader = createClassLoader(factory, classPathInTheSameClassLoader);
GeneratedClassLoader loader = createClassLoader(state.getFactory(), classPathInTheSameClassLoader); JetFile firstFile = myFiles.getPsiFiles().get(0);
String fqName = NamespaceCodegen.getJVMClassNameForKotlinNs(JetPsiUtil.getFQName(firstFile)).getFqName().getFqName();
String r;
try { try {
if (myFiles.isScript()) { Class<?> namespaceClass = loader.loadClass(fqName);
String scriptClassName = ScriptNameUtil.classNameForScript(myFiles.getPsiFile()); Method method = namespaceClass.getMethod("box");
Class<?> scriptClass = loader.loadClass(scriptClassName);
Constructor constructor = getConstructor(scriptClass, state.getScriptCodegen().getScriptConstructorMethod()); String r = (String) method.invoke(null);
scriptInstance = constructor.newInstance(myFiles.getScriptParameterValues().toArray()); assertEquals("OK", r);
assertFalse("expecting at least one expectation", myFiles.getExpectedValues().isEmpty());
for (Pair<String, String> nameValue : myFiles.getExpectedValues()) {
String fieldName = nameValue.first;
String expectedValue = nameValue.second;
if (expectedValue.equals("<nofield>")) {
try {
scriptClass.getDeclaredField(fieldName);
fail("must have no field " + fieldName);
} catch (NoSuchFieldException e) {
continue;
}
}
Field field = scriptClass.getDeclaredField(fieldName);
field.setAccessible(true);
Object result = field.get(scriptInstance);
String resultString = result != null ? result.toString() : "null";
assertEquals("comparing field " + fieldName, expectedValue, resultString);
}
}
else {
String fqName = NamespaceCodegen.getJVMClassNameForKotlinNs(JetPsiUtil.getFQName(myFiles.getPsiFiles().get(0))).getFqName().getFqName();
Class<?> namespaceClass = loader.loadClass(fqName);
try {
Method method = namespaceClass.getMethod("box");
r = (String) method.invoke(null);
assertEquals("OK", r);
}
catch (NoSuchMethodException e) {
Method method = namespaceClass.getMethod("main",String[].class);
method.invoke(null,new Object[]{new String[0]});
}
}
} catch (Error e) {
System.out.println(generateToText());
throw e;
} catch (Throwable e) { } catch (Throwable e) {
System.out.println(generateToText()); System.out.println(generateToText());
throw new RuntimeException(e); ExceptionUtils.rethrow(e);
} }
} }
@@ -394,10 +284,9 @@ public abstract class CodegenTestCase extends UsefulTestCase {
try { try {
return createClassLoader(state).loadClass(fqName); return createClassLoader(state).loadClass(fqName);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
fail("No classfile was generated for: " + fqName);
return null;
} }
fail("No classfile was generated for: " + fqName);
return null;
} }
@NotNull @NotNull
@@ -407,7 +296,7 @@ public abstract class CodegenTestCase extends UsefulTestCase {
} }
@NotNull @NotNull
private GenerationState generateClassesInFileGetState() { protected GenerationState generateClassesInFileGetState() {
GenerationState generationState; GenerationState generationState;
try { try {
generationState = generateCommon(ClassBuilderFactories.TEST, myEnvironment, myFiles); generationState = generateCommon(ClassBuilderFactories.TEST, myEnvironment, myFiles);
@@ -474,8 +363,4 @@ public abstract class CodegenTestCase extends UsefulTestCase {
assertTrue("Difference with current time: " + diff + " (this test is a bad one: it may fail even if the generated code is correct)", assertTrue("Difference with current time: " + diff + " (this test is a bad one: it may fail even if the generated code is correct)",
diff <= toleratedDifference); diff <= toleratedDifference);
} }
protected Class loadImplementationClass(@NotNull ClassFileFactory codegens, final String name) {
return loadClass(name, codegens);
}
} }
@@ -82,10 +82,6 @@ public class CodegenTestFiles {
return psiFiles; return psiFiles;
} }
public boolean isScript() {
return psiFiles.size() == 1 && psiFiles.get(0).isScript();
}
public static CodegenTestFiles create(Project project, String[] names) { public static CodegenTestFiles create(Project project, String[] names) {
ArrayList<JetFile> files = new ArrayList<JetFile>(); ArrayList<JetFile> files = new ArrayList<JetFile>();
for (String name : names) { for (String name : names) {
@@ -33,13 +33,13 @@ public class EnumGenTest extends CodegenTestCase {
public void testSuperclassIsEnum() throws NoSuchFieldException, IllegalAccessException { public void testSuperclassIsEnum() throws NoSuchFieldException, IllegalAccessException {
loadFile("enum/simple.kt"); loadFile("enum/simple.kt");
Class season = loadImplementationClass(generateClassesInFile(), "Season"); Class season = loadClass("Season", generateClassesInFile());
assertEquals("java.lang.Enum", season.getSuperclass().getName()); assertEquals("java.lang.Enum", season.getSuperclass().getName());
} }
public void testEnumClassModifiers() throws NoSuchFieldException, IllegalAccessException { public void testEnumClassModifiers() throws NoSuchFieldException, IllegalAccessException {
loadFile("enum/simple.kt"); loadFile("enum/simple.kt");
Class season = loadImplementationClass(generateClassesInFile(), "Season"); Class season = loadClass("Season", generateClassesInFile());
int modifiers = season.getModifiers(); int modifiers = season.getModifiers();
assertTrue((modifiers & 0x4000) != 0); // ACC_ENUM assertTrue((modifiers & 0x4000) != 0); // ACC_ENUM
assertTrue((modifiers & Modifier.FINAL) != 0); assertTrue((modifiers & Modifier.FINAL) != 0);
@@ -47,7 +47,7 @@ public class EnumGenTest extends CodegenTestCase {
public void testEnumFieldModifiers() throws NoSuchFieldException, IllegalAccessException { public void testEnumFieldModifiers() throws NoSuchFieldException, IllegalAccessException {
loadFile("enum/simple.kt"); loadFile("enum/simple.kt");
Class season = loadImplementationClass(generateClassesInFile(), "Season"); Class season = loadClass("Season", generateClassesInFile());
Field summer = season.getField("SUMMER"); Field summer = season.getField("SUMMER");
int modifiers = summer.getModifiers(); int modifiers = summer.getModifiers();
assertTrue((modifiers & 0x4000) != 0); // ACC_ENUM assertTrue((modifiers & 0x4000) != 0); // ACC_ENUM
@@ -120,7 +120,7 @@ public class EnumGenTest extends CodegenTestCase {
public void testNoClassForSimpleEnum() public void testNoClassForSimpleEnum()
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
loadFile("enum/name.kt"); loadFile("enum/name.kt");
Class cls = loadImplementationClass(generateClassesInFile(), "State"); Class cls = loadClass("State", generateClassesInFile());
Field field = cls.getField("O"); Field field = cls.getField("O");
assertEquals("State", field.get(null).getClass().getName()); assertEquals("State", field.get(null).getClass().getName());
} }
@@ -128,7 +128,7 @@ public class EnumGenTest extends CodegenTestCase {
public void testYesClassForComplexEnum() public void testYesClassForComplexEnum()
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
loadFile("enum/abstractmethod.kt"); loadFile("enum/abstractmethod.kt");
Class cls = loadImplementationClass(generateClassesInFile(), "IssueState"); Class cls = loadClass("IssueState", generateClassesInFile());
Field field = cls.getField("DEFAULT"); Field field = cls.getField("DEFAULT");
assertEquals("IssueState", field.get(null).getClass().getName()); assertEquals("IssueState", field.get(null).getClass().getName());
field = cls.getField("FIXED"); field = cls.getField("FIXED");
@@ -16,11 +16,13 @@
package org.jetbrains.jet.codegen; package org.jetbrains.jet.codegen;
import org.jetbrains.jet.JetTestUtils;
public class FullJdkCodegenTest extends CodegenTestCase { public class FullJdkCodegenTest extends CodegenTestCase {
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
createEnvironmentWithFullJdk(); myEnvironment = JetTestUtils.createEnvironmentWithFullJdk(getTestRootDisposable());
} }
public void testKt434() { public void testKt434() {
@@ -17,6 +17,7 @@
package org.jetbrains.jet.codegen; package org.jetbrains.jet.codegen;
import jet.IntRange; import jet.IntRange;
import org.jetbrains.jet.ConfigurationKind;
import java.awt.*; import java.awt.*;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@@ -28,7 +29,7 @@ public class NamespaceGenTest extends CodegenTestCase {
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
createEnvironmentWithMockJdkAndIdeaAnnotations(); createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL);
} }
public void testPSVM() throws Exception { public void testPSVM() throws Exception {
@@ -35,7 +35,7 @@ public class PropertyGenTest extends CodegenTestCase {
public void testPrivateVal() throws Exception { public void testPrivateVal() throws Exception {
loadFile(); loadFile();
final Class aClass = loadImplementationClass(generateClassesInFile(), "PrivateVal"); final Class aClass = loadClass("PrivateVal", generateClassesInFile());
final Field[] fields = aClass.getDeclaredFields(); final Field[] fields = aClass.getDeclaredFields();
assertEquals(1, fields.length); // prop assertEquals(1, fields.length); // prop
final Field field = fields[0]; final Field field = fields[0];
@@ -44,7 +44,7 @@ public class PropertyGenTest extends CodegenTestCase {
public void testPrivateVar() throws Exception { public void testPrivateVar() throws Exception {
loadFile(); loadFile();
final Class aClass = loadImplementationClass(generateClassesInFile(), "PrivateVar"); final Class aClass = loadClass("PrivateVar", generateClassesInFile());
final Object instance = aClass.newInstance(); final Object instance = aClass.newInstance();
Method setter = findMethodByName(aClass, "setValueOfX"); Method setter = findMethodByName(aClass, "setValueOfX");
setter.invoke(instance, 239); setter.invoke(instance, 239);
@@ -54,7 +54,7 @@ public class PropertyGenTest extends CodegenTestCase {
public void testPublicVar() throws Exception { public void testPublicVar() throws Exception {
loadText("class PublicVar() { public var foo : Int = 0; }"); loadText("class PublicVar() { public var foo : Int = 0; }");
final Class aClass = loadImplementationClass(generateClassesInFile(), "PublicVar"); final Class aClass = loadClass("PublicVar", generateClassesInFile());
final Object instance = aClass.newInstance(); final Object instance = aClass.newInstance();
Method setter = findMethodByName(aClass, "setFoo"); Method setter = findMethodByName(aClass, "setFoo");
setter.invoke(instance, 239); setter.invoke(instance, 239);
@@ -116,7 +116,7 @@ public class PropertyGenTest extends CodegenTestCase {
public void testAccessorsWithoutBody() throws Exception { public void testAccessorsWithoutBody() throws Exception {
loadText("class AccessorsWithoutBody() { protected var foo: Int = 349\n get\n private set\n fun setter() { foo = 610; } } "); loadText("class AccessorsWithoutBody() { protected var foo: Int = 349\n get\n private set\n fun setter() { foo = 610; } } ");
final Class aClass = loadImplementationClass(generateClassesInFile(), "AccessorsWithoutBody"); final Class aClass = loadClass("AccessorsWithoutBody", generateClassesInFile());
final Object instance = aClass.newInstance(); final Object instance = aClass.newInstance();
final Method getFoo = findMethodByName(aClass, "getFoo"); final Method getFoo = findMethodByName(aClass, "getFoo");
getFoo.setAccessible(true); getFoo.setAccessible(true);
@@ -139,7 +139,7 @@ public class PropertyGenTest extends CodegenTestCase {
public void testPropertyReceiverOnStack() throws Exception { public void testPropertyReceiverOnStack() throws Exception {
loadFile(); loadFile();
final Class aClass = loadImplementationClass(generateClassesInFile(), "Evaluator"); final Class aClass = loadClass("Evaluator", generateClassesInFile());
final Constructor constructor = aClass.getConstructor(StringBuilder.class); final Constructor constructor = aClass.getConstructor(StringBuilder.class);
StringBuilder sb = new StringBuilder("xyzzy"); StringBuilder sb = new StringBuilder("xyzzy");
final Object instance = constructor.newInstance(sb); final Object instance = constructor.newInstance(sb);
@@ -212,7 +212,7 @@ public class PropertyGenTest extends CodegenTestCase {
public void testKt1846() { public void testKt1846() {
loadFile("regressions/kt1846.kt"); loadFile("regressions/kt1846.kt");
final Class aClass = loadImplementationClass(generateClassesInFile(), "A"); final Class aClass = loadClass("A", generateClassesInFile());
try { try {
Method v1 = aClass.getMethod("getV1"); Method v1 = aClass.getMethod("getV1");
System.out.println(generateToText()); System.out.println(generateToText());
@@ -256,7 +256,7 @@ public class PropertyGenTest extends CodegenTestCase {
public void testKt2589() { public void testKt2589() {
loadFile("regressions/kt2589.kt"); loadFile("regressions/kt2589.kt");
final Class aClass = loadImplementationClass(generateClassesInFile(), "Foo"); final Class aClass = loadClass("Foo", generateClassesInFile());
assertTrue((aClass.getModifiers() & Opcodes.ACC_FINAL) == 0); assertTrue((aClass.getModifiers() & Opcodes.ACC_FINAL) == 0);
try { try {
@@ -284,7 +284,7 @@ public class PropertyGenTest extends CodegenTestCase {
public void testKt2677() { public void testKt2677() {
loadFile("regressions/kt2677.kt"); loadFile("regressions/kt2677.kt");
final Class aClass = loadImplementationClass(generateClassesInFile(), "DerivedWeatherReport"); final Class aClass = loadClass("DerivedWeatherReport", generateClassesInFile());
final Class bClass = aClass.getSuperclass(); final Class bClass = aClass.getSuperclass();
try { try {
@@ -16,23 +16,24 @@
package org.jetbrains.jet.codegen; package org.jetbrains.jet.codegen;
import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.Opcodes; import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.Type;
import org.jetbrains.jet.ConfigurationKind; import org.jetbrains.jet.ConfigurationKind;
import org.jetbrains.jet.lang.parsing.JetParserDefinition; import org.jetbrains.jet.codegen.state.GenerationState;
import org.jetbrains.jet.lang.parsing.JetScriptDefinition; import org.jetbrains.jet.lang.parsing.JetScriptDefinition;
import org.jetbrains.jet.lang.parsing.JetScriptDefinitionProvider; import org.jetbrains.jet.lang.parsing.JetScriptDefinitionProvider;
import org.jetbrains.jet.lang.resolve.AnalyzerScriptParameter; import org.jetbrains.jet.lang.resolve.AnalyzerScriptParameter;
import org.jetbrains.jet.lang.resolve.ScriptNameUtil;
import org.jetbrains.jet.utils.ExceptionUtils;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class ScriptGenTest extends CodegenTestCase { public class ScriptGenTest extends CodegenTestCase {
protected Object scriptInstance;
public static final JetScriptDefinition FIB_SCRIPT_DEFINITION = public static final JetScriptDefinition FIB_SCRIPT_DEFINITION =
new JetScriptDefinition(".lang.kt", new AnalyzerScriptParameter("num", "jet.Int")); new JetScriptDefinition(".lang.kt", new AnalyzerScriptParameter("num", "jet.Int"));
@@ -43,59 +44,146 @@ public class ScriptGenTest extends CodegenTestCase {
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.JDK_ONLY); createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.JDK_ONLY);
} }
@Override
protected void tearDown() throws Exception {
scriptInstance = null;
}
private void blackBoxScript(String filename) {
loadFile(filename);
GenerationState state = generateClassesInFileGetState();
GeneratedClassLoader loader = createClassLoader(state.getFactory(), false);
String scriptClassName = ScriptNameUtil.classNameForScript(myFiles.getPsiFile());
try {
Class<?> scriptClass = loader.loadClass(scriptClassName);
Constructor constructor = getConstructor(scriptClass, state.getScriptCodegen().getScriptConstructorMethod());
scriptInstance = constructor.newInstance(myFiles.getScriptParameterValues().toArray());
assertFalse("expecting at least one expectation", myFiles.getExpectedValues().isEmpty());
for (Pair<String, String> nameValue : myFiles.getExpectedValues()) {
String fieldName = nameValue.first;
String expectedValue = nameValue.second;
if (expectedValue.equals("<nofield>")) {
try {
scriptClass.getDeclaredField(fieldName);
fail("must have no field " + fieldName);
} catch (NoSuchFieldException e) {
continue;
}
}
Field field = scriptClass.getDeclaredField(fieldName);
field.setAccessible(true);
Object result = field.get(scriptInstance);
String resultString = result != null ? result.toString() : "null";
assertEquals("comparing field " + fieldName, expectedValue, resultString);
}
}
catch (Throwable e) {
System.out.println(generateToText());
ExceptionUtils.rethrow(e);
}
}
protected Constructor getConstructor(@NotNull Class<?> clazz, org.jetbrains.asm4.commons.Method method) {
if (!method.getName().equals("<init>")) {
throw new IllegalArgumentException("not constructor: " + method);
}
Class[] classes = new Class[method.getArgumentTypes().length];
for (int i = 0; i < classes.length; ++i) {
classes[i] = loadClassFromType(method.getArgumentTypes()[i]);
}
try {
return clazz.getConstructor(classes);
}
catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
@NotNull
private Class<?> loadClassFromType(@NotNull Type type) {
try {
switch (type.getSort()) {
case Type.OBJECT:
return Class.forName(type.getClassName());
case Type.INT:
return int.class;
case Type.LONG:
return long.class;
default:
// AFAIK there is no way to create array class from class
if (type.getDescriptor().equals("[Ljava/lang/String;")) {
return String[].class;
}
throw new IllegalStateException("not implemented: " + type.getDescriptor());
}
}
catch (Exception e) {
throw ExceptionUtils.rethrow(e);
}
}
public void testHelloWorld() { public void testHelloWorld() {
blackBoxFile("script/helloWorld.ktscript"); blackBoxScript("script/helloWorld.ktscript");
} }
public void testString() { public void testString() {
blackBoxFile("script/string.ktscript"); blackBoxScript("script/string.ktscript");
} }
public void testTopLevelFunction() throws Exception { public void testTopLevelFunction() throws Exception {
blackBoxFile("script/topLevelFunction.ktscript"); blackBoxScript("script/topLevelFunction.ktscript");
Method method = scriptInstance.getClass().getMethod("factorial", new Class<?>[]{ int.class }); Method method = scriptInstance.getClass().getMethod("factorial", new Class<?>[]{ int.class });
Object r = method.invoke(scriptInstance, 4); Object r = method.invoke(scriptInstance, 4);
assertEquals(24, r); assertEquals(24, r);
} }
public void testTopLevelFunctionClosure() { public void testTopLevelFunctionClosure() {
blackBoxFile("script/topLevelFunctionClosure.ktscript"); blackBoxScript("script/topLevelFunctionClosure.ktscript");
} }
public void testSecondLevelFunction() { public void testSecondLevelFunction() {
blackBoxFile("script/secondLevelFunction.ktscript"); blackBoxScript("script/secondLevelFunction.ktscript");
} }
public void testSecondLevelFunctionClosure() { public void testSecondLevelFunctionClosure() {
blackBoxFile("script/secondLevelFunctionClosure.ktscript"); blackBoxScript("script/secondLevelFunctionClosure.ktscript");
} }
public void testSecondLevelVal() { public void testSecondLevelVal() {
blackBoxFile("script/secondLevelVal.ktscript"); blackBoxScript("script/secondLevelVal.ktscript");
} }
public void testTopLevelProperty() { public void testTopLevelProperty() {
blackBoxFile("script/topLevelProperty.ktscript"); blackBoxScript("script/topLevelProperty.ktscript");
} }
public void testScriptParameter() { public void testScriptParameter() {
blackBoxFile("script/parameter.ktscript"); blackBoxScript("script/parameter.ktscript");
} }
public void testScriptParameterLong() { public void testScriptParameterLong() {
blackBoxFile("script/parameterLong.ktscript"); blackBoxScript("script/parameterLong.ktscript");
} }
public void testScriptParameterArray() { public void testScriptParameterArray() {
blackBoxFile("script/parameterArray.ktscript"); blackBoxScript("script/parameterArray.ktscript");
} }
public void testScriptParameterClosure() { public void testScriptParameterClosure() {
blackBoxFile("script/parameterClosure.ktscript"); blackBoxScript("script/parameterClosure.ktscript");
} }
public void testEmpty() { public void testEmpty() {
blackBoxFile("script/empty.ktscript"); blackBoxScript("script/empty.ktscript");
} }
public void testLanguage() { public void testLanguage() {