From b7ffdb9fdeb7b68adf89ca14f9dd3374cb20e376 Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Tue, 26 Mar 2019 19:00:07 +0300 Subject: [PATCH] JS IR: support kotlin.test --- .../backend/common/ir/descriptorBasedUtils.kt | 11 + .../ir/backend/js/JsIrBackendContext.kt | 15 ++ .../kotlin/ir/backend/js/JsLoweringPhases.kt | 9 +- .../kotlin/ir/backend/js/ir/IrBuilder.kt | 19 +- .../ir/backend/js/lower/TestGenerator.kt | 161 +++++++++++++ .../irToJs/IrDeclarationToJsTransformer.kt | 5 +- .../irToJs/IrModuleToJsTransformer.kt | 4 + compiler/ir/serialization.js/build.gradle.kts | 213 +++++++++++++++++- .../test/GenerateIrRuntime.kt | 101 ++++++--- .../serialization.js/test/JsIrTestRuntime.kt | 178 --------------- .../kotlin/code/CodeConformanceTest.kt | 3 +- js/js.tests/build.gradle.kts | 6 +- .../jetbrains/kotlin/js/test/BasicBoxTest.kt | 21 +- .../kotlin/js/test/BasicIrBoxTest.kt | 42 +--- .../testData/box/kotlin.test/_common.kt | 113 ++++++++-- .../testData/box/kotlin.test/beforeAfter.kt | 5 +- .../testData/box/kotlin.test/ignore.kt | 5 +- .../testData/box/kotlin.test/incremental.kt | 4 +- .../testData/box/kotlin.test/inherited.kt | 5 +- .../testData/box/kotlin.test/mpp.kt | 4 +- .../testData/box/kotlin.test/nested.kt | 5 +- .../box/kotlin.test/returnTestResult.kt | 4 +- .../testData/box/kotlin.test/simple.kt | 5 +- .../stdlib/js/irRuntime/kotlinNativeHacks.kt | 19 ++ 24 files changed, 632 insertions(+), 325 deletions(-) create mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/descriptorBasedUtils.kt create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt delete mode 100644 compiler/ir/serialization.js/test/JsIrTestRuntime.kt create mode 100644 libraries/stdlib/js/irRuntime/kotlinNativeHacks.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/descriptorBasedUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/descriptorBasedUtils.kt new file mode 100644 index 00000000000..11ea03b2941 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/descriptorBasedUtils.kt @@ -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 } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index a71a13c469c..b1465681df5 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.SourceManager import org.jetbrains.kotlin.ir.SourceRangeInfo 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.ConstructorPair 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 declarationFactory = JsDeclarationFactory() @@ -224,6 +236,9 @@ class JsIrBackendContext( 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 coroutineImplLabelPropertySetter by lazy { ir.symbols.coroutineImpl.getPropertySetter("state")!!.owner } val coroutineImplResultSymbolGetter by lazy { ir.symbols.coroutineImpl.getPropertyGetter("result")!!.owner } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 6c2f0bddf40..dd05f694dba 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -333,10 +333,17 @@ private val callsLoweringPhase = makeJsModulePhase( description = "Handle intrinsics" ) +private val testGenerationPhase = makeJsModulePhase( + ::TestGenerator, + name = "TestGenerationLowering", + description = "Generate invocations to kotlin.test suite and test functions" +) + val jsPhases = namedIrModulePhase( name = "IrModuleLowering", description = "IR module lowering", - lower = expectDeclarationsRemovingPhase then + lower = testGenerationPhase then + expectDeclarationsRemovingPhase then functionInliningPhase then lateinitLoweringPhase then tailrecLoweringPhase then diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt index 479d2ee58d7..c40b33a4ab3 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt @@ -5,27 +5,18 @@ package org.jetbrains.kotlin.ir.backend.js.ir -import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor -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.backend.common.descriptors.* import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl -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.declarations.impl.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.* -import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl -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.symbols.impl.* import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classifierOrFail 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) = 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 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 buildTry(type: IrType) = IrTryImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type) fun buildCatch(ex: IrVariable, block: IrBlockImpl) = IrCatchImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, ex, block) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt new file mode 100644 index 00000000000..fdc724bcad6 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TestGenerator.kt @@ -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() + + 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().filter { it.isBefore } + val afterFunctions = irClass.declarations.filterIsInstance().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, + afterFuns: List, + 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().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) } +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt index 0dd1ff158b8..aee92bd1cb0 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt @@ -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.declarations.* -import org.jetbrains.kotlin.js.backend.ast.JsDeclarationScope -import org.jetbrains.kotlin.js.backend.ast.JsEmpty -import org.jetbrains.kotlin.js.backend.ast.JsStatement -import org.jetbrains.kotlin.js.backend.ast.JsVars +import org.jetbrains.kotlin.js.backend.ast.* class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt index 69dcd0b6513..fd4f11d741e 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrModuleToJsTransformer.kt @@ -49,6 +49,10 @@ class IrModuleToJsTransformer(private val backendContext: JsIrBackendContext) : statements += postDeclarationBlock statements += context.staticContext.initializerBlock + if (backendContext.hasTests) { + statements += JsInvocation(context.getNameForSymbol(backendContext.testContainer.symbol).makeRef()).makeStmt() + } + return statements } diff --git a/compiler/ir/serialization.js/build.gradle.kts b/compiler/ir/serialization.js/build.gradle.kts index b6201f7a715..22057c82a32 100644 --- a/compiler/ir/serialization.js/build.gradle.kts +++ b/compiler/ir/serialization.js/build.gradle.kts @@ -21,25 +21,214 @@ sourceSets { "test" { projectDefault() } } -val generateIrRuntimeKlib by task { - val inDirs = arrayOf(fileFrom(rootDir, "core", "builtins", "src"), - fileFrom(rootDir, "core", "builtins", "native"), - 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 unimplementedNativeBuiltIns = + (file("$rootDir/core/builtins/native/kotlin/").list().toSet() - file("$rootDir/libraries/stdlib/js/irRuntime/builtins/").list()) + .map { "core/builtins/native/kotlin/$it" } - val outDir = "$rootDir/js/js.translator/testData/out/klibs/" - outputs.dir(outDir) +// Required to compile native builtins with the rest of runtime +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 { + 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 { + 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, dependencies: List, outPath: String) { + inputs.files(sources) + outputs.dir(file(outPath).parent) classpath = sourceSets.test.get().runtimeClasspath main = "org.jetbrains.kotlin.ir.backend.js.GenerateIrRuntimeKt" workingDir = rootDir + args = sources.toList() + listOf("-o", outPath) + dependencies.flatMap { listOf("-d", it) } passClasspathInJar() } +val generateFullRuntimeKLib by task { + dependsOn(fullRuntimeSources) + + buildKLib(sources = listOf(fullRuntimeSources.outputs.files.singleFile.path), + dependencies = emptyList(), + outPath = "$buildDir/fullRuntime/klib/JS_IR_RUNTIME.klm") +} + +val generateReducedRuntimeKLib by task { + dependsOn(reducedRuntimeSources) + + buildKLib(sources = listOf(reducedRuntimeSources.outputs.files.singleFile.path), + dependencies = emptyList(), + outPath = "$buildDir/reducedRuntime/klib/JS_IR_RUNTIME.klm") +} + +val generateKotlinTestKLib by task { + 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 {} \ No newline at end of file diff --git a/compiler/ir/serialization.js/test/GenerateIrRuntime.kt b/compiler/ir/serialization.js/test/GenerateIrRuntime.kt index 94f2d4a2837..a7dd99c9ba4 100644 --- a/compiler/ir/serialization.js/test/GenerateIrRuntime.kt +++ b/compiler/ir/serialization.js/test/GenerateIrRuntime.kt @@ -5,21 +5,21 @@ package org.jetbrains.kotlin.ir.backend.js +import com.intellij.openapi.Disposable import com.intellij.openapi.vfs.StandardFileSystems import com.intellij.openapi.vfs.VirtualFileManager 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.js.config.JSConfigurationKeys import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.serialization.js.ModuleKind +import java.io.File -import com.intellij.openapi.Disposable -import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment - -fun buildConfiguration(environment: KotlinCoreEnvironment): CompilerConfiguration { +fun buildConfiguration(environment: KotlinCoreEnvironment, moduleName: String): CompilerConfiguration { 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.languageVersionSettings = LanguageVersionSettingsImpl( @@ -37,34 +37,69 @@ fun buildConfiguration(environment: KotlinCoreEnvironment): CompilerConfiguratio return runtimeConfiguration } -private val fullRuntimeKlibPath = "js/js.translator/testData/out/klibs/runtimeFull/" -private val defaultRuntimeKlibPath = "js/js.translator/testData/out/klibs/runtimeDefault/" +val environment = KotlinCoreEnvironment.createForTests(Disposable { }, CompilerConfiguration(), EnvironmentConfigFiles.JS_CONFIG_FILES) -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 { - val psiManager = PsiManager.getInstance(environment.project) - val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) - - val file = fileSystem.findFileByPath(fileName) ?: error("File not found: $fileName") - - return psiManager.findFile(file) as KtFile - } - - - fun buildKlib(sources: List, outputPath: String) { - generateKLib( - project = environment.project, - files = sources.map(::createPsiFile), - configuration = buildConfiguration(environment), - immediateDependencies = emptyList(), - allDependencies = emptyList(), - outputKlibPath = outputPath - ) - } - - buildKlib(JsIrTestRuntime.FULL.sources, fullRuntimeKlibPath) - buildKlib(JsIrTestRuntime.DEFAULT.sources, defaultRuntimeKlibPath) + return psiManager.findFile(file) as KtFile +} + + +fun buildKLib(moduleName: String, sources: List, outputPath: String, dependencies: List): KlibModuleRef { + return generateKLib( + project = environment.project, + files = sources.map(::createPsiFile), + configuration = buildConfiguration(environment, moduleName), + immediateDependencies = dependencies, + allDependencies = dependencies, + outputKlibPath = outputPath + ) +} + +private fun listOfKtFilesFrom(paths: List): List { + val currentDir = File("") + return paths.flatMap { path -> + File(path) + .walkTopDown() + .filter { it.extension == "kt" } + .map { it.relativeToOrSelf(currentDir).path } + .asIterable() + }.distinct() +} + +fun main(args: Array) { + + val inputFiles = mutableListOf() + var outputPath: String? = null + val dependencies = mutableListOf() + + 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) } diff --git a/compiler/ir/serialization.js/test/JsIrTestRuntime.kt b/compiler/ir/serialization.js/test/JsIrTestRuntime.kt deleted file mode 100644 index ba13ae60553..00000000000 --- a/compiler/ir/serialization.js/test/JsIrTestRuntime.kt +++ /dev/null @@ -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, - 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 { - val currentDir = File("") - return paths.flatMap { path -> - File(path) - .walkTopDown() - .filter { it.extension == "kt" } - .map { it.relativeToOrSelf(currentDir).path } - .asIterable() - }.distinct() -} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/code/CodeConformanceTest.kt b/compiler/tests/org/jetbrains/kotlin/code/CodeConformanceTest.kt index 07d2dec4be8..ed6dc30a055 100644 --- a/compiler/tests/org/jetbrains/kotlin/code/CodeConformanceTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/code/CodeConformanceTest.kt @@ -76,7 +76,8 @@ class CodeConformanceTest : TestCase() { "libraries/tools/kotlin-gradle-plugin-integration-tests/build", "libraries/tools/kotlin-test-nodejs-runner/node_modules", "libraries/tools/kotlin-test-nodejs-runner/.gradle", - "buildSrc/prepare-deps/build" + "buildSrc/prepare-deps/build", + "compiler/ir/serialization.js/build" ) } diff --git a/js/js.tests/build.gradle.kts b/js/js.tests/build.gradle.kts index 5967628af09..dda44985923 100644 --- a/js/js.tests/build.gradle.kts +++ b/js/js.tests/build.gradle.kts @@ -71,7 +71,11 @@ sourceSets { fun Test.setUpBoxTests(jsEnabled: Boolean, jsIrEnabled: Boolean) { dependsOn(":dist") 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) include("org/jetbrains/kotlin/js/test/ir/semantics/*") diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt index bb93a2e13bf..a801457c428 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt @@ -25,7 +25,6 @@ import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.incremental.js.IncrementalDataProviderImpl import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumerImpl 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.backend.JsToStringGenerationVisitor import org.jetbrains.kotlin.js.backend.ast.* @@ -110,11 +109,7 @@ abstract class BasicBoxTest( fileContent = fileContent.replace("COROUTINES_PACKAGE", coroutinesPackage) } - val runtimeType = - if (KJS_WITH_FULL_RUNTIME.matcher(fileContent).find()) - JsIrTestRuntime.FULL - else - JsIrTestRuntime.DEFAULT + val needsFullIrRuntime = KJS_WITH_FULL_RUNTIME.matcher(fileContent).find() 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, !SKIP_SOURCEMAP_REMAPPING.matcher(fileContent).find(), outputPrefixFile, outputPostfixFile, actualMainCallParameters, testPackage, testFunction, - runtimeType, isMainModule + needsFullIrRuntime, isMainModule ) when { @@ -333,7 +328,7 @@ abstract class BasicBoxTest( mainCallParameters: MainCallParameters, testPackage: String?, testFunction: String, - runtime: JsIrTestRuntime, + needsFullIrRuntime: Boolean, isMainModule: Boolean ) { val kotlinFiles = module.files.filter { it.fileName.endsWith(".kt") } @@ -356,13 +351,13 @@ abstract class BasicBoxTest( val incrementalData = IncrementalData() translateFiles( 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) { checkIncrementalCompilation( 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, testPackage: String?, testFunction: String, - runtime: JsIrTestRuntime + needsFullIrRuntime: Boolean ) { val sourceToTranslationUnit = hashMapOf() for (testFile in kotlinFiles) { @@ -405,7 +400,7 @@ abstract class BasicBoxTest( translateFiles( translationUnits, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile, - mainCallParameters, incrementalData, remap, testPackage, testFunction, runtime, false + mainCallParameters, incrementalData, remap, testPackage, testFunction, needsFullIrRuntime, false ) val originalOutput = FileUtil.loadFile(outputFile) @@ -470,7 +465,7 @@ abstract class BasicBoxTest( remap: Boolean, testPackage: String?, testFunction: String, - runtime: JsIrTestRuntime, + needsFullIrRuntime: Boolean, isMainModule: Boolean ) { val translator = K2JSTranslator(config, false) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt index b92d3713478..23722987d59 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt @@ -7,7 +7,10 @@ package org.jetbrains.kotlin.js.test import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig 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.JsConfig 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 java.io.File -private val fullRuntimeKlibPath = "js/js.translator/testData/out/klibs/runtimeFull/" -private val defaultRuntimeKlibPath = "js/js.translator/testData/out/klibs/runtimeDefault/" - -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) +private val fullRuntimeKlib = KlibModuleRef("JS_IR_RUNTIME", "compiler/ir/serialization.js/build/fullRuntime/klib") +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") abstract class BasicIrBoxTest( pathToTestDir: String, @@ -53,10 +52,6 @@ abstract class BasicIrBoxTest( override val testChecker get() = if (runTestInNashorn) NashornIrJsTestChecker() else V8IrJsTestChecker - private val runtimes = mapOf( - JsIrTestRuntime.DEFAULT to defaultRuntimeKlib, - JsIrTestRuntime.FULL to fullRuntimeKlib) - override fun translateFiles( units: List, outputFile: File, @@ -68,7 +63,7 @@ abstract class BasicIrBoxTest( remap: Boolean, testPackage: String?, testFunction: String, - runtime: JsIrTestRuntime, + needsFullIrRuntime: Boolean, isMainModule: Boolean ) { val filesToCompile = units @@ -76,35 +71,20 @@ abstract class BasicIrBoxTest( // TODO: split input files to some parts (global common, local common, test) .filterNot { it.virtualFilePath.contains(BasicBoxTest.COMMON_FILES_DIR_PATH) } -// config.configuration.put(CommonConfigurationKeys.EXCLUDED_ELEMENTS_FROM_DUMPING, setOf("")) -// config.configuration.put( -// CommonConfigurationKeys.PHASES_TO_VALIDATE_AFTER, -// setOf( -// "RemoveInlineFunctionsWithReifiedTypeParametersLowering", -// "InnerClassConstructorCallsLowering", -// "InlineClassLowering", "ConstLowering" -// ) -// ) - - val runtimeKlib = runtimes[runtime]!! + val runtimeKlibs = if (needsFullIrRuntime) listOf(fullRuntimeKlib, kotlinTestKLib) else listOf(defaultRuntimeKlib) val libraries = config.configuration[JSConfigurationKeys.LIBRARIES]!!.map { File(it).name } val transitiveLibraries = config.configuration[JSConfigurationKeys.TRANSITIVE_LIBRARIES]!!.map { File(it).name } // 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") } - val allDependencies = listOf(runtimeKlib) + transitiveLibraries.map { + val allDependencies = runtimeKlibs + transitiveLibraries.map { 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 { if (!isMainModule) it.replace("_v5.js", "/") else it } diff --git a/js/js.translator/testData/box/kotlin.test/_common.kt b/js/js.translator/testData/box/kotlin.test/_common.kt index e5e2fa02afc..eb8913ab570 100644 --- a/js/js.translator/testData/box/kotlin.test/_common.kt +++ b/js/js.translator/testData/box/kotlin.test/_common.kt @@ -1,44 +1,107 @@ package common 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 { - context.raised(name) + bodyContext!!.raised(name) throw Exception(name) } @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) { - context.suite(name, ignored) { suiteFn() } + sortingContext.suite(name, ignored) { suiteFn() } } 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() + + 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 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 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)}) {") - body.runSafely() + runSafely { this.body() } record("}") } - - fun test(name: String, ignored: Boolean = false, body: TestContext.() -> Unit = {}) = indent { + override fun test(name: String, ignored: Boolean, body: TestBodyContext.() -> Unit) = indent { val num = record("test(\"$name\"${optionalIgnore(ignored)}) {") - body.runSafely() + runSafely { this.body() } if (!writtenSince(num)) { 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\")") } - fun raised(msg: String) = indent { + override fun raised(msg: String) = indent { record("raised(\"$msg\")") } - fun caught(msg: String) = indent { + override fun caught(msg: String) = indent { record("caught(\"$msg\")") } - fun returned(msg: dynamic) = indent { + override fun returned(msg: dynamic) = indent { if (msg is String) record("returned(\"$msg\")") } - private fun (TestContext.() -> Unit).runSafely() { + private fun runSafely(body: () -> Unit) { try { - this() + body() } catch (t: Throwable) { caught(t.message ?: "") @@ -100,8 +163,8 @@ class TestContext { private fun optionalIgnore(ignored: Boolean) = if (ignored) ", true" else "" } -fun checkLog(wrapInEmptySuite: Boolean = true, body: TestContext.() -> Unit): String { - val expectedContext = TestContext() +fun checkLog(wrapInEmptySuite: Boolean = true, body: SuiteContext.() -> Unit): String { + val expectedContext = SortingContext() if (wrapInEmptySuite) { expectedContext.suite("") { body() @@ -109,8 +172,12 @@ fun checkLog(wrapInEmptySuite: Boolean = true, body: TestContext.() -> Unit): St } else { 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 { return "OK" diff --git a/js/js.translator/testData/box/kotlin.test/beforeAfter.kt b/js/js.translator/testData/box/kotlin.test/beforeAfter.kt index 3d54a47996a..a5505373110 100644 --- a/js/js.translator/testData/box/kotlin.test/beforeAfter.kt +++ b/js/js.translator/testData/box/kotlin.test/beforeAfter.kt @@ -1,5 +1,6 @@ -// IGNORE_BACKEND: JS_IR -// EXPECTED_REACHABLE_NODES: 1339 +// EXPECTED_REACHABLE_NODES: 1706 +// KJS_WITH_FULL_RUNTIME + import common.* import kotlin.test.Test import kotlin.test.BeforeTest diff --git a/js/js.translator/testData/box/kotlin.test/ignore.kt b/js/js.translator/testData/box/kotlin.test/ignore.kt index ff0577967d2..7cbbcf0eff2 100644 --- a/js/js.translator/testData/box/kotlin.test/ignore.kt +++ b/js/js.translator/testData/box/kotlin.test/ignore.kt @@ -1,5 +1,6 @@ -// IGNORE_BACKEND: JS_IR -// EXPECTED_REACHABLE_NODES: 1340 +// EXPECTED_REACHABLE_NODES: 1709 +// KJS_WITH_FULL_RUNTIME + import common.* import kotlin.test.Test import kotlin.test.Ignore diff --git a/js/js.translator/testData/box/kotlin.test/incremental.kt b/js/js.translator/testData/box/kotlin.test/incremental.kt index 63ed88c2608..a3a6d1cf91a 100644 --- a/js/js.translator/testData/box/kotlin.test/incremental.kt +++ b/js/js.translator/testData/box/kotlin.test/incremental.kt @@ -1,5 +1,5 @@ -// IGNORE_BACKEND: JS_IR -// EXPECTED_REACHABLE_NODES: 1330 +// EXPECTED_REACHABLE_NODES: 1829 +// KJS_WITH_FULL_RUNTIME // FILE: a.kt package a diff --git a/js/js.translator/testData/box/kotlin.test/inherited.kt b/js/js.translator/testData/box/kotlin.test/inherited.kt index 51d1ec31379..0b9af9bfa14 100644 --- a/js/js.translator/testData/box/kotlin.test/inherited.kt +++ b/js/js.translator/testData/box/kotlin.test/inherited.kt @@ -1,5 +1,6 @@ -// IGNORE_BACKEND: JS_IR -// EXPECTED_REACHABLE_NODES: 1351 +// EXPECTED_REACHABLE_NODES: 1719 +// KJS_WITH_FULL_RUNTIME + import common.* import kotlin.test.Test import kotlin.test.BeforeTest diff --git a/js/js.translator/testData/box/kotlin.test/mpp.kt b/js/js.translator/testData/box/kotlin.test/mpp.kt index 4f1c2b85414..c09b01ee688 100644 --- a/js/js.translator/testData/box/kotlin.test/mpp.kt +++ b/js/js.translator/testData/box/kotlin.test/mpp.kt @@ -1,6 +1,6 @@ +// EXPECTED_REACHABLE_NODES: 1697 // !LANGUAGE: +MultiPlatformProjects -// EXPECTED_REACHABLE_NODES: 1328 -// IGNORE_BACKEND: JS_IR +// KJS_WITH_FULL_RUNTIME // FILE: lib.kt import kotlin.test.Test diff --git a/js/js.translator/testData/box/kotlin.test/nested.kt b/js/js.translator/testData/box/kotlin.test/nested.kt index fd170a2599a..ca86a588a2e 100644 --- a/js/js.translator/testData/box/kotlin.test/nested.kt +++ b/js/js.translator/testData/box/kotlin.test/nested.kt @@ -1,5 +1,6 @@ -// IGNORE_BACKEND: JS_IR -// EXPECTED_REACHABLE_NODES: 1367 +// EXPECTED_REACHABLE_NODES: 1735 +// KJS_WITH_FULL_RUNTIME + import common.* import kotlin.test.Test diff --git a/js/js.translator/testData/box/kotlin.test/returnTestResult.kt b/js/js.translator/testData/box/kotlin.test/returnTestResult.kt index 4d2e54b301a..30ccb85385d 100644 --- a/js/js.translator/testData/box/kotlin.test/returnTestResult.kt +++ b/js/js.translator/testData/box/kotlin.test/returnTestResult.kt @@ -1,5 +1,5 @@ -// IGNORE_BACKEND: JS_IR -// EXPECTED_REACHABLE_NODES: 1297 +// EXPECTED_REACHABLE_NODES: 1737 +// KJS_WITH_FULL_RUNTIME import common.* import kotlin.test.Test import kotlin.test.BeforeTest diff --git a/js/js.translator/testData/box/kotlin.test/simple.kt b/js/js.translator/testData/box/kotlin.test/simple.kt index 4b91019f36a..e1fe5c1631a 100644 --- a/js/js.translator/testData/box/kotlin.test/simple.kt +++ b/js/js.translator/testData/box/kotlin.test/simple.kt @@ -1,5 +1,6 @@ -// IGNORE_BACKEND: JS_IR -// EXPECTED_REACHABLE_NODES: 1330 +// EXPECTED_REACHABLE_NODES: 1698 +// KJS_WITH_FULL_RUNTIME + import common.* import kotlin.test.Test diff --git a/libraries/stdlib/js/irRuntime/kotlinNativeHacks.kt b/libraries/stdlib/js/irRuntime/kotlinNativeHacks.kt new file mode 100644 index 00000000000..11cfe6681c6 --- /dev/null +++ b/libraries/stdlib/js/irRuntime/kotlinNativeHacks.kt @@ -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()