Fix JS DCE removing reachable functions
This commit is contained in:
@@ -60,7 +60,7 @@ class K2JSDce : CLITool<K2JSDceArguments>() {
|
||||
}
|
||||
val dceResult = DeadCodeElimination.run(files, includedDeclarations, logConsumer)
|
||||
if (dceResult.status == DeadCodeEliminationStatus.FAILED) return ExitCode.COMPILATION_ERROR
|
||||
val nodes = dceResult.reachableNodes
|
||||
val nodes = dceResult.reachableNodes.filterTo(mutableSetOf()) { it.reachable }
|
||||
|
||||
val reachabilitySeverity = if (arguments.printReachabilityInfo) CompilerMessageSeverity.INFO else CompilerMessageSeverity.LOGGING
|
||||
messageCollector.report(reachabilitySeverity, "")
|
||||
|
||||
@@ -57,9 +57,21 @@ class ReachabilityTracker(
|
||||
analysisResult.nodeMap[x] == null && x !in analysisResult.astNodesToEliminate
|
||||
|
||||
override fun visitNameRef(nameRef: JsNameRef) {
|
||||
if (nameRef in analysisResult.astNodesToSkip) return
|
||||
if (visitNameLikeNode(nameRef)) {
|
||||
super.visitNameRef(nameRef)
|
||||
}
|
||||
}
|
||||
|
||||
val node = context.extractNode(nameRef)
|
||||
override fun visitArrayAccess(x: JsArrayAccess) {
|
||||
if (visitNameLikeNode(x)) {
|
||||
super.visitArrayAccess(x)
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitNameLikeNode(x: JsExpression): Boolean {
|
||||
if (x in analysisResult.astNodesToSkip) return false
|
||||
|
||||
val node = context.extractNode(x)
|
||||
if (node != null) {
|
||||
if (!node.reachable) {
|
||||
reportAndNest("reach: referenced name $node", currentNodeWithLocation) {
|
||||
@@ -67,9 +79,10 @@ class ReachabilityTracker(
|
||||
currentNodeWithLocation?.let { node.usedByAstNodes += it }
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
else {
|
||||
super.visitNameRef(nameRef)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,6 +192,7 @@ class ReachabilityTracker(
|
||||
}
|
||||
else if (!node.declarationReachable) {
|
||||
node.declarationReachable = true
|
||||
reachableNodesImpl += node
|
||||
|
||||
node.original.qualifier?.parent?.let {
|
||||
reportAndNest("reach-decl: parent $it", null) {
|
||||
@@ -197,8 +211,8 @@ class ReachabilityTracker(
|
||||
override fun visitPrefixOperation(x: JsPrefixOperation) {
|
||||
if (x.operator == JsUnaryOperator.TYPEOF) {
|
||||
val arg = x.arg
|
||||
if (arg is JsNameRef && arg.qualifier == null) {
|
||||
context.extractNode(arg)?.let { reachDeclaration(it) }
|
||||
context.extractNode(arg)?.let {
|
||||
reachDeclaration(it)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,15 +562,18 @@ public class JsAstMapper {
|
||||
Node fromFnNameNode = fnNode.getFirstChild();
|
||||
Node fromParamNode = fnNode.getFirstChild().getNext().getFirstChild();
|
||||
Node fromBodyNode = fnNode.getFirstChild().getNext().getNext();
|
||||
JsFunction toFn = scopeContext.enterFunction();
|
||||
|
||||
// Decide the function's name, if any.
|
||||
//
|
||||
String fnNameIdent = fromFnNameNode.getString();
|
||||
JsName functionName = null;
|
||||
if (fnNameIdent != null && fnNameIdent.length() > 0) {
|
||||
toFn.setName(scopeContext.globalNameFor(fnNameIdent));
|
||||
functionName = scopeContext.localNameFor(fnNameIdent);
|
||||
}
|
||||
|
||||
JsFunction toFn = scopeContext.enterFunction();
|
||||
toFn.setName(functionName);
|
||||
|
||||
while (fromParamNode != null) {
|
||||
String fromParamName = fromParamNode.getString();
|
||||
JsName name = scopeContext.localNameFor(fromParamName);
|
||||
|
||||
@@ -542,13 +542,14 @@ abstract class BasicBoxTest(
|
||||
val testFunctionFqn = testModuleName + (if (testPackage.isNullOrEmpty()) "" else ".$testPackage") + ".$testFunction"
|
||||
val additionalReachableNodes = setOf(
|
||||
testFunctionFqn, "kotlin.kotlin.io.BufferedOutput", "kotlin.kotlin.io.output.flush",
|
||||
"kotlin.kotlin.io.output.buffer", "kotlin-test.kotlin.test.overrideAsserter_wbnzx$"
|
||||
"kotlin.kotlin.io.output.buffer", "kotlin-test.kotlin.test.overrideAsserter_wbnzx$",
|
||||
"kotlin-test.kotlin.test.DefaultAsserter"
|
||||
)
|
||||
val allFilesToMinify = filesToMinify.values + kotlinJsInputFile + kotlinTestJsInputFile
|
||||
val dceResult = DeadCodeElimination.run(allFilesToMinify, additionalReachableNodes) { _, _ -> }
|
||||
|
||||
val reachableNodes = dceResult.reachableNodes
|
||||
minificationThresholdChecker(reachableNodes.size)
|
||||
minificationThresholdChecker(reachableNodes.count { it.reachable })
|
||||
|
||||
val runList = mutableListOf<String>()
|
||||
runList += kotlinJsLibOutput
|
||||
|
||||
@@ -42,6 +42,12 @@ public class DceTestGenerated extends AbstractDceTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("arrayAccess.js")
|
||||
public void testArrayAccess() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/dce/arrayAccess.js");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("commonjs.js")
|
||||
public void testCommonjs() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/dce/commonjs.js");
|
||||
@@ -53,4 +59,16 @@ public class DceTestGenerated extends AbstractDceTest {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/dce/cycle.js");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localVarAndFunction.js")
|
||||
public void testLocalVarAndFunction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/dce/localVarAndFunction.js");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("typeOf.js")
|
||||
public void testTypeOf() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/dce/typeOf.js");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1230
|
||||
// EXPECTED_REACHABLE_NODES: 1106
|
||||
package foo
|
||||
|
||||
class SimpleEnumerator {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1222
|
||||
// EXPECTED_REACHABLE_NODES: 1098
|
||||
package foo
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1249
|
||||
// EXPECTED_REACHABLE_NODES: 1123
|
||||
package foo
|
||||
|
||||
class C(val i: Int) : Comparable<C>, A() {
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
(function(_) {
|
||||
var x = {};
|
||||
function foo() {
|
||||
return x["bar"]();
|
||||
}
|
||||
x.bar = function() {
|
||||
return "bar";
|
||||
};
|
||||
x.baz = function() {
|
||||
return "baz";
|
||||
};
|
||||
_.foo = foo;
|
||||
})(module.exports);
|
||||
|
||||
// REQUEST_REACHABLE: main.foo
|
||||
// ASSERT_REACHABLE: x.bar
|
||||
// ASSERT_UNREACHABLE: x.baz
|
||||
@@ -0,0 +1,27 @@
|
||||
(function(_, Kotlin) {
|
||||
Kotlin.defineInlineFunction("bar", Kotlin.wrapFunction(function() {
|
||||
function baz() {
|
||||
return "bar";
|
||||
}
|
||||
return function() {
|
||||
return baz();
|
||||
};
|
||||
}));
|
||||
return _;
|
||||
})(module.exports, require("kotlin"));
|
||||
|
||||
(function(_) {
|
||||
function baz() {
|
||||
return "baz";
|
||||
}
|
||||
|
||||
function foo() {
|
||||
return baz();
|
||||
}
|
||||
_.foo = foo;
|
||||
})(module.exports);
|
||||
|
||||
|
||||
// REQUEST_REACHABLE: main.foo
|
||||
// ASSERT_REACHABLE: main.foo
|
||||
// ASSERT_REACHABLE: baz
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
(function(_) {
|
||||
var x = {};
|
||||
x.y = {};
|
||||
x.y.bar = function() {
|
||||
return "bar";
|
||||
};
|
||||
function foo() {
|
||||
return typeof x.y;
|
||||
}
|
||||
_.foo = foo;
|
||||
})(module.exports);
|
||||
|
||||
// REQUEST_REACHABLE: main.foo
|
||||
// ASSERT_REACHABLE: x.y
|
||||
// ASSERT_UNREACHABLE: x.y.bar
|
||||
Reference in New Issue
Block a user