public void Visit(DirectivePrologue node) { Debug.Fail("shouldn't get here"); }
public void Visit(DirectivePrologue node) { // invalid! ignore IsValid = false; }
private static int RelocateDirectivePrologue(Block block, int insertAt, DirectivePrologue directivePrologue) { // skip over any important comments while (insertAt < block.Count && (block[insertAt] is ImportantComment)) { ++insertAt; } // if the one we want to insert is already at this spot, then we're good to go if (block[insertAt] != directivePrologue) { // remove it from where it is right now and insert it into the proper location directivePrologue.Parent.ReplaceChild(directivePrologue, null); block.Insert(insertAt, directivePrologue); } // and move up to the next slot return ++insertAt; }
public override void Visit(DirectivePrologue node) { if (node != null) { // if this is a minification hint, then process it now // and then remove it. Otherwise treat it as a directive prologue that // we need to preserve if (IsMinificationHint(node)) { node.Parent.ReplaceChild(node, null); } else { // no need to call the base, just add it to the list if (m_moduleDirectives == null) { m_moduleDirectives = new List<DirectivePrologue>(); } m_moduleDirectives.Add(node); } } }
public void Visit(DirectivePrologue node) { if (node != null) { node.Index = NextOrderIndex; if (node.UseStrict) { CurrentVariableScope.UseStrict = true; } } }
public void Visit(DirectivePrologue node) { // not applicable; terminate }
private AstNode ParseStatement(bool fSourceElement) { AstNode statement = null; if (m_importantComments.Count > 0 && m_settings.PreserveImportantComments && m_settings.IsModificationAllowed(TreeModifications.PreserveImportantComments)) { // we have at least one important comment before the upcoming statement. // pop the first important comment off the queue, return that node instead. // don't advance the token -- we'll probably be coming back again for the next one (if any) statement = new ImportantComment(m_importantComments[0], this); m_importantComments.RemoveAt(0); } else { String id = null; var isNewModule = m_newModule; switch (m_currentToken.Token) { case JSToken.EndOfFile: EOFError(JSError.ErrorEndOfFile); throw new EndOfFileException(); // abort parsing, get back to the main parse routine case JSToken.Semicolon: // make an empty statement statement = new EmptyStatement(m_currentToken.Clone(), this); GetNextToken(); return statement; case JSToken.RightCurly: ReportError(JSError.SyntaxError); SkipTokensAndThrow(); break; case JSToken.LeftCurly: return ParseBlock(); case JSToken.Debugger: return ParseDebuggerStatement(); case JSToken.Var: case JSToken.Const: case JSToken.Let: return ParseVariableStatement(); case JSToken.If: return ParseIfStatement(); case JSToken.For: return ParseForStatement(); case JSToken.Do: return ParseDoStatement(); case JSToken.While: return ParseWhileStatement(); case JSToken.Continue: statement = ParseContinueStatement(); if (null == statement) return new Block(CurrentPositionContext(), this); else return statement; case JSToken.Break: statement = ParseBreakStatement(); if (null == statement) return new Block(CurrentPositionContext(), this); else return statement; case JSToken.Return: statement = ParseReturnStatement(); if (null == statement) return new Block(CurrentPositionContext(), this); else return statement; case JSToken.With: return ParseWithStatement(); case JSToken.Switch: return ParseSwitchStatement(); case JSToken.Throw: statement = ParseThrowStatement(); if (statement == null) return new Block(CurrentPositionContext(), this); else break; case JSToken.Try: return ParseTryStatement(); case JSToken.Function: // parse a function declaration FunctionObject function = ParseFunction(FunctionType.Declaration, m_currentToken.Clone()); function.IsSourceElement = fSourceElement; return function; case JSToken.Else: ReportError(JSError.InvalidElse); SkipTokensAndThrow(); break; case JSToken.ConditionalCommentStart: return ParseStatementLevelConditionalComment(fSourceElement); case JSToken.ConditionalCompilationOn: { ConditionalCompilationOn ccOn = new ConditionalCompilationOn(m_currentToken.Clone(), this); GetNextToken(); return ccOn; } case JSToken.ConditionalCompilationSet: return ParseConditionalCompilationSet(); case JSToken.ConditionalCompilationIf: return ParseConditionalCompilationIf(false); case JSToken.ConditionalCompilationElseIf: return ParseConditionalCompilationIf(true); case JSToken.ConditionalCompilationElse: { ConditionalCompilationElse elseStatement = new ConditionalCompilationElse(m_currentToken.Clone(), this); GetNextToken(); return elseStatement; } case JSToken.ConditionalCompilationEnd: { ConditionalCompilationEnd endStatement = new ConditionalCompilationEnd(m_currentToken.Clone(), this); GetNextToken(); return endStatement; } default: m_noSkipTokenSet.Add(NoSkipTokenSet.s_EndOfStatementNoSkipTokenSet); bool exprError = false; try { bool bAssign; // if this statement starts with a function within parens, we want to know now bool parenFunction = (m_currentToken.Token == JSToken.LeftParenthesis && PeekToken() == JSToken.Function); statement = ParseUnaryExpression(out bAssign, false); if (statement != null && parenFunction) { FunctionObject functionObject = statement.LeftHandSide as FunctionObject; if (functionObject != null) { functionObject.LeftHandFunctionExpression = true; } } // look for labels if (statement is Lookup && JSToken.Colon == m_currentToken.Token) { // can be a label id = statement.ToString(); if (m_labelTable.ContainsKey(id)) { // there is already a label with that name. Ignore the current label ReportError(JSError.BadLabel, statement.Context.Clone(), true); id = null; GetNextToken(); // skip over ':' return new Block(CurrentPositionContext(), this); } else { var colonContext = m_currentToken.Clone(); GetNextToken(); int labelNestCount = m_labelTable.Count + 1; m_labelTable.Add(id, new LabelInfo(m_blockType.Count, labelNestCount)); if (JSToken.EndOfFile != m_currentToken.Token) { statement = new LabeledStatement(statement.Context.Clone(), this) { Label = id, ColonContext = colonContext, NestCount = labelNestCount, Statement = ParseStatement(fSourceElement) }; } else { // end of the file! //just pass null for the labeled statement statement = new LabeledStatement(statement.Context.Clone(), this) { Label = id, ColonContext = colonContext, NestCount = labelNestCount }; } m_labelTable.Remove(id); return statement; } } statement = ParseExpression(statement, false, bAssign, JSToken.None); // if we just started a new module and this statement happens to be an expression statement... if (isNewModule && statement.IsExpression) { // see if it's a constant wrapper var constantWrapper = statement as ConstantWrapper; if (constantWrapper != null && constantWrapper.PrimitiveType == PrimitiveType.String) { // we found a string constant expression statement right after the start of a new // module. Let's make it a DirectivePrologue if it isn't already if (!(statement is DirectivePrologue)) { statement = new DirectivePrologue(constantWrapper.Value.ToString(), constantWrapper.Context, this) { MayHaveIssues = constantWrapper.MayHaveIssues }; } } } var binaryOp = statement as BinaryOperator; if (binaryOp != null && (binaryOp.OperatorToken == JSToken.Equal || binaryOp.OperatorToken == JSToken.StrictEqual)) { // an expression statement with equality operator? Doesn't really do anything. // Did the developer intend this to be an assignment operator instead? Low-pri warning. binaryOp.OperatorContext.IfNotNull(c => c.HandleError(JSError.SuspectEquality, false)); } var lookup = statement as Lookup; if (lookup != null && lookup.Name.StartsWith("<%=", StringComparison.Ordinal) && lookup.Name.EndsWith("%>", StringComparison.Ordinal)) { // single lookup, but it's actually one or more ASP.NET blocks. // convert back to an asp.net block node statement = new AspNetBlockNode(statement.Context, this) { AspNetBlockText = lookup.Name }; } var aspNetBlock = statement as AspNetBlockNode; if (aspNetBlock != null && JSToken.Semicolon == m_currentToken.Token) { aspNetBlock.IsTerminatedByExplicitSemicolon = true; statement.IfNotNull(s => s.TerminatingContext = m_currentToken.Clone()); GetNextToken(); } // we just parsed an expression statement. Now see if we have an appropriate // semicolon to terminate it. if (JSToken.Semicolon == m_currentToken.Token) { statement.IfNotNull(s => s.TerminatingContext = m_currentToken.Clone()); GetNextToken(); } else if (m_foundEndOfLine || JSToken.RightCurly == m_currentToken.Token || JSToken.EndOfFile == m_currentToken.Token) { // semicolon insertion rules // (if there was no statement parsed, then don't fire a warning) // a right-curly or an end of line is something we don't WANT to throw a warning for. // Just too common and doesn't really warrant a warning (in my opinion) if (statement != null && JSToken.RightCurly != m_currentToken.Token && JSToken.EndOfFile != m_currentToken.Token) { ReportError(JSError.SemicolonInsertion, statement.Context.IfNotNull(c => c.FlattenToEnd()), true); } } else { ReportError(JSError.NoSemicolon, true); } } catch (RecoveryTokenException exc) { if (exc._partiallyComputedNode != null) statement = exc._partiallyComputedNode; if (statement == null) { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_EndOfStatementNoSkipTokenSet); exprError = true; SkipTokensAndThrow(); } if (IndexOfToken(NoSkipTokenSet.s_EndOfStatementNoSkipTokenSet, exc) == -1) { exc._partiallyComputedNode = statement; throw; } } finally { if (!exprError) m_noSkipTokenSet.Remove(NoSkipTokenSet.s_EndOfStatementNoSkipTokenSet); } break; } } return statement; }
//--------------------------------------------------------------------------------------- // ParseStatements // // statements : // <empty> | // statement statements // //--------------------------------------------------------------------------------------- private Block ParseStatements() { var program = new Block(CurrentPositionContext(), this); m_blockType.Add(BlockType.Block); m_useCurrentForNext = false; try { // get the first token GetNextToken(); // if the block doesn't have a proper file context, then let's set it from the // first token -- that token might have had a ///#source directive! if (string.IsNullOrEmpty(program.Context.Document.FileContext)) { program.Context.Document.FileContext = m_currentToken.Document.FileContext; } m_noSkipTokenSet.Add(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); m_noSkipTokenSet.Add(NoSkipTokenSet.s_TopLevelNoSkipTokenSet); try { var possibleDirectivePrologue = true; int lastEndPosition = m_currentToken.EndPosition; while (m_currentToken.Token != JSToken.EndOfFile) { AstNode ast = null; try { // parse a statement -- pass true because we really want a SourceElement, // which is a Statement OR a FunctionDeclaration. Technically, FunctionDeclarations // are not statements! ast = ParseStatement(true); // if we are still possibly looking for directive prologues if (possibleDirectivePrologue) { var constantWrapper = ast as ConstantWrapper; if (constantWrapper != null && constantWrapper.PrimitiveType == PrimitiveType.String) { if (!(constantWrapper is DirectivePrologue)) { // use a directive prologue node instead ast = new DirectivePrologue(constantWrapper.Value.ToString(), ast.Context, ast.Parser) { MayHaveIssues = constantWrapper.MayHaveIssues }; } } else if (!m_newModule) { // nope -- no longer finding directive prologues possibleDirectivePrologue = false; } } else if (m_newModule) { // we aren't looking for directive prologues anymore, but we did scan // into a new module after that last AST, so reset the flag because that // new module might have directive prologues for next time possibleDirectivePrologue = true; } } catch (RecoveryTokenException exc) { if (TokenInList(NoSkipTokenSet.s_TopLevelNoSkipTokenSet, exc) || TokenInList(NoSkipTokenSet.s_StartStatementNoSkipTokenSet, exc)) { ast = exc._partiallyComputedNode; GetNextToken(); } else { m_useCurrentForNext = false; do { GetNextToken(); } while (m_currentToken.Token != JSToken.EndOfFile && !TokenInList(NoSkipTokenSet.s_TopLevelNoSkipTokenSet, m_currentToken.Token) && !TokenInList(NoSkipTokenSet.s_StartStatementNoSkipTokenSet, m_currentToken.Token)); } } if (null != ast) { // append the token to the program program.Append(ast); // set the last end position to be the start of the current token. // if we parse the next statement and the end is still the start, we know // something is up and might get into an infinite loop. lastEndPosition = m_currentToken.EndPosition; } else if (!m_scanner.IsEndOfFile && m_currentToken.StartLinePosition == lastEndPosition) { // didn't parse a statement, we're not at the EOF, and we didn't move // anywhere in the input stream. If we just keep looping around, we're going // to get into an infinite loop. Break it. m_currentToken.HandleError(JSError.ApplicationError, true); break; } } if (m_importantComments.Count > 0 && m_settings.PreserveImportantComments && m_settings.IsModificationAllowed(TreeModifications.PreserveImportantComments)) { // we have important comments before the EOF. Add the comment(s) to the program. foreach(var importantComment in m_importantComments) { program.Append(new ImportantComment(importantComment, this)); } m_importantComments.Clear(); } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_TopLevelNoSkipTokenSet); m_noSkipTokenSet.Remove(NoSkipTokenSet.s_StartStatementNoSkipTokenSet); } } catch (EndOfFileException) { } catch (ScannerException se) { // a scanner exception implies that the end of file has been reached with an error. // Mark the end of file as the error location EOFError(se.Error); } program.UpdateWith(CurrentPositionContext()); return program; }
private FunctionObject ParseFunction(FunctionType functionType, Context fncCtx) { Lookup name = null; AstNodeList formalParameters = null; Block body = null; bool inExpression = (functionType == FunctionType.Expression); Context paramsContext = null; GetNextToken(); // get the function name or make an anonymous function if in expression "position" if (JSToken.Identifier == m_currentToken.Token) { name = new Lookup(m_currentToken.Clone(), this) { Name = m_scanner.Identifier }; GetNextToken(); } else { string identifier = JSKeyword.CanBeIdentifier(m_currentToken.Token); if (null != identifier) { name = new Lookup(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 Lookup(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 AstNodeList(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 { ParameterDeclaration 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 ParameterDeclaration(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 Block(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 ConstantWrapper; if (constantWrapper != null && constantWrapper.PrimitiveType == PrimitiveType.String) { // if it's already a directive prologues, we're good to go if (!(constantWrapper is DirectivePrologue)) { // make the statement a directive prologue instead of a constant wrapper statement = new DirectivePrologue(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 if (m_importantComments.Count > 0 && m_settings.PreserveImportantComments && m_settings.IsModificationAllowed(TreeModifications.PreserveImportantComments)) { // we have important comments before the EOF. Add the comment(s) to the program. foreach (var importantComment in m_importantComments) { body.Append(new ImportantComment(importantComment, this)); } m_importantComments.Clear(); } body.Context.UpdateWith(m_currentToken); fncCtx.UpdateWith(m_currentToken); } catch (EndOfFileException) { // 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 FunctionObject(fncCtx, this) { FunctionType = (inExpression ? FunctionType.Expression : FunctionType.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 FunctionObject(fncCtx, this) { FunctionType = functionType, IdContext = name.IfNotNull(n => n.Context), Name = name.IfNotNull(n => n.Name), ParameterDeclarations = formalParameters, ParametersContext = paramsContext, Body = body }; }