Ejemplo n.º 1
0
        public override AstNode Clone()
        {
            // creates a new EMPTY statement
            Var newVar = new Var((Context == null ? null : Context.Clone()), Parser);

            // now go through and clone all the actual declarations
            for (int ndx = 0; ndx < m_list.Count; ++ndx)
            {
                if (m_list[ndx] != null)
                {
                    // cloning the declaration will add a field to the current scope
                    // (better already be in the proper scope chain)
                    newVar.Append(m_list[ndx].Clone());
                }
            }
            return(newVar);
        }
Ejemplo n.º 2
0
        internal void AddGeneratedVar(string name, AstNode initializer, bool isLiteral)
        {
            // if the body is empty, create one now
            if (Body == null)
            {
                Body = new Block(null, Parser);
            }

            // see if the first statement in the body (if any) is a var already
            Var var = null;

            if (Body.Count > 0)
            {
                var = Body[0] as Var;
            }

            VariableDeclaration varDecl = new VariableDeclaration(
                null,
                Parser,
                name,
                new Context(Parser),
                initializer,
                (isLiteral ? FieldAttributes.Literal : 0)
                );

            varDecl.IsGenerated = true;

            // make sure we set the crunchability of this field to TRUE. Doesn't matter
            // whether it's a global or within a with-scope or what-have-you. It didn't
            // exist in the sources (we are generating it now) so we can rename it whatever
            // the heck we want.
            varDecl.Field.CanCrunch = true;

            if (var != null)
            {
                // the first statement is a var; just add a new declaration to the front
                var.InsertAt(0, varDecl);
            }
            else
            {
                // not a var; create a new one
                var = new Var(null, Parser);
                var.Append(varDecl);
                Body.Insert(0, var);
            }
        }
Ejemplo n.º 3
0
        //---------------------------------------------------------------------------------------
        // ParseVariableStatement
        //
        //  VariableStatement :
        //    'var' VariableDeclarationList
        //
        //  VariableDeclarationList :
        //    VariableDeclaration |
        //    VariableDeclaration ',' VariableDeclarationList
        //
        //  VariableDeclaration :
        //    Identifier Initializer
        //
        //  Initializer :
        //    <empty> |
        //    '=' AssignmentExpression
        //---------------------------------------------------------------------------------------
        private AstNode ParseVariableStatement(FieldAttributes visibility)
        {
            Var varList = new Var(m_currentToken.Clone(), this);
            bool single = true;
            AstNode vdecl = null;
            AstNode identInit = null;

            for (; ; )
            {
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_EndOfLineToken);
                try
                {
                    identInit = ParseIdentifierInitializer(JSToken.None, visibility);
                }
                catch (RecoveryTokenException exc)
                {
                    // an exception is passing by, possibly bringing some info, save the info if any
                    if (exc._partiallyComputedNode != null)
                    {
                        if (!single)
                        {
                            varList.Append(exc._partiallyComputedNode);
                            varList.Context.UpdateWith(exc._partiallyComputedNode.Context);
                            exc._partiallyComputedNode = varList;
                        }
                    }
                    if (IndexOfToken(NoSkipTokenSet.s_EndOfLineToken, exc) == -1)
                        throw;
                    else
                    {
                        if (single)
                            identInit = exc._partiallyComputedNode;
                    }
                }
                finally
                {
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_EndOfLineToken);
                }

                if (identInit != null)
                {
                    vdecl = identInit;
                    varList.Append(vdecl);
                }

                if (JSToken.Semicolon == m_currentToken.Token
                    || JSToken.RightCurly == m_currentToken.Token)
                {
                    if (JSToken.Semicolon == m_currentToken.Token)
                    {
                        vdecl.Context.UpdateWith(m_currentToken);
                        GetNextToken();
                    }
                    break;
                }
                
                if (JSToken.Comma == m_currentToken.Token)
                {
                    single = false;
                    continue;
                }
                
                if (m_scanner.GotEndOfLine)
                {
                    break;
                }

                // assume the variable statement was terminated and move on
                ReportError(JSError.NoSemicolon, true);
                break;
            }

            if (vdecl != null)
            {
                varList.Context.UpdateWith(vdecl.Context);
            }
            return varList;
        }
Ejemplo n.º 4
0
        //---------------------------------------------------------------------------------------
        // ParseForStatement
        //
        //  ForStatement :
        //    'for' '(' OptionalExpressionNoIn ';' OptionalExpression ';' OptionalExpression ')'
        //    'for' '(' 'var' VariableDeclarationListNoIn ';' OptionalExpression ';' OptionalExpression ')'
        //    'for' '(' LeftHandSideExpression 'in' Expression')'
        //    'for' '(' 'var' Identifier OptionalInitializerNoIn 'in' Expression')'
        //
        //  OptionalExpressionNoIn :
        //    <empty> |
        //    ExpressionNoIn // same as Expression but does not process 'in' as an operator
        //
        //  OptionalInitializerNoIn :
        //    <empty> |
        //    InitializerNoIn // same as initializer but does not process 'in' as an operator
        //---------------------------------------------------------------------------------------
        private AstNode ParseForStatement()
        {
            m_blockType.Add(BlockType.Loop);
            AstNode forNode = null;
            try
            {
                Context forCtx = m_currentToken.Clone();
                GetNextToken();
                if (JSToken.LeftParenthesis != m_currentToken.Token)
                    ReportError(JSError.NoLeftParenthesis);
                GetNextToken();
                bool isForIn = false, recoveryInForIn = false;
                AstNode lhs = null, initializer = null, condOrColl = null, increment = null;

                try
                {
                    if (JSToken.Var == m_currentToken.Token)
                    {
                        isForIn = true;
                        Var varList = new Var(m_currentToken.Clone(), this);
                        varList.Append(ParseIdentifierInitializer(JSToken.In, (FieldAttributes)0));

                        // a list of variable initializers is allowed only in a for(;;)
                        while (JSToken.Comma == m_currentToken.Token)
                        {
                            isForIn = false;
                            varList.Append(ParseIdentifierInitializer(JSToken.In, (FieldAttributes)0));
                            //initializer = new Comma(initializer.context.CombineWith(var.context), initializer, var);
                        }

                        initializer = varList;

                        // if it could still be a for..in, now it's time to get the 'in'
                        if (isForIn)
                        {
                            if (JSToken.In == m_currentToken.Token)
                            {
                                GetNextToken();
                                condOrColl = ParseExpression();
                            }
                            else
                                isForIn = false;
                        }
                    }
                    else
                    {
                        if (JSToken.Semicolon != m_currentToken.Token)
                        {
                            bool isLHS;
                            initializer = ParseUnaryExpression(out isLHS, false);
                            if (isLHS && JSToken.In == m_currentToken.Token)
                            {
                                isForIn = true;
                                lhs = initializer;
                                initializer = null;
                                GetNextToken();
                                m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                                try
                                {
                                    condOrColl = ParseExpression();
                                }
                                catch (RecoveryTokenException exc)
                                {
                                    if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1)
                                    {
                                        exc._partiallyComputedNode = null;
                                        throw;
                                    }
                                    else
                                    {
                                        if (exc._partiallyComputedNode == null)
                                            condOrColl = new ConstantWrapper(true, PrimitiveType.Boolean, CurrentPositionContext(), this); // what could we put here?
                                        else
                                            condOrColl = exc._partiallyComputedNode;
                                    }
                                    if (exc._token == JSToken.RightParenthesis)
                                    {
                                        GetNextToken();
                                        recoveryInForIn = true;
                                    }
                                }
                                finally
                                {
                                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                                }
                            }
                            else
                                initializer = ParseExpression(initializer, false, isLHS, JSToken.In);
                        }
                    }
                }
                catch (RecoveryTokenException exc)
                {
                    // error is too early abort for
                    exc._partiallyComputedNode = null;
                    throw;
                }

                // at this point we know whether or not is a for..in
                if (isForIn)
                {
                    if (!recoveryInForIn)
                    {
                        if (JSToken.RightParenthesis != m_currentToken.Token)
                            ReportError(JSError.NoRightParenthesis);
                        forCtx.UpdateWith(m_currentToken);
                        GetNextToken();
                    }
                    AstNode body = null;
                    // if the statements aren't withing curly-braces, throw a possible error
                    if (JSToken.LeftCurly != m_currentToken.Token)
                    {
                        ReportError(JSError.StatementBlockExpected, forCtx, true);
                    }
                    try
                    {
                        // parse a Statement, not a SourceElement
                        body = ParseStatement(false);
                    }
                    catch (RecoveryTokenException exc)
                    {
                        if (exc._partiallyComputedNode == null)
                            body = new Block(CurrentPositionContext(), this);
                        else
                            body = exc._partiallyComputedNode;
                        exc._partiallyComputedNode = new ForIn(forCtx, this, (lhs != null ? lhs : initializer), condOrColl, body);
                        throw;
                    }
                    // for (a in b)
                    //      lhs = a, initializer = null
                    // for (var a in b)
                    //      lhs = null, initializer = var a
                    forNode = new ForIn(forCtx, this, (lhs != null ? lhs : initializer), condOrColl, body);
                }
                else
                {
                    m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                    try
                    {
                        if (JSToken.Semicolon != m_currentToken.Token)
                        {
                            ReportError(JSError.NoSemicolon);
                            if (JSToken.Colon == m_currentToken.Token)
                            {
                                m_noSkipTokenSet.Add(NoSkipTokenSet.s_VariableDeclNoSkipTokenSet);
                                try
                                {
                                    SkipTokensAndThrow();
                                }
                                catch (RecoveryTokenException)
                                {
                                    if (JSToken.Semicolon == m_currentToken.Token)
                                        m_errorToken = null;
                                    else
                                        throw;
                                }
                                finally
                                {
                                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_VariableDeclNoSkipTokenSet);
                                }
                            }
                        }
                        GetNextToken();
                        if (JSToken.Semicolon != m_currentToken.Token)
                        {
                            condOrColl = ParseExpression();
                            if (JSToken.Semicolon != m_currentToken.Token)
                                ReportError(JSError.NoSemicolon);
                        }

                        GetNextToken();
                        if (JSToken.RightParenthesis != m_currentToken.Token)
                            increment = ParseExpression();
                        if (JSToken.RightParenthesis != m_currentToken.Token)
                            ReportError(JSError.NoRightParenthesis);
                        forCtx.UpdateWith(m_currentToken);
                        GetNextToken();
                    }
                    catch (RecoveryTokenException exc)
                    {
                        if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1)
                        {
                            exc._partiallyComputedNode = null;
                            throw;
                        }
                        else
                        {
                            // discard any partial info, just genrate empty condition and increment and keep going
                            exc._partiallyComputedNode = null;
                            if (condOrColl == null)
                                condOrColl = new ConstantWrapper(true, PrimitiveType.Boolean, CurrentPositionContext(), this);
                        }
                        if (exc._token == JSToken.RightParenthesis)
                        {
                            GetNextToken();
                        }
                    }
                    finally
                    {
                        m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                    }
                    AstNode body = null;
                    // if the statements aren't withing curly-braces, throw a possible error
                    if (JSToken.LeftCurly != m_currentToken.Token)
                    {
                        ReportError(JSError.StatementBlockExpected, forCtx, true);
                    }
                    try
                    {
                        // parse a Statement, not a SourceElement
                        body = ParseStatement(false);
                    }
                    catch (RecoveryTokenException exc)
                    {
                        if (exc._partiallyComputedNode == null)
                            body = new Block(CurrentPositionContext(), this);
                        else
                            body = exc._partiallyComputedNode;
                        exc._partiallyComputedNode = new ForNode(forCtx, this, initializer, condOrColl, increment, body);
                        throw;
                    }
                    forNode = new ForNode(forCtx, this, initializer, condOrColl, increment, body);
                }
            }
            finally
            {
                m_blockType.RemoveAt(m_blockType.Count - 1);
            }

            return forNode;
        }
        private static int RelocateForInVar(Block block, int insertAt, Var varStatement, ForIn forIn)
        {
            // there should only be one decl in the for-in var statement. There should not be any initializer.
            // If not, then ignore it
            VariableDeclaration varDecl;

            if (varStatement.Count == 1 && (varDecl = varStatement[0]).Initializer == null)
            {
                // if there are more than three names, then we don't want to move them
                var bindingNames = BindingsVisitor.Bindings(varDecl.Binding);
                //if (bindingNames.Count < 3)
                {
                    // replace the varStatement in the for-in with a reference version of the binding
                    forIn.Variable = BindingTransform.FromBinding(varDecl.Binding);

                    // if this is a simple binding identifier, then leave it as-is. Otherwise we
                    // need to flatten it for the move to the front of the scope.
                    if (!(varDecl.Binding is BindingIdentifier))
                    {
                        // then flatten all the name in the binding and add them to the
                        var first = true;
                        foreach (var declName in bindingNames)
                        {
                            if (first)
                            {
                                varStatement[0] = new VariableDeclaration(declName.Context)
                                {
                                    Binding = new BindingIdentifier(declName.Context)
                                    {
                                        Name          = declName.Name,
                                        VariableField = declName.VariableField
                                    }
                                };
                                first = false;
                            }
                            else
                            {
                                // otherwise we want to insert a new one at the current position + 1
                                varStatement.Append(new VariableDeclaration(declName.Context)
                                {
                                    Binding = new BindingIdentifier(declName.Context)
                                    {
                                        Name          = declName.Name,
                                        VariableField = declName.VariableField
                                    }
                                });
                            }
                        }
                    }

                    // then move the var statement to the front of the scope
                    // if the statement at the insertion point is a var-statement already,
                    // then we just need to append our vardecls to it. Otherwise we'll insert our
                    // var statement at the right point
                    var existingVar = block[insertAt] as Var;
                    if (existingVar != null)
                    {
                        // append the varstatement we want to move to the existing var, which will
                        // transfer all the vardecls to it.
                        existingVar.Append(varStatement);
                    }
                    else
                    {
                        // insert it at the insert point
                        block.Insert(insertAt, varStatement);
                    }
                }
            }

            return(insertAt);
        }
Ejemplo n.º 6
0
        internal override void AnalyzeNode()
        {
            // javascript doesn't have block scope, so there really is no point
            // in nesting blocks. Unnest any now, before we start combining var statements
            UnnestBlocks();

            // if we want to remove debug statements...
            if (Parser.Settings.StripDebugStatements && Parser.Settings.IsModificationAllowed(TreeModifications.StripDebugStatements))
            {
                // do it now before we try doing other things
                StripDebugStatements();
            }

            // these variables are used to check for combining a particular type of
            // for-statement with preceding var-statements.
            ForNode targetForNode = null;
            string  targetName    = null;

            // check to see if we want to combine adjacent var statements
            bool combineVarStatements = Parser.Settings.IsModificationAllowed(TreeModifications.CombineVarStatements);

            // check to see if we want to combine a preceding var with a for-statement
            bool moveVarIntoFor = Parser.Settings.IsModificationAllowed(TreeModifications.MoveVarIntoFor);

            // look at the statements in the block.
            // if there are multiple var statements adjacent to each other, combine them.
            // walk BACKWARDS down the list because we'll be removing items when we encounter
            // multiple vars.
            // we also don't need to check the first one, since there is nothing before it.
            for (int ndx = m_list.Count - 1; ndx > 0; --ndx)
            {
                // if the previous node is not a Var, then we don't need to try and combine
                // it withthe current node
                Var previousVar = m_list[ndx - 1] as Var;
                if (previousVar != null)
                {
                    // see if THIS item is also a Var...
                    if (m_list[ndx] is Var && combineVarStatements)
                    {
                        // add the items in this VAR to the end of the previous
                        previousVar.Append(m_list[ndx]);

                        // delete this item from the block
                        m_list.RemoveAt(ndx);

                        // if we have a target for-node waiting for another comparison....
                        if (targetForNode != null)
                        {
                            // check to see if the variable we are looking for is in the new list
                            if (previousVar.Contains(targetName))
                            {
                                // IT DOES! we can combine the var statement with the initializer in the for-statement
                                // we already know it's a binaryop, or it wouldn't be a target for-statement
                                BinaryOperator binaryOp = targetForNode.Initializer as BinaryOperator;

                                // create a vardecl that matches our assignment initializer
                                // ignore duplicates because this scope will already have the variable defined.
                                VariableDeclaration varDecl = new VariableDeclaration(
                                    binaryOp.Context.Clone(),
                                    Parser,
                                    targetName,
                                    binaryOp.Operand1.Context.Clone(),
                                    binaryOp.Operand2,
                                    0,
                                    true
                                    );
                                // append it to the preceding var-statement
                                previousVar.Append(varDecl);

                                // move the previous vardecl to our initializer
                                targetForNode.ReplaceChild(targetForNode.Initializer, previousVar);

                                // and remove the previous var from the list.
                                m_list.RemoveAt(ndx - 1);
                                // this will bump the for node up one position in the list, so the next iteration
                                // will be right back on this node, but the initializer will not be null

                                // but now we no longer need the target mechanism -- the for-statement is
                                // not the current node again
                                targetForNode = null;
                            }
                        }
                    }
                    else if (moveVarIntoFor)
                    {
                        // see if this item is a ForNode
                        ForNode forNode = m_list[ndx] as ForNode;
                        if (forNode != null)
                        {
                            // and see if the forNode's initializer is empty
                            if (forNode.Initializer != null)
                            {
                                // not empty -- see if it is a Var node
                                Var varInitializer = forNode.Initializer as Var;
                                if (varInitializer != null)
                                {
                                    // we want to PREPEND the initializers in the previous var statement
                                    // to our for-statement's initializer list
                                    varInitializer.InsertAt(0, previousVar);

                                    // then remove the previous var statement
                                    m_list.RemoveAt(ndx - 1);
                                    // this will bump the for node up one position in the list, so the next iteration
                                    // will be right back on this node in case there are other var statements we need
                                    // to combine
                                }
                                else
                                {
                                    // see if the initializer is a simple assignment
                                    BinaryOperator binaryOp = forNode.Initializer as BinaryOperator;
                                    if (binaryOp != null && binaryOp.OperatorToken == JSToken.Assign)
                                    {
                                        // it is. See if it's a simple lookup
                                        Lookup lookup = binaryOp.Operand1 as Lookup;
                                        if (lookup != null)
                                        {
                                            // it is. see if that variable is in the previous var statement
                                            if (previousVar.Contains(lookup.Name))
                                            {
                                                // create a vardecl that matches our assignment initializer
                                                // ignore duplicates because this scope will already have the variable defined.
                                                VariableDeclaration varDecl = new VariableDeclaration(
                                                    binaryOp.Context.Clone(),
                                                    Parser,
                                                    lookup.Name,
                                                    lookup.Context.Clone(),
                                                    binaryOp.Operand2,
                                                    0,
                                                    true
                                                    );
                                                // append it to the var statement before us
                                                previousVar.Append(varDecl);

                                                // move the previous vardecl to our initializer
                                                forNode.ReplaceChild(forNode.Initializer, previousVar);

                                                // and remove the previous var from the list.
                                                m_list.RemoveAt(ndx - 1);
                                                // this will bump the for node up one position in the list, so the next iteration
                                                // will be right back on this node, but the initializer will not be null
                                            }
                                            else
                                            {
                                                // it's not in the immediately preceding var-statement, but that doesn't mean it won't be in
                                                // a var-statement immediately preceding that one -- in which case they'll get combined and
                                                // then it WILL be in the immediately preceding var-statement. So hold on to this
                                                // for statement and we'll check after we do a combine.
                                                targetForNode = forNode;
                                                targetName    = lookup.Name;
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                // if it's empty, then we're free to add the previous var statement
                                // to this for statement's initializer. remove it from it's current
                                // position and add it as the initializer
                                m_list.RemoveAt(ndx - 1);
                                forNode.ReplaceChild(forNode.Initializer, previousVar);
                                // this will bump the for node up one position in the list, so the next iteration
                                // will be right back on this node, but the initializer will not be null
                            }
                        }
                    }
                }
                else
                {
                    // not a var statement. make sure the target for-node is cleared.
                    targetForNode = null;

                    ConditionalCompilationComment previousComment = m_list[ndx - 1] as ConditionalCompilationComment;
                    if (previousComment != null)
                    {
                        ConditionalCompilationComment thisComment = m_list[ndx] as ConditionalCompilationComment;
                        if (thisComment != null)
                        {
                            // two adjacent conditional comments -- combine them into the first.
                            // this will actually make the second block a nested block within the first block,
                            // but they'll be flattened when the comment's block gets recursed.
                            previousComment.Statements.Append(thisComment.Statements);

                            // and remove the second one (which is now a duplicate)
                            m_list.RemoveAt(ndx);
                        }
                    }
                }
            }

            if (m_blockScope != null)
            {
                ScopeStack.Push(m_blockScope);
            }
            try
            {
                // call the base class to recurse
                base.AnalyzeNode();
            }
            finally
            {
                if (m_blockScope != null)
                {
                    ScopeStack.Pop();
                }
            }

            // NOW that we've recursively analyzed all the child nodes in this block, let's see
            // if we can further reduce the statements by checking for a couple good opportunities
            if (Parser.Settings.RemoveUnneededCode)
            {
                // Transform: {var foo=expression;return foo;} to: {return expression;}
                if (m_list.Count == 2 && Parser.Settings.IsModificationAllowed(TreeModifications.VarInitializeReturnToReturnInitializer))
                {
                    Var        varStatement    = m_list[0] as Var;
                    ReturnNode returnStatement = m_list[1] as ReturnNode;

                    // see if we have two statements in our block: a var with a single declaration, and a return
                    if (returnStatement != null && varStatement != null &&
                        varStatement.Count == 1 && varStatement[0].Initializer != null)
                    {
                        // now see if the return is returning a lookup for the same var we are declaring in the
                        // previous statement
                        Lookup lookup = returnStatement.Operand as Lookup;
                        if (lookup != null &&
                            string.Compare(lookup.Name, varStatement[0].Identifier, StringComparison.Ordinal) == 0)
                        {
                            // it's a match!
                            // create a combined context starting with the var and adding in the return
                            Context context = varStatement.Context.Clone();
                            context.UpdateWith(returnStatement.Context);

                            // create a new return statement
                            ReturnNode newReturn = new ReturnNode(context, Parser, varStatement[0].Initializer);

                            // clear out the existing statements
                            m_list.Clear();

                            // and add our new one
                            Append(newReturn);
                        }
                    }
                }

                // we do things differently if these statements are the last in a function
                // because we can assume the implicit return
                bool isFunctionLevel = (Parent is FunctionObject);

                // see if we want to change if-statement that forces a return to a return conditional
                if (Parser.Settings.IsModificationAllowed(TreeModifications.IfElseReturnToReturnConditional))
                {
                    // transform: {...; if(cond1)return;} to {...;cond;}
                    // transform: {...; if(cond1)return exp1;else return exp2;} to {...;return cond1?exp1:exp2;}
                    if (m_list.Count >= 1)
                    {
                        // see if the last statement is an if-statement with a true-block containing only one statement
                        IfNode ifStatement = m_list[m_list.Count - 1] as IfNode;
                        if (ifStatement != null &&
                            ifStatement.TrueBlock != null)
                        {
                            // see if this if-statement is structured such that we can convert it to a
                            // Conditional node that is the operand of a return statement
                            Conditional returnOperand = ifStatement.CanBeReturnOperand(null, isFunctionLevel);
                            if (returnOperand != null)
                            {
                                // it can! change it.
                                ReturnNode returnNode = new ReturnNode(
                                    (Context == null ? null : Context.Clone()),
                                    Parser,
                                    returnOperand);

                                // replace the if-statement with the return statement
                                ReplaceChild(ifStatement, returnNode);
                            }
                        }
                        // else last statement is not an if-statement, or true block is not a single statement
                    }

                    // transform: {...; if(cond1)return exp1;return exp2;} to {...; return cond1?exp1:exp2;}
                    // my cascade! changing the two statements to a return may cause us to run this again if the
                    // third statement up becomes the penultimate and is an if-statement
                    while (m_list.Count > 1)
                    {
                        int lastIndex = m_list.Count - 1;
                        // end in a return statement?
                        ReturnNode finalReturn = m_list[lastIndex] as ReturnNode;
                        if (finalReturn != null)
                        {
                            // it does -- see if the penultimate statement is an if-block
                            IfNode ifNode = m_list[lastIndex - 1] as IfNode;
                            if (ifNode != null)
                            {
                                // if followed by return. See if the if statement can be changed to a
                                // return of a conditional, using the operand of the following return
                                // as the ultimate expression
                                Conditional returnConditional = ifNode.CanBeReturnOperand(finalReturn.Operand, isFunctionLevel);
                                if (returnConditional != null)
                                {
                                    // it can! so create the new return statement.
                                    // the context of this new return statement should start with a clone of
                                    // the if-statement and updated with the return statement
                                    Context context = ifNode.Context.Clone();
                                    context.UpdateWith(finalReturn.Context);

                                    // create the new return node
                                    ReturnNode newReturn = new ReturnNode(
                                        context,
                                        Parser,
                                        returnConditional);

                                    // remove the last node (the old return)
                                    m_list.RemoveAt(lastIndex--);

                                    // and replace the if-statement with the new return
                                    m_list[lastIndex] = newReturn;
                                    newReturn.Parent  = this;

                                    // we collapsed the last two statements, and we KNOW the last one is a
                                    // return -- go back up to the top of the loop to see if we can keep going.
                                    continue;
                                }
                            }
                        }

                        // if we get here, then something went wrong, we didn't collapse the last
                        // two statements, so break out of the loop
                        break;
                    }

                    // now we may have converted the last functional statement
                    // from if(cond)return expr to return cond?expr:void 0, which is four
                    // extra bytes. So let's check to see if the last statement in the function
                    // now fits this pattern, and if so, change it back.
                    // We didn't just NOT change it in the first place because changing it could've
                    // enabled even more changes that would save a lot more space. But apparently
                    // those subsequent changes didn't pan out.
                    if (m_list.Count >= 1)
                    {
                        int        lastIndex  = m_list.Count - 1;
                        ReturnNode returnNode = m_list[lastIndex] as ReturnNode;
                        if (returnNode != null)
                        {
                            Conditional conditional = returnNode.Operand as Conditional;
                            if (conditional != null)
                            {
                                VoidNode falseVoid = conditional.FalseExpression as VoidNode;
                                if (falseVoid != null && falseVoid.Operand is ConstantWrapper)
                                {
                                    // we have the required pattern: "return cond?expr:void 0"
                                    // (well, the object of the void is a constant, at least).
                                    // undo it back to "if(cond)return expr" because that takes fewer bytes.

                                    // by default, the operand of the return operator will be the
                                    // true branch of the conditional
                                    AstNode returnOperand = conditional.TrueExpression;

                                    VoidNode trueVoid = conditional.TrueExpression as VoidNode;
                                    if (trueVoid != null && trueVoid.Operand is ConstantWrapper)
                                    {
                                        // the true branch of the conditional is a void operator acting
                                        // on a constant! So really, there is no operand to the return statement
                                        returnOperand = null;

                                        if (Parser.Settings.IsModificationAllowed(TreeModifications.IfConditionReturnToCondition))
                                        {
                                            // actually, we have return cond?void 0:void 0,
                                            // which would get changed back to function{...;if(cond)return}
                                            // BUT we can just shorten it to function{...;cond}
                                            m_list[lastIndex]            = conditional.Condition;
                                            conditional.Condition.Parent = this;
                                            return;
                                        }
                                    }

                                    IfNode ifNode = new IfNode(
                                        returnNode.Context.Clone(),
                                        Parser,
                                        conditional.Condition,
                                        new ReturnNode(returnNode.Context.Clone(), Parser, returnOperand),
                                        null);
                                    m_list[lastIndex] = ifNode;
                                    ifNode.Parent     = this;
                                }
                            }
                        }
                    }
                }
            }
        }
Ejemplo n.º 7
0
        internal void AddGeneratedVar(string name, AstNode initializer, bool isLiteral)
        {
            // if the body is empty, create one now
            if (Body == null)
            {
                Body = new Block(null, Parser);
            }

            // see if the first statement in the body (if any) is a var already
            Var var = null;
            if (Body.Count > 0)
            {
                var = Body[0] as Var;
            }

            VariableDeclaration varDecl = new VariableDeclaration(
                null,
                Parser,
                name,
                new Context(Parser),
                initializer,
                (isLiteral ? FieldAttributes.Literal : 0)
                );
            varDecl.IsGenerated = true;

            // make sure we set the crunchability of this field to TRUE. Doesn't matter
            // whether it's a global or within a with-scope or what-have-you. It didn't
            // exist in the sources (we are generating it now) so we can rename it whatever
            // the heck we want.
            varDecl.Field.CanCrunch = true;

            if (var != null)
            {
                // the first statement is a var; just add a new declaration to the front
                var.InsertAt(0, varDecl);
            }
            else
            {
                // not a var; create a new one
                var = new Var(null, Parser);
                var.Append(varDecl);
                Body.Insert(0, var);
            }
        }