Update emit for tagged templates to use a per-site cached template objects.

This commit is contained in:
Daniel Rosenwasser
2017-09-06 20:18:13 -07:00
parent 1edecac0ab
commit c4b1a9d94f
3 changed files with 76 additions and 28 deletions

View File

@@ -16587,6 +16587,9 @@ namespace ts {
}
function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type {
if (languageVersion < ScriptTarget.ES2015) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.GetTemplateObject);
}
return getReturnTypeOfSignature(getResolvedSignature(node));
}
@@ -23919,6 +23922,7 @@ namespace ts {
case ExternalEmitHelpers.AsyncDelegator: return "__asyncDelegator";
case ExternalEmitHelpers.AsyncValues: return "__asyncValues";
case ExternalEmitHelpers.ExportStar: return "__exportStar";
case ExternalEmitHelpers.GetTemplateObject: return "__getTemplateObject";
default: Debug.fail("Unrecognized helper");
}
}

View File

@@ -279,6 +279,16 @@ namespace ts {
let currentSourceFile: SourceFile;
let currentText: string;
let hierarchyFacts: HierarchyFacts;
let taggedTemplateStringDeclarations: VariableDeclaration[];
function recordTaggedTemplateString(temp: Identifier) {
const decl = createVariableDeclaration(temp);
if (!taggedTemplateStringDeclarations) {
taggedTemplateStringDeclarations = [decl];
}
else {
taggedTemplateStringDeclarations.push(decl);
}
}
/**
* Used to track if we are emitting body of the converted loop
@@ -307,6 +317,7 @@ namespace ts {
currentSourceFile = undefined;
currentText = undefined;
taggedTemplateStringDeclarations = undefined;
hierarchyFacts = HierarchyFacts.None;
return visited;
}
@@ -520,6 +531,11 @@ namespace ts {
addCaptureThisForNodeIfNeeded(statements, node);
statementOffset = addCustomPrologue(statements, node.statements, statementOffset, visitor);
addRange(statements, visitNodes(node.statements, visitor, isStatement, statementOffset));
if (taggedTemplateStringDeclarations) {
statements.push(
createVariableStatement(/*modifiers*/ undefined,
createVariableDeclarationList(taggedTemplateStringDeclarations)));
}
addRange(statements, endLexicalEnvironment());
exitSubtree(ancestorFacts, HierarchyFacts.None, HierarchyFacts.None);
return updateSourceFileNode(
@@ -3637,10 +3653,12 @@ namespace ts {
const tag = visitNode(node.tag, visitor, isExpression);
// Allocate storage for the template site object
const temp = createTempVariable(hoistVariableDeclaration);
const temp = createTempVariable(recordTaggedTemplateString);
// Build up the template arguments and the raw and cooked strings for the template.
const templateArguments: Expression[] = [temp];
// We start out with 'undefined' for the first argument and revisit later
// to avoid walking over the template string twice and shifting all our arguments over after the fact.
const templateArguments: Expression[] = [undefined];
const cookedStrings: Expression[] = [];
const rawStrings: Expression[] = [];
const template = node.template;
@@ -3658,16 +3676,14 @@ namespace ts {
}
}
// NOTE: The parentheses here is entirely optional as we are now able to auto-
// parenthesize when rebuilding the tree. This should be removed in a
// future version. It is here for now to match our existing emit.
return createParen(
inlineExpressions([
createAssignment(temp, createArrayLiteral(cookedStrings)),
createAssignment(createPropertyAccess(temp, "raw"), createArrayLiteral(rawStrings)),
createCall(tag, /*typeArguments*/ undefined, templateArguments)
])
);
// Initialize the template object if necessary
templateArguments[0] = createLogicalOr(
temp,
createAssignment(
temp,
createTemplateObjectHelper(context, createArrayLiteral(cookedStrings), createArrayLiteral(rawStrings))));
return createCall(tag, /*typeArguments*/ undefined, templateArguments);
}
/**
@@ -4036,6 +4052,18 @@ namespace ts {
);
}
function createTemplateObjectHelper(context: TransformationContext, cooked: ArrayLiteralExpression, raw: ArrayLiteralExpression) {
context.requestEmitHelper(templateObjectHelper);
return createCall(
getHelperName("__getTemplateObject"),
/*typeArguments*/ undefined,
[
cooked,
raw
]
);
}
const extendsHelper: EmitHelper = {
name: "typescript:extends",
scoped: false,
@@ -4052,4 +4080,19 @@ namespace ts {
};
})();`
};
const templateObjectHelper: EmitHelper = {
name: "typescript:getTemplateObject",
scoped: false,
priority: 0,
text: `
var __getTemplateObject = (this && this.__getTemplateObject) || function (cooked, raw) {
if (Object.freeze && Object.defineProperty) {
return Object.freeze(Object.defineProperty(cooked, "raw", { value: Object.freeze(raw) }));
}
cooked.raw = raw;
return cooked;
};`
};
}

View File

@@ -4217,22 +4217,23 @@ namespace ts {
*/
/* @internal */
export const enum ExternalEmitHelpers {
Extends = 1 << 0, // __extends (used by the ES2015 class transformation)
Assign = 1 << 1, // __assign (used by Jsx and ESNext object spread transformations)
Rest = 1 << 2, // __rest (used by ESNext object rest transformation)
Decorate = 1 << 3, // __decorate (used by TypeScript decorators transformation)
Metadata = 1 << 4, // __metadata (used by TypeScript decorators transformation)
Param = 1 << 5, // __param (used by TypeScript decorators transformation)
Awaiter = 1 << 6, // __awaiter (used by ES2017 async functions transformation)
Generator = 1 << 7, // __generator (used by ES2015 generator transformation)
Values = 1 << 8, // __values (used by ES2015 for..of and yield* transformations)
Read = 1 << 9, // __read (used by ES2015 iterator destructuring transformation)
Spread = 1 << 10, // __spread (used by ES2015 array spread and argument list spread transformations)
Await = 1 << 11, // __await (used by ES2017 async generator transformation)
AsyncGenerator = 1 << 12, // __asyncGenerator (used by ES2017 async generator transformation)
AsyncDelegator = 1 << 13, // __asyncDelegator (used by ES2017 async generator yield* transformation)
AsyncValues = 1 << 14, // __asyncValues (used by ES2017 for..await..of transformation)
ExportStar = 1 << 15, // __exportStar (used by CommonJS/AMD/UMD module transformation)
Extends = 1 << 0, // __extends (used by the ES2015 class transformation)
Assign = 1 << 1, // __assign (used by Jsx and ESNext object spread transformations)
Rest = 1 << 2, // __rest (used by ESNext object rest transformation)
Decorate = 1 << 3, // __decorate (used by TypeScript decorators transformation)
Metadata = 1 << 4, // __metadata (used by TypeScript decorators transformation)
Param = 1 << 5, // __param (used by TypeScript decorators transformation)
Awaiter = 1 << 6, // __awaiter (used by ES2017 async functions transformation)
Generator = 1 << 7, // __generator (used by ES2015 generator transformation)
Values = 1 << 8, // __values (used by ES2015 for..of and yield* transformations)
Read = 1 << 9, // __read (used by ES2015 iterator destructuring transformation)
Spread = 1 << 10, // __spread (used by ES2015 array spread and argument list spread transformations)
Await = 1 << 11, // __await (used by ES2017 async generator transformation)
AsyncGenerator = 1 << 12, // __asyncGenerator (used by ES2017 async generator transformation)
AsyncDelegator = 1 << 13, // __asyncDelegator (used by ES2017 async generator yield* transformation)
AsyncValues = 1 << 14, // __asyncValues (used by ES2017 for..await..of transformation)
ExportStar = 1 << 15, // __exportStar (used by CommonJS/AMD/UMD module transformation)
GetTemplateObject = 1 << 16, // __getTemplateObject (used for constructing template string array objects)
// Helpers included by ES2015 for..of
ForOfIncludes = Values,