Skip to content

Commit

Permalink
Declare global define aliases that J2clUtilGetDefineRewriterPass can …
Browse files Browse the repository at this point in the history
…read from

This will allow goog.defines declared in goog.modules to be consumable by J2CL's
System.getProperty infrastructure. Since these reads are out-of-scope of the
declaration, we need to provide some hook to link the two. To do this we define
a global variable to serve as a well-known alias to the define.

For the J2CL pass we can assume what the global variable is based on the define
name, so we just simply replace the getProperty references with the appropriate
global, if it exists.

PiperOrigin-RevId: 719370016
  • Loading branch information
kevinoconnor7 authored and copybara-github committed Jan 24, 2025
1 parent 480bf0e commit 287fd41
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ private void rewriteUtilGetDefine(NodeTraversal t, Node callNode) {
}

private Node getDefineReplacement(Node firstExpr, Node secondExpr) {
if (defines.contains(firstExpr.getString())) {
Node define = NodeUtil.newQName(compiler, firstExpr.getString());
String defineName = firstExpr.getString();
if (defines.contains(defineName)) {
Node define = NodeUtil.newQName(compiler, ProcessDefines.getGlobalDefineAlias(defineName));
Node defineStringValue = NodeUtil.newCallNode(IR.name("String"), define);
return IR.comma(secondExpr, defineStringValue);
} else {
Expand Down
42 changes: 41 additions & 1 deletion src/com/google/javascript/jscomp/ProcessDefines.java
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,31 @@ private void overrideDefines() {

compiler.reportChangeToEnclosingScope(define.valueParent);
}

// When requested, create a globally accessible alias for all define values. This will
// provide a hook for passes like J2clUtilGetDefineRewriterPass to read them in out-of-scope
// contexts.
if (J2clSourceFileChecker.shouldRunJ2clPasses(compiler)) {
String alias = getGlobalDefineAlias(define.defineName);
if (!alias.equals(define.defineName)) {
// If we had:
// var x = goog.define('y', ...);
// we'll add an additional statement:
// var goog$defines$y = x;
Node globalDefineName =
NodeUtil.newName(
compiler, getGlobalDefineAlias(define.defineName), define.valueParent);
Node defineLhs =
define.valueParent.isAssign()
? define.valueParent.getFirstChild().cloneTree()
: define.valueParent.cloneNode();
checkState(defineLhs.isName() || defineLhs.isQualifiedName(), defineLhs);
Node globalDefineVar =
IR.var(globalDefineName, defineLhs).srcrefTreeIfMissing(define.valueParent);
globalDefineVar.insertAfter(define.valueParent.getParent());
compiler.reportChangeToEnclosingScope(define.valueParent.getParent());
}
}
}
}

Expand All @@ -261,6 +286,17 @@ private void overrideDefines() {
}
}

static String getGlobalDefineAlias(String defineName) {
// Known defines are already globally accessible and may not be present in code. Therefore we'll
// reference them directly.
// goog.LOCALE is a special case as it's not processed as a define, it is instead
// late-substituted.
if (KNOWN_DEFINES.contains(defineName) || defineName.equals("goog.LOCALE")) {
return defineName;
}
return "jscomp$defines$" + defineName.replace('.', '$');
}

/**
* Returns the replacement value for a @define, if any.
*
Expand Down Expand Up @@ -347,7 +383,11 @@ private void collectDefines(Node root) {
}
}
}
compiler.setDefineNames(defineByDefineName.keySet());
compiler.setDefineNames(
ImmutableSet.<String>builder()
.addAll(KNOWN_DEFINES)
.addAll(defineByDefineName.keySet())
.build());
}

private @Nullable Ref selectDefineDeclaration(Name name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ public void testUtilGetDefine() {
"var a = {};",
"a.b = {}",
"/** @define {boolean} */ a.b.c = true;",
"('def', String(a.b.c));"));
"var jscomp$defines$a$b$c = a.b.c;",
"('def', String(jscomp$defines$a$b$c));"));
test(
lines(
"var a = {};",
Expand All @@ -64,7 +65,22 @@ public void testUtilGetDefine() {
"var a = {};",
"a.b = {}",
"/** @define {boolean} */ a.b.c = true;",
"(null, String(a.b.c));"));
"var jscomp$defines$a$b$c = a.b.c;",
"(null, String(jscomp$defines$a$b$c));"));
test(
lines(
"/** @define {boolean} */ var x = goog.define('x', 1);",
"/** @define {boolean} */ var y = goog.define('y', x);",
"nativebootstrap.Util.$getDefine('x');",
"nativebootstrap.Util.$getDefine('y');"),
lines(
"/** @define {boolean} */ var x = 1;",
"var jscomp$defines$x = x;",
"/** @define {boolean} */ var y = x;",
"var jscomp$defines$y = y;",
"(null, String(jscomp$defines$x));",
"(null, String(jscomp$defines$y));"));
test(lines("nativebootstrap.Util.$getDefine('COMPILED');"), "(null, String(COMPILED));");
}

@Test
Expand Down
29 changes: 29 additions & 0 deletions test/com/google/javascript/jscomp/ProcessDefinesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public ProcessDefinesTest() {
private ProcessDefines.Mode mode;
private boolean recognizeClosureDefines = true;

private boolean enableJ2clPasses = false;

@Override
@Before
public void setUp() throws Exception {
Expand All @@ -55,6 +57,9 @@ public void setUp() throws Exception {

@Override
protected CompilerPass getProcessor(Compiler compiler) {
if (enableJ2clPasses) {
J2clSourceFileChecker.markToRunJ2clPasses(compiler);
}
return new ProcessDefinesWithInjectedNamespace(compiler);
}

Expand Down Expand Up @@ -940,6 +945,30 @@ public void testClosureDefinesErrors_enabledByRecognizeClosureDefines() {
testSame("var CLOSURE_DEFINES = {'FOO': a};");
}

@Test
public void testGenerateGlobalAliases() {
enableJ2clPasses = true;

test(
lines("var a = {};", "/** @define {number} */ a.b = goog.define('a.b', 1);"),
lines("var a = {};", "/** @define {number} */ a.b = 1;", "var jscomp$defines$a$b = a.b;"));
test(
lines("var a = {};", "/** @define {number} */ a.b = goog.define('c.d', 1);"),
lines("var a = {};", "/** @define {number} */ a.b = 1;", "var jscomp$defines$c$d = a.b;"));
test(
"/** @define {number} */ var a = goog.define('c.d', 1);",
lines("/** @define {number} */ var a = 1;", "var jscomp$defines$c$d = a;"));
test(
lines(
"/** @define {number} */ var a = goog.define('a', 1);",
"/** @define {number} */ var b = goog.define('b', a);"),
lines(
"/** @define {number} */ var a = 1;",
"var jscomp$defines$a = a;",
"/** @define {number} */ var b = a;",
"var jscomp$defines$b = b;"));
}

private class ProcessDefinesWithInjectedNamespace implements CompilerPass {
private final Compiler compiler;

Expand Down

0 comments on commit 287fd41

Please sign in to comment.