From bba41a8a0c9740ff57eed37ea281c4a6b046c21b Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 15 May 2017 18:42:49 -0700 Subject: [PATCH] avoid ascii escaping literal text --- src/compiler/checker.ts | 8 +++++--- src/compiler/emitter.ts | 4 +++- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 19 +++++++++---------- tests/cases/compiler/unicodeStringLiteral.ts | 1 + ...ImplementInterfaceIndexSignaturesString.ts | 4 ++-- ...mplementInterfaceInheritsAbstractMethod.ts | 4 ++-- ...ssImplementInterfaceMultipleImplements2.ts | 4 ++-- 8 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 tests/cases/compiler/unicodeStringLiteral.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a7ca6769082..d9b3c52b73d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2385,7 +2385,7 @@ namespace ts { return createTypeReferenceNode(name, /*typeArguments*/ undefined); } if (type.flags & (TypeFlags.StringLiteral)) { - return createLiteralTypeNode((createLiteral((type).text))); + return createLiteralTypeNode(setEmitFlags(createLiteral((type).text), EmitFlags.NoAsciiEscaping)); } if (type.flags & (TypeFlags.NumberLiteral)) { return createLiteralTypeNode((createNumericLiteral((type).text))); @@ -2783,7 +2783,9 @@ namespace ts { let returnTypeNode: TypeNode; if (signature.typePredicate) { const typePredicate = signature.typePredicate; - const parameterName = typePredicate.kind === TypePredicateKind.Identifier ? createIdentifier((typePredicate).parameterName) : createThisTypeNode(); + const parameterName = typePredicate.kind === TypePredicateKind.Identifier ? + setEmitFlags(createIdentifier((typePredicate).parameterName), EmitFlags.NoAsciiEscaping) : + createThisTypeNode(); const typeNode = typeToTypeNodeHelper(typePredicate.type, context); returnTypeNode = createTypePredicateNode(parameterName, typeNode); } @@ -2894,7 +2896,7 @@ namespace ts { } const symbolName = getNameOfSymbol(symbol, context); - const identifier = createIdentifier(symbolName, typeParameterNodes); + const identifier = setEmitFlags(createIdentifier(symbolName, typeParameterNodes), EmitFlags.NoAsciiEscaping); return index > 0 ? createQualifiedName(createEntityNameFromSymbolChain(chain, index - 1), identifier) : identifier; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 8ec03e90c77..826b636e71e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2653,7 +2653,9 @@ namespace ts { if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { const textSourceNode = (node).textSourceNode; if (isIdentifier(textSourceNode)) { - return "\"" + escapeNonAsciiCharacters(escapeString(getTextOfNode(textSourceNode))) + "\""; + return getEmitFlags(node) & EmitFlags.NoAsciiEscaping ? + `"${escapeString(getTextOfNode(textSourceNode))}"` : + `"${escapeNonAsciiCharacters(escapeString(getTextOfNode(textSourceNode)))}"`; } else { return getLiteralTextOfNode(textSourceNode); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cf204d158a1..4bc97c56c00 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3985,6 +3985,7 @@ namespace ts { NoHoisting = 1 << 21, // Do not hoist this declaration in --module system HasEndOfDeclarationMarker = 1 << 22, // Declaration has an associated NotEmittedStatement to mark the end of the declaration Iterator = 1 << 23, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable. + NoAsciiEscaping = 1 << 24, // When synthesizing nodes that lack an original node or textSourceNode, we want to write the text on the node with ASCII escaping substitutions. } export interface EmitHelper { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 80daf383639..9dac18eec89 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -328,19 +328,22 @@ namespace ts { return getSourceTextOfNodeFromSourceFile(sourceFile, node); } + const escapeText = getEmitFlags(node) & EmitFlags.NoAsciiEscaping ? + (text: string) => escapeString(text) : + (text: string) => escapeNonAsciiCharacters(escapeString(text)); // If we can't reach the original source text, use the canonical form if it's a number, - // or an escaped quoted form of the original text if it's string-like. + // or a (possibly escaped) quoted form of the original text if it's string-like. switch (node.kind) { case SyntaxKind.StringLiteral: - return getQuotedEscapedLiteralText('"', node.text, '"'); + return '"' + escapeText(node.text) + '"'; case SyntaxKind.NoSubstitutionTemplateLiteral: - return getQuotedEscapedLiteralText("`", node.text, "`"); + return "`" + escapeText(node.text) + "`"; case SyntaxKind.TemplateHead: - return getQuotedEscapedLiteralText("`", node.text, "${"); + return "`" + escapeText(node.text) + "${"; case SyntaxKind.TemplateMiddle: - return getQuotedEscapedLiteralText("}", node.text, "${"); + return "}" + escapeText(node.text) + "${"; case SyntaxKind.TemplateTail: - return getQuotedEscapedLiteralText("}", node.text, "`"); + return "}" + escapeText(node.text) + "`"; case SyntaxKind.NumericLiteral: return node.text; } @@ -348,10 +351,6 @@ namespace ts { Debug.fail(`Literal kind '${node.kind}' not accounted for.`); } - function getQuotedEscapedLiteralText(leftQuote: string, text: string, rightQuote: string) { - return leftQuote + escapeNonAsciiCharacters(escapeString(text)) + rightQuote; - } - // Add an extra underscore to identifiers that start with two underscores to avoid issues with magic names like '__proto__' export function escapeIdentifier(identifier: string): string { return identifier.length >= 2 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ ? "_" + identifier : identifier; diff --git a/tests/cases/compiler/unicodeStringLiteral.ts b/tests/cases/compiler/unicodeStringLiteral.ts new file mode 100644 index 00000000000..fa2e1283309 --- /dev/null +++ b/tests/cases/compiler/unicodeStringLiteral.ts @@ -0,0 +1 @@ +var ੳ = "Ü­ਲĭ"; \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts index 4b595c6eda7..9d42faada36 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts @@ -1,11 +1,11 @@ /// //// interface I { -//// [x: string]: X; +//// [Ƚ: string]: X; //// } //// //// class C implements I {[| |]} verify.rangeAfterCodeFix(` - [x: string]: number; + [Ƚ: string]: number; `); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts index c141592823a..7bb230a2dfb 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts @@ -2,12 +2,12 @@ //// abstract class C1 { } //// abstract class C2 { -//// abstract f1(); +//// abstract fA(); //// } //// interface I1 extends C1, C2 { } //// class C3 implements I1 {[| |]} -verify.rangeAfterCodeFix(`f1(){ +verify.rangeAfterCodeFix(`fA(){ throw new Error("Method not implemented."); } `); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts index e5100b88f6c..946b6495c5f 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts @@ -4,7 +4,7 @@ //// x: number; //// } //// interface I2 { -//// y: number; +//// y: "𣋝ઢȴ¬⏊"; //// } //// //// class C implements I1,I2 {[| @@ -12,7 +12,7 @@ //// } verify.rangeAfterCodeFix(` -y: number; +y: "𣋝ઢȴ¬⏊"; `); verify.not.codeFixAvailable();