//--------------------------------------------------------------------------------------- // ReportError // // Generate a parser error. // The function is told whether or not next call to GetToken() should return the same // token or not //--------------------------------------------------------------------------------------- private void ReportError(JsError errorId, JsContext context, bool skipToken) { Debug.Assert(context != null); int previousSeverity = m_severity; m_severity = JsException.GetSeverity(errorId); // EOF error is special and it's the last error we can possibly get if (JsToken.EndOfFile == context.Token) EOFError(errorId); // EOF context is special else { // report the error if not in error condition and the // error for this token is not worse than the one for the // previous token if (m_goodTokensProcessed > 0 || m_severity < previousSeverity) context.HandleError(errorId); // reset proper info if (skipToken) m_goodTokensProcessed = -1; else { m_useCurrentForNext = true; m_goodTokensProcessed = 0; } } }
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 }; }