Centralized parent navigation and node stack logic

This commit is contained in:
Ron Buckton 2015-09-03 15:19:47 -07:00
parent 14d079e4d7
commit 49a3a3f5b2
5 changed files with 706 additions and 502 deletions

View File

@ -780,6 +780,9 @@ namespace ts {
end: -1,
flags: 0,
parent: undefined,
createParentNavigator(): ParentNavigator {
return createParentNavigator(<Node>this);
}
};
return <any>Node;
},

View File

@ -153,10 +153,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
// tree as we descend into the branch. It is used to derive the parent
// node of any node without needing parent pointers, to help with
// the emit of synthesized nodes that do not maintain parent pointers.
let nodeStackSize: number = 0;
let ancestorStack: Node[] = [];
let parentNode: Node;
let currentNode: Node;
let nodeStack: NodeStack;
let pushNode: (node: Node) => void;
let popNode: () => void;
let extendsEmitted = false;
let decorateEmitted = false;
@ -228,52 +227,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
function emitSourceFile(sourceFile: SourceFile): void {
currentSourceFile = sourceFile;
exportFunctionForFile = undefined;
nodeStackSize = 0;
ancestorStack.length = 0;
parentNode = undefined;
currentNode = undefined;
nodeStack = createNodeStack();
pushNode = nodeStack.pushNode;
popNode = nodeStack.popNode;
emit(sourceFile);
}
function pushNode(node: Node): void {
Debug.assert(node !== undefined, "Incorrect node stack behavior during push. Argument `node` is undefined.")
Debug.assert(currentNode !== node, "Incorrect node stack behavior during push. Argument `node` is already the current node.");
Debug.assert(parentNode !== node, "Incorrect node stack behavior during push. Argument `node` is already the parent node.");
nodeStackSize++;
if (nodeStackSize > 2) {
ancestorStack.push(parentNode);
}
parentNode = currentNode;
currentNode = node;
}
function popNode(): void {
currentNode = parentNode;
parentNode = nodeStackSize > 2 ? ancestorStack.pop() : undefined;
nodeStackSize--;
}
function peekNode(offset: number): Node {
switch (offset) {
case 0: return currentNode;
case 1: return parentNode;
default: return nodeStackSize > 2 ? ancestorStack[nodeStackSize - 1 - offset] : undefined;
}
}
function findAncestorNode<T extends Node>(match: (node: Node) => node is T): T;
function findAncestorNode(match: (node: Node) => boolean): Node;
function findAncestorNode(match: (node: Node) => boolean) {
if (parentNode && match(parentNode)) {
return parentNode;
}
for (let i = ancestorStack.length; i >= 0; i--) {
let node = ancestorStack[i];
if (match(node)) {
return node;
}
}
return undefined;
return nodeStack.findAncestorNode(match);
}
function isUniqueName(name: string): boolean {
@ -1098,20 +1061,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
// Now we emit the expressions
if (node.template.kind === SyntaxKind.TemplateExpression) {
pushNode(node.template);
forEach((<TemplateExpression>node.template).templateSpans, templateSpan => {
write(", ");
pushNode(templateSpan);
let needsParens = templateSpan.expression.kind === SyntaxKind.BinaryExpression
&& (<BinaryExpression>templateSpan.expression).operatorToken.kind === SyntaxKind.CommaToken;
emitParenthesizedIf(templateSpan.expression, needsParens);
popNode();
});
popNode();
visitNodeWithPushStackBehavior(node.template, emitTemplateSpansInTemplateExpressionForDownlevelTaggedTemplate);
}
write("))");
}
function emitTemplateSpansInTemplateExpressionForDownlevelTaggedTemplate(node: TemplateExpression) {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
forEach(node.templateSpans, emitTemplateSpanForDownlevelTaggedTemplateWithPushStackBehavior);
}
function emitTemplateSpanForDownlevelTaggedTemplateWithPushStackBehavior(node: TemplateSpan) {
visitNodeWithPushStackBehavior(node, emitTemplateSpanForDownlevelTaggedTemplate);
}
function emitTemplateSpanForDownlevelTaggedTemplate(node: TemplateSpan) {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
write(", ");
let needsParens = node.expression.kind === SyntaxKind.BinaryExpression
&& (<BinaryExpression>node.expression).operatorToken.kind === SyntaxKind.CommaToken;
emitParenthesizedIf(node.expression, needsParens);
}
function emitTemplateExpression(node: TemplateExpression): void {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
@ -1121,135 +1092,84 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
forEachChild(node, emit);
return;
}
emitTemplateExpressionBelowES6(node);
}
function emitTemplateExpressionBelowES6(node: TemplateExpression): void {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
let emitOuterParens = isExpression(parentNode, peekNode, 1) && templateNeedsParens(node);
let nav = nodeStack.createParentNavigator();
nav.moveToParent();
let emitOuterParens = isExpression(nav) && templateNeedsParens(node);
if (emitOuterParens) {
write("(");
}
let headEmitted = false;
if (shouldEmitTemplateHead()) {
pushNode(node.head);
emitLiteral(node.head);
popNode();
if (shouldEmitTemplateHead(node)) {
visitNodeWithPushStackBehavior(node.head, emitLiteral);
headEmitted = true;
}
for (let i = 0, n = node.templateSpans.length; i < n; i++) {
let templateSpan = node.templateSpans[i];
pushNode(templateSpan);
// Check if the expression has operands and binds its operands less closely than binary '+'.
// If it does, we need to wrap the expression in parentheses. Otherwise, something like
// `abc${ 1 << 2 }`
// becomes
// "abc" + 1 << 2 + ""
// which is really
// ("abc" + 1) << (2 + "")
// rather than
// "abc" + (1 << 2) + ""
let needsParens = templateSpan.expression.kind !== SyntaxKind.ParenthesizedExpression
&& comparePrecedenceToBinaryPlus(templateSpan.expression) !== Comparison.GreaterThan;
if (i > 0 || headEmitted) {
// If this is the first span and the head was not emitted, then this templateSpan's
// expression will be the first to be emitted. Don't emit the preceding ' + ' in that
// case.
write(" + ");
}
emitParenthesizedIf(templateSpan.expression, needsParens);
// Only emit if the literal is non-empty.
// The binary '+' operator is left-associative, so the first string concatenation
// with the head will force the result up to this point to be a string.
// Emitting a '+ ""' has no semantic effect for middles and tails.
if (templateSpan.literal.text.length !== 0) {
write(" + ");
pushNode(templateSpan.literal);
emitLiteral(templateSpan.literal);
popNode();
}
popNode();
visitNodeWithPushStackBehavior(templateSpan, emitTemplateSpanBelowES6);
}
if (emitOuterParens) {
write(")");
}
}
function shouldEmitTemplateHead(node: TemplateExpression) {
verifyStackBehavior(StackBehavior.Unspecified, node);
// If this expression has an empty head literal and the first template span has a non-empty
// literal, then emitting the empty head literal is not necessary.
// `${ foo } and ${ bar }`
// can be emitted as
// foo + " and " + bar
// This is because it is only required that one of the first two operands in the emit
// output must be a string literal, so that the other operand and all following operands
// are forced into strings.
//
// If the first template span has an empty literal, then the head must still be emitted.
// `${ foo }${ bar }`
// must still be emitted as
// "" + foo + bar
function shouldEmitTemplateHead() {
// If this expression has an empty head literal and the first template span has a non-empty
// literal, then emitting the empty head literal is not necessary.
// `${ foo } and ${ bar }`
// can be emitted as
// foo + " and " + bar
// This is because it is only required that one of the first two operands in the emit
// output must be a string literal, so that the other operand and all following operands
// are forced into strings.
//
// If the first template span has an empty literal, then the head must still be emitted.
// `${ foo }${ bar }`
// must still be emitted as
// "" + foo + bar
// There is always atleast one templateSpan in this code path, since
// NoSubstitutionTemplateLiterals are directly emitted via emitLiteral()
Debug.assert(node.templateSpans.length !== 0);
// There is always atleast one templateSpan in this code path, since
// NoSubstitutionTemplateLiterals are directly emitted via emitLiteral()
Debug.assert(node.templateSpans.length !== 0);
return node.head.text.length !== 0 || node.templateSpans[0].literal.text.length === 0;
}
function templateNeedsParens(template: TemplateExpression) {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, template);
switch (parentNode.kind) {
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return (<CallExpression>parentNode).expression === template;
case SyntaxKind.TaggedTemplateExpression:
case SyntaxKind.ParenthesizedExpression:
return false;
default:
return comparePrecedenceToBinaryPlus(<Expression>parentNode) !== Comparison.LessThan;
}
}
/**
* Returns whether the expression has lesser, greater,
* or equal precedence to the binary '+' operator
*/
function comparePrecedenceToBinaryPlus(expression: Expression): Comparison {
// All binary expressions have lower precedence than '+' apart from '*', '/', and '%'
// which have greater precedence and '-' which has equal precedence.
// All unary operators have a higher precedence apart from yield.
// Arrow functions and conditionals have a lower precedence,
// although we convert the former into regular function expressions in ES5 mode,
// and in ES6 mode this function won't get called anyway.
//
// TODO (drosen): Note that we need to account for the upcoming 'yield' and
// spread ('...') unary operators that are anticipated for ES6.
switch (expression.kind) {
case SyntaxKind.BinaryExpression:
switch ((<BinaryExpression>expression).operatorToken.kind) {
case SyntaxKind.AsteriskToken:
case SyntaxKind.SlashToken:
case SyntaxKind.PercentToken:
return Comparison.GreaterThan;
case SyntaxKind.PlusToken:
case SyntaxKind.MinusToken:
return Comparison.EqualTo;
default:
return Comparison.LessThan;
}
case SyntaxKind.YieldExpression:
case SyntaxKind.ConditionalExpression:
return Comparison.LessThan;
default:
return Comparison.GreaterThan;
}
}
return node.head.text.length !== 0 || node.templateSpans[0].literal.text.length === 0;
}
function templateNeedsParens(template: TemplateExpression) {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, template);
let parentNode = nodeStack.getParent();
switch (parentNode.kind) {
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return (<CallExpression>parentNode).expression === template;
case SyntaxKind.TaggedTemplateExpression:
case SyntaxKind.ParenthesizedExpression:
return false;
default:
return comparePrecedenceToBinaryPlus(<Expression>parentNode) !== Comparison.LessThan;
}
}
function emitTemplateSpan(span: TemplateSpan) {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, span);
@ -1257,6 +1177,33 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
emit(span.literal);
}
function emitTemplateSpanBelowES6(node: TemplateSpan) {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
// Check if the expression has operands and binds its operands less closely than binary '+'.
// If it does, we need to wrap the expression in parentheses. Otherwise, something like
// `abc${ 1 << 2 }`
// becomes
// "abc" + 1 << 2 + ""
// which is really
// ("abc" + 1) << (2 + "")
// rather than
// "abc" + (1 << 2) + ""
let needsParens = node.expression.kind !== SyntaxKind.ParenthesizedExpression
&& comparePrecedenceToBinaryPlus(node.expression) !== Comparison.GreaterThan;
emitParenthesizedIf(node.expression, needsParens);
// Only emit if the literal is non-empty.
// The binary '+' operator is left-associative, so the first string concatenation
// with the head will force the result up to this point to be a string.
// Emitting a '+ ""' has no semantic effect for middles and tails.
if (node.literal.text.length !== 0) {
write(" + ");
visitNodeWithPushStackBehavior(node.literal, emitLiteral);
}
}
function jsxEmitReact(node: JsxElement|JsxSelfClosingElement) {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
@ -1304,7 +1251,22 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
write("true");
}
}
function emitJsxSpreadAttribute(node: JsxSpreadAttribute) {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
emit(node.expression);
}
function emitNonEmptyJsxText(node: JsxText) {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
let text = getTextToEmit(node);
if (text !== undefined) {
write(", \"");
write(text);
write("\"");
}
}
function emitJsxElement(openingNode: JsxOpeningLikeElement, children?: JsxChild[]) {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, openingNode);
@ -1334,7 +1296,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
let haveOpenedObjectLiteral = false;
for (let i = 0; i < attrs.length; i++) {
pushNode(attrs[i]);
if (attrs[i].kind === SyntaxKind.JsxSpreadAttribute) {
// If this is the first argument, we need to emit a {} as the first argument
if (i === 0) {
@ -1348,7 +1309,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
if (i > 0) {
write(", ");
}
emit((<JsxSpreadAttribute>attrs[i]).expression);
visitNodeWithPushStackBehavior(<JsxSpreadAttribute>attrs[i], emitJsxSpreadAttribute);
}
else {
Debug.assert(attrs[i].kind === SyntaxKind.JsxAttribute);
@ -1362,9 +1323,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
write("{");
}
emitJsxAttribute(<JsxAttribute>attrs[i]);
visitNodeWithPushStackBehavior(<JsxAttribute>attrs[i], emitJsxAttribute);
}
popNode();
}
if (haveOpenedObjectLiteral) write("}");
@ -1378,9 +1338,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
write(", ");
}
pushNode(attrs[i]);
emitJsxAttribute(<JsxAttribute>attrs[i]);
popNode();
visitNodeWithPushStackBehavior(attrs[i], emitJsxAttribute);
}
write("}");
}
@ -1396,14 +1354,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
// Don't emit empty strings
if (children[i].kind === SyntaxKind.JsxText) {
pushNode(children[i]);
let text = getTextToEmit(<JsxText>children[i]);
if (text !== undefined) {
write(", \"");
write(text);
write("\"");
}
popNode();
visitNodeWithPushStackBehavior(<JsxText>children[i], emitNonEmptyJsxText);
}
else {
write(", ");
@ -1416,7 +1367,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
write(")"); // closes "React.createElement("
emitTrailingComments(openingNode);
}
if (node.kind === SyntaxKind.JsxElement) {
pushNode((<JsxElement>node).openingElement);
emitJsxElement((<JsxElement>node).openingElement, (<JsxElement>node).children);
@ -1427,7 +1378,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
emitJsxElement(<JsxSelfClosingElement>node);
}
}
function jsxEmitPreserve(node: JsxElement|JsxSelfClosingElement) {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
@ -1567,6 +1518,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
//
// Object.defineProperty(C.prototype, _a, __decorate([dec], C.prototype, _a, Object.getOwnPropertyDescriptor(C.prototype, _a)));
//
let parentNode = nodeStack.getParent();
if (parentNode && nodeIsDecorated(parentNode)) {
if (!computedPropertyNamesToGeneratedNames) {
computedPropertyNamesToGeneratedNames = [];
@ -1591,6 +1543,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
function isExpressionIdentifier(node: Node): boolean {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
let parentNode = nodeStack.getParent();
switch (parentNode.kind) {
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.BinaryExpression:
@ -1707,6 +1660,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
if (languageVersion < ScriptTarget.ES6) {
let parentNode = nodeStack.getParent();
switch (parentNode.kind) {
case SyntaxKind.BindingElement:
case SyntaxKind.ClassDeclaration:
@ -1841,6 +1795,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
function needsParenthesisForAwaitExpressionAsYield(node: AwaitExpression) {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
let parentNode = nodeStack.getParent();
if (parentNode.kind === SyntaxKind.BinaryExpression && !isAssignmentOperator((<BinaryExpression>parentNode).operatorToken.kind)) {
return true;
}
@ -2601,11 +2556,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
function emitParenExpression(node: ParenthesizedExpression) {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
let parent = parentNode;
// If the node is synthesized, it means the emitter put the parentheses there,
// not the user. If we didn't want them, the emitter would not have put them
// there.
let parentNode = nodeStack.getParent();
if (!nodeIsSynthesized(node) && parentNode.kind !== SyntaxKind.ArrowFunction) {
if (node.expression.kind === SyntaxKind.TypeAssertionExpression || node.expression.kind === SyntaxKind.AsExpression) {
let operand = (<TypeAssertion>node.expression).expression;
@ -2630,8 +2584,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
operand.kind !== SyntaxKind.DeleteExpression &&
operand.kind !== SyntaxKind.PostfixUnaryExpression &&
operand.kind !== SyntaxKind.NewExpression &&
!(operand.kind === SyntaxKind.CallExpression && parent.kind === SyntaxKind.NewExpression) &&
!(operand.kind === SyntaxKind.FunctionExpression && parent.kind === SyntaxKind.CallExpression)) {
!(operand.kind === SyntaxKind.CallExpression && parentNode.kind === SyntaxKind.NewExpression) &&
!(operand.kind === SyntaxKind.FunctionExpression && parentNode.kind === SyntaxKind.CallExpression)) {
emit(operand);
return;
}
@ -2674,9 +2628,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
return false;
}
debugger;
const parentNode = currentNode;
const parentNode = nodeStack.getNode();
const isVariableDeclarationOrBindingElement =
parentNode && (parentNode.kind === SyntaxKind.VariableDeclaration || parentNode.kind === SyntaxKind.BindingElement);
@ -2788,21 +2740,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
// node from the checker, so we will be looking at the original source file.
// If `node` is not on top of the stack, then we can get the combined node flags
// by walking the node stack (preferred).
let followParentReferences = node !== currentNode;
let offset = 0;
let current: Node = node;
while (current) {
let navigable = node !== nodeStack.getNode() ? node : nodeStack;
let nav = navigable.createParentNavigator();
do {
let current = nav.getNode();
if (current.kind === SyntaxKind.SourceFile) {
let nodeFlags = followParentReferences ? ts.getCombinedNodeFlags(node) : getCombinedNodeFlags();
let nodeFlags = getCombinedNodeFlags(navigable);
return !isExported || ((nodeFlags & NodeFlags.Export) !== 0);
}
else if (isFunctionLike(current) || current.kind === SyntaxKind.ModuleBlock) {
return false;
}
else {
current = followParentReferences ? current.parent : peekNode(++offset);
}
}
while (nav.moveToParent());
}
function emitBinaryExpression(node: BinaryExpression) {
@ -2810,6 +2760,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
if (languageVersion < ScriptTarget.ES6 && node.operatorToken.kind === SyntaxKind.EqualsToken &&
(node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) {
let parentNode = nodeStack.getParent();
emitDestructuring(node, parentNode.kind === SyntaxKind.ExpressionStatement);
}
else {
@ -2888,6 +2839,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
return;
}
let parentNode = nodeStack.getParent();
emitToken(SyntaxKind.OpenBraceToken, node.pos);
increaseIndent();
scopeEmitStart(parentNode);
@ -3400,7 +3352,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
emitStart(node.name);
if (getCombinedNodeFlags() & NodeFlags.Export) {
if (getCombinedNodeFlags(nodeStack) & NodeFlags.Export) {
let container = findAncestorNode(isModuleDeclaration);
if (container) {
write(getGeneratedNameForNode(container));
@ -3451,6 +3403,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
emitStart(node);
// emit call to exporter only for top level nodes
let parentNode = nodeStack.getParent();
if (compilerOptions.module === ModuleKind.System && parentNode === currentSourceFile) {
// emit export default <smth> as
// export("default", <smth>)
@ -3510,6 +3463,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
function emitExportSpecifierInSystemModule(specifier: ExportSpecifier): void {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, specifier);
Debug.assert(compilerOptions.module === ModuleKind.System);
writeLine();
@ -3534,7 +3488,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
// because actual variable declarations are hoisted
let canDefineTempVariablesInPlace = false;
if (root.kind === SyntaxKind.VariableDeclaration) {
let isExported = getCombinedNodeFlags() & NodeFlags.Export;
let isExported = getCombinedNodeFlags(nodeStack) & NodeFlags.Export;
let isSourceLevelForSystemModuleKind = shouldHoistDeclarationInSystemJsModule(root);
canDefineTempVariablesInPlace = !isExported && !isSourceLevelForSystemModuleKind;
}
@ -3557,8 +3511,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
write(", ");
}
let parentNode = nodeStack.getNode();
const isVariableDeclarationOrBindingElement =
!isTempVariable && (currentNode.kind === SyntaxKind.VariableDeclaration || currentNode.kind === SyntaxKind.BindingElement);
!isTempVariable && (parentNode.kind === SyntaxKind.VariableDeclaration || parentNode.kind === SyntaxKind.BindingElement);
let exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(name);
@ -3569,7 +3524,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
if (isVariableDeclarationOrBindingElement) {
emitModuleMemberName(<Declaration>currentNode);
emitModuleMemberName(<Declaration>parentNode);
}
else {
emit(name);
@ -3736,6 +3691,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
emitDestructuringAssignment(target, value);
}
else {
let parentNode = nodeStack.getParent();
if (parentNode.kind !== SyntaxKind.ParenthesizedExpression) {
write("(");
}
@ -3825,7 +3781,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
// NOTE: default initialization should not be added to let bindings in for-in\for-of statements
if (isUninitializedLet) {
let grandparent = peekNode(2);
let grandparent = nodeStack.getGrandparent();
if (grandparent.kind !== SyntaxKind.ForInStatement &&
grandparent.kind !== SyntaxKind.ForOfStatement) {
initializer = createVoidZero();
@ -3834,6 +3790,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
let exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.name);
visitNodeWithPushStackBehavior
if (exportChanged) {
write(`${exportFunctionForFile}("`);
@ -3872,19 +3829,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
popNode();
}
function getCombinedNodeFlags(): NodeFlags {
return ts.getCombinedNodeFlags(currentNode, peekNode, 0);
}
function getCombinedFlagsForIdentifier(name: Identifier): NodeFlags {
verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, name);
let parent = currentNode;
if (!parent || (parent.kind !== SyntaxKind.VariableDeclaration && parent.kind !== SyntaxKind.BindingElement)) {
let parentNode = nodeStack.getNode();
if (!parentNode || (parentNode.kind !== SyntaxKind.VariableDeclaration && parentNode.kind !== SyntaxKind.BindingElement)) {
return 0;
}
return getCombinedNodeFlags();
return getCombinedNodeFlags(nodeStack);
}
function isES6ExportedDeclaration(node: Node) {
@ -3892,7 +3845,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
return !!(node.flags & NodeFlags.Export) &&
languageVersion >= ScriptTarget.ES6 &&
parentNode.kind === SyntaxKind.SourceFile;
nodeStack.getParent().kind === SyntaxKind.SourceFile;
}
function emitVariableStatement(node: VariableStatement) {
@ -3931,7 +3884,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
}
if (languageVersion < ScriptTarget.ES6 && parentNode === currentSourceFile) {
if (languageVersion < ScriptTarget.ES6 && nodeStack.getParent() === currentSourceFile) {
pushNode(declarationList);
forEach(declarationList.declarations, emitExportVariableAssignmentsOfChild);
popNode();
@ -4182,7 +4135,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
emitSignatureAndBody(node);
if (languageVersion < ScriptTarget.ES6 && node.kind === SyntaxKind.FunctionDeclaration && parentNode === currentSourceFile && node.name) {
if (languageVersion < ScriptTarget.ES6 && node.kind === SyntaxKind.FunctionDeclaration && nodeStack.getParent() === currentSourceFile && node.name) {
emitExportMemberAssignments((<FunctionDeclaration>node).name);
}
@ -5215,7 +5168,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
emitExportMemberAssignment(<ClassDeclaration>node);
}
if (languageVersion < ScriptTarget.ES6 && parentNode === currentSourceFile && node.name) {
if (languageVersion < ScriptTarget.ES6 && nodeStack.getParent() === currentSourceFile && node.name) {
emitExportMemberAssignments(node.name);
}
}
@ -5570,6 +5523,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
function emitSerializedTypeNode(node: TypeNode) {
verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node);
if (!node) {
return;
}
@ -5732,7 +5686,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
let parameter = parameters[i];
pushNode(parameter);
if (parameter.dotDotDotToken) {
let parameterType = parameters[i].type;
if (parameterType.kind === SyntaxKind.ArrayType) {
@ -5745,13 +5698,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
parameterType = undefined;
}
emitSerializedTypeNode(parameterType);
visitNodeWithPushStackBehavior(parameterType, emitSerializedTypeNode);
}
else {
emitSerializedTypeOfNode(parameters[i]);
visitNodeWithPushStackBehavior(parameters[i], emitSerializedTypeOfNode);
}
}
popNode();
}
}
}
@ -5878,7 +5830,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
emitEnd(node);
write(";");
}
if (languageVersion < ScriptTarget.ES6 && parentNode === currentSourceFile) {
if (languageVersion < ScriptTarget.ES6 && nodeStack.getParent() === currentSourceFile) {
if (compilerOptions.module === ModuleKind.System && (node.flags & NodeFlags.Export)) {
// write the call to exporter for enum
writeLine();
@ -5895,7 +5847,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
function emitEnumMember(node: EnumMember) {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
let enumParent = <EnumDeclaration>parentNode;
let enumParent = <EnumDeclaration>nodeStack.getParent();
emitStart(node);
write(getGeneratedNameForNode(enumParent));
write("[");
@ -6014,7 +5966,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
emitModuleMemberName(node);
write(" = {}));");
emitEnd(node);
if (!isES6ExportedDeclaration(node) && node.name.kind === SyntaxKind.Identifier && parentNode === currentSourceFile) {
if (!isES6ExportedDeclaration(node) && node.name.kind === SyntaxKind.Identifier && nodeStack.getParent() === currentSourceFile) {
if (compilerOptions.module === ModuleKind.System && (node.flags & NodeFlags.Export)) {
writeLine();
write(`${exportFunctionForFile}("`);
@ -6505,6 +6457,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
write("}");
}
}
function getLocalNameForExternalImportWithPushStackBehavior(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): string {
verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node);
pushNode(node);
let result = getLocalNameForExternalImport(node);
popNode();
return result;
}
function getLocalNameForExternalImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration): string {
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
@ -6559,9 +6519,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
write(", ");
}
pushNode(importNode);
write(getLocalNameForExternalImport(importNode));
popNode();
write(getLocalNameForExternalImportWithPushStackBehavior(importNode));
}
if (started) {
@ -6805,7 +6763,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
emitDeclarationName(node);
let flags = getCombinedNodeFlags();
let flags = getCombinedNodeFlags(nodeStack);
if (flags & NodeFlags.Export) {
if (!exportedDeclarations) {
exportedDeclarations = [];
@ -6858,8 +6816,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
// - it is top level block scoped
// if block scoped variables are nested in some another block then
// no other functions can use them except ones that are defined at least in the same block
return (getCombinedNodeFlags() & NodeFlags.BlockScoped) === 0 ||
getEnclosingBlockScopeContainer(node, peekNode, /*offset*/ 0).kind === SyntaxKind.SourceFile;
return (getCombinedNodeFlags(nodeStack) & NodeFlags.BlockScoped) === 0 ||
getEnclosingBlockScopeContainer(nodeStack).kind === SyntaxKind.SourceFile;
}
function isCurrentFileSystemExternalModule() {
@ -6938,12 +6896,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
let group = dependencyGroups[i];
// derive a unique name for parameter from the first named entry in the group
let parameterName = makeUniqueName(forEach(group, getLocalNameForExternalImport) || "");
let parameterName = makeUniqueName(forEach(group, getLocalNameForExternalImportWithPushStackBehavior) || "");
write(`function (${parameterName}) {`);
increaseIndent();
for(let entry of group) {
let importVariableName = getLocalNameForExternalImport(entry) || "";
let importVariableName = getLocalNameForExternalImportWithPushStackBehavior(entry) || "";
switch (entry.kind) {
case SyntaxKind.ImportDeclaration:
@ -7014,7 +6972,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
write("}");
decreaseIndent();
popNode();
}
write("],");
}
@ -7037,10 +6994,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
continue;
case SyntaxKind.ExportDeclaration:
if (!(<ExportDeclaration>statement).moduleSpecifier) {
pushNode(statement);
pushNode((<ExportDeclaration>statement).exportClause);
for (let element of (<ExportDeclaration>statement).exportClause.elements) {
// write call to exporter function for every export specifier in exports list
pushNode(element);
emitExportSpecifierInSystemModule(element);
popNode();
}
popNode();
popNode();
}
continue;
case SyntaxKind.ImportEqualsDeclaration:
@ -7090,6 +7053,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
let externalImport = externalImports[i];
pushNode(externalImport);
let text = getExternalModuleNameText(externalImport);
popNode();
if (hasProperty(groupIndices, text)) {
// deduplicate/group entries in dependency list by the dependency name
let groupIndex = groupIndices[text];
@ -7106,8 +7070,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
write(text);
popNode();
}
write(`], function(${exportFunctionForFile}) {`);
writeLine();
increaseIndent();
@ -7498,26 +7462,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
emitTempDeclarations(/*newLine*/ true);
}
pushNode(node.endOfFileToken);
emitLeadingComments(node.endOfFileToken);
popNode();
visitNodeWithPushStackBehavior(node.endOfFileToken, emitLeadingComments);
}
function emitNodeWithPushStackBehavior(node: Node, emitNodeWithoutStackBehavior: (node: Node, emitNode: (node: Node) => void) => void, emitNode?: (node: Node) => void): void {
function visitNodeWithPushStackBehavior<T extends Node, U>(node: T, visit: (node: T) => U): U;
function visitNodeWithPushStackBehavior<T extends Node, U>(node: T, pipe: (node: T, emitNode: (node: T) => U) => U, visit?: (node: T) => U): U;
function visitNodeWithPushStackBehavior<T extends Node, U>(node: T, pipe: (node: T, emitNode?: (node: T) => U) => U, visit?: (node: T) => U): U {
let result: U;
if (node) {
verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node);
let debugStackSize = nodeStackSize;
let debugParentNode = parentNode;
let debugCurrentNode = currentNode;
pushNode(node);
emitNodeWithoutStackBehavior(node, emitNode);
result = visit ? pipe(node, visit) : pipe(node);
popNode();
Debug.assert(debugStackSize === nodeStackSize
&& debugParentNode === parentNode
&& debugCurrentNode === currentNode, "Incorrect stack behavior. Node stack after emit does not match node stack before emit.");
}
return result;
}
function emitNodeWithCommentsAndWithoutSourcemap(node: Node): void {
@ -7529,7 +7488,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
verifyStackBehavior(StackBehavior.ParentIsOnTopOfStack, node);
if (node.flags & NodeFlags.Ambient) {
return emitNodeWithPushStackBehavior(node, emitOnlyPinnedOrTripleSlashComments);
return visitNodeWithPushStackBehavior(node, emitOnlyPinnedOrTripleSlashComments);
}
if (isSpecializedCommentHandling(node)) {
@ -7537,11 +7496,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
return emitNodeWithoutSourceMap(node);
}
emitNodeWithPushStackBehavior(node, emitNodeWithCommentsAndWithoutStackBehavior, emitNodeConsideringSourcemapWithoutStackBehavior);
visitNodeWithPushStackBehavior(node, emitNodeWithCommentsAndWithoutStackBehavior, emitNodeConsideringSourcemapWithoutStackBehavior);
}
}
function emitNodeWithCommentsAndWithoutStackBehavior(node: Node, emitNodeConsideringSourcemapWithoutStackBehavior: (node: Node) => void): void {
verifyStackBehavior(StackBehavior.Unspecified, node);
let emitComments = shouldEmitLeadingAndTrailingComments(node);
if (emitComments) {
emitLeadingComments(node);
@ -7556,7 +7517,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
function emitNodeWithoutSourceMap(node: Node): void {
if (node) {
emitNodeWithPushStackBehavior(node, emitNodeWithoutSourceMapAndWithoutStackBehavior);
visitNodeWithPushStackBehavior(node, emitNodeWithoutSourceMapAndWithoutStackBehavior);
}
}
@ -7606,6 +7567,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
// then we don't want to emit comments when we emit the body. It will have already
// been taken care of when we emitted the 'return' statement for the function
// expression body.
let parentNode = nodeStack.getParent();
if (node.kind !== SyntaxKind.Block &&
parentNode &&
parentNode.kind === SyntaxKind.ArrowFunction &&
@ -7838,6 +7800,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
// Emit the leading comments only if the parent's pos doesn't match because parent should take care of emitting these comments
let parentNode = nodeStack.getParent();
if (parentNode) {
if (parentNode.kind === SyntaxKind.SourceFile || node.pos !== parentNode.pos) {
if (hasDetachedComments(node.pos)) {
@ -7856,6 +7819,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
verifyStackBehavior(StackBehavior.NodeIsOnTopOfStack, node);
// Emit the trailing comments only if the parent's pos doesn't match because parent should take care of emitting these comments
let parentNode = nodeStack.getParent();
if (parentNode && !nodeIsSynthesized(parentNode)) {
if (parentNode.kind === SyntaxKind.SourceFile || node.end !== parentNode.end) {
return getTrailingCommentRanges(currentSourceFile.text, node.end);
@ -8001,6 +7965,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
function verifyStackBehavior(behavior: StackBehavior, node?: Node) {
let currentNode = nodeStack.getNode();
switch (behavior) {
case StackBehavior.Unspecified:
return;

View File

@ -441,6 +441,54 @@ namespace ts {
FailedAndReported = 3
}
// @internal
export interface ParentNavigable {
/** Creates an object used to navigate parent nodes of this node. */
createParentNavigator(): ParentNavigator;
}
// @internal
export interface ParentNavigator extends ParentNavigable {
/** Gets the root node for the current node. */
getRoot(): Node;
/** Gets the grandparent node for the current node. */
getGrandparent(): Node;
/** Gets the parent node for the current node. */
getParent(): Node;
/** Gets the current node. */
getNode(): Node;
/** Gets the SyntaxKind of the current node. */
getKind(): SyntaxKind;
/** Moves the navigator to the parent node of the current node. */
moveToParent(): boolean;
/** Moves the navigator to the root node of the current node. */
moveToRoot(): boolean;
}
// @internal
export interface NodeStack extends ParentNavigable {
/** Gets the node at the bottom of the stack. */
getRoot(): Node;
/** Gets the node two steps down from the top of the stack. */
getGrandparent(): Node;
/** Gets the node one step down from the top of the stack. */
getParent(): Node;
/** Gets the node at the top of the stack. */
getNode(): Node;
/** Gets the SyntaxKind for the node at the top of the stack. */
getKind(): SyntaxKind;
/** Traverses the stack from top to bottom until it finds a node that matches the supplied predicate. */
findAncestorNode<T extends Node>(match: (node: Node) => node is T): T;
/** Traverses the stack from top to bottom until it finds a node that matches the supplied predicate. */
findAncestorNode(match: (node: Node) => boolean): Node;
/** Pushes a node onto the stack. */
pushNode(node: Node): void;
/** Replaces the node at the top of the stack. */
setNode(node: Node): void;
/** Pops the top node from the stack. */
popNode(): void;
}
export interface Node extends TextRange {
kind: SyntaxKind;
flags: NodeFlags;
@ -456,6 +504,7 @@ namespace ts {
/* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding)
/* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding)
/* @internal */ localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes)
/* @internal */ createParentNavigator(): ParentNavigator;
}
export interface NodeArray<T> extends Array<T>, TextRange {

View File

@ -243,6 +243,249 @@ namespace ts {
return getBaseFileName(moduleName).replace(/^(\d)/, "_$1").replace(/\W/g, "_");
}
/**
* Creates an object used to navigate the ancestor's of a node by following parent pointers.
* @param currentNode The current node for the navigator.
*/
export function createParentNavigator(currentNode: Node): ParentNavigator {
/** Traverses the parent pointers for the current node to find the root node. */
function getRoot() {
let rootNode = currentNode;
while (rootNode && rootNode.parent) {
rootNode = rootNode.parent;
}
return rootNode;
}
/** Gets the grandparent of the current node, without moving the navigator. */
function getGrandparent() {
let parent = getParent();
return parent ? parent.parent : undefined;
}
/** Gets the parent of the current node, without moving the navigator. */
function getParent() {
return currentNode ? currentNode.parent : undefined;
}
/** Gets the current node. */
function getNode() {
return currentNode;
}
/** Gets the SyntaxKind for the current node. */
function getKind() {
return currentNode ? currentNode.kind : undefined;
}
/** Navigates to the parent of the current node if it has one. */
function moveToParent(): boolean {
let parent = getParent();
if (parent) {
currentNode = parent;
return true;
}
return false;
}
/** Navigates to the root node for the current node. */
function moveToRoot(): boolean {
let rootNode = getRoot();
if (rootNode) {
currentNode = rootNode;
return true;
}
return false;
}
/** Creates a new ParentNavigator from the current node. */
function createParentNavigator() {
return ts.createParentNavigator(currentNode);
}
return {
getRoot,
getGrandparent,
getParent,
getNode,
getKind,
moveToParent,
moveToRoot,
createParentNavigator,
};
}
/**
* Creates a node stack used to maintain parent relationships without parent pointers.
*/
export function createNodeStack(): NodeStack {
let stackSize: number = 0;
let stack: Node[] = [];
let rootNode: Node;
let parentNode: Node;
let currentNode: Node;
/** Gets the node at the bottom of the stack. */
function getRoot() {
return rootNode;
}
/** Gets the node two steps back from the top of the stack. */
function getGrandparent() {
return peekNode(2);
}
/** Gets the node one step back from the top of the stack. */
function getParent() {
return parentNode;
}
/** Gets the node at the top of the stack. */
function getNode() {
return currentNode;
}
/** Gets the SyntaxKind for the node at the top of the stack. */
function getKind() {
return currentNode ? currentNode.kind : undefined;
}
/** Pushes a node onto the stack. */
function pushNode(node: Node): void {
stackSize++;
if (stackSize > 2) {
stack.push(parentNode);
}
else if (stackSize === 1) {
rootNode = node;
}
parentNode = currentNode;
currentNode = node;
}
/** Pops the top node from the stack. */
function popNode(): void {
currentNode = parentNode;
parentNode = stackSize > 2 ? stack.pop() : undefined;
stackSize--;
if (stackSize === 0) {
rootNode = undefined;
}
}
/** Replaces the node at the top of the stack. */
function setNode(node: Node): void {
currentNode = node;
}
/** Peeks at a node a specified number of steps back from the top of the stack. */
function peekNode(stackOffset: number): Node {
switch (stackOffset) {
case 0: return currentNode;
case 1: return parentNode;
default: return stackSize > 2 ? stack[stackSize - 1 - stackOffset] : undefined;
}
}
/** Traverses the stack from top to bottom until it finds a node that matches the supplied predicate. */
function findAncestorNode<T extends Node>(match: (node: Node) => node is T): T;
/** Traverses the stack from top to bottom until it finds a node that matches the supplied predicate. */
function findAncestorNode(match: (node: Node) => boolean): Node;
function findAncestorNode(match: (node: Node) => boolean) {
if (parentNode && match(parentNode)) {
return parentNode;
}
for (let i = stack.length; i >= 0; i--) {
let node = stack[i];
if (match(node)) {
return node;
}
}
return undefined;
}
/** Creates a parent navigator from the top of the stack. */
function createParentNavigator() {
return createParentNavigatorFromStackOffset(0);
}
/** Creates a parent navigator a specified number of steps back from the top of the stack. */
function createParentNavigatorFromStackOffset(stackOffset: number): ParentNavigator {
/** Gets the node two steps back from the current stack offset. */
function getGrandparent() {
return peekNode(stackOffset + 2);
}
/** Gets the node one step back from the current stack offset. */
function getParent() {
return peekNode(stackOffset + 1);
}
/** Gets the node at the current stack offset. */
function getNode() {
return peekNode(stackOffset);
}
/** Gets the SyntaxKind of the node at the current stack offset. */
function getKind() {
let node = getNode();
return node ? node.kind : undefined;
}
/** Navigates to the node one step back from the current stack offset. */
function moveToParent() {
if (getParent()) {
stackOffset++;
return true;
}
return false;
}
/** Navigates to the node at the bottom of the stack. */
function moveToRoot() {
if (stackSize > 0) {
stackOffset = stackSize;
return true;
}
return false;
}
/** Creates a new ParentNavigator from the current stack offset. */
function createParentNavigator() {
return createParentNavigatorFromStackOffset(stackOffset);
}
return {
getRoot,
getGrandparent,
getParent,
getNode,
getKind,
moveToRoot,
moveToParent,
createParentNavigator,
};
}
return {
getRoot,
getGrandparent,
getParent,
getNode,
getKind,
pushNode,
popNode,
setNode,
findAncestorNode,
createParentNavigator,
};
}
export function isBlockOrCatchScoped(declaration: Declaration) {
return (getCombinedNodeFlags(declaration) & NodeFlags.BlockScoped) !== 0 ||
isCatchClauseVariableDeclaration(declaration);
@ -250,38 +493,10 @@ namespace ts {
// Gets the nearest enclosing block scope container that has the provided node
// as a descendant, that is not the provided node.
export function getEnclosingBlockScopeContainer(node: Node): Node;
/**
* Gets the nearest enclosing block scope container that has the provided node
* as a descendant, that is not the provided node.
* @param node The starting node
* @param getAncestorOrSelf A callback used to get the ancestor of the starting node, used
* only when traversing the ancestors of the current node in the emitter.
* @param offset The offset in the node stack used to get the ancestor of the current node
* from the emitter's node stack.
*/
export function getEnclosingBlockScopeContainer(node: Node, getAncestorOrSelf: (offset: number) => Node, offset: number): Node;
/**
* Gets the nearest enclosing block scope container that has the provided node
* as a descendant, that is not the provided node.
* @param node The starting node
* @param getAncestorOrSelf A callback used to get the ancestor of the starting node, used
* only when traversing the ancestors of the current node in the emitter.
* @param offset The offset in the node stack used to get the ancestor of the current node
* from the emitter's node stack.
* @remarks
* The emitter tracks the parent of the current node as it descends into the source file,
* so that we can properly emit synthesized nodes that may not have parent pointers.
* We can call `getAncestorOrSelf` with an offset that specifies how far back in the node's
* ancestry to retrieve a parent, grandparent, etc. An offset of zero (0) refers to the
* current node on the top of the node stack, an offset of one (1) refers to its parent, an
* offset of two (2) refers to the grandparent, and so on.
*/
export function getEnclosingBlockScopeContainer(node: Node, getAncestorOrSelf?: (offset: number) => Node, offset?: number): Node {
let current = getAncestorOrSelf ? getAncestorOrSelf(++offset) : node.parent;
while (current) {
export function getEnclosingBlockScopeContainer(navigable: ParentNavigable): Node {
let nav = navigable.createParentNavigator();
while (nav.moveToParent()) {
let current = nav.getNode();
if (isFunctionLike(current)) {
return current;
}
@ -297,12 +512,10 @@ namespace ts {
case SyntaxKind.Block:
// function block is not considered block-scope container
// see comment in binder.ts: bind(...), case for SyntaxKind.Block
if (!isFunctionLike(getAncestorOrSelf ? getAncestorOrSelf(offset + 1) : current.parent)) {
if (!isFunctionLike(nav.getParent())) {
return current;
}
}
current = getAncestorOrSelf ? getAncestorOrSelf(++offset) : current.parent;
}
}
@ -397,56 +610,22 @@ namespace ts {
return node.kind === SyntaxKind.EnumDeclaration && isConst(node);
}
/**
* Returns the node flags for this node and all relevant parent nodes.
*/
export function getCombinedNodeFlags(node: Node): NodeFlags;
/**
* Returns the node flags for this node and all relevant parent nodes.
* @param node The starting node
* @param getAncestorOrSelf A callback used to get the ancestor of the starting node, used
* only when traversing the ancestors of the current node in the emitter.
* @param offset The offset in the node stack used to get the ancestor of the current node
* from the emitter's node stack.
*/
export function getCombinedNodeFlags(node: Node, getAncestorOrSelf: (offset: number) => Node, offset: number): NodeFlags;
/**
* Returns the node flags for this node and all relevant parent nodes.
* @param node The starting node
* @param getAncestorOrSelf A callback used to get the ancestor of the starting node, used
* only when traversing the ancestors of the current node in the emitter.
* @param offset The offset in the node stack used to get the ancestor of the current node
* from the emitter's node stack.
* @remarks
* This is done so that nodes like variable declarations and binding elements can return
* a view of their flags that includes the modifiers from their container. i.e. flags like
* export/declare aren't stored on the variable declaration directly, but on the containing
* variable statement (if it has one). Similarly, flags for let/const are store on the
* variable declaration list. By calling this function, all those flags are combined so
* that the client can treat the node as if it actually had those flags.
*
* The emitter tracks the parent of the current node as it descends into the source file,
* so that we can properly emit synthesized nodes that may not have parent pointers.
* We can call `getAncestorOrSelf` with an offset that specifies how far back in the node's
* ancestry to retrieve a parent, grandparent, etc. An offset of zero (0) refers to the
* current node on the top of the node stack, an offset of one (1) refers to its parent, an
* offset of two (2) refers to the grandparent, and so on.
*/
export function getCombinedNodeFlags(node: Node, getAncestorOrSelf?: (offset: number) => Node, offset?: number): NodeFlags {
/** Returns the node flags for this node and all relevant parent nodes. */
export function getCombinedNodeFlags(navigable: ParentNavigable): NodeFlags {
let nav = navigable.createParentNavigator();
let node = nav.getNode();
while (node && (node.kind === SyntaxKind.BindingElement || isBindingPattern(node))) {
node = getAncestorOrSelf ? getAncestorOrSelf(++offset) : node.parent;
node = nav.moveToParent() ? nav.getNode() : undefined;
}
let flags = node.flags;
if (node.kind === SyntaxKind.VariableDeclaration) {
node = getAncestorOrSelf ? getAncestorOrSelf(++offset) : node.parent;
node = nav.moveToParent() ? nav.getNode() : undefined;
}
if (node && node.kind === SyntaxKind.VariableDeclarationList) {
flags |= node.flags;
node = getAncestorOrSelf ? getAncestorOrSelf(++offset) : node.parent;
node = nav.moveToParent() ? nav.getNode() : undefined;
}
if (node && node.kind === SyntaxKind.VariableStatement) {
@ -456,12 +635,12 @@ namespace ts {
return flags;
}
export function isConst(node: Node): boolean {
return !!(getCombinedNodeFlags(node) & NodeFlags.Const);
export function isConst(navigable: ParentNavigable): boolean {
return !!(getCombinedNodeFlags(navigable) & NodeFlags.Const);
}
export function isLet(node: Node): boolean {
return !!(getCombinedNodeFlags(node) & NodeFlags.Let);
export function isLet(navigable: ParentNavigable): boolean {
return !!(getCombinedNodeFlags(navigable) & NodeFlags.Let);
}
export function isPrologueDirective(node: Node): boolean {
@ -716,37 +895,41 @@ namespace ts {
return node && node.kind === SyntaxKind.MethodDeclaration && node.parent.kind === SyntaxKind.ObjectLiteralExpression;
}
export function getContainingFunction(node: Node): FunctionLikeDeclaration {
while (true) {
node = node.parent;
if (!node || isFunctionLike(node)) {
export function getContainingFunction(navigable: ParentNavigable): FunctionLikeDeclaration {
let nav = navigable.createParentNavigator();
while (nav.moveToParent()) {
let node = nav.getNode();
if (isFunctionLike(node)) {
return <FunctionLikeDeclaration>node;
}
}
return undefined;
}
export function getContainingClass(node: Node): ClassLikeDeclaration {
while (true) {
node = node.parent;
if (!node || isClassLike(node)) {
export function getContainingClass(navigable: ParentNavigable): ClassLikeDeclaration {
let nav = navigable.createParentNavigator();
while (nav.moveToParent()) {
let node = nav.getNode();
if (isClassLike(node)) {
return <ClassLikeDeclaration>node;
}
}
return undefined;
}
export function getThisContainer(node: Node, includeArrowFunctions: boolean): Node {
while (true) {
node = node.parent;
if (!node) {
return undefined;
}
export function getThisContainer(navigable: ParentNavigable, includeArrowFunctions: boolean): Node {
let nav = navigable.createParentNavigator();
while (nav.moveToParent()) {
let node = nav.getNode();
switch (node.kind) {
case SyntaxKind.ComputedPropertyName:
// If the grandparent node is an object literal (as opposed to a class),
// then the computed property is not a 'this' container.
// A computed property name in a class needs to be a this container
// so that we can error on it.
if (isClassLike(node.parent.parent)) {
if (isClassLike(nav.getGrandparent())) {
return node;
}
// If this is a computed property, then the parent should not
@ -754,19 +937,20 @@ namespace ts {
// in an object literal, like a method or accessor. But in order for
// such a parent to be a this container, the reference must be in
// the *body* of the container.
node = node.parent;
nav.moveToParent();
break;
case SyntaxKind.Decorator:
// Decorators are always applied outside of the body of a class or method.
if (node.parent.kind === SyntaxKind.Parameter && isClassElement(node.parent.parent)) {
if (nav.getParent().kind === SyntaxKind.Parameter && isClassElement(nav.getGrandparent())) {
// If the decorator's parent is a Parameter, we resolve the this container from
// the grandparent class declaration.
node = node.parent.parent;
nav.moveToParent();
nav.moveToParent();
}
else if (isClassElement(node.parent)) {
else if (isClassElement(nav.getParent())) {
// If the decorator's parent is a class element, we resolve the 'this' container
// from the parent class declaration.
node = node.parent;
nav.moveToParent();
}
break;
case SyntaxKind.ArrowFunction:
@ -791,17 +975,17 @@ namespace ts {
}
}
export function getSuperContainer(node: Node, includeFunctions: boolean): Node {
while (true) {
node = node.parent;
if (!node) return node;
export function getSuperContainer(navigable: ParentNavigable, includeFunctions: boolean): Node {
let nav = navigable.createParentNavigator();
while (nav.moveToParent()) {
let node = nav.getNode();
switch (node.kind) {
case SyntaxKind.ComputedPropertyName:
// If the grandparent node is an object literal (as opposed to a class),
// then the computed property is not a 'super' container.
// A computed property name in a class needs to be a super container
// so that we can error on it.
if (isClassLike(node.parent.parent)) {
if (isClassLike(nav.getGrandparent())) {
return node;
}
// If this is a computed property, then the parent should not
@ -809,19 +993,20 @@ namespace ts {
// in an object literal, like a method or accessor. But in order for
// such a parent to be a super container, the reference must be in
// the *body* of the container.
node = node.parent;
nav.moveToParent();
break;
case SyntaxKind.Decorator:
// Decorators are always applied outside of the body of a class or method.
if (node.parent.kind === SyntaxKind.Parameter && isClassElement(node.parent.parent)) {
if (nav.getParent().kind === SyntaxKind.Parameter && isClassElement(nav.getGrandparent())) {
// If the decorator's parent is a Parameter, we resolve the this container from
// the grandparent class declaration.
node = node.parent.parent;
nav.moveToParent();
nav.moveToParent();
}
else if (isClassElement(node.parent)) {
else if (isClassElement(nav.getParent())) {
// If the decorator's parent is a class element, we resolve the 'this' container
// from the parent class declaration.
node = node.parent;
nav.moveToParent();
}
break;
case SyntaxKind.FunctionDeclaration:
@ -867,7 +1052,9 @@ namespace ts {
return (<CallExpression | Decorator>node).expression;
}
export function nodeCanBeDecorated(node: Node): boolean {
export function nodeCanBeDecorated(navigable: ParentNavigable): boolean {
let nav = navigable.createParentNavigator();
let node = nav.getNode();
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
// classes are valid targets
@ -875,17 +1062,17 @@ namespace ts {
case SyntaxKind.PropertyDeclaration:
// property declarations are valid if their parent is a class declaration.
return node.parent.kind === SyntaxKind.ClassDeclaration;
return nav.getParent().kind === SyntaxKind.ClassDeclaration;
case SyntaxKind.Parameter:
// if the parameter's parent has a body and its grandparent is a class declaration, this is a valid target;
return (<FunctionLikeDeclaration>node.parent).body && node.parent.parent.kind === SyntaxKind.ClassDeclaration;
return (<FunctionLikeDeclaration>nav.getParent()).body && nav.getGrandparent().kind === SyntaxKind.ClassDeclaration;
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.MethodDeclaration:
// if this method has a body and its parent is a class declaration, this is a valid target.
return (<FunctionLikeDeclaration>node).body && node.parent.kind === SyntaxKind.ClassDeclaration;
return (<FunctionLikeDeclaration>node).body && nav.getParent().kind === SyntaxKind.ClassDeclaration;
}
return false;
@ -944,142 +1131,151 @@ namespace ts {
return nodeIsDecorated(node) || childIsDecorated(node);
}
/**
* Returns whether the node is part of an expression.
* @param node The node to test
*/
export function isExpression(node: Node): boolean;
/**
* Returns whether the node is part of an expression.
* @param node The node to test
* @param getAncestorOrSelf A callback used to get the ancestor of the starting node, used
* only when traversing the ancestors of the current node in the emitter.
* @param offset The offset in the node stack used to get the ancestor of the current node
* from the emitter's node stack.
*/
export function isExpression(node: Node, getAncestorOrSelf: (offset: number) => Node, offset: number): boolean;
/**
* Returns whether the node is part of an expression.
* @param node The node to test
* @param getAncestorOrSelf A callback used to get the ancestor of the starting node, used
* only when traversing the ancestors of the current node in the emitter.
* @param offset The offset in the node stack used to get the ancestor of the current node
* from the emitter's node stack.
* @remarks
* The emitter tracks the parent of the current node as it descends into the source file,
* so that we can properly emit synthesized nodes that may not have parent pointers.
* We can call `getAncestorOrSelf` with an offset that specifies how far back in the node's
* ancestry to retrieve a parent, grandparent, etc. An offset of zero (0) refers to the
* current node on the top of the node stack, an offset of one (1) refers to its parent, an
* offset of two (2) refers to the grandparent, and so on.
*/
export function isExpression(node: Node, getAncestorOrSelf?: (offset: number) => Node, offset?: number): boolean {
let parent: Node;
switch (node.kind) {
case SyntaxKind.ThisKeyword:
case SyntaxKind.SuperKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.RegularExpressionLiteral:
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.ObjectLiteralExpression:
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
case SyntaxKind.TaggedTemplateExpression:
case SyntaxKind.AsExpression:
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.ParenthesizedExpression:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ClassExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.VoidExpression:
case SyntaxKind.DeleteExpression:
case SyntaxKind.TypeOfExpression:
case SyntaxKind.PrefixUnaryExpression:
case SyntaxKind.PostfixUnaryExpression:
case SyntaxKind.BinaryExpression:
case SyntaxKind.ConditionalExpression:
case SyntaxKind.SpreadElementExpression:
case SyntaxKind.TemplateExpression:
case SyntaxKind.NoSubstitutionTemplateLiteral:
case SyntaxKind.OmittedExpression:
case SyntaxKind.JsxElement:
case SyntaxKind.JsxSelfClosingElement:
case SyntaxKind.YieldExpression:
return true;
case SyntaxKind.QualifiedName:
parent = getAncestorOrSelf ? getAncestorOrSelf(++offset) : node.parent;
while (parent.kind === SyntaxKind.QualifiedName) {
node = parent;
parent = getAncestorOrSelf ? getAncestorOrSelf(++offset) : node.parent;
}
return parent.kind === SyntaxKind.TypeQuery;
case SyntaxKind.Identifier:
parent = getAncestorOrSelf ? getAncestorOrSelf(offset + 1) : node.parent;
if (parent.kind === SyntaxKind.TypeQuery) {
/** Returns whether the node is part of an expression. */
export function isExpression(navigable: ParentNavigable): boolean {
let nav = navigable.createParentNavigator();
while (true) {
let node = nav.getNode();
switch (node.kind) {
case SyntaxKind.ThisKeyword:
case SyntaxKind.SuperKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.RegularExpressionLiteral:
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.ObjectLiteralExpression:
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
case SyntaxKind.TaggedTemplateExpression:
case SyntaxKind.AsExpression:
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.ParenthesizedExpression:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ClassExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.VoidExpression:
case SyntaxKind.DeleteExpression:
case SyntaxKind.TypeOfExpression:
case SyntaxKind.PrefixUnaryExpression:
case SyntaxKind.PostfixUnaryExpression:
case SyntaxKind.BinaryExpression:
case SyntaxKind.ConditionalExpression:
case SyntaxKind.SpreadElementExpression:
case SyntaxKind.TemplateExpression:
case SyntaxKind.NoSubstitutionTemplateLiteral:
case SyntaxKind.OmittedExpression:
case SyntaxKind.JsxElement:
case SyntaxKind.JsxSelfClosingElement:
case SyntaxKind.YieldExpression:
return true;
}
// fall through
case SyntaxKind.NumericLiteral:
case SyntaxKind.StringLiteral:
parent = getAncestorOrSelf ? getAncestorOrSelf(offset + 1) : node.parent;
switch (parent.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Parameter:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.EnumMember:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.BindingElement:
return (<VariableLikeDeclaration>parent).initializer === node;
case SyntaxKind.ExpressionStatement:
case SyntaxKind.IfStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.ReturnStatement:
case SyntaxKind.WithStatement:
case SyntaxKind.SwitchStatement:
case SyntaxKind.CaseClause:
case SyntaxKind.ThrowStatement:
case SyntaxKind.SwitchStatement:
return (<ExpressionStatement>parent).expression === node;
case SyntaxKind.ForStatement:
let forStatement = <ForStatement>parent;
return (forStatement.initializer === node && forStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
forStatement.condition === node ||
forStatement.incrementor === node;
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
let forInStatement = <ForInStatement | ForOfStatement>parent;
return (forInStatement.initializer === node && forInStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
forInStatement.expression === node;
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.AsExpression:
return node === (<AssertionExpression>parent).expression;
case SyntaxKind.TemplateSpan:
return node === (<TemplateSpan>parent).expression;
case SyntaxKind.ComputedPropertyName:
return node === (<ComputedPropertyName>parent).expression;
case SyntaxKind.Decorator:
case SyntaxKind.JsxExpression:
return true;
case SyntaxKind.ExpressionWithTypeArguments:
return (<ExpressionWithTypeArguments>parent).expression === node && isExpressionWithTypeArgumentsInClassExtendsClause(parent, getAncestorOrSelf, offset + 1);
default:
if (isExpression(parent, getAncestorOrSelf, offset)) {
return true;
case SyntaxKind.QualifiedName:
while (nav.moveToParent()) {
if (nav.getKind() !== SyntaxKind.QualifiedName) {
break;
}
}
}
return nav.getKind() === SyntaxKind.TypeQuery;
case SyntaxKind.Identifier:
if (nav.getParent().kind === SyntaxKind.TypeQuery) {
return true;
}
// fall through
case SyntaxKind.NumericLiteral:
case SyntaxKind.StringLiteral:
nav.moveToParent();
let parent = nav.getNode();
switch (parent.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Parameter:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.EnumMember:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.BindingElement:
return (<VariableLikeDeclaration>parent).initializer === node;
case SyntaxKind.ExpressionStatement:
case SyntaxKind.IfStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.ReturnStatement:
case SyntaxKind.WithStatement:
case SyntaxKind.SwitchStatement:
case SyntaxKind.CaseClause:
case SyntaxKind.ThrowStatement:
case SyntaxKind.SwitchStatement:
return (<ExpressionStatement>parent).expression === node;
case SyntaxKind.ForStatement:
let forStatement = <ForStatement>parent;
return (forStatement.initializer === node && forStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
forStatement.condition === node ||
forStatement.incrementor === node;
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
let forInStatement = <ForInStatement | ForOfStatement>parent;
return (forInStatement.initializer === node && forInStatement.initializer.kind !== SyntaxKind.VariableDeclarationList) ||
forInStatement.expression === node;
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.AsExpression:
return node === (<AssertionExpression>parent).expression;
case SyntaxKind.TemplateSpan:
return node === (<TemplateSpan>parent).expression;
case SyntaxKind.ComputedPropertyName:
return node === (<ComputedPropertyName>parent).expression;
case SyntaxKind.Decorator:
case SyntaxKind.JsxExpression:
return true;
case SyntaxKind.ExpressionWithTypeArguments:
return (<ExpressionWithTypeArguments>parent).expression === node && isExpressionWithTypeArgumentsInClassExtendsClause(nav);
default:
continue;
}
}
return false;
}
return false;
}
/**
* Returns whether the expression has lesser, greater,
* or equal precedence to the binary '+' operator
*/
export function comparePrecedenceToBinaryPlus(expression: Expression): Comparison {
// All binary expressions have lower precedence than '+' apart from '*', '/', and '%'
// which have greater precedence and '-' which has equal precedence.
// All unary operators have a higher precedence apart from yield.
// Arrow functions and conditionals have a lower precedence,
// although we convert the former into regular function expressions in ES5 mode,
// and in ES6 mode this function won't get called anyway.
//
// TODO (drosen): Note that we need to account for the upcoming 'yield' and
// spread ('...') unary operators that are anticipated for ES6.
switch (expression.kind) {
case SyntaxKind.BinaryExpression:
switch ((<BinaryExpression>expression).operatorToken.kind) {
case SyntaxKind.AsteriskToken:
case SyntaxKind.SlashToken:
case SyntaxKind.PercentToken:
return Comparison.GreaterThan;
case SyntaxKind.PlusToken:
case SyntaxKind.MinusToken:
return Comparison.EqualTo;
default:
return Comparison.LessThan;
}
case SyntaxKind.YieldExpression:
case SyntaxKind.ConditionalExpression:
return Comparison.LessThan;
default:
return Comparison.GreaterThan;
}
}
export function isInstantiatedModule(node: ModuleDeclaration, preserveConstEnums: boolean) {
let moduleState = getModuleInstanceState(node);
return moduleState === ModuleInstanceState.Instantiated ||
@ -2103,25 +2299,12 @@ namespace ts {
/**
* Tests whether the node is an ExpressionWithTypeArguments node that is part of the `extends`
* clause of a class.
* @param node The node to test
* @param getAncestorOrSelf A callback used to get the ancestor of the starting node, used
* only when traversing the ancestors of the current node in the emitter.
* @param offset The offset in the node stack used to get the ancestor of the current node
* from the emitter's node stack.
* @remarks
* The emitter tracks the parent of the current node as it descends into the source file,
* so that we can properly emit synthesized nodes that may not have parent pointers.
* We can call `getAncestorOrSelf` with an offset that specifies how far back in the node's
* ancestry to retrieve a parent, grandparent, etc. An offset of zero (0) refers to the
* current node on the top of the node stack, an offset of one (1) refers to its parent, an
* offset of two (2) refers to the grandparent, and so on.
*/
export function isExpressionWithTypeArgumentsInClassExtendsClause(node: Node, getAncestorOrSelf?: (offset: number) => Node, offset?: number): boolean {
if (node.kind === SyntaxKind.ExpressionWithTypeArguments) {
let parent = getAncestorOrSelf ? getAncestorOrSelf(offset + 1) : node.parent;
if ((<HeritageClause>parent).token === SyntaxKind.ExtendsKeyword) {
let grandparent = getAncestorOrSelf ? getAncestorOrSelf(offset + 2) : parent.parent;
return isClassLike(grandparent);
export function isExpressionWithTypeArgumentsInClassExtendsClause(navigable: ParentNavigable): boolean {
let nav = navigable.createParentNavigator();
if (nav.getKind() === SyntaxKind.ExpressionWithTypeArguments) {
if ((<HeritageClause>nav.getParent()).token === SyntaxKind.ExtendsKeyword) {
return isClassLike(nav.getGrandparent());
}
}
return false;

View File

@ -322,6 +322,10 @@ namespace ts {
return child.kind < SyntaxKind.FirstNode ? child : child.getLastToken(sourceFile);
}
public createParentNavigator(): ParentNavigator {
return createParentNavigator(this);
}
}
class SymbolObject implements Symbol {