Reformat and cleanup most JVM codegen test classes

This commit is contained in:
Alexander Udalov
2019-03-29 14:56:35 +01:00
parent 23a214710b
commit 7fdb9c990e
43 changed files with 491 additions and 580 deletions
@@ -33,9 +33,9 @@ public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase {
@Override
protected void doMultiFileTest(
@NotNull File wholeFile,
@NotNull List<TestFile> files,
@Nullable File javaFilesDir
@NotNull File wholeFile,
@NotNull List<TestFile> files,
@Nullable File javaFilesDir
) throws Exception {
boolean isIgnored = IGNORE_EXPECTED_FAILURES && InTextDirectivesUtils.isIgnoredTarget(getBackend(), wholeFile);
@@ -24,8 +24,7 @@ abstract class AbstractBlackBoxInlineCodegenTest : AbstractBlackBoxCodegenTest()
try {
InlineTestUtil.checkNoCallsToInline(initializedClassLoader.allGeneratedFiles.filterClassFiles(), myFiles.psiFiles)
SMAPTestUtil.checkSMAP(files, generateClassesInFile().getClassFiles(), false)
}
catch (e: Throwable) {
} catch (e: Throwable) {
println(generateToText())
throw e
}
@@ -34,7 +34,7 @@ abstract class AbstractBytecodeListingTest : CodegenTestCase() {
}
private fun isWithSignatures(wholeFile: File): Boolean =
WITH_SIGNATURES.containsMatchIn(wholeFile.readText())
WITH_SIGNATURES.containsMatchIn(wholeFile.readText())
companion object {
private val WITH_SIGNATURES = Regex.fromLiteral("// WITH_SIGNATURES")
@@ -132,13 +132,7 @@ class BytecodeListingTextCollectingVisitor(val filter: Filter, val withSignature
}
}.toString()
override fun visitMethod(
access: Int,
name: String,
desc: String,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor? {
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
if (!filter.shouldWriteMethod(access, name, desc)) {
return null
}
@@ -176,7 +170,7 @@ class BytecodeListingTextCollectingVisitor(val filter: Filter, val withSignature
}.joinToString()
val signatureIfRequired = if (withSignatures) "<$signature> " else ""
declarationsInsideClass.add(
Declaration("${signatureIfRequired}method $name($parameterWithAnnotations): $returnType", methodAnnotations)
Declaration("${signatureIfRequired}method $name($parameterWithAnnotations): $returnType", methodAnnotations)
)
super.visitEnd()
}
@@ -218,14 +212,7 @@ class BytecodeListingTextCollectingVisitor(val filter: Filter, val withSignature
return super.visitAnnotation(desc, visible)
}
override fun visit(
version: Int,
access: Int,
name: String,
signature: String?,
superName: String?,
interfaces: Array<out String>?
) {
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array<out String>?) {
className = name
classAccess = access
classSignature = signature
@@ -5,12 +5,10 @@
package org.jetbrains.kotlin.codegen
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.utils.rethrow
import org.junit.Assert
import java.io.File
import java.util.*
@@ -18,24 +16,25 @@ import java.util.regex.Matcher
import java.util.regex.Pattern
abstract class AbstractBytecodeTextTest : CodegenTestCase() {
@Throws(Exception::class)
override fun doMultiFileTest(wholeFile: File, files: List<CodegenTestCase.TestFile>, javaFilesDir: File?) {
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, files, TestJdkKind.MOCK_JDK, javaFilesDir)
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>, javaFilesDir: File?) {
createEnvironmentWithMockJdkAndIdeaAnnotations(
ConfigurationKind.ALL,
files,
TestJdkKind.MOCK_JDK,
*listOfNotNull(javaFilesDir).toTypedArray()
)
loadMultiFiles(files)
if (isMultiFileTest(files) && !InTextDirectivesUtils.isDirectiveDefined(wholeFile.readText(), "TREAT_AS_ONE_FILE")) {
doTestMultiFile(files)
}
else {
} else {
val expected = readExpectedOccurrences(wholeFile.path)
val actual = generateToText("helpers/")
checkGeneratedTextAgainstExpectedOccurrences(actual, expected)
}
}
@Throws(Exception::class)
private fun doTestMultiFile(files: List<CodegenTestCase.TestFile>) {
private fun doTestMultiFile(files: List<TestFile>) {
val expectedOccurrencesByOutputFile = LinkedHashMap<String, List<OccurrenceInfo>>()
for (file in files) {
readExpectedOccurrencesForMultiFileTest(file, expectedOccurrencesByOutputFile)
@@ -50,10 +49,9 @@ abstract class AbstractBytecodeTextTest : CodegenTestCase() {
}
}
@Throws(Exception::class)
protected fun readExpectedOccurrences(filename: String): List<OccurrenceInfo> {
val result = ArrayList<OccurrenceInfo>()
val lines = FileUtil.loadFile(File(filename), Charsets.UTF_8.name(), true).split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val lines = File(filename).readLines().dropLastWhile(String::isEmpty)
for (line in lines) {
val matcher = EXPECTED_OCCURRENCES_PATTERN.matcher(line)
@@ -80,7 +78,7 @@ abstract class AbstractBytecodeTextTest : CodegenTestCase() {
private val AT_OUTPUT_FILE_PATTERN = Pattern.compile("^\\s*//\\s*@(.*):$")
private val EXPECTED_OCCURRENCES_PATTERN = Pattern.compile("^\\s*//\\s*(\\d+)\\s*(.*)$")
private fun isMultiFileTest(files: List<CodegenTestCase.TestFile>): Boolean {
private fun isMultiFileTest(files: List<TestFile>): Boolean {
var kotlinFiles = 0
for (file in files) {
if (file.name.endsWith(".kt")) {
@@ -90,8 +88,7 @@ abstract class AbstractBytecodeTextTest : CodegenTestCase() {
return kotlinFiles > 1
}
fun checkGeneratedTextAgainstExpectedOccurrences(text: String,
expectedOccurrences: List<OccurrenceInfo>) {
fun checkGeneratedTextAgainstExpectedOccurrences(text: String, expectedOccurrences: List<OccurrenceInfo>) {
val expected = StringBuilder()
val actual = StringBuilder()
@@ -102,10 +99,9 @@ abstract class AbstractBytecodeTextTest : CodegenTestCase() {
try {
Assert.assertEquals(text, expected.toString(), actual.toString())
}
catch (e: Throwable) {
} catch (e: Throwable) {
println(text)
throw rethrow(e)
throw e
}
}
@@ -121,27 +117,23 @@ abstract class AbstractBytecodeTextTest : CodegenTestCase() {
}
}
private fun readExpectedOccurrencesForMultiFileTest(
file: CodegenTestCase.TestFile,
occurrenceMap: MutableMap<String, List<OccurrenceInfo>>) {
private fun readExpectedOccurrencesForMultiFileTest(file: TestFile, occurrenceMap: MutableMap<String, List<OccurrenceInfo>>) {
var currentOccurrenceInfos: MutableList<OccurrenceInfo>? = null
for (line in file.content.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
val atOutputFileMatcher = AT_OUTPUT_FILE_PATTERN.matcher(line)
if (atOutputFileMatcher.matches()) {
val outputFileName = atOutputFileMatcher.group(1)
if (occurrenceMap.containsKey(outputFileName)) {
throw AssertionError(
file.name + ": Expected occurrences for output file " + outputFileName + " were already provided")
throw AssertionError("${file.name}: Expected occurrences for output file $outputFileName were already provided")
}
currentOccurrenceInfos = ArrayList<OccurrenceInfo>()
occurrenceMap.put(outputFileName, currentOccurrenceInfos)
currentOccurrenceInfos = ArrayList()
occurrenceMap[outputFileName] = currentOccurrenceInfos
}
val expectedOccurrencesMatcher = EXPECTED_OCCURRENCES_PATTERN.matcher(line)
if (expectedOccurrencesMatcher.matches()) {
if (currentOccurrenceInfos == null) {
throw AssertionError(
file.name + ": Should specify output file with '// @<OUTPUT_FILE_NAME>:' before expectations")
throw AssertionError("${file.name}: Should specify output file with '// @<OUTPUT_FILE_NAME>:' before expectations")
}
val occurrenceInfo = parseOccurrenceInfo(expectedOccurrencesMatcher)
currentOccurrenceInfos.add(occurrenceInfo)
@@ -26,8 +26,7 @@ abstract class AbstractCompileKotlinAgainstInlineKotlinTest : AbstractCompileKot
val sourceFiles = factory1.inputFiles + factory2.inputFiles
InlineTestUtil.checkNoCallsToInline(allGeneratedFiles.filterClassFiles(), sourceFiles)
SMAPTestUtil.checkSMAP(files, allGeneratedFiles.filterClassFiles(), true)
}
catch (e: Throwable) {
} catch (e: Throwable) {
println("FIRST:\n\n${factory1.createText()}\n\nSECOND:\n\n${factory2.createText()}")
throw e
}
@@ -49,13 +49,13 @@ public abstract class AbstractCompileKotlinAgainstKotlinTest extends CodegenTest
}
@Override
protected void doMultiFileTest(@NotNull File wholeFile, @NotNull List<TestFile> files, @Nullable File javaFilesDir) throws Exception {
protected void doMultiFileTest(@NotNull File wholeFile, @NotNull List<TestFile> files, @Nullable File javaFilesDir) {
assert javaFilesDir == null : ".java files are not supported yet in this test";
doTwoFileTest(files);
}
@NotNull
protected Pair<ClassFileFactory, ClassFileFactory> doTwoFileTest(@NotNull List<TestFile> files) throws Exception {
protected Pair<ClassFileFactory, ClassFileFactory> doTwoFileTest(@NotNull List<TestFile> files) {
// Note that it may be beneficial to improve this test to handle many files, compiling them successively against all previous
assert files.size() == 2 || (files.size() == 3 && files.get(2).name.equals("CoroutineUtil.kt")) : "There should be exactly two files in this test";
TestFile fileA = files.get(0);
@@ -17,7 +17,7 @@ abstract class AbstractDumpDeclarationsTest : CodegenTestCase() {
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>, javaFilesDir: File?) {
val expectedResult = KotlinTestUtils.replaceExtension(wholeFile, "json")
dumpToFile = KotlinTestUtils.tmpDirForTest(this).resolve(name + ".json")
dumpToFile = KotlinTestUtils.tmpDirForTest(this).resolve("$name.json")
compile(files, null)
classFileFactory.generationState.destroy()
KotlinTestUtils.assertEqualsToFile(expectedResult, dumpToFile.readText()) {
@@ -34,10 +34,10 @@ abstract class AbstractLightAnalysisModeTest : CodegenTestCase() {
}
val fullTxt = compileWithFullAnalysis(files, javaFilesDir)
.replace("final enum class", "enum class")
.replace("final enum class", "enum class")
val liteTxt = compileWithLightAnalysis(wholeFile, files, javaFilesDir)
.replace("@synthetic.kotlin.jvm.GeneratedByJvmOverloads ", "")
.replace("@synthetic.kotlin.jvm.GeneratedByJvmOverloads ", "")
assertEquals(fullTxt, liteTxt)
}
@@ -53,7 +53,7 @@ abstract class AbstractLightAnalysisModeTest : CodegenTestCase() {
assert(!relativePath.startsWith(".."))
val configuration = createConfiguration(
configurationKind, getJdkKind(files), listOf(getAnnotationsJar()), javaFilesDir?.let(::listOf).orEmpty(), files
configurationKind, getJdkKind(files), listOf(getAnnotationsJar()), javaFilesDir?.let(::listOf).orEmpty(), files
)
val environment = KotlinCoreEnvironment.createForTests(testRootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
AnalysisHandlerExtension.registerExtension(environment.project, PartialAnalysisHandlerExtension())
@@ -65,14 +65,14 @@ abstract class AbstractLightAnalysisModeTest : CodegenTestCase() {
}
protected fun compileWithFullAnalysis(
files: List<TestFile>,
javaSourceDir: File?
files: List<TestFile>,
javaSourceDir: File?
): String {
compile(files, javaSourceDir)
classFileFactory.getClassFiles()
val classInternalNames = classFileFactory.generationState.bindingContext
.getSliceContents(CodegenBinding.ASM_TYPE).map { it.value.internalName to it.key }.toMap()
.getSliceContents(CodegenBinding.ASM_TYPE).map { it.value.internalName to it.key }.toMap()
return BytecodeListingTextCollectingVisitor.getText(classFileFactory, object : ListAnalysisFilter() {
override fun shouldWriteClass(access: Int, name: String): Boolean {
@@ -29,7 +29,7 @@ import kotlin.collections.ArrayList
abstract class AbstractLineNumberTest : CodegenTestCase() {
override fun doMultiFileTest(
wholeFile: File, files: MutableList<CodegenTestCase.TestFile>, javaFilesDir: File?
wholeFile: File, files: MutableList<TestFile>, javaFilesDir: File?
) {
val isCustomTest = wholeFile.parentFile.name.equals("custom", ignoreCase = true)
if (!isCustomTest) {
@@ -159,7 +159,7 @@ abstract class AbstractLineNumberTest : CodegenTestCase() {
private val TEST_LINE_NUMBER_PATTERN = Pattern.compile("^.*test.$LINE_NUMBER_FUN\\(\\).*$")
private fun createLineNumberDeclaration() =
CodegenTestCase.TestFile(
TestFile(
"$LINE_NUMBER_FUN.kt",
"package test;\n\npublic fun $LINE_NUMBER_FUN(): Int = 0\n"
)
@@ -21,13 +21,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.config.CompilerConfiguration;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.test.ConfigurationKind;
import org.jetbrains.kotlin.test.TestJdkKind;
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.List;
import static org.jetbrains.kotlin.script.ScriptTestUtilKt.loadScriptingPlugin;
@@ -56,7 +56,6 @@ import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -82,7 +81,7 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
private static final String DEFAULT_TEST_FILE_NAME = "a_test";
private static final String DEFAULT_JVM_TARGET_FOR_TEST = "kotlin.test.default.jvm.target";
private static final String JAVA_COMPILATION_TARGET = "kotlin.test.java.compilation.target";
public static final String RUN_BOX_TEST_IN_SEPARATE_PROCESS_PORT = "kotlin.test.box.in.separate.process.port";
private static final String RUN_BOX_TEST_IN_SEPARATE_PROCESS_PORT = "kotlin.test.box.in.separate.process.port";
protected KotlinCoreEnvironment myEnvironment;
protected CodegenTestFiles myFiles;
@@ -90,7 +89,7 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
protected GeneratedClassLoader initializedClassLoader;
protected File javaClassesOutputDirectory = null;
protected List<File> additionalDependencies = null;
protected String coroutinesPackage;
protected String coroutinesPackage = "";
protected ConfigurationKind configurationKind = ConfigurationKind.JDK_ONLY;
private final String defaultJvmTarget = System.getProperty(DEFAULT_JVM_TARGET_FOR_TEST);
@@ -99,7 +98,7 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
protected final void createEnvironmentWithMockJdkAndIdeaAnnotations(
@NotNull ConfigurationKind configurationKind,
@Nullable File... javaSourceRoots
@NotNull File... javaSourceRoots
) {
createEnvironmentWithMockJdkAndIdeaAnnotations(configurationKind, Collections.emptyList(), TestJdkKind.MOCK_JDK, javaSourceRoots);
}
@@ -118,7 +117,7 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
@NotNull ConfigurationKind configurationKind,
@NotNull List<TestFile> testFilesWithConfigurationDirectives,
@NotNull TestJdkKind testJdkKind,
@Nullable File... javaSourceRoots
@NotNull File... javaSourceRoots
) {
if (myEnvironment != null) {
throw new IllegalStateException("must not set up myEnvironment twice");
@@ -161,7 +160,7 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
updateConfigurationByDirectivesInTestFiles(testFilesWithConfigurationDirectives, configuration, "");
}
protected static void updateConfigurationByDirectivesInTestFiles(
private static void updateConfigurationByDirectivesInTestFiles(
@NotNull List<TestFile> testFilesWithConfigurationDirectives,
@NotNull CompilerConfiguration configuration,
@NotNull String coroutinesPackage
@@ -306,12 +305,6 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
}
}
@Override
protected void setUp() throws Exception {
coroutinesPackage = "";
super.setUp();
}
@Override
protected void tearDown() throws Exception {
myFiles = null;
@@ -343,7 +336,8 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
assert myFiles == null : "Should not initialize myFiles twice";
myFiles = CodegenTestFiles.create(file.getName(), content, myEnvironment.getProject());
return content;
} catch (IOException e) {
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
@@ -512,44 +506,46 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
}
@NotNull
protected ClassFileFactory generateClassesInFile(boolean reportProblems) {
if (classFileFactory == null) {
try {
GenerationState generationState = GenerationUtils.compileFiles(
myFiles.getPsiFiles(), myEnvironment, getClassBuilderFactory(),
new NoScopeRecordCliBindingTrace()
);
classFileFactory = generationState.getFactory();
private ClassFileFactory generateClassesInFile(boolean reportProblems) {
if (classFileFactory != null) return classFileFactory;
if (verifyWithDex() && DxChecker.RUN_DX_CHECKER) {
DxChecker.check(classFileFactory);
}
try {
GenerationState generationState = GenerationUtils.compileFiles(
myFiles.getPsiFiles(), myEnvironment, getClassBuilderFactory(),
new NoScopeRecordCliBindingTrace()
);
classFileFactory = generationState.getFactory();
if (verifyWithDex() && DxChecker.RUN_DX_CHECKER) {
DxChecker.check(classFileFactory);
}
catch (TestsCompiletimeError e) {
if (reportProblems) {
e.getOriginal().printStackTrace();
System.err.println("Generating instructions as text...");
try {
if (classFileFactory == null) {
System.err.println("Cannot generate text: exception was thrown during generation");
}
else {
System.err.println(classFileFactory.createText());
}
}
catch (TestsCompiletimeError e) {
if (reportProblems) {
e.getOriginal().printStackTrace();
System.err.println("Generating instructions as text...");
try {
if (classFileFactory == null) {
System.err.println("Cannot generate text: exception was thrown during generation");
}
catch (Throwable e1) {
System.err.println("Exception thrown while trying to generate text, the actual exception follows:");
e1.printStackTrace();
System.err.println("-----------------------------------------------------------------------------");
else {
System.err.println(classFileFactory.createText());
}
System.err.println("See exceptions above");
} else {
System.err.println("Compilation failure");
}
throw e;
} catch (Throwable e) {
throw new TestsCompilerError(e);
catch (Throwable e1) {
System.err.println("Exception thrown while trying to generate text, the actual exception follows:");
e1.printStackTrace();
System.err.println("-----------------------------------------------------------------------------");
}
System.err.println("See exceptions above");
}
else {
System.err.println("Compilation failure");
}
throw e;
}
catch (Throwable e) {
throw new TestsCompilerError(e);
}
return classFileFactory;
}
@@ -558,7 +554,7 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
return true;
}
protected static boolean verifyAllFilesWithAsm(ClassFileFactory factory, ClassLoader loader, boolean reportProblems) {
private static boolean verifyAllFilesWithAsm(ClassFileFactory factory, ClassLoader loader, boolean reportProblems) {
boolean noErrors = true;
for (OutputFile file : ClassFileUtilsKt.getClassFiles(factory)) {
noErrors &= verifyWithAsm(file, loader, reportProblems);
@@ -607,7 +603,8 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
Class<?> aClass = generateFacadeClass();
try {
return findTheOnlyMethod(aClass);
} catch (Error e) {
}
catch (Error e) {
System.out.println(generateToText());
throw e;
}
@@ -618,22 +615,11 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
return findDeclaredMethodByName(generateFacadeClass(), name);
}
@NotNull
@SuppressWarnings("unchecked")
public Class<? extends Annotation> loadAnnotationClassQuietly(@NotNull String fqName) {
try {
return (Class<? extends Annotation>) initializedClassLoader.loadClass(fqName);
}
catch (ClassNotFoundException e) {
throw ExceptionUtilsKt.rethrow(e);
}
}
protected void updateConfiguration(@NotNull CompilerConfiguration configuration) {
}
protected ClassBuilderFactory getClassBuilderFactory(){
protected ClassBuilderFactory getClassBuilderFactory() {
return ClassBuilderFactories.TEST;
}
@@ -641,7 +627,7 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
}
protected void setCustomDefaultJvmTarget(CompilerConfiguration configuration) {
private void setCustomDefaultJvmTarget(CompilerConfiguration configuration) {
JvmTarget target = configuration.get(JVMConfigurationKeys.JVM_TARGET);
if (target == null && defaultJvmTarget != null) {
JvmTarget value = JvmTarget.fromString(defaultJvmTarget);
@@ -663,8 +649,8 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
boolean reportProblems
) {
configurationKind = extractConfigurationKind(files);
boolean loadAndroidAnnotations = files.stream().anyMatch(it ->
InTextDirectivesUtils.isDirectiveDefined(it.content, "ANDROID_ANNOTATIONS")
boolean loadAndroidAnnotations = files.stream().anyMatch(
it -> InTextDirectivesUtils.isDirectiveDefined(it.content, "ANDROID_ANNOTATIONS")
);
List<String> javacOptions = extractJavacOptions(files);
@@ -744,7 +730,7 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
return javacOptions;
}
protected void updateJavacOptions(List<String> javacOptions) {
private void updateJavacOptions(List<String> javacOptions) {
if (javaCompilationTarget != null && !javacOptions.contains("-target")) {
javacOptions.add("-source");
javacOptions.add(javaCompilationTarget);
@@ -832,9 +818,9 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
}
protected void doMultiFileTest(
@NotNull File wholeFile,
@NotNull List<TestFile> files,
@Nullable File javaFilesDir
@NotNull File wholeFile,
@NotNull List<TestFile> files,
@Nullable File javaFilesDir
) throws Exception {
throw new UnsupportedOperationException("Multi-file test cases are not supported in this test");
}
@@ -843,7 +829,7 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
throws IOException, InvocationTargetException, IllegalAccessException {
Class<?> aClass = getGeneratedClass(classLoader, className);
Method method = getBoxMethodOrNull(aClass);
assertTrue("Can't find box method in " + aClass,method != null);
assertNotNull("Can't find box method in " + aClass, method);
callBoxMethodAndCheckResult(classLoader, aClass, method);
}
@@ -876,7 +862,8 @@ public abstract class CodegenTestCase extends KtUsefulTestCase {
List<URL> classPath = extractUrls(classLoader);
if (classLoader instanceof GeneratedClassLoader) {
File outDir = KotlinTestUtils.tmpDirForTest(this);
SimpleOutputFileCollection currentOutput = new SimpleOutputFileCollection(((GeneratedClassLoader) classLoader).getAllGeneratedFiles());
SimpleOutputFileCollection currentOutput =
new SimpleOutputFileCollection(((GeneratedClassLoader) classLoader).getAllGeneratedFiles());
writeAllTo(currentOutput, outDir);
classPath.add(0, outDir.toURI().toURL());
}
@@ -127,7 +127,8 @@ public class CodegenTestFiles {
String[] values = valueString.split(" ");
scriptParameterValues.add(values);
} else {
}
else {
scriptParameterValues.add(ArrayUtil.EMPTY_STRING_ARRAY);
}
}
@@ -39,15 +39,17 @@ import java.util.List;
import static org.junit.Assert.assertTrue;
public class CodegenTestUtil {
private CodegenTestUtil() {}
private CodegenTestUtil() {
}
@NotNull
public static ClassFileFactory generateFiles(@NotNull KotlinCoreEnvironment environment, @NotNull CodegenTestFiles files) {
return GenerationUtils.compileFiles(files.getPsiFiles(), environment).getFactory();
}
public static void assertThrows(@NotNull Method foo, @NotNull Class<? extends Throwable> exceptionClass,
@Nullable Object instance, @NotNull Object... args) throws IllegalAccessException {
public static void assertThrows(
@NotNull Method foo, @NotNull Class<? extends Throwable> exceptionClass, @Nullable Object instance, @NotNull Object... args
) throws IllegalAccessException {
boolean caught = false;
try {
foo.invoke(instance, args);
@@ -46,10 +46,6 @@ object GenerationUtils {
writeAllTo(output)
}
@JvmStatic
fun compileFile(ktFile: KtFile, environment: KotlinCoreEnvironment): ClassFileFactory =
compileFiles(listOf(ktFile), environment).factory
@JvmStatic
@JvmOverloads
fun compileFiles(
@@ -35,7 +35,7 @@ object InlineTestUtil {
fun checkNoCallsToInline(outputFiles: Iterable<OutputFile>, sourceFiles: List<KtFile>) {
val inlineInfo = obtainInlineInfo(outputFiles)
val inlineMethods = inlineInfo.inlineMethods
assert(!inlineMethods.isEmpty()) { "There are no inline methods" }
assert(inlineMethods.isNotEmpty()) { "There are no inline methods" }
val notInlinedCalls = checkInlineMethodNotInvoked(outputFiles, inlineMethods)
assert(notInlinedCalls.isEmpty()) { "All inline methods should be inlined but:\n" + notInlinedCalls.joinToString("\n") }
@@ -48,7 +48,7 @@ object InlineTestUtil {
val notInlinedParameters = checkParametersInlined(outputFiles, inlineInfo, sourceFiles)
assert(notInlinedParameters.isEmpty()) {
"All inline parameters should be inlined but:\n${notInlinedParameters.joinToString("\n")}\n" +
"but if you have not inlined lambdas or anonymous objects enable NO_CHECK_LAMBDA_INLINING directive"
"but if you have not inlined lambdas or anonymous objects enable NO_CHECK_LAMBDA_INLINING directive"
}
}
}
@@ -61,7 +61,9 @@ object InlineTestUtil {
val inlineFunctions = inlineFunctionsJvmNames(binaryClass.classHeader)
val classVisitor = object : ClassVisitorWithName() {
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?): MethodVisitor? {
override fun visitMethod(
access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?
): MethodVisitor? {
if (name + desc in inlineFunctions) {
inlineMethods.add(MethodInfo(className, name, desc))
}
@@ -70,7 +72,7 @@ object InlineTestUtil {
}
ClassReader(file.asByteArray()).accept(classVisitor, 0)
binaryClasses.put(classVisitor.className, binaryClass)
binaryClasses[classVisitor.className] = binaryClass
}
return InlineInfo(inlineMethods, binaryClasses)
@@ -84,9 +86,11 @@ object InlineTestUtil {
//if inline function creates anonymous object then do not try to check that all lambdas are inlined
val classVisitor = object : ClassVisitorWithName() {
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?): MethodVisitor? {
override fun visitMethod(
access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?
): MethodVisitor? {
if (name + desc in inlineFunctions) {
return object: MethodNodeWithAnonymousObjectCheck(inlineInfo, access, name, desc, signature, exceptions) {
return object : MethodNodeWithAnonymousObjectCheck(inlineInfo, access, name, desc, signature, exceptions) {
override fun onAnonymousConstructorCallOrSingletonAccess(owner: String) {
doLambdaInliningCheck = false
}
@@ -126,7 +130,9 @@ object InlineTestUtil {
return null
}
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?): MethodVisitor? {
override fun visitMethod(
access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?
): MethodVisitor? {
if (skipMethodsOfThisClass) {
return null
}
@@ -160,11 +166,13 @@ object InlineTestUtil {
return notInlined
}
private fun checkParametersInlined(outputFiles: Iterable<OutputFile>, inlineInfo: InlineInfo, sourceFiles: List<KtFile>): ArrayList<NotInlinedParameter> {
private fun checkParametersInlined(
outputFiles: Iterable<OutputFile>, inlineInfo: InlineInfo, sourceFiles: List<KtFile>
): ArrayList<NotInlinedParameter> {
val skipMethods =
sourceFiles.flatMap {
InTextDirectivesUtils.findLinesWithPrefixesRemoved(it.text, "// SKIP_INLINE_CHECK_IN: ")
}.toSet()
sourceFiles.flatMap {
InTextDirectivesUtils.findLinesWithPrefixesRemoved(it.text, "// SKIP_INLINE_CHECK_IN: ")
}.toSet()
val inlinedMethods = inlineInfo.inlineMethods
val notInlinedParameters = ArrayList<NotInlinedParameter>()
@@ -172,7 +180,9 @@ object InlineTestUtil {
if (!isClassOrPackagePartKind(loadBinaryClass(file))) continue
ClassReader(file.asByteArray()).accept(object : ClassVisitorWithName() {
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?): MethodVisitor? {
override fun visitMethod(
access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?
): MethodVisitor? {
val declaration = MethodInfo(className, name, desc)
//do not check anonymous object creation in inline functions and in package facades
@@ -184,7 +194,7 @@ object InlineTestUtil {
return null
}
return object: MethodNodeWithAnonymousObjectCheck(inlineInfo, access, name, desc, signature, exceptions) {
return object : MethodNodeWithAnonymousObjectCheck(inlineInfo, access, name, desc, signature, exceptions) {
override fun onAnonymousConstructorCallOrSingletonAccess(owner: String) {
val fromCall = MethodInfo(className, this.name, this.desc)
notInlinedParameters.add(NotInlinedParameter(owner, fromCall))
@@ -201,29 +211,27 @@ object InlineTestUtil {
if (classInternalName.startsWith("kotlin/jvm/internal/"))
return true
return isClassOrPackagePartKind(inlineInfo.binaryClasses[classInternalName]!!)
return isClassOrPackagePartKind(inlineInfo.binaryClasses.getValue(classInternalName))
}
private fun isClassOrPackagePartKind(klass: KotlinJvmBinaryClass): Boolean {
return klass.classHeader.kind == KotlinClassHeader.Kind.CLASS && !klass.classId.isLocal
|| klass.classHeader.kind == KotlinClassHeader.Kind.FILE_FACADE /*single file facade equals to package part*/
|| klass.classHeader.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART
|| klass.classHeader.kind == KotlinClassHeader.Kind.FILE_FACADE /*single file facade equals to package part*/
|| klass.classHeader.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART
}
private fun loadBinaryClass(file: OutputFile): KotlinJvmBinaryClass {
val klass = FileBasedKotlinClass.create(file.asByteArray()) {
className, classVersion, classHeader, innerClasses ->
private fun loadBinaryClass(file: OutputFile): KotlinJvmBinaryClass =
FileBasedKotlinClass.create<FileBasedKotlinClass>(file.asByteArray()) { className, classVersion, classHeader, innerClasses ->
object : FileBasedKotlinClass(className, classVersion, classHeader, innerClasses) {
override val location: String
get() = throw UnsupportedOperationException()
override fun getFileContents(): ByteArray = throw UnsupportedOperationException()
override fun hashCode(): Int = throw UnsupportedOperationException()
override fun equals(other: Any?): Boolean = throw UnsupportedOperationException()
override fun toString(): String = throw UnsupportedOperationException()
}
}!!
return klass
}
private class InlineInfo(val inlineMethods: Set<MethodInfo>, val binaryClasses: Map<String, KotlinJvmBinaryClass>)
@@ -242,9 +250,11 @@ object InlineTestUtil {
}
}
private abstract class MethodNodeWithAnonymousObjectCheck(val inlineInfo: InlineInfo, access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?) : MethodNode(Opcodes.API_VERSION, access, name, desc, signature, exceptions) {
private abstract class MethodNodeWithAnonymousObjectCheck(
val inlineInfo: InlineInfo, access: Int, name: String, desc: String, signature: String?, exceptions: Array<String>?
) : MethodNode(Opcodes.API_VERSION, access, name, desc, signature, exceptions) {
private fun isInlineParameterLikeOwner(owner: String) =
"$" in owner && !isTopLevelOrInnerOrPackageClass(owner, inlineInfo)
"$" in owner && !isTopLevelOrInnerOrPackageClass(owner, inlineInfo)
override fun visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, itf: Boolean) {
if ("<init>" == name && isInlineParameterLikeOwner(owner)) {
@@ -41,7 +41,7 @@ object SMAPTestUtil {
}
}, 0)
SMAPAndFile.SMAPAndFile(debugInfo, outputFile.sourceFiles.single(), outputFile.relativePath)
SMAPAndFile(debugInfo, outputFile.sourceFiles.single(), outputFile.relativePath)
}
}
@@ -61,7 +61,10 @@ object SMAPTestUtil {
}
private fun checkExtension(file: CodegenTestCase.TestFile, separateCompilation: Boolean) =
file.name.run { endsWith(".smap") || if (separateCompilation) endsWith(".smap-separate-compilation") else endsWith(".smap-nonseparate-compilation") }
file.name.run {
endsWith(".smap") ||
if (separateCompilation) endsWith(".smap-separate-compilation") else endsWith(".smap-nonseparate-compilation")
}
fun checkSMAP(inputFiles: List<CodegenTestCase.TestFile>, outputFiles: Iterable<OutputFile>, separateCompilation: Boolean) {
if (!GENERATE_SMAP) return
@@ -71,15 +74,15 @@ object SMAPTestUtil {
val compiledData = compiledSmaps.groupBy {
it.sourceFile
}.map {
val smap = it.value.sortedByDescending { it.outputFile }.mapNotNull { it.smap }.joinToString("\n")
val smap = it.value.sortedByDescending(SMAPAndFile::outputFile).mapNotNull(SMAPAndFile::smap).joinToString("\n")
SMAPAndFile(if (smap.isNotEmpty()) smap else null, it.key, "NOT_SORTED")
}.associateBy { it.sourceFile }
for (source in sourceData) {
val ktFileName = "/" + source.sourceFile.
replace(".smap-nonseparate-compilation", ".kt").
replace(".smap-separate-compilation", ".kt").
replace(".smap", ".kt")
val ktFileName = "/" + source.sourceFile
.replace(".smap-nonseparate-compilation", ".kt")
.replace(".smap-separate-compilation", ".kt")
.replace(".smap", ".kt")
val data = compiledData[ktFileName]
Assert.assertEquals("Smap data differs for $ktFileName", normalize(source.smap), normalize(data?.smap))
}
@@ -90,37 +93,32 @@ object SMAPTestUtil {
private fun checkNoConflictMappings(compiledSmap: List<SMAPAndFile>?) {
if (compiledSmap == null) return
compiledSmap.mapNotNull { it.smap }.forEach {
val smap = SMAPParser.parse(it)
val conflictingLines = smap.fileMappings.flatMap {
fileMapping ->
fileMapping.lineMappings.flatMap {
lineMapping: RangeMapping ->
compiledSmap.mapNotNull(SMAPAndFile::smap).forEach { smapString ->
val smap = SMAPParser.parse(smapString)
val conflictingLines = smap.fileMappings.flatMap { fileMapping ->
fileMapping.lineMappings.flatMap { lineMapping: RangeMapping ->
lineMapping.toRange.keysToMap { lineMapping }.entries
}
}.groupBy { it.key }.entries.filter { it.value.size != 1 }
Assert.assertTrue(
conflictingLines.joinToString(separator = "\n") {
"Conflicting mapping for line ${it.key} in ${it.value.joinToString { it.toString() }} "
},
conflictingLines.isEmpty()
conflictingLines.joinToString(separator = "\n") {
"Conflicting mapping for line ${it.key} in ${it.value.joinToString(transform = Any::toString)}"
},
conflictingLines.isEmpty()
)
}
}
private fun normalize(text: String?) =
text?.let { StringUtil.convertLineSeparators(it.trim()) }
text?.let { StringUtil.convertLineSeparators(it.trim()) }
private class SMAPAndFile(val smap: String?, val sourceFile: String, val outputFile: String) {
companion object {
fun SMAPAndFile(smap: String?, sourceFile: File, outputFile: String) =
SMAPAndFile(smap, getPath(sourceFile), outputFile)
constructor(smap: String?, sourceFile: File, outputFile: String) : this(smap, getPath(sourceFile), outputFile)
fun getPath(file: File): String {
return getPath(file.canonicalPath)
}
companion object {
fun getPath(file: File): String =
getPath(file.canonicalPath)
fun getPath(canonicalPath: String): String {
//There are some problems with disk name on windows cause LightVirtualFile return it without disk name
@@ -20,6 +20,6 @@ import org.jetbrains.kotlin.codegen.AbstractBlackBoxCodegenTest
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
abstract class AbstractIrBlackBoxInlineCodegenTest: AbstractBlackBoxCodegenTest() {
abstract class AbstractIrBlackBoxInlineCodegenTest : AbstractBlackBoxCodegenTest() {
override fun updateConfiguration(configuration: CompilerConfiguration) = configuration.put(JVMConfigurationKeys.IR, true)
}
@@ -16,10 +16,7 @@
package org.jetbrains.kotlin.codegen
import junit.framework.TestCase
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.utils.rethrow
import java.lang.reflect.Method
import java.net.URL
import java.net.URLClassLoader
@@ -28,17 +25,13 @@ fun clearReflectionCache(classLoader: ClassLoader) {
val klass = classLoader.loadClass(JvmAbi.REFLECTION_FACTORY_IMPL.asSingleFqName().asString())
val method = klass.getDeclaredMethod("clearCaches")
method.invoke(null)
}
catch (e: ClassNotFoundException) {
} catch (e: ClassNotFoundException) {
// This is OK for a test without kotlin-reflect in the dependencies
}
}
fun ClassLoader?.extractUrls(): List<URL> {
return (this as? URLClassLoader)?.let {
it.urLs.toList() + it.parent.extractUrls()
} ?: emptyList()
}
}