Skip to content

Commit

Permalink
Inject call to createTemplateTagFirstArg() at the nearest SCRIPT scop…
Browse files Browse the repository at this point in the history
…ed node

Currently we inject the calls to createTemplateTagFirstArg at beginning of the SCRIPT which contains a tagged template literal. This creates a runtime error for tagged template literals used in the first SCRIPT.

PiperOrigin-RevId: 309995860
  • Loading branch information
rishipal authored and rrdelaney committed May 5, 2020
1 parent a1c41ba commit cedbad5
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 2 deletions.
24 changes: 22 additions & 2 deletions src/com/google/javascript/jscomp/Es6TemplateLiterals.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.google.javascript.jscomp;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.javascript.jscomp.Es6ToEs3Util.createGenericType;
import static com.google.javascript.jscomp.Es6ToEs3Util.createType;
Expand Down Expand Up @@ -158,8 +159,12 @@ void visitTaggedTemplateLiteral(NodeTraversal t, Node n, boolean addTypes) {
.createSingleVarNameDeclaration(TEMPLATELIT_VAR + uniqueId, callTemplateTagArgCreator)
.setJSDocInfo(info)
.useSourceInfoIfMissingFromForTree(n);
Node script = NodeUtil.getEnclosingScript(n);
script.addChildToFront(tagFnFirstArgDeclaration);

// Get the nearest SCRIPT-scoped node as insertion point for this assignment as injecting to the
// top of the script causes runtime errors
// https://github.com/google/closure-compiler/issues/3589
Node insertionPoint = findInsertionPoint(n);
insertionPoint.getParent().addChildBefore(tagFnFirstArgDeclaration, insertionPoint);
t.reportCodeChange(tagFnFirstArgDeclaration);

// Generate the call expression.
Expand All @@ -186,6 +191,21 @@ private Node createRawStringArray(Node n, JSType arrayType) {
return array;
}

/**
* Finds the closest enclosing node whose parent is a SCRIPT. We'll want to insert the call to
* `var tagFnFirstArg = $jscomp.createTemplateTagFirstArg...` just before that one.
*/
private static Node findInsertionPoint(Node n) {
Node insertionPoint = n;
Node insertionParent = checkNotNull(insertionPoint.getParent(), n);
while (!insertionParent.isScript()) {
insertionPoint = insertionParent;
insertionParent = checkNotNull(insertionPoint.getParent(), insertionPoint);
}
checkState(insertionParent.isScript(), insertionParent);
return insertionPoint;
}

private Node createCookedStringArray(Node n, JSType templateArrayType) {
Node array = withType(IR.arraylit(), templateArrayType);
for (Node child = n.getFirstChild(); child != null; child = child.getNext()) {
Expand Down
30 changes: 30 additions & 0 deletions test/com/google/javascript/jscomp/LateEs6ToEs3ConverterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,36 @@ public void testTaggedTemplateLiteral() {
"tag(TAGGED_TEMPLATE_TMP_VAR$0);"));
}

@Test
public void testTaggedTemplateLiteral_InsertPosition() {
// inserted at the directly enclosing script scope
taggedTemplateLiteral_TestRunner(
lines("var a = {};", "tag``;"),
lines(
"var a = {};",
"/** @noinline */ var TAGGED_TEMPLATE_TMP_VAR$0 =",
" $jscomp.createTemplateTagFirstArg(['']);",
"tag(TAGGED_TEMPLATE_TMP_VAR$0);"));

// inserted at the script scope above the immediate enclosing function at the script scope
taggedTemplateLiteral_TestRunner(
lines("var a = {};", "function foo() {tag``;}"),
lines(
"var a = {};",
"/** @noinline */ var TAGGED_TEMPLATE_TMP_VAR$0 =",
" $jscomp.createTemplateTagFirstArg(['']);",
"function foo() {tag(TAGGED_TEMPLATE_TMP_VAR$0);}"));

// inserted at the script scope above the outer enclosing function at the script scope
taggedTemplateLiteral_TestRunner(
lines("var a = {};", "function foo() {function bar() {tag``;}}"),
lines(
"var a = {};",
"/** @noinline */ var TAGGED_TEMPLATE_TMP_VAR$0 =",
" $jscomp.createTemplateTagFirstArg(['']);",
"function foo() {function bar() {tag(TAGGED_TEMPLATE_TMP_VAR$0);}}"));
}

@Test
public void testUnicodeEscapes() {
test("var \\u{73} = \'\\u{2603}\'", "var s = \'\u2603\'"); // ☃
Expand Down

0 comments on commit cedbad5

Please sign in to comment.