mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
initial revision of downlevel compilation for let/const bindings
This commit is contained in:
+26
-11
@@ -264,7 +264,7 @@ module ts {
|
||||
Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
|
||||
if (symbol.flags & meaning) {
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
|
||||
if (symbol.flags & SymbolFlags.Import) {
|
||||
var target = resolveImport(symbol);
|
||||
@@ -10207,7 +10207,30 @@ module ts {
|
||||
}
|
||||
|
||||
function isUnknownIdentifier(location: Node, name: string): boolean {
|
||||
return !resolveName(location, name, SymbolFlags.Value, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined);
|
||||
return !resolveName(location, name, SymbolFlags.Value | SymbolFlags.Import, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined);
|
||||
}
|
||||
|
||||
function getBlockScopedVariableId(n: Identifier): number {
|
||||
Debug.assert(n.parent !== undefined);
|
||||
|
||||
// ignore name parts of property access expressions
|
||||
if (n.parent.kind === SyntaxKind.PropertyAccessExpression &&
|
||||
(<PropertyAccessExpression>n.parent).name === n) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// for names in variable declarations and binding elements try to short circuit and fetch symbol from the node
|
||||
var declarationSymbol: Symbol =
|
||||
(n.parent.kind === SyntaxKind.VariableDeclaration && (<VariableDeclaration>n.parent).name === n) ||
|
||||
n.parent.kind === SyntaxKind.BindingElement
|
||||
? getSymbolOfNode(n.parent)
|
||||
: undefined;
|
||||
|
||||
var symbol = declarationSymbol ||
|
||||
getNodeLinks(n).resolvedSymbol ||
|
||||
resolveName(n, n.text, SymbolFlags.BlockScopedVariable | SymbolFlags.Import, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined);
|
||||
|
||||
return symbol && symbol.flags & SymbolFlags.BlockScopedVariable ? symbol.id : undefined;
|
||||
}
|
||||
|
||||
function createResolver(): EmitResolver {
|
||||
@@ -10226,6 +10249,7 @@ module ts {
|
||||
isEntityNameVisible,
|
||||
getConstantValue,
|
||||
isUnknownIdentifier,
|
||||
getBlockScopedVariableId,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10953,15 +10977,6 @@ module ts {
|
||||
if (!declarationList.declarations.length) {
|
||||
return grammarErrorAtPos(getSourceFileOfNode(declarationList), declarations.pos, declarations.end - declarations.pos, Diagnostics.Variable_declaration_list_cannot_be_empty);
|
||||
}
|
||||
|
||||
if (languageVersion < ScriptTarget.ES6) {
|
||||
if (isLet(declarationList)) {
|
||||
return grammarErrorOnFirstToken(declarationList, Diagnostics.let_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher);
|
||||
}
|
||||
else if (isConst(declarationList)) {
|
||||
return grammarErrorOnFirstToken(declarationList, Diagnostics.const_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function allowLetAndConstDeclarations(parent: Node): boolean {
|
||||
|
||||
+210
-28
@@ -21,6 +21,12 @@ module ts {
|
||||
diagnosticMessage: DiagnosticMessage;
|
||||
typeName?: DeclarationName;
|
||||
}
|
||||
|
||||
interface ScopeFrame {
|
||||
names: Map<string>;
|
||||
previous: ScopeFrame;
|
||||
}
|
||||
|
||||
type GetSymbolAccessibilityDiagnostic = (symbolAccesibilityResult: SymbolAccessiblityResult) => SymbolAccessibilityDiagnostic;
|
||||
|
||||
interface EmitTextWriterWithSymbolWriter extends EmitTextWriter, SymbolWriter {
|
||||
@@ -353,7 +359,6 @@ module ts {
|
||||
var enclosingDeclaration: Node;
|
||||
var currentSourceFile: SourceFile;
|
||||
var reportedDeclarationError = false;
|
||||
|
||||
var emitJsDocComments = compilerOptions.removeComments ? function (declaration: Node) { } : writeJsDocComments;
|
||||
var emit = compilerOptions.stripInternal ? stripInternal : emitNode;
|
||||
|
||||
@@ -1557,6 +1562,11 @@ module ts {
|
||||
|
||||
var currentSourceFile: SourceFile;
|
||||
|
||||
var lastFrame: ScopeFrame;
|
||||
var currentScopeNames: Map<string>;
|
||||
|
||||
var generatedBlockScopeNames: string[];
|
||||
|
||||
var extendsEmitted = false;
|
||||
var tempCount = 0;
|
||||
var tempVariables: Identifier[];
|
||||
@@ -1629,6 +1639,82 @@ module ts {
|
||||
writeEmittedFiles(writer.getText(), /*writeByteOrderMark*/ compilerOptions.emitBOM);
|
||||
return;
|
||||
|
||||
function enterScope(): boolean {
|
||||
var names = currentScopeNames;
|
||||
currentScopeNames = undefined;
|
||||
if (names) {
|
||||
lastFrame = { names, previous: lastFrame };
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function exitScope(popFrame: boolean): void {
|
||||
if (popFrame) {
|
||||
currentScopeNames = lastFrame.names;
|
||||
lastFrame = lastFrame.previous;
|
||||
}
|
||||
else {
|
||||
currentScopeNames = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function makeUniqueName(location: Node, baseName: string): string {
|
||||
if (!isExistingName(location, baseName)) {
|
||||
// use current name as is
|
||||
return setGeneratedName(baseName);
|
||||
}
|
||||
|
||||
// First try '_name'
|
||||
if (baseName.charCodeAt(0) !== CharacterCodes._) {
|
||||
var baseName = "_" + baseName;
|
||||
if (!isExistingName(location, baseName)) {
|
||||
return setGeneratedName(baseName);
|
||||
}
|
||||
}
|
||||
// Find the first unique '_name_n', where n is a positive number
|
||||
if (baseName.charCodeAt(baseName.length - 1) !== CharacterCodes._) {
|
||||
baseName += "_";
|
||||
}
|
||||
var i = 1;
|
||||
while (true) {
|
||||
name = baseName + i;
|
||||
if (!isExistingName(location, name)) {
|
||||
return setGeneratedName(name);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
function setGeneratedName(name: string): string {
|
||||
if (!currentScopeNames) {
|
||||
currentScopeNames = {};
|
||||
}
|
||||
|
||||
return currentScopeNames[name] = name;
|
||||
}
|
||||
|
||||
|
||||
function isExistingName(location: Node, name: string) {
|
||||
if (!resolver.isUnknownIdentifier(location, name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (currentScopeNames && hasProperty(currentScopeNames, name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var frame = lastFrame;
|
||||
while (frame) {
|
||||
if (hasProperty(frame.names, name)) {
|
||||
return true;
|
||||
}
|
||||
frame = frame.previous;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function initializeEmitterWithSourceMaps() {
|
||||
var sourceMapDir: string; // The directory in which sourcemap will be
|
||||
|
||||
@@ -1990,7 +2076,7 @@ module ts {
|
||||
function createTempVariable(location: Node, forLoopVariable?: boolean): Identifier {
|
||||
var name = forLoopVariable ? "_i" : undefined;
|
||||
while (true) {
|
||||
if (name && resolver.isUnknownIdentifier(location, name)) {
|
||||
if (name && !isExistingName(location, name)) {
|
||||
break;
|
||||
}
|
||||
// _a .. _h, _j ... _z, _0, _1, ...
|
||||
@@ -2338,7 +2424,20 @@ module ts {
|
||||
writeTextOfNode(currentSourceFile, node);
|
||||
}
|
||||
|
||||
function getBlockScopedVariableId(node: Identifier): number {
|
||||
// return undefined for synthesized nodes
|
||||
return node.parent && resolver.getBlockScopedVariableId(node);
|
||||
}
|
||||
|
||||
function emitIdentifier(node: Identifier) {
|
||||
var symbolId = getBlockScopedVariableId(node);
|
||||
if (symbolId !== undefined && generatedBlockScopeNames) {
|
||||
var text = generatedBlockScopeNames[symbolId];
|
||||
if (text) {
|
||||
write(text);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!node.parent) {
|
||||
write(node.text);
|
||||
}
|
||||
@@ -2881,6 +2980,32 @@ module ts {
|
||||
emitEmbeddedStatement(node.statement);
|
||||
}
|
||||
|
||||
function emitStartOfVariableDeclarationList(decl: Node, startPos?: number): void {
|
||||
var tokenKind = SyntaxKind.VarKeyword;
|
||||
if (decl && languageVersion >= ScriptTarget.ES6) {
|
||||
if (isLet(decl)) {
|
||||
tokenKind = SyntaxKind.LetKeyword;
|
||||
}
|
||||
else if (isConst(decl)) {
|
||||
tokenKind = SyntaxKind.ConstKeyword;
|
||||
}
|
||||
}
|
||||
|
||||
if (startPos !== undefined) {
|
||||
emitToken(tokenKind, startPos);
|
||||
}
|
||||
else {
|
||||
switch (tokenKind) {
|
||||
case SyntaxKind.VarKeyword:
|
||||
return write("var ");
|
||||
case SyntaxKind.LetKeyword:
|
||||
return write("let ");
|
||||
case SyntaxKind.ConstKeyword:
|
||||
return write("const ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function emitForStatement(node: ForStatement) {
|
||||
var endPos = emitToken(SyntaxKind.ForKeyword, node.pos);
|
||||
write(" ");
|
||||
@@ -2888,17 +3013,9 @@ module ts {
|
||||
if (node.initializer && node.initializer.kind === SyntaxKind.VariableDeclarationList) {
|
||||
var variableDeclarationList = <VariableDeclarationList>node.initializer;
|
||||
var declarations = variableDeclarationList.declarations;
|
||||
if (declarations[0] && isLet(declarations[0])) {
|
||||
emitToken(SyntaxKind.LetKeyword, endPos);
|
||||
}
|
||||
else if (declarations[0] && isConst(declarations[0])) {
|
||||
emitToken(SyntaxKind.ConstKeyword, endPos);
|
||||
}
|
||||
else {
|
||||
emitToken(SyntaxKind.VarKeyword, endPos);
|
||||
}
|
||||
emitStartOfVariableDeclarationList(declarations[0], endPos);
|
||||
write(" ");
|
||||
emitCommaList(variableDeclarationList.declarations);
|
||||
emitCommaList(declarations);
|
||||
}
|
||||
else if (node.initializer) {
|
||||
emit(node.initializer);
|
||||
@@ -2919,12 +3036,7 @@ module ts {
|
||||
var variableDeclarationList = <VariableDeclarationList>node.initializer;
|
||||
if (variableDeclarationList.declarations.length >= 1) {
|
||||
var decl = variableDeclarationList.declarations[0];
|
||||
if (isLet(decl)) {
|
||||
emitToken(SyntaxKind.LetKeyword, endPos);
|
||||
}
|
||||
else {
|
||||
emitToken(SyntaxKind.VarKeyword, endPos);
|
||||
}
|
||||
emitStartOfVariableDeclarationList(decl, endPos);
|
||||
write(" ");
|
||||
emit(decl);
|
||||
}
|
||||
@@ -3075,6 +3187,8 @@ module ts {
|
||||
if (emitCount++) {
|
||||
write(", ");
|
||||
}
|
||||
|
||||
renameNonTopLevelLetAndConst(name);
|
||||
if (name.parent && (name.parent.kind === SyntaxKind.VariableDeclaration || name.parent.kind === SyntaxKind.BindingElement)) {
|
||||
emitModuleMemberName(<Declaration>name.parent);
|
||||
}
|
||||
@@ -3184,7 +3298,7 @@ module ts {
|
||||
emitDestructuringAssignment(e, createElementAccess(value, createNumericLiteral(i)));
|
||||
}
|
||||
else {
|
||||
if (i === elements.length - 1) {
|
||||
if (i === elements.length - 1 && (<SpreadElementExpression>e).expression.kind === SyntaxKind.Identifier) {
|
||||
value = ensureIdentifier(value);
|
||||
emitAssignment(<Identifier>(<SpreadElementExpression>e).expression, value);
|
||||
write(".slice(" + i + ")");
|
||||
@@ -3286,22 +3400,75 @@ module ts {
|
||||
}
|
||||
}
|
||||
else {
|
||||
renameNonTopLevelLetAndConst(<Identifier>node.name);
|
||||
emitModuleMemberName(node);
|
||||
emitOptional(" = ", node.initializer);
|
||||
}
|
||||
}
|
||||
|
||||
function getEnclosingBlockScopeContainer(node: Node): Node {
|
||||
var current = node;
|
||||
while (current) {
|
||||
if (isAnyFunction(current)) {
|
||||
return current.parent;
|
||||
}
|
||||
switch (current.kind) {
|
||||
case SyntaxKind.CatchClause:
|
||||
case SyntaxKind.ForStatement:
|
||||
case SyntaxKind.ForInStatement:
|
||||
case SyntaxKind.SwitchKeyword:
|
||||
return current.parent;
|
||||
case SyntaxKind.Block:
|
||||
if (isAnyFunction(current.parent)) {
|
||||
return current.parent.parent;
|
||||
}
|
||||
else {
|
||||
return current.parent;
|
||||
}
|
||||
case SyntaxKind.SourceFile:
|
||||
return current;
|
||||
}
|
||||
|
||||
current = current.parent;
|
||||
}
|
||||
}
|
||||
|
||||
function renameNonTopLevelLetAndConst(node: Node): void {
|
||||
// do not rename if
|
||||
// - language version is ES6+
|
||||
// - node is synthesized (does not have a parent)
|
||||
// - node is definitely not name of variable declaration.
|
||||
// it still can be part of parameter declaration, this check will be done next
|
||||
if (languageVersion >= ScriptTarget.ES6 ||
|
||||
!node.parent ||
|
||||
(node.parent.kind !== SyntaxKind.VariableDeclaration && node.parent.kind !== SyntaxKind.BindingElement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var combinedFlags = getCombinedNodeFlags(node.parent);
|
||||
if (((combinedFlags & NodeFlags.BlockScoped) === 0) || combinedFlags & NodeFlags.Export) {
|
||||
// do not rename exported or non-block scoped variables
|
||||
return;
|
||||
}
|
||||
|
||||
// here it is known that node is a block scoped variable
|
||||
var list = getAncestor(node, SyntaxKind.VariableDeclarationList);
|
||||
if (list.parent.kind === SyntaxKind.VariableStatement && list.parent.parent.kind === SyntaxKind.SourceFile) {
|
||||
// do not rename variables that are defined on source file level
|
||||
return;
|
||||
}
|
||||
|
||||
var generatedName = makeUniqueName(getEnclosingBlockScopeContainer(node), (<Identifier>node).text);
|
||||
var symbolId = resolver.getBlockScopedVariableId(<Identifier>node);
|
||||
if (!generatedBlockScopeNames) {
|
||||
generatedBlockScopeNames = [];
|
||||
}
|
||||
generatedBlockScopeNames[symbolId] = generatedName;
|
||||
}
|
||||
|
||||
function emitVariableStatement(node: VariableStatement) {
|
||||
if (!(node.flags & NodeFlags.Export)) {
|
||||
if (isLet(node.declarationList)) {
|
||||
write("let ");
|
||||
}
|
||||
else if (isConst(node.declarationList)) {
|
||||
write("const ");
|
||||
}
|
||||
else {
|
||||
write("var ");
|
||||
}
|
||||
emitStartOfVariableDeclarationList(node.declarationList);
|
||||
}
|
||||
emitCommaList(node.declarationList.declarations);
|
||||
write(";");
|
||||
@@ -3472,6 +3639,8 @@ module ts {
|
||||
tempVariables = undefined;
|
||||
tempParameters = undefined;
|
||||
|
||||
var popFrame = enterScope()
|
||||
|
||||
// When targeting ES6, emit arrow function natively in ES6
|
||||
if (shouldEmitAsArrowFunction(node)) {
|
||||
emitSignatureParametersForArrow(node);
|
||||
@@ -3563,6 +3732,9 @@ module ts {
|
||||
emitEnd(node);
|
||||
write(";");
|
||||
}
|
||||
|
||||
exitScope(popFrame);
|
||||
|
||||
tempCount = saveTempCount;
|
||||
tempVariables = saveTempVariables;
|
||||
tempParameters = saveTempParameters;
|
||||
@@ -3773,6 +3945,9 @@ module ts {
|
||||
tempCount = 0;
|
||||
tempVariables = undefined;
|
||||
tempParameters = undefined;
|
||||
|
||||
var popFrame = enterScope();
|
||||
|
||||
// Emit the constructor overload pinned comments
|
||||
forEach(node.members, member => {
|
||||
if (member.kind === SyntaxKind.Constructor && !(<ConstructorDeclaration>member).body) {
|
||||
@@ -3833,6 +4008,9 @@ module ts {
|
||||
if (ctor) {
|
||||
emitTrailingComments(ctor);
|
||||
}
|
||||
|
||||
exitScope(popFrame);
|
||||
|
||||
tempCount = saveTempCount;
|
||||
tempVariables = saveTempVariables;
|
||||
tempParameters = saveTempParameters;
|
||||
@@ -3962,7 +4140,11 @@ module ts {
|
||||
var saveTempVariables = tempVariables;
|
||||
tempCount = 0;
|
||||
tempVariables = undefined;
|
||||
var popFrame = enterScope();
|
||||
|
||||
emit(node.body);
|
||||
|
||||
exitScope(popFrame);
|
||||
tempCount = saveTempCount;
|
||||
tempVariables = saveTempVariables;
|
||||
}
|
||||
|
||||
@@ -1138,6 +1138,7 @@ module ts {
|
||||
// Returns the constant value this property access resolves to, or 'undefined' for a non-constant
|
||||
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number;
|
||||
isUnknownIdentifier(location: Node, name: string): boolean;
|
||||
getBlockScopedVariableId(node: Identifier): number;
|
||||
}
|
||||
|
||||
export const enum SymbolFlags {
|
||||
|
||||
Reference in New Issue
Block a user