Skip to content

Commit

Permalink
Add support for class declarations to RescopeGlobalSymbols.
Browse files Browse the repository at this point in the history
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=178319242
  • Loading branch information
lauraharker authored and blickly committed Dec 8, 2017
1 parent d45edd5 commit 7118119
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 13 deletions.
5 changes: 3 additions & 2 deletions src/com/google/javascript/jscomp/NodeUtil.java
Expand Up @@ -479,13 +479,14 @@ public static Node getNameNode(Node n) {

default:
// function name() ...
// or
// class Name ...
Node funNameNode = n.getFirstChild();
// Don't return the name node for anonymous functions/classes.
// TODO(tbreisacher): Currently we do two kinds of "empty" checks because
// anonymous classes have an EMPTY name node while anonymous functions
// have a STRING node with an empty string. Consider making these the same.
return (funNameNode.isEmpty() || funNameNode.getString().isEmpty())
? null : funNameNode;
return (funNameNode.isEmpty() || funNameNode.getString().isEmpty()) ? null : funNameNode;
}
}

Expand Down
40 changes: 29 additions & 11 deletions src/com/google/javascript/jscomp/RescopeGlobalSymbols.java
Expand Up @@ -160,9 +160,7 @@ public void process(Node externs, Node root) {

// Turn global named function statements into var assignments.
NodeTraversal.traverseEs6(
compiler,
root,
new RewriteGlobalFunctionStatementsToVarAssignmentsCallback());
compiler, root, new RewriteGlobalClassFunctionDeclarationsToVarAssignmentsCallback());

// Find global names that are used in more than one module. Those that
// are have to be rewritten.
Expand All @@ -184,23 +182,43 @@ public void process(Node externs, Node root) {
}

/**
* Rewrites function statements to var statements + assignment.
* Rewrites global function and class declarations to var statements + assignment. Ignores
* non-global function and class declarations.
*
* <pre>function test(){}</pre>
*
* becomes
*
* <pre>var test = function (){}</pre>
*
* After this traversal, the special case of global function statements
* can be ignored.
* <pre>class A {}</pre>
*
* becomes
*
* <pre>var A = class {}</pre>
*
* After this traversal, the special case of global class and function statements can be ignored.
*
* <p>This is helpful when rewriting simple names to property accesses on the global symbol, since
* {@code class A {}} cannot be rewritten directly to {@code class NS.A {}}
*/
private class RewriteGlobalFunctionStatementsToVarAssignmentsCallback
private class RewriteGlobalClassFunctionDeclarationsToVarAssignmentsCallback
extends AbstractShallowStatementCallback {
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (NodeUtil.isFunctionDeclaration(n)) {
String name = NodeUtil.getName(n);
n.getFirstChild().setString("");
compiler.reportChangeToEnclosingScope(n.getFirstChild());
if (NodeUtil.isFunctionDeclaration(n)
// Since class declarations are block-scoped, only handle them if in the global scope.
|| (NodeUtil.isClassDeclaration(n) && t.inGlobalScope())) {
Node nameNode = NodeUtil.getNameNode(n);
String name = nameNode.getString();
// Remove the class or function name. Anonymous classes have an EMPTY node, while anonymous
// functions have a NAME node with an empty string.
if (n.isClass()) {
nameNode.replaceWith(IR.empty().srcref(nameNode));
} else {
nameNode.setString("");
compiler.reportChangeToEnclosingScope(nameNode);
}
Node prev = n.getPrevious();
n.detach();
Node var = NodeUtil.newVarNode(name, n);
Expand Down
47 changes: 47 additions & 0 deletions test/com/google/javascript/jscomp/RescopeGlobalSymbolsTest.java
Expand Up @@ -310,6 +310,46 @@ public void testArrayDestructuringAssignments() {
" _.obj = {}; [_.obj['foo bar']] = []; _.obj['foo bar'];");
}

public void testClasses() {
test("class A {}", "_.A = class {};");
test("class A {} class B extends A {}", "_.A = class {}; _.B = class extends _.A {}");
test("class A {} let a = new A;", "_.A = class {}; _.a = new _.A;");
test(
lines(
"const PI = 3.14;",
"class A {",
" static printPi() {",
" console.log(PI);",
" }",
"}",
"A.printPi();"),
lines(
"_.PI = 3.14;",
"_.A = class {",
" static printPi() {",
" window.console.log(_.PI);",
" }",
"}",
"_.A.printPi();"));

// Test that class expression names are not rewritten.
test("var A = class Name {};", "_.A = class Name {};");
test("var A = class A {};", "_.A = class A {};");
}

public void testClasses_nonGlobal() {
testSame("if (true) { class A {} }");
test("function foo() { class A {} }", "_.foo = function() { class A {} };");
test("const A = 5; { class A {} }", "_.A = 5; { class A {} }");
}

public void testClasses_allSameModule() {
assumeCrossModuleNames = false;
test("class A {}", "var A = class {};");
test("class A {} class B extends A {}", "var A = class {}; var B = class extends A {}");
testSame("if (true) { class A {} }");
}

public void testForLoops() {
assumeCrossModuleNames = false;
test(createModules(
Expand Down Expand Up @@ -399,6 +439,12 @@ public void testFunctionStatements() {
"if(1)_.test=function (){}");
}

public void testFunctionStatements_allSameModule() {
assumeCrossModuleNames = false;
test("function f() {}", "var f = function() {}");
test("if (true) { function f() {} }", "if (true) { var f = function() {}; }");
}

public void testFunctionStatements_freeCallSemantics1() throws Exception {
disableCompareAsTree();

Expand Down Expand Up @@ -554,6 +600,7 @@ public void testExterns() {
"var iframes",
"var foo = iframes;",
"_.foo = window.iframes;");
test("class A {}", "A", "window.A");
// Special names.
test(
"var arguments, window, eval;",
Expand Down

0 comments on commit 7118119

Please sign in to comment.