[JS IR] Add to IR keep possibility similar to legacy-dce one

It helps to:

- keep declarations even if they are not reachable and not exported
- not minify names of not exported declarations

Compiler argument: -Xir-keep=A,B

Can be used for top-level declarations or for member

^KT-54118 fixed
This commit is contained in:
Ilya Goncharov
2022-09-20 16:06:17 +00:00
committed by Space
parent 4863e5d47b
commit d203dc35bf
28 changed files with 460 additions and 13 deletions
@@ -100,6 +100,13 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
@Argument(value = "-target", valueDescription = "{ v5 }", description = "Generate JS files for specific ECMA version")
var target: String? by NullableStringFreezableVar(null)
@Argument(
value = "-Xir-keep",
description = "Comma-separated list of fully-qualified names to not be eliminated by DCE (if it can be reached), " +
"and for which to keep non-minified names."
)
var irKeep: String? by NullableStringFreezableVar(null)
@GradleOption(
value = DefaultValues.JsModuleKinds::class,
gradleInputType = GradleInputTypes.INPUT
@@ -100,6 +100,10 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
module,
phaseConfig,
irFactory,
keep = arguments.irKeep?.split(",")
?.filterNot { it.isEmpty() }
?.toSet()
?: emptySet(),
dceRuntimeDiagnostic = RuntimeDiagnostic.resolve(
arguments.irDceRuntimeDiagnostic,
messageCollector
@@ -66,6 +66,7 @@ class JsIrBackendContext(
val symbolTable: SymbolTable,
irModuleFragment: IrModuleFragment,
val additionalExportedDeclarationNames: Set<FqName>,
keep: Set<String>,
override val configuration: CompilerConfiguration, // TODO: remove configuration from backend context
override val scriptMode: Boolean = false,
override val es6mode: Boolean = false,
@@ -86,6 +87,9 @@ class JsIrBackendContext(
val minimizedNameGenerator: MinimizedNameGenerator =
MinimizedNameGenerator()
val keeper: Keeper =
Keeper(keep)
val fieldDataCache = mutableMapOf<IrClass, Map<IrField, String>>()
override val builtIns = module.builtIns
@@ -51,6 +51,7 @@ fun compile(
phaseConfig: PhaseConfig,
irFactory: IrFactory,
exportedDeclarations: Set<FqName> = emptySet(),
keep: Set<String> = emptySet(),
dceRuntimeDiagnostic: RuntimeDiagnostic? = null,
es6mode: Boolean = false,
verifySignatures: Boolean = true,
@@ -76,6 +77,7 @@ fun compile(
deserializer,
phaseConfig,
exportedDeclarations,
keep,
dceRuntimeDiagnostic,
es6mode,
baseClassIntoMetadata,
@@ -97,6 +99,7 @@ fun compileIr(
irLinker: JsIrLinker,
phaseConfig: PhaseConfig,
exportedDeclarations: Set<FqName>,
keep: Set<String>,
dceRuntimeDiagnostic: RuntimeDiagnostic?,
es6mode: Boolean,
baseClassIntoMetadata: Boolean,
@@ -119,6 +122,7 @@ fun compileIr(
symbolTable,
allModules.first(),
exportedDeclarations,
keep,
configuration,
es6mode = es6mode,
dceRuntimeDiagnostic = dceRuntimeDiagnostic,
@@ -126,7 +130,7 @@ fun compileIr(
safeExternalBoolean = safeExternalBoolean,
safeExternalBooleanDiagnostic = safeExternalBooleanDiagnostic,
granularity = granularity,
icCompatibleIr2Js = if (icCompatibleIr2Js) IcCompatibleIr2Js.COMPATIBLE else IcCompatibleIr2Js.DISABLED
icCompatibleIr2Js = if (icCompatibleIr2Js) IcCompatibleIr2Js.COMPATIBLE else IcCompatibleIr2Js.DISABLED,
)
// Load declarations referenced during `context` initialization
@@ -32,6 +32,7 @@ fun compileWithIC(
filesToLower: Collection<IrFile>,
mainArguments: List<String>? = null,
exportedDeclarations: Set<FqName> = emptySet(),
keep: Set<String> = emptySet(),
generateFullJs: Boolean = true,
generateDceJs: Boolean = false,
dceDriven: Boolean = false,
@@ -42,7 +43,7 @@ fun compileWithIC(
baseClassIntoMetadata: Boolean = false,
lowerPerModule: Boolean = false,
safeExternalBoolean: Boolean = false,
safeExternalBooleanDiagnostic: RuntimeDiagnostic? = null
safeExternalBooleanDiagnostic: RuntimeDiagnostic? = null,
): List<JsIrFragmentAndBinaryAst> {
val irBuiltIns = mainModule.irBuiltins
val symbolTable = (irBuiltIns as IrBuiltInsOverDescriptors).symbolTable
@@ -53,13 +54,14 @@ fun compileWithIC(
symbolTable,
mainModule,
exportedDeclarations,
keep,
configuration,
es6mode = es6mode,
dceRuntimeDiagnostic = dceRuntimeDiagnostic,
baseClassIntoMetadata = baseClassIntoMetadata,
safeExternalBoolean = safeExternalBoolean,
safeExternalBooleanDiagnostic = safeExternalBooleanDiagnostic,
icCompatibleIr2Js = IcCompatibleIr2Js.IC_MODE
icCompatibleIr2Js = IcCompatibleIr2Js.IC_MODE,
)
// Load declarations referenced during `context` initialization
@@ -57,6 +57,7 @@ private fun IrDeclaration.addRootsTo(
getter?.addRootsTo(nestedVisitor, context)
setter?.addRootsTo(nestedVisitor, context)
}
isEffectivelyExternal() -> {
val correspondingProperty = when (this) {
is IrField -> correspondingPropertySymbol?.owner
@@ -68,15 +69,18 @@ private fun IrDeclaration.addRootsTo(
acceptVoid(nestedVisitor)
}
}
isExported(context) -> {
acceptVoid(nestedVisitor)
}
this is IrField -> {
// TODO: simplify
if ((initializer != null && !isKotlinPackage() || correspondingPropertySymbol?.owner?.isExported(context) == true) && !isConstant()) {
acceptVoid(nestedVisitor)
}
}
this is IrSimpleFunction -> {
val correspondingProperty = correspondingPropertySymbol?.owner ?: return
if (correspondingProperty.isExported(context)) {
@@ -43,6 +43,7 @@ internal class JsUsefulDeclarationProcessor(
val constructor = inlineClass.declarations.filterIsInstance<IrConstructor>().single { it.isPrimary }
constructor.enqueue(data, "intrinsic: jsBoxIntrinsic")
}
context.intrinsics.jsClass -> {
val ref = expression.getTypeArgument(0)!!.classifierOrFail.owner as IrDeclaration
ref.enqueue(data, "intrinsic: jsClass")
@@ -63,34 +64,42 @@ internal class JsUsefulDeclarationProcessor(
}
}
}
context.reflectionSymbols.getKClassFromExpression -> {
val ref = expression.getTypeArgument(0)?.classOrNull ?: context.irBuiltIns.anyClass
referencedJsClassesFromExpressions += ref.owner
}
context.intrinsics.jsObjectCreateSymbol -> {
val classToCreate = expression.getTypeArgument(0)!!.classifierOrFail.owner as IrClass
classToCreate.enqueue(data, "intrinsic: jsObjectCreateSymbol")
constructedClasses += classToCreate
}
context.intrinsics.jsEquals -> {
equalsMethod.enqueue(data, "intrinsic: jsEquals")
}
context.intrinsics.jsToString -> {
toStringMethod.enqueue(data, "intrinsic: jsToString")
}
context.intrinsics.jsHashCode -> {
hashCodeMethod.enqueue(data, "intrinsic: jsHashCode")
}
context.intrinsics.jsPlus -> {
if (expression.getValueArgument(0)?.type?.classOrNull == context.irBuiltIns.stringClass) {
toStringMethod.enqueue(data, "intrinsic: jsPlus")
}
}
context.intrinsics.jsConstruct -> {
val callType = expression.getTypeArgument(0)!!
val constructor = callType.getClass()!!.primaryConstructor
constructor!!.enqueue(data, "ctor call from jsConstruct-intrinsic")
}
context.intrinsics.es6DefaultType -> {
//same as jsClass
val ref = expression.getTypeArgument(0)!!.classifierOrFail.owner as IrDeclaration
@@ -104,6 +113,7 @@ internal class JsUsefulDeclarationProcessor(
constructedClasses.add(klass)
}
}
context.intrinsics.jsInvokeSuspendSuperType,
context.intrinsics.jsInvokeSuspendSuperTypeWithReceiver,
context.intrinsics.jsInvokeSuspendSuperTypeWithReceiverAndParam -> {
@@ -126,6 +136,14 @@ internal class JsUsefulDeclarationProcessor(
override fun processClass(irClass: IrClass) {
super.processClass(irClass)
if (context.keeper.shouldKeep(irClass)) {
irClass.declarations
.filter { context.keeper.shouldKeep(it) }
.forEach { declaration ->
declaration.enqueue(irClass, "kept declaration")
}
}
if (irClass.containsMetadata()) {
when {
irClass.isObject -> context.intrinsics.metadataObjectConstructorSymbol.owner.enqueue(irClass, "object metadata")
@@ -114,6 +114,18 @@ class IrModuleToJsTransformerTmp(
}
private fun doStaticMembersLowering(modules: Iterable<IrModuleFragment>) {
modules.forEach { module ->
module.files.forEach {
it.accept(
backendContext.keeper,
Keeper.KeepData(
classInKeep = false,
classShouldBeKept = false
)
)
}
}
modules.forEach { module ->
module.files.forEach { StaticMembersLowering(backendContext).lower(it) }
}
@@ -86,10 +86,15 @@ class JsNameLinkingNamer(private val context: JsIrBackendContext, private val mi
override fun getNameForMemberFunction(function: IrSimpleFunction): JsName {
require(function.dispatchReceiverParameter != null)
val signature = jsFunctionSignature(function, context)
if (context.keeper.shouldKeep(function)) {
context.minimizedNameGenerator.keepName(signature)
}
val result = if (minimizedMemberNames && !function.hasStableJsName(context)) {
function.parentAsClass.fieldData()
context.minimizedNameGenerator.nameBySignature(signature)
} else signature
} else {
signature
}
return result.toJsName()
}
@@ -125,6 +130,7 @@ class JsNameLinkingNamer(private val context: JsIrBackendContext, private val mi
context.minimizedNameGenerator.reserveName(signature)
}
}
declaration is IrProperty -> {
if (declaration.isExported(context)) {
context.minimizedNameGenerator.reserveName(declaration.getJsNameOrKotlinName().identifier)
@@ -146,7 +152,7 @@ class JsNameLinkingNamer(private val context: JsIrBackendContext, private val mi
correspondingProperty.isSimpleProperty
val safeName = when {
hasStableName -> (correspondingProperty ?: it).getJsNameOrKotlinName().identifier
minimizedMemberNames -> context.minimizedNameGenerator.generateNextName()
minimizedMemberNames && !context.keeper.shouldKeep(it) -> context.minimizedNameGenerator.generateNextName()
else -> it.safeName()
}
val resultName = if (!hasStableName) {
@@ -156,6 +162,7 @@ class JsNameLinkingNamer(private val context: JsIrBackendContext, private val mi
} else safeName
result[it] = resultName
}
it is IrFunction && it.dispatchReceiverParameter != null -> {
nameCnt[jsFunctionSignature(it, context)] = 1 // avoid clashes with member functions
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
class Keeper(private val keep: Set<String>) : IrElementVisitor<Unit, Keeper.KeepData> {
private val keptDeclarations: MutableSet<IrDeclaration> = mutableSetOf()
fun shouldKeep(declaration: IrDeclaration): Boolean {
return declaration in keptDeclarations
}
override fun visitElement(element: IrElement, data: KeepData) {
element.acceptChildren(this, data)
}
/** Keep declarations can work both ways
* if member of a class is in keep, the class should be also kept
* if a class is kept, members of the class should be also kept
* but there can be nested classes, and for nested classes we need only to propagate "keep" from top-level to nested (not vice versa)
* because we have 2 directions, we need 2 boolean flags
* [KeepData.classInKeep] responsible to propagate "keep" from class level to members direction
* [KeepData.classShouldBeKept] responsible to bubble "keep" from members to class level direction
*/
override fun visitClass(declaration: IrClass, data: KeepData) {
val prevShouldBeKept = data.classShouldBeKept
val prevClassInKeep = data.classInKeep
data.classShouldBeKept = false
val keptClass = data.classInKeep || isInKeep(declaration)
if (keptClass) {
keptDeclarations.add(declaration)
}
data.classInKeep = keptClass
super.visitClass(declaration, data)
if (data.classShouldBeKept) {
keptDeclarations.add(declaration)
}
data.classShouldBeKept = prevShouldBeKept
data.classInKeep = prevClassInKeep
}
override fun visitDeclaration(declaration: IrDeclarationBase, data: KeepData) {
super.visitDeclaration(declaration, data)
if (declaration in keptDeclarations) {
return
}
if (declaration is IrDeclarationWithName && isInKeep(declaration) || data.classInKeep) {
keptDeclarations.add(declaration)
data.classShouldBeKept = true
return
}
}
private fun isInKeep(declaration: IrDeclarationWithName): Boolean {
return declaration.fqNameWhenAvailable?.asString() in keep
}
class KeepData(var classInKeep: Boolean, var classShouldBeKept: Boolean)
}
@@ -9,6 +9,7 @@ class MinimizedNameGenerator {
private var index = 0
private val functionSignatureToName = mutableMapOf<String, String>()
private val reservedNames = mutableSetOf<String>()
private val keptNames = mutableSetOf<String>()
fun generateNextName(): String {
var candidate = index++.toJsIdentifier()
@@ -19,11 +20,16 @@ class MinimizedNameGenerator {
}
fun nameBySignature(signature: String): String {
if (signature in keptNames) return signature
return functionSignatureToName.getOrPut(signature) {
generateNextName()
}
}
fun keepName(signature: String): Boolean {
return keptNames.add(signature)
}
fun reserveName(signature: String) {
reservedNames.add(signature)
}
+1
View File
@@ -19,6 +19,7 @@ where advanced options include:
Enable runtime diagnostics when performing DCE instead of removing declarations
-Xir-generate-inline-anonymous-functions
Lambda expressions that capture values are translated into in-line anonymous JavaScript functions
-Xir-keep Comma-separated list of fully-qualified names to not be eliminated by DCE (if it can be reached), and for which to keep non-minified names.
-Xir-minimized-member-names Perform minimization for names of members
-Xir-module-name=<name> Specify a compilation module name for IR backend
-Xir-new-ir2js New fragment-based ir2js
@@ -210,6 +210,11 @@ object JsEnvironmentConfigurationDirectives : SimpleDirectivesContainer() {
applicability = DirectiveApplicability.Global
)
val ONLY_IR_DCE by directive(
description = "Disable non DCE build",
applicability = DirectiveApplicability.Global
)
val RUN_IC by directive(
description = "",
applicability = DirectiveApplicability.Global
@@ -251,4 +256,9 @@ object JsEnvironmentConfigurationDirectives : SimpleDirectivesContainer() {
""".trimIndent(),
applicability = DirectiveApplicability.Global,
)
val KEEP by stringDirective(
description = "Keep declarations",
applicability = DirectiveApplicability.Global
)
}
@@ -593,7 +593,15 @@ class GenerateIrRuntime {
private fun doBackEnd(
module: IrModuleFragment, symbolTable: SymbolTable, irBuiltIns: IrBuiltIns, jsLinker: JsIrLinker
): CompilerResult {
val context = JsIrBackendContext(module.descriptor, irBuiltIns, symbolTable, module, emptySet(), configuration)
val context = JsIrBackendContext(
module.descriptor,
irBuiltIns,
symbolTable,
module,
additionalExportedDeclarationNames = emptySet(),
keep = emptySet(),
configuration
)
ExternalDependenciesGenerator(symbolTable, listOf(jsLinker)).generateUnboundSymbolsAsDependencies()
@@ -81,6 +81,7 @@ class JsIrBackendFacade(
val splitPerFile = JsEnvironmentConfigurationDirectives.SPLIT_PER_FILE in module.directives
val perModule = JsEnvironmentConfigurationDirectives.PER_MODULE in module.directives
val runNewIr2Js = JsEnvironmentConfigurationDirectives.RUN_NEW_IR_2_JS in module.directives
val keep = module.directives[JsEnvironmentConfigurationDirectives.KEEP].toSet()
val granularity = when {
!firstTimeCompilation -> JsGenerationGranularity.WHOLE_PROGRAM
@@ -145,6 +146,7 @@ class JsIrBackendFacade(
deserializer,
phaseConfig,
exportedDeclarations = setOf(FqName.fromSegments(listOfNotNull(testPackage, TEST_FUNCTION))),
keep = keep,
dceRuntimeDiagnostic = null,
es6mode = false,
baseClassIntoMetadata = false,
@@ -166,6 +168,7 @@ class JsIrBackendFacade(
val mainArguments = JsEnvironmentConfigurator.getMainCallParametersForModule(module)
.run { if (shouldBeGenerated()) arguments() else null }
val runIrDce = JsEnvironmentConfigurationDirectives.RUN_IR_DCE in module.directives
val onlyIrDce = JsEnvironmentConfigurationDirectives.ONLY_IR_DCE in module.directives
val esModules = JsEnvironmentConfigurationDirectives.ES_MODULES in module.directives
val runNewIr2Js = JsEnvironmentConfigurationDirectives.RUN_NEW_IR_2_JS in module.directives
val perModuleOnly = JsEnvironmentConfigurationDirectives.SPLIT_PER_MODULE in module.directives
@@ -180,7 +183,7 @@ class JsIrBackendFacade(
// If perModuleOnly then skip whole program
// (it.dce => runIrDce) && (perModuleOnly => it.perModule)
val translationModes = TranslationMode.values()
.filter { (!it.dce || runIrDce) && (!perModuleOnly || it.perModule) }
.filter { (it.dce || !onlyIrDce) && (!it.dce || runIrDce) && (!perModuleOnly || it.perModule) }
.filter { it.dce == it.minimizedMemberNames }
.toSet()
val compilationOut = transformer.generateModule(loweredIr.allModules, translationModes, false)
@@ -33,8 +33,9 @@ class JsMinifierRunner(testServices: TestServices) : AbstractJsArtifactsCollecto
val dontRunGeneratedCode = globalDirectives[JsEnvironmentConfigurationDirectives.DONT_RUN_GENERATED_CODE]
.contains(testServices.defaultsProvider.defaultTargetBackend?.name)
val esModules = JsEnvironmentConfigurationDirectives.ES_MODULES in globalDirectives
val onlyIrDce = JsEnvironmentConfigurationDirectives.ONLY_IR_DCE in globalDirectives
if (dontRunGeneratedCode || esModules) return
if (dontRunGeneratedCode || esModules || onlyIrDce) return
val allJsFiles = getOnlyJsFilesForRunner(testServices, modulesToArtifact)
@@ -22,7 +22,6 @@ import java.io.File
class NodeJsGeneratorHandler(testServices: TestServices) : AbstractJsArtifactsCollector(testServices) {
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
if (someAssertionWasFailed) return
val allJsFiles = getOnlyJsFilesForRunner(testServices, modulesToArtifact)
val globalDirectives = testServices.moduleStructure.allDirectives
@@ -31,8 +30,11 @@ class NodeJsGeneratorHandler(testServices: TestServices) : AbstractJsArtifactsCo
val generateNodeJsRunner = JsEnvironmentConfigurationDirectives.GENERATE_NODE_JS_RUNNER in globalDirectives
val skipNodeJs = JsEnvironmentConfigurationDirectives.SKIP_NODE_JS in globalDirectives
val esModules = JsEnvironmentConfigurationDirectives.ES_MODULES in globalDirectives
val onlyIrDce = JsEnvironmentConfigurationDirectives.ONLY_IR_DCE in globalDirectives
if (dontRunGeneratedCode || !generateNodeJsRunner || skipNodeJs || esModules) return
if (dontRunGeneratedCode || !generateNodeJsRunner || skipNodeJs || esModules || onlyIrDce) return
val allJsFiles = getOnlyJsFilesForRunner(testServices, modulesToArtifact)
val mainModuleName = getMainModuleName(testServices)
val outputDir = File(JsEnvironmentConfigurator.getJsArtifactsOutputDir(testServices).absolutePath)
@@ -136,10 +136,14 @@ fun getAllFilesForRunner(
val artifactsPaths = modulesToArtifact.values.map { it.outputFile.absolutePath }.filter { !File(it).isDirectory }
val allJsFiles = additionalFiles + inputJsFilesBefore +artifactsPaths + commonFiles + additionalMainFiles + inputJsFilesAfter
val result = mutableMapOf(TranslationMode.FULL to allJsFiles)
val result = mutableMapOf<TranslationMode, List<String>>()
val globalDirectives = testServices.moduleStructure.allDirectives
val runIrDce = JsEnvironmentConfigurationDirectives.RUN_IR_DCE in globalDirectives
val onlyIrDce = JsEnvironmentConfigurationDirectives.ONLY_IR_DCE in globalDirectives
if (!onlyIrDce) {
result[TranslationMode.FULL] = allJsFiles
}
if (runIrDce) {
val dceJsFiles = artifactsPaths.map { it.replace(outputDir.absolutePath, dceOutputDir.absolutePath) }
val dceAllJsFiles = additionalFiles + inputJsFilesBefore + dceJsFiles + commonFiles + additionalMainFiles + inputJsFilesAfter
@@ -6797,6 +6797,16 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/keep")
@TestDataPath("$PROJECT_ROOT")
public class Keep {
@Test
public void testAllFilesPresentInKeep() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/keep"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS, true);
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/kotlin.test")
@TestDataPath("$PROJECT_ROOT")
@@ -7269,6 +7269,46 @@ public class FirJsTestGenerated extends AbstractFirJsTest {
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/keep")
@TestDataPath("$PROJECT_ROOT")
public class Keep {
@Test
public void testAllFilesPresentInKeep() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/keep"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
}
@Test
@TestMetadata("keepClass.kt")
public void testKeepClass() throws Exception {
runTest("js/js.translator/testData/box/keep/keepClass.kt");
}
@Test
@TestMetadata("keepMethod.kt")
public void testKeepMethod() throws Exception {
runTest("js/js.translator/testData/box/keep/keepMethod.kt");
}
@Test
@TestMetadata("keepNestedClass.kt")
public void testKeepNestedClass() throws Exception {
runTest("js/js.translator/testData/box/keep/keepNestedClass.kt");
}
@Test
@TestMetadata("keepNestedClassIfKeptTopLevelClass.kt")
public void testKeepNestedClassIfKeptTopLevelClass() throws Exception {
runTest("js/js.translator/testData/box/keep/keepNestedClassIfKeptTopLevelClass.kt");
}
@Test
@TestMetadata("keepOverriddenMethod.kt")
public void testKeepOverriddenMethod() throws Exception {
runTest("js/js.translator/testData/box/keep/keepOverriddenMethod.kt");
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/kotlin.test")
@TestDataPath("$PROJECT_ROOT")
@@ -7269,6 +7269,46 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/keep")
@TestDataPath("$PROJECT_ROOT")
public class Keep {
@Test
public void testAllFilesPresentInKeep() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/keep"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
}
@Test
@TestMetadata("keepClass.kt")
public void testKeepClass() throws Exception {
runTest("js/js.translator/testData/box/keep/keepClass.kt");
}
@Test
@TestMetadata("keepMethod.kt")
public void testKeepMethod() throws Exception {
runTest("js/js.translator/testData/box/keep/keepMethod.kt");
}
@Test
@TestMetadata("keepNestedClass.kt")
public void testKeepNestedClass() throws Exception {
runTest("js/js.translator/testData/box/keep/keepNestedClass.kt");
}
@Test
@TestMetadata("keepNestedClassIfKeptTopLevelClass.kt")
public void testKeepNestedClassIfKeptTopLevelClass() throws Exception {
runTest("js/js.translator/testData/box/keep/keepNestedClassIfKeptTopLevelClass.kt");
}
@Test
@TestMetadata("keepOverriddenMethod.kt")
public void testKeepOverriddenMethod() throws Exception {
runTest("js/js.translator/testData/box/keep/keepOverriddenMethod.kt");
}
}
@Nested
@TestMetadata("js/js.translator/testData/box/kotlin.test")
@TestDataPath("$PROJECT_ROOT")
+33
View File
@@ -0,0 +1,33 @@
// TARGET_BACKEND: JS_IR
// ONLY_IR_DCE
// RUN_PLAIN_BOX_FUNCTION
// INFER_MAIN_MODULE
// KEEP: A
// MODULE: keep_class
// FILE: lib.kt
class A {
fun foo(): String {
return "foo"
}
fun bar(): String {
return "bar"
}
}
@JsExport
fun bar(): A {
return A()
}
// FILE: test.js
function box() {
var a = this["keep_class"].bar()
if (a.foo_26di_k$() != "foo") return "fail 1"
if (a.bar_232r_k$() != "bar") return "fail 2"
return "OK"
}
+33
View File
@@ -0,0 +1,33 @@
// TARGET_BACKEND: JS_IR
// ONLY_IR_DCE
// RUN_PLAIN_BOX_FUNCTION
// INFER_MAIN_MODULE
// KEEP: A.foo
// MODULE: keep_method
// FILE: lib.kt
class A {
fun foo(): String {
return "foo"
}
fun bar(): String {
return "bar"
}
}
@JsExport
fun bar(): A {
return A()
}
// FILE: test.js
function box() {
var a = this["keep_method"].bar()
if (a.foo_26di_k$() != "foo") return "fail 1"
if (typeof a.bar_232r_k$ !== "undefined") return "fail 2"
return "OK"
}
+44
View File
@@ -0,0 +1,44 @@
// TARGET_BACKEND: JS_IR
// ONLY_IR_DCE
// RUN_PLAIN_BOX_FUNCTION
// INFER_MAIN_MODULE
// KEEP: A.B
// MODULE: keep_nested_class
// FILE: lib.kt
class A {
fun foo(): String {
return "foo"
}
fun bar(): String {
return "bar"
}
class B {
fun baz() = "baz"
}
}
@JsExport
fun barA(): A {
return A()
}
@JsExport
fun bar(): A.B {
return A.B()
}
// FILE: test.js
function box() {
var a = this["keep_nested_class"].barA()
var b = this["keep_nested_class"].bar()
if (typeof a.foo_26di_k$ !== "undefined") return "fail 1"
if (typeof a.bar_232r_k$ !== "undefined") return "fail 2"
if (b.baz_232z_k$() != "baz") return "fail 3"
return "OK"
}
@@ -0,0 +1,44 @@
// TARGET_BACKEND: JS_IR
// ONLY_IR_DCE
// RUN_PLAIN_BOX_FUNCTION
// INFER_MAIN_MODULE
// KEEP: A
// MODULE: keep_nested_class_if_kept_top_level_class
// FILE: lib.kt
class A {
fun foo(): String {
return "foo"
}
fun bar(): String {
return "bar"
}
class B {
fun baz() = "baz"
}
}
@JsExport
fun barA(): A {
return A()
}
@JsExport
fun bar(): A.B {
return A.B()
}
// FILE: test.js
function box() {
var a = this["keep_nested_class_if_kept_top_level_class"].barA()
var b = this["keep_nested_class_if_kept_top_level_class"].bar()
if (a.foo_26di_k$() != "foo") return "fail 1"
if (a.bar_232r_k$() != "bar") return "fail 2"
if (b.baz_232z_k$() != "baz") return "fail 3"
return "OK"
}
@@ -0,0 +1,38 @@
// TARGET_BACKEND: JS_IR
// ONLY_IR_DCE
// RUN_PLAIN_BOX_FUNCTION
// INFER_MAIN_MODULE
// KEEP: A.foo
// MODULE: keep_overridden_method
// FILE: lib.kt
open class A {
open fun foo(): String {
return "foo"
}
open fun bar(): String {
return "bar"
}
}
open class B : A() {
override fun foo(): String {
return super.foo() + "!"
}
}
@JsExport
fun bar(): B {
return B()
}
// FILE: test.js
function box() {
var b = this["keep_overridden_method"].bar()
if (b.foo_26di_k$() != "foo!") return "fail 1"
return "OK"
}
@@ -92,7 +92,8 @@ class JsCoreScriptingCompiler(
psi2irContext.irBuiltIns,
psi2irContext.symbolTable,
irModuleFragment,
emptySet(),
additionalExportedDeclarationNames = emptySet(),
keep = emptySet(),
environment.configuration,
true
)
@@ -62,7 +62,8 @@ class JsScriptDependencyCompiler(
irBuiltIns,
symbolTable,
moduleFragment,
emptySet(),
additionalExportedDeclarationNames = emptySet(),
keep = emptySet(),
configuration,
true
)