Пример #1
0
        public static void Apply(JsBlock block, JsParser parser)
        {
            // create a new instance of the visitor and apply it to the block
            var visitor = new JsReorderScopeVisitor(parser);

            block.Accept(visitor);

            // if there were any module directive prologues we need to promote, do them first
            var insertAt = 0;

            if (visitor.m_moduleDirectives != null)
            {
                foreach (var directivePrologue in visitor.m_moduleDirectives)
                {
                    insertAt = RelocateDirectivePrologue(block, insertAt, directivePrologue);
                }
            }

            // Make sure that we skip over any remaining comments and directive prologues.
            // we do NOT want to insert anything between the start of the scope and any directive prologues.
            while (insertAt < block.Count &&
                   (block[insertAt] is JsDirectivePrologue || block[insertAt] is JsImportantComment))
            {
                ++insertAt;
            }

            // first, we want to move all function declarations to the top of this block
            if (visitor.m_functionDeclarations != null)
            {
                foreach (var funcDecl in visitor.m_functionDeclarations)
                {
                    insertAt = RelocateFunction(block, insertAt, funcDecl);
                }
            }

            // special case: if there is only one var statement in the entire scope,
            // then just leave it alone because we will only add bytes by moving it around,
            // or be byte-neutral at best (no initializers and not in a for-statement).
            if (visitor.m_varStatements != null && visitor.m_varStatements.Count > 1)
            {
                // then we want to move all variable declarations after to the top (after the functions)
                foreach (var varStatement in visitor.m_varStatements)
                {
                    insertAt = RelocateVar(block, insertAt, varStatement);
                }
            }

            // then we want to do the same thing for all child functions (declarations AND other)
            if (visitor.m_functionDeclarations != null)
            {
                foreach (var funcDecl in visitor.m_functionDeclarations)
                {
                    Apply(funcDecl.Body, parser);
                }
            }

            if (visitor.m_functionExpressions != null)
            {
                foreach (var funcExpr in visitor.m_functionExpressions)
                {
                    Apply(funcExpr.Body, parser);
                }
            }
        }
Пример #2
0
        /// <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;
        }
        public static void Apply(JsBlock block, JsParser parser)
        {
            // create a new instance of the visitor and apply it to the block
            var visitor = new JsReorderScopeVisitor(parser);
            block.Accept(visitor);

            // if there were any module directive prologues we need to promote, do them first
            var insertAt = 0;
            if (visitor.m_moduleDirectives != null)
            {
                foreach (var directivePrologue in visitor.m_moduleDirectives)
                {
                    insertAt = RelocateDirectivePrologue(block, insertAt, directivePrologue);
                }
            }

            // Make sure that we skip over any remaining comments and directive prologues.
            // we do NOT want to insert anything between the start of the scope and any directive prologues.
            while (insertAt < block.Count
                && (block[insertAt] is JsDirectivePrologue || block[insertAt] is JsImportantComment))
            {
                ++insertAt;
            }

            // first, we want to move all function declarations to the top of this block
            if (visitor.m_functionDeclarations != null)
            {
                foreach (var funcDecl in visitor.m_functionDeclarations)
                {
                    insertAt = RelocateFunction(block, insertAt, funcDecl);
                }
            }

            // special case: if there is only one var statement in the entire scope,
            // then just leave it alone because we will only add bytes by moving it around,
            // or be byte-neutral at best (no initializers and not in a for-statement).
            if (visitor.m_varStatements != null && visitor.m_varStatements.Count > 1)
            {
                // then we want to move all variable declarations after to the top (after the functions)
                foreach (var varStatement in visitor.m_varStatements)
                {
                    insertAt = RelocateVar(block, insertAt, varStatement);
                }
            }

            // then we want to do the same thing for all child functions (declarations AND other)
            if (visitor.m_functionDeclarations != null)
            {
                foreach (var funcDecl in visitor.m_functionDeclarations)
                {
                    Apply(funcDecl.Body, parser);
                }
            }

            if (visitor.m_functionExpressions != null)
            {
                foreach (var funcExpr in visitor.m_functionExpressions)
                {
                    Apply(funcExpr.Body, parser);
                }
            }
        }
        private void OutputBlockWithBraces(JsBlock block)
        {
            if (m_settings.BlocksStartOnSameLine == MinifierBlockStart.NewLine
                || (m_settings.BlocksStartOnSameLine == MinifierBlockStart.UseSource && block.BraceOnNewLine))
            {
                NewLine();
            }
            else if (m_settings.OutputMode == MinifierOutputMode.MultipleLines)
            {
                OutputPossibleLineBreak(' ');
            }

            block.Accept(this);
        }