Skip to content

Commit 7a8150f

Browse files
Closure Teamcopybara-github
authored andcommitted
Internal change
PiperOrigin-RevId: 485609851
1 parent 46a9016 commit 7a8150f

File tree

9 files changed

+196
-4
lines changed

9 files changed

+196
-4
lines changed

src/com/google/javascript/jscomp/CheckClosureImports.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,28 @@ boolean mustBeOrdered() {
114114
String callName() {
115115
return "goog.requireType";
116116
}
117+
},
118+
119+
REQUIRE_DYNAMIC {
120+
@Override
121+
boolean allowDestructuring() {
122+
return true;
123+
}
124+
125+
@Override
126+
boolean aliasMustBeConstant() {
127+
return true;
128+
}
129+
130+
@Override
131+
boolean mustBeOrdered() {
132+
return false;
133+
}
134+
135+
@Override
136+
String callName() {
137+
return "goog.requireDynamic";
138+
}
117139
};
118140

119141
/**
@@ -188,6 +210,7 @@ String callName() {
188210
private static final Node GOOG_MODULE_GET = IR.getprop(IR.name("goog"), "module", "get");
189211
private static final Node GOOG_FORWARD_DECLARE = IR.getprop(IR.name("goog"), "forwardDeclare");
190212
private static final Node GOOG_REQUIRE_TYPE = IR.getprop(IR.name("goog"), "requireType");
213+
private static final Node GOOG_REQUIRE_DYNAMIC = IR.getprop(IR.name("goog"), "requireDynamic");
191214

192215
private final AbstractCompiler compiler;
193216
private final Checker checker;
@@ -257,6 +280,8 @@ public void visit(
257280
return ClosureImport.FORWARD_DECLARE;
258281
} else if (callNode.getFirstChild().matchesQualifiedName(GOOG_REQUIRE_TYPE)) {
259282
return ClosureImport.REQUIRE_TYPE;
283+
} else if (callNode.getFirstChild().matchesQualifiedName(GOOG_REQUIRE_DYNAMIC)) {
284+
return ClosureImport.REQUIRE_DYNAMIC;
260285
}
261286
return null;
262287
}
@@ -420,7 +445,8 @@ private void checkImport(
420445
boolean validAssignment = isModule || parent.isExprResult();
421446
boolean isAliased = NodeUtil.isNameDeclaration(parent.getParent());
422447

423-
if (!atTopLevelScope || !validAssignment) {
448+
// goog.requireDynamic() can be in non-top scope.
449+
if ((!atTopLevelScope || !validAssignment) && importType != ClosureImport.REQUIRE_DYNAMIC) {
424450
t.report(call, INVALID_CLOSURE_CALL_SCOPE_ERROR);
425451
return;
426452
}

src/com/google/javascript/jscomp/ClosureCheckModule.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ public final class ClosureCheckModule extends AbstractModuleCallback implements
8787
"JSC_INVALID_DESTRUCTURING_REQUIRE",
8888
"Destructuring goog.require must be a simple object pattern.");
8989

90+
static final DiagnosticType AWAIT_GOOG_REQUIRE_CALLS =
91+
DiagnosticType.error(
92+
"JSC_AWAIT_GOOG_REQUIRE_CALLS",
93+
"goog.require(Type) and goog.forwardDeclare can not be in an 'await' expression.");
94+
95+
static final DiagnosticType GOOG_REQUIRE_DYNAMIC_MISSING_AWAIT =
96+
DiagnosticType.error(
97+
"JSC_GOOG_REQUIRE_DYNAMIC_MISSING_AWAIT",
98+
"goog.requireDynamic must be proceeded by 'await' in an assignment expression.");
99+
90100
static final DiagnosticType LET_GOOG_REQUIRE =
91101
DiagnosticType.disabled(
92102
"JSC_LET_GOOG_REQUIRE",
@@ -167,6 +177,7 @@ public final class ClosureCheckModule extends AbstractModuleCallback implements
167177
private static final QualifiedName GOOG_PROVIDE = QualifiedName.of("goog.provide");
168178
private static final QualifiedName GOOG_REQUIRE = QualifiedName.of("goog.require");
169179
private static final QualifiedName GOOG_REQUIRE_TYPE = QualifiedName.of("goog.requireType");
180+
private static final QualifiedName GOOG_REQUIRE_DYNAMIC = QualifiedName.of("goog.requireDynamic");
170181
private static final QualifiedName GOOG_MODULE_GET = QualifiedName.of("goog.module.get");
171182
private static final QualifiedName GOOG_FORWARD_DECLARE = QualifiedName.of("goog.forwardDeclare");
172183
private static final QualifiedName GOOG_MODULE_DECLARE_LEGACY_NAMESPACE =
@@ -251,6 +262,7 @@ protected void visit(
251262
}
252263
} else if (GOOG_REQUIRE.matches(callee)
253264
|| GOOG_REQUIRE_TYPE.matches(callee)
265+
|| GOOG_REQUIRE_DYNAMIC.matches(callee)
254266
|| GOOG_FORWARD_DECLARE.matches(callee)) {
255267
checkRequireCall(t, n, parent);
256268
} else if (GOOG_MODULE_GET.matches(callee) && t.inModuleHoistScope()) {
@@ -456,8 +468,23 @@ private void checkRequireCall(NodeTraversal t, Node callNode, Node parent) {
456468
return;
457469
case NAME:
458470
case DESTRUCTURING_LHS:
471+
if (GOOG_REQUIRE_DYNAMIC.matches(callNode.getFirstChild())) {
472+
t.report(parent, GOOG_REQUIRE_DYNAMIC_MISSING_AWAIT);
473+
return;
474+
}
459475
checkShortGoogRequireCall(t, callNode, parent.getParent());
460476
return;
477+
case AWAIT:
478+
if (!GOOG_REQUIRE_DYNAMIC.matches(callNode.getFirstChild())) {
479+
t.report(parent, AWAIT_GOOG_REQUIRE_CALLS);
480+
return;
481+
}
482+
Token grandParentToken = parent.getParent().getToken();
483+
if (grandParentToken.equals(Token.DESTRUCTURING_LHS)
484+
|| grandParentToken.equals(Token.NAME)) {
485+
checkShortGoogRequireCall(t, callNode, parent.getGrandparent());
486+
}
487+
return;
461488
default:
462489
break;
463490
}

src/com/google/javascript/jscomp/ClosurePrimitiveErrors.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ private ClosurePrimitiveErrors() {}
5656
"JSC_GOOG_MODULE_INVALID_GET_NAMESPACE",
5757
"goog.module.get parameter must be a string literal.");
5858

59+
static final DiagnosticType INVALID_REQUIRE_DYNAMIC =
60+
DiagnosticType.error(
61+
"JSC_GOOG_MODULE_INVALID_REQUIRE_DYNAMIC_NAMESPACE",
62+
"goog.requireDynamic parameter must be a string literal.");
63+
5964
static final DiagnosticType INVALID_REQUIRE_NAMESPACE =
6065
DiagnosticType.error(
6166
"JSC_GOOG_MODULE_INVALID_REQUIRE_NAMESPACE",

src/com/google/javascript/jscomp/ClosureRewriteModule.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import static com.google.javascript.jscomp.AstFactory.type;
2323
import static com.google.javascript.jscomp.ClosurePrimitiveErrors.INVALID_FORWARD_DECLARE_NAMESPACE;
2424
import static com.google.javascript.jscomp.ClosurePrimitiveErrors.INVALID_GET_NAMESPACE;
25+
import static com.google.javascript.jscomp.ClosurePrimitiveErrors.INVALID_REQUIRE_DYNAMIC;
2526
import static com.google.javascript.jscomp.ClosurePrimitiveErrors.INVALID_REQUIRE_NAMESPACE;
2627
import static com.google.javascript.jscomp.ClosurePrimitiveErrors.INVALID_REQUIRE_TYPE_NAMESPACE;
2728

@@ -153,6 +154,7 @@ final class ClosureRewriteModule implements CompilerPass {
153154
private static final Node GOOG_PROVIDE = IR.getprop(IR.name("goog"), "provide");
154155
private static final Node GOOG_REQUIRE = IR.getprop(IR.name("goog"), "require");
155156
private static final Node GOOG_REQUIRETYPE = IR.getprop(IR.name("goog"), "requireType");
157+
private static final Node GOOG_REQUIREDYNAMIC = IR.getprop(IR.name("goog"), "requireDynamic");
156158

157159
private final AbstractCompiler compiler;
158160
private final AstFactory astFactory;
@@ -370,6 +372,8 @@ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
370372
recordGoogRequire(t, n, /* mustBeOrdered= */ true);
371373
} else if (method.matchesQualifiedName(GOOG_REQUIRETYPE)) {
372374
recordGoogRequireType(t, n);
375+
} else if (method.matchesQualifiedName(GOOG_REQUIREDYNAMIC)) {
376+
recordGoogRequireDynamic(t, n);
373377
} else if (method.matchesQualifiedName(GOOG_FORWARDDECLARE) && !parent.isExprResult()) {
374378
recordGoogForwardDeclare(t, n);
375379
} else if (method.matchesQualifiedName(GOOG_MODULE_GET)) {
@@ -687,6 +691,7 @@ JSType getGoogModuleNamespaceType(String namespaceId) {
687691
private final Set<String> legacyScriptNamespacesAndPrefixes = new HashSet<>();
688692
private final List<UnrecognizedRequire> unrecognizedRequires = new ArrayList<>();
689693
private final ArrayList<Node> googModuleGetCalls = new ArrayList<>();
694+
private final ArrayList<Node> googRequireDynamicCalls = new ArrayList<>();
690695

691696
private final TypedScope globalTypedScope;
692697

@@ -768,6 +773,7 @@ public void process(Node externs, Node root) {
768773
NodeTraversal.traverseRoots(compiler, new ScriptUpdater(scriptDescriptions), externs, root);
769774
declareSyntheticExterns();
770775
this.googModuleGetCalls.forEach(this::updateGoogModuleGetCall);
776+
this.googRequireDynamicCalls.forEach(this::updateGoogRequireDynamicCall);
771777
}
772778

773779
/**
@@ -941,6 +947,22 @@ private void recordGoogForwardDeclare(NodeTraversal t, Node call) {
941947
recordGoogRequire(t, call, mustBeOrdered);
942948
}
943949

950+
private void recordGoogRequireDynamic(NodeTraversal t, Node call) {
951+
Node namespaceIdNode = call.getLastChild();
952+
if (!namespaceIdNode.isStringLit()) {
953+
t.report(namespaceIdNode, INVALID_REQUIRE_DYNAMIC);
954+
return;
955+
}
956+
957+
String namespaceId = namespaceIdNode.getString();
958+
959+
if (!rewriteState.containsModule(namespaceId)) {
960+
unrecognizedRequires.add(
961+
new UnrecognizedRequire(call, namespaceId, /* mustBeOrdered= */ false));
962+
}
963+
this.googRequireDynamicCalls.add(call);
964+
}
965+
944966
private void recordGoogModuleGet(NodeTraversal t, Node call) {
945967
Node namespaceIdNode = call.getLastChild();
946968
if (!call.hasTwoChildren() || !namespaceIdNode.isStringLit()) {
@@ -1194,6 +1216,42 @@ private void updateGoogForwardDeclare(NodeTraversal t, Node call) {
11941216
updateGoogRequire(t, call);
11951217
}
11961218

1219+
// Rewrite
1220+
// "await goog.requireDynamic('a.b.c')" to
1221+
// "((await goog.importHandler_('tJJovc')),module$exports$a$b$c)"
1222+
private void updateGoogRequireDynamicCall(Node call) {
1223+
Node namespaceIdNode = call.getSecondChild();
1224+
String namespaceId = namespaceIdNode.getString();
1225+
String exportedNamespace = rewriteState.getExportedNamespaceOrScript(namespaceId);
1226+
if (exportedNamespace == null) {
1227+
return;
1228+
}
1229+
compiler.reportChangeToEnclosingScope(call);
1230+
Node exportedNamespaceNameNode =
1231+
this.astFactory.createQName(this.globalTypedScope, exportedNamespace).srcrefTree(call);
1232+
exportedNamespaceNameNode.setJSType(rewriteState.getGoogModuleNamespaceType(namespaceId));
1233+
exportedNamespaceNameNode.setOriginalName(namespaceId);
1234+
Xid.HashFunction hashFunction = this.compiler.getOptions().xidHashFunction;
1235+
Xid xid = hashFunction == null ? new Xid() : new Xid(hashFunction);
1236+
Node argNode = this.astFactory.createString(xid.get(namespaceId));
1237+
1238+
Node calleeNode = this.astFactory.createQNameWithUnknownType("goog.importHandler_");
1239+
Node importHandlerNode = this.astFactory.createCallWithUnknownType(calleeNode, argNode);
1240+
1241+
if (call.getParent().isAwait()) {
1242+
Node awaitNode =
1243+
this.astFactory
1244+
.createAwait(type(exportedNamespaceNameNode), importHandlerNode)
1245+
.srcrefTreeIfMissing(call);
1246+
Node commaExpression =
1247+
this.astFactory
1248+
.createComma(awaitNode, exportedNamespaceNameNode)
1249+
.srcrefTreeIfMissing(call);
1250+
call.getParent().replaceWith(commaExpression);
1251+
compiler.reportChangeToEnclosingScope(commaExpression);
1252+
}
1253+
}
1254+
11971255
private void updateGoogModuleGetCall(Node call) {
11981256
Node namespaceIdNode = call.getSecondChild();
11991257
String namespaceId = namespaceIdNode.getString();

src/com/google/javascript/jscomp/ModuleImportResolver.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ final class ModuleImportResolver {
4949

5050
private static final String GOOG = "goog";
5151
private static final ImmutableSet<String> GOOG_DEPENDENCY_CALLS =
52-
ImmutableSet.of("require", "requireType", "forwardDeclare");
52+
ImmutableSet.of("require", "requireType", "forwardDeclare", "requireDynamic");
5353
private static final QualifiedName GOOG_MODULE_GET = QualifiedName.of("goog.module.get");
5454

5555
ModuleImportResolver(
@@ -60,8 +60,8 @@ final class ModuleImportResolver {
6060
}
6161

6262
/**
63-
* Returns whether this is a CALL node for goog.require(Type), goog.forwardDeclare, or
64-
* goog.module.get.
63+
* Returns whether this is a CALL node for goog.require, goog.requireType, goog.requireDynamic,
64+
* goog.forwardDeclare, or goog.module.get.
6565
*
6666
* <p>This method does not verify that the call is actually in a valid location. For example, this
6767
* method does not verify that goog.require calls are at the top-level. That is left to the

src/com/google/javascript/jscomp/testing/TestExternsBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ private static final String lines(String... lines) {
5050
"goog.require = function(ns) {};",
5151
"/** @return {?} */",
5252
"goog.requireType = function(ns) {};",
53+
"goog.requireDynamic = function(ns) {};",
5354
"goog.loadModule = function(ns) {};",
5455
"/** @return {?} */",
5556
"goog.forwardDeclare = function(ns) {};",

test/com/google/javascript/jscomp/CheckClosureImportsTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ public void referenceMissingSymbolIsError() {
163163
// Not an error for goog.forwardDeclare in scripts.
164164
testError("goog.require('dne');", MISSING_MODULE_OR_PROVIDE);
165165
testError("goog.requireType('dne');", MISSING_MODULE_OR_PROVIDE);
166+
testError("goog.requireDynamic('dne');", MISSING_MODULE_OR_PROVIDE);
166167
testError("() => goog.module.get('dne');", MISSING_MODULE_OR_PROVIDE);
167168
}
168169

test/com/google/javascript/jscomp/ClosureCheckModuleTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package com.google.javascript.jscomp;
1717

18+
import static com.google.javascript.jscomp.ClosureCheckModule.AWAIT_GOOG_REQUIRE_CALLS;
1819
import static com.google.javascript.jscomp.ClosureCheckModule.DECLARE_LEGACY_NAMESPACE_IN_NON_MODULE;
1920
import static com.google.javascript.jscomp.ClosureCheckModule.DUPLICATE_NAME_SHORT_REQUIRE;
2021
import static com.google.javascript.jscomp.ClosureCheckModule.EXPORT_NOT_AT_MODULE_SCOPE;
@@ -24,6 +25,7 @@
2425
import static com.google.javascript.jscomp.ClosureCheckModule.GOOG_MODULE_MISPLACED;
2526
import static com.google.javascript.jscomp.ClosureCheckModule.GOOG_MODULE_REFERENCES_THIS;
2627
import static com.google.javascript.jscomp.ClosureCheckModule.GOOG_MODULE_USES_THROW;
28+
import static com.google.javascript.jscomp.ClosureCheckModule.GOOG_REQUIRE_DYNAMIC_MISSING_AWAIT;
2729
import static com.google.javascript.jscomp.ClosureCheckModule.INCORRECT_SHORTNAME_CAPITALIZATION;
2830
import static com.google.javascript.jscomp.ClosureCheckModule.INVALID_DESTRUCTURING_REQUIRE;
2931
import static com.google.javascript.jscomp.ClosureCheckModule.LEGACY_NAMESPACE_ARGUMENT;
@@ -598,6 +600,45 @@ public void testIllegalShortImportReferencedByLongNameInJsDoc() {
598600
"/** @type {foo.A} */ var a;"),
599601
REFERENCE_TO_SHORT_IMPORT_BY_LONG_NAME_INCLUDING_SHORT_NAME);
600602

603+
testError(
604+
lines(
605+
"goog.module('x.y.z');",
606+
"",
607+
"async function test() {",
608+
"",
609+
" var A = await goog.requireDynamic('foo.A');",
610+
"",
611+
" /** @type {foo.A} */ var a; ",
612+
"",
613+
"}"),
614+
REFERENCE_TO_SHORT_IMPORT_BY_LONG_NAME_INCLUDING_SHORT_NAME);
615+
616+
testError(
617+
lines(
618+
"goog.module('x.y.z');",
619+
"",
620+
"async function test() {",
621+
"",
622+
" var A = await goog.require('foo.A');",
623+
"",
624+
" /** @type {foo.A} */ var a; ",
625+
"",
626+
"}"),
627+
AWAIT_GOOG_REQUIRE_CALLS);
628+
629+
testError(
630+
lines(
631+
"goog.module('x.y.z');",
632+
"",
633+
"async function test() {",
634+
"",
635+
" var A = goog.requireDynamic('foo.A');",
636+
"",
637+
" /** @type {foo.A} */ var a; ",
638+
"",
639+
"}"),
640+
GOOG_REQUIRE_DYNAMIC_MISSING_AWAIT);
641+
601642
testSame(
602643
lines(
603644
"goog.module('x.y.z');",

test/com/google/javascript/jscomp/ClosureRewriteModuleTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,6 +1159,39 @@ public void testThis() {
11591159
lines("/** @const */ var module$exports$a = {};", "this;"));
11601160
}
11611161

1162+
@Test
1163+
public void testGoogImport_await() {
1164+
test(
1165+
srcs(
1166+
"goog.module('a.b.c');",
1167+
lines("async function test() {", " var d = await goog.requireDynamic('a.b.c');", "}")),
1168+
expected(
1169+
"/** @const */ var module$exports$a$b$c = {}",
1170+
lines(
1171+
"async function test() {",
1172+
" var d = ((await goog.importHandler_('sG5M4c')),module$exports$a$b$c);",
1173+
"}")));
1174+
}
1175+
1176+
@Test
1177+
public void testGoogImport_await_destructuring() {
1178+
test(
1179+
srcs(
1180+
lines("goog.module('a.b.c');", "exports.Foo=class{}"),
1181+
lines(
1182+
"async function test() {",
1183+
" var {Foo} = await goog.requireDynamic('a.b.c');",
1184+
"}")),
1185+
expected(
1186+
lines(
1187+
"/** @const */ var module$exports$a$b$c = {};",
1188+
"/** @const */ module$exports$a$b$c.Foo = class {}"),
1189+
lines(
1190+
"async function test() {",
1191+
" var {Foo} = ((await goog.importHandler_('sG5M4c')),module$exports$a$b$c);",
1192+
"}")));
1193+
}
1194+
11621195
@Test
11631196
public void testGoogModuleGet1() {
11641197
test(

0 commit comments

Comments
 (0)