Skip to content

Commit c4b1a9d

Browse files
Update emit for tagged templates to use a per-site cached template objects.
1 parent 1edecac commit c4b1a9d

File tree

3 files changed

+76
-28
lines changed

3 files changed

+76
-28
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16587,6 +16587,9 @@ namespace ts {
1658716587
}
1658816588

1658916589
function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type {
16590+
if (languageVersion < ScriptTarget.ES2015) {
16591+
checkExternalEmitHelpers(node, ExternalEmitHelpers.GetTemplateObject);
16592+
}
1659016593
return getReturnTypeOfSignature(getResolvedSignature(node));
1659116594
}
1659216595

@@ -23919,6 +23922,7 @@ namespace ts {
2391923922
case ExternalEmitHelpers.AsyncDelegator: return "__asyncDelegator";
2392023923
case ExternalEmitHelpers.AsyncValues: return "__asyncValues";
2392123924
case ExternalEmitHelpers.ExportStar: return "__exportStar";
23925+
case ExternalEmitHelpers.GetTemplateObject: return "__getTemplateObject";
2392223926
default: Debug.fail("Unrecognized helper");
2392323927
}
2392423928
}

src/compiler/transformers/es2015.ts

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,16 @@ namespace ts {
279279
let currentSourceFile: SourceFile;
280280
let currentText: string;
281281
let hierarchyFacts: HierarchyFacts;
282+
let taggedTemplateStringDeclarations: VariableDeclaration[];
283+
function recordTaggedTemplateString(temp: Identifier) {
284+
const decl = createVariableDeclaration(temp);
285+
if (!taggedTemplateStringDeclarations) {
286+
taggedTemplateStringDeclarations = [decl];
287+
}
288+
else {
289+
taggedTemplateStringDeclarations.push(decl);
290+
}
291+
}
282292

283293
/**
284294
* Used to track if we are emitting body of the converted loop
@@ -307,6 +317,7 @@ namespace ts {
307317

308318
currentSourceFile = undefined;
309319
currentText = undefined;
320+
taggedTemplateStringDeclarations = undefined;
310321
hierarchyFacts = HierarchyFacts.None;
311322
return visited;
312323
}
@@ -520,6 +531,11 @@ namespace ts {
520531
addCaptureThisForNodeIfNeeded(statements, node);
521532
statementOffset = addCustomPrologue(statements, node.statements, statementOffset, visitor);
522533
addRange(statements, visitNodes(node.statements, visitor, isStatement, statementOffset));
534+
if (taggedTemplateStringDeclarations) {
535+
statements.push(
536+
createVariableStatement(/*modifiers*/ undefined,
537+
createVariableDeclarationList(taggedTemplateStringDeclarations)));
538+
}
523539
addRange(statements, endLexicalEnvironment());
524540
exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None);
525541
return updateSourceFileNode(
@@ -3637,10 +3653,12 @@ namespace ts {
36373653
const tag = visitNode(node.tag, visitor, isExpression);
36383654

36393655
// Allocate storage for the template site object
3640-
const temp = createTempVariable(hoistVariableDeclaration);
3656+
const temp = createTempVariable(recordTaggedTemplateString);
36413657

36423658
// Build up the template arguments and the raw and cooked strings for the template.
3643-
const templateArguments: Expression[] = [temp];
3659+
// We start out with 'undefined' for the first argument and revisit later
3660+
// to avoid walking over the template string twice and shifting all our arguments over after the fact.
3661+
const templateArguments: Expression[] = [undefined];
36443662
const cookedStrings: Expression[] = [];
36453663
const rawStrings: Expression[] = [];
36463664
const template = node.template;
@@ -3658,16 +3676,14 @@ namespace ts {
36583676
}
36593677
}
36603678

3661-
// NOTE: The parentheses here is entirely optional as we are now able to auto-
3662-
// parenthesize when rebuilding the tree. This should be removed in a
3663-
// future version. It is here for now to match our existing emit.
3664-
return createParen(
3665-
inlineExpressions([
3666-
createAssignment(temp, createArrayLiteral(cookedStrings)),
3667-
createAssignment(createPropertyAccess(temp, "raw"), createArrayLiteral(rawStrings)),
3668-
createCall(tag, /*typeArguments*/ undefined, templateArguments)
3669-
])
3670-
);
3679+
// Initialize the template object if necessary
3680+
templateArguments[0] = createLogicalOr(
3681+
temp,
3682+
createAssignment(
3683+
temp,
3684+
createTemplateObjectHelper(context, createArrayLiteral(cookedStrings), createArrayLiteral(rawStrings))));
3685+
3686+
return createCall(tag, /*typeArguments*/ undefined, templateArguments);
36713687
}
36723688

36733689
/**
@@ -4036,6 +4052,18 @@ namespace ts {
40364052
);
40374053
}
40384054

4055+
function createTemplateObjectHelper(context: TransformationContext, cooked: ArrayLiteralExpression, raw: ArrayLiteralExpression) {
4056+
context.requestEmitHelper(templateObjectHelper);
4057+
return createCall(
4058+
getHelperName("__getTemplateObject"),
4059+
/*typeArguments*/ undefined,
4060+
[
4061+
cooked,
4062+
raw
4063+
]
4064+
);
4065+
}
4066+
40394067
const extendsHelper: EmitHelper = {
40404068
name: "typescript:extends",
40414069
scoped: false,
@@ -4052,4 +4080,19 @@ namespace ts {
40524080
};
40534081
})();`
40544082
};
4083+
4084+
const templateObjectHelper: EmitHelper = {
4085+
name: "typescript:getTemplateObject",
4086+
scoped: false,
4087+
priority: 0,
4088+
text: `
4089+
var __getTemplateObject = (this && this.__getTemplateObject) || function (cooked, raw) {
4090+
if (Object.freeze && Object.defineProperty) {
4091+
return Object.freeze(Object.defineProperty(cooked, "raw", { value: Object.freeze(raw) }));
4092+
}
4093+
cooked.raw = raw;
4094+
return cooked;
4095+
};`
4096+
};
4097+
40554098
}

src/compiler/types.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4217,22 +4217,23 @@ namespace ts {
42174217
*/
42184218
/* @internal */
42194219
export const enum ExternalEmitHelpers {
4220-
Extends = 1 << 0, // __extends (used by the ES2015 class transformation)
4221-
Assign = 1 << 1, // __assign (used by Jsx and ESNext object spread transformations)
4222-
Rest = 1 << 2, // __rest (used by ESNext object rest transformation)
4223-
Decorate = 1 << 3, // __decorate (used by TypeScript decorators transformation)
4224-
Metadata = 1 << 4, // __metadata (used by TypeScript decorators transformation)
4225-
Param = 1 << 5, // __param (used by TypeScript decorators transformation)
4226-
Awaiter = 1 << 6, // __awaiter (used by ES2017 async functions transformation)
4227-
Generator = 1 << 7, // __generator (used by ES2015 generator transformation)
4228-
Values = 1 << 8, // __values (used by ES2015 for..of and yield* transformations)
4229-
Read = 1 << 9, // __read (used by ES2015 iterator destructuring transformation)
4230-
Spread = 1 << 10, // __spread (used by ES2015 array spread and argument list spread transformations)
4231-
Await = 1 << 11, // __await (used by ES2017 async generator transformation)
4232-
AsyncGenerator = 1 << 12, // __asyncGenerator (used by ES2017 async generator transformation)
4233-
AsyncDelegator = 1 << 13, // __asyncDelegator (used by ES2017 async generator yield* transformation)
4234-
AsyncValues = 1 << 14, // __asyncValues (used by ES2017 for..await..of transformation)
4235-
ExportStar = 1 << 15, // __exportStar (used by CommonJS/AMD/UMD module transformation)
4220+
Extends = 1 << 0, // __extends (used by the ES2015 class transformation)
4221+
Assign = 1 << 1, // __assign (used by Jsx and ESNext object spread transformations)
4222+
Rest = 1 << 2, // __rest (used by ESNext object rest transformation)
4223+
Decorate = 1 << 3, // __decorate (used by TypeScript decorators transformation)
4224+
Metadata = 1 << 4, // __metadata (used by TypeScript decorators transformation)
4225+
Param = 1 << 5, // __param (used by TypeScript decorators transformation)
4226+
Awaiter = 1 << 6, // __awaiter (used by ES2017 async functions transformation)
4227+
Generator = 1 << 7, // __generator (used by ES2015 generator transformation)
4228+
Values = 1 << 8, // __values (used by ES2015 for..of and yield* transformations)
4229+
Read = 1 << 9, // __read (used by ES2015 iterator destructuring transformation)
4230+
Spread = 1 << 10, // __spread (used by ES2015 array spread and argument list spread transformations)
4231+
Await = 1 << 11, // __await (used by ES2017 async generator transformation)
4232+
AsyncGenerator = 1 << 12, // __asyncGenerator (used by ES2017 async generator transformation)
4233+
AsyncDelegator = 1 << 13, // __asyncDelegator (used by ES2017 async generator yield* transformation)
4234+
AsyncValues = 1 << 14, // __asyncValues (used by ES2017 for..await..of transformation)
4235+
ExportStar = 1 << 15, // __exportStar (used by CommonJS/AMD/UMD module transformation)
4236+
GetTemplateObject = 1 << 16, // __getTemplateObject (used for constructing template string array objects)
42364237

42374238
// Helpers included by ES2015 for..of
42384239
ForOfIncludes = Values,

0 commit comments

Comments
 (0)