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)
|
val dceResult = DeadCodeElimination.run(files, includedDeclarations, logConsumer)
|
||||||
if (dceResult.status == DeadCodeEliminationStatus.FAILED) return ExitCode.COMPILATION_ERROR
|
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
|
val reachabilitySeverity = if (arguments.printReachabilityInfo) CompilerMessageSeverity.INFO else CompilerMessageSeverity.LOGGING
|
||||||
messageCollector.report(reachabilitySeverity, "")
|
messageCollector.report(reachabilitySeverity, "")
|
||||||
|
|||||||
@@ -57,9 +57,21 @@ class ReachabilityTracker(
|
|||||||
analysisResult.nodeMap[x] == null && x !in analysisResult.astNodesToEliminate
|
analysisResult.nodeMap[x] == null && x !in analysisResult.astNodesToEliminate
|
||||||
|
|
||||||
override fun visitNameRef(nameRef: JsNameRef) {
|
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 != null) {
|
||||||
if (!node.reachable) {
|
if (!node.reachable) {
|
||||||
reportAndNest("reach: referenced name $node", currentNodeWithLocation) {
|
reportAndNest("reach: referenced name $node", currentNodeWithLocation) {
|
||||||
@@ -67,9 +79,10 @@ class ReachabilityTracker(
|
|||||||
currentNodeWithLocation?.let { node.usedByAstNodes += it }
|
currentNodeWithLocation?.let { node.usedByAstNodes += it }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
super.visitNameRef(nameRef)
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +192,7 @@ class ReachabilityTracker(
|
|||||||
}
|
}
|
||||||
else if (!node.declarationReachable) {
|
else if (!node.declarationReachable) {
|
||||||
node.declarationReachable = true
|
node.declarationReachable = true
|
||||||
|
reachableNodesImpl += node
|
||||||
|
|
||||||
node.original.qualifier?.parent?.let {
|
node.original.qualifier?.parent?.let {
|
||||||
reportAndNest("reach-decl: parent $it", null) {
|
reportAndNest("reach-decl: parent $it", null) {
|
||||||
@@ -197,8 +211,8 @@ class ReachabilityTracker(
|
|||||||
override fun visitPrefixOperation(x: JsPrefixOperation) {
|
override fun visitPrefixOperation(x: JsPrefixOperation) {
|
||||||
if (x.operator == JsUnaryOperator.TYPEOF) {
|
if (x.operator == JsUnaryOperator.TYPEOF) {
|
||||||
val arg = x.arg
|
val arg = x.arg
|
||||||
if (arg is JsNameRef && arg.qualifier == null) {
|
context.extractNode(arg)?.let {
|
||||||
context.extractNode(arg)?.let { reachDeclaration(it) }
|
reachDeclaration(it)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -562,15 +562,18 @@ public class JsAstMapper {
|
|||||||
Node fromFnNameNode = fnNode.getFirstChild();
|
Node fromFnNameNode = fnNode.getFirstChild();
|
||||||
Node fromParamNode = fnNode.getFirstChild().getNext().getFirstChild();
|
Node fromParamNode = fnNode.getFirstChild().getNext().getFirstChild();
|
||||||
Node fromBodyNode = fnNode.getFirstChild().getNext().getNext();
|
Node fromBodyNode = fnNode.getFirstChild().getNext().getNext();
|
||||||
JsFunction toFn = scopeContext.enterFunction();
|
|
||||||
|
|
||||||
// Decide the function's name, if any.
|
// Decide the function's name, if any.
|
||||||
//
|
//
|
||||||
String fnNameIdent = fromFnNameNode.getString();
|
String fnNameIdent = fromFnNameNode.getString();
|
||||||
|
JsName functionName = null;
|
||||||
if (fnNameIdent != null && fnNameIdent.length() > 0) {
|
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) {
|
while (fromParamNode != null) {
|
||||||
String fromParamName = fromParamNode.getString();
|
String fromParamName = fromParamNode.getString();
|
||||||
JsName name = scopeContext.localNameFor(fromParamName);
|
JsName name = scopeContext.localNameFor(fromParamName);
|
||||||
|
|||||||
@@ -542,13 +542,14 @@ abstract class BasicBoxTest(
|
|||||||
val testFunctionFqn = testModuleName + (if (testPackage.isNullOrEmpty()) "" else ".$testPackage") + ".$testFunction"
|
val testFunctionFqn = testModuleName + (if (testPackage.isNullOrEmpty()) "" else ".$testPackage") + ".$testFunction"
|
||||||
val additionalReachableNodes = setOf(
|
val additionalReachableNodes = setOf(
|
||||||
testFunctionFqn, "kotlin.kotlin.io.BufferedOutput", "kotlin.kotlin.io.output.flush",
|
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 allFilesToMinify = filesToMinify.values + kotlinJsInputFile + kotlinTestJsInputFile
|
||||||
val dceResult = DeadCodeElimination.run(allFilesToMinify, additionalReachableNodes) { _, _ -> }
|
val dceResult = DeadCodeElimination.run(allFilesToMinify, additionalReachableNodes) { _, _ -> }
|
||||||
|
|
||||||
val reachableNodes = dceResult.reachableNodes
|
val reachableNodes = dceResult.reachableNodes
|
||||||
minificationThresholdChecker(reachableNodes.size)
|
minificationThresholdChecker(reachableNodes.count { it.reachable })
|
||||||
|
|
||||||
val runList = mutableListOf<String>()
|
val runList = mutableListOf<String>()
|
||||||
runList += kotlinJsLibOutput
|
runList += kotlinJsLibOutput
|
||||||
|
|||||||
@@ -42,6 +42,12 @@ public class DceTestGenerated extends AbstractDceTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("commonjs.js")
|
||||||
public void testCommonjs() throws Exception {
|
public void testCommonjs() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/dce/commonjs.js");
|
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");
|
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/dce/cycle.js");
|
||||||
doTest(fileName);
|
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
|
package foo
|
||||||
|
|
||||||
class SimpleEnumerator {
|
class SimpleEnumerator {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// EXPECTED_REACHABLE_NODES: 1222
|
// EXPECTED_REACHABLE_NODES: 1098
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// EXPECTED_REACHABLE_NODES: 1249
|
// EXPECTED_REACHABLE_NODES: 1123
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
class C(val i: Int) : Comparable<C>, A() {
|
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