From 349841e2e34ab2bc640ad2b6e8d0e9dcf22fec9c Mon Sep 17 00:00:00 2001 From: Ivo Gabe de Wolff Date: Fri, 9 Jan 2015 21:22:42 +0100 Subject: [PATCH] Emit var in front of statement with tagged template --- src/compiler/binder.ts | 21 ++++++++ src/compiler/emitter.ts | 114 ++++++++++++++++++++++++++++++---------- src/compiler/types.ts | 2 + 3 files changed, 109 insertions(+), 28 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 201913a50b4..87501045e20 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -65,6 +65,7 @@ module ts { var container: Node; var blockScopeContainer: Node; var lastContainer: Node; + var statement: Statement; var symbolCount = 0; var Symbol = objectAllocator.getSymbolConstructor(); @@ -371,9 +372,25 @@ module ts { function getDestructuringParameterName(node: Declaration) { return "__" + indexOf((node.parent).parameters, node); } + + function bindTaggedTemplateExpression(node: TaggedTemplateExpression) { + if (file.languageVersion >= ScriptTarget.ES6) return; + + statement.downlevelTaggedTemplates.push(node); + } function bind(node: Node) { node.parent = parent; + + var savedStatement = statement; + + if (isStatement(node)) { + statement = node; + if (file.languageVersion < ScriptTarget.ES6) { + statement.downlevelTaggedTemplates = []; + } + } + switch (node.kind) { case SyntaxKind.TypeParameter: bindDeclaration(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes, /*isBlockScopeContainer*/ false); @@ -484,12 +501,16 @@ module ts { case SyntaxKind.SwitchStatement: bindChildren(node, 0, /*isBlockScopeContainer*/ true); break; + case SyntaxKind.TaggedTemplateExpression: + bindTaggedTemplateExpression( node); default: var saveParent = parent; parent = node; forEachChild(node, bind); parent = saveParent; } + + statement = savedStatement; } function bindParameter(node: ParameterDeclaration) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 3300e542cc5..ddc8a110acd 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2030,19 +2030,8 @@ module ts { } return false; } - - function emitDownlevelTaggedTemplateLiteral(node: LiteralExpression) { - // Emit tagged template as foo(["string"]) - write("["); - writer.writeLiteral(getTemplateLiteralAsStringLiteral(node)); - write("]"); - } function emitLiteral(node: LiteralExpression) { - if (node.parent.kind === SyntaxKind.TaggedTemplateExpression && compilerOptions.target < ScriptTarget.ES6) { - return emitDownlevelTaggedTemplateLiteral(node); - } - var text = compilerOptions.target < ScriptTarget.ES6 && isTemplateLiteralKind(node.kind) ? getTemplateLiteralAsStringLiteral(node) : node.parent ? getSourceTextOfNodeFromSourceFile(currentSourceFile, node) : node.text; @@ -2062,25 +2051,89 @@ module ts { return '"' + escapeString(node.text) + '"'; } - function emitDownlevelTaggedTemplate(node: TemplateExpression): void { - // Emit should like: - // foo(["a", "b", "c"], expressions0, expression1) - // First we emit the string literal array - write("["); - emitLiteral(node.head); - forEach(node.templateSpans, templateSpan => { - write(", "); - emitLiteral(templateSpan.literal); - }); + function emitDownlevelRawTemplateLiteral(node: LiteralExpression, isLast: boolean) { + var text = getSourceTextOfNodeFromSourceFile(currentSourceFile, node); + + // text contains the original source, it will also contain quotes ("`"), dolar signs and braces ("${" en "}"), + // thus we need to remove those characters. + // First template piece starts with "`", others with "}" + // Last template piece ends with "`", others with "${" + text = text.substring(1, text.length - (isLast ? 1 : 2)); + + write('"' + escapeString(text) + '"'); + } + + function emitDownlevelTaggedTemplateVariable(node: TaggedTemplateExpression) { + node.tempVariable = createTempVariable(node); + + write("var "); + emit(node.tempVariable); + write(";"); + writeLine(); + } + function emitDownlevelTaggedTemplateStrings(node: TaggedTemplateExpression, inLoop: boolean) { + if (!inLoop) { + node.tempVariable = createTempVariable(node); + + write("var "); + } else { + // node.tempVariable is initialized in emitDownlevelTaggedTemplateVariable + + write("("); + } + emit(node.tempVariable); + write(" = ["); + + if (node.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral) { + emit(node.template); + } else { + emit(( node.template).head); + forEach(( node.template).templateSpans, (child) => { + write(", "); + emit(child.literal); + }); + } write("]"); - // Now we emit the expressions - forEach(node.templateSpans, templateSpan => { + if (!inLoop) { + write(";"); + writeLine(); + } + else { write(", "); - var needsParens = templateSpan.expression.kind === SyntaxKind.BinaryExpression - && ( templateSpan.expression).operator === SyntaxKind.CommaToken; - emitParenthesized(templateSpan.expression, needsParens); - }); + } + emit(node.tempVariable); + write(".raw = ["); + if (node.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral) { + emitDownlevelRawTemplateLiteral( node.template, true); + } else { + emitDownlevelRawTemplateLiteral(( node.template).head, false); + forEach(( node.template).templateSpans, (child, index) => { + write(", "); + emitDownlevelRawTemplateLiteral(child.literal, index === ( node.template).templateSpans.length - 1); + }); + } + write("]"); + if (!inLoop) { + write(";"); + writeLine(); + } + } + + function emitDownlevelTaggedTemplate(node: LiteralExpression | TemplateExpression): void { + // Emit should like: + // foo(tempVar, expressions0, expression1) + emit(( node.parent).tempVariable); + + // Now we emit the expressions + if (node.kind === SyntaxKind.TemplateExpression) { + forEach(( node).templateSpans, templateSpan => { + write(", "); + var needsParens = templateSpan.expression.kind === SyntaxKind.BinaryExpression + && ( templateSpan.expression).operator === SyntaxKind.CommaToken; + emitParenthesized(templateSpan.expression, needsParens); + }); + } } function emitTemplateExpression(node: TemplateExpression): void { @@ -2527,7 +2580,7 @@ module ts { emit(node.template); } else { write("("); - emit(node.template); + emitDownlevelTaggedTemplate(node.template); write(")"); } } @@ -3969,6 +4022,11 @@ module ts { return; } + if (isStatement(node)) { + // TODO: Check whether node is a loop + forEach(( node).downlevelTaggedTemplates, node => emitDownlevelTaggedTemplateStrings(node, false)); + } + if (node.flags & NodeFlags.Ambient) { return emitPinnedOrTripleSlashComments(node); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 60ed8352b9a..52a95acfc91 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -692,6 +692,7 @@ module ts { export interface TaggedTemplateExpression extends MemberExpression { tag: LeftHandSideExpression; template: LiteralExpression | TemplateExpression; + tempVariable?: Identifier; // Initialized in emitter.ts } export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression; @@ -703,6 +704,7 @@ module ts { export interface Statement extends Node, ModuleElement { _statementBrand: any; + downlevelTaggedTemplates?: TaggedTemplateExpression[]; // Initialized in binder.ts } export interface Block extends Statement {