// // IVisitor implementations // public override void Visit(JsAstNodeList node) { if (node != null) { var commaOperator = node.Parent as JsCommaOperator; JsAstNodeList list; if (commaOperator != null && (list = commaOperator.Operand2 as JsAstNodeList) != null) { // this list is part of a comma-operator, which is a collection of contiguous // expression statements that we combined together. What we want to do is // delete all constant elements from the list. // if the parent is a block, then this was just a collection of statements and // we can delete ALL constant expressions. But if the parent is not a block, then // we will want to keep the last one as-is because it is the return value of the // overall expression. for (var ndx = list.Count - (node.Parent is JsBlock ? 1 : 2); ndx >= 0; --ndx) { if (list[ndx] is JsConstantWrapper) { list.RemoveAt(ndx); } } } // then normally recurse whatever is left over base.Visit(node); } }
public virtual void Visit(JsAstNodeList node) { if (node != null) { foreach (var childNode in node.Children) { childNode.Accept(this); } } }
public override void Visit(JsAstNodeList node) { if (node != null && node.Count > 0) { // this is really only ever not-ed when it's the right-hand operand // of a comma operator, which we flattened to decrease stack recursion. // so to logical-not this element, we only need to not the last item // in the list (because all the others are comma-separated) node[node.Count - 1].Accept(this); } }
public override bool ReplaceChild(JsAstNode oldNode, JsAstNode newNode) { if (oldNode == m_properties) { var properties = newNode as JsAstNodeList; if (newNode == null || properties != null) { Properties = properties; } } return false; }
public void Visit(JsAstNodeList node) { if (node != null) { // don't bother setting the order of the list itself, just the items for (var ndx = 0; ndx < node.Count; ++ndx) { var item = node[ndx]; if (item != null) { item.Accept(this); } } } }
public void Visit(JsAstNodeList node) { // not applicable; terminate }
private void PossiblyBreakExpressionList(JsBinaryOperator node, JsBlock parentBlock, JsAstNodeList nodeList) { // if the first item can be broken, then we an break it and be done. // otherwise we're going to have to walk until we find a breaking place if (CanBeBroken(nodeList[0])) { // break the first item. insert the left-hand side at our position and // recurse it. Then rotate the node. var index = parentBlock.IndexOf(node); var temp = node.Operand1; RotateOpeator(node, nodeList); parentBlock.Insert(index, temp); // assumes nothing will cause the node to be deleted, because then it // would cause us to miss the following item temp.Accept(this); } else { // the first one can't be broken, so find the first one that can (if any) for (var ndx = 1; ndx < nodeList.Count; ++ndx) { if (CanBeBroken(nodeList[ndx])) { if (ndx == 1) { // the second item is where we are breaking it, so we're going to pull // the first item, replace the list with that first item, then insert // a new comma operator after the current node var temp = nodeList[0]; nodeList.RemoveAt(0); node.Operand2 = temp; // if there's nothing left, then let it die. Otherwise split off // the remainder and insert after the current item. if (nodeList.Count > 0) { parentBlock.InsertAfter(node, CreateSplitNodeFromEnd(nodeList, 0)); } } else { // split off items from the index where we want to split, and insert // it after the current node and leave the node list where it is. parentBlock.InsertAfter(node, CreateSplitNodeFromEnd(nodeList, ndx)); } // and now that we've broken it, bail. break; } } // regardless if anything changed, recurse this node now base.Visit(node); } }
public void Visit(JsAstNodeList node) { // shoudn't get here Debug.Fail("shouldn't get here"); }
private JsAstNode ParseLeftHandSideExpression(bool isMinus) { JsAstNode ast = null; bool skipToken = true; List<JsContext> newContexts = null; TryItAgain: // new expression while (JsToken.New == m_currentToken.Token) { if (null == newContexts) newContexts = new List<JsContext>(4); newContexts.Add(m_currentToken.Clone()); GetNextToken(); } JsToken token = m_currentToken.Token; switch (token) { // primary expression case JsToken.Identifier: ast = new JsLookup(m_currentToken.Clone(), this) { Name = m_scanner.Identifier }; break; case JsToken.ConditionalCommentStart: // skip past the start to the next token GetNextToken(); if (m_currentToken.Token == JsToken.ConditionalCompilationVariable) { // we have /*@id ast = new JsConstantWrapperPP(m_currentToken.Clone(), this) { VarName = m_currentToken.Code, ForceComments = true }; GetNextToken(); if (m_currentToken.Token == JsToken.ConditionalCommentEnd) { // skip past the closing comment GetNextToken(); } else { // we ONLY support /*@id@*/ in expressions right now. If there's not // a closing comment after the ID, then we don't support it. // throw an error, skip to the end of the comment, then ignore it and start // looking for the next token. CCTooComplicated(null); goto TryItAgain; } } else if (m_currentToken.Token == JsToken.ConditionalCommentEnd) { // empty conditional comment! Ignore. GetNextToken(); goto TryItAgain; } else { // we DON'T have "/*@IDENT". We only support "/*@IDENT @*/", so since this isn't // and id, throw the error, skip to the end of the comment, and ignore it // by looping back and looking for the NEXT token. m_currentToken.HandleError(JsError.ConditionalCompilationTooComplex); // skip to end of conditional comment while (m_currentToken.Token != JsToken.EndOfFile && m_currentToken.Token != JsToken.ConditionalCommentEnd) { GetNextToken(); } GetNextToken(); goto TryItAgain; } break; case JsToken.This: ast = new JsThisLiteral(m_currentToken.Clone(), this); break; case JsToken.StringLiteral: ast = new JsConstantWrapper(m_scanner.StringLiteralValue, JsPrimitiveType.String, m_currentToken.Clone(), this) { MayHaveIssues = m_scanner.LiteralHasIssues }; break; case JsToken.IntegerLiteral: case JsToken.NumericLiteral: { JsContext numericContext = m_currentToken.Clone(); double doubleValue; if (ConvertNumericLiteralToDouble(m_currentToken.Code, (token == JsToken.IntegerLiteral), out doubleValue)) { // conversion worked fine // check for some boundary conditions var mayHaveIssues = m_scanner.LiteralHasIssues; if (doubleValue == double.MaxValue) { ReportError(JsError.NumericMaximum, numericContext, true); } else if (isMinus && -doubleValue == double.MinValue) { ReportError(JsError.NumericMinimum, numericContext, true); } // create the constant wrapper from the value ast = new JsConstantWrapper(doubleValue, JsPrimitiveType.Number, numericContext, this) { MayHaveIssues = mayHaveIssues }; } else { // if we went overflow or are not a number, then we will use the "Other" // primitive type so we don't try doing any numeric calcs with it. if (double.IsInfinity(doubleValue)) { // overflow // and if we ARE an overflow, report it ReportError(JsError.NumericOverflow, numericContext, true); } // regardless, we're going to create a special constant wrapper // that simply echos the input as-is ast = new JsConstantWrapper(m_currentToken.Code, JsPrimitiveType.Other, numericContext, this) { MayHaveIssues = true }; } break; } case JsToken.True: ast = new JsConstantWrapper(true, JsPrimitiveType.Boolean, m_currentToken.Clone(), this); break; case JsToken.False: ast = new JsConstantWrapper(false, JsPrimitiveType.Boolean, m_currentToken.Clone(), this); break; case JsToken.Null: ast = new JsConstantWrapper(null, JsPrimitiveType.Null, m_currentToken.Clone(), this); break; case JsToken.ConditionalCompilationVariable: ast = new JsConstantWrapperPP(m_currentToken.Clone(), this) { VarName = m_currentToken.Code, ForceComments = false }; break; case JsToken.DivideAssign: // normally this token is not allowed on the left-hand side of an expression. // BUT, this might be the start of a regular expression that begins with an equals sign! // we need to test to see if we can parse a regular expression, and if not, THEN // we can fail the parse. case JsToken.Divide: // could it be a regexp? ast = ScanRegularExpression(); if (ast != null) { // yup -- we're done here break; } // nope -- go to the default branch goto default; // expression case JsToken.LeftParenthesis: { var groupingOp = new JsGroupingOperator(m_currentToken.Clone(), this); ast = groupingOp; GetNextToken(); m_noSkipTokenSet.Add(NoSkipTokenSet.s_ParenExpressionNoSkipToken); try { // parse an expression groupingOp.Operand = ParseExpression(); if (JsToken.RightParenthesis != m_currentToken.Token) { ReportError(JsError.NoRightParenthesis); } else { // add the closing paren to the expression context ast.Context.UpdateWith(m_currentToken); } } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_ParenExpressionNoSkipToken, exc) == -1) throw; else groupingOp.Operand = exc._partiallyComputedNode; } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ParenExpressionNoSkipToken); } } break; // array initializer case JsToken.LeftBracket: JsContext listCtx = m_currentToken.Clone(); GetNextToken(); JsAstNodeList list = new JsAstNodeList(CurrentPositionContext(), this); var hasTrailingCommas = false; while (JsToken.RightBracket != m_currentToken.Token) { if (JsToken.Comma != m_currentToken.Token) { m_noSkipTokenSet.Add(NoSkipTokenSet.s_ArrayInitNoSkipTokenSet); try { var expression = ParseExpression(true); list.Append(expression); if (JsToken.Comma != m_currentToken.Token) { if (JsToken.RightBracket != m_currentToken.Token) { ReportError(JsError.NoRightBracket); } break; } else { // we have a comma -- skip it after adding it as a terminator // on the previous expression var commaContext = m_currentToken.Clone(); expression.IfNotNull(e => e.TerminatingContext = commaContext); GetNextToken(); // if the next token is the closing brackets, then we need to // add a missing value to the array because we end in a comma and // we need to keep it for cross-platform compat. // TECHNICALLY, that puts an extra item into the array for most modern browsers, but not ALL. if (m_currentToken.Token == JsToken.RightBracket) { hasTrailingCommas = true; list.Append(new JsConstantWrapper(JsMissing.Value, JsPrimitiveType.Other, m_currentToken.Clone(), this)); // throw a cross-browser warning about trailing commas commaContext.HandleError(JsError.ArrayLiteralTrailingComma); break; } } } catch (RecoveryTokenException exc) { if (exc._partiallyComputedNode != null) list.Append(exc._partiallyComputedNode); if (IndexOfToken(NoSkipTokenSet.s_ArrayInitNoSkipTokenSet, exc) == -1) { listCtx.UpdateWith(CurrentPositionContext()); exc._partiallyComputedNode = new JsArrayLiteral(listCtx, this) { Elements = list, MayHaveIssues = true }; throw; } else { if (JsToken.RightBracket == m_currentToken.Token) break; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ArrayInitNoSkipTokenSet); } } else { // comma -- missing array item in the list var commaContext = m_currentToken.Clone(); list.Append(new JsConstantWrapper(JsMissing.Value, JsPrimitiveType.Other, m_currentToken.Clone(), this) { TerminatingContext = commaContext }); // skip over the comma GetNextToken(); // if the next token is the closing brace, then we end with a comma -- and we need to // add ANOTHER missing value to make sure this last comma doesn't get left off. // TECHNICALLY, that puts an extra item into the array for most modern browsers, but not ALL. if (m_currentToken.Token == JsToken.RightBracket) { hasTrailingCommas = true; list.Append(new JsConstantWrapper(JsMissing.Value, JsPrimitiveType.Other, m_currentToken.Clone(), this)); // throw a cross-browser warning about trailing commas commaContext.HandleError(JsError.ArrayLiteralTrailingComma); break; } } } listCtx.UpdateWith(m_currentToken); ast = new JsArrayLiteral(listCtx, this) { Elements = list, MayHaveIssues = hasTrailingCommas }; break; // object initializer case JsToken.LeftCurly: JsContext objCtx = m_currentToken.Clone(); GetNextToken(); var propertyList = new JsAstNodeList(CurrentPositionContext(), this); if (JsToken.RightCurly != m_currentToken.Token) { for (; ; ) { JsObjectLiteralField field = null; JsAstNode value = null; bool getterSetter = false; string ident; switch (m_currentToken.Token) { case JsToken.Identifier: field = new JsObjectLiteralField(m_scanner.Identifier, JsPrimitiveType.String, m_currentToken.Clone(), this); break; case JsToken.StringLiteral: field = new JsObjectLiteralField(m_scanner.StringLiteralValue, JsPrimitiveType.String, m_currentToken.Clone(), this) { MayHaveIssues = m_scanner.LiteralHasIssues }; break; case JsToken.IntegerLiteral: case JsToken.NumericLiteral: { double doubleValue; if (ConvertNumericLiteralToDouble(m_currentToken.Code, (m_currentToken.Token == JsToken.IntegerLiteral), out doubleValue)) { // conversion worked fine field = new JsObjectLiteralField( doubleValue, JsPrimitiveType.Number, m_currentToken.Clone(), this ); } else { // something went wrong and we're not sure the string representation in the source is // going to convert to a numeric value well if (double.IsInfinity(doubleValue)) { ReportError(JsError.NumericOverflow, m_currentToken.Clone(), true); } // use the source as the field name, not the numeric value field = new JsObjectLiteralField( m_currentToken.Code, JsPrimitiveType.Other, m_currentToken.Clone(), this); } break; } case JsToken.Get: case JsToken.Set: if (PeekToken() == JsToken.Colon) { // the field is either "get" or "set" and isn't the special Mozilla getter/setter field = new JsObjectLiteralField(m_currentToken.Code, JsPrimitiveType.String, m_currentToken.Clone(), this); } else { // ecma-script get/set property construct getterSetter = true; bool isGet = (m_currentToken.Token == JsToken.Get); value = ParseFunction( (JsToken.Get == m_currentToken.Token ? JsFunctionType.Getter : JsFunctionType.Setter), m_currentToken.Clone() ); JsFunctionObject funcExpr = value as JsFunctionObject; if (funcExpr != null) { // getter/setter is just the literal name with a get/set flag field = new JsGetterSetter( funcExpr.Name, isGet, funcExpr.IdContext.Clone(), this ); } else { ReportError(JsError.FunctionExpressionExpected); } } break; default: // NOT: identifier token, string, number, or getter/setter. // see if it's a token that COULD be an identifierName. ident = m_scanner.Identifier; if (JsScanner.IsValidIdentifier(ident)) { // BY THE SPEC, if it's a valid identifierName -- which includes reserved words -- then it's // okay for object literal syntax. However, reserved words here won't work in all browsers, // so if it is a reserved word, let's throw a low-sev cross-browser warning on the code. if (JsKeyword.CanBeIdentifier(m_currentToken.Token) == null) { ReportError(JsError.ObjectLiteralKeyword, m_currentToken.Clone(), true); } field = new JsObjectLiteralField(ident, JsPrimitiveType.String, m_currentToken.Clone(), this); } else { // throw an error but use it anyway, since that's what the developer has going on ReportError(JsError.NoMemberIdentifier, m_currentToken.Clone(), true); field = new JsObjectLiteralField(m_currentToken.Code, JsPrimitiveType.String, m_currentToken.Clone(), this); } break; } if (field != null) { if (!getterSetter) { GetNextToken(); } m_noSkipTokenSet.Add(NoSkipTokenSet.s_ObjectInitNoSkipTokenSet); try { if (!getterSetter) { // get the value if (JsToken.Colon != m_currentToken.Token) { ReportError(JsError.NoColon, true); value = ParseExpression(true); } else { field.ColonContext = m_currentToken.Clone(); GetNextToken(); value = ParseExpression(true); } } // put the pair into the list of fields var propCtx = field.Context.Clone().CombineWith(value.IfNotNull(v => v.Context)); var property = new JsObjectLiteralProperty(propCtx, this) { Name = field, Value = value }; propertyList.Append(property); if (JsToken.RightCurly == m_currentToken.Token) { break; } else { if (JsToken.Comma == m_currentToken.Token) { // skip the comma after adding it to the property as a terminating context property.IfNotNull(p => p.TerminatingContext = m_currentToken.Clone()); GetNextToken(); // if the next token is the right-curly brace, then we ended // the list with a comma, which is perfectly fine if (m_currentToken.Token == JsToken.RightCurly) { break; } } else { if (m_foundEndOfLine) { ReportError(JsError.NoRightCurly); } else ReportError(JsError.NoComma, true); SkipTokensAndThrow(); } } } catch (RecoveryTokenException exc) { if (exc._partiallyComputedNode != null) { // the problem was in ParseExpression trying to determine value value = exc._partiallyComputedNode; var propCtx = field.Context.Clone().CombineWith(value.IfNotNull(v => v.Context)); var property = new JsObjectLiteralProperty(propCtx, this) { Name = field, Value = value }; propertyList.Append(property); } if (IndexOfToken(NoSkipTokenSet.s_ObjectInitNoSkipTokenSet, exc) == -1) { exc._partiallyComputedNode = new JsObjectLiteral(objCtx, this) { Properties = propertyList }; throw; } else { if (JsToken.Comma == m_currentToken.Token) GetNextToken(); if (JsToken.RightCurly == m_currentToken.Token) break; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ObjectInitNoSkipTokenSet); } } } } objCtx.UpdateWith(m_currentToken); ast = new JsObjectLiteral(objCtx, this) { Properties = propertyList }; break; // function expression case JsToken.Function: ast = ParseFunction(JsFunctionType.Expression, m_currentToken.Clone()); skipToken = false; break; case JsToken.AspNetBlock: ast = new JsAspNetBlockNode(m_currentToken.Clone(), this) { AspNetBlockText = m_currentToken.Code }; break; default: string identifier = JsKeyword.CanBeIdentifier(m_currentToken.Token); if (null != identifier) { ast = new JsLookup(m_currentToken.Clone(), this) { Name = identifier }; } else { ReportError(JsError.ExpressionExpected); SkipTokensAndThrow(); } break; } // can be a CallExpression, that is, followed by '.' or '(' or '[' if (skipToken) GetNextToken(); return MemberExpression(ast, newContexts); }
private JsFunctionObject ParseFunction(JsFunctionType functionType, JsContext fncCtx) { JsLookup name = null; JsAstNodeList formalParameters = null; JsBlock body = null; bool inExpression = (functionType == JsFunctionType.Expression); JsContext paramsContext = null; GetNextToken(); // get the function name or make an anonymous function if in expression "position" if (JsToken.Identifier == m_currentToken.Token) { name = new JsLookup(m_currentToken.Clone(), this) { Name = m_scanner.Identifier }; GetNextToken(); } else { string identifier = JsKeyword.CanBeIdentifier(m_currentToken.Token); if (null != identifier) { name = new JsLookup(m_currentToken.Clone(), this) { Name = identifier }; GetNextToken(); } else { if (!inExpression) { // if this isn't a function expression, then we need to throw an error because // function DECLARATIONS always need a valid identifier name ReportError(JsError.NoIdentifier, m_currentToken.Clone(), true); // BUT if the current token is a left paren, we don't want to use it as the name. // (fix for issue #14152) if (m_currentToken.Token != JsToken.LeftParenthesis && m_currentToken.Token != JsToken.LeftCurly) { identifier = m_currentToken.Code; name = new JsLookup(CurrentPositionContext(), this) { Name = identifier }; GetNextToken(); } } } } // make a new state and save the old one List<BlockType> blockType = m_blockType; m_blockType = new List<BlockType>(16); Dictionary<string, LabelInfo> labelTable = m_labelTable; m_labelTable = new Dictionary<string, LabelInfo>(); try { // get the formal parameters if (JsToken.LeftParenthesis != m_currentToken.Token) { // we expect a left paren at this point for standard cross-browser support. // BUT -- some versions of IE allow an object property expression to be a function name, like window.onclick. // we still want to throw the error, because it syntax errors on most browsers, but we still want to // be able to parse it and return the intended results. // Skip to the open paren and use whatever is in-between as the function name. Doesn't matter that it's // an invalid identifier; it won't be accessible as a valid field anyway. bool expandedIndentifier = false; while (m_currentToken.Token != JsToken.LeftParenthesis && m_currentToken.Token != JsToken.LeftCurly && m_currentToken.Token != JsToken.Semicolon && m_currentToken.Token != JsToken.EndOfFile) { name.Context.UpdateWith(m_currentToken); GetNextToken(); expandedIndentifier = true; } // if we actually expanded the identifier context, then we want to report that // the function name needs to be an identifier. Otherwise we didn't expand the // name, so just report that we expected an open paren at this point. if (expandedIndentifier) { name.Name = name.Context.Code; name.Context.HandleError(JsError.FunctionNameMustBeIdentifier, false); } else { ReportError(JsError.NoLeftParenthesis, true); } } if (m_currentToken.Token == JsToken.LeftParenthesis) { // create the parameter list formalParameters = new JsAstNodeList(m_currentToken.Clone(), this); paramsContext = m_currentToken.Clone(); // skip the open paren GetNextToken(); // create the list of arguments and update the context while (JsToken.RightParenthesis != m_currentToken.Token) { String id = null; m_noSkipTokenSet.Add(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet); try { JsParameterDeclaration paramDecl = null; if (JsToken.Identifier != m_currentToken.Token && (id = JsKeyword.CanBeIdentifier(m_currentToken.Token)) == null) { if (JsToken.LeftCurly == m_currentToken.Token) { ReportError(JsError.NoRightParenthesis); break; } else if (JsToken.Comma == m_currentToken.Token) { // We're missing an argument (or previous argument was malformed and // we skipped to the comma.) Keep trying to parse the argument list -- // we will skip the comma below. ReportError(JsError.SyntaxError, true); } else { ReportError(JsError.SyntaxError, true); SkipTokensAndThrow(); } } else { if (null == id) { id = m_scanner.Identifier; } paramDecl = new JsParameterDeclaration(m_currentToken.Clone(), this) { Name = id, Position = formalParameters.Count }; formalParameters.Append(paramDecl); GetNextToken(); } // got an arg, it should be either a ',' or ')' if (JsToken.RightParenthesis == m_currentToken.Token) { break; } else if (JsToken.Comma == m_currentToken.Token) { // append the comma context as the terminator for the parameter paramDecl.IfNotNull(p => p.TerminatingContext = m_currentToken.Clone()); } else { // deal with error in some "intelligent" way if (JsToken.LeftCurly == m_currentToken.Token) { ReportError(JsError.NoRightParenthesis); break; } else { if (JsToken.Identifier == m_currentToken.Token) { // it's possible that the guy was writing the type in C/C++ style (i.e. int x) ReportError(JsError.NoCommaOrTypeDefinitionError); } else ReportError(JsError.NoComma); } } GetNextToken(); } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet, exc) == -1) throw; } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet); } } fncCtx.UpdateWith(m_currentToken); GetNextToken(); } // read the function body of non-abstract functions. if (JsToken.LeftCurly != m_currentToken.Token) ReportError(JsError.NoLeftCurly, true); m_blockType.Add(BlockType.Block); m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet); m_noSkipTokenSet.Add(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); try { // parse the block locally to get the exact end of function body = new JsBlock(m_currentToken.Clone(), this); body.BraceOnNewLine = m_foundEndOfLine; GetNextToken(); var possibleDirectivePrologue = true; while (JsToken.RightCurly != m_currentToken.Token) { try { // function body's are SourceElements (Statements + FunctionDeclarations) var statement = ParseStatement(true); if (possibleDirectivePrologue) { var constantWrapper = statement as JsConstantWrapper; if (constantWrapper != null && constantWrapper.PrimitiveType == JsPrimitiveType.String) { // if it's already a directive prologues, we're good to go if (!(constantWrapper is JsDirectivePrologue)) { // make the statement a directive prologue instead of a constant wrapper statement = new JsDirectivePrologue(constantWrapper.Value.ToString(), constantWrapper.Context, constantWrapper.Parser) { MayHaveIssues = constantWrapper.MayHaveIssues }; } } else if (!m_newModule) { // no longer considering constant wrappers possibleDirectivePrologue = false; } } else if (m_newModule) { // we scanned into a new module -- we might find directive prologues again possibleDirectivePrologue = true; } // add it to the body body.Append(statement); } catch (RecoveryTokenException exc) { if (exc._partiallyComputedNode != null) { body.Append(exc._partiallyComputedNode); } if (IndexOfToken(NoSkipTokenSet.s_StartStatementNoSkipTokenSet, exc) == -1) throw; } } // make sure any important comments before the closing brace are kept AppendImportantComments(body); body.Context.UpdateWith(m_currentToken); fncCtx.UpdateWith(m_currentToken); } catch (EndOfStreamException) { // if we get an EOF here, we never had a chance to find the closing curly-brace fncCtx.HandleError(JsError.UnclosedFunction, true); } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exc) == -1) { exc._partiallyComputedNode = new JsFunctionObject(fncCtx, this) { FunctionType = (inExpression ? JsFunctionType.Expression : JsFunctionType.Declaration), IdContext = name.IfNotNull(n => n.Context), Name = name.IfNotNull(n => n.Name), ParameterDeclarations = formalParameters, ParametersContext = paramsContext, Body = body }; throw; } } finally { m_blockType.RemoveAt(m_blockType.Count - 1); m_noSkipTokenSet.Remove(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet); } GetNextToken(); } finally { // restore state m_blockType = blockType; m_labelTable = labelTable; } return new JsFunctionObject(fncCtx, this) { FunctionType = functionType, IdContext = name.IfNotNull(n => n.Context), Name = name.IfNotNull(n => n.Name), ParameterDeclarations = formalParameters, ParametersContext = paramsContext, Body = body }; }
/// <summary> /// Parse the source code using the given settings, getting back an abstract syntax tree Block node as the root /// representing the list of statements in the source code. /// </summary> /// <param name="settings">code settings to use to process the source code</param> /// <returns>root Block node representing the top-level statements</returns> public JsBlock Parse(JsSettings settings) { // initialize the scanner with our settings // make sure the RawTokens setting is OFF or we won't be able to create our AST InitializeScanner(settings); // make sure we initialize the global scope's strict mode to our flag, whether or not it // is true. This means if the setting is false, we will RESET the flag to false if we are // reusing the scope and a previous Parse call had code that set it to strict with a // program directive. GlobalScope.UseStrict = m_settings.StrictMode; // make sure the global scope knows about our known global names GlobalScope.SetAssumedGlobals(m_settings); // start of a new module m_newModule = true; var timePoints = m_timingPoints = new long[9]; var timeIndex = timePoints.Length; var stopWatch = new Stopwatch(); stopWatch.Start(); JsBlock scriptBlock = null; JsBlock returnBlock = null; try { switch (m_settings.SourceMode) { case JsSourceMode.Program: // simply parse a block of statements returnBlock = scriptBlock = ParseStatements(); break; case JsSourceMode.Expression: // create a block, get the first token, add in the parse of a single expression, // and we'll go fron there. returnBlock = scriptBlock = new JsBlock(CurrentPositionContext(), this); GetNextToken(); try { var expr = ParseExpression(); if (expr != null) { scriptBlock.Append(expr); scriptBlock.UpdateWith(expr.Context); } } catch (EndOfStreamException) { Debug.WriteLine("EOF"); } break; case JsSourceMode.EventHandler: // we're going to create the global block, add in a function expression with a single // parameter named "event", and then we're going to parse the input as the body of that // function expression. We're going to resolve the global block, but only return the body // of the function. scriptBlock = new JsBlock(null, this); var parameters = new JsAstNodeList(null, this); parameters.Append(new JsParameterDeclaration(null, this) { Name = "event", RenameNotAllowed = true }); var funcExpression = new JsFunctionObject(null, this) { FunctionType = JsFunctionType.Expression, ParameterDeclarations = parameters }; scriptBlock.Append(funcExpression); funcExpression.Body = returnBlock = ParseStatements(); break; default: Debug.Fail("Unexpected source mode enumeration"); return null; } } catch (RecoveryTokenException) { // this should never happen but let's make SURE we don't expose our // private exception object to the outside world m_currentToken.HandleError(JsError.ApplicationError, true); } timePoints[--timeIndex] = stopWatch.ElapsedTicks; if (scriptBlock != null) { // resolve everything JsResolutionVisitor.Apply(scriptBlock, GlobalScope, m_settings); } timePoints[--timeIndex] = stopWatch.ElapsedTicks; if (scriptBlock != null && Settings.MinifyCode && !Settings.PreprocessOnly) { // this visitor doesn't just reorder scopes. It also combines the adjacent var variables, // unnests blocks, identifies prologue directives, and sets the strict mode on scopes. JsReorderScopeVisitor.Apply(scriptBlock, this); timePoints[--timeIndex] = stopWatch.ElapsedTicks; // analyze the entire node tree (needed for hypercrunch) // root to leaf (top down) var analyzeVisitor = new JsAnalyzeNodeVisitor(this); scriptBlock.Accept(analyzeVisitor); timePoints[--timeIndex] = stopWatch.ElapsedTicks; // analyze the scope chain (also needed for hypercrunch) // root to leaf (top down) GlobalScope.AnalyzeScope(); timePoints[--timeIndex] = stopWatch.ElapsedTicks; // if we want to crunch any names.... if (m_settings.LocalRenaming != JsLocalRenaming.KeepAll && m_settings.IsModificationAllowed(JsTreeModifications.LocalRenaming)) { // then do a top-down traversal of the scope tree. For each field that had not // already been crunched (globals and outers will already be crunched), crunch // the name with a crunch iterator that does not use any names in the verboten set. GlobalScope.AutoRenameFields(); } timePoints[--timeIndex] = stopWatch.ElapsedTicks; // if we want to evaluate literal expressions, do so now if (m_settings.EvalLiteralExpressions) { var visitor = new JsEvaluateLiteralVisitor(this); scriptBlock.Accept(visitor); } timePoints[--timeIndex] = stopWatch.ElapsedTicks; // make the final cleanup pass JsFinalPassVisitor.Apply(scriptBlock, this); timePoints[--timeIndex] = stopWatch.ElapsedTicks; // we want to walk all the scopes to make sure that any generated // variables that haven't been crunched have been assigned valid // variable names that don't collide with any existing variables. GlobalScope.ValidateGeneratedNames(); timePoints[--timeIndex] = stopWatch.ElapsedTicks; } if (returnBlock != null && returnBlock.Parent != null) { returnBlock.Parent = null; } return returnBlock; }
//--------------------------------------------------------------------------------------- // ParseExpressionList // // Given a starting this.currentToken '(' or '[', parse a list of expression separated by // ',' until matching ')' or ']' //--------------------------------------------------------------------------------------- private JsAstNodeList ParseExpressionList(JsToken terminator) { JsContext listCtx = m_currentToken.Clone(); GetNextToken(); JsAstNodeList list = new JsAstNodeList(listCtx, this); if (terminator != m_currentToken.Token) { for (; ; ) { m_noSkipTokenSet.Add(NoSkipTokenSet.s_ExpressionListNoSkipTokenSet); try { JsAstNode item; if (JsToken.Comma == m_currentToken.Token) { item = new JsConstantWrapper(JsMissing.Value, JsPrimitiveType.Other, m_currentToken.Clone(), this); list.Append(item); } else if (terminator == m_currentToken.Token) { break; } else { item = ParseExpression(true); list.Append(item); } if (terminator == m_currentToken.Token) { break; } else { if (JsToken.Comma == m_currentToken.Token) { item.IfNotNull(n => n.TerminatingContext = m_currentToken.Clone()); } else { if (terminator == JsToken.RightParenthesis) { // in ASP+ it's easy to write a semicolon at the end of an expression // not realizing it is going to go inside a function call // (ie. Response.Write()), so make a special check here if (JsToken.Semicolon == m_currentToken.Token) { if (JsToken.RightParenthesis == PeekToken()) { ReportError(JsError.UnexpectedSemicolon, true); GetNextToken(); break; } } ReportError(JsError.NoRightParenthesisOrComma); } else { ReportError(JsError.NoRightBracketOrComma); } SkipTokensAndThrow(); } } } catch (RecoveryTokenException exc) { if (exc._partiallyComputedNode != null) list.Append(exc._partiallyComputedNode); if (IndexOfToken(NoSkipTokenSet.s_ExpressionListNoSkipTokenSet, exc) == -1) { exc._partiallyComputedNode = list; throw; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ExpressionListNoSkipTokenSet); } GetNextToken(); } } listCtx.UpdateWith(m_currentToken); return list; }
public void Visit(JsAstNodeList node) { if (node != null && node.Count > 0) { var symbol = StartSymbol(node); // see if this parent is a comma operator whose parent in turn is a block. // if so, then these expressions were expression statements that we've combined. // if that's the case, we're going to put newlines in so it's a little easier // to read in multi-line mode var addNewLines = node.Parent is JsCommaOperator && node.Parent.Parent is JsBlock && m_settings.OutputMode == MinifierOutputMode.MultipleLines; // output as comma-separated expressions starting with the first one node[0].Accept(this); SetContextOutputPosition(node.Context, node[0].Context); // this should never be the first element of the line, but // just in case, reset the flag after the first expression. m_startOfStatement = false; // if we aren't breaking them up by newlines, indent now in case // one of the items causes a newline to be inserted. if (!addNewLines) { Indent(); } for (var ndx = 1; ndx < node.Count; ++ndx) { // output a comma OutputPossibleLineBreak(','); MarkSegment(node, null, node[ndx-1].IfNotNull(n => n.TerminatingContext)); if (addNewLines) { NewLine(); } else if (m_settings.OutputMode == MinifierOutputMode.MultipleLines) { OutputPossibleLineBreak(' '); } // output the next node node[ndx].Accept(this); } // if we aren't breaking by newlines, unindent our previous indent if (!addNewLines) { Unindent(); } EndSymbol(symbol); } }
public static JsAstNode CombineWithComma(JsContext context, JsParser parser, JsAstNode operand1, JsAstNode operand2) { var comma = new JsCommaOperator(context, parser); // if the left is a comma-operator already.... var leftBinary = operand1 as JsBinaryOperator; var rightBinary = operand2 as JsBinaryOperator; if (leftBinary != null && leftBinary.OperatorToken == JsToken.Comma) { // the left-hand side is already a comma operator. Instead of nesting these, we're // going to combine them // move the old list's left-hand side to our left-hand side comma.Operand1 = leftBinary.Operand1; JsAstNodeList list; if (rightBinary != null && rightBinary.OperatorToken == JsToken.Comma) { // the right is ALSO a comma operator. Create a new list, append all the rest of the operands // and set our right-hand side to be the list list = new JsAstNodeList(null, parser); list.Append(leftBinary.Operand2).Append(rightBinary.Operand1).Append(rightBinary.Operand2); } else { // the right is not a comma operator. // see if the left-hand side already has a list we can use list = leftBinary.Operand2 as JsAstNodeList; if (list == null) { // it's not a list already // create a new list with the left's right and our right and set it to our right list = new JsAstNodeList(null, parser); list.Append(leftBinary.Operand2); } // and add our right-hand operand to the end of the list list.Append(operand2); } // set the list on the right comma.Operand2 = list; } else if (rightBinary != null && rightBinary.OperatorToken == JsToken.Comma) { // the left hand side is NOT a comma operator. comma.Operand1 = operand1; // the right-hand side is already a comma-operator, but the left is not. // see if it already has a list we can reuse var rightList = rightBinary.Operand2 as JsAstNodeList; if (rightList != null) { // it does. Prepend its right-hand operand and use the list rightList.Insert(0, rightBinary.Operand1); } else { // it's not -- create a new list containing the operands rightList = new JsAstNodeList(rightBinary.Context, parser); rightList.Append(rightBinary.Operand1); rightList.Append(rightBinary.Operand2); } comma.Operand2 = rightList; } else { comma.Operand1 = operand1; comma.Operand2 = operand2; } return comma; }
private static JsAstNode CreateSplitNodeFromEnd(JsAstNodeList nodeList, int ndx) { JsAstNode newNode; if (ndx == nodeList.Count - 1) { // the LAST one can be broken. Pull it off the list and we will just // insert it after the current node. newNode = nodeList[ndx]; nodeList.RemoveAt(ndx); } else if (ndx == nodeList.Count - 2) { // the PENULTIMATE item can be broken. So create a new comma operator // with the just the last two item and we'll insert it after the current node var left = nodeList[ndx]; nodeList.RemoveAt(ndx); var right = nodeList[ndx]; nodeList.RemoveAt(ndx); newNode = new JsCommaOperator(null, nodeList.Parser) { Operand1 = left, Operand2 = right }; } else { // at least three items will be pulled off, which means there will // be at least two items on the right, so we'll create a new astlist to // insert those items into a new comma operator var left = nodeList[ndx]; nodeList.RemoveAt(ndx); // if we were passed zero, then just reuse the node list. // otherwise we need to create a new one and move the items // from the index position over. JsAstNodeList right; if (ndx == 0) { right = nodeList; } else { right = new JsAstNodeList(null, nodeList.Parser); while (ndx < nodeList.Count) { var temp = nodeList[ndx]; nodeList.RemoveAt(ndx); right.Append(temp); } } newNode = new JsCommaOperator(null, nodeList.Parser) { Operand1 = left, Operand2 = right }; } return newNode; }
private JsAstNode ParseSwitchStatement() { JsContext switchCtx = m_currentToken.Clone(); JsAstNode expr = null; JsAstNodeList cases = null; var braceOnNewLine = false; JsContext braceContext = null; m_blockType.Add(BlockType.Switch); try { // read switch(expr) GetNextToken(); if (JsToken.LeftParenthesis != m_currentToken.Token) ReportError(JsError.NoLeftParenthesis); GetNextToken(); m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); m_noSkipTokenSet.Add(NoSkipTokenSet.s_SwitchNoSkipTokenSet); try { expr = ParseExpression(); if (JsToken.RightParenthesis != m_currentToken.Token) { ReportError(JsError.NoRightParenthesis); } GetNextToken(); if (JsToken.LeftCurly != m_currentToken.Token) { ReportError(JsError.NoLeftCurly); } braceOnNewLine = m_foundEndOfLine; braceContext = m_currentToken.Clone(); GetNextToken(); } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1 && IndexOfToken(NoSkipTokenSet.s_SwitchNoSkipTokenSet, exc) == -1) { // give up exc._partiallyComputedNode = null; throw; } else { if (exc._partiallyComputedNode == null) expr = new JsConstantWrapper(true, JsPrimitiveType.Boolean, CurrentPositionContext(), this); else expr = exc._partiallyComputedNode; if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) != -1) { if (exc._token == JsToken.RightParenthesis) GetNextToken(); if (JsToken.LeftCurly != m_currentToken.Token) { ReportError(JsError.NoLeftCurly); } braceOnNewLine = m_foundEndOfLine; braceContext = m_currentToken.Clone(); GetNextToken(); } } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_SwitchNoSkipTokenSet); m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); } // parse the switch body cases = new JsAstNodeList(CurrentPositionContext(), this); bool defaultStatement = false; m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet); try { while (JsToken.RightCurly != m_currentToken.Token) { JsSwitchCase caseClause = null; JsAstNode caseValue = null; var caseCtx = m_currentToken.Clone(); JsContext colonContext = null; m_noSkipTokenSet.Add(NoSkipTokenSet.s_CaseNoSkipTokenSet); try { if (JsToken.Case == m_currentToken.Token) { // get the case GetNextToken(); caseValue = ParseExpression(); } else if (JsToken.Default == m_currentToken.Token) { // get the default if (defaultStatement) { // we report an error but we still accept the default ReportError(JsError.DupDefault, true); } else { defaultStatement = true; } GetNextToken(); } else { // This is an error, there is no case or default. Assume a default was missing and keep going defaultStatement = true; ReportError(JsError.BadSwitch); } if (JsToken.Colon != m_currentToken.Token) { ReportError(JsError.NoColon); } else { colonContext = m_currentToken.Clone(); } // read the statements inside the case or default GetNextToken(); } catch (RecoveryTokenException exc) { // right now we can only get here for the 'case' statement if (IndexOfToken(NoSkipTokenSet.s_CaseNoSkipTokenSet, exc) == -1) { // ignore the current case or default exc._partiallyComputedNode = null; throw; } else { caseValue = exc._partiallyComputedNode; if (exc._token == JsToken.Colon) { GetNextToken(); } } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_CaseNoSkipTokenSet); } m_blockType.Add(BlockType.Block); try { var statements = new JsBlock(m_currentToken.Clone(), this); m_noSkipTokenSet.Add(NoSkipTokenSet.s_SwitchNoSkipTokenSet); m_noSkipTokenSet.Add(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); try { while (JsToken.RightCurly != m_currentToken.Token && JsToken.Case != m_currentToken.Token && JsToken.Default != m_currentToken.Token) { try { // parse a Statement, not a SourceElement statements.Append(ParseStatement(false)); } catch (RecoveryTokenException exc) { if (exc._partiallyComputedNode != null) { statements.Append(exc._partiallyComputedNode); exc._partiallyComputedNode = null; } if (IndexOfToken(NoSkipTokenSet.s_StartStatementNoSkipTokenSet, exc) == -1) { throw; } } } } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_SwitchNoSkipTokenSet, exc) == -1) { caseClause = new JsSwitchCase(caseCtx, this) { CaseValue = caseValue, ColonContext = colonContext, Statements = statements }; cases.Append(caseClause); throw; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); m_noSkipTokenSet.Remove(NoSkipTokenSet.s_SwitchNoSkipTokenSet); } caseCtx.UpdateWith(statements.Context); caseClause = new JsSwitchCase(caseCtx, this) { CaseValue = caseValue, ColonContext = colonContext, Statements = statements }; cases.Append(caseClause); } finally { m_blockType.RemoveAt(m_blockType.Count - 1); } } } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exc) == -1) { //save what you can a rethrow switchCtx.UpdateWith(CurrentPositionContext()); exc._partiallyComputedNode = new JsSwitch(switchCtx, this) { Expression = expr, BraceContext = braceContext, Cases = cases, BraceOnNewLine = braceOnNewLine }; throw; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet); } switchCtx.UpdateWith(m_currentToken); GetNextToken(); } finally { m_blockType.RemoveAt(m_blockType.Count - 1); } return new JsSwitch(switchCtx, this) { Expression = expr, BraceContext = braceContext, Cases = cases, BraceOnNewLine = braceOnNewLine }; }
//--------------------------------------------------------------------------------------- // MemberExpression // // Accessor : // <empty> | // Arguments Accessor // '[' Expression ']' Accessor | // '.' Identifier Accessor | // // Don't have this function throwing an exception without checking all the calling sites. // There is state in instance variable that is saved on the calling stack in some function // (i.e ParseFunction and ParseClass) and you don't want to blow up the stack //--------------------------------------------------------------------------------------- private JsAstNode MemberExpression(JsAstNode expression, List<JsContext> newContexts) { for (; ; ) { m_noSkipTokenSet.Add(NoSkipTokenSet.s_MemberExprNoSkipTokenSet); try { switch (m_currentToken.Token) { case JsToken.LeftParenthesis: JsAstNodeList args = null; RecoveryTokenException callError = null; m_noSkipTokenSet.Add(NoSkipTokenSet.s_ParenToken); try { args = ParseExpressionList(JsToken.RightParenthesis); } catch (RecoveryTokenException exc) { args = (JsAstNodeList)exc._partiallyComputedNode; if (IndexOfToken(NoSkipTokenSet.s_ParenToken, exc) == -1) callError = exc; // thrown later on } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ParenToken); } expression = new JsCallNode(expression.Context.CombineWith(args.Context), this) { Function = expression, Arguments = args, InBrackets = false }; if (null != newContexts && newContexts.Count > 0) { (newContexts[newContexts.Count - 1]).UpdateWith(expression.Context); if (!(expression is JsCallNode)) { expression = new JsCallNode(newContexts[newContexts.Count - 1], this) { Function = expression, Arguments = new JsAstNodeList(CurrentPositionContext(), this) }; } else { expression.Context = newContexts[newContexts.Count - 1]; } ((JsCallNode)expression).IsConstructor = true; newContexts.RemoveAt(newContexts.Count - 1); } if (callError != null) { callError._partiallyComputedNode = expression; throw callError; } GetNextToken(); break; case JsToken.LeftBracket: m_noSkipTokenSet.Add(NoSkipTokenSet.s_BracketToken); try { // // ROTOR parses a[b,c] as a call to a, passing in the arguments b and c. // the correct parse is a member lookup on a of c -- the "b,c" should be // a single expression with a comma operator that evaluates b but only // returns c. // So we'll change the default behavior from parsing an expression list to // parsing a single expression, but returning a single-item list (or an empty // list if there is no expression) so the rest of the code will work. // //args = ParseExpressionList(JSToken.RightBracket); GetNextToken(); args = new JsAstNodeList(CurrentPositionContext(), this); JsAstNode accessor = ParseExpression(); if (accessor != null) { args.Append(accessor); } } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_BracketToken, exc) == -1) { if (exc._partiallyComputedNode != null) { exc._partiallyComputedNode = new JsCallNode(expression.Context.CombineWith(m_currentToken.Clone()), this) { Function = expression, Arguments = (JsAstNodeList)exc._partiallyComputedNode, InBrackets = true }; } else { exc._partiallyComputedNode = expression; } throw; } else args = (JsAstNodeList)exc._partiallyComputedNode; } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BracketToken); } expression = new JsCallNode(expression.Context.CombineWith(m_currentToken.Clone()), this) { Function = expression, Arguments = args, InBrackets = true }; // there originally was code here in the ROTOR sources that checked the new context list and // changed this member call to a constructor call, effectively combining the two. I believe they // need to remain separate. // remove the close bracket token GetNextToken(); break; case JsToken.AccessField: JsConstantWrapper id = null; JsContext nameContext = m_currentToken.Clone(); GetNextToken(); if (JsToken.Identifier != m_currentToken.Token) { string identifier = JsKeyword.CanBeIdentifier(m_currentToken.Token); if (null != identifier) { // don't report an error here -- it's actually okay to have a property name // that is a keyword which is okay to be an identifier. For instance, // jQuery has a commonly-used method named "get" to make an ajax request //ForceReportInfo(JSError.KeywordUsedAsIdentifier); id = new JsConstantWrapper(identifier, JsPrimitiveType.String, m_currentToken.Clone(), this); } else if (JsScanner.IsValidIdentifier(m_currentToken.Code)) { // it must be a keyword, because it can't technically be an identifier, // but it IS a valid identifier format. Throw a warning but still // create the constant wrapper so we can output it as-is ReportError(JsError.KeywordUsedAsIdentifier, m_currentToken.Clone(), true); id = new JsConstantWrapper(m_currentToken.Code, JsPrimitiveType.String, m_currentToken.Clone(), this); } else { ReportError(JsError.NoIdentifier); SkipTokensAndThrow(expression); } } else { id = new JsConstantWrapper(m_scanner.Identifier, JsPrimitiveType.String, m_currentToken.Clone(), this); } GetNextToken(); expression = new JsMember(expression.Context.CombineWith(id.Context), this) { Root = expression, Name = id.Context.Code, NameContext = nameContext.CombineWith(id.Context) }; break; default: if (null != newContexts) { while (newContexts.Count > 0) { (newContexts[newContexts.Count - 1]).UpdateWith(expression.Context); expression = new JsCallNode(newContexts[newContexts.Count - 1], this) { Function = expression, Arguments = new JsAstNodeList(CurrentPositionContext(), this) }; ((JsCallNode)expression).IsConstructor = true; newContexts.RemoveAt(newContexts.Count - 1); } } return expression; } } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_MemberExprNoSkipTokenSet, exc) != -1) expression = exc._partiallyComputedNode; else { Debug.Assert(exc._partiallyComputedNode == expression); throw; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_MemberExprNoSkipTokenSet); } } }
public void Visit(JsAstNodeList node) { if (node != null) { for (var ndx = 0; ndx < node.Count; ++ndx) { if (ndx > 0) { m_writer.Write(','); } if (node[ndx] != null) { node[ndx].Accept(this); } } } }
private static void RotateOpeator(JsBinaryOperator node, JsAstNodeList rightSide) { if (rightSide.Count == 0) { // the list is empty -- remove the node altogether node.Parent.ReplaceChild(node, null); } else if (rightSide.Count == 1) { // the list has only one item -- replace the node with the one item node.Parent.ReplaceChild(node, rightSide[0]); } else if (rightSide.Count == 2) { // there are only two items -- rotate the first to the left-hand side // and replace the right-hand side with the second item node.Operand1 = rightSide[0]; node.Operand2 = rightSide[1]; } else { // there will still be more than one left in the list after we peel off the // first one. rotate the first item to the left-hand side var temp = rightSide[0]; rightSide.RemoveAt(0); node.Operand1 = temp; } }