[Test] Migrate AbstractBlackBoxCodegenTest to new infrastructure
This commit is contained in:
committed by
TeamCityServer
parent
f01122d8dc
commit
85c87f7df9
@@ -29,6 +29,17 @@ object KtAssert {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
fun assertNull(message: String, value: Any?) {
|
||||
contract {
|
||||
returns() implies (value == null)
|
||||
}
|
||||
if (value != null) {
|
||||
fail(message)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun assertTrue(message: String, value: Boolean) {
|
||||
if (!value) {
|
||||
|
||||
@@ -19,7 +19,8 @@ enum class TargetBackend(
|
||||
JS_IR(true, JS),
|
||||
JS_IR_ES6(true, JS_IR),
|
||||
WASM(true),
|
||||
ANDROID(false, JVM);
|
||||
ANDROID(false, JVM),
|
||||
NATIVE(true);
|
||||
|
||||
val compatibleWith get() = compatibleWithTargetBackend ?: ANY
|
||||
}
|
||||
|
||||
+2
-2
@@ -4,9 +4,9 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1304
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// IGNORE_BACKEND: JS_IR_ES6
|
||||
// IGNORE_BACKEND: JVM
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
// IGNORE_LIGHT_ANALYSIS
|
||||
// MODULE: lib1
|
||||
// FILE: lib1.kt
|
||||
package pkg
|
||||
@@ -31,4 +31,4 @@ fun box(): String {
|
||||
if (foo() != "42") return "FAIL: ${foo()}"
|
||||
|
||||
return bar()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package helpers
|
||||
|
||||
fun isIR(): Boolean = true
|
||||
@@ -0,0 +1,3 @@
|
||||
package helpers
|
||||
|
||||
fun isIR(): Boolean = false
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
// JVM_TARGET: 1.8
|
||||
|
||||
// FILE: A.java
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ dependencies {
|
||||
testCompileOnly(project(":kotlin-reflect-api"))
|
||||
testRuntimeOnly(project(":kotlin-reflect"))
|
||||
testRuntimeOnly(project(":core:descriptors.runtime"))
|
||||
testRuntimeOnly(androidDxJar())
|
||||
|
||||
testImplementation(projectTests(":generators:test-generator"))
|
||||
|
||||
|
||||
+6841
-2446
File diff suppressed because it is too large
Load Diff
+11
-4
@@ -9,6 +9,8 @@ import org.jetbrains.kotlin.codegen.ClassBuilderFactories
|
||||
import org.jetbrains.kotlin.codegen.DefaultCodegenFactory
|
||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.model.ArtifactKinds
|
||||
import org.jetbrains.kotlin.test.model.BinaryArtifacts
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
@@ -18,12 +20,17 @@ import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
|
||||
class ClassicJvmBackendFacade(
|
||||
testServices: TestServices
|
||||
) : ClassicBackendFacade<BinaryArtifacts.Jvm>(testServices, ArtifactKinds.Jvm) {
|
||||
private val javaCompilerFacade = JavaCompilerFacade(testServices)
|
||||
|
||||
override val additionalDirectives: List<DirectivesContainer>
|
||||
get() = listOf(CodegenTestDirectives)
|
||||
|
||||
override fun transform(
|
||||
module: TestModule,
|
||||
inputArtifact: ClassicBackendInput
|
||||
): BinaryArtifacts.Jvm {
|
||||
val compilerConfiguration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
val (psiFiles, bindingContext, moduleDescriptor, project, languageVersionSettings) = inputArtifact
|
||||
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
val (psiFiles, bindingContext, moduleDescriptor, project, _) = inputArtifact
|
||||
// TODO: add configuring classBuilderFactory
|
||||
val generationState = GenerationState.Builder(
|
||||
project,
|
||||
@@ -31,11 +38,11 @@ class ClassicJvmBackendFacade(
|
||||
moduleDescriptor,
|
||||
bindingContext,
|
||||
psiFiles.toList(),
|
||||
compilerConfiguration
|
||||
configuration
|
||||
).codegenFactory(DefaultCodegenFactory).build()
|
||||
|
||||
KotlinCodegenFacade.compileCorrectFiles(generationState)
|
||||
|
||||
javaCompilerFacade.compileJavaFiles(module, configuration, generationState.factory)
|
||||
return BinaryArtifacts.Jvm(generationState.factory)
|
||||
}
|
||||
}
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.backend.classic
|
||||
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.codegen.ClassFileFactory
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestUtil
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.test.compileJavaFilesExternally
|
||||
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
|
||||
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.assertions
|
||||
import org.jetbrains.kotlin.test.services.javaFiles
|
||||
import org.jetbrains.kotlin.test.services.jvm.compiledClassesManager
|
||||
import org.jetbrains.kotlin.test.services.sourceFileProvider
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
|
||||
class JavaCompilerFacade(private val testServices: TestServices) {
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
fun compileJavaFiles(module: TestModule, configuration: CompilerConfiguration, classFileFactory: ClassFileFactory) {
|
||||
if (module.javaFiles.isEmpty()) return
|
||||
val javaClasspath = buildList {
|
||||
add(testServices.compiledClassesManager.getCompiledKotlinDirForModule(module, classFileFactory).path)
|
||||
addAll(configuration.jvmClasspathRoots.map { it.absolutePath })
|
||||
if (JvmEnvironmentConfigurationDirectives.ANDROID_ANNOTATIONS in module.directives) {
|
||||
add(ForTestCompileRuntime.androidAnnotationsForTests().path)
|
||||
}
|
||||
}
|
||||
|
||||
val javaClassesOutputDirectory = testServices.compiledClassesManager.getOrCreateCompiledJavaDirForModule(module)
|
||||
|
||||
val javacOptions = extractJavacOptions(
|
||||
module,
|
||||
configuration[JVMConfigurationKeys.JVM_TARGET],
|
||||
configuration.getBoolean(JVMConfigurationKeys.ENABLE_JVM_PREVIEW)
|
||||
)
|
||||
val finalJavacOptions = CodegenTestUtil.prepareJavacOptions(
|
||||
javaClasspath,
|
||||
javacOptions,
|
||||
javaClassesOutputDirectory
|
||||
)
|
||||
|
||||
val javaFiles = module.javaFiles.map { testServices.sourceFileProvider.getRealFileForSourceFile(it) }
|
||||
compileJavaFiles(configuration[JVMConfigurationKeys.JVM_TARGET] ?: JvmTarget.DEFAULT, javaFiles, finalJavacOptions)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun extractJavacOptions(module: TestModule, kotlinTarget: JvmTarget?, isJvmPreviewEnabled: Boolean): List<String> {
|
||||
return buildList {
|
||||
addAll(module.directives[CodegenTestDirectives.JAVAC_OPTIONS])
|
||||
if (kotlinTarget != null && isJvmPreviewEnabled) {
|
||||
add("--release")
|
||||
add(kotlinTarget.description)
|
||||
add("--enable-preview")
|
||||
return@buildList
|
||||
}
|
||||
CodegenTestUtil.computeJavaTarget(this, kotlinTarget)?.let { javaTarget ->
|
||||
add("-source")
|
||||
add(javaTarget)
|
||||
add("-target")
|
||||
add(javaTarget)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun compileJavaFiles(jvmTarget: JvmTarget, files: List<File>, javacOptions: List<String>) {
|
||||
val targetIsJava8OrLower = System.getProperty("java.version").startsWith("1.")
|
||||
if (targetIsJava8OrLower) {
|
||||
org.jetbrains.kotlin.test.compileJavaFiles(files, javacOptions, assertions = testServices.assertions)
|
||||
return
|
||||
}
|
||||
val jdkHome = when (jvmTarget) {
|
||||
JvmTarget.JVM_9 -> KtTestUtil.getJdk9Home()
|
||||
JvmTarget.JVM_11 -> KtTestUtil.getJdk11Home()
|
||||
JvmTarget.JVM_15 -> KtTestUtil.getJdk15Home()
|
||||
else -> null
|
||||
} ?: error("JDK for $jvmTarget is not found")
|
||||
|
||||
compileJavaFilesExternally(files, javacOptions, jdkHome)
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.backend.handlers
|
||||
|
||||
import org.jetbrains.kotlin.codegen.BytecodeListingTextCollectingVisitor
|
||||
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
|
||||
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.CHECK_BYTECODE_LISTING
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.model.BinaryArtifacts
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.defaultsProvider
|
||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||
import org.jetbrains.kotlin.test.utils.MultiModuleInfoDumperImpl
|
||||
import org.jetbrains.kotlin.test.utils.withSuffixAndExtension
|
||||
|
||||
class BytecodeListingHandler(testServices: TestServices) : JvmBinaryArtifactHandler(testServices) {
|
||||
override val directivesContainers: List<DirectivesContainer>
|
||||
get() = listOf(CodegenTestDirectives)
|
||||
|
||||
private val multiModuleInfoDumper = MultiModuleInfoDumperImpl()
|
||||
|
||||
override fun processModule(module: TestModule, info: BinaryArtifacts.Jvm) {
|
||||
if (CHECK_BYTECODE_LISTING !in module.directives) return
|
||||
val dump = BytecodeListingTextCollectingVisitor.getText(
|
||||
info.classFileFactory,
|
||||
BytecodeListingTextCollectingVisitor.Filter.ForCodegenTests
|
||||
)
|
||||
multiModuleInfoDumper.builderForModule(module).append(dump)
|
||||
|
||||
}
|
||||
|
||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
|
||||
if (multiModuleInfoDumper.isEmpty()) return
|
||||
val suffix = if (testServices.defaultsProvider.defaultTargetBackend?.isIR == true) "_ir" else ""
|
||||
val file = testServices.moduleStructure.originalTestDataFiles.first()
|
||||
.withSuffixAndExtension(suffix, ".txt")
|
||||
assertions.assertEqualsToFile(file, multiModuleInfoDumper.generateResultingDump())
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.backend.handlers
|
||||
|
||||
import org.jetbrains.kotlin.codegen.D8Checker
|
||||
import org.jetbrains.kotlin.codegen.DxChecker
|
||||
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
|
||||
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.IGNORE_DEXING
|
||||
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.RUN_DEX_CHECKER
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.model.BinaryArtifacts
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
class DxCheckerHandler(testServices: TestServices) : JvmBinaryArtifactHandler(testServices) {
|
||||
override val directivesContainers: List<DirectivesContainer>
|
||||
get() = listOf(CodegenTestDirectives)
|
||||
|
||||
override fun processModule(module: TestModule, info: BinaryArtifacts.Jvm) {
|
||||
if (RUN_DEX_CHECKER !in module.directives || IGNORE_DEXING in module.directives) return
|
||||
DxChecker.check(info.classFileFactory)
|
||||
D8Checker.check(info.classFileFactory)
|
||||
}
|
||||
|
||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
|
||||
}
|
||||
+63
-8
@@ -9,20 +9,24 @@ import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil.getMemberDeclarationsToGenerate
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.codegen.ClassFileFactory
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestUtil
|
||||
import org.jetbrains.kotlin.codegen.GeneratedClassLoader
|
||||
import org.jetbrains.kotlin.codegen.clearReflectionCache
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil.getFileClassInfoNoResolve
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
|
||||
import org.jetbrains.kotlin.test.model.BinaryArtifacts
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
|
||||
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator.Companion.TEST_CONFIGURATION_KIND_KEY
|
||||
import org.jetbrains.kotlin.test.services.jvm.compiledClassesManager
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import java.lang.reflect.Method
|
||||
import java.net.URLClassLoader
|
||||
|
||||
class JvmBoxRunner(
|
||||
testServices: TestServices
|
||||
) : JvmBinaryArtifactHandler(testServices) {
|
||||
class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testServices) {
|
||||
companion object {
|
||||
private val BOX_IN_SEPARATE_PROCESS_PORT = System.getProperty("kotlin.test.box.in.separate.process.port")
|
||||
}
|
||||
@@ -37,17 +41,45 @@ class JvmBoxRunner(
|
||||
|
||||
override fun processModule(module: TestModule, info: BinaryArtifacts.Jvm) {
|
||||
val ktFiles = info.classFileFactory.inputFiles
|
||||
val classLoader = createClassLoader(module, info.classFileFactory)
|
||||
val reportProblems = module.targetBackend !in module.directives[CodegenTestDirectives.IGNORE_BACKEND]
|
||||
val classLoader = createAndVerifyClassLoader(module, info.classFileFactory, reportProblems)
|
||||
for (ktFile in ktFiles) {
|
||||
val className = ktFile.getFacadeFqName() ?: continue
|
||||
val clazz = classLoader.getGeneratedClass(className)
|
||||
val method = clazz.getBoxMethodOrNull() ?: continue
|
||||
boxMethodFound = true
|
||||
callBoxMethodAndCheckResult(classLoader, clazz, method, unexpectedBehaviour = false)
|
||||
callBoxMethodAndCheckResultWithCleanup(
|
||||
info.classFileFactory,
|
||||
classLoader,
|
||||
clazz,
|
||||
method,
|
||||
unexpectedBehaviour = false,
|
||||
reportProblems = reportProblems
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private fun callBoxMethodAndCheckResultWithCleanup(
|
||||
factory: ClassFileFactory,
|
||||
classLoader: URLClassLoader,
|
||||
clazz: Class<*>?,
|
||||
method: Method,
|
||||
unexpectedBehaviour: Boolean,
|
||||
reportProblems: Boolean
|
||||
) {
|
||||
try {
|
||||
callBoxMethodAndCheckResult(classLoader, clazz, method, unexpectedBehaviour)
|
||||
} catch (e: Throwable) {
|
||||
if (reportProblems) {
|
||||
println(factory.createText())
|
||||
}
|
||||
throw e
|
||||
} finally {
|
||||
clearReflectionCache(classLoader)
|
||||
}
|
||||
}
|
||||
|
||||
private fun callBoxMethodAndCheckResult(
|
||||
classLoader: URLClassLoader,
|
||||
clazz: Class<*>?,
|
||||
@@ -78,11 +110,34 @@ class JvmBoxRunner(
|
||||
}
|
||||
}
|
||||
|
||||
private fun createAndVerifyClassLoader(
|
||||
module: TestModule,
|
||||
classFileFactory: ClassFileFactory,
|
||||
reportProblems: Boolean
|
||||
): GeneratedClassLoader {
|
||||
val classLoader = createClassLoader(module, classFileFactory)
|
||||
val verificationSucceeded = CodegenTestUtil.verifyAllFilesWithAsm(classFileFactory, classLoader, reportProblems)
|
||||
if (!verificationSucceeded ) {
|
||||
assertions.fail { "Verification failed: see exceptions above" }
|
||||
}
|
||||
return classLoader
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun createClassLoader(module: TestModule, classFileFactory: ClassFileFactory): GeneratedClassLoader {
|
||||
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
val urls = configuration.jvmClasspathRoots.map { it.toURI().toURL() }
|
||||
val classLoader = URLClassLoader(urls.toTypedArray())
|
||||
return GeneratedClassLoader(classFileFactory, classLoader)
|
||||
val urls = buildList {
|
||||
addAll(configuration.jvmClasspathRoots)
|
||||
testServices.compiledClassesManager.getCompiledJavaDirForModule(module)?.let {
|
||||
add(it)
|
||||
}
|
||||
}.map { it.toURI().toURL() }
|
||||
val parentClassLoader = if (configuration[TEST_CONFIGURATION_KIND_KEY]?.withReflection == true) {
|
||||
ForTestCompileRuntime.runtimeAndReflectJarClassLoader()
|
||||
} else {
|
||||
ForTestCompileRuntime.runtimeJarClassLoader()
|
||||
}
|
||||
return GeneratedClassLoader(classFileFactory, parentClassLoader, *urls.toTypedArray())
|
||||
}
|
||||
|
||||
private fun KtFile.getFacadeFqName(): String? {
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.backend.handlers
|
||||
|
||||
import org.jetbrains.kotlin.resolve.AnalyzingUtils
|
||||
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
|
||||
import org.jetbrains.kotlin.test.frontend.classic.handlers.ClassicFrontendAnalysisHandler
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
class NoCompilationErrorsHandler(testServices: TestServices) : ClassicFrontendAnalysisHandler(testServices) {
|
||||
override fun processModule(module: TestModule, info: ClassicFrontendOutputArtifact) {
|
||||
AnalyzingUtils.throwExceptionOnErrors(info.analysisResult.bindingContext)
|
||||
}
|
||||
|
||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.backend.handlers
|
||||
|
||||
import org.jetbrains.kotlin.TestsCompiletimeError
|
||||
import org.jetbrains.kotlin.resolve.AnalyzingUtils
|
||||
import org.jetbrains.kotlin.test.model.BinaryArtifacts
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
class NoJvmSpecificCompilationErrorsHandler(testServices: TestServices) : JvmBinaryArtifactHandler(testServices) {
|
||||
override fun processModule(module: TestModule, info: BinaryArtifacts.Jvm) {
|
||||
val generationState = info.classFileFactory.generationState
|
||||
try {
|
||||
AnalyzingUtils.throwExceptionOnErrors(generationState.collectedExtraJvmDiagnostics)
|
||||
} catch (e: Throwable) {
|
||||
throw TestsCompiletimeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
|
||||
}
|
||||
+23
@@ -12,4 +12,27 @@ object CodegenTestDirectives : SimpleDirectivesContainer() {
|
||||
val IGNORE_BACKEND by enumDirective<TargetBackend>(
|
||||
description = "Ignore failures of test on target backend"
|
||||
)
|
||||
|
||||
val JAVAC_OPTIONS by stringDirective(
|
||||
description = "Specify javac options to compile java files"
|
||||
)
|
||||
|
||||
val WITH_HELPERS by directive(
|
||||
"""
|
||||
Adds util functions for checking coroutines
|
||||
See files in ./compiler/testData/codegen/helpers/
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val CHECK_BYTECODE_LISTING by directive(
|
||||
description = "Dump resulting bytecode to .txt or _ir.txt file"
|
||||
)
|
||||
|
||||
val RUN_DEX_CHECKER by directive(
|
||||
description = "Run DxChecker and D8Checker"
|
||||
)
|
||||
|
||||
val IGNORE_DEXING by directive(
|
||||
description = "Ignore dex checkers"
|
||||
)
|
||||
}
|
||||
|
||||
+2
-2
@@ -24,8 +24,8 @@ import org.jetbrains.kotlin.test.frontend.classic.handlers.OldNewInferenceMetaIn
|
||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.configuration.ScriptingEnvironmentConfigurator
|
||||
|
||||
|
||||
+2
-2
@@ -15,8 +15,8 @@ import org.jetbrains.kotlin.test.frontend.classic.handlers.OldNewInferenceMetaIn
|
||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
|
||||
|
||||
abstract class AbstractDiagnosticsNativeTest : AbstractKotlinCompilerTest() {
|
||||
override fun TestConfigurationBuilder.configuration() {
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ import org.jetbrains.kotlin.test.frontend.classic.handlers.OldNewInferenceMetaIn
|
||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator
|
||||
|
||||
abstract class AbstractDiagnosticsTestWithJsStdLib : AbstractKotlinCompilerTest() {
|
||||
|
||||
+2
-2
@@ -21,8 +21,8 @@ import org.jetbrains.kotlin.test.frontend.classic.handlers.OldNewInferenceMetaIn
|
||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
|
||||
|
||||
abstract class AbstractDiagnosticsTestWithJvmBackend : AbstractKotlinCompilerTest() {
|
||||
|
||||
+2
-2
@@ -21,8 +21,8 @@ import org.jetbrains.kotlin.test.frontend.fir.handlers.*
|
||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.fir.FirOldFrontendMetaConfigurator
|
||||
|
||||
|
||||
+2
-2
@@ -20,10 +20,10 @@ import org.jetbrains.kotlin.test.frontend.classic.handlers.OldNewInferenceMetaIn
|
||||
import org.jetbrains.kotlin.test.model.BackendKind
|
||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.configuration.*
|
||||
import org.jetbrains.kotlin.test.services.jvm.ForeignAnnotationAgainstCompiledJavaTestSuppressor
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
|
||||
|
||||
abstract class AbstractForeignAnnotationsTestBase : AbstractKotlinCompilerTest() {
|
||||
protected abstract val foreignAnnotationsConfigurator: Constructor<JvmForeignAnnotationsConfigurator>
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ abstract class AbstractKotlinCompilerTest {
|
||||
builder.configuration()
|
||||
}
|
||||
|
||||
fun runTest(@TestDataFile filePath: String) {
|
||||
open fun runTest(@TestDataFile filePath: String) {
|
||||
testRunner(filePath, configuration).runTest(filePath)
|
||||
}
|
||||
}
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.runners
|
||||
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
|
||||
|
||||
abstract class AbstractKotlinCompilerWithTargetBackendTest(
|
||||
override val targetBackend: TargetBackend
|
||||
) : AbstractKotlinCompilerTest(), RunnerWithTargetBackendForTestGeneratorMarker {
|
||||
override fun configure(builder: TestConfigurationBuilder) {
|
||||
super.configure(builder)
|
||||
with(builder) {
|
||||
globalDefaults {
|
||||
targetBackend = this@AbstractKotlinCompilerWithTargetBackendTest.targetBackend
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.runners.codegen
|
||||
|
||||
import org.jetbrains.kotlin.test.Constructor
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.jetbrains.kotlin.test.backend.classic.ClassicJvmBackendFacade
|
||||
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontend2ClassicBackendConverter
|
||||
import org.jetbrains.kotlin.test.model.BackendFacade
|
||||
import org.jetbrains.kotlin.test.model.BinaryArtifacts
|
||||
import org.jetbrains.kotlin.test.model.Frontend2BackendConverter
|
||||
|
||||
open class AbstractBlackBoxCodegenTest : AbstractJvmBlackBoxCodegenTestBase(TargetBackend.JVM) {
|
||||
override val frontendToBackendConverter: Constructor<Frontend2BackendConverter<*, *>>
|
||||
get() = ::ClassicFrontend2ClassicBackendConverter
|
||||
|
||||
override val backendFacade: Constructor<BackendFacade<*, BinaryArtifacts.Jvm>>
|
||||
get() = ::ClassicJvmBackendFacade
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.runners.codegen
|
||||
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.test.Constructor
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.jetbrains.kotlin.test.backend.BlackBoxCodegenSuppressor
|
||||
import org.jetbrains.kotlin.test.backend.handlers.*
|
||||
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
|
||||
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives.RUN_DEX_CHECKER
|
||||
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives.REPORT_JVM_DIAGNOSTICS_ON_FRONTEND
|
||||
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives.USE_PSI_CLASS_FILES_READING
|
||||
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendFacade
|
||||
import org.jetbrains.kotlin.test.model.*
|
||||
import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerWithTargetBackendTest
|
||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.CodegenHelpersSourceFilesProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
|
||||
|
||||
abstract class AbstractJvmBlackBoxCodegenTestBase(
|
||||
targetBackend: TargetBackend
|
||||
) : AbstractKotlinCompilerWithTargetBackendTest(targetBackend) {
|
||||
abstract val frontendToBackendConverter: Constructor<Frontend2BackendConverter<*, *>>
|
||||
abstract val backendFacade: Constructor<BackendFacade<*, BinaryArtifacts.Jvm>>
|
||||
|
||||
override fun TestConfigurationBuilder.configuration() {
|
||||
globalDefaults {
|
||||
frontend = FrontendKinds.ClassicFrontend
|
||||
targetPlatform = JvmPlatforms.defaultJvmPlatform
|
||||
dependencyKind = DependencyKind.Binary
|
||||
}
|
||||
|
||||
defaultDirectives {
|
||||
+USE_PSI_CLASS_FILES_READING
|
||||
+REPORT_JVM_DIAGNOSTICS_ON_FRONTEND
|
||||
+RUN_DEX_CHECKER
|
||||
}
|
||||
|
||||
useConfigurators(
|
||||
::CommonEnvironmentConfigurator,
|
||||
::JvmEnvironmentConfigurator
|
||||
)
|
||||
|
||||
useAdditionalSourceProviders(
|
||||
::AdditionalDiagnosticsSourceFilesProvider,
|
||||
::CoroutineHelpersSourceFilesProvider,
|
||||
::CodegenHelpersSourceFilesProvider,
|
||||
)
|
||||
|
||||
useFrontendFacades(::ClassicFrontendFacade)
|
||||
useFrontend2BackendConverters(frontendToBackendConverter)
|
||||
useBackendFacades(backendFacade)
|
||||
|
||||
useFrontendHandlers(::NoCompilationErrorsHandler)
|
||||
useArtifactsHandlers(
|
||||
::JvmBoxRunner,
|
||||
::NoJvmSpecificCompilationErrorsHandler,
|
||||
::BytecodeListingHandler,
|
||||
::DxCheckerHandler,
|
||||
)
|
||||
|
||||
useAfterAnalysisCheckers(::BlackBoxCodegenSuppressor)
|
||||
}
|
||||
}
|
||||
+58
-12
@@ -10,8 +10,10 @@ import com.intellij.openapi.util.SystemInfo
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
|
||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
@@ -24,18 +26,23 @@ import org.jetbrains.kotlin.test.model.DependencyDescription
|
||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
import org.jetbrains.kotlin.test.services.jvm.CompiledJarManager
|
||||
import org.jetbrains.kotlin.test.services.jvm.compiledJarManager
|
||||
import org.jetbrains.kotlin.test.services.jvm.CompiledClassesManager
|
||||
import org.jetbrains.kotlin.test.services.jvm.compiledClassesManager
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil
|
||||
import org.jetbrains.kotlin.test.util.joinToArrayString
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.io.File
|
||||
|
||||
class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
|
||||
companion object {
|
||||
val TEST_CONFIGURATION_KIND_KEY = CompilerConfigurationKey.create<ConfigurationKind>("ConfigurationKind")
|
||||
}
|
||||
|
||||
override val directivesContainers: List<DirectivesContainer>
|
||||
get() = listOf(JvmEnvironmentConfigurationDirectives)
|
||||
|
||||
override val additionalServices: List<ServiceRegistrationData>
|
||||
get() = listOf(service(::CompiledJarManager))
|
||||
get() = listOf(service(::CompiledClassesManager))
|
||||
|
||||
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule, project: MockProject) {
|
||||
if (module.targetPlatform !in JvmPlatforms.allJvmPlatforms) return
|
||||
@@ -77,7 +84,9 @@ class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfig
|
||||
}
|
||||
}
|
||||
|
||||
val configurationKind = extractConfigurationKind(registeredDirectives)
|
||||
val configurationKind = extractConfigurationKind(registeredDirectives).also {
|
||||
configuration.put(TEST_CONFIGURATION_KIND_KEY, it)
|
||||
}
|
||||
|
||||
if (configurationKind.withRuntime) {
|
||||
configuration.addJvmClasspathRoot(ForTestCompileRuntime.runtimeJarForTests())
|
||||
@@ -119,6 +128,8 @@ class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfig
|
||||
if (JvmEnvironmentConfigurationDirectives.USE_PSI_CLASS_FILES_READING in module.directives) {
|
||||
configuration.put(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING, true)
|
||||
}
|
||||
|
||||
initBinaryDependencies(module, configuration)
|
||||
}
|
||||
|
||||
private fun extractJdkKind(registeredDirectives: RegisteredDirectives): TestJdkKind {
|
||||
@@ -146,12 +157,9 @@ class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfig
|
||||
if (noRuntime && withRuntime) {
|
||||
error("NO_RUNTIME and WITH_RUNTIME can not be used together")
|
||||
}
|
||||
if (withReflect && !withRuntime) {
|
||||
error("WITH_REFLECT may be used only with WITH_RUNTIME")
|
||||
}
|
||||
return when {
|
||||
withRuntime && withReflect -> ConfigurationKind.ALL
|
||||
withRuntime -> ConfigurationKind.NO_KOTLIN_REFLECT
|
||||
withRuntime && !withReflect -> ConfigurationKind.NO_KOTLIN_REFLECT
|
||||
withRuntime || withReflect -> ConfigurationKind.ALL
|
||||
noRuntime -> ConfigurationKind.JDK_NO_RUNTIME
|
||||
else -> ConfigurationKind.JDK_ONLY
|
||||
}
|
||||
@@ -164,9 +172,47 @@ class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfig
|
||||
.map { dependencyProvider.getTestModule(it.moduleName) }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?: return
|
||||
val jarManager = testServices.compiledJarManager
|
||||
val dependenciesClassPath = modulesFromDependencies.map { jarManager.getCompiledJarForModule(it) }
|
||||
val jarManager = testServices.compiledClassesManager
|
||||
val dependenciesClassPath = modulesFromDependencies.map { jarManager.getCompiledKotlinDirForModule(it) }
|
||||
addJvmClasspathRoots(dependenciesClassPath)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun initBinaryDependencies(
|
||||
module: TestModule,
|
||||
configuration: CompilerConfiguration,
|
||||
) {
|
||||
val binaryDependencies = module.dependencies.filter { it.kind == DependencyKind.Binary }
|
||||
val binaryFriends = module.friends.filter { it.kind == DependencyKind.Binary }
|
||||
val dependencyProvider = testServices.dependencyProvider
|
||||
val compiledClassesManager = testServices.compiledClassesManager
|
||||
val compilerConfigurationProvider = testServices.compilerConfigurationProvider
|
||||
|
||||
fun addDependenciesToClasspath(dependencies: List<DependencyDescription>): List<File> {
|
||||
val jvmClasspathRoots = buildList<File> {
|
||||
dependencies.forEach {
|
||||
val dependencyModule = dependencyProvider.getTestModule(it.moduleName)
|
||||
|
||||
add(compiledClassesManager.getCompiledKotlinDirForModule(dependencyModule))
|
||||
addIfNotNull(compiledClassesManager.getCompiledJavaDirForModule(dependencyModule))
|
||||
addAll(compilerConfigurationProvider.getCompilerConfiguration(dependencyModule).jvmClasspathRoots)
|
||||
}
|
||||
}
|
||||
configuration.addJvmClasspathRoots(jvmClasspathRoots)
|
||||
return jvmClasspathRoots
|
||||
}
|
||||
|
||||
addDependenciesToClasspath(binaryDependencies)
|
||||
addDependenciesToClasspath(binaryFriends)
|
||||
|
||||
if (binaryFriends.isNotEmpty()) {
|
||||
configuration.put(JVMConfigurationKeys.FRIEND_PATHS, binaryFriends.flatMap {
|
||||
val friendModule = dependencyProvider.getTestModule(it.moduleName)
|
||||
listOfNotNull(
|
||||
compiledClassesManager.getCompiledKotlinDirForModule(friendModule),
|
||||
compiledClassesManager.getCompiledJavaDirForModule(friendModule)
|
||||
)
|
||||
}.map { it.absolutePath })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-16
@@ -13,21 +13,8 @@ import org.jetbrains.kotlin.test.services.TestServices
|
||||
|
||||
class BackendKindExtractorImpl(testServices: TestServices) : BackendKindExtractor(testServices) {
|
||||
override fun backendKind(targetBackend: TargetBackend?): BackendKind<*> {
|
||||
return when (targetBackend) {
|
||||
TargetBackend.ANY,
|
||||
TargetBackend.JVM,
|
||||
TargetBackend.JVM_OLD,
|
||||
TargetBackend.ANDROID,
|
||||
TargetBackend.JVM_MULTI_MODULE_OLD_AGAINST_IR -> BackendKinds.ClassicBackend
|
||||
|
||||
TargetBackend.JVM_IR,
|
||||
TargetBackend.JVM_MULTI_MODULE_IR_AGAINST_OLD,
|
||||
TargetBackend.JS,
|
||||
TargetBackend.JS_IR,
|
||||
TargetBackend.JS_IR_ES6,
|
||||
TargetBackend.WASM -> BackendKinds.IrBackend
|
||||
|
||||
null -> BackendKind.NoBackend
|
||||
}
|
||||
if (targetBackend == null) return BackendKind.NoBackend
|
||||
return if (targetBackend.isIR) BackendKinds.IrBackend
|
||||
else BackendKinds.ClassicBackend
|
||||
}
|
||||
}
|
||||
|
||||
+7
-2
@@ -278,9 +278,14 @@ class ModuleStructureExtractorImpl(
|
||||
}
|
||||
|
||||
private fun finishFile() {
|
||||
val filename = currentFileName ?: defaultFileName
|
||||
val actualDefaultFileName = if (currentModuleName == null) {
|
||||
defaultFileName
|
||||
} else {
|
||||
"module_${currentModuleName}_$defaultFileName"
|
||||
}
|
||||
val filename = currentFileName ?: actualDefaultFileName
|
||||
if (filesOfCurrentModule.any { it.name == filename }) {
|
||||
error("File with name \"$filename\" already defined in module ${currentModuleName ?: defaultModuleName}")
|
||||
error("File with name \"$filename\" already defined in module ${currentModuleName ?: actualDefaultFileName}")
|
||||
}
|
||||
filesOfCurrentModule.add(
|
||||
TestFile(
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.services.jvm
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.output.writeAll
|
||||
import org.jetbrains.kotlin.codegen.ClassFileFactory
|
||||
import org.jetbrains.kotlin.test.model.ArtifactKinds
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
import java.io.File
|
||||
|
||||
class CompiledClassesManager(val testServices: TestServices) : TestService {
|
||||
private val compiledKotlinCache = mutableMapOf<TestModule, File>()
|
||||
private val compiledJavaCache = mutableMapOf<TestModule, File>()
|
||||
|
||||
fun getCompiledKotlinDirForModule(module: TestModule, classFileFactory: ClassFileFactory? = null): File {
|
||||
return compiledKotlinCache.getOrPut(module) {
|
||||
val outputDir = testServices.createTempDirectory("module_${module.name}_kotlin-classes")
|
||||
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val classFileFactory = classFileFactory
|
||||
?: testServices.dependencyProvider.getArtifact(module, ArtifactKinds.Jvm).classFileFactory
|
||||
val outputFileCollection = SimpleOutputFileCollection(classFileFactory.currentOutput)
|
||||
val messageCollector = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
outputFileCollection.writeAll(outputDir, messageCollector, reportOutputFiles = false)
|
||||
outputDir
|
||||
}
|
||||
}
|
||||
|
||||
fun getCompiledJavaDirForModule(module: TestModule): File? {
|
||||
return compiledJavaCache[module]
|
||||
}
|
||||
|
||||
fun getOrCreateCompiledJavaDirForModule(module: TestModule): File {
|
||||
return compiledJavaCache.getOrPut(module) {
|
||||
testServices.createTempDirectory("module_${module.name}_java-classes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val TestServices.compiledClassesManager: CompiledClassesManager by TestServices.testServiceAccessor()
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.services.jvm
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection
|
||||
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
|
||||
import org.jetbrains.kotlin.cli.common.output.writeAll
|
||||
import org.jetbrains.kotlin.test.model.ArtifactKinds
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
import java.io.File
|
||||
|
||||
class CompiledJarManager(val testServices: TestServices) : TestService {
|
||||
private val jarCache = mutableMapOf<TestModule, File>()
|
||||
|
||||
fun getCompiledJarForModule(module: TestModule): File {
|
||||
return jarCache.getOrPut(module) {
|
||||
val outputDir = testServices.createTempDirectory("module_${module.name}")
|
||||
val classFileFactory = testServices.dependencyProvider.getArtifact(module, ArtifactKinds.Jvm).classFileFactory
|
||||
val outputFileCollection = SimpleOutputFileCollection(classFileFactory.currentOutput)
|
||||
val messageCollector = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
outputFileCollection.writeAll(outputDir, messageCollector, reportOutputFiles = false)
|
||||
classFileFactory.releaseGeneratedOutput()
|
||||
outputDir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val TestServices.compiledJarManager: CompiledJarManager by TestServices.testServiceAccessor()
|
||||
+3
-1
@@ -3,13 +3,15 @@
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.services
|
||||
package org.jetbrains.kotlin.test.services.sourceProviders
|
||||
|
||||
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
||||
import org.jetbrains.kotlin.test.model.TestFile
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.AdditionalSourceProvider
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import java.io.File
|
||||
|
||||
class AdditionalDiagnosticsSourceFilesProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.services.sourceProviders
|
||||
|
||||
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
|
||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
||||
import org.jetbrains.kotlin.test.model.TestFile
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.AdditionalSourceProvider
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import java.io.File
|
||||
|
||||
class CodegenHelpersSourceFilesProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
|
||||
companion object {
|
||||
private const val HELPERS_PATH = "./compiler/testData/codegen/helpers"
|
||||
private const val CLASSIC_BACKEND_PATH = "$HELPERS_PATH/CodegenTestHelpersOldBackend.kt"
|
||||
private const val IR_BACKEND_PATH = "$HELPERS_PATH/CodegenTestHelpersIR.kt"
|
||||
}
|
||||
|
||||
override val directives: List<DirectivesContainer> =
|
||||
listOf(CodegenTestDirectives)
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
override fun produceAdditionalFiles(globalDirectives: RegisteredDirectives, module: TestModule): List<TestFile> {
|
||||
if (CodegenTestDirectives.WITH_HELPERS !in module.directives) return emptyList()
|
||||
return buildList {
|
||||
val targetBackend = module.targetBackend ?: return@buildList
|
||||
val helpersPath = if (targetBackend.isIR) {
|
||||
IR_BACKEND_PATH
|
||||
} else {
|
||||
CLASSIC_BACKEND_PATH
|
||||
}
|
||||
add(File(helpersPath).toTestFile())
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -3,7 +3,7 @@
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.test.services
|
||||
package org.jetbrains.kotlin.test.services.sourceProviders
|
||||
|
||||
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives
|
||||
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives.CHECK_STATE_MACHINE
|
||||
@@ -13,6 +13,8 @@ import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
||||
import org.jetbrains.kotlin.test.model.TestFile
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.services.AdditionalSourceProvider
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import java.io.File
|
||||
|
||||
class CoroutineHelpersSourceFilesProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
|
||||
@@ -28,9 +28,13 @@ val File.firTestDataFile: File
|
||||
}
|
||||
|
||||
fun File.withExtension(extension: String): File {
|
||||
return withSuffixAndExtension(suffix = "", extension)
|
||||
}
|
||||
|
||||
fun File.withSuffixAndExtension(suffix: String, extension: String): File {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val extension = extension.removePrefix(".")
|
||||
return parentFile.resolve("$nameWithoutExtension.$extension")
|
||||
return parentFile.resolve("$nameWithoutExtension$suffix.$extension")
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+2
@@ -26,6 +26,8 @@ import static org.jetbrains.kotlin.test.KotlinTestUtils.assertEqualsToFile;
|
||||
import static org.jetbrains.kotlin.test.clientserver.TestProcessServerKt.getBoxMethodOrNull;
|
||||
import static org.jetbrains.kotlin.test.clientserver.TestProcessServerKt.getGeneratedClass;
|
||||
|
||||
// Prefer using new test runner: org.jetbrains.kotlin.test.runners.codegen.AbstractBlackBoxCodegenTest
|
||||
@Deprecated
|
||||
public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase {
|
||||
protected void doMultiFileTest(
|
||||
@NotNull File wholeFile,
|
||||
|
||||
-289
@@ -8,11 +8,7 @@ package org.jetbrains.kotlin.codegen
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import java.io.File
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
|
||||
abstract class AbstractBytecodeListingTest : CodegenTestCase() {
|
||||
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>) {
|
||||
@@ -57,288 +53,3 @@ abstract class AbstractBytecodeListingTest : CodegenTestCase() {
|
||||
private val IGNORE_ANNOTATIONS = Regex.fromLiteral("// IGNORE_ANNOTATIONS")
|
||||
}
|
||||
}
|
||||
|
||||
class BytecodeListingTextCollectingVisitor(
|
||||
val filter: Filter,
|
||||
val withSignatures: Boolean,
|
||||
api: Int = API_VERSION,
|
||||
val withAnnotations: Boolean = true
|
||||
) : ClassVisitor(api) {
|
||||
companion object {
|
||||
@JvmOverloads
|
||||
fun getText(
|
||||
factory: ClassFileFactory,
|
||||
filter: Filter = Filter.EMPTY,
|
||||
withSignatures: Boolean = false,
|
||||
withAnnotations: Boolean = true
|
||||
) = factory.getClassFiles()
|
||||
.sortedBy { it.relativePath }
|
||||
.mapNotNull {
|
||||
val cr = ClassReader(it.asByteArray())
|
||||
val visitor = BytecodeListingTextCollectingVisitor(filter, withSignatures, withAnnotations = withAnnotations)
|
||||
cr.accept(visitor, ClassReader.SKIP_CODE)
|
||||
|
||||
if (!filter.shouldWriteClass(cr.access, cr.className)) null else visitor.text
|
||||
}.joinToString("\n\n", postfix = "\n")
|
||||
|
||||
private val CLASS_OR_FIELD_OR_METHOD = setOf(ModifierTarget.CLASS, ModifierTarget.FIELD, ModifierTarget.METHOD)
|
||||
private val CLASS_OR_METHOD = setOf(ModifierTarget.CLASS, ModifierTarget.METHOD)
|
||||
private val FIELD_ONLY = setOf(ModifierTarget.FIELD)
|
||||
private val METHOD_ONLY = setOf(ModifierTarget.METHOD)
|
||||
private val FIELD_OR_METHOD = setOf(ModifierTarget.FIELD, ModifierTarget.METHOD)
|
||||
|
||||
// TODO ACC_MANDATED - requires reading Parameters attribute, which we don't generate by default
|
||||
internal val MODIFIERS =
|
||||
arrayOf(
|
||||
Modifier("public", ACC_PUBLIC, CLASS_OR_FIELD_OR_METHOD),
|
||||
Modifier("protected", ACC_PROTECTED, CLASS_OR_FIELD_OR_METHOD),
|
||||
Modifier("private", ACC_PRIVATE, CLASS_OR_FIELD_OR_METHOD),
|
||||
Modifier("synthetic", ACC_SYNTHETIC, CLASS_OR_FIELD_OR_METHOD),
|
||||
Modifier("bridge", ACC_BRIDGE, METHOD_ONLY),
|
||||
Modifier("volatile", ACC_VOLATILE, FIELD_ONLY),
|
||||
Modifier("synchronized", ACC_SYNCHRONIZED, METHOD_ONLY),
|
||||
Modifier("varargs", ACC_VARARGS, METHOD_ONLY),
|
||||
Modifier("transient", ACC_TRANSIENT, FIELD_ONLY),
|
||||
Modifier("native", ACC_NATIVE, METHOD_ONLY),
|
||||
Modifier("deprecated", ACC_DEPRECATED, CLASS_OR_FIELD_OR_METHOD),
|
||||
Modifier("final", ACC_FINAL, CLASS_OR_FIELD_OR_METHOD),
|
||||
Modifier("strict", ACC_STRICT, METHOD_ONLY),
|
||||
Modifier("enum", ACC_ENUM, FIELD_ONLY), // ACC_ENUM modifier on class is handled in 'classOrInterface'
|
||||
Modifier("abstract", ACC_ABSTRACT, CLASS_OR_METHOD, excludedMask = ACC_INTERFACE),
|
||||
Modifier("static", ACC_STATIC, FIELD_OR_METHOD)
|
||||
)
|
||||
}
|
||||
|
||||
interface Filter {
|
||||
fun shouldWriteClass(access: Int, name: String): Boolean
|
||||
fun shouldWriteMethod(access: Int, name: String, desc: String): Boolean
|
||||
fun shouldWriteField(access: Int, name: String, desc: String): Boolean
|
||||
fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?): Boolean
|
||||
|
||||
object EMPTY : Filter {
|
||||
override fun shouldWriteClass(access: Int, name: String) = true
|
||||
override fun shouldWriteMethod(access: Int, name: String, desc: String) = true
|
||||
override fun shouldWriteField(access: Int, name: String, desc: String) = true
|
||||
override fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?) = true
|
||||
}
|
||||
|
||||
object ForCodegenTests : Filter {
|
||||
override fun shouldWriteClass(access: Int, name: String): Boolean = !name.startsWith("helpers/")
|
||||
override fun shouldWriteMethod(access: Int, name: String, desc: String): Boolean = true
|
||||
override fun shouldWriteField(access: Int, name: String, desc: String): Boolean = true
|
||||
override fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?): Boolean = true
|
||||
}
|
||||
}
|
||||
|
||||
private class Declaration(val text: String, val annotations: MutableList<String> = arrayListOf())
|
||||
|
||||
private val declarationsInsideClass = arrayListOf<Declaration>()
|
||||
private val classAnnotations = arrayListOf<String>()
|
||||
private var className = ""
|
||||
private var classAccess = 0
|
||||
private var classSignature: String? = ""
|
||||
|
||||
private fun addAnnotation(desc: String, list: MutableList<String> = declarationsInsideClass.last().annotations) {
|
||||
val name = Type.getType(desc).className
|
||||
list.add("@$name ")
|
||||
}
|
||||
|
||||
private fun addModifier(text: String, list: MutableList<String>) {
|
||||
list.add("$text ")
|
||||
}
|
||||
|
||||
internal enum class ModifierTarget {
|
||||
CLASS, FIELD, METHOD
|
||||
}
|
||||
|
||||
internal class Modifier(
|
||||
val text: String,
|
||||
private val mask: Int,
|
||||
private val applicableTo: Set<ModifierTarget>,
|
||||
private val excludedMask: Int = 0
|
||||
) {
|
||||
fun hasModifier(access: Int, target: ModifierTarget) =
|
||||
access and mask != 0 &&
|
||||
access and excludedMask == 0 &&
|
||||
applicableTo.contains(target)
|
||||
}
|
||||
|
||||
private fun handleModifiers(
|
||||
target: ModifierTarget,
|
||||
access: Int,
|
||||
list: MutableList<String> = declarationsInsideClass.last().annotations
|
||||
) {
|
||||
for (modifier in MODIFIERS) {
|
||||
if (modifier.hasModifier(access, target)) {
|
||||
addModifier(modifier.text, list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getModifiers(target: ModifierTarget, access: Int) =
|
||||
MODIFIERS.filter { it.hasModifier(access, target) }.joinToString(separator = " ") { it.text }
|
||||
|
||||
private fun classOrInterface(access: Int): String {
|
||||
return when {
|
||||
access and ACC_ANNOTATION != 0 -> "annotation class"
|
||||
access and ACC_ENUM != 0 -> "enum class"
|
||||
access and ACC_INTERFACE != 0 -> "interface"
|
||||
else -> "class"
|
||||
}
|
||||
}
|
||||
|
||||
val text: String
|
||||
get() = StringBuilder().apply {
|
||||
if (classAnnotations.isNotEmpty()) {
|
||||
append(classAnnotations.joinToString("\n", postfix = "\n"))
|
||||
}
|
||||
arrayListOf<String>().apply { handleModifiers(ModifierTarget.CLASS, classAccess, this) }.forEach { append(it) }
|
||||
append(classOrInterface(classAccess))
|
||||
if (withSignatures) {
|
||||
append("<$classSignature> ")
|
||||
}
|
||||
append(" ")
|
||||
append(className)
|
||||
if (declarationsInsideClass.isNotEmpty()) {
|
||||
append(" {\n")
|
||||
for (declaration in declarationsInsideClass.sortedBy { it.text }) {
|
||||
append(" ").append(declaration.annotations.joinToString("")).append(declaration.text).append("\n")
|
||||
}
|
||||
append("}")
|
||||
}
|
||||
}.toString()
|
||||
|
||||
override fun visitSource(source: String?, debug: String?) {
|
||||
if (source != null) {
|
||||
declarationsInsideClass.add(Declaration("// source: '$source'"))
|
||||
} else {
|
||||
declarationsInsideClass.add(Declaration("// source: null"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
|
||||
if (!filter.shouldWriteMethod(access, name, desc)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val returnType = Type.getReturnType(desc).className
|
||||
val parameterTypes = Type.getArgumentTypes(desc).map { it.className }
|
||||
val methodAnnotations = arrayListOf<String>()
|
||||
val parameterAnnotations = hashMapOf<Int, MutableList<String>>()
|
||||
|
||||
handleModifiers(ModifierTarget.METHOD, access, methodAnnotations)
|
||||
val methodParamCount = Type.getArgumentTypes(desc).size
|
||||
|
||||
return object : MethodVisitor(API_VERSION) {
|
||||
private var visibleAnnotableParameterCount = methodParamCount
|
||||
private var invisibleAnnotableParameterCount = methodParamCount
|
||||
|
||||
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
||||
if (withAnnotations) {
|
||||
val type = Type.getType(desc).className
|
||||
methodAnnotations += "@$type "
|
||||
}
|
||||
return super.visitAnnotation(desc, visible)
|
||||
}
|
||||
|
||||
override fun visitParameterAnnotation(parameter: Int, desc: String, visible: Boolean): AnnotationVisitor? {
|
||||
if (withAnnotations) {
|
||||
val type = Type.getType(desc).className
|
||||
parameterAnnotations.getOrPut(
|
||||
parameter + methodParamCount - (if (visible) visibleAnnotableParameterCount else invisibleAnnotableParameterCount),
|
||||
{ arrayListOf() }).add("@$type ")
|
||||
}
|
||||
return super.visitParameterAnnotation(parameter, desc, visible)
|
||||
}
|
||||
|
||||
override fun visitEnd() {
|
||||
val parameterWithAnnotations = parameterTypes.mapIndexed { index, parameter ->
|
||||
val annotations = parameterAnnotations.getOrElse(index, { emptyList() }).joinToString("")
|
||||
"${annotations}p$index: $parameter"
|
||||
}.joinToString()
|
||||
val signatureIfRequired = if (withSignatures) "<$signature> " else ""
|
||||
declarationsInsideClass.add(
|
||||
Declaration("${signatureIfRequired}method $name($parameterWithAnnotations): $returnType", methodAnnotations)
|
||||
)
|
||||
super.visitEnd()
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_OVERRIDE")
|
||||
override fun visitAnnotableParameterCount(parameterCount: Int, visible: Boolean) {
|
||||
if (visible)
|
||||
visibleAnnotableParameterCount = parameterCount
|
||||
else {
|
||||
invisibleAnnotableParameterCount = parameterCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
|
||||
if (!filter.shouldWriteField(access, name, desc)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val type = Type.getType(desc).className
|
||||
val fieldSignature = if (withSignatures) "<$signature> " else ""
|
||||
val fieldDeclaration = Declaration("field $fieldSignature$name: $type")
|
||||
declarationsInsideClass.add(fieldDeclaration)
|
||||
handleModifiers(ModifierTarget.FIELD, access)
|
||||
if (access and ACC_VOLATILE != 0) addModifier("volatile", fieldDeclaration.annotations)
|
||||
|
||||
return object : FieldVisitor(API_VERSION) {
|
||||
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
||||
if (withAnnotations) {
|
||||
addAnnotation(desc)
|
||||
}
|
||||
return super.visitAnnotation(desc, visible)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
||||
if (withAnnotations) {
|
||||
val name = Type.getType(desc).className
|
||||
classAnnotations.add("@$name")
|
||||
}
|
||||
return super.visitAnnotation(desc, visible)
|
||||
}
|
||||
|
||||
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array<out String>?) {
|
||||
className = name
|
||||
classAccess = access
|
||||
classSignature = signature
|
||||
}
|
||||
|
||||
override fun visitOuterClass(owner: String, name: String?, descriptor: String?) {
|
||||
if (name == null) {
|
||||
assertNull(descriptor)
|
||||
declarationsInsideClass.add(Declaration("enclosing class $owner"))
|
||||
} else {
|
||||
assertNotNull(descriptor)
|
||||
declarationsInsideClass.add(Declaration("enclosing method $owner.$name$descriptor"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) {
|
||||
if (!filter.shouldWriteInnerClass(name, outerName, innerName)) {
|
||||
return
|
||||
}
|
||||
|
||||
when {
|
||||
innerName == null -> {
|
||||
assertNull(outerName, "Anonymous classes should have neither innerName nor outerName. Name=$name, outerName=$outerName")
|
||||
declarationsInsideClass.add(Declaration("inner (anonymous) class $name"))
|
||||
}
|
||||
outerName == null -> {
|
||||
declarationsInsideClass.add(Declaration("inner (local) class $name $innerName"))
|
||||
}
|
||||
name == "$outerName$$innerName" -> {
|
||||
declarationsInsideClass.add(Declaration("${getModifiers(ModifierTarget.CLASS, access)} inner class $name"))
|
||||
}
|
||||
else -> {
|
||||
declarationsInsideClass.add(Declaration("inner (unrecognized) class $name $outerName $innerName"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ public abstract class CodegenTestCase extends KotlinBaseTest<KotlinBaseTest.Test
|
||||
private static final String DEFAULT_TEST_FILE_NAME = "a_test";
|
||||
private static final String DEFAULT_JVM_TARGET = System.getProperty("kotlin.test.default.jvm.target");
|
||||
public static final String BOX_IN_SEPARATE_PROCESS_PORT = System.getProperty("kotlin.test.box.in.separate.process.port");
|
||||
private static final String JAVA_COMPILATION_TARGET = System.getProperty("kotlin.test.java.compilation.target");
|
||||
|
||||
protected KotlinCoreEnvironment myEnvironment;
|
||||
protected CodegenTestFiles myFiles;
|
||||
@@ -203,7 +202,7 @@ public abstract class CodegenTestCase extends KotlinBaseTest<KotlinBaseTest.Test
|
||||
|
||||
initializedClassLoader = createClassLoader();
|
||||
|
||||
if (!verifyAllFilesWithAsm(generateClassesInFile(reportProblems), initializedClassLoader, reportProblems)) {
|
||||
if (!CodegenTestUtil.verifyAllFilesWithAsm(generateClassesInFile(reportProblems), initializedClassLoader, reportProblems)) {
|
||||
fail("Verification failed: see exceptions above");
|
||||
}
|
||||
|
||||
@@ -388,50 +387,6 @@ public abstract class CodegenTestCase extends KotlinBaseTest<KotlinBaseTest.Test
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean verifyAllFilesWithAsm(ClassFileFactory factory, ClassLoader loader, boolean reportProblems) {
|
||||
boolean noErrors = true;
|
||||
for (OutputFile file : ClassFileUtilsKt.getClassFiles(factory)) {
|
||||
noErrors &= verifyWithAsm(file, loader, reportProblems);
|
||||
}
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
private static boolean verifyWithAsm(@NotNull OutputFile file, ClassLoader loader, boolean reportProblems) {
|
||||
ClassNode classNode = new ClassNode();
|
||||
new ClassReader(file.asByteArray()).accept(classNode, 0);
|
||||
|
||||
SimpleVerifier verifier = new SimpleVerifier();
|
||||
verifier.setClassLoader(loader);
|
||||
Analyzer<BasicValue> analyzer = new Analyzer<>(verifier);
|
||||
|
||||
boolean noErrors = true;
|
||||
for (MethodNode method : classNode.methods) {
|
||||
try {
|
||||
analyzer.analyze(classNode.name, method);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
if (reportProblems) {
|
||||
System.err.println(file.asText());
|
||||
System.err.println(classNode.name + "::" + method.name + method.desc);
|
||||
|
||||
//noinspection InstanceofCatchParameter
|
||||
if (e instanceof AnalyzerException) {
|
||||
// Print the erroneous instruction
|
||||
TraceMethodVisitor tmv = new TraceMethodVisitor(new Textifier());
|
||||
((AnalyzerException) e).node.accept(tmv);
|
||||
PrintWriter pw = new PrintWriter(System.err);
|
||||
tmv.p.print(pw);
|
||||
pw.flush();
|
||||
}
|
||||
|
||||
e.printStackTrace();
|
||||
}
|
||||
noErrors = false;
|
||||
}
|
||||
}
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Method generateFunction() {
|
||||
Class<?> aClass = generateFacadeClass();
|
||||
@@ -575,7 +530,7 @@ public abstract class CodegenTestCase extends KotlinBaseTest<KotlinBaseTest.Test
|
||||
return javacOptions;
|
||||
}
|
||||
|
||||
String javaTarget = computeJavaTarget(javacOptions, kotlinTarget);
|
||||
String javaTarget = CodegenTestUtil.computeJavaTarget(javacOptions, kotlinTarget);
|
||||
if (javaTarget != null) {
|
||||
javacOptions.add("-source");
|
||||
javacOptions.add(javaTarget);
|
||||
@@ -585,20 +540,6 @@ public abstract class CodegenTestCase extends KotlinBaseTest<KotlinBaseTest.Test
|
||||
return javacOptions;
|
||||
}
|
||||
|
||||
private static final boolean IS_SOURCE_6_STILL_SUPPORTED =
|
||||
// JDKs up to 11 do support -source/target 1.6, but later -- don't.
|
||||
Arrays.asList("1.6", "1.7", "1.8", "9", "10", "11").contains(System.getProperty("java.specification.version"));
|
||||
|
||||
private static String computeJavaTarget(@NotNull List<String> javacOptions, @Nullable JvmTarget kotlinTarget) {
|
||||
if (JAVA_COMPILATION_TARGET != null && !javacOptions.contains("-target"))
|
||||
return JAVA_COMPILATION_TARGET;
|
||||
if (kotlinTarget != null && kotlinTarget.compareTo(JvmTarget.JVM_1_6) > 0)
|
||||
return kotlinTarget.getDescription();
|
||||
if (IS_SOURCE_6_STILL_SUPPORTED)
|
||||
return "1.6";
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected TargetBackend getBackend() {
|
||||
|
||||
@@ -25,6 +25,8 @@ dependencies {
|
||||
testCompile(project(":compiler:fir:entrypoint"))
|
||||
testCompile(projectTests(":compiler:test-infrastructure-utils"))
|
||||
testCompile(project(":kotlin-preloader"))
|
||||
testCompile(androidDxJar()) { isTransitive = false }
|
||||
testCompile(commonDep("com.android.tools:r8"))
|
||||
testCompileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
|
||||
testCompile(intellijDep()) {
|
||||
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
|
||||
import org.jetbrains.kotlin.test.KtAssert
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
|
||||
class BytecodeListingTextCollectingVisitor(
|
||||
val filter: Filter,
|
||||
val withSignatures: Boolean,
|
||||
api: Int = Opcodes.API_VERSION,
|
||||
val withAnnotations: Boolean = true
|
||||
) : ClassVisitor(api) {
|
||||
companion object {
|
||||
@JvmOverloads
|
||||
fun getText(
|
||||
factory: ClassFileFactory,
|
||||
filter: Filter = Filter.EMPTY,
|
||||
withSignatures: Boolean = false,
|
||||
withAnnotations: Boolean = true
|
||||
) = factory.getClassFiles()
|
||||
.sortedBy { it.relativePath }
|
||||
.mapNotNull {
|
||||
val cr = ClassReader(it.asByteArray())
|
||||
val visitor = BytecodeListingTextCollectingVisitor(filter, withSignatures, withAnnotations = withAnnotations)
|
||||
cr.accept(visitor, ClassReader.SKIP_CODE)
|
||||
|
||||
if (!filter.shouldWriteClass(cr.access, cr.className)) null else visitor.text
|
||||
}.joinToString("\n\n", postfix = "\n")
|
||||
|
||||
private val CLASS_OR_FIELD_OR_METHOD = setOf(ModifierTarget.CLASS, ModifierTarget.FIELD, ModifierTarget.METHOD)
|
||||
private val CLASS_OR_METHOD = setOf(ModifierTarget.CLASS, ModifierTarget.METHOD)
|
||||
private val FIELD_ONLY = setOf(ModifierTarget.FIELD)
|
||||
private val METHOD_ONLY = setOf(ModifierTarget.METHOD)
|
||||
private val FIELD_OR_METHOD = setOf(ModifierTarget.FIELD, ModifierTarget.METHOD)
|
||||
|
||||
// TODO ACC_MANDATED - requires reading Parameters attribute, which we don't generate by default
|
||||
internal val MODIFIERS =
|
||||
arrayOf(
|
||||
Modifier("public", Opcodes.ACC_PUBLIC, CLASS_OR_FIELD_OR_METHOD),
|
||||
Modifier("protected", Opcodes.ACC_PROTECTED, CLASS_OR_FIELD_OR_METHOD),
|
||||
Modifier("private", Opcodes.ACC_PRIVATE, CLASS_OR_FIELD_OR_METHOD),
|
||||
Modifier("synthetic", Opcodes.ACC_SYNTHETIC, CLASS_OR_FIELD_OR_METHOD),
|
||||
Modifier("bridge", Opcodes.ACC_BRIDGE, METHOD_ONLY),
|
||||
Modifier("volatile", Opcodes.ACC_VOLATILE, FIELD_ONLY),
|
||||
Modifier("synchronized", Opcodes.ACC_SYNCHRONIZED, METHOD_ONLY),
|
||||
Modifier("varargs", Opcodes.ACC_VARARGS, METHOD_ONLY),
|
||||
Modifier("transient", Opcodes.ACC_TRANSIENT, FIELD_ONLY),
|
||||
Modifier("native", Opcodes.ACC_NATIVE, METHOD_ONLY),
|
||||
Modifier("deprecated", Opcodes.ACC_DEPRECATED, CLASS_OR_FIELD_OR_METHOD),
|
||||
Modifier("final", Opcodes.ACC_FINAL, CLASS_OR_FIELD_OR_METHOD),
|
||||
Modifier("strict", Opcodes.ACC_STRICT, METHOD_ONLY),
|
||||
Modifier("enum", Opcodes.ACC_ENUM, FIELD_ONLY), // ACC_ENUM modifier on class is handled in 'classOrInterface'
|
||||
Modifier("abstract", Opcodes.ACC_ABSTRACT, CLASS_OR_METHOD, excludedMask = Opcodes.ACC_INTERFACE),
|
||||
Modifier("static", Opcodes.ACC_STATIC, FIELD_OR_METHOD)
|
||||
)
|
||||
}
|
||||
|
||||
interface Filter {
|
||||
fun shouldWriteClass(access: Int, name: String): Boolean
|
||||
fun shouldWriteMethod(access: Int, name: String, desc: String): Boolean
|
||||
fun shouldWriteField(access: Int, name: String, desc: String): Boolean
|
||||
fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?): Boolean
|
||||
|
||||
object EMPTY : Filter {
|
||||
override fun shouldWriteClass(access: Int, name: String) = true
|
||||
override fun shouldWriteMethod(access: Int, name: String, desc: String) = true
|
||||
override fun shouldWriteField(access: Int, name: String, desc: String) = true
|
||||
override fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?) = true
|
||||
}
|
||||
|
||||
object ForCodegenTests : Filter {
|
||||
override fun shouldWriteClass(access: Int, name: String): Boolean = !name.startsWith("helpers/")
|
||||
override fun shouldWriteMethod(access: Int, name: String, desc: String): Boolean = true
|
||||
override fun shouldWriteField(access: Int, name: String, desc: String): Boolean = true
|
||||
override fun shouldWriteInnerClass(name: String, outerName: String?, innerName: String?): Boolean = true
|
||||
}
|
||||
}
|
||||
|
||||
private class Declaration(val text: String, val annotations: MutableList<String> = arrayListOf())
|
||||
|
||||
private val declarationsInsideClass = arrayListOf<Declaration>()
|
||||
private val classAnnotations = arrayListOf<String>()
|
||||
private var className = ""
|
||||
private var classAccess = 0
|
||||
private var classSignature: String? = ""
|
||||
|
||||
private fun addAnnotation(desc: String, list: MutableList<String> = declarationsInsideClass.last().annotations) {
|
||||
val name = Type.getType(desc).className
|
||||
list.add("@$name ")
|
||||
}
|
||||
|
||||
private fun addModifier(text: String, list: MutableList<String>) {
|
||||
list.add("$text ")
|
||||
}
|
||||
|
||||
internal enum class ModifierTarget {
|
||||
CLASS, FIELD, METHOD
|
||||
}
|
||||
|
||||
internal class Modifier(
|
||||
val text: String,
|
||||
private val mask: Int,
|
||||
private val applicableTo: Set<ModifierTarget>,
|
||||
private val excludedMask: Int = 0
|
||||
) {
|
||||
fun hasModifier(access: Int, target: ModifierTarget) =
|
||||
access and mask != 0 &&
|
||||
access and excludedMask == 0 &&
|
||||
applicableTo.contains(target)
|
||||
}
|
||||
|
||||
private fun handleModifiers(
|
||||
target: ModifierTarget,
|
||||
access: Int,
|
||||
list: MutableList<String> = declarationsInsideClass.last().annotations
|
||||
) {
|
||||
for (modifier in MODIFIERS) {
|
||||
if (modifier.hasModifier(access, target)) {
|
||||
addModifier(modifier.text, list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getModifiers(target: ModifierTarget, access: Int) =
|
||||
MODIFIERS.filter { it.hasModifier(access, target) }.joinToString(separator = " ") { it.text }
|
||||
|
||||
private fun classOrInterface(access: Int): String {
|
||||
return when {
|
||||
access and Opcodes.ACC_ANNOTATION != 0 -> "annotation class"
|
||||
access and Opcodes.ACC_ENUM != 0 -> "enum class"
|
||||
access and Opcodes.ACC_INTERFACE != 0 -> "interface"
|
||||
else -> "class"
|
||||
}
|
||||
}
|
||||
|
||||
val text: String
|
||||
get() = StringBuilder().apply {
|
||||
if (classAnnotations.isNotEmpty()) {
|
||||
append(classAnnotations.joinToString("\n", postfix = "\n"))
|
||||
}
|
||||
arrayListOf<String>().apply { handleModifiers(ModifierTarget.CLASS, classAccess, this) }.forEach { append(it) }
|
||||
append(classOrInterface(classAccess))
|
||||
if (withSignatures) {
|
||||
append("<$classSignature> ")
|
||||
}
|
||||
append(" ")
|
||||
append(className)
|
||||
if (declarationsInsideClass.isNotEmpty()) {
|
||||
append(" {\n")
|
||||
for (declaration in declarationsInsideClass.sortedBy { it.text }) {
|
||||
append(" ").append(declaration.annotations.joinToString("")).append(declaration.text).append("\n")
|
||||
}
|
||||
append("}")
|
||||
}
|
||||
}.toString()
|
||||
|
||||
override fun visitSource(source: String?, debug: String?) {
|
||||
if (source != null) {
|
||||
declarationsInsideClass.add(Declaration("// source: '$source'"))
|
||||
} else {
|
||||
declarationsInsideClass.add(Declaration("// source: null"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
|
||||
if (!filter.shouldWriteMethod(access, name, desc)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val returnType = Type.getReturnType(desc).className
|
||||
val parameterTypes = Type.getArgumentTypes(desc).map { it.className }
|
||||
val methodAnnotations = arrayListOf<String>()
|
||||
val parameterAnnotations = hashMapOf<Int, MutableList<String>>()
|
||||
|
||||
handleModifiers(ModifierTarget.METHOD, access, methodAnnotations)
|
||||
val methodParamCount = Type.getArgumentTypes(desc).size
|
||||
|
||||
return object : MethodVisitor(Opcodes.API_VERSION) {
|
||||
private var visibleAnnotableParameterCount = methodParamCount
|
||||
private var invisibleAnnotableParameterCount = methodParamCount
|
||||
|
||||
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
||||
if (withAnnotations) {
|
||||
val type = Type.getType(desc).className
|
||||
methodAnnotations += "@$type "
|
||||
}
|
||||
return super.visitAnnotation(desc, visible)
|
||||
}
|
||||
|
||||
override fun visitParameterAnnotation(parameter: Int, desc: String, visible: Boolean): AnnotationVisitor? {
|
||||
if (withAnnotations) {
|
||||
val type = Type.getType(desc).className
|
||||
parameterAnnotations.getOrPut(
|
||||
parameter + methodParamCount - (if (visible) visibleAnnotableParameterCount else invisibleAnnotableParameterCount),
|
||||
{ arrayListOf() }).add("@$type ")
|
||||
}
|
||||
return super.visitParameterAnnotation(parameter, desc, visible)
|
||||
}
|
||||
|
||||
override fun visitEnd() {
|
||||
val parameterWithAnnotations = parameterTypes.mapIndexed { index, parameter ->
|
||||
val annotations = parameterAnnotations.getOrElse(index, { emptyList() }).joinToString("")
|
||||
"${annotations}p$index: $parameter"
|
||||
}.joinToString()
|
||||
val signatureIfRequired = if (withSignatures) "<$signature> " else ""
|
||||
declarationsInsideClass.add(
|
||||
Declaration("${signatureIfRequired}method $name($parameterWithAnnotations): $returnType", methodAnnotations)
|
||||
)
|
||||
super.visitEnd()
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_OVERRIDE")
|
||||
override fun visitAnnotableParameterCount(parameterCount: Int, visible: Boolean) {
|
||||
if (visible)
|
||||
visibleAnnotableParameterCount = parameterCount
|
||||
else {
|
||||
invisibleAnnotableParameterCount = parameterCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
|
||||
if (!filter.shouldWriteField(access, name, desc)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val type = Type.getType(desc).className
|
||||
val fieldSignature = if (withSignatures) "<$signature> " else ""
|
||||
val fieldDeclaration = Declaration("field $fieldSignature$name: $type")
|
||||
declarationsInsideClass.add(fieldDeclaration)
|
||||
handleModifiers(ModifierTarget.FIELD, access)
|
||||
if (access and Opcodes.ACC_VOLATILE != 0) addModifier("volatile", fieldDeclaration.annotations)
|
||||
|
||||
return object : FieldVisitor(Opcodes.API_VERSION) {
|
||||
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
||||
if (withAnnotations) {
|
||||
addAnnotation(desc)
|
||||
}
|
||||
return super.visitAnnotation(desc, visible)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
|
||||
if (withAnnotations) {
|
||||
val name = Type.getType(desc).className
|
||||
classAnnotations.add("@$name")
|
||||
}
|
||||
return super.visitAnnotation(desc, visible)
|
||||
}
|
||||
|
||||
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array<out String>?) {
|
||||
className = name
|
||||
classAccess = access
|
||||
classSignature = signature
|
||||
}
|
||||
|
||||
override fun visitOuterClass(owner: String, name: String?, descriptor: String?) {
|
||||
if (name == null) {
|
||||
KtAssert.assertNull("", descriptor)
|
||||
declarationsInsideClass.add(Declaration("enclosing class $owner"))
|
||||
} else {
|
||||
KtAssert.assertNotNull("", descriptor)
|
||||
declarationsInsideClass.add(Declaration("enclosing method $owner.$name$descriptor"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) {
|
||||
if (!filter.shouldWriteInnerClass(name, outerName, innerName)) {
|
||||
return
|
||||
}
|
||||
|
||||
when {
|
||||
innerName == null -> {
|
||||
KtAssert.assertNull("Anonymous classes should have neither innerName nor outerName. Name=$name, outerName=$outerName", outerName)
|
||||
declarationsInsideClass.add(Declaration("inner (anonymous) class $name"))
|
||||
}
|
||||
outerName == null -> {
|
||||
declarationsInsideClass.add(Declaration("inner (local) class $name $innerName"))
|
||||
}
|
||||
name == "$outerName$$innerName" -> {
|
||||
declarationsInsideClass.add(Declaration("${getModifiers(ModifierTarget.CLASS, access)} inner class $name"))
|
||||
}
|
||||
else -> {
|
||||
declarationsInsideClass.add(Declaration("inner (unrecognized) class $name $outerName $innerName"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,17 +11,29 @@ import kotlin.collections.CollectionsKt;
|
||||
import kotlin.io.FilesKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
||||
import org.jetbrains.kotlin.config.JvmTarget;
|
||||
import org.jetbrains.kotlin.test.Assertions;
|
||||
import org.jetbrains.kotlin.test.JvmCompilationUtils;
|
||||
import org.jetbrains.kotlin.test.KtAssert;
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
import org.jetbrains.kotlin.utils.StringsKt;
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader;
|
||||
import org.jetbrains.org.objectweb.asm.tree.ClassNode;
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue;
|
||||
import org.jetbrains.org.objectweb.asm.tree.analysis.SimpleVerifier;
|
||||
import org.jetbrains.org.objectweb.asm.util.Textifier;
|
||||
import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
@@ -165,4 +177,65 @@ public class CodegenTestUtil {
|
||||
|
||||
return javaFilePaths;
|
||||
}
|
||||
|
||||
private static final boolean IS_SOURCE_6_STILL_SUPPORTED =
|
||||
// JDKs up to 11 do support -source/target 1.6, but later -- don't.
|
||||
Arrays.asList("1.6", "1.7", "1.8", "9", "10", "11").contains(System.getProperty("java.specification.version"));
|
||||
|
||||
private static final String JAVA_COMPILATION_TARGET = System.getProperty("kotlin.test.java.compilation.target");
|
||||
|
||||
@Nullable
|
||||
public static String computeJavaTarget(@NotNull List<String> javacOptions, @Nullable JvmTarget kotlinTarget) {
|
||||
if (JAVA_COMPILATION_TARGET != null && !javacOptions.contains("-target"))
|
||||
return JAVA_COMPILATION_TARGET;
|
||||
if (kotlinTarget != null && kotlinTarget.compareTo(JvmTarget.JVM_1_6) > 0)
|
||||
return kotlinTarget.getDescription();
|
||||
if (IS_SOURCE_6_STILL_SUPPORTED)
|
||||
return "1.6";
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean verifyAllFilesWithAsm(ClassFileFactory factory, ClassLoader loader, boolean reportProblems) {
|
||||
boolean noErrors = true;
|
||||
for (OutputFile file : ClassFileUtilsKt.getClassFiles(factory)) {
|
||||
noErrors &= verifyWithAsm(file, loader, reportProblems);
|
||||
}
|
||||
return noErrors;
|
||||
}
|
||||
|
||||
private static boolean verifyWithAsm(@NotNull OutputFile file, ClassLoader loader, boolean reportProblems) {
|
||||
ClassNode classNode = new ClassNode();
|
||||
new ClassReader(file.asByteArray()).accept(classNode, 0);
|
||||
|
||||
SimpleVerifier verifier = new SimpleVerifier();
|
||||
verifier.setClassLoader(loader);
|
||||
Analyzer<BasicValue> analyzer = new Analyzer<>(verifier);
|
||||
|
||||
boolean noErrors = true;
|
||||
for (MethodNode method : classNode.methods) {
|
||||
try {
|
||||
analyzer.analyze(classNode.name, method);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
if (reportProblems) {
|
||||
System.err.println(file.asText());
|
||||
System.err.println(classNode.name + "::" + method.name + method.desc);
|
||||
|
||||
//noinspection InstanceofCatchParameter
|
||||
if (e instanceof AnalyzerException) {
|
||||
// Print the erroneous instruction
|
||||
TraceMethodVisitor tmv = new TraceMethodVisitor(new Textifier());
|
||||
((AnalyzerException) e).node.accept(tmv);
|
||||
PrintWriter pw = new PrintWriter(System.err);
|
||||
tmv.p.print(pw);
|
||||
pw.flush();
|
||||
}
|
||||
|
||||
e.printStackTrace();
|
||||
}
|
||||
noErrors = false;
|
||||
}
|
||||
}
|
||||
return noErrors;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -9,7 +9,7 @@ import com.android.tools.r8.*;
|
||||
import com.android.tools.r8.origin.PathOrigin;
|
||||
import kotlin.Pair;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||
import org.junit.Assert;
|
||||
import org.jetbrains.kotlin.test.KtAssert;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
@@ -44,17 +44,17 @@ public class D8Checker {
|
||||
static class TestDiagnosticsHandler implements DiagnosticsHandler {
|
||||
@Override
|
||||
public void error(Diagnostic diagnostic) {
|
||||
Assert.fail("D8 dexing error: " + diagnostic.getDiagnosticMessage());
|
||||
KtAssert.fail("D8 dexing error: " + diagnostic.getDiagnosticMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(Diagnostic diagnostic) {
|
||||
Assert.fail("D8 dexing warning: " + diagnostic.getDiagnosticMessage());
|
||||
KtAssert.fail("D8 dexing warning: " + diagnostic.getDiagnosticMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Diagnostic diagnostic) {
|
||||
Assert.fail("D8 dexing info: " + diagnostic.getDiagnosticMessage());
|
||||
KtAssert.fail("D8 dexing info: " + diagnostic.getDiagnosticMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public class D8Checker {
|
||||
D8.run(builder.build(), Executors.newSingleThreadExecutor());
|
||||
}
|
||||
catch (CompilationFailedException e) {
|
||||
Assert.fail(generateExceptionMessage(e));
|
||||
KtAssert.fail(generateExceptionMessage(e));
|
||||
}
|
||||
}
|
||||
|
||||
+4
-15
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen;
|
||||
@@ -23,8 +12,8 @@ import com.android.dx.dex.cf.CfTranslator;
|
||||
import com.android.dx.dex.file.DexFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||
import org.jetbrains.kotlin.test.KtAssert;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -52,7 +41,7 @@ public class DxChecker {
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
Assert.fail(generateExceptionMessage(e));
|
||||
KtAssert.fail(generateExceptionMessage(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-13
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen
|
||||
-4
@@ -94,10 +94,6 @@ fun generateJUnit3CompilerTests(args: Array<String>) {
|
||||
model("parseCodeFragment/block", testMethod = "doBlockCodeFragmentParsingTest", extension = "kt")
|
||||
}
|
||||
|
||||
testClass<AbstractBlackBoxCodegenTest> {
|
||||
model("codegen/box", targetBackend = TargetBackend.JVM)
|
||||
}
|
||||
|
||||
testClass<AbstractLightAnalysisModeTest> {
|
||||
// "ranges/stepped" is excluded because it contains hundreds of generated tests and only have a box() method.
|
||||
// There isn't much to be gained from running light analysis tests on them.
|
||||
|
||||
+5
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.test.generators
|
||||
import org.jetbrains.kotlin.generators.util.TestGeneratorUtil
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.jetbrains.kotlin.test.runners.*
|
||||
import org.jetbrains.kotlin.test.runners.codegen.AbstractBlackBoxCodegenTest
|
||||
|
||||
fun generateJUnit5CompilerTests(args: Array<String>) {
|
||||
val excludedFirTestdataPattern = "^(.+)\\.fir\\.kts?\$"
|
||||
@@ -53,6 +54,10 @@ fun generateJUnit5CompilerTests(args: Array<String>) {
|
||||
model("foreignAnnotations/tests")
|
||||
model("foreignAnnotations/java8Tests", excludeDirs = listOf("jspecify", "typeEnhancementOnCompiledJava"))
|
||||
}
|
||||
|
||||
testClass<AbstractBlackBoxCodegenTest> {
|
||||
model("codegen/box")
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------- FIR tests ----------------------------------------------
|
||||
|
||||
+5
-5
@@ -16223,11 +16223,6 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class SerializationRegressions extends AbstractLightAnalysisModeTest {
|
||||
@TestMetadata("transitiveClash.kt")
|
||||
public void ignoreTransitiveClash() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/ir/serializationRegressions/transitiveClash.kt");
|
||||
}
|
||||
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
||||
}
|
||||
@@ -16261,6 +16256,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/ir/serializationRegressions/signatureClash.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("transitiveClash.kt")
|
||||
public void testTransitiveClash() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/ir/serializationRegressions/transitiveClash.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("useImportedMember.kt")
|
||||
public void testUseImportedMember() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/ir/serializationRegressions/useImportedMember.kt");
|
||||
|
||||
Reference in New Issue
Block a user