diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0ffb7917500..bcff651c518 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9276,6 +9276,10 @@ module ts { getSymbolDisplayBuilder().buildTypeDisplay(getReturnTypeOfSignature(signature), writer, enclosingDeclaration, flags); } + function isUnknownIdentifier(location: Node, name: string): boolean { + return !resolveName(location, name, SymbolFlags.Value, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined); + } + function invokeEmitter(targetSourceFile?: SourceFile) { var resolver: EmitResolver = { getProgram: () => program, @@ -9295,6 +9299,7 @@ module ts { isSymbolAccessible, isImportDeclarationEntityNameReferenceDeclarationVisibile, getConstantValue, + isUnknownIdentifier, }; checkProgram(); return emitFiles(resolver, targetSourceFile); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index e7df9e9136a..dabd6c943e7 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -322,6 +322,7 @@ module ts { var decreaseIndent = writer.decreaseIndent; var extendsEmitted = false; + var tempCount = 0; /** write emitted output to disk*/ var writeEmittedFiles = writeJavaScriptFile; @@ -716,6 +717,14 @@ module ts { writeFile(jsFilePath, emitOutput, writeByteOrderMark); } + function getTempVariableName(location: Node) { + do { + var name = "_" + tempCount++; + } + while (!resolver.isUnknownIdentifier(location, name)); + return name; + } + function emitTokenText(tokenKind: SyntaxKind, startPos: number, emitFn?: () => void) { var tokenString = tokenToString(tokenKind); if (emitFn) { @@ -789,22 +798,15 @@ module ts { } function emitLiteral(node: LiteralExpression): void { - var text = getLiteralText(); - + var text = compilerOptions.target < ScriptTarget.ES6 && isTemplateLiteralKind(node.kind) ? getTemplateLiteralAsStringLiteral(node) : + node.parent ? getSourceTextOfLocalNode(node) : + node.text; if (compilerOptions.sourceMap && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) { writer.writeLiteral(text); } else { write(text); } - - function getLiteralText() { - if (compilerOptions.target < ScriptTarget.ES6 && isTemplateLiteralKind(node.kind)) { - return getTemplateLiteralAsStringLiteral(node) - } - - return getSourceTextOfLocalNode(node); - } } function getTemplateLiteralAsStringLiteral(node: LiteralExpression): string { @@ -981,7 +983,10 @@ module ts { } function emitIdentifier(node: Identifier) { - if (!isNotExpressionIdentifier(node)) { + if (!node.parent) { + write(node.text); + } + else if (!isNotExpressionIdentifier(node)) { emitExpressionIdentifier(node); } else { @@ -1498,20 +1503,128 @@ module ts { emitEnd(node.name); } - function emitVariableDeclaration(node: VariableDeclaration) { - emitLeadingComments(node); - if (node.propertyName) { - emit(node.propertyName); - write(": "); + function rewriteDestructuringDeclaration(node: VariableDeclaration): NodeArray { + var declarations = >[]; + rewriteDeclaration(node, undefined); + return declarations; + + function addVariableDeclaration(name: Identifier, initializer: Expression) { + var node = createNode(SyntaxKind.VariableDeclaration); + node.name = name; + node.initializer = initializer; + declarations.push(node); } - if (node.name.kind === SyntaxKind.Identifier) { - emitModuleMemberName(node); + + function createTemporaryVariable(expr: Expression): Expression { + var tempName = getTempVariableName(node); + var identifier = createNode(SyntaxKind.Identifier); + identifier.text = tempName; + addVariableDeclaration(identifier, expr); + return identifier; + } + + function createVoidZero(): Expression { + var zero = createNode(SyntaxKind.NumericLiteral); + zero.text = "0"; + var result = createNode(SyntaxKind.PrefixOperator); + result.operator = SyntaxKind.VoidKeyword; + result.operand = zero; + return result; + } + + function createDefaultValueCheck(value: Expression, defaultValue: Expression): Expression { + if (value.kind !== SyntaxKind.Identifier) { + value = createTemporaryVariable(value); + } + var equals = createNode(SyntaxKind.BinaryExpression); + equals.left = value; + equals.operator = SyntaxKind.EqualsEqualsEqualsToken; + equals.right = createVoidZero(); + var cond = createNode(SyntaxKind.ConditionalExpression); + cond.condition = equals; + cond.whenTrue = defaultValue; + cond.whenFalse = value; + return cond; + } + + function createNumericLiteral(value: number) { + var node = createNode(SyntaxKind.NumericLiteral); + node.text = "" + value; + return node; + } + + function parenthesizeForAccess(expr: Expression): Expression { + if (expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.PropertyAccess || expr.kind === SyntaxKind.IndexedAccess) { + return expr; + } + var node = createNode(SyntaxKind.ParenExpression); + node.expression = expr; + return node; + } + + function createPropertyAccess(object: Expression, propName: Identifier): Expression { + if (propName.kind !== SyntaxKind.Identifier) { + return createIndexedAccess(object, propName); + } + var node = createNode(SyntaxKind.PropertyAccess); + node.left = parenthesizeForAccess(object); + node.right = propName; + return node; + } + + function createIndexedAccess(object: Expression, index: Expression): Expression { + var node = createNode(SyntaxKind.IndexedAccess); + node.object = parenthesizeForAccess(object); + node.index = index; + return node; + } + + function rewriteDeclaration(target: BindingElement, value: Expression) { + if (target.initializer) { + value = value ? createDefaultValueCheck(value, target.initializer) : target.initializer; + } + else if (!value) { + value = createVoidZero(); + } + if (isBindingPattern(target.name)) { + var pattern = target.name; + var elements = pattern.elements; + if (elements.length !== 1) { + value = createTemporaryVariable(value); + } + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + if (pattern.kind === SyntaxKind.ObjectBindingPattern) { + var propName = element.propertyName || element.name; + rewriteDeclaration(element, createPropertyAccess(value, propName)); + } + else { + rewriteDeclaration(element, createIndexedAccess(value, createNumericLiteral(i))); + } + } + } + else { + addVariableDeclaration(target.name, value); + } + } + } + + function emitVariableDeclaration(node: VariableDeclaration) { + if (node.parent) { + emitLeadingComments(node); + if (isBindingPattern(node.name)) { + emitCommaList(rewriteDestructuringDeclaration(node), /*includeTrailingComma*/ false); + } + else { + emitModuleMemberName(node); + emitOptional(" = ", node.initializer); + } + emitTrailingComments(node); } else { emit(node.name); + emitOptional(" = ", node.initializer); } - emitOptional(" = ", node.initializer); - emitTrailingComments(node); } function emitVariableStatement(node: VariableStatement) { @@ -1577,6 +1690,7 @@ module ts { if (hasRestParameters(node)) { var restIndex = node.parameters.length - 1; var restParam = node.parameters[restIndex]; + var tempName = "_i"; writeLine(); emitLeadingComments(restParam); emitStart(restParam); @@ -1588,22 +1702,22 @@ module ts { writeLine(); write("for ("); emitStart(restParam); - write("var _i = " + restIndex + ";"); + write("var " + tempName + " = " + restIndex + ";"); emitEnd(restParam); write(" "); emitStart(restParam); - write("_i < arguments.length;"); + write(tempName + " < arguments.length;"); emitEnd(restParam); write(" "); emitStart(restParam); - write("_i++"); + write(tempName + "++"); emitEnd(restParam); write(") {"); increaseIndent(); writeLine(); emitStart(restParam); emitNode(restParam.name); - write("[_i - " + restIndex + "] = arguments[_i];"); + write("[" + tempName + " - " + restIndex + "] = arguments[" + tempName + "];"); emitEnd(restParam); decreaseIndent(); writeLine(); @@ -1660,6 +1774,7 @@ module ts { function emitSignatureAndBody(node: FunctionLikeDeclaration) { emitSignatureParameters(node); write(" {"); + var saveTempCount = tempCount; scopeEmitStart(node); increaseIndent(); @@ -1711,6 +1826,7 @@ module ts { } } scopeEmitEnd(); + tempCount = saveTempCount; if (node.flags & NodeFlags.Export) { writeLine(); emitStart(node); @@ -1936,6 +2052,7 @@ module ts { emit(node.name); emitSignatureParameters(ctor); write(" {"); + var saveTempCount = tempCount; scopeEmitStart(node, "constructor"); increaseIndent(); if (ctor) { @@ -1975,6 +2092,7 @@ module ts { decreaseIndent(); emitToken(SyntaxKind.CloseBraceToken, ctor ? (ctor.body).statements.end : node.members.end); scopeEmitEnd(); + tempCount = saveTempCount; emitEnd(ctor || node); if (ctor) { emitTrailingComments(ctor); @@ -2082,6 +2200,7 @@ module ts { write(resolver.getLocalNameOfContainer(node)); emitEnd(node.name); write(") "); + var saveTempCount = tempCount; if (node.body.kind === SyntaxKind.ModuleBlock) { emit(node.body); } @@ -2098,6 +2217,7 @@ module ts { emitToken(SyntaxKind.CloseBraceToken, moduleBlock.statements.end); scopeEmitEnd(); } + tempCount = saveTempCount; write(")("); if (node.flags & NodeFlags.Export) { emit(node.name); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 7fd68b9ce03..ecebcaa309b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -9,12 +9,8 @@ module ts { return nodeConstructors[kind] || (nodeConstructors[kind] = objectAllocator.getNodeConstructor(kind)); } - function createRootNode(kind: SyntaxKind, pos: number, end: number, flags: NodeFlags): Node { - var node = new (getNodeConstructor(kind))(); - node.pos = pos; - node.end = end; - node.flags = flags; - return node; + export function createNode(kind: SyntaxKind): Node { + return new (getNodeConstructor(kind))(); } interface ReferenceComments { @@ -4389,12 +4385,12 @@ module ts { } scanner = createScanner(languageVersion, /*skipTrivia*/ true, sourceText, scanError, onComment); - var rootNodeFlags: NodeFlags = 0; + file = createNode(SyntaxKind.SourceFile); if (fileExtensionIs(filename, ".d.ts")) { - rootNodeFlags = NodeFlags.DeclarationFile; + file.flags = NodeFlags.DeclarationFile; inAmbientContext = true; } - file = createRootNode(SyntaxKind.SourceFile, 0, sourceText.length, rootNodeFlags); + file.end = sourceText.length; file.filename = normalizePath(filename); file.text = sourceText; file.getLineAndCharacterFromPosition = getLineAndCharacterFromSourcePosition; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 706e6fe7ae2..9807cee4649 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -841,6 +841,7 @@ module ts { // Returns the constant value this property access resolves to, or 'undefined' for a non-constant getConstantValue(node: PropertyAccess | IndexedAccess): number; isEmitBlocked(sourceFile?: SourceFile): boolean; + isUnknownIdentifier(location: Node, name: string): boolean; } export const enum SymbolFlags {