Speed up JS IR BE tests compiling the common parts (runtime + test common utils) once (on demand) and sharing the result between tests

It's a temporary hack until we implement IR based library format.
It relies on the fact that currently, IR BE generates stable (enough) names.​
This commit is contained in:
Zalim Bashorov
2018-07-13 18:28:30 +03:00
parent 34758110ee
commit 3f0b31dc23
12 changed files with 76 additions and 45 deletions
@@ -104,6 +104,13 @@ class JsIntrinsics(
val jsToLong = getInternalFunction("toLong")
// RTTI:
val isInterfaceSymbol = getInternalFunction("isInterface")
val isArraySymbol = getInternalFunction("isArray")
// val isCharSymbol = getInternalFunction("isChar")
val isObjectSymbol = getInternalFunction("isObject")
// Other:
val jsObjectCreate = defineObjectCreateIntrinsic() // Object.create
@@ -9,6 +9,8 @@ import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.ir.backend.js.lower.*
import org.jetbrains.kotlin.ir.backend.js.lower.inline.*
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
@@ -21,14 +23,25 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
import org.jetbrains.kotlin.serialization.js.JsModuleDescriptor
import org.jetbrains.kotlin.serialization.js.ModuleKind
data class Result(val moduleDescriptor: ModuleDescriptor, val generatedCode: String)
fun compile(
project: Project,
files: List<KtFile>,
configuration: CompilerConfiguration,
export: FqName
): String {
val analysisResult = TopDownAnalyzerFacadeForJS.analyzeFiles(files, project, configuration, emptyList(), emptyList())
export: FqName? = null,
dependencies: List<ModuleDescriptor> = listOf()
): Result {
val moduleDescriptors =
dependencies
.filterIsInstance<ModuleDescriptorImpl>()
.map { JsModuleDescriptor("", ModuleKind.PLAIN, listOf(), it) }
val analysisResult = TopDownAnalyzerFacadeForJS.analyzeFiles(files, project, configuration, moduleDescriptors, emptyList())
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
TopDownAnalyzerFacadeForJS.checkForErrors(files, analysisResult.bindingContext)
@@ -56,10 +69,10 @@ fun compile(
val program = moduleFragment.accept(IrModuleToJsTransformer(context), null)
return program.toString()
return Result(analysisResult.moduleDescriptor, program.toString())
}
fun JsIrBackendContext.performInlining(moduleFragment: IrModuleFragment) {
private fun JsIrBackendContext.performInlining(moduleFragment: IrModuleFragment) {
FunctionInlining(this).inline(moduleFragment)
moduleFragment.referenceAllTypeExternalClassifiers(symbolTable)
@@ -77,7 +90,7 @@ fun JsIrBackendContext.performInlining(moduleFragment: IrModuleFragment) {
}
}
fun JsIrBackendContext.lower(file: IrFile) {
private fun JsIrBackendContext.lower(file: IrFile) {
LateinitLowering(this, true).lower(file)
DefaultArgumentStubGenerator(this).runOnFilePostfix(file)
DefaultParameterInjector(this).runOnFilePostfix(file)
@@ -110,4 +123,4 @@ private fun IrModuleFragment.removeDuplicates(): IrModuleFragment {
dependencyModules.forEach { it.externalPackageFragments.removeDuplicates() }
return this
}
}
@@ -43,10 +43,10 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
private val eqeq = context.irBuiltIns.eqeqSymbol
private val isInterfaceSymbol = getInternalFunction("isInterface")
private val isArraySymbol = getInternalFunction("isArray")
// private val isCharSymbol = getInternalFunction("isChar")
private val isObjectSymbol = getInternalFunction("isObject")
private val isInterfaceSymbol get() = context.intrinsics.isInterfaceSymbol
private val isArraySymbol get() = context.intrinsics.isArraySymbol
// private val isCharSymbol get() = context.intrinsics.isCharSymbol
private val isObjectSymbol get() = context.intrinsics.isObjectSymbol
private val instanceOfIntrinsicSymbol = context.intrinsics.jsInstanceOf.symbol
private val typeOfIntrinsicSymbol = context.intrinsics.jsTypeOf.symbol
@@ -60,8 +60,6 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
private val litTrue: IrExpression = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
private val litNull: IrExpression = JsIrBuilder.buildNull(context.irBuiltIns.nothingNType)
private fun getInternalFunction(name: String) = context.symbolTable.referenceSimpleFunction(context.getInternalFunctions(name).single())
override fun lower(irFile: IrFile) {
// TODO: get rid of descriptors
irFile.transformChildren(object : IrElementTransformer<DeclarationDescriptor> {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR
// LANGUAGE_VERSION: 1.0
@@ -1,5 +1,4 @@
// LANGUAGE_VERSION: 1.0
// IGNORE_BACKEND: JS_IR
fun myEquals(a: Double?, b: Double?) = a == b
fun myEquals1(a: Double?, b: Double) = a == b
@@ -1,5 +1,4 @@
// LANGUAGE_VERSION: 1.0
// IGNORE_BACKEND: JS_IR
fun myNotEquals(a: Double?, b: Double?) = a != b
fun myNotEquals1(a: Double?, b: Double) = a != b
@@ -1,5 +1,4 @@
// LANGUAGE_VERSION: 1.0
// IGNORE_BACKEND: JS_IR
fun myEquals(a: Float?, b: Float?) = a == b
fun myEquals1(a: Float?, b: Float) = a == b
@@ -1,5 +1,4 @@
// LANGUAGE_VERSION: 1.0
// IGNORE_BACKEND: JS_IR
fun myNotEquals(a: Float?, b: Float?) = a != b
fun myNotEquals1(a: Float?, b: Float) = a != b
@@ -1,5 +1,4 @@
// LANGUAGE_VERSION: 1.0
// IGNORE_BACKEND: JS_IR
fun box(): String {
val nullValue: Any? = null
val nullDouble: Double? = null
@@ -131,8 +131,7 @@ abstract class BasicBoxTest(
val mainModuleName = if (TEST_MODULE in modules) TEST_MODULE else DEFAULT_MODULE
val mainModule = modules[mainModuleName] ?: error("No module with name \"$mainModuleName\"")
val globalCommonFiles = JsTestUtils.getFilesInDirectoryByExtension(
TEST_DATA_DIR_PATH + COMMON_FILES_DIR, JavaScript.EXTENSION)
val globalCommonFiles = JsTestUtils.getFilesInDirectoryByExtension(COMMON_FILES_DIR_PATH, JavaScript.EXTENSION)
val localCommonFile = file.parent + "/" + COMMON_FILES_NAME + JavaScript.DOT_EXTENSION
val localCommonFiles = if (File(localCommonFile).exists()) listOf(localCommonFile) else emptyList()
@@ -305,10 +304,10 @@ abstract class BasicBoxTest(
) {
val kotlinFiles = module.files.filter { it.fileName.endsWith(".kt") }
val testFiles = kotlinFiles.map { it.fileName }
val globalCommonFiles = JsTestUtils.getFilesInDirectoryByExtension(
TEST_DATA_DIR_PATH + COMMON_FILES_DIR, KotlinFileType.EXTENSION)
val globalCommonFiles = JsTestUtils.getFilesInDirectoryByExtension(COMMON_FILES_DIR_PATH, KotlinFileType.EXTENSION)
val localCommonFile = directory + "/" + COMMON_FILES_NAME + "." + KotlinFileType.EXTENSION
val localCommonFiles = if (File(localCommonFile).exists()) listOf(localCommonFile) else emptyList()
// TODO probably it's no longer needed.
val additionalCommonFiles = additionalCommonFileDirectories.flatMap { baseDir ->
JsTestUtils.getFilesInDirectoryByExtension(baseDir + "/", KotlinFileType.EXTENSION)
}
@@ -750,9 +749,11 @@ abstract class BasicBoxTest(
const val TEST_DATA_DIR_PATH = "js/js.translator/testData/"
const val DIST_DIR_JS_PATH = "dist/js/"
private val COMMON_FILES_NAME = "_common"
private val COMMON_FILES_DIR = "_commonFiles/"
private val MODULE_EMULATION_FILE = TEST_DATA_DIR_PATH + "/moduleEmulation.js"
private const val COMMON_FILES_NAME = "_common"
private const val COMMON_FILES_DIR = "_commonFiles/"
const val COMMON_FILES_DIR_PATH = TEST_DATA_DIR_PATH + COMMON_FILES_DIR
private const val MODULE_EMULATION_FILE = TEST_DATA_DIR_PATH + "/moduleEmulation.js"
private val MODULE_KIND_PATTERN = Pattern.compile("^// *MODULE_KIND: *(.+)$", Pattern.MULTILINE)
private val NO_MODULE_SYSTEM_PATTERN = Pattern.compile("^// *NO_JS_MODULE_SYSTEM", Pattern.MULTILINE)
@@ -5,6 +5,11 @@
package org.jetbrains.kotlin.js.test
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.ir.backend.js.Result
import org.jetbrains.kotlin.ir.backend.js.compile
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.facade.MainCallParameters
@@ -17,9 +22,13 @@ private val runtimeSources = listOfKtFilesFrom(
"libraries/stdlib/js/src/kotlin/core.kt",
"core/builtins/native/kotlin/Number.kt",
"core/builtins/native/kotlin/Comparable.kt",
"libraries/stdlib/js/irRuntime"
"libraries/stdlib/js/irRuntime",
BasicBoxTest.COMMON_FILES_DIR_PATH
)
private var runtimeResult: Result? = null
private val runtimeFile = File("js/js.translator/testData/out/irBox/testRuntime.js")
abstract class BasicIrBoxTest(
pathToTestDir: String,
testGroupOutputDirPrefix: String,
@@ -50,27 +59,29 @@ abstract class BasicIrBoxTest(
testPackage: String?,
testFunction: String
) {
val runtime = runtimeSources.map { createPsiFile(it) }
val filesToIgnore = listOf(
// TODO: temporary ignore some files from _commonFiles directory since they can't be compiled correctly by JS IR BE yet.
// Also, some declarations depends on stdlib but we don't have any library support in JS IR BE yet
// and probably it will be better to avoid using stdlib in testData as much as possible.
"js/js.translator/testData/_commonFiles/arrayAsserts.kt"
)
val filesToCompile = units
.map { (it as TranslationUnit.SourceFile).file }
.filter { file -> filesToIgnore.none { file.virtualFilePath.endsWith(it) } }
// TODO: split input files to some parts (global common, local common, test)
.filterNot { it.virtualFilePath.contains(BasicBoxTest.COMMON_FILES_DIR_PATH) }
val code = compile(
if (runtimeResult == null) {
val myConfiguration = config.configuration.copy()
// TODO: is it right in general? Maybe sometimes we need to compile with newer versions or with additional language features.
myConfiguration.languageVersionSettings = LanguageVersionSettingsImpl(LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE)
runtimeResult = compile(config.project, runtimeSources.map(::createPsiFile), myConfiguration)
runtimeFile.write(runtimeResult!!.generatedCode)
}
val result = compile(
config.project,
runtime + filesToCompile,
filesToCompile,
config.configuration,
FqName((testPackage?.let { "$it." } ?: "") + testFunction))
FqName((testPackage?.let { "$it." } ?: "") + testFunction),
listOf(runtimeResult!!.moduleDescriptor))
outputFile.parentFile.mkdirs()
outputFile.writeText(code)
outputFile.write(result.generatedCode)
}
override fun runGeneratedCode(
@@ -82,7 +93,9 @@ abstract class BasicIrBoxTest(
withModuleSystem: Boolean
) {
// TODO: should we do anything special for module systems?
super.runGeneratedCode(jsFiles, null, null, testFunction, expectedResult, false)
// TODO: return list of js from translateFiles and provide then to this function with other js files
// TODO: cache runtime.js and don't cache kotlin.js for IR BE tests
super.runGeneratedCode(listOf(runtimeFile.path) + jsFiles, null, null, testFunction, expectedResult, false)
}
}
@@ -95,4 +108,9 @@ private fun listOfKtFilesFrom(vararg paths: String): List<String> {
.map { it.relativeToOrSelf(currentDir).path }
.asIterable()
}
}
}
private fun File.write(text: String) {
parentFile.mkdirs()
writeText(text)
}
+1 -1
View File
@@ -10,7 +10,7 @@ fun <T> assertArrayEquals(expected: Array<out T>, actual: Array<out T>, message:
private fun <T> arraysEqual(first: Array<out T>, second: Array<out T>): Boolean {
if (first === second) return true
if (first.size != second.size) return false
for (index in first.indices) {
for (index in 0..first.size - 1) {
if (!equal(first[index], second[index])) return false
}
return true