From 095cbea12527d3f2a98d4123f9703906ad59ae78 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Wed, 16 Mar 2016 12:05:09 +0300 Subject: [PATCH] KOTLIN_CONFIGURATION_FLAGS directive for passing compiler configuration parameters to tests. So far, only boolean flags are supported. --- .../AbstractBlackBoxAgainstJavaCodegenTest.kt | 8 +- .../codegen/AbstractBlackBoxCodegenTest.java | 12 +- .../AbstractBlackBoxInlineCodegenTest.kt | 2 +- .../codegen/AbstractBytecodeTextTest.java | 2 +- .../kotlin/codegen/CodegenTestCase.java | 177 ++++++++++++++---- .../kotlin/codegen/CodegenTestUtil.java | 10 +- .../kotlin/codegen/GenerationUtils.java | 48 ++++- 7 files changed, 204 insertions(+), 55 deletions(-) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxAgainstJavaCodegenTest.kt b/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxAgainstJavaCodegenTest.kt index 6cff07f5f4d..a67ebb443c9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxAgainstJavaCodegenTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxAgainstJavaCodegenTest.kt @@ -34,9 +34,11 @@ abstract class AbstractBlackBoxAgainstJavaCodegenTest : AbstractBlackBoxCodegenT CodegenTestUtil.compileJava(findJavaSourcesInDirectory(directory), emptyList(), javacOptions) } - val configuration = KotlinTestUtils.compilerConfigurationForTests( - ConfigurationKind.ALL, jdkKind, KotlinTestUtils.getAnnotationsJar(), javaOutputDir - ) + val configuration = createCompilerConfigurationForTests( + ConfigurationKind.ALL, jdkKind, + /* classpath = */ listOf(KotlinTestUtils.getAnnotationsJar(), javaOutputDir), + /* javaSource = */ emptyList(), + files) myEnvironment = KotlinCoreEnvironment.createForTests(testRootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxCodegenTest.java b/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxCodegenTest.java index ba8058d6a58..e61955a6e8d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxCodegenTest.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxCodegenTest.java @@ -47,7 +47,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static org.jetbrains.kotlin.test.KotlinTestUtils.compilerConfigurationForTests; import static org.jetbrains.kotlin.test.KotlinTestUtils.getAnnotationsJar; public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase { @@ -87,9 +86,11 @@ public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase { @NotNull TestJdkKind jdkKind, @NotNull List javacOptions ) { - CompilerConfiguration configuration = compilerConfigurationForTests( - configurationKind, jdkKind, Collections.singletonList(getAnnotationsJar()), - ArraysKt.filterNotNull(new File[] {javaSourceDir}) + CompilerConfiguration configuration = createCompilerConfigurationForTests( + configurationKind, jdkKind, + Collections.singletonList(getAnnotationsJar()), + ArraysKt.filterNotNull(new File[] {javaSourceDir}), + files ); myEnvironment = KotlinCoreEnvironment.createForTests( @@ -99,7 +100,8 @@ public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase { loadMultiFiles(files); classFileFactory = GenerationUtils.compileManyFilesGetGenerationStateForTest( - myEnvironment.getProject(), myFiles.getPsiFiles(), new JvmPackagePartProvider(myEnvironment) + myEnvironment.getProject(), myFiles.getPsiFiles(), new JvmPackagePartProvider(myEnvironment), + myEnvironment.getConfiguration() ).getFactory(); if (javaSourceDir != null) { diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxInlineCodegenTest.kt b/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxInlineCodegenTest.kt index 35cf2a652a9..7ad77e70713 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxInlineCodegenTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxInlineCodegenTest.kt @@ -21,7 +21,7 @@ import java.io.File abstract class AbstractBlackBoxInlineCodegenTest : AbstractBlackBoxCodegenTest() { override fun doMultiFileTest(file: File, files: List, javaFilesDir: File?) { - createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL) + createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, files) loadMultiFiles(files) blackBox() diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBytecodeTextTest.java b/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBytecodeTextTest.java index 45f31b402f5..944c154d2c2 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBytecodeTextTest.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/AbstractBytecodeTextTest.java @@ -38,7 +38,7 @@ public abstract class AbstractBytecodeTextTest extends CodegenTestCase { @Override protected void doMultiFileTest(@NotNull File wholeFile, @NotNull List files, @Nullable File javaFilesDir) throws Exception { - createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, javaFilesDir); + createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, files, javaFilesDir); loadMultiFiles(files); if (isMultiFileTest(files)) { diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java b/compiler/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java index 87256cd802a..57f31de73e9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java @@ -16,6 +16,8 @@ package org.jetbrains.kotlin.codegen; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; @@ -28,15 +30,19 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.backend.common.output.OutputFile; import org.jetbrains.kotlin.checkers.CheckerTestUtil; +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys; import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles; import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment; +import org.jetbrains.kotlin.cli.jvm.config.JVMConfigurationKeys; import org.jetbrains.kotlin.cli.jvm.config.JvmContentRootsKt; import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime; import org.jetbrains.kotlin.config.CompilerConfiguration; +import org.jetbrains.kotlin.config.CompilerConfigurationKey; import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.test.ConfigurationKind; +import org.jetbrains.kotlin.test.InTextDirectivesUtils; import org.jetbrains.kotlin.test.KotlinTestUtils; import org.jetbrains.kotlin.test.TestJdkKind; import org.jetbrains.kotlin.utils.ExceptionUtilsKt; @@ -54,6 +60,7 @@ 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.Method; import java.net.MalformedURLException; import java.net.URL; @@ -61,6 +68,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static org.jetbrains.kotlin.codegen.CodegenTestUtil.*; import static org.jetbrains.kotlin.test.KotlinTestUtils.compilerConfigurationForTests; @@ -78,14 +87,25 @@ public abstract class CodegenTestCase extends UsefulTestCase { protected final void createEnvironmentWithMockJdkAndIdeaAnnotations( @NotNull ConfigurationKind configurationKind, @Nullable File... javaSourceRoots + ) { + createEnvironmentWithMockJdkAndIdeaAnnotations(configurationKind, Collections.emptyList(), javaSourceRoots); + } + + protected final void createEnvironmentWithMockJdkAndIdeaAnnotations( + @NotNull ConfigurationKind configurationKind, + @NotNull List testFilesWithConfigurationDirectives, + @Nullable File... javaSourceRoots ) { if (myEnvironment != null) { throw new IllegalStateException("must not set up myEnvironment twice"); } - CompilerConfiguration configuration = compilerConfigurationForTests( - configurationKind, TestJdkKind.MOCK_JDK, Collections.singletonList(getAnnotationsJar()), - ArraysKt.filterNotNull(javaSourceRoots) + CompilerConfiguration configuration = createCompilerConfigurationForTests( + configurationKind, + TestJdkKind.MOCK_JDK, + Collections.singletonList(getAnnotationsJar()), + ArraysKt.filterNotNull(javaSourceRoots), + testFilesWithConfigurationDirectives ); myEnvironment = KotlinCoreEnvironment.createForTests( @@ -93,6 +113,96 @@ public abstract class CodegenTestCase extends UsefulTestCase { ); } + @NotNull + protected static CompilerConfiguration createCompilerConfigurationForTests( + @NotNull ConfigurationKind kind, + @NotNull TestJdkKind jdkKind, + @NotNull List classpath, + @NotNull List javaSource, + @NotNull List testFilesWithConfigurationDirectives + ) { + CompilerConfiguration configuration = compilerConfigurationForTests(kind, jdkKind, classpath, javaSource); + + updateConfigurationByDirectivesInTestFiles(testFilesWithConfigurationDirectives, configuration); + + return configuration; + } + + private static void updateConfigurationByDirectivesInTestFiles( + @NotNull List testFilesWithConfigurationDirectives, + @NotNull CompilerConfiguration configuration + ) { + List kotlinConfigurationFlags = new ArrayList(0); + for (TestFile testFile : testFilesWithConfigurationDirectives) { + kotlinConfigurationFlags.addAll(InTextDirectivesUtils.findListWithPrefixes(testFile.content, "// KOTLIN_CONFIGURATION_FLAGS:")); + } + + updateConfigurationWithFlags(configuration, kotlinConfigurationFlags); + } + + private static final Map> FLAG_NAMESPACE_TO_CLASS = ImmutableMap.of( + "CLI", CLIConfigurationKeys.class, + "JVM", JVMConfigurationKeys.class + ); + + private static final List> FLAG_CLASSES = ImmutableList.of(CLIConfigurationKeys.class, JVMConfigurationKeys.class); + + private static final Pattern BOOLEAN_FLAG_PATTERN = Pattern.compile("([+-])(([a-zA-Z_0-9]*)\\.)?([a-zA-Z_0-9]*)"); + + private static void updateConfigurationWithFlags(@NotNull CompilerConfiguration configuration, @NotNull List flags) { + for (String flag : flags) { + Matcher m = BOOLEAN_FLAG_PATTERN.matcher(flag); + if (m.matches()) { + boolean flagEnabled = !"-".equals(m.group(1)); + String flagNamespace = m.group(3); + String flagName = m.group(4); + + tryApplyBooleanFlag(configuration, flag, flagEnabled, flagNamespace, flagName); + } + } + } + + private static void tryApplyBooleanFlag( + @NotNull CompilerConfiguration configuration, + @NotNull String flag, + boolean flagEnabled, + @Nullable String flagNamespace, + @NotNull String flagName + ) { + Class configurationKeysClass; + Field configurationKeyField = null; + if (flagNamespace == null) { + for (Class flagClass : FLAG_CLASSES) { + try { + configurationKeyField = flagClass.getField(flagName); + break; + } + catch (Exception ignored) { + } + } + } + else { + configurationKeysClass = FLAG_NAMESPACE_TO_CLASS.get(flagNamespace); + assert configurationKeysClass != null : "Expected [+|-][namespace.]configurationKey, got: " + flag; + try { + configurationKeyField = configurationKeysClass.getField(flagName); + } + catch (Exception e) { + configurationKeyField = null; + } + } + assert configurationKeyField != null : "Expected [+|-][namespace.]configurationKey, got: " + flag; + + try { + //noinspection unchecked + CompilerConfigurationKey configurationKey = (CompilerConfigurationKey) configurationKeyField.get(null); + configuration.put(configurationKey, flagEnabled); + } + catch (Exception e) { + assert false : "Expected [+|-][namespace.]configurationKey, got: " + flag; + } + } + @Override protected void tearDown() throws Exception { myFiles = null; @@ -187,9 +297,8 @@ public abstract class CodegenTestCase extends UsefulTestCase { protected GeneratedClassLoader createClassLoader() { return new GeneratedClassLoader( generateClassesInFile(), - configurationKind.getWithReflection() - ? ForTestCompileRuntime.runtimeAndReflectJarClassLoader() - : ForTestCompileRuntime.runtimeJarClassLoader(), + configurationKind.getWithReflection() ? ForTestCompileRuntime.runtimeAndReflectJarClassLoader() + : ForTestCompileRuntime.runtimeJarClassLoader(), getClassPathURLs() ); } @@ -378,36 +487,40 @@ public abstract class CodegenTestCase extends UsefulTestCase { String expectedText = KotlinTestUtils.doLoadFile(file); final Ref javaFilesDir = Ref.create(); - List testFiles = - KotlinTestUtils.createTestFiles(file.getName(), expectedText, new KotlinTestUtils.TestFileFactoryNoModules() { - @NotNull - @Override - public TestFile create(@NotNull String fileName, @NotNull String text, @NotNull Map directives) { - if (fileName.endsWith(".java")) { - if (javaFilesDir.isNull()) { - try { - javaFilesDir.set(KotlinTestUtils.tmpDir("java-files")); - } - catch (IOException e) { - throw ExceptionUtilsKt.rethrow(e); - } - } - writeSourceFile(fileName, text, javaFilesDir.get()); - } - - return new TestFile(fileName, text); - } - - private void writeSourceFile(@NotNull String fileName, @NotNull String content, @NotNull File targetDir) { - File file = new File(targetDir, fileName); - KotlinTestUtils.mkdirs(file.getParentFile()); - FilesKt.writeText(file, content, Charsets.UTF_8); - } - }); + List testFiles = createTestFiles(file, expectedText, javaFilesDir); doMultiFileTest(file, testFiles, javaFilesDir.get()); } + @NotNull + private List createTestFiles(File file, String expectedText, final Ref javaFilesDir) { + return KotlinTestUtils.createTestFiles(file.getName(), expectedText, new KotlinTestUtils.TestFileFactoryNoModules() { + @NotNull + @Override + public TestFile create(@NotNull String fileName, @NotNull String text, @NotNull Map directives) { + if (fileName.endsWith(".java")) { + if (javaFilesDir.isNull()) { + try { + javaFilesDir.set(KotlinTestUtils.tmpDir("java-files")); + } + catch (IOException e) { + throw ExceptionUtilsKt.rethrow(e); + } + } + writeSourceFile(fileName, text, javaFilesDir.get()); + } + + return new TestFile(fileName, text); + } + + private void writeSourceFile(@NotNull String fileName, @NotNull String content, @NotNull File targetDir) { + File file = new File(targetDir, fileName); + KotlinTestUtils.mkdirs(file.getParentFile()); + FilesKt.writeText(file, content, Charsets.UTF_8); + } + }); + } + protected void doMultiFileTest(@NotNull File wholeFile, @NotNull List files, @Nullable File javaFilesDir) throws Exception { throw new UnsupportedOperationException("Multi-file test cases are not supported in this test"); } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CodegenTestUtil.java b/compiler/tests/org/jetbrains/kotlin/codegen/CodegenTestUtil.java index ff861471225..a0812fb1941 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CodegenTestUtil.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CodegenTestUtil.java @@ -57,14 +57,18 @@ public class CodegenTestUtil { AnalyzingUtils.throwExceptionOnErrors(analysisResult.getBindingContext()); CompilerConfiguration configuration = environment.getConfiguration(); GenerationState state = new GenerationState( - environment.getProject(), ClassBuilderFactories.TEST, - analysisResult.getModuleDescriptor(), analysisResult.getBindingContext(), files.getPsiFiles(), + environment.getProject(), + ClassBuilderFactories.TEST, + analysisResult.getModuleDescriptor(), + analysisResult.getBindingContext(), + files.getPsiFiles(), configuration.get(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, false), configuration.get(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, false), GenerationState.GenerateClassFilter.GENERATE_ALL, configuration.get(JVMConfigurationKeys.DISABLE_INLINE, false), configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false), - /* useTypeTableInSerializer = */ false + /* useTypeTableInSerializer = */ false, + configuration.get(JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, false) ); KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/GenerationUtils.java b/compiler/tests/org/jetbrains/kotlin/codegen/GenerationUtils.java index 64f0fc7a584..5d7649818fd 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/GenerationUtils.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/GenerationUtils.java @@ -18,10 +18,14 @@ package org.jetbrains.kotlin.codegen; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.analyzer.AnalysisResult; import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider; import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment; +import org.jetbrains.kotlin.cli.jvm.config.JVMConfigurationKeys; import org.jetbrains.kotlin.codegen.state.GenerationState; +import org.jetbrains.kotlin.config.CompilerConfiguration; +import org.jetbrains.kotlin.config.CompilerConfigurationKey; import org.jetbrains.kotlin.descriptors.PackagePartProvider; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil; @@ -49,21 +53,24 @@ public class GenerationUtils { ) { AnalysisResult analysisResult = JvmResolveUtil.analyzeOneFileWithJavaIntegrationAndCheckForErrors(psiFile, new JvmPackagePartProvider(environment)); - return compileFilesGetGenerationState(psiFile.getProject(), analysisResult, Collections.singletonList(psiFile), false); + return compileFilesGetGenerationState(psiFile.getProject(), analysisResult, Collections.singletonList(psiFile), false, null); } @NotNull public static GenerationState compileManyFilesGetGenerationStateForTest(@NotNull Project project, @NotNull List files) { - return compileManyFilesGetGenerationStateForTest(project, files, PackagePartProvider.Companion.getEMPTY()); + return compileManyFilesGetGenerationStateForTest(project, files, PackagePartProvider.Companion.getEMPTY(), null); } @NotNull - public static GenerationState compileManyFilesGetGenerationStateForTest(@NotNull Project project, @NotNull List files, - @NotNull PackagePartProvider packagePartProvider + public static GenerationState compileManyFilesGetGenerationStateForTest( + @NotNull Project project, + @NotNull List files, + @NotNull PackagePartProvider packagePartProvider, + @Nullable CompilerConfiguration configuration ) { AnalysisResult analysisResult = JvmResolveUtil.analyzeFilesWithJavaIntegrationAndCheckForErrors( project, files, packagePartProvider); - return compileFilesGetGenerationState(project, analysisResult, files, false); + return compileFilesGetGenerationState(project, analysisResult, files, false, configuration); } @NotNull @@ -72,20 +79,41 @@ public class GenerationUtils { @NotNull AnalysisResult analysisResult, @NotNull List files, boolean useTypeTableInSerializer + ) { + return compileFilesGetGenerationState(project, analysisResult, files, useTypeTableInSerializer, null); + } + + @NotNull + public static GenerationState compileFilesGetGenerationState( + @NotNull Project project, + @NotNull AnalysisResult analysisResult, + @NotNull List files, + boolean useTypeTableInSerializer, + @Nullable CompilerConfiguration configuration ) { analysisResult.throwIfError(); GenerationState state = new GenerationState( project, ClassBuilderFactories.TEST, analysisResult.getModuleDescriptor(), analysisResult.getBindingContext(), files, - /* disableCallAssertions = */ false, - /* disableParamAssertions = */ false, + getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS, false), + getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS, false), GenerationState.GenerateClassFilter.GENERATE_ALL, - /* disableInline = */ false, - /* disableOptimization = */ false, - useTypeTableInSerializer + getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.DISABLE_INLINE, false), + getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.DISABLE_OPTIMIZATION, false), + useTypeTableInSerializer, + getConfigurationValueOrDefault(configuration, JVMConfigurationKeys.INHERIT_MULTIFILE_PARTS, false) ); KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION); return state; } + + private static T getConfigurationValueOrDefault( + @Nullable CompilerConfiguration configuration, + @NotNull CompilerConfigurationKey key, + T defaultValue + ) { + if (configuration == null) return defaultValue; + return configuration.get(key, defaultValue); + } }