private Block InternalParse()

in AjaxMinDll/JavaScript/jsparser.cs [270:509]


        private Block InternalParse()
        {
            // if the settings list is not null, use it to initialize a new list
            // with the same settings. If it is null, initialize an empty list 
            // because we already determined that we want to strip debug statements,
            // and the scanner might add items to the list as it scans the source.
            DebugLookups = new HashSet<string>(m_settings.DebugLookupCollection);

            // pass our list to the scanner -- it might add more as we encounter special comments
            m_scanner.DebugLookupCollection = DebugLookups;

            m_scanner.AllowEmbeddedAspNetBlocks = m_settings.AllowEmbeddedAspNetBlocks;
            m_scanner.IgnoreConditionalCompilation = m_settings.IgnoreConditionalCompilation;

            // set any defines
            m_scanner.UsePreprocessorDefines = !m_settings.IgnorePreprocessorDefines;
            if (m_scanner.UsePreprocessorDefines)
            {
                m_scanner.SetPreprocessorDefines(m_settings.PreprocessorValues);
            }

            // if we want to strip debug statements, let's also strip ///#DEBUG comment
            // blocks for legacy reasons. ///#DEBUG will get stripped ONLY is this
            // flag is true AND the name "DEBUG" is not in the preprocessor defines.
            // Alternately, we will keep those blocks in the output is this flag is
            // set to false OR we define "DEBUG" in the preprocessor defines.
            m_scanner.StripDebugCommentBlocks = m_settings.StripDebugStatements;

            // assume ES5 unless we find ES6-specific constructs
            ParsedVersion = ScriptVersion.EcmaScript5;

            // 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();

            // get the first token.
            GetNextToken();

            Block scriptBlock = null;
            Block returnBlock = null;
            switch (m_settings.SourceMode)
            {
                case JavaScriptSourceMode.Program:
                    // simply parse a block of statements.
                    // however, when parsing this block, we mght determine it's really part of
                    // a larger structure, and we could return a different block that we would need
                    // to continue processing.
                    scriptBlock = returnBlock = ParseStatements(new Block(CurrentPositionContext)
                        {
                            EnclosingScope = this.GlobalScope
                        });
                    break;

                case JavaScriptSourceMode.Module:
                    // an implicit module as referenced by an import statement.
                    // create a root block with the global scope, add a module with its module body,
                    // then parse the input as statements into the module body.
                    returnBlock = scriptBlock = new Block(CurrentPositionContext)
                        {
                            EnclosingScope = this.GlobalScope
                        };
                    var module = new ModuleDeclaration(CurrentPositionContext)
                        {
                            IsImplicit = true,
                            Body = new Block(CurrentPositionContext)
                                {
                                    IsModule = true
                                }
                        };
                    scriptBlock.Append(module);

                    // we just created an implicit ES6 module, so we are already parsing as ES6
                    ParsedVersion = ScriptVersion.EcmaScript6;

                    // we don't need to worry about this function returning a different block, because
                    // we've already created the module structure.
                    ParseStatements(module.Body);
                    break;

                case JavaScriptSourceMode.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 Block(CurrentPositionContext)
                        {
                            EnclosingScope = this.GlobalScope
                        };
                    try
                    {
                        var expr = ParseExpression();
                        if (expr != null)
                        {
                            scriptBlock.Append(expr);
                            scriptBlock.UpdateWith(expr.Context);
                        }
                    }
                    catch (EndOfStreamException)
                    {
                        Debug.WriteLine("EOF");
                    }
                    break;

                case JavaScriptSourceMode.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 Block(CurrentPositionContext)
                        {
                            EnclosingScope = this.GlobalScope
                        };

                    var parameters = new AstNodeList(CurrentPositionContext);
                    parameters.Append(new ParameterDeclaration(CurrentPositionContext)
                        {
                            Binding = new BindingIdentifier(CurrentPositionContext)
                            {
                                Name = "event",
                                RenameNotAllowed = true
                            }
                        });

                    var funcExpression = new FunctionObject(CurrentPositionContext)
                        {
                            FunctionType = FunctionType.Expression,
                            ParameterDeclarations = parameters,
                            Body = new Block(CurrentPositionContext)
                        };
                    scriptBlock.Append(funcExpression);
                    ParseFunctionBody(funcExpression.Body);

                    // but we ONLY want to return the body
                    returnBlock = funcExpression.Body;
                    break;

                default:
                    Debug.Fail("Unexpected source mode enumeration");
                    return null;
            }

            timePoints[--timeIndex] = stopWatch.ElapsedTicks;

            // resolve everything
            ResolutionVisitor.Apply(scriptBlock, GlobalScope, this);
            timePoints[--timeIndex] = stopWatch.ElapsedTicks;

            if (Settings.AmdSupport)
            {
                // we're doing some AMD support. At this time, walk through the top-level
                // statements and if there are any duplicate define(name... calls, remove all
                // but the last one.
                RemoveDuplicateDefines(scriptBlock);
            }

            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. 
                ReorderScopeVisitor.Apply(scriptBlock, this);

                timePoints[--timeIndex] = stopWatch.ElapsedTicks;

                // analyze the entire node tree (needed for hypercrunch)
                // root to leaf (top down)
                var analyzeVisitor = new AnalyzeNodeVisitor(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 != LocalRenaming.KeepAll
                    && m_settings.IsModificationAllowed(TreeModifications.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 EvaluateLiteralVisitor(this);
                    scriptBlock.Accept(visitor);
                }

                timePoints[--timeIndex] = stopWatch.ElapsedTicks;

                // make the final cleanup pass
                FinalPassVisitor.Apply(scriptBlock, m_settings);

                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;
                stopWatch.Stop();
            }

            // mark all child scopes under the global scope as existing so we don't go and re-optimize
            // them again should we parse another piece of source code using the same parser
            foreach (var childScope in GlobalScope.ChildScopes)
            {
                childScope.Existing = true;
            }

            // if the return block is not the entire script block, then we don't want to include
            // any thing we must've generated in the process of building our code. Take the scope
            // from the parent, but break the tree relationship.
            if (returnBlock != scriptBlock)
            {
                returnBlock.EnclosingScope = returnBlock.Parent.EnclosingScope;
                returnBlock.Parent = null;
            }

            return returnBlock;
        }