Skip to content

Commit

Permalink
Modify PureFunctionIdentifier to run under OptimizeCalls.
Browse files Browse the repository at this point in the history
Doing so eliminates it's dependence (the last in the compiler) on `NameBasedDefinitionProvider`.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=242210304
  • Loading branch information
nreid260 authored and brad4d committed Apr 8, 2019
1 parent edd9274 commit b4551ce
Show file tree
Hide file tree
Showing 7 changed files with 411 additions and 221 deletions.
2 changes: 2 additions & 0 deletions src/com/google/javascript/jscomp/OptimizeCalls.java
Expand Up @@ -100,6 +100,7 @@ public Builder addPass(CallGraphCompilerPass pass) {
} }


public OptimizeCalls build() { public OptimizeCalls build() {
checkNotNull(compiler);
checkNotNull(considerExterns); checkNotNull(considerExterns);


return new OptimizeCalls(compiler, passes.build(), considerExterns); return new OptimizeCalls(compiler, passes.build(), considerExterns);
Expand Down Expand Up @@ -412,6 +413,7 @@ public void visit(NodeTraversal t, Node n, Node unused) {


private void maybeAddNameReference(Node n) { private void maybeAddNameReference(Node n) {
String name = n.getString(); String name = n.getString();
// TODO(b/129503101): Why are we limiting ourselves to global names?
Var var = globalScope.getSlot(name); Var var = globalScope.getSlot(name);
if (var != null && (considerExterns || !var.isExtern())) { if (var != null && (considerExterns || !var.isExtern())) {
// As every name declaration is unique due to normalizations, it is only necessary to build // As every name declaration is unique due to normalizations, it is only necessary to build
Expand Down
525 changes: 344 additions & 181 deletions src/com/google/javascript/jscomp/PureFunctionIdentifier.java

Large diffs are not rendered by default.

Expand Up @@ -62,10 +62,7 @@ protected CompilerPass getProcessor(final Compiler compiler) {


@Override @Override
public void process(Node externs, Node root) { public void process(Node externs, Node root) {
NameBasedDefinitionProvider defFinder = new NameBasedDefinitionProvider(compiler, true); new PureFunctionIdentifier.Driver(compiler).process(externs, root);
defFinder.process(externs, root);

new PureFunctionIdentifier(compiler, defFinder).process(externs, root);
new RemoveUnusedCode.Builder(compiler) new RemoveUnusedCode.Builder(compiler)
.removeLocalVars(true) .removeLocalVars(true)
.removeGlobals(true) .removeGlobals(true)
Expand Down
Expand Up @@ -46,9 +46,8 @@ protected CompilerPass getProcessor(final Compiler compiler) {
return new CompilerPass() { return new CompilerPass() {
@Override @Override
public void process(Node externs, Node root) { public void process(Node externs, Node root) {
NameBasedDefinitionProvider defFinder = new NameBasedDefinitionProvider(compiler, true); new PureFunctionIdentifier.Driver(compiler).process(externs, root);
defFinder.process(externs, root);
new PureFunctionIdentifier(compiler, defFinder).process(externs, root);
PeepholeOptimizationsPass peepholePass = PeepholeOptimizationsPass peepholePass =
new PeepholeOptimizationsPass(compiler, getName(), new PeepholeRemoveDeadCode()); new PeepholeOptimizationsPass(compiler, getName(), new PeepholeRemoveDeadCode());
peepholePass.process(externs, root); peepholePass.process(externs, root);
Expand Down
72 changes: 51 additions & 21 deletions test/com/google/javascript/jscomp/PureFunctionIdentifierTest.java
Expand Up @@ -199,8 +199,9 @@ protected int getNumRepetitions() {
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();


enableTypeCheck();
enableNormalize(); enableNormalize();
enableGatherExternProperties();
enableTypeCheck();
} }


@Override @Override
Expand Down Expand Up @@ -228,13 +229,8 @@ public void process(Node externs, Node root) {
localResultCalls = new ArrayList<>(); localResultCalls = new ArrayList<>();
compiler.setHasRegExpGlobalReferences(regExpHaveSideEffects); compiler.setHasRegExpGlobalReferences(regExpHaveSideEffects);
compiler.getOptions().setUseTypesForLocalOptimization(true); compiler.getOptions().setUseTypesForLocalOptimization(true);
NameBasedDefinitionProvider defFinder = new NameBasedDefinitionProvider(compiler, true);
defFinder.process(externs, root);

PureFunctionIdentifier pureFunctionIdentifier =
new PureFunctionIdentifier(compiler, defFinder);
pureFunctionIdentifier.process(externs, root);


new PureFunctionIdentifier.Driver(compiler).process(externs, root);
NodeTraversal.traverse(compiler, externs, this); NodeTraversal.traverse(compiler, externs, this);
NodeTraversal.traverse(compiler, root, this); NodeTraversal.traverse(compiler, root, this);
} }
Expand Down Expand Up @@ -566,24 +562,33 @@ public void testSharedFunctionName2() {


@Test @Test
public void testAnnotationInExternStubs1() { public void testAnnotationInExternStubs1() {
assertPureCallsMarked("o.propWithStubBefore('a');", // In the case where a property is defined first as a stub and then with a FUNCTION:
ImmutableList.of("o.propWithStubBefore")); // we have to make a conservative assumption about the behaviour of the extern, since the stub
// carried no information.
assertNoPureCalls("o.propWithStubBefore('a');");
} }


@Test @Test
public void testAnnotationInExternStubs1b() { public void testAnnotationInExternStubs1b() {
assertPureCallsMarked("o.propWithStubBeforeWithJSDoc('a');", // In the case where a property is defined first as a stub and then with a FUNCTION:
ImmutableList.of("o.propWithStubBeforeWithJSDoc")); // we have to make a conservative assumption about the behaviour of the extern, since the stub
// carried no information.
assertNoPureCalls("o.propWithStubBeforeWithJSDoc('a');");
} }


@Test @Test
public void testAnnotationInExternStubs2() { public void testAnnotationInExternStubs2() {
assertPureCallsMarked("o.propWithStubAfter('a');", // In the case where a property is defined first with a FUNCTION and then as a stub:
ImmutableList.of("o.propWithStubAfter")); // we have to make a conservative assumption about the behaviour of the extern, since the stub
// carried no information.
assertNoPureCalls("o.propWithStubAfter('a');");
} }


@Test @Test
public void testAnnotationInExternStubs3() { public void testAnnotationInExternStubs3() {
// In the case where a property is defined first with a FUNCTION and then as a stub:
// we have to make a conservative assumption about the behaviour of the extern, since the stub
// carried no information.
assertNoPureCalls("propWithAnnotatedStubAfter('a');"); assertNoPureCalls("propWithAnnotatedStubAfter('a');");
} }


Expand Down Expand Up @@ -1586,7 +1591,9 @@ public void testAmbiguousDefinitionsAllCallThis() {
"C.f = function() { };", "C.f = function() { };",
"var g = function() {D.f()};", "var g = function() {D.f()};",
"/** @constructor */ var h = function() {E.f.apply(this)};", "/** @constructor */ var h = function() {E.f.apply(this)};",
"var i = function() {F.f.apply({})};", // it can't tell {} is local. // TODO(nickreid): Detect that `{}` being passed as `this` is local. With that
// understanding, we could determine that `i` has no side-effects.
"var i = function() { F.f.apply({}); };",
"g();", "g();",
"new h();", "new h();",
"i();"); "i();");
Expand Down Expand Up @@ -1679,7 +1686,7 @@ public void testAmbiguousDefinitionsDoubleDefinition2() {
"a = function() {}", // This is the only `a` is in scope here. "a = function() {}", // This is the only `a` is in scope here.
"B.x();", "B.x();",
"a();"); "a();");
assertPureCallsMarked(source, ImmutableList.of("a")); assertNoPureCalls(source);
} }


@Test @Test
Expand All @@ -1700,10 +1707,12 @@ public void testAmbiguousDefinitionsDoubleDefinition4() {
String source = String source =
lines( lines(
"var global = 1;", "var global = 1;",
"",
"A.x = function a() {}", "A.x = function a() {}",
"B.x = function() { global++; }", "B.x = function() { global++; }",
"",
"B.x();", "B.x();",
"a();" // `a` is not in scope here. "a();" // `a` isn't in scope here.
); );
assertNoPureCalls(source); assertNoPureCalls(source);
} }
Expand All @@ -1713,11 +1722,13 @@ public void testAmbiguousDefinitionsDoubleDefinition5() {
String source = String source =
lines( lines(
"var global = 1;", "var global = 1;",
"",
"A.x = cond ? function a() { global++ } : function b() {}", "A.x = cond ? function a() { global++ } : function b() {}",
"B.x = function() { global++; }", "B.x = function() { global++; }",
"",
"B.x();", "B.x();",
"a();", // `a` is not in scope here. "a();", // `a` isn't in scope here.
"b();" // `b` is not in scope here. "b();" // `b` isn't in scope here.
); );
assertNoPureCalls(source); assertNoPureCalls(source);
} }
Expand All @@ -1733,6 +1744,27 @@ public void testAmbiguousDefinitionsDoubleDefinition6() {
assertNoPureCalls(source); assertNoPureCalls(source);
} }


@Test
public void testInnerFunction_isNeverPure() {
// TODO(b/129503101): `pure` should be marked pure in this case.
assertNoPureCalls(
lines(
"function f() {", //
" function pure() { };",
" pure();",
"}"));
}

@Test
public void testNamedFunctionExpression_isNeverPure() {
// TODO(b/129503101): `pure` should be marked pure in this case.
assertNoPureCalls(
lines(
"(function pure() {", //
" pure();",
"})"));
}

@Test @Test
public void testCallBeforeDefinition() { public void testCallBeforeDefinition() {
assertPureCallsMarked("f(); function f(){}", ImmutableList.of("f")); assertPureCallsMarked("f(); function f(){}", ImmutableList.of("f"));
Expand Down Expand Up @@ -2341,9 +2373,7 @@ public void testDefaultValueInitializers_areConsidered_whenAnalyzingFunctions()
"", "",
"function foo(a = pure()) { }", "function foo(a = pure()) { }",
"foo();"), "foo();"),
ImmutableList.of( ImmutableList.of("pure", "foo"));
// TODO(b/128035138): Should be ["pure", "foo"]. "pure" is being polluted.
));


assertPureCallsMarked( assertPureCallsMarked(
lines( lines(
Expand Down
Expand Up @@ -470,7 +470,10 @@ public void testNoSideEffectAnnotation5() {


@Test @Test
public void testNoSideEffectAnnotation6() { public void testNoSideEffectAnnotation6() {
test(externs("f = /** @nosideeffects */ function(){};"), srcs("var a = f();"), expected("")); test(
externs("var f = /** @nosideeffects */ function(){};"), //
srcs("var a = f();"),
expected(""));
} }


@Test @Test
Expand All @@ -495,15 +498,11 @@ public void testNoSideEffectAnnotation8() {
} }


@Test @Test
public void testNoSideEffectAnnotation9() { public void testNoSideEffectAnnotation_whenUsedOnDuplicateDefinitions_eliminatesSideEffects() {
test( test(
externs( externs(
"f = /** @nosideeffects */ function(){};", "f = /** @nosideeffects */ function(){};"), "var f = /** @nosideeffects */ function(){};",
srcs("var a = f();"), "var f = /** @nosideeffects */ function(){};"),
expected(""));

test(
externs("f = /** @nosideeffects */ function(){};"), //
srcs("var a = f();"), srcs("var a = f();"),
expected("")); expected(""));
} }
Expand Down
Expand Up @@ -558,10 +558,10 @@ public void testRemoveFromImportStatement_ES6Modules() {
} }


@Test @Test
public void testLetConstBlocks_withES6Modules() { public void testLetConstBlocks_inFunction_exportedFromEs6Module() {
test( test(
"export function f() {return 1; let a; } f();", "export function f() {return 1; let a; }", //
"export function f() {return 1;}"); "export function f() {return 1;};");


test( test(
"export function f() {return 1; const a = 1; }", "export function f() {return 1; const a = 1; }",
Expand All @@ -578,7 +578,7 @@ public void testLetConstBlocks_withES6Modules() {
// TODO(tbreisacher): Fix and enable. // TODO(tbreisacher): Fix and enable.
@Test @Test
@Ignore @Ignore
public void testLetConstBlocks_withES6Modules2() { public void testLetConstBlocks_asEs6ModuleExport() {
test("export let x = 2;", ""); test("export let x = 2;", "");
} }


Expand Down

0 comments on commit b4551ce

Please sign in to comment.