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:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user