JS IR: support kotlin.test

This commit is contained in:
Anton Bannykh
2019-03-26 19:00:07 +03:00
parent b0ed688986
commit b7ffdb9fde
24 changed files with 632 additions and 325 deletions
@@ -0,0 +1,11 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.backend.common.ir
import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
val IrDeclaration.isExpect get() = descriptor.let { it is MemberDescriptor && it.isExpect }
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.SourceManager import org.jetbrains.kotlin.ir.SourceManager
import org.jetbrains.kotlin.ir.SourceRangeInfo import org.jetbrains.kotlin.ir.SourceRangeInfo
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.lower.CallableReferenceKey import org.jetbrains.kotlin.ir.backend.js.lower.CallableReferenceKey
import org.jetbrains.kotlin.ir.backend.js.lower.ConstructorPair import org.jetbrains.kotlin.ir.backend.js.lower.ConstructorPair
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
@@ -81,6 +82,17 @@ class JsIrBackendContext(
} }
} }
private var testContainerField: IrSimpleFunction? = null
val hasTests get() = testContainerField != null
val testContainer
get() = testContainerField ?: JsIrBuilder.buildFunction("test fun", irBuiltIns.unitType, implicitDeclarationFile).apply {
body = JsIrBuilder.buildBlockBody(emptyList())
testContainerField = this
implicitDeclarationFile.declarations += this
}
override val sharedVariablesManager = JsSharedVariablesManager(irBuiltIns, implicitDeclarationFile) override val sharedVariablesManager = JsSharedVariablesManager(irBuiltIns, implicitDeclarationFile)
override val declarationFactory = JsDeclarationFactory() override val declarationFactory = JsDeclarationFactory()
@@ -224,6 +236,9 @@ class JsIrBackendContext(
val throwISEymbol = symbolTable.referenceSimpleFunction(getFunctions(kotlinPackageFqn.child(Name.identifier("THROW_ISE"))).single()) val throwISEymbol = symbolTable.referenceSimpleFunction(getFunctions(kotlinPackageFqn.child(Name.identifier("THROW_ISE"))).single())
val suiteFun = getFunctions(FqName("kotlin.test.suite")).singleOrNull()?.let { symbolTable.referenceSimpleFunction(it) }
val testFun = getFunctions(FqName("kotlin.test.test")).singleOrNull()?.let { symbolTable.referenceSimpleFunction(it) }
val coroutineImplLabelPropertyGetter by lazy { ir.symbols.coroutineImpl.getPropertyGetter("state")!!.owner } val coroutineImplLabelPropertyGetter by lazy { ir.symbols.coroutineImpl.getPropertyGetter("state")!!.owner }
val coroutineImplLabelPropertySetter by lazy { ir.symbols.coroutineImpl.getPropertySetter("state")!!.owner } val coroutineImplLabelPropertySetter by lazy { ir.symbols.coroutineImpl.getPropertySetter("state")!!.owner }
val coroutineImplResultSymbolGetter by lazy { ir.symbols.coroutineImpl.getPropertyGetter("result")!!.owner } val coroutineImplResultSymbolGetter by lazy { ir.symbols.coroutineImpl.getPropertyGetter("result")!!.owner }
@@ -333,10 +333,17 @@ private val callsLoweringPhase = makeJsModulePhase(
description = "Handle intrinsics" description = "Handle intrinsics"
) )
private val testGenerationPhase = makeJsModulePhase(
::TestGenerator,
name = "TestGenerationLowering",
description = "Generate invocations to kotlin.test suite and test functions"
)
val jsPhases = namedIrModulePhase( val jsPhases = namedIrModulePhase(
name = "IrModuleLowering", name = "IrModuleLowering",
description = "IR module lowering", description = "IR module lowering",
lower = expectDeclarationsRemovingPhase then lower = testGenerationPhase then
expectDeclarationsRemovingPhase then
functionInliningPhase then functionInliningPhase then
lateinitLoweringPhase then lateinitLoweringPhase then
tailrecLoweringPhase then tailrecLoweringPhase then
@@ -5,27 +5,18 @@
package org.jetbrains.kotlin.ir.backend.js.ir package org.jetbrains.kotlin.ir.backend.js.ir
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor import org.jetbrains.kotlin.backend.common.descriptors.*
import org.jetbrains.kotlin.backend.common.descriptors.WrappedTypeParameterDescriptor
import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor
import org.jetbrains.kotlin.backend.common.descriptors.WrappedVariableDescriptor
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.declarations.impl.*
import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.*
import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -149,6 +140,9 @@ object JsIrBuilder {
} }
} }
fun buildAnonymousInitializer() =
IrAnonymousInitializerImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, SYNTHESIZED_DECLARATION, IrAnonymousInitializerSymbolImpl(WrappedClassDescriptor()))
fun buildGetObjectValue(type: IrType, classSymbol: IrClassSymbol) = fun buildGetObjectValue(type: IrType, classSymbol: IrClassSymbol) =
IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, classSymbol) IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, classSymbol)
@@ -257,6 +251,7 @@ object JsIrBuilder {
fun buildBoolean(type: IrType, v: Boolean) = IrConstImpl.boolean(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v) fun buildBoolean(type: IrType, v: Boolean) = IrConstImpl.boolean(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v)
fun buildInt(type: IrType, v: Int) = IrConstImpl.int(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v) fun buildInt(type: IrType, v: Int) = IrConstImpl.int(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v)
fun buildString(type: IrType, s: String) = IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, s) fun buildString(type: IrType, s: String) = IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, s)
fun buildTry(type: IrType) = IrTryImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type)
fun buildCatch(ex: IrVariable, block: IrBlockImpl) = IrCatchImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, ex, block) fun buildCatch(ex: IrVariable, block: IrBlockImpl) = IrCatchImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, ex, block)
} }
@@ -0,0 +1,161 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.ir.isExpect
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.fqNameSafe
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
import org.jetbrains.kotlin.name.FqName
class TestGenerator(val context: JsIrBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.declarations.forEach {
if (it is IrClass) {
generateTestCalls(it) { suiteForPackage(irFile.fqName).body }
}
// TODO top-level functions
}
}
private val packageSuites = mutableMapOf<FqName, FunctionWithBody>()
private fun suiteForPackage(fqName: FqName) = packageSuites.getOrPut(fqName) {
context.suiteFun!!.createInvocation(fqName.asString(), context.testContainer.body as IrBlockBody)
}
private data class FunctionWithBody(val function: IrSimpleFunction, val body: IrBlockBody)
private fun IrSimpleFunctionSymbol.createInvocation(
name: String,
parentBody: IrBlockBody,
ignored: Boolean = false
): FunctionWithBody {
val body = JsIrBuilder.buildBlockBody(emptyList())
val function = JsIrBuilder.buildFunction(
"$name test fun",
context.irBuiltIns.unitType,
context.implicitDeclarationFile
).also {
it.body = body
context.implicitDeclarationFile.declarations += it
}
parentBody.statements += JsIrBuilder.buildCall(this).apply {
putValueArgument(0, JsIrBuilder.buildString(context.irBuiltIns.stringType, name))
putValueArgument(1, JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, ignored))
val refType = IrSimpleTypeImpl(context.functionN(0), false, emptyList(), emptyList())
putValueArgument(2, JsIrBuilder.buildFunctionReference(refType, function.symbol))
}
return FunctionWithBody(function, body)
}
private fun generateTestCalls(irClass: IrClass, parentBody: () -> IrBlockBody) {
if (irClass.modality == Modality.ABSTRACT || irClass.isEffectivelyExternal() || irClass.isExpect) return
val suiteFunBody by lazy { context.suiteFun!!.createInvocation(irClass.name.asString(), parentBody(), irClass.isIgnored) }
val beforeFunctions = irClass.declarations.filterIsInstance<IrSimpleFunction>().filter { it.isBefore }
val afterFunctions = irClass.declarations.filterIsInstance<IrSimpleFunction>().filter { it.isAfter }
irClass.declarations.forEach {
when {
it is IrClass ->
generateTestCalls(it) { suiteFunBody.body }
it is IrSimpleFunction && it.isTest ->
generateCodeForTestMethod(it, beforeFunctions, afterFunctions, irClass, suiteFunBody.body)
}
}
}
private fun generateCodeForTestMethod(
testFun: IrSimpleFunction,
beforeFuns: List<IrSimpleFunction>,
afterFuns: List<IrSimpleFunction>,
irClass: IrClass,
parentBody: IrBlockBody
) {
val (fn, body) = context.testFun!!.createInvocation(testFun.name.asString(), parentBody, testFun.isIgnored)
val classVal = JsIrBuilder.buildVar(irClass.defaultType, fn, initializer = irClass.instance())
body.statements += classVal
body.statements += beforeFuns.map {
JsIrBuilder.buildCall(it.symbol).apply {
dispatchReceiver = JsIrBuilder.buildGetValue(classVal.symbol)
}
}
val returnStatement = JsIrBuilder.buildReturn(
fn.symbol,
JsIrBuilder.buildCall(testFun.symbol).apply {
dispatchReceiver = JsIrBuilder.buildGetValue(classVal.symbol)
},
context.irBuiltIns.unitType
)
if (afterFuns.isEmpty()) {
body.statements += returnStatement
} else {
body.statements += JsIrBuilder.buildTry(context.irBuiltIns.unitType).apply {
tryResult = returnStatement
finallyExpression = JsIrBuilder.buildComposite(context.irBuiltIns.unitType).apply {
statements += afterFuns.map {
JsIrBuilder.buildCall(it.symbol).apply {
dispatchReceiver = JsIrBuilder.buildGetValue(classVal.symbol)
}
}
}
}
}
}
private fun IrClass.instance(): IrExpression {
return if (kind == ClassKind.OBJECT) {
JsIrBuilder.buildGetObjectValue(defaultType, symbol)
} else {
declarations.asSequence().filterIsInstance<IrConstructor>().single { it.isPrimary }.let { constructor ->
JsIrBuilder.buildCall(constructor.symbol).also {
if (isInner) {
it.dispatchReceiver = (parent as IrClass).instance()
}
}
}
}
}
private val IrAnnotationContainer.isTest
get() = hasAnnotation("kotlin.test.Test")
private val IrAnnotationContainer.isIgnored
get() = hasAnnotation("kotlin.test.Ignore")
private val IrAnnotationContainer.isBefore
get() = hasAnnotation("kotlin.test.BeforeTest")
private val IrAnnotationContainer.isAfter
get() = hasAnnotation("kotlin.test.AfterTest")
private fun IrAnnotationContainer.hasAnnotation(fqName: String) =
annotations.any { it.symbol.owner.parent.fqNameSafe == FqName(fqName) }
}
@@ -7,10 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.js.backend.ast.JsDeclarationScope import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.JsEmpty
import org.jetbrains.kotlin.js.backend.ast.JsStatement
import org.jetbrains.kotlin.js.backend.ast.JsVars
class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> { class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
@@ -49,6 +49,10 @@ class IrModuleToJsTransformer(private val backendContext: JsIrBackendContext) :
statements += postDeclarationBlock statements += postDeclarationBlock
statements += context.staticContext.initializerBlock statements += context.staticContext.initializerBlock
if (backendContext.hasTests) {
statements += JsInvocation(context.getNameForSymbol(backendContext.testContainer.symbol).makeRef()).makeStmt()
}
return statements return statements
} }
+201 -12
View File
@@ -21,25 +21,214 @@ sourceSets {
"test" { projectDefault() } "test" { projectDefault() }
} }
val generateIrRuntimeKlib by task<NoDebugJavaExec> { val unimplementedNativeBuiltIns =
val inDirs = arrayOf(fileFrom(rootDir, "core", "builtins", "src"), (file("$rootDir/core/builtins/native/kotlin/").list().toSet() - file("$rootDir/libraries/stdlib/js/irRuntime/builtins/").list())
fileFrom(rootDir, "core", "builtins", "native"), .map { "core/builtins/native/kotlin/$it" }
fileFrom(rootDir, "libraries", "stdlib", "common", "src"),
fileFrom(rootDir, "libraries", "stdlib", "src", "kotlin"),
fileFrom(rootDir, "libraries", "stdlib", "js", "src", "generated"),
fileFrom(rootDir, "libraries", "stdlib", "js", "irRuntime"),
fileFrom(rootDir, "libraries", "stdlib", "unsigned"),
fileFrom(rootDir, "js", "js.translator", "testData", "_commonFiles"))
inDirs.forEach { inputs.dir(it) }
val outDir = "$rootDir/js/js.translator/testData/out/klibs/" // Required to compile native builtins with the rest of runtime
outputs.dir(outDir) val builtInsHeader = """@file:Suppress(
"NON_ABSTRACT_FUNCTION_WITH_NO_BODY",
"MUST_BE_INITIALIZED_OR_BE_ABSTRACT",
"EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE",
"PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED",
"WRONG_MODIFIER_TARGET"
)
"""
val fullRuntimeSources by task<Copy> {
val sources = listOf(
"core/builtins/src/kotlin/",
"libraries/stdlib/common/src/",
"libraries/stdlib/src/kotlin/",
"libraries/stdlib/js/src/kotlin/",
"libraries/stdlib/js/src/generated/",
"libraries/stdlib/js/irRuntime/",
"libraries/stdlib/js/runtime/",
"libraries/stdlib/unsigned/",
// TODO get rid - move to test module
"js/js.translator/testData/_commonFiles/"
) + unimplementedNativeBuiltIns
val excluded = listOf(
"libraries/stdlib/common/src/kotlin/JvmAnnotationsH.kt",
"libraries/stdlib/src/kotlin/annotations/Multiplatform.kt",
"libraries/stdlib/common/src/kotlin/NativeAnnotationsH.kt",
// TODO: Support Int.pow
"libraries/stdlib/js/src/kotlin/random/PlatformRandom.kt",
// Fails with: EXPERIMENTAL_IS_NOT_ENABLED
"libraries/stdlib/common/src/kotlin/annotations/Annotations.kt",
// Conflicts with libraries/stdlib/js/src/kotlin/annotations.kt
"libraries/stdlib/js/runtime/hacks.kt",
// TODO: Reuse in IR BE
"libraries/stdlib/js/runtime/Enum.kt",
// JS-specific optimized version of emptyArray() already defined
"core/builtins/src/kotlin/ArrayIntrinsics.kt",
// Unnecessary for now
"libraries/stdlib/js/src/kotlin/dom/**",
"libraries/stdlib/js/src/kotlin/browser/**",
// TODO: fix compilation issues in arrayPlusCollection
// Replaced with irRuntime/kotlinHacks.kt
"libraries/stdlib/js/src/kotlin/kotlin.kt",
"libraries/stdlib/js/src/kotlin/currentBeMisc.kt",
// IR BE has its own generated sources
"libraries/stdlib/js/src/generated/**",
"libraries/stdlib/js/src/kotlin/collectionsExternal.kt",
// Full version is defined in stdlib
// This file is useful for smaller subset of runtime sources
"libraries/stdlib/js/irRuntime/smallRuntimeMissingDeclarations.kt",
// Mostly array-specific stuff
"libraries/stdlib/js/src/kotlin/builtins.kt",
// coroutines
// TODO: merge coroutines_13 with JS BE coroutines
"libraries/stdlib/js/src/kotlin/coroutines/intrinsics/IntrinsicsJs.kt",
"libraries/stdlib/js/src/kotlin/coroutines/CoroutineImpl.kt",
// Inlining of js fun doesn't update the variables inside
"libraries/stdlib/js/src/kotlin/jsTypeOf.kt",
"libraries/stdlib/js/src/kotlin/collections/utils.kt",
// TODO: Remove stub
"libraries/stdlib/js/src/kotlin/builtins.kt",
// Expect declarations get thrown away and libraries/kotlin.test/common/src/main/kotlin/kotlin/test/Assertions.kt doesn't compile
"libraries/stdlib/common/src/kotlin/NativeAnnotationsH.kt"
)
sources.forEach { path ->
from("$rootDir/$path") {
into(path.dropLastWhile { it != '/' })
excluded.filter { it.startsWith(path) }.forEach {
exclude(it.substring(path.length))
}
}
}
into("$buildDir/fullRuntime/src")
doLast {
unimplementedNativeBuiltIns.forEach { path ->
val file = File("$buildDir/fullRuntime/src/$path")
val sourceCode = builtInsHeader + file.readText()
file.writeText(sourceCode)
}
}
}
val reducedRuntimeSources by task<Copy> {
dependsOn(fullRuntimeSources)
from(fullRuntimeSources.outputs.files.singleFile) {
exclude(
listOf(
"libraries/stdlib/unsigned/**",
"libraries/stdlib/common/src/generated/_Arrays.kt",
"libraries/stdlib/common/src/generated/_Collections.kt",
"libraries/stdlib/common/src/generated/_Comparisons.kt",
"libraries/stdlib/common/src/generated/_Maps.kt",
"libraries/stdlib/common/src/generated/_Sequences.kt",
"libraries/stdlib/common/src/generated/_Sets.kt",
"libraries/stdlib/common/src/generated/_Strings.kt",
"libraries/stdlib/common/src/generated/_UArrays.kt",
"libraries/stdlib/common/src/generated/_URanges.kt",
"libraries/stdlib/common/src/generated/_UCollections.kt",
"libraries/stdlib/common/src/generated/_UComparisons.kt",
"libraries/stdlib/common/src/generated/_USequences.kt",
"libraries/stdlib/common/src/kotlin/SequencesH.kt",
"libraries/stdlib/common/src/kotlin/TextH.kt",
"libraries/stdlib/common/src/kotlin/UMath.kt",
"libraries/stdlib/common/src/kotlin/collections/**",
"libraries/stdlib/common/src/kotlin/ioH.kt",
"libraries/stdlib/js/irRuntime/collectionsHacks.kt",
"libraries/stdlib/js/irRuntime/generated/**",
"libraries/stdlib/js/src/kotlin/char.kt",
"libraries/stdlib/js/src/kotlin/collections.kt",
"libraries/stdlib/js/src/kotlin/collections/**",
"libraries/stdlib/js/src/kotlin/console.kt",
"libraries/stdlib/js/src/kotlin/coreDeprecated.kt",
"libraries/stdlib/js/src/kotlin/date.kt",
"libraries/stdlib/js/src/kotlin/debug.kt",
"libraries/stdlib/js/src/kotlin/grouping.kt",
"libraries/stdlib/js/src/kotlin/json.kt",
"libraries/stdlib/js/src/kotlin/numberConversions.kt",
"libraries/stdlib/js/src/kotlin/promise.kt",
"libraries/stdlib/js/src/kotlin/regex.kt",
"libraries/stdlib/js/src/kotlin/regexp.kt",
"libraries/stdlib/js/src/kotlin/sequence.kt",
"libraries/stdlib/js/src/kotlin/string.kt",
"libraries/stdlib/js/src/kotlin/stringsCode.kt",
"libraries/stdlib/js/src/kotlin/text.kt",
"libraries/stdlib/src/kotlin/collections/**",
"libraries/stdlib/src/kotlin/experimental/bitwiseOperations.kt",
"libraries/stdlib/src/kotlin/properties/Delegates.kt",
"libraries/stdlib/src/kotlin/random/URandom.kt",
"libraries/stdlib/src/kotlin/text/**",
"libraries/stdlib/src/kotlin/util/KotlinVersion.kt",
"libraries/stdlib/src/kotlin/util/Tuples.kt"
)
)
}
from("$rootDir/libraries/stdlib/js/irRuntime/smallRuntimeMissingDeclarations.kt") {
into("libraries/stdlib/js/irRuntime/")
}
into("$buildDir/reducedRuntime/src")
}
fun JavaExec.buildKLib(sources: List<String>, dependencies: List<String>, outPath: String) {
inputs.files(sources)
outputs.dir(file(outPath).parent)
classpath = sourceSets.test.get().runtimeClasspath classpath = sourceSets.test.get().runtimeClasspath
main = "org.jetbrains.kotlin.ir.backend.js.GenerateIrRuntimeKt" main = "org.jetbrains.kotlin.ir.backend.js.GenerateIrRuntimeKt"
workingDir = rootDir workingDir = rootDir
args = sources.toList() + listOf("-o", outPath) + dependencies.flatMap { listOf("-d", it) }
passClasspathInJar() passClasspathInJar()
} }
val generateFullRuntimeKLib by task<NoDebugJavaExec> {
dependsOn(fullRuntimeSources)
buildKLib(sources = listOf(fullRuntimeSources.outputs.files.singleFile.path),
dependencies = emptyList(),
outPath = "$buildDir/fullRuntime/klib/JS_IR_RUNTIME.klm")
}
val generateReducedRuntimeKLib by task<NoDebugJavaExec> {
dependsOn(reducedRuntimeSources)
buildKLib(sources = listOf(reducedRuntimeSources.outputs.files.singleFile.path),
dependencies = emptyList(),
outPath = "$buildDir/reducedRuntime/klib/JS_IR_RUNTIME.klm")
}
val generateKotlinTestKLib by task<NoDebugJavaExec> {
dependsOn(generateFullRuntimeKLib)
buildKLib(
sources = listOf(
"$rootDir/libraries/kotlin.test/annotations-common/src/main",
"$rootDir/libraries/kotlin.test/common/src/main",
"$rootDir/libraries/kotlin.test/js/src/main"
),
dependencies = listOf("${generateFullRuntimeKLib.outputs.files.singleFile.path}/JS_IR_RUNTIME.klm"),
outPath = "$buildDir/kotlin.test/klib/kotlin.test.klm"
)
}
testsJar {} testsJar {}
@@ -5,21 +5,21 @@
package org.jetbrains.kotlin.ir.backend.js package org.jetbrains.kotlin.ir.backend.js
import com.intellij.openapi.Disposable
import com.intellij.openapi.vfs.StandardFileSystems import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiManager import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.serialization.js.ModuleKind import org.jetbrains.kotlin.serialization.js.ModuleKind
import java.io.File
import com.intellij.openapi.Disposable fun buildConfiguration(environment: KotlinCoreEnvironment, moduleName: String): CompilerConfiguration {
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
fun buildConfiguration(environment: KotlinCoreEnvironment): CompilerConfiguration {
val runtimeConfiguration = environment.configuration.copy() val runtimeConfiguration = environment.configuration.copy()
runtimeConfiguration.put(CommonConfigurationKeys.MODULE_NAME, "JS_IR_RUNTIME") runtimeConfiguration.put(CommonConfigurationKeys.MODULE_NAME, moduleName)
runtimeConfiguration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN) runtimeConfiguration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN)
runtimeConfiguration.languageVersionSettings = LanguageVersionSettingsImpl( runtimeConfiguration.languageVersionSettings = LanguageVersionSettingsImpl(
@@ -37,34 +37,69 @@ fun buildConfiguration(environment: KotlinCoreEnvironment): CompilerConfiguratio
return runtimeConfiguration return runtimeConfiguration
} }
private val fullRuntimeKlibPath = "js/js.translator/testData/out/klibs/runtimeFull/" val environment = KotlinCoreEnvironment.createForTests(Disposable { }, CompilerConfiguration(), EnvironmentConfigFiles.JS_CONFIG_FILES)
private val defaultRuntimeKlibPath = "js/js.translator/testData/out/klibs/runtimeDefault/"
fun main() { fun createPsiFile(fileName: String): KtFile {
val psiManager = PsiManager.getInstance(environment.project)
val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
val environment = KotlinCoreEnvironment.createForTests(Disposable { }, CompilerConfiguration(), EnvironmentConfigFiles.JS_CONFIG_FILES) val file = fileSystem.findFileByPath(fileName) ?: error("File not found: $fileName")
fun createPsiFile(fileName: String): KtFile { return psiManager.findFile(file) as KtFile
val psiManager = PsiManager.getInstance(environment.project) }
val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
val file = fileSystem.findFileByPath(fileName) ?: error("File not found: $fileName") fun buildKLib(moduleName: String, sources: List<String>, outputPath: String, dependencies: List<KlibModuleRef>): KlibModuleRef {
return generateKLib(
return psiManager.findFile(file) as KtFile project = environment.project,
} files = sources.map(::createPsiFile),
configuration = buildConfiguration(environment, moduleName),
immediateDependencies = dependencies,
fun buildKlib(sources: List<String>, outputPath: String) { allDependencies = dependencies,
generateKLib( outputKlibPath = outputPath
project = environment.project, )
files = sources.map(::createPsiFile), }
configuration = buildConfiguration(environment),
immediateDependencies = emptyList(), private fun listOfKtFilesFrom(paths: List<String>): List<String> {
allDependencies = emptyList(), val currentDir = File("")
outputKlibPath = outputPath return paths.flatMap { path ->
) File(path)
} .walkTopDown()
.filter { it.extension == "kt" }
buildKlib(JsIrTestRuntime.FULL.sources, fullRuntimeKlibPath) .map { it.relativeToOrSelf(currentDir).path }
buildKlib(JsIrTestRuntime.DEFAULT.sources, defaultRuntimeKlibPath) .asIterable()
}.distinct()
}
fun main(args: Array<String>) {
val inputFiles = mutableListOf<String>()
var outputPath: String? = null
val dependencies = mutableListOf<String>()
var index = 0
while (index < args.size) {
val arg = args[index++]
when (arg) {
"-o" -> outputPath = args[index++]
"-d" -> dependencies += args[index++]
else -> inputFiles += arg
}
}
if (outputPath == null) {
error("Please set path to .klm file: `-o some/dir/module-name.klm`")
}
val name = outputPath.takeLastWhile { it != '/' }
if (!name.endsWith(".klm")) error("invalid output file name")
val dependencyKLibs = dependencies.map {
val file = File(it)
KlibModuleRef(file.name.dropLast(4), file.parent)
}
buildKLib(name.dropLast(4), listOfKtFilesFrom(inputFiles), File(outputPath).parent, dependencyKLibs)
} }
@@ -1,178 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.ir.backend.js
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
private const val runtimeDir = "js/js.translator/testData/out/irBox"
enum class JsIrTestRuntime(
val sources: List<String>,
val path: String
) {
// Includes all built-ins and reduced subset of stdlib
DEFAULT(
sources = reducedRuntimeSources,
path = "$runtimeDir/testRuntime.js"
),
// Includes almost full stdlib
FULL(
sources = fullRuntimeSources,
path = "$runtimeDir/testRuntimeFull.js"
)
}
// Required to compile native builtins with the rest of runtime
private const val builtInsHeader = """@file:Suppress(
"NON_ABSTRACT_FUNCTION_WITH_NO_BODY",
"MUST_BE_INITIALIZED_OR_BE_ABSTRACT",
"EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE",
"PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED",
"WRONG_MODIFIER_TARGET"
)
"""
// Native builtins that were not implemented in JS IR runtime
private val unimplementedNativeBuiltInsDir = KotlinTestUtils.tmpDir("unimplementedBuiltins").also { tmpDir ->
val allBuiltins = listOfKtFilesFrom("core/builtins/native/kotlin").map { File(it).name }
val implementedBuiltIns = listOfKtFilesFrom("libraries/stdlib/js/irRuntime/builtins/").map { File(it).name }
val unimplementedBuiltIns = allBuiltins - implementedBuiltIns
for (filename in unimplementedBuiltIns) {
val originalFile = File("core/builtins/native/kotlin", filename)
val newFile = File(tmpDir, filename)
val sourceCode = builtInsHeader + originalFile.readText()
newFile.writeText(sourceCode)
}
}
private val fullRuntimeSources = listOfKtFilesFrom(
"core/builtins/src/kotlin",
"libraries/stdlib/common/src",
"libraries/stdlib/src/kotlin/",
"libraries/stdlib/js/src/kotlin",
"libraries/stdlib/js/src/generated",
"libraries/stdlib/js/irRuntime",
"libraries/stdlib/js/runtime",
"libraries/stdlib/unsigned",
"core/builtins/src/kotlin/Unit.kt",
unimplementedNativeBuiltInsDir.path,
"js/js.translator/testData/_commonFiles"
) - listOfKtFilesFrom(
"libraries/stdlib/common/src/kotlin/JvmAnnotationsH.kt",
"libraries/stdlib/src/kotlin/annotations/Multiplatform.kt",
"libraries/stdlib/common/src/kotlin/NativeAnnotationsH.kt",
// TODO: Support Int.pow
"libraries/stdlib/js/src/kotlin/random/PlatformRandom.kt",
// Fails with: EXPERIMENTAL_IS_NOT_ENABLED
"libraries/stdlib/common/src/kotlin/annotations/Annotations.kt",
// Conflicts with libraries/stdlib/js/src/kotlin/annotations.kt
"libraries/stdlib/js/runtime/hacks.kt",
// TODO: Reuse in IR BE
"libraries/stdlib/js/runtime/Enum.kt",
// JS-specific optimized version of emptyArray() already defined
"core/builtins/src/kotlin/ArrayIntrinsics.kt",
// Unnecessary for now
"libraries/stdlib/js/src/kotlin/dom",
"libraries/stdlib/js/src/kotlin/browser",
// TODO: fix compilation issues in arrayPlusCollection
// Replaced with irRuntime/kotlinHacks.kt
"libraries/stdlib/js/src/kotlin/kotlin.kt",
"libraries/stdlib/js/src/kotlin/currentBeMisc.kt",
// IR BE has its own generated sources
"libraries/stdlib/js/src/generated",
"libraries/stdlib/js/src/kotlin/collectionsExternal.kt",
// Full version is defined in stdlib
// This file is useful for smaller subset of runtime sources
"libraries/stdlib/js/irRuntime/smallRuntimeMissingDeclarations.kt",
// Mostly array-specific stuff
"libraries/stdlib/js/src/kotlin/builtins.kt",
// coroutines
// TODO: merge coroutines_13 with JS BE coroutines
"libraries/stdlib/js/src/kotlin/coroutines/intrinsics/IntrinsicsJs.kt",
"libraries/stdlib/js/src/kotlin/coroutines/CoroutineImpl.kt",
// Inlining of js fun doesn't update the variables inside
"libraries/stdlib/js/src/kotlin/jsTypeOf.kt",
"libraries/stdlib/js/src/kotlin/collections/utils.kt",
// TODO: Remove stub
"libraries/stdlib/js/src/kotlin/builtins.kt"
)
private val reducedRuntimeSources = fullRuntimeSources - listOfKtFilesFrom(
"libraries/stdlib/unsigned",
"libraries/stdlib/common/src/generated/_Arrays.kt",
"libraries/stdlib/common/src/generated/_Collections.kt",
"libraries/stdlib/common/src/generated/_Comparisons.kt",
"libraries/stdlib/common/src/generated/_Maps.kt",
"libraries/stdlib/common/src/generated/_Sequences.kt",
"libraries/stdlib/common/src/generated/_Sets.kt",
"libraries/stdlib/common/src/generated/_Strings.kt",
"libraries/stdlib/common/src/generated/_UArrays.kt",
"libraries/stdlib/common/src/generated/_URanges.kt",
"libraries/stdlib/common/src/generated/_UCollections.kt",
"libraries/stdlib/common/src/generated/_UComparisons.kt",
"libraries/stdlib/common/src/generated/_USequences.kt",
"libraries/stdlib/common/src/kotlin/SequencesH.kt",
"libraries/stdlib/common/src/kotlin/TextH.kt",
"libraries/stdlib/common/src/kotlin/UMath.kt",
"libraries/stdlib/common/src/kotlin/collections/",
"libraries/stdlib/common/src/kotlin/ioH.kt",
"libraries/stdlib/js/irRuntime/collectionsHacks.kt",
"libraries/stdlib/js/irRuntime/generated/",
"libraries/stdlib/js/src/kotlin/char.kt",
"libraries/stdlib/js/src/kotlin/collections.kt",
"libraries/stdlib/js/src/kotlin/collections/",
"libraries/stdlib/js/src/kotlin/console.kt",
"libraries/stdlib/js/src/kotlin/coreDeprecated.kt",
"libraries/stdlib/js/src/kotlin/date.kt",
"libraries/stdlib/js/src/kotlin/debug.kt",
"libraries/stdlib/js/src/kotlin/grouping.kt",
"libraries/stdlib/js/src/kotlin/json.kt",
"libraries/stdlib/js/src/kotlin/numberConversions.kt",
"libraries/stdlib/js/src/kotlin/promise.kt",
"libraries/stdlib/js/src/kotlin/regex.kt",
"libraries/stdlib/js/src/kotlin/regexp.kt",
"libraries/stdlib/js/src/kotlin/sequence.kt",
"libraries/stdlib/js/src/kotlin/string.kt",
"libraries/stdlib/js/src/kotlin/stringsCode.kt",
"libraries/stdlib/js/src/kotlin/text.kt",
"libraries/stdlib/src/kotlin/collections/",
"libraries/stdlib/src/kotlin/experimental/bitwiseOperations.kt",
"libraries/stdlib/src/kotlin/properties/Delegates.kt",
"libraries/stdlib/src/kotlin/random/URandom.kt",
"libraries/stdlib/src/kotlin/text/",
"libraries/stdlib/src/kotlin/util/KotlinVersion.kt",
"libraries/stdlib/src/kotlin/util/Tuples.kt"
) + listOfKtFilesFrom(
"libraries/stdlib/js/irRuntime/smallRuntimeMissingDeclarations.kt"
)
private fun listOfKtFilesFrom(vararg paths: String): List<String> {
val currentDir = File("")
return paths.flatMap { path ->
File(path)
.walkTopDown()
.filter { it.extension == "kt" }
.map { it.relativeToOrSelf(currentDir).path }
.asIterable()
}.distinct()
}
@@ -76,7 +76,8 @@ class CodeConformanceTest : TestCase() {
"libraries/tools/kotlin-gradle-plugin-integration-tests/build", "libraries/tools/kotlin-gradle-plugin-integration-tests/build",
"libraries/tools/kotlin-test-nodejs-runner/node_modules", "libraries/tools/kotlin-test-nodejs-runner/node_modules",
"libraries/tools/kotlin-test-nodejs-runner/.gradle", "libraries/tools/kotlin-test-nodejs-runner/.gradle",
"buildSrc/prepare-deps/build" "buildSrc/prepare-deps/build",
"compiler/ir/serialization.js/build"
) )
} }
+5 -1
View File
@@ -71,7 +71,11 @@ sourceSets {
fun Test.setUpBoxTests(jsEnabled: Boolean, jsIrEnabled: Boolean) { fun Test.setUpBoxTests(jsEnabled: Boolean, jsIrEnabled: Boolean) {
dependsOn(":dist") dependsOn(":dist")
if (jsEnabled) dependsOn(testJsRuntime) if (jsEnabled) dependsOn(testJsRuntime)
if (jsIrEnabled) dependsOn(":compiler:ir.serialization.js:generateIrRuntimeKlib") if (jsIrEnabled) {
dependsOn(":compiler:ir.serialization.js:generateFullRuntimeKLib")
dependsOn(":compiler:ir.serialization.js:generateReducedRuntimeKLib")
dependsOn(":compiler:ir.serialization.js:generateKotlinTestKLib")
}
if (jsEnabled && !jsIrEnabled) exclude("org/jetbrains/kotlin/js/test/ir/semantics/*") if (jsEnabled && !jsIrEnabled) exclude("org/jetbrains/kotlin/js/test/ir/semantics/*")
if (!jsEnabled && jsIrEnabled) include("org/jetbrains/kotlin/js/test/ir/semantics/*") if (!jsEnabled && jsIrEnabled) include("org/jetbrains/kotlin/js/test/ir/semantics/*")
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.incremental.js.IncrementalDataProviderImpl import org.jetbrains.kotlin.incremental.js.IncrementalDataProviderImpl
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumerImpl import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumerImpl
import org.jetbrains.kotlin.incremental.js.TranslationResultValue import org.jetbrains.kotlin.incremental.js.TranslationResultValue
import org.jetbrains.kotlin.ir.backend.js.JsIrTestRuntime
import org.jetbrains.kotlin.js.JavaScript import org.jetbrains.kotlin.js.JavaScript
import org.jetbrains.kotlin.js.backend.JsToStringGenerationVisitor import org.jetbrains.kotlin.js.backend.JsToStringGenerationVisitor
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
@@ -110,11 +109,7 @@ abstract class BasicBoxTest(
fileContent = fileContent.replace("COROUTINES_PACKAGE", coroutinesPackage) fileContent = fileContent.replace("COROUTINES_PACKAGE", coroutinesPackage)
} }
val runtimeType = val needsFullIrRuntime = KJS_WITH_FULL_RUNTIME.matcher(fileContent).find()
if (KJS_WITH_FULL_RUNTIME.matcher(fileContent).find())
JsIrTestRuntime.FULL
else
JsIrTestRuntime.DEFAULT
val actualMainCallParameters = if (CALL_MAIN_PATTERN.matcher(fileContent).find()) MainCallParameters.mainWithArguments(listOf("testArg")) else mainCallParameters val actualMainCallParameters = if (CALL_MAIN_PATTERN.matcher(fileContent).find()) MainCallParameters.mainWithArguments(listOf("testArg")) else mainCallParameters
@@ -150,7 +145,7 @@ abstract class BasicBoxTest(
file.parent, module, outputFileName, dependencies, allDependencies, friends, modules.size > 1, file.parent, module, outputFileName, dependencies, allDependencies, friends, modules.size > 1,
!SKIP_SOURCEMAP_REMAPPING.matcher(fileContent).find(), !SKIP_SOURCEMAP_REMAPPING.matcher(fileContent).find(),
outputPrefixFile, outputPostfixFile, actualMainCallParameters, testPackage, testFunction, outputPrefixFile, outputPostfixFile, actualMainCallParameters, testPackage, testFunction,
runtimeType, isMainModule needsFullIrRuntime, isMainModule
) )
when { when {
@@ -333,7 +328,7 @@ abstract class BasicBoxTest(
mainCallParameters: MainCallParameters, mainCallParameters: MainCallParameters,
testPackage: String?, testPackage: String?,
testFunction: String, testFunction: String,
runtime: JsIrTestRuntime, needsFullIrRuntime: Boolean,
isMainModule: Boolean isMainModule: Boolean
) { ) {
val kotlinFiles = module.files.filter { it.fileName.endsWith(".kt") } val kotlinFiles = module.files.filter { it.fileName.endsWith(".kt") }
@@ -356,13 +351,13 @@ abstract class BasicBoxTest(
val incrementalData = IncrementalData() val incrementalData = IncrementalData()
translateFiles( translateFiles(
psiFiles.map(TranslationUnit::SourceFile), outputFile, config, outputPrefixFile, outputPostfixFile, psiFiles.map(TranslationUnit::SourceFile), outputFile, config, outputPrefixFile, outputPostfixFile,
mainCallParameters, incrementalData, remap, testPackage, testFunction, runtime, isMainModule mainCallParameters, incrementalData, remap, testPackage, testFunction, needsFullIrRuntime, isMainModule
) )
if (incrementalCompilationChecksEnabled && module.hasFilesToRecompile) { if (incrementalCompilationChecksEnabled && module.hasFilesToRecompile) {
checkIncrementalCompilation( checkIncrementalCompilation(
sourceDirs, module, kotlinFiles, dependencies, allDependencies, friends, multiModule, remap, sourceDirs, module, kotlinFiles, dependencies, allDependencies, friends, multiModule, remap,
outputFile, outputPrefixFile, outputPostfixFile, mainCallParameters, incrementalData, testPackage, testFunction, runtime outputFile, outputPrefixFile, outputPostfixFile, mainCallParameters, incrementalData, testPackage, testFunction, needsFullIrRuntime
) )
} }
} }
@@ -383,7 +378,7 @@ abstract class BasicBoxTest(
incrementalData: IncrementalData, incrementalData: IncrementalData,
testPackage: String?, testPackage: String?,
testFunction: String, testFunction: String,
runtime: JsIrTestRuntime needsFullIrRuntime: Boolean
) { ) {
val sourceToTranslationUnit = hashMapOf<File, TranslationUnit>() val sourceToTranslationUnit = hashMapOf<File, TranslationUnit>()
for (testFile in kotlinFiles) { for (testFile in kotlinFiles) {
@@ -405,7 +400,7 @@ abstract class BasicBoxTest(
translateFiles( translateFiles(
translationUnits, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile, translationUnits, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile,
mainCallParameters, incrementalData, remap, testPackage, testFunction, runtime, false mainCallParameters, incrementalData, remap, testPackage, testFunction, needsFullIrRuntime, false
) )
val originalOutput = FileUtil.loadFile(outputFile) val originalOutput = FileUtil.loadFile(outputFile)
@@ -470,7 +465,7 @@ abstract class BasicBoxTest(
remap: Boolean, remap: Boolean,
testPackage: String?, testPackage: String?,
testFunction: String, testFunction: String,
runtime: JsIrTestRuntime, needsFullIrRuntime: Boolean,
isMainModule: Boolean isMainModule: Boolean
) { ) {
val translator = K2JSTranslator(config, false) val translator = K2JSTranslator(config, false)
@@ -7,7 +7,10 @@ package org.jetbrains.kotlin.js.test
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.ir.backend.js.* import org.jetbrains.kotlin.ir.backend.js.KlibModuleRef
import org.jetbrains.kotlin.ir.backend.js.compile
import org.jetbrains.kotlin.ir.backend.js.generateKLib
import org.jetbrains.kotlin.ir.backend.js.jsPhases
import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.JsConfig import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.facade.MainCallParameters import org.jetbrains.kotlin.js.facade.MainCallParameters
@@ -15,13 +18,9 @@ import org.jetbrains.kotlin.js.facade.TranslationUnit
import org.jetbrains.kotlin.test.TargetBackend import org.jetbrains.kotlin.test.TargetBackend
import java.io.File import java.io.File
private val fullRuntimeKlibPath = "js/js.translator/testData/out/klibs/runtimeFull/" private val fullRuntimeKlib = KlibModuleRef("JS_IR_RUNTIME", "compiler/ir/serialization.js/build/fullRuntime/klib")
private val defaultRuntimeKlibPath = "js/js.translator/testData/out/klibs/runtimeDefault/" private val defaultRuntimeKlib = KlibModuleRef("JS_IR_RUNTIME", "compiler/ir/serialization.js/build/reducedRuntime/klib")
private val kotlinTestKLib = KlibModuleRef("kotlin.test", "compiler/ir/serialization.js/build/kotlin.test/klib")
private val JS_IR_RUNTIME_MODULE_NAME = "JS_IR_RUNTIME"
private val fullRuntimeKlib = KlibModuleRef(JS_IR_RUNTIME_MODULE_NAME, fullRuntimeKlibPath)
private val defaultRuntimeKlib = KlibModuleRef(JS_IR_RUNTIME_MODULE_NAME, defaultRuntimeKlibPath)
abstract class BasicIrBoxTest( abstract class BasicIrBoxTest(
pathToTestDir: String, pathToTestDir: String,
@@ -53,10 +52,6 @@ abstract class BasicIrBoxTest(
override val testChecker get() = if (runTestInNashorn) NashornIrJsTestChecker() else V8IrJsTestChecker override val testChecker get() = if (runTestInNashorn) NashornIrJsTestChecker() else V8IrJsTestChecker
private val runtimes = mapOf(
JsIrTestRuntime.DEFAULT to defaultRuntimeKlib,
JsIrTestRuntime.FULL to fullRuntimeKlib)
override fun translateFiles( override fun translateFiles(
units: List<TranslationUnit>, units: List<TranslationUnit>,
outputFile: File, outputFile: File,
@@ -68,7 +63,7 @@ abstract class BasicIrBoxTest(
remap: Boolean, remap: Boolean,
testPackage: String?, testPackage: String?,
testFunction: String, testFunction: String,
runtime: JsIrTestRuntime, needsFullIrRuntime: Boolean,
isMainModule: Boolean isMainModule: Boolean
) { ) {
val filesToCompile = units val filesToCompile = units
@@ -76,35 +71,20 @@ abstract class BasicIrBoxTest(
// TODO: split input files to some parts (global common, local common, test) // TODO: split input files to some parts (global common, local common, test)
.filterNot { it.virtualFilePath.contains(BasicBoxTest.COMMON_FILES_DIR_PATH) } .filterNot { it.virtualFilePath.contains(BasicBoxTest.COMMON_FILES_DIR_PATH) }
// config.configuration.put(CommonConfigurationKeys.EXCLUDED_ELEMENTS_FROM_DUMPING, setOf("<JS_IR_RUNTIME>")) val runtimeKlibs = if (needsFullIrRuntime) listOf(fullRuntimeKlib, kotlinTestKLib) else listOf(defaultRuntimeKlib)
// config.configuration.put(
// CommonConfigurationKeys.PHASES_TO_VALIDATE_AFTER,
// setOf(
// "RemoveInlineFunctionsWithReifiedTypeParametersLowering",
// "InnerClassConstructorCallsLowering",
// "InlineClassLowering", "ConstLowering"
// )
// )
val runtimeKlib = runtimes[runtime]!!
val libraries = config.configuration[JSConfigurationKeys.LIBRARIES]!!.map { File(it).name } val libraries = config.configuration[JSConfigurationKeys.LIBRARIES]!!.map { File(it).name }
val transitiveLibraries = config.configuration[JSConfigurationKeys.TRANSITIVE_LIBRARIES]!!.map { File(it).name } val transitiveLibraries = config.configuration[JSConfigurationKeys.TRANSITIVE_LIBRARIES]!!.map { File(it).name }
// TODO: Add proper depencencies // TODO: Add proper depencencies
val dependencies = listOf(runtimeKlib) + libraries.map { val dependencies = runtimeKlibs + libraries.map {
compilationCache[it] ?: error("Can't find compiled module for dependency $it") compilationCache[it] ?: error("Can't find compiled module for dependency $it")
} }
val allDependencies = listOf(runtimeKlib) + transitiveLibraries.map { val allDependencies = runtimeKlibs + transitiveLibraries.map {
compilationCache[it] ?: error("Can't find compiled module for dependency $it") compilationCache[it] ?: error("Can't find compiled module for dependency $it")
} }
// config.configuration.put(CommonConfigurationKeys.PHASES_TO_DUMP_STATE, setOf("UnitMaterializationLowering"))
// config.configuration.put(CommonConfigurationKeys.PHASES_TO_DUMP_STATE_BEFORE, setOf("ReturnableBlockLowering"))
// config.configuration.put(CommonConfigurationKeys.PHASES_TO_DUMP_STATE_AFTER, setOf("MultipleCatchesLowering"))
// config.configuration.put(CommonConfigurationKeys.PHASES_TO_VALIDATE, setOf("ALL"))
val actualOutputFile = outputFile.absolutePath.let { val actualOutputFile = outputFile.absolutePath.let {
if (!isMainModule) it.replace("_v5.js", "/") else it if (!isMainModule) it.replace("_v5.js", "/") else it
} }
+90 -23
View File
@@ -1,44 +1,107 @@
package common package common
import kotlin.test.FrameworkAdapter import kotlin.test.FrameworkAdapter
import kotlin.collections.*
private val context = TestContext() private var sortingContext = SortingContext()
fun call(name: String) = context.call(name) private var bodyContext: TestBodyContext? = null
fun call(name: String) = bodyContext!!.call(name)
fun raise(name: String): Nothing { fun raise(name: String): Nothing {
context.raised(name) bodyContext!!.raised(name)
throw Exception(name) throw Exception(name)
} }
@Suppress("INVISIBLE_MEMBER") @Suppress("INVISIBLE_MEMBER")
val underscore = kotlin.test.setAdapter(object : FrameworkAdapter { private val underscore = kotlin.test.setAdapter(object : FrameworkAdapter {
override fun suite(name: String, ignored: Boolean, suiteFn: () -> Unit) { override fun suite(name: String, ignored: Boolean, suiteFn: () -> Unit) {
context.suite(name, ignored) { suiteFn() } sortingContext.suite(name, ignored) { suiteFn() }
} }
override fun test(name: String, ignored: Boolean, testFn: () -> dynamic) { override fun test(name: String, ignored: Boolean, testFn: () -> dynamic) {
context.test(name, ignored) { returned(testFn()) } sortingContext.test(name, ignored) { returned(testFn()) }
} }
}) })
class TestContext { interface SuiteContext {
fun suite(name: String, ignored: Boolean = false, body: SuiteContext.() -> Unit)
fun test(name: String, ignored: Boolean = false, body: TestBodyContext.() -> Unit = {})
}
interface TestBodyContext {
fun call(name: String)
fun raised(msg: String)
fun caught(msg: String)
fun returned(msg: dynamic)
}
private sealed class Entity(val name: String,
val ignored: Boolean)
private class Suite(name: String, ignored: Boolean, val body: SuiteContext.() -> Unit): Entity(name, ignored)
private class Test(name: String, ignored: Boolean, val body: TestBodyContext.() -> Unit): Entity(name, ignored)
private class SortingContext: SuiteContext {
val structure = mutableListOf<Entity>()
override fun suite(name: String, ignored: Boolean, body: SuiteContext.() -> Unit) {
structure += Suite(name, ignored, body)
}
override fun test(name: String, ignored: Boolean, body: TestBodyContext.() -> Unit) {
structure += Test(name, ignored, body)
}
fun <T: SuiteContext> replayInto(context: T): T {
structure.sortedBy { it.name }.forEach {
when (it) {
is Suite -> context.suite(it.name, it.ignored) {
val oldSorter = sortingContext
sortingContext = SortingContext()
it.body(sortingContext)
sortingContext.replayInto(this)
sortingContext = oldSorter
}
is Test -> context.test(it.name, it.ignored) {
bodyContext = this
it.body(this)
bodyContext = null
}
}
}
return context
}
}
private class LoggingContext : SuiteContext, TestBodyContext{
val log: String val log: String
get() = logHead + (lastRecord ?: "") get() = logHead + (lastRecord ?: "")
var indentation = "" private var indentation = ""
fun suite(name: String, ignored: Boolean = false, body: TestContext.() -> Unit) = indent { override fun suite(name: String, ignored: Boolean, body: SuiteContext.() -> Unit) = indent {
record("suite(\"$name\"${optionalIgnore(ignored)}) {") record("suite(\"$name\"${optionalIgnore(ignored)}) {")
body.runSafely() runSafely { this.body() }
record("}") record("}")
} }
override fun test(name: String, ignored: Boolean, body: TestBodyContext.() -> Unit) = indent {
fun test(name: String, ignored: Boolean = false, body: TestContext.() -> Unit = {}) = indent {
val num = record("test(\"$name\"${optionalIgnore(ignored)}) {") val num = record("test(\"$name\"${optionalIgnore(ignored)}) {")
body.runSafely() runSafely { this.body() }
if (!writtenSince(num)) { if (!writtenSince(num)) {
record("test(\"$name\"${optionalIgnore(ignored)})", replaceLast = true) record("test(\"$name\"${optionalIgnore(ignored)})", replaceLast = true)
@@ -48,25 +111,25 @@ class TestContext {
} }
} }
fun call(name: String) = indent { override fun call(name: String) = indent {
record("call(\"$name\")") record("call(\"$name\")")
} }
fun raised(msg: String) = indent { override fun raised(msg: String) = indent {
record("raised(\"$msg\")") record("raised(\"$msg\")")
} }
fun caught(msg: String) = indent { override fun caught(msg: String) = indent {
record("caught(\"$msg\")") record("caught(\"$msg\")")
} }
fun returned(msg: dynamic) = indent { override fun returned(msg: dynamic) = indent {
if (msg is String) record("returned(\"$msg\")") if (msg is String) record("returned(\"$msg\")")
} }
private fun (TestContext.() -> Unit).runSafely() { private fun runSafely(body: () -> Unit) {
try { try {
this() body()
} }
catch (t: Throwable) { catch (t: Throwable) {
caught(t.message ?: "") caught(t.message ?: "")
@@ -100,8 +163,8 @@ class TestContext {
private fun optionalIgnore(ignored: Boolean) = if (ignored) ", true" else "" private fun optionalIgnore(ignored: Boolean) = if (ignored) ", true" else ""
} }
fun checkLog(wrapInEmptySuite: Boolean = true, body: TestContext.() -> Unit): String { fun checkLog(wrapInEmptySuite: Boolean = true, body: SuiteContext.() -> Unit): String {
val expectedContext = TestContext() val expectedContext = SortingContext()
if (wrapInEmptySuite) { if (wrapInEmptySuite) {
expectedContext.suite("") { expectedContext.suite("") {
body() body()
@@ -109,8 +172,12 @@ fun checkLog(wrapInEmptySuite: Boolean = true, body: TestContext.() -> Unit): St
} else { } else {
expectedContext.body() expectedContext.body()
} }
if (context.log != expectedContext.log) {
return "Failed test structure check. Expected: ${expectedContext.log}; actual: ${context.log}." val expectedLog = expectedContext.replayInto(LoggingContext()).log
val actualLog = sortingContext.replayInto(LoggingContext()).log
if (actualLog != expectedLog) {
return "Failed test structure check. Expected: ${expectedLog}; actual: ${actualLog}."
} }
else { else {
return "OK" return "OK"
+3 -2
View File
@@ -1,5 +1,6 @@
// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1706
// EXPECTED_REACHABLE_NODES: 1339 // KJS_WITH_FULL_RUNTIME
import common.* import common.*
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.BeforeTest import kotlin.test.BeforeTest
+3 -2
View File
@@ -1,5 +1,6 @@
// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1709
// EXPECTED_REACHABLE_NODES: 1340 // KJS_WITH_FULL_RUNTIME
import common.* import common.*
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.Ignore import kotlin.test.Ignore
+2 -2
View File
@@ -1,5 +1,5 @@
// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1829
// EXPECTED_REACHABLE_NODES: 1330 // KJS_WITH_FULL_RUNTIME
// FILE: a.kt // FILE: a.kt
package a package a
+3 -2
View File
@@ -1,5 +1,6 @@
// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1719
// EXPECTED_REACHABLE_NODES: 1351 // KJS_WITH_FULL_RUNTIME
import common.* import common.*
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.BeforeTest import kotlin.test.BeforeTest
+2 -2
View File
@@ -1,6 +1,6 @@
// EXPECTED_REACHABLE_NODES: 1697
// !LANGUAGE: +MultiPlatformProjects // !LANGUAGE: +MultiPlatformProjects
// EXPECTED_REACHABLE_NODES: 1328 // KJS_WITH_FULL_RUNTIME
// IGNORE_BACKEND: JS_IR
// FILE: lib.kt // FILE: lib.kt
import kotlin.test.Test import kotlin.test.Test
+3 -2
View File
@@ -1,5 +1,6 @@
// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1735
// EXPECTED_REACHABLE_NODES: 1367 // KJS_WITH_FULL_RUNTIME
import common.* import common.*
import kotlin.test.Test import kotlin.test.Test
@@ -1,5 +1,5 @@
// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1737
// EXPECTED_REACHABLE_NODES: 1297 // KJS_WITH_FULL_RUNTIME
import common.* import common.*
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.BeforeTest import kotlin.test.BeforeTest
+3 -2
View File
@@ -1,5 +1,6 @@
// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1698
// EXPECTED_REACHABLE_NODES: 1330 // KJS_WITH_FULL_RUNTIME
import common.* import common.*
import kotlin.test.Test import kotlin.test.Test
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.native.concurrent
// Current serialization removes expect declarations, so some dummy annotations are needed
// Expect declarations: libraries/stdlib/common/src/kotlin/NativeAnnotationsH.kt
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
@OptionalExpectation
public annotation class ThreadLocal()
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.BINARY)
@OptionalExpectation
public annotation class SharedImmutable()