[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
|
@JvmStatic
|
||||||
fun assertTrue(message: String, value: Boolean) {
|
fun assertTrue(message: String, value: Boolean) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ enum class TargetBackend(
|
|||||||
JS_IR(true, JS),
|
JS_IR(true, JS),
|
||||||
JS_IR_ES6(true, JS_IR),
|
JS_IR_ES6(true, JS_IR),
|
||||||
WASM(true),
|
WASM(true),
|
||||||
ANDROID(false, JVM);
|
ANDROID(false, JVM),
|
||||||
|
NATIVE(true);
|
||||||
|
|
||||||
val compatibleWith get() = compatibleWithTargetBackend ?: ANY
|
val compatibleWith get() = compatibleWithTargetBackend ?: ANY
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -4,9 +4,9 @@
|
|||||||
// EXPECTED_REACHABLE_NODES: 1304
|
// EXPECTED_REACHABLE_NODES: 1304
|
||||||
// IGNORE_BACKEND: JS_IR
|
// IGNORE_BACKEND: JS_IR
|
||||||
// IGNORE_BACKEND: JS_IR_ES6
|
// IGNORE_BACKEND: JS_IR_ES6
|
||||||
// IGNORE_BACKEND: JVM
|
|
||||||
// IGNORE_BACKEND: JVM_IR
|
// IGNORE_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND: NATIVE
|
// IGNORE_BACKEND: NATIVE
|
||||||
|
// IGNORE_LIGHT_ANALYSIS
|
||||||
// MODULE: lib1
|
// MODULE: lib1
|
||||||
// FILE: lib1.kt
|
// FILE: lib1.kt
|
||||||
package pkg
|
package pkg
|
||||||
@@ -31,4 +31,4 @@ fun box(): String {
|
|||||||
if (foo() != "42") return "FAIL: ${foo()}"
|
if (foo() != "42") return "FAIL: ${foo()}"
|
||||||
|
|
||||||
return bar()
|
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
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
|
// JVM_TARGET: 1.8
|
||||||
|
|
||||||
// FILE: A.java
|
// FILE: A.java
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ dependencies {
|
|||||||
testCompileOnly(project(":kotlin-reflect-api"))
|
testCompileOnly(project(":kotlin-reflect-api"))
|
||||||
testRuntimeOnly(project(":kotlin-reflect"))
|
testRuntimeOnly(project(":kotlin-reflect"))
|
||||||
testRuntimeOnly(project(":core:descriptors.runtime"))
|
testRuntimeOnly(project(":core:descriptors.runtime"))
|
||||||
|
testRuntimeOnly(androidDxJar())
|
||||||
|
|
||||||
testImplementation(projectTests(":generators:test-generator"))
|
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.DefaultCodegenFactory
|
||||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
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.ArtifactKinds
|
||||||
import org.jetbrains.kotlin.test.model.BinaryArtifacts
|
import org.jetbrains.kotlin.test.model.BinaryArtifacts
|
||||||
import org.jetbrains.kotlin.test.model.TestModule
|
import org.jetbrains.kotlin.test.model.TestModule
|
||||||
@@ -18,12 +20,17 @@ import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
|
|||||||
class ClassicJvmBackendFacade(
|
class ClassicJvmBackendFacade(
|
||||||
testServices: TestServices
|
testServices: TestServices
|
||||||
) : ClassicBackendFacade<BinaryArtifacts.Jvm>(testServices, ArtifactKinds.Jvm) {
|
) : ClassicBackendFacade<BinaryArtifacts.Jvm>(testServices, ArtifactKinds.Jvm) {
|
||||||
|
private val javaCompilerFacade = JavaCompilerFacade(testServices)
|
||||||
|
|
||||||
|
override val additionalDirectives: List<DirectivesContainer>
|
||||||
|
get() = listOf(CodegenTestDirectives)
|
||||||
|
|
||||||
override fun transform(
|
override fun transform(
|
||||||
module: TestModule,
|
module: TestModule,
|
||||||
inputArtifact: ClassicBackendInput
|
inputArtifact: ClassicBackendInput
|
||||||
): BinaryArtifacts.Jvm {
|
): BinaryArtifacts.Jvm {
|
||||||
val compilerConfiguration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||||
val (psiFiles, bindingContext, moduleDescriptor, project, languageVersionSettings) = inputArtifact
|
val (psiFiles, bindingContext, moduleDescriptor, project, _) = inputArtifact
|
||||||
// TODO: add configuring classBuilderFactory
|
// TODO: add configuring classBuilderFactory
|
||||||
val generationState = GenerationState.Builder(
|
val generationState = GenerationState.Builder(
|
||||||
project,
|
project,
|
||||||
@@ -31,11 +38,11 @@ class ClassicJvmBackendFacade(
|
|||||||
moduleDescriptor,
|
moduleDescriptor,
|
||||||
bindingContext,
|
bindingContext,
|
||||||
psiFiles.toList(),
|
psiFiles.toList(),
|
||||||
compilerConfiguration
|
configuration
|
||||||
).codegenFactory(DefaultCodegenFactory).build()
|
).codegenFactory(DefaultCodegenFactory).build()
|
||||||
|
|
||||||
KotlinCodegenFacade.compileCorrectFiles(generationState)
|
KotlinCodegenFacade.compileCorrectFiles(generationState)
|
||||||
|
javaCompilerFacade.compileJavaFiles(module, configuration, generationState.factory)
|
||||||
return BinaryArtifacts.Jvm(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.backend.common.CodegenUtil.getMemberDeclarationsToGenerate
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
|
||||||
import org.jetbrains.kotlin.codegen.ClassFileFactory
|
import org.jetbrains.kotlin.codegen.ClassFileFactory
|
||||||
|
import org.jetbrains.kotlin.codegen.CodegenTestUtil
|
||||||
import org.jetbrains.kotlin.codegen.GeneratedClassLoader
|
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.fileClasses.JvmFileClassUtil.getFileClassInfoNoResolve
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
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.BinaryArtifacts
|
||||||
import org.jetbrains.kotlin.test.model.TestModule
|
import org.jetbrains.kotlin.test.model.TestModule
|
||||||
import org.jetbrains.kotlin.test.services.TestServices
|
import org.jetbrains.kotlin.test.services.TestServices
|
||||||
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
|
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 org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
|
|
||||||
class JvmBoxRunner(
|
class JvmBoxRunner(testServices: TestServices) : JvmBinaryArtifactHandler(testServices) {
|
||||||
testServices: TestServices
|
|
||||||
) : JvmBinaryArtifactHandler(testServices) {
|
|
||||||
companion object {
|
companion object {
|
||||||
private val BOX_IN_SEPARATE_PROCESS_PORT = System.getProperty("kotlin.test.box.in.separate.process.port")
|
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) {
|
override fun processModule(module: TestModule, info: BinaryArtifacts.Jvm) {
|
||||||
val ktFiles = info.classFileFactory.inputFiles
|
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) {
|
for (ktFile in ktFiles) {
|
||||||
val className = ktFile.getFacadeFqName() ?: continue
|
val className = ktFile.getFacadeFqName() ?: continue
|
||||||
val clazz = classLoader.getGeneratedClass(className)
|
val clazz = classLoader.getGeneratedClass(className)
|
||||||
val method = clazz.getBoxMethodOrNull() ?: continue
|
val method = clazz.getBoxMethodOrNull() ?: continue
|
||||||
boxMethodFound = true
|
boxMethodFound = true
|
||||||
callBoxMethodAndCheckResult(classLoader, clazz, method, unexpectedBehaviour = false)
|
callBoxMethodAndCheckResultWithCleanup(
|
||||||
|
info.classFileFactory,
|
||||||
|
classLoader,
|
||||||
|
clazz,
|
||||||
|
method,
|
||||||
|
unexpectedBehaviour = false,
|
||||||
|
reportProblems = reportProblems
|
||||||
|
)
|
||||||
return
|
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(
|
private fun callBoxMethodAndCheckResult(
|
||||||
classLoader: URLClassLoader,
|
classLoader: URLClassLoader,
|
||||||
clazz: Class<*>?,
|
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 {
|
private fun createClassLoader(module: TestModule, classFileFactory: ClassFileFactory): GeneratedClassLoader {
|
||||||
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||||
val urls = configuration.jvmClasspathRoots.map { it.toURI().toURL() }
|
val urls = buildList {
|
||||||
val classLoader = URLClassLoader(urls.toTypedArray())
|
addAll(configuration.jvmClasspathRoots)
|
||||||
return GeneratedClassLoader(classFileFactory, classLoader)
|
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? {
|
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>(
|
val IGNORE_BACKEND by enumDirective<TargetBackend>(
|
||||||
description = "Ignore failures of test on target backend"
|
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.DependencyKind
|
||||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||||
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
|
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
|
||||||
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
|
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
|
||||||
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
|
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
|
||||||
import org.jetbrains.kotlin.test.services.configuration.ScriptingEnvironmentConfigurator
|
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.DependencyKind
|
||||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||||
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
|
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
|
||||||
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
|
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
|
||||||
|
|
||||||
abstract class AbstractDiagnosticsNativeTest : AbstractKotlinCompilerTest() {
|
abstract class AbstractDiagnosticsNativeTest : AbstractKotlinCompilerTest() {
|
||||||
override fun TestConfigurationBuilder.configuration() {
|
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.DependencyKind
|
||||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||||
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
|
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
|
||||||
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
|
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
|
||||||
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator
|
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator
|
||||||
|
|
||||||
abstract class AbstractDiagnosticsTestWithJsStdLib : AbstractKotlinCompilerTest() {
|
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.DependencyKind
|
||||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||||
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
|
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
|
||||||
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
|
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
|
||||||
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
|
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
|
||||||
|
|
||||||
abstract class AbstractDiagnosticsTestWithJvmBackend : AbstractKotlinCompilerTest() {
|
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.DependencyKind
|
||||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
import org.jetbrains.kotlin.test.model.FrontendKinds
|
||||||
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
|
||||||
import org.jetbrains.kotlin.test.services.AdditionalDiagnosticsSourceFilesProvider
|
import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider
|
||||||
import org.jetbrains.kotlin.test.services.CoroutineHelpersSourceFilesProvider
|
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
|
||||||
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
|
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
|
||||||
import org.jetbrains.kotlin.test.services.fir.FirOldFrontendMetaConfigurator
|
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.BackendKind
|
||||||
import org.jetbrains.kotlin.test.model.DependencyKind
|
import org.jetbrains.kotlin.test.model.DependencyKind
|
||||||
import org.jetbrains.kotlin.test.model.FrontendKinds
|
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.configuration.*
|
||||||
import org.jetbrains.kotlin.test.services.jvm.ForeignAnnotationAgainstCompiledJavaTestSuppressor
|
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() {
|
abstract class AbstractForeignAnnotationsTestBase : AbstractKotlinCompilerTest() {
|
||||||
protected abstract val foreignAnnotationsConfigurator: Constructor<JvmForeignAnnotationsConfigurator>
|
protected abstract val foreignAnnotationsConfigurator: Constructor<JvmForeignAnnotationsConfigurator>
|
||||||
|
|||||||
+1
-1
@@ -76,7 +76,7 @@ abstract class AbstractKotlinCompilerTest {
|
|||||||
builder.configuration()
|
builder.configuration()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun runTest(@TestDataFile filePath: String) {
|
open fun runTest(@TestDataFile filePath: String) {
|
||||||
testRunner(filePath, configuration).runTest(filePath)
|
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.addJavaSourceRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
|
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
|
||||||
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
|
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.codegen.forTestCompile.ForTestCompileRuntime
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||||
|
import org.jetbrains.kotlin.config.CompilerConfigurationKey
|
||||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||||
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
|
||||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
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.DependencyKind
|
||||||
import org.jetbrains.kotlin.test.model.TestModule
|
import org.jetbrains.kotlin.test.model.TestModule
|
||||||
import org.jetbrains.kotlin.test.services.*
|
import org.jetbrains.kotlin.test.services.*
|
||||||
import org.jetbrains.kotlin.test.services.jvm.CompiledJarManager
|
import org.jetbrains.kotlin.test.services.jvm.CompiledClassesManager
|
||||||
import org.jetbrains.kotlin.test.services.jvm.compiledJarManager
|
import org.jetbrains.kotlin.test.services.jvm.compiledClassesManager
|
||||||
import org.jetbrains.kotlin.test.util.KtTestUtil
|
import org.jetbrains.kotlin.test.util.KtTestUtil
|
||||||
import org.jetbrains.kotlin.test.util.joinToArrayString
|
import org.jetbrains.kotlin.test.util.joinToArrayString
|
||||||
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
|
class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
|
||||||
|
companion object {
|
||||||
|
val TEST_CONFIGURATION_KIND_KEY = CompilerConfigurationKey.create<ConfigurationKind>("ConfigurationKind")
|
||||||
|
}
|
||||||
|
|
||||||
override val directivesContainers: List<DirectivesContainer>
|
override val directivesContainers: List<DirectivesContainer>
|
||||||
get() = listOf(JvmEnvironmentConfigurationDirectives)
|
get() = listOf(JvmEnvironmentConfigurationDirectives)
|
||||||
|
|
||||||
override val additionalServices: List<ServiceRegistrationData>
|
override val additionalServices: List<ServiceRegistrationData>
|
||||||
get() = listOf(service(::CompiledJarManager))
|
get() = listOf(service(::CompiledClassesManager))
|
||||||
|
|
||||||
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule, project: MockProject) {
|
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule, project: MockProject) {
|
||||||
if (module.targetPlatform !in JvmPlatforms.allJvmPlatforms) return
|
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) {
|
if (configurationKind.withRuntime) {
|
||||||
configuration.addJvmClasspathRoot(ForTestCompileRuntime.runtimeJarForTests())
|
configuration.addJvmClasspathRoot(ForTestCompileRuntime.runtimeJarForTests())
|
||||||
@@ -119,6 +128,8 @@ class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfig
|
|||||||
if (JvmEnvironmentConfigurationDirectives.USE_PSI_CLASS_FILES_READING in module.directives) {
|
if (JvmEnvironmentConfigurationDirectives.USE_PSI_CLASS_FILES_READING in module.directives) {
|
||||||
configuration.put(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING, true)
|
configuration.put(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initBinaryDependencies(module, configuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractJdkKind(registeredDirectives: RegisteredDirectives): TestJdkKind {
|
private fun extractJdkKind(registeredDirectives: RegisteredDirectives): TestJdkKind {
|
||||||
@@ -146,12 +157,9 @@ class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfig
|
|||||||
if (noRuntime && withRuntime) {
|
if (noRuntime && withRuntime) {
|
||||||
error("NO_RUNTIME and WITH_RUNTIME can not be used together")
|
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 {
|
return when {
|
||||||
withRuntime && withReflect -> ConfigurationKind.ALL
|
withRuntime && !withReflect -> ConfigurationKind.NO_KOTLIN_REFLECT
|
||||||
withRuntime -> ConfigurationKind.NO_KOTLIN_REFLECT
|
withRuntime || withReflect -> ConfigurationKind.ALL
|
||||||
noRuntime -> ConfigurationKind.JDK_NO_RUNTIME
|
noRuntime -> ConfigurationKind.JDK_NO_RUNTIME
|
||||||
else -> ConfigurationKind.JDK_ONLY
|
else -> ConfigurationKind.JDK_ONLY
|
||||||
}
|
}
|
||||||
@@ -164,9 +172,47 @@ class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfig
|
|||||||
.map { dependencyProvider.getTestModule(it.moduleName) }
|
.map { dependencyProvider.getTestModule(it.moduleName) }
|
||||||
.takeIf { it.isNotEmpty() }
|
.takeIf { it.isNotEmpty() }
|
||||||
?: return
|
?: return
|
||||||
val jarManager = testServices.compiledJarManager
|
val jarManager = testServices.compiledClassesManager
|
||||||
val dependenciesClassPath = modulesFromDependencies.map { jarManager.getCompiledJarForModule(it) }
|
val dependenciesClassPath = modulesFromDependencies.map { jarManager.getCompiledKotlinDirForModule(it) }
|
||||||
addJvmClasspathRoots(dependenciesClassPath)
|
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) {
|
class BackendKindExtractorImpl(testServices: TestServices) : BackendKindExtractor(testServices) {
|
||||||
override fun backendKind(targetBackend: TargetBackend?): BackendKind<*> {
|
override fun backendKind(targetBackend: TargetBackend?): BackendKind<*> {
|
||||||
return when (targetBackend) {
|
if (targetBackend == null) return BackendKind.NoBackend
|
||||||
TargetBackend.ANY,
|
return if (targetBackend.isIR) BackendKinds.IrBackend
|
||||||
TargetBackend.JVM,
|
else BackendKinds.ClassicBackend
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-2
@@ -278,9 +278,14 @@ class ModuleStructureExtractorImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun finishFile() {
|
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 }) {
|
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(
|
filesOfCurrentModule.add(
|
||||||
TestFile(
|
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.
|
* 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
|
||||||
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
|
||||||
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
|
||||||
import org.jetbrains.kotlin.test.model.TestFile
|
import org.jetbrains.kotlin.test.model.TestFile
|
||||||
import org.jetbrains.kotlin.test.model.TestModule
|
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
|
import java.io.File
|
||||||
|
|
||||||
class AdditionalDiagnosticsSourceFilesProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
|
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.
|
* 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
|
||||||
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives.CHECK_STATE_MACHINE
|
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.directives.model.RegisteredDirectives
|
||||||
import org.jetbrains.kotlin.test.model.TestFile
|
import org.jetbrains.kotlin.test.model.TestFile
|
||||||
import org.jetbrains.kotlin.test.model.TestModule
|
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
|
import java.io.File
|
||||||
|
|
||||||
class CoroutineHelpersSourceFilesProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
|
class CoroutineHelpersSourceFilesProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
|
||||||
@@ -28,9 +28,13 @@ val File.firTestDataFile: File
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun File.withExtension(extension: String): File {
|
fun File.withExtension(extension: String): File {
|
||||||
|
return withSuffixAndExtension(suffix = "", extension)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun File.withSuffixAndExtension(suffix: String, extension: String): File {
|
||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
val extension = extension.removePrefix(".")
|
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.getBoxMethodOrNull;
|
||||||
import static org.jetbrains.kotlin.test.clientserver.TestProcessServerKt.getGeneratedClass;
|
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 {
|
public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase {
|
||||||
protected void doMultiFileTest(
|
protected void doMultiFileTest(
|
||||||
@NotNull File wholeFile,
|
@NotNull File wholeFile,
|
||||||
|
|||||||
-289
@@ -8,11 +8,7 @@ package org.jetbrains.kotlin.codegen
|
|||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||||
import org.jetbrains.kotlin.utils.sure
|
import org.jetbrains.kotlin.utils.sure
|
||||||
import org.jetbrains.org.objectweb.asm.*
|
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.test.assertNotNull
|
|
||||||
import kotlin.test.assertNull
|
|
||||||
|
|
||||||
abstract class AbstractBytecodeListingTest : CodegenTestCase() {
|
abstract class AbstractBytecodeListingTest : CodegenTestCase() {
|
||||||
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>) {
|
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>) {
|
||||||
@@ -57,288 +53,3 @@ abstract class AbstractBytecodeListingTest : CodegenTestCase() {
|
|||||||
private val IGNORE_ANNOTATIONS = Regex.fromLiteral("// IGNORE_ANNOTATIONS")
|
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_TEST_FILE_NAME = "a_test";
|
||||||
private static final String DEFAULT_JVM_TARGET = System.getProperty("kotlin.test.default.jvm.target");
|
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");
|
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 KotlinCoreEnvironment myEnvironment;
|
||||||
protected CodegenTestFiles myFiles;
|
protected CodegenTestFiles myFiles;
|
||||||
@@ -203,7 +202,7 @@ public abstract class CodegenTestCase extends KotlinBaseTest<KotlinBaseTest.Test
|
|||||||
|
|
||||||
initializedClassLoader = createClassLoader();
|
initializedClassLoader = createClassLoader();
|
||||||
|
|
||||||
if (!verifyAllFilesWithAsm(generateClassesInFile(reportProblems), initializedClassLoader, reportProblems)) {
|
if (!CodegenTestUtil.verifyAllFilesWithAsm(generateClassesInFile(reportProblems), initializedClassLoader, reportProblems)) {
|
||||||
fail("Verification failed: see exceptions above");
|
fail("Verification failed: see exceptions above");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,50 +387,6 @@ public abstract class CodegenTestCase extends KotlinBaseTest<KotlinBaseTest.Test
|
|||||||
return true;
|
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
|
@NotNull
|
||||||
protected Method generateFunction() {
|
protected Method generateFunction() {
|
||||||
Class<?> aClass = generateFacadeClass();
|
Class<?> aClass = generateFacadeClass();
|
||||||
@@ -575,7 +530,7 @@ public abstract class CodegenTestCase extends KotlinBaseTest<KotlinBaseTest.Test
|
|||||||
return javacOptions;
|
return javacOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
String javaTarget = computeJavaTarget(javacOptions, kotlinTarget);
|
String javaTarget = CodegenTestUtil.computeJavaTarget(javacOptions, kotlinTarget);
|
||||||
if (javaTarget != null) {
|
if (javaTarget != null) {
|
||||||
javacOptions.add("-source");
|
javacOptions.add("-source");
|
||||||
javacOptions.add(javaTarget);
|
javacOptions.add(javaTarget);
|
||||||
@@ -585,20 +540,6 @@ public abstract class CodegenTestCase extends KotlinBaseTest<KotlinBaseTest.Test
|
|||||||
return javacOptions;
|
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
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
protected TargetBackend getBackend() {
|
protected TargetBackend getBackend() {
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ dependencies {
|
|||||||
testCompile(project(":compiler:fir:entrypoint"))
|
testCompile(project(":compiler:fir:entrypoint"))
|
||||||
testCompile(projectTests(":compiler:test-infrastructure-utils"))
|
testCompile(projectTests(":compiler:test-infrastructure-utils"))
|
||||||
testCompile(project(":kotlin-preloader"))
|
testCompile(project(":kotlin-preloader"))
|
||||||
|
testCompile(androidDxJar()) { isTransitive = false }
|
||||||
|
testCompile(commonDep("com.android.tools:r8"))
|
||||||
testCompileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
testCompileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||||
|
|
||||||
testCompile(intellijDep()) {
|
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 kotlin.io.FilesKt;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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.cli.jvm.compiler.KotlinCoreEnvironment;
|
||||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime;
|
||||||
|
import org.jetbrains.kotlin.config.JvmTarget;
|
||||||
import org.jetbrains.kotlin.test.Assertions;
|
import org.jetbrains.kotlin.test.Assertions;
|
||||||
import org.jetbrains.kotlin.test.JvmCompilationUtils;
|
import org.jetbrains.kotlin.test.JvmCompilationUtils;
|
||||||
import org.jetbrains.kotlin.test.KtAssert;
|
import org.jetbrains.kotlin.test.KtAssert;
|
||||||
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
import org.jetbrains.kotlin.test.util.KtTestUtil;
|
||||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||||
import org.jetbrains.kotlin.utils.StringsKt;
|
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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -165,4 +177,65 @@ public class CodegenTestUtil {
|
|||||||
|
|
||||||
return javaFilePaths;
|
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 com.android.tools.r8.origin.PathOrigin;
|
||||||
import kotlin.Pair;
|
import kotlin.Pair;
|
||||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
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.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
@@ -44,17 +44,17 @@ public class D8Checker {
|
|||||||
static class TestDiagnosticsHandler implements DiagnosticsHandler {
|
static class TestDiagnosticsHandler implements DiagnosticsHandler {
|
||||||
@Override
|
@Override
|
||||||
public void error(Diagnostic diagnostic) {
|
public void error(Diagnostic diagnostic) {
|
||||||
Assert.fail("D8 dexing error: " + diagnostic.getDiagnosticMessage());
|
KtAssert.fail("D8 dexing error: " + diagnostic.getDiagnosticMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void warning(Diagnostic diagnostic) {
|
public void warning(Diagnostic diagnostic) {
|
||||||
Assert.fail("D8 dexing warning: " + diagnostic.getDiagnosticMessage());
|
KtAssert.fail("D8 dexing warning: " + diagnostic.getDiagnosticMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void info(Diagnostic diagnostic) {
|
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());
|
D8.run(builder.build(), Executors.newSingleThreadExecutor());
|
||||||
}
|
}
|
||||||
catch (CompilationFailedException e) {
|
catch (CompilationFailedException e) {
|
||||||
Assert.fail(generateExceptionMessage(e));
|
KtAssert.fail(generateExceptionMessage(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+4
-15
@@ -1,17 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* 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.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.codegen;
|
package org.jetbrains.kotlin.codegen;
|
||||||
@@ -23,8 +12,8 @@ import com.android.dx.dex.cf.CfTranslator;
|
|||||||
import com.android.dx.dex.file.DexFile;
|
import com.android.dx.dex.file.DexFile;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||||
|
import org.jetbrains.kotlin.test.KtAssert;
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||||
import org.junit.Assert;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
@@ -52,7 +41,7 @@ public class DxChecker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Throwable e) {
|
catch (Throwable e) {
|
||||||
Assert.fail(generateExceptionMessage(e));
|
KtAssert.fail(generateExceptionMessage(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-13
@@ -1,17 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2017 JetBrains s.r.o.
|
* 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.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.codegen
|
package org.jetbrains.kotlin.codegen
|
||||||
-4
@@ -94,10 +94,6 @@ fun generateJUnit3CompilerTests(args: Array<String>) {
|
|||||||
model("parseCodeFragment/block", testMethod = "doBlockCodeFragmentParsingTest", extension = "kt")
|
model("parseCodeFragment/block", testMethod = "doBlockCodeFragmentParsingTest", extension = "kt")
|
||||||
}
|
}
|
||||||
|
|
||||||
testClass<AbstractBlackBoxCodegenTest> {
|
|
||||||
model("codegen/box", targetBackend = TargetBackend.JVM)
|
|
||||||
}
|
|
||||||
|
|
||||||
testClass<AbstractLightAnalysisModeTest> {
|
testClass<AbstractLightAnalysisModeTest> {
|
||||||
// "ranges/stepped" is excluded because it contains hundreds of generated tests and only have a box() method.
|
// "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.
|
// 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.generators.util.TestGeneratorUtil
|
||||||
import org.jetbrains.kotlin.test.TargetBackend
|
import org.jetbrains.kotlin.test.TargetBackend
|
||||||
import org.jetbrains.kotlin.test.runners.*
|
import org.jetbrains.kotlin.test.runners.*
|
||||||
|
import org.jetbrains.kotlin.test.runners.codegen.AbstractBlackBoxCodegenTest
|
||||||
|
|
||||||
fun generateJUnit5CompilerTests(args: Array<String>) {
|
fun generateJUnit5CompilerTests(args: Array<String>) {
|
||||||
val excludedFirTestdataPattern = "^(.+)\\.fir\\.kts?\$"
|
val excludedFirTestdataPattern = "^(.+)\\.fir\\.kts?\$"
|
||||||
@@ -53,6 +54,10 @@ fun generateJUnit5CompilerTests(args: Array<String>) {
|
|||||||
model("foreignAnnotations/tests")
|
model("foreignAnnotations/tests")
|
||||||
model("foreignAnnotations/java8Tests", excludeDirs = listOf("jspecify", "typeEnhancementOnCompiledJava"))
|
model("foreignAnnotations/java8Tests", excludeDirs = listOf("jspecify", "typeEnhancementOnCompiledJava"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testClass<AbstractBlackBoxCodegenTest> {
|
||||||
|
model("codegen/box")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------- FIR tests ----------------------------------------------
|
// ---------------------------------------------- FIR tests ----------------------------------------------
|
||||||
|
|||||||
+5
-5
@@ -16223,11 +16223,6 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public static class SerializationRegressions extends AbstractLightAnalysisModeTest {
|
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 {
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
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");
|
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")
|
@TestMetadata("useImportedMember.kt")
|
||||||
public void testUseImportedMember() throws Exception {
|
public void testUseImportedMember() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/ir/serializationRegressions/useImportedMember.kt");
|
runTest("compiler/testData/codegen/box/ir/serializationRegressions/useImportedMember.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user