Esempio n. 1
0
        protected virtual void CreateLiteralShortcuts()
        {
            if (m_literalMap != null)
            {
                // get a reference to the first function scope in the chain
                // might be this, might be a parent
                FunctionScope    functionScope = null;
                ActivationObject scope         = this;
                while (scope != null && (functionScope = scope as FunctionScope) == null)
                {
                    scope = scope.Parent;
                }

                // if we didn't find a parent function scope, then don't do any combining
                // because the literals are globals
                if (functionScope != null)
                {
                    // for each value in our literal map
                    foreach (string constantString in m_literalMap.Keys)
                    {
                        LiteralReference literalReference = m_literalMap[constantString];

                        // if the child scope isn't null, then we don't reference the literal
                        // and only one of our child scopes does, so we don't want to add the
                        // shortcut here.
                        // OR if there are no constant wrappers left in the list, then we've already
                        // replaced them all and there's nothing left to do.
                        // BUT if the child scope is null, either we reference it, or more than
                        // one child references it. So if there are any constant wrappers in the list,
                        // then we want to add the shortcut and replace all the constants
                        if (literalReference.ChildScope == null && literalReference.ConstantWrapperList.Count > 0)
                        {
                            // AND we only want to do it if it will be worthwhile.
                            // (and a constant of length 1 is never worthwhile)
                            int constantLength = constantString.Length;
                            if (constantLength > 1)
                            {
                                int minCount = (constantLength + 7) / (constantLength - 1);
                                if (literalReference.Count > minCount)
                                {
                                    // create a special name that won't collide with any other variable names
                                    string specialName = string.Format(CultureInfo.InvariantCulture, "[literal:{0}]", ++s_literalCounter);

                                    // add a generated var statement at the top of the function block that
                                    // is equal to the literal value (just use the first constant wrapper as a model)
                                    ConstantWrapper modelConstant = literalReference.ConstantWrapperList[0];

                                    // by default we will use the value of the first instance as the generated variable's value
                                    object generatedValue = modelConstant.Value;

                                    // BUT....
                                    // if this is a numeric value, then we need to determine whether we should use a
                                    // positive or negative version of this value to minimize the number of minus operators in the results
                                    if (modelConstant.IsNumericLiteral)
                                    {
                                        // first we need to go through the existing references and count how many negative values there are
                                        var numberOfNegatives = 0;
                                        foreach (ConstantWrapper constantWrapper in literalReference.ConstantWrapperList)
                                        {
                                            // since the model us numeric, we shouldn't have any problems calling the
                                            // ToNumber method on the others (which should all also be numeric)
                                            if (constantWrapper.ToNumber() < 0)
                                            {
                                                ++numberOfNegatives;
                                            }
                                        }

                                        // now if more than half of the references are negative, we will want the generated value
                                        // to also be negative! Otherwise we want to force it to Positive.
                                        var absoluteValue = Math.Abs((double)generatedValue);
                                        if (numberOfNegatives > literalReference.ConstantWrapperList.Count / 2)
                                        {
                                            // force it to negative
                                            generatedValue = -absoluteValue;
                                        }
                                        else
                                        {
                                            // force it to positive
                                            generatedValue = absoluteValue;
                                        }
                                    }

                                    // add the generated variable to the function scope
                                    functionScope.FunctionObject.AddGeneratedVar(
                                        specialName,
                                        new ConstantWrapper(
                                            generatedValue,
                                            modelConstant.PrimitiveType,
                                            modelConstant.Context,
                                            Parser),
                                        true);

                                    // walk the list of constant wrappers backwards (because we'll be removing them
                                    // as we go along) and replace each one with a lookup for the generated variable.
                                    // Don't forget to analyze the lookup.
                                    for (int ndx = literalReference.ConstantWrapperList.Count - 1; ndx >= 0; --ndx)
                                    {
                                        ConstantWrapper constantWrapper = literalReference.ConstantWrapperList[ndx];

                                        // create the lookup based on the thisliteral context
                                        Lookup lookup = new Lookup(specialName, constantWrapper.Context, Parser);
                                        // indicate this is generated by our code, not the user
                                        lookup.IsGenerated = true;

                                        // by default, we're just going to replace the constant with the lookup
                                        AstNode replacement = lookup;

                                        // if the constant wrapper is a numeric value that is the NEGATIVE of the
                                        // combined numeric value (which would happen if the literal was subsequently
                                        // combined with a unary minus operator), then we need to change this to a unary-minus
                                        // operator on the lookup, not just the lookup.
                                        if (constantWrapper.IsNumericLiteral)
                                        {
                                            // since the constant wrapper is numeric, we shouldn't have any problems
                                            // calling ToNumber
                                            if ((double)generatedValue == -constantWrapper.ToNumber())
                                            {
                                                // it has been negated! Change the replacement to a unary minus operator
                                                // with the lookup as its operand
                                                replacement = new NumericUnary(
                                                    constantWrapper.Context,
                                                    Parser,
                                                    lookup,
                                                    JSToken.Minus);
                                            }
                                        }

                                        // replace the this literal with the appropriate node
                                        constantWrapper.Parent.ReplaceChild(constantWrapper, replacement);

                                        // set up the lookup's outer local field using the scope of the
                                        // original constant wrapper
                                        lookup.SetOuterLocalField(constantWrapper.EnclosingScope);

                                        // and remove it from the list. This is so child scopes don't also try to
                                        // add a shortcut -- the list will be empty.
                                        literalReference.ConstantWrapperList.RemoveAt(ndx);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
Esempio n. 2
0
File: if.cs Progetto: formist/LinkMe
        internal override void AnalyzeNode()
        {
            if (Parser.Settings.StripDebugStatements &&
                Parser.Settings.IsModificationAllowed(TreeModifications.StripDebugStatements))
            {
                if (TrueBlock != null && TrueBlock.IsDebuggerStatement)
                {
                    TrueBlock = null;
                }

                if (FalseBlock != null && FalseBlock.IsDebuggerStatement)
                {
                    FalseBlock = null;
                }
            }

            // recurse....
            base.AnalyzeNode();

            // now check to see if the two branches are now empty.
            // if they are, null them out.
            if (TrueBlock != null && TrueBlock.Count == 0)
            {
                TrueBlock = null;
            }
            if (FalseBlock != null && FalseBlock.Count == 0)
            {
                FalseBlock = null;
            }

            // if there is no true branch but a false branch, then
            // put a not on the condition and move the false branch to the true branch.
            if (TrueBlock == null && FalseBlock != null &&
                Parser.Settings.IsModificationAllowed(TreeModifications.IfConditionFalseToIfNotConditionTrue))
            {
                // check to see if not-ing the condition produces a quick and easy
                // version first
                AstNode nottedCondition = Condition.LogicalNot();
                if (nottedCondition != null)
                {
                    // it does -- use it
                    Condition = nottedCondition;
                }
                else
                {
                    // it doesn't. Just wrap it.
                    Condition = new NumericUnary(
                        null,
                        Parser,
                        Condition,
                        JSToken.LogicalNot
                        );
                }
                // don't forget to set the parent
                Condition.Parent = this;

                // and swap the branches
                TrueBlock  = FalseBlock;
                FalseBlock = null;
            }
            else if (TrueBlock == null && FalseBlock == null &&
                     Parser.Settings.IsModificationAllowed(TreeModifications.IfEmptyToExpression))
            {
                // NEITHER branches have anything now!

                // something we can do in the future: as long as the condition doesn't
                // contain calls or assignments, we should be able to completely delete
                // the statement altogether rather than changing it to an expression
                // statement on the condition.

                // I'm just not doing it yet because I don't
                // know what the effect will be on the iteration of block statements.
                // if we're on item, 5, for instance, and we delete it, will the next
                // item be item 6, or will it return the NEW item 5 (since the old item
                // 5 was deleted and everything shifted up)?

                // We don't know what it is and what the side-effects may be, so
                // just change this statement into an expression statement by replacing us with
                // the expression
                Parent.ReplaceChild(this, Condition);
                // no need to analyze -- we already recursed
            }

            // if this statement is now of the pattern "if (condtion) callNode" then
            // we can further reduce it by changing it to "condition && callNode".
            if (TrueBlock != null && FalseBlock == null &&
                TrueBlock.Count == 1 &&
                Parser.Settings.IsModificationAllowed(TreeModifications.IfConditionCallToConditionAndCall))
            {
                // BUT we don't want to replace the statement if the true branch is a
                // call to an onXXXX method of an object. This is because of an IE bug
                // that throws an error if you pass any parameter to onclick or onfocus or
                // any of those event handlers directly from within an and-expression --
                // although expression-statements seem to work just fine.
                CallNode callNode = TrueBlock[0] as CallNode;
                if (callNode != null)
                {
                    Member callMember = callNode.Function as Member;
                    if (callMember == null ||
                        !callMember.Name.StartsWith("on", StringComparison.Ordinal) ||
                        callNode.Arguments.Count == 0)
                    {
                        // we're good -- go ahead and replace it
                        BinaryOperator binaryOp = new BinaryOperator(
                            Context,
                            Parser,
                            Condition,
                            TrueBlock,
                            JSToken.LogicalAnd
                            );

                        // we don't need to analyse this new node because we've already analyzed
                        // the pieces parts as part of the if. And the AnalyzeNode for the BinaryOperator
                        // doesn't really do anything else. Just replace our current node with this
                        // new node
                        Parent.ReplaceChild(this, binaryOp);
                    }
                }
            }
        }
Esempio n. 3
0
        //---------------------------------------------------------------------------------------
        // ParseUnaryExpression
        //
        //  UnaryExpression :
        //    PostfixExpression |
        //    'delete' UnaryExpression |
        //    'void' UnaryExpression |
        //    'typeof' UnaryExpression |
        //    '++' UnaryExpression |
        //    '--' UnaryExpression |
        //    '+' UnaryExpression |
        //    '-' UnaryExpression |
        //    '~' UnaryExpression |
        //    '!' UnaryExpression
        //
        //---------------------------------------------------------------------------------------
        private AstNode ParseUnaryExpression(out bool isLeftHandSideExpr, bool isMinus)
        {
            AstNode ast = null;
            isLeftHandSideExpr = false;
            bool dummy = false;
            Context exprCtx = null;
            AstNode expr = null;
            switch (m_currentToken.Token)
            {
                case JSToken.Void:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    exprCtx.UpdateWith(expr.Context);
                    ast = new VoidNode(exprCtx, this, expr);
                    break;
                case JSToken.TypeOf:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    exprCtx.UpdateWith(expr.Context);
                    ast = new TypeOfNode(exprCtx, this, expr);
                    break;
                case JSToken.Plus:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    exprCtx.UpdateWith(expr.Context);
                    ast = new NumericUnary(exprCtx, this, expr, JSToken.Plus);
                    break;
                case JSToken.Minus:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, true);
                    exprCtx.UpdateWith(expr.Context);
                    ast = new NumericUnary(exprCtx, this, expr, JSToken.Minus);
                    break;
                case JSToken.BitwiseNot:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    exprCtx.UpdateWith(expr.Context);
                    ast = new NumericUnary(exprCtx, this, expr, JSToken.BitwiseNot);
                    break;
                case JSToken.LogicalNot:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    exprCtx.UpdateWith(expr.Context);
                    ast = new NumericUnary(exprCtx, this, expr, JSToken.LogicalNot);
                    break;
                case JSToken.Delete:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    exprCtx.UpdateWith(expr.Context);
                    ast = new Delete(exprCtx, this, expr);
                    break;
                case JSToken.Increment:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    exprCtx.UpdateWith(expr.Context);
                    ast = new PostOrPrefixOperator(exprCtx, this, expr, m_currentToken.Token, PostOrPrefix.PrefixIncrement);
                    break;
                case JSToken.Decrement:
                    exprCtx = m_currentToken.Clone();
                    GetNextToken();
                    expr = ParseUnaryExpression(out dummy, false);
                    exprCtx.UpdateWith(expr.Context);
                    ast = new PostOrPrefixOperator(exprCtx, this, expr, m_currentToken.Token, PostOrPrefix.PrefixDecrement);
                    break;
                default:
                    m_noSkipTokenSet.Add(NoSkipTokenSet.s_PostfixExpressionNoSkipTokenSet);
                    try
                    {
                        ast = ParseLeftHandSideExpression(isMinus);
                    }
                    catch (RecoveryTokenException exc)
                    {
                        if (IndexOfToken(NoSkipTokenSet.s_PostfixExpressionNoSkipTokenSet, exc) == -1)
                        {
                            throw;
                        }
                        else
                        {
                            if (exc._partiallyComputedNode == null)
                                SkipTokensAndThrow();
                            else
                                ast = exc._partiallyComputedNode;
                        }
                    }
                    finally
                    {
                        m_noSkipTokenSet.Remove(NoSkipTokenSet.s_PostfixExpressionNoSkipTokenSet);
                    }
                    ast = ParsePostfixExpression(ast, out isLeftHandSideExpr);
                    break;
            }

            return ast;
        }
Esempio n. 4
0
        internal override void AnalyzeNode()
        {
            // see if this is a member (we'll need it for a couple checks)
            Member member = m_func as Member;

            if (Parser.Settings.StripDebugStatements && Parser.Settings.IsModificationAllowed(TreeModifications.StripDebugStatements))
            {
                // if this is a member, and it's a debugger object, and it's a constructor....
                if (member != null && member.IsDebuggerStatement && m_isConstructor)
                {
                    // we need to replace our debugger object with a generic Object
                    m_func = new Lookup("Object", m_func.Context, Parser);
                    // and make sure the node list is empty
                    if (m_args != null && m_args.Count > 0)
                    {
                        m_args = new AstNodeList(m_args.Context, Parser);
                    }
                }
            }

            // if this is a constructor and we want to collapse
            // some of them to literals...
            if (m_isConstructor && Parser.Settings.CollapseToLiteral)
            {
                // see if this is a lookup, and if so, if it's pointing to one
                // of the two constructors we want to collapse
                Lookup lookup = m_func as Lookup;
                if (lookup != null)
                {
                    if (lookup.Name == "Object" &&
                        Parser.Settings.IsModificationAllowed(TreeModifications.NewObjectToObjectLiteral))
                    {
                        // no arguments -- the Object constructor with no arguments is the exact same as an empty
                        // object literal
                        if (m_args == null || m_args.Count == 0)
                        {
                            // replace our node with an object literal
                            ObjectLiteral objLiteral = new ObjectLiteral(Context, Parser, null, null);
                            if (Parent.ReplaceChild(this, objLiteral))
                            {
                                // and bail now. No need to recurse -- it's an empty literal
                                return;
                            }
                        }
                        else if (m_args.Count == 1)
                        {
                            // one argument
                            // check to see if it's an object literal.
                            ObjectLiteral objectLiteral = m_args[0] as ObjectLiteral;
                            if (objectLiteral != null)
                            {
                                // the Object constructor with an argument that is a JavaScript object merely returns the
                                // argument. Since the argument is an object literal, it is by definition a JavaScript object
                                // and therefore we can replace the constructor call with the object literal
                                Parent.ReplaceChild(this, objectLiteral);

                                // don't forget to recurse the object now
                                objectLiteral.AnalyzeNode();

                                // and then bail -- we don't want to process this call
                                // operation any more; we've gotten rid of it
                                return;
                            }
                        }
                    }
                    else if (lookup.Name == "Array" &&
                             Parser.Settings.IsModificationAllowed(TreeModifications.NewArrayToArrayLiteral))
                    {
                        // Array is trickier.
                        // If there are no arguments, then just use [].
                        // if there are multiple arguments, then use [arg0,arg1...argN].
                        // but if there is one argument and it's numeric, we can't crunch it.
                        // also can't crunch if it's a function call or a member or something, since we won't
                        // KNOW whether or not it's numeric.
                        //
                        // so first see if it even is a single-argument constant wrapper.
                        ConstantWrapper constWrapper = (m_args != null && m_args.Count == 1 ? m_args[0] as ConstantWrapper : null);

                        // if the argument count is not one, then we crunch.
                        // if the argument count IS one, we only crunch if we have a constant wrapper,
                        // AND it's not numeric.
                        if (m_args == null ||
                            m_args.Count != 1 ||
                            (constWrapper != null && !constWrapper.IsNumericLiteral))
                        {
                            // create the new array literal object
                            ArrayLiteral arrayLiteral = new ArrayLiteral(Context, Parser, m_args);
                            // replace ourself within our parent
                            if (Parent.ReplaceChild(this, arrayLiteral))
                            {
                                // recurse
                                arrayLiteral.AnalyzeNode();
                                // and bail -- we don't want to recurse this node any more
                                return;
                            }
                        }
                    }
                }
            }

            // if we are replacing resource references with strings generated from resource files
            // and this is a brackets call: lookup[args]
            ResourceStrings resourceStrings = Parser.ResourceStrings;

            if (m_inBrackets && resourceStrings != null && resourceStrings.Count > 0)
            {
                // see if the root object is a lookup that corresponds to the
                // global value (not a local field) for our resource object
                // (same name)
                Lookup rootLookup = m_func as Lookup;
                if (rootLookup != null &&
                    rootLookup.LocalField == null &&
                    string.CompareOrdinal(rootLookup.Name, resourceStrings.Name) == 0)
                {
                    // we're going to replace this node with a string constant wrapper
                    // but first we need to make sure that this is a valid lookup.
                    // if the parameter contains anything that would vary at run-time,
                    // then we need to throw an error.
                    // the parser will always have either one or zero nodes in the arguments
                    // arg list. We're not interested in zero args, so just make sure there is one
                    if (m_args.Count == 1)
                    {
                        // must be a constant wrapper
                        ConstantWrapper argConstant = m_args[0] as ConstantWrapper;
                        if (argConstant != null)
                        {
                            string resourceName = argConstant.Value.ToString();

                            // get the localized string from the resources object
                            ConstantWrapper resourceLiteral = new ConstantWrapper(
                                resourceStrings[resourceName],
                                PrimitiveType.String,
                                Context,
                                Parser);

                            // replace this node with localized string, analyze it, and bail
                            // so we don't anaylze the tree we just replaced
                            Parent.ReplaceChild(this, resourceLiteral);
                            resourceLiteral.AnalyzeNode();
                            return;
                        }
                        else
                        {
                            // error! must be a constant
                            Context.HandleError(
                                JSError.ResourceReferenceMustBeConstant,
                                true);
                        }
                    }
                    else
                    {
                        // error! can only be a single constant argument to the string resource object.
                        // the parser will only have zero or one arguments, so this must be zero
                        // (since the parser won't pass multiple args to a [] operator)
                        Context.HandleError(
                            JSError.ResourceReferenceMustBeConstant,
                            true);
                    }
                }
            }

            // and finally, if this is a backets call and the argument is a constantwrapper that can
            // be an identifier, just change us to a member node:  obj["prop"] to obj.prop.
            // but ONLY if the string value is "safe" to be an identifier. Even though the ECMA-262
            // spec says certain Unicode categories are okay, in practice the various major browsers
            // all seem to have problems with certain characters in identifiers. Rather than risking
            // some browsers breaking when we change this syntax, don't do it for those "danger" categories.
            if (m_inBrackets && m_args != null)
            {
                // see if there is a single, constant argument
                string argText = m_args.SingleConstantArgument;
                if (argText != null)
                {
                    // see if we want to replace the name
                    string newName;
                    if (Parser.Settings.HasRenamePairs && Parser.Settings.ManualRenamesProperties &&
                        Parser.Settings.IsModificationAllowed(TreeModifications.PropertyRenaming) &&
                        !string.IsNullOrEmpty(newName = Parser.Settings.GetNewName(argText)))
                    {
                        // yes -- we are going to replace the name, either as a string literal, or by converting
                        // to a member-dot operation.
                        // See if we can't turn it into a dot-operator. If we can't, then we just want to replace the operator with
                        // a new constant wrapper. Otherwise we'll just replace the operator with a new constant wrapper.
                        if (Parser.Settings.IsModificationAllowed(TreeModifications.BracketMemberToDotMember) &&
                            JSScanner.IsSafeIdentifier(newName) &&
                            !JSScanner.IsKeyword(newName))
                        {
                            // the new name is safe to convert to a member-dot operator.
                            // but we don't want to convert the node to the NEW name, because we still need to Analyze the
                            // new member node -- and it might convert the new name to something else. So instead we're
                            // just going to convert this existing string to a member node WITH THE OLD STRING,
                            // and THEN analyze it (which will convert the old string to newName)
                            Member replacementMember = new Member(Context, Parser, m_func, argText);
                            Parent.ReplaceChild(this, replacementMember);

                            // this analyze call will convert the old-name member to the newName value
                            replacementMember.AnalyzeNode();
                            return;
                        }
                        else
                        {
                            // nope; can't convert to a dot-operator.
                            // we're just going to replace the first argument with a new string literal
                            // and continue along our merry way.
                            m_args.ReplaceChild(m_args[0], new ConstantWrapper(newName, PrimitiveType.String, m_args[0].Context, Parser));
                        }
                    }
                    else if (Parser.Settings.IsModificationAllowed(TreeModifications.BracketMemberToDotMember) &&
                             JSScanner.IsSafeIdentifier(argText) &&
                             !JSScanner.IsKeyword(argText))
                    {
                        // not a replacement, but the string literal is a safe identifier. So we will
                        // replace this call node with a Member-dot operation
                        Member replacementMember = new Member(Context, Parser, m_func, argText);
                        Parent.ReplaceChild(this, replacementMember);
                        replacementMember.AnalyzeNode();
                        return;
                    }
                }
            }

            // call the base class to recurse
            base.AnalyzeNode();

            // call this AFTER recursing to give the fields a chance to resolve, because we only
            // want to make this replacement if we are working on the global Date object.
            if (!m_inBrackets && !m_isConstructor &&
                (m_args == null || m_args.Count == 0) &&
                member != null && string.CompareOrdinal(member.Name, "getTime") == 0 &&
                Parser.Settings.IsModificationAllowed(TreeModifications.DateGetTimeToUnaryPlus))
            {
                // this is not a constructor and it's not a brackets call, and there are no arguments.
                // if the function is a member operation to "getTime" and the object of the member is a
                // constructor call to the global "Date" object (not a local), then we want to replace the call
                // with a unary plus on the Date constructor. Converting to numeric type is the same as
                // calling getTime, so it's the equivalent with much fewer bytes.
                CallNode dateConstructor = member.Root as CallNode;
                if (dateConstructor != null &&
                    dateConstructor.IsConstructor)
                {
                    // lookup for the predifined (not local) "Date" field
                    Lookup lookup = dateConstructor.Function as Lookup;
                    if (lookup != null && string.CompareOrdinal(lookup.Name, "Date") == 0 &&
                        lookup.LocalField == null)
                    {
                        // this is in the pattern: (new Date()).getTime()
                        // we want to replace it with +new Date
                        // use the same date constructor node as the operand
                        NumericUnary unary = new NumericUnary(Context, Parser, dateConstructor, JSToken.Plus);

                        // replace us (the call to the getTime method) with this unary operator
                        Parent.ReplaceChild(this, unary);

                        // don't need to AnalyzeNode on the unary operator. The operand has already
                        // been analyzed when we recursed, and the unary operator wouldn't do anything
                        // special anyway (since the operand is not a numeric constant)
                    }
                }
            }
            else if (Parser.Settings.EvalTreatment != EvalTreatment.Ignore)
            {
                // if this is a window.eval call, then we need to mark this scope as unknown just as
                // we would if this was a regular eval call.
                // (unless, of course, the parser settings say evals are safe)
                // call AFTER recursing so we know the left-hand side properties have had a chance to
                // lookup their fields to see if they are local or global
                if (member != null && string.CompareOrdinal(member.Name, "eval") == 0)
                {
                    if (member.LeftHandSide.IsWindowLookup)
                    {
                        // this is a call to window.eval()
                        // mark this scope as unknown so we don't crunch out locals
                        // we might reference in the eval at runtime
                        ScopeStack.Peek().IsKnownAtCompileTime = false;
                    }
                }
                else
                {
                    CallNode callNode = m_func as CallNode;
                    if (callNode != null &&
                        callNode.InBrackets &&
                        callNode.LeftHandSide.IsWindowLookup &&
                        callNode.Arguments.IsSingleConstantArgument("eval"))
                    {
                        // this is a call to window["eval"]
                        // mark this scope as unknown so we don't crunch out locals
                        // we might reference in the eval at runtime
                        ScopeStack.Peek().IsKnownAtCompileTime = false;
                    }
                }
            }

            /* REVIEW: may be too late. lookups may alread have been analyzed and
             * found undefined
             * // check to see if this is an assignment to a window["prop"] structure
             * BinaryOperator binaryOp = Parent as BinaryOperator;
             * if (binaryOp != null && binaryOp.IsAssign
             *  && m_inBrackets
             *  && m_func.IsWindowLookup
             *  && m_args != null)
             * {
             *  // and IF the property is a non-empty constant that isn't currently
             *  // a global field...
             *  string propertyName = m_args.SingleConstantArgument;
             *  if (!string.IsNullOrEmpty(propertyName)
             *      && Parser.GlobalScope[propertyName] == null)
             *  {
             *      // we want to also add it to the global fields so it's not undefined
             *      Parser.GlobalScope.DeclareField(propertyName, null, 0);
             *  }
             * }
             */
        }
Esempio n. 5
0
        public override void Visit(CallNode node)
        {
            if (node != null)
            {
                // see if this is a member (we'll need it for a couple checks)
                Member member = node.Function as Member;

                if (m_parser.Settings.StripDebugStatements
                    && m_parser.Settings.IsModificationAllowed(TreeModifications.StripDebugStatements))
                {
                    // if this is a member, and it's a debugger object, and it's a constructor....
                    if (member != null && member.IsDebuggerStatement && node.IsConstructor)
                    {
                        // we need to replace our debugger object with a generic Object
                        node.ReplaceChild(node.Function, new Lookup("Object", node.Function.Context, m_parser));

                        // and make sure the node list is empty
                        if (node.Arguments != null && node.Arguments.Count > 0)
                        {
                            node.ReplaceChild(node.Arguments, new AstNodeList(node.Arguments.Context, m_parser));
                        }
                    }
                }

                // if this is a constructor and we want to collapse
                // some of them to literals...
                if (node.IsConstructor && m_parser.Settings.CollapseToLiteral)
                {
                    // see if this is a lookup, and if so, if it's pointing to one
                    // of the two constructors we want to collapse
                    Lookup lookup = node.Function as Lookup;
                    if (lookup != null)
                    {
                        if (lookup.Name == "Object"
                            && m_parser.Settings.IsModificationAllowed(TreeModifications.NewObjectToObjectLiteral))
                        {
                            // no arguments -- the Object constructor with no arguments is the exact same as an empty
                            // object literal
                            if (node.Arguments == null || node.Arguments.Count == 0)
                            {
                                // replace our node with an object literal
                                ObjectLiteral objLiteral = new ObjectLiteral(node.Context, m_parser, null, null);
                                if (node.Parent.ReplaceChild(node, objLiteral))
                                {
                                    // and bail now. No need to recurse -- it's an empty literal
                                    return;
                                }
                            }
                            else if (node.Arguments.Count == 1)
                            {
                                // one argument
                                // check to see if it's an object literal.
                                ObjectLiteral objectLiteral = node.Arguments[0] as ObjectLiteral;
                                if (objectLiteral != null)
                                {
                                    // the Object constructor with an argument that is a JavaScript object merely returns the
                                    // argument. Since the argument is an object literal, it is by definition a JavaScript object
                                    // and therefore we can replace the constructor call with the object literal
                                    node.Parent.ReplaceChild(node, objectLiteral);

                                    // don't forget to recurse the object now
                                    objectLiteral.Accept(this);

                                    // and then bail -- we don't want to process this call
                                    // operation any more; we've gotten rid of it
                                    return;
                                }
                            }
                        }
                        else if (lookup.Name == "Array"
                            && m_parser.Settings.IsModificationAllowed(TreeModifications.NewArrayToArrayLiteral))
                        {
                            // Array is trickier.
                            // If there are no arguments, then just use [].
                            // if there are multiple arguments, then use [arg0,arg1...argN].
                            // but if there is one argument and it's numeric, we can't crunch it.
                            // also can't crunch if it's a function call or a member or something, since we won't
                            // KNOW whether or not it's numeric.
                            //
                            // so first see if it even is a single-argument constant wrapper.
                            ConstantWrapper constWrapper = (node.Arguments != null && node.Arguments.Count == 1
                                ? node.Arguments[0] as ConstantWrapper
                                : null);

                            // if the argument count is not one, then we crunch.
                            // if the argument count IS one, we only crunch if we have a constant wrapper,
                            // AND it's not numeric.
                            if (node.Arguments == null
                              || node.Arguments.Count != 1
                              || (constWrapper != null && !constWrapper.IsNumericLiteral))
                            {
                                // create the new array literal object
                                ArrayLiteral arrayLiteral = new ArrayLiteral(node.Context, m_parser, node.Arguments);

                                // replace ourself within our parent
                                if (node.Parent.ReplaceChild(node, arrayLiteral))
                                {
                                    // recurse
                                    arrayLiteral.Accept(this);
                                    // and bail -- we don't want to recurse this node any more
                                    return;
                                }
                            }
                        }
                    }
                }

                // if we are replacing resource references with strings generated from resource files
                // and this is a brackets call: lookup[args]
                ResourceStrings resourceStrings = m_parser.ResourceStrings;
                if (node.InBrackets && resourceStrings != null && resourceStrings.Count > 0)
                {
                    // see if the root object is a lookup that corresponds to the
                    // global value (not a local field) for our resource object
                    // (same name)
                    Lookup rootLookup = node.Function as Lookup;
                    if (rootLookup != null
                        && rootLookup.LocalField == null
                        && string.CompareOrdinal(rootLookup.Name, resourceStrings.Name) == 0)
                    {
                        // we're going to replace this node with a string constant wrapper
                        // but first we need to make sure that this is a valid lookup.
                        // if the parameter contains anything that would vary at run-time,
                        // then we need to throw an error.
                        // the parser will always have either one or zero nodes in the arguments
                        // arg list. We're not interested in zero args, so just make sure there is one
                        if (node.Arguments.Count == 1)
                        {
                            // must be a constant wrapper
                            ConstantWrapper argConstant = node.Arguments[0] as ConstantWrapper;
                            if (argConstant != null)
                            {
                                string resourceName = argConstant.Value.ToString();

                                // get the localized string from the resources object
                                ConstantWrapper resourceLiteral = new ConstantWrapper(
                                    resourceStrings[resourceName],
                                    PrimitiveType.String,
                                    node.Context,
                                    m_parser);

                                // replace this node with localized string, analyze it, and bail
                                // so we don't anaylze the tree we just replaced
                                node.Parent.ReplaceChild(node, resourceLiteral);
                                resourceLiteral.Accept(this);
                                return;
                            }
                            else
                            {
                                // error! must be a constant
                                node.Context.HandleError(
                                    JSError.ResourceReferenceMustBeConstant,
                                    true);
                            }
                        }
                        else
                        {
                            // error! can only be a single constant argument to the string resource object.
                            // the parser will only have zero or one arguments, so this must be zero
                            // (since the parser won't pass multiple args to a [] operator)
                            node.Context.HandleError(
                                JSError.ResourceReferenceMustBeConstant,
                                true);
                        }
                    }
                }

                // and finally, if this is a backets call and the argument is a constantwrapper that can
                // be an identifier, just change us to a member node:  obj["prop"] to obj.prop.
                // but ONLY if the string value is "safe" to be an identifier. Even though the ECMA-262
                // spec says certain Unicode categories are okay, in practice the various major browsers
                // all seem to have problems with certain characters in identifiers. Rather than risking
                // some browsers breaking when we change this syntax, don't do it for those "danger" categories.
                if (node.InBrackets && node.Arguments != null)
                {
                    // see if there is a single, constant argument
                    string argText = node.Arguments.SingleConstantArgument;
                    if (argText != null)
                    {
                        // see if we want to replace the name
                        string newName;
                        if (m_parser.Settings.HasRenamePairs && m_parser.Settings.ManualRenamesProperties
                            && m_parser.Settings.IsModificationAllowed(TreeModifications.PropertyRenaming)
                            && !string.IsNullOrEmpty(newName = m_parser.Settings.GetNewName(argText)))
                        {
                            // yes -- we are going to replace the name, either as a string literal, or by converting
                            // to a member-dot operation.
                            // See if we can't turn it into a dot-operator. If we can't, then we just want to replace the operator with
                            // a new constant wrapper. Otherwise we'll just replace the operator with a new constant wrapper.
                            if (m_parser.Settings.IsModificationAllowed(TreeModifications.BracketMemberToDotMember)
                                && JSScanner.IsSafeIdentifier(newName)
                                && !JSScanner.IsKeyword(newName, node.EnclosingScope.UseStrict))
                            {
                                // the new name is safe to convert to a member-dot operator.
                                // but we don't want to convert the node to the NEW name, because we still need to Analyze the
                                // new member node -- and it might convert the new name to something else. So instead we're
                                // just going to convert this existing string to a member node WITH THE OLD STRING,
                                // and THEN analyze it (which will convert the old string to newName)
                                Member replacementMember = new Member(node.Context, m_parser, node.Function, argText, node.Arguments[0].Context);
                                node.Parent.ReplaceChild(node, replacementMember);

                                // this analyze call will convert the old-name member to the newName value
                                replacementMember.Accept(this);
                                return;
                            }
                            else
                            {
                                // nope; can't convert to a dot-operator.
                                // we're just going to replace the first argument with a new string literal
                                // and continue along our merry way.
                                node.Arguments.ReplaceChild(node.Arguments[0], new ConstantWrapper(newName, PrimitiveType.String, node.Arguments[0].Context, m_parser));
                            }
                        }
                        else if (m_parser.Settings.IsModificationAllowed(TreeModifications.BracketMemberToDotMember)
                            && JSScanner.IsSafeIdentifier(argText)
                            && !JSScanner.IsKeyword(argText, node.EnclosingScope.UseStrict))
                        {
                            // not a replacement, but the string literal is a safe identifier. So we will
                            // replace this call node with a Member-dot operation
                            Member replacementMember = new Member(node.Context, m_parser, node.Function, argText, node.Arguments[0].Context);
                            node.Parent.ReplaceChild(node, replacementMember);
                            replacementMember.Accept(this);
                            return;
                        }
                    }
                }

                // call the base class to recurse
                base.Visit(node);

                // might have changed
                member = node.Function as Member;

                // call this AFTER recursing to give the fields a chance to resolve, because we only
                // want to make this replacement if we are working on the global Date object.
                if (!node.InBrackets && !node.IsConstructor
                    && (node.Arguments == null || node.Arguments.Count == 0)
                    && member != null && string.CompareOrdinal(member.Name, "getTime") == 0
                    && m_parser.Settings.IsModificationAllowed(TreeModifications.DateGetTimeToUnaryPlus))
                {
                    // this is not a constructor and it's not a brackets call, and there are no arguments.
                    // if the function is a member operation to "getTime" and the object of the member is a
                    // constructor call to the global "Date" object (not a local), then we want to replace the call
                    // with a unary plus on the Date constructor. Converting to numeric type is the same as
                    // calling getTime, so it's the equivalent with much fewer bytes.
                    CallNode dateConstructor = member.Root as CallNode;
                    if (dateConstructor != null
                        && dateConstructor.IsConstructor)
                    {
                        // lookup for the predifined (not local) "Date" field
                        Lookup lookup = dateConstructor.Function as Lookup;
                        if (lookup != null && string.CompareOrdinal(lookup.Name, "Date") == 0
                            && lookup.LocalField == null)
                        {
                            // this is in the pattern: (new Date()).getTime()
                            // we want to replace it with +new Date
                            // use the same date constructor node as the operand
                            NumericUnary unary = new NumericUnary(node.Context, m_parser, dateConstructor, JSToken.Plus);

                            // replace us (the call to the getTime method) with this unary operator
                            node.Parent.ReplaceChild(node, unary);

                            // don't need to recurse on the unary operator. The operand has already
                            // been analyzed when we recursed, and the unary operator wouldn't do anything
                            // special anyway (since the operand is not a numeric constant)
                        }
                    }
                }
                else
                {
                    var isEval = false;

                    var lookup = node.Function as Lookup;
                    if (lookup != null
                        && string.CompareOrdinal(lookup.Name, "eval") == 0
                        && lookup.VariableField is JSPredefinedField)
                    {
                        // call to predefined eval function
                        isEval = true;
                    }
                    else if (member != null && string.CompareOrdinal(member.Name, "eval") == 0)
                    {
                        // if this is a window.eval call, then we need to mark this scope as unknown just as
                        // we would if this was a regular eval call.
                        // (unless, of course, the parser settings say evals are safe)
                        // call AFTER recursing so we know the left-hand side properties have had a chance to
                        // lookup their fields to see if they are local or global
                        if (member.LeftHandSide.IsWindowLookup)
                        {
                            // this is a call to window.eval()
                            isEval = true;
                        }
                    }
                    else
                    {
                        CallNode callNode = node.Function as CallNode;
                        if (callNode != null
                            && callNode.InBrackets
                            && callNode.LeftHandSide.IsWindowLookup
                            && callNode.Arguments.IsSingleConstantArgument("eval"))
                        {
                            // this is a call to window["eval"]
                            isEval = true;
                        }
                    }

                    if (isEval)
                    {
                        if (m_parser.Settings.EvalTreatment != EvalTreatment.Ignore)
                        {
                            // mark this scope as unknown so we don't crunch out locals
                            // we might reference in the eval at runtime
                            ScopeStack.Peek().IsKnownAtCompileTime = false;
                        }
                    }
                }
            }
        }
Esempio n. 6
0
        public override void Visit(BinaryOperator node)
        {
            if (node != null)
            {
                base.Visit(node);

                // see if this operation is subtracting zero from a lookup -- that is typically done to
                // coerce a value to numeric. There's a simpler way: unary plus operator.
                if (node.OperatorToken == JSToken.Minus
                    && m_parser.Settings.IsModificationAllowed(TreeModifications.SimplifyStringToNumericConversion))
                {
                    Lookup lookup = node.Operand1 as Lookup;
                    if (lookup != null)
                    {
                        ConstantWrapper right = node.Operand2 as ConstantWrapper;
                        if (right != null && right.IsIntegerLiteral && right.ToNumber() == 0)
                        {
                            // okay, so we have "lookup - 0"
                            // this is done frequently to force a value to be numeric.
                            // There is an easier way: apply the unary + operator to it.
                            NumericUnary unary = new NumericUnary(node.Context, m_parser, lookup, JSToken.Plus);
                            node.Parent.ReplaceChild(node, unary);

                            // because we recursed at the top of this function, we don't need to Analyze
                            // the new Unary node. This visitor's method for NumericUnary only does something
                            // if the operand is a constant -- and this one is a Lookup. And we already analyzed
                            // the lookup.
                        }
                    }
                }
                else if ((node.OperatorToken == JSToken.StrictEqual || node.OperatorToken == JSToken.StrictNotEqual)
                    && m_parser.Settings.IsModificationAllowed(TreeModifications.ReduceStrictOperatorIfTypesAreSame))
                {
                    PrimitiveType leftType = node.Operand1.FindPrimitiveType();
                    if (leftType != PrimitiveType.Other)
                    {
                        PrimitiveType rightType = node.Operand2.FindPrimitiveType();
                        if (leftType == rightType)
                        {
                            // the are the same known types. We can reduce the operators
                            node.OperatorToken = node.OperatorToken == JSToken.StrictEqual ? JSToken.Equal : JSToken.NotEqual;
                        }
                        else if (rightType != PrimitiveType.Other)
                        {
                            // they are not the same, but they are both known. We can completely remove the operator
                            // and replace it with true (!==) or false (===).
                            node.Context.HandleError(JSError.StrictComparisonIsAlwaysTrueOrFalse, false);
                            node.Parent.ReplaceChild(
                                node,
                                new ConstantWrapper(node.OperatorToken == JSToken.StrictNotEqual, PrimitiveType.Boolean, node.Context, m_parser));
                        }
                    }
                }
                else if (node.IsAssign && ScopeStack.Peek().UseStrict)
                {
                    // strict mode cannot assign to lookup "eval" or "arguments"
                    var lookup = node.Operand1 as Lookup;
                    if (lookup != null)
                    {
                        if (lookup.VariableField is JSArgumentsField
                            || (lookup.VariableField is JSPredefinedField && string.CompareOrdinal(lookup.Name, "eval") == 0))
                        {
                            node.Operand1.Context.HandleError(JSError.StrictModeInvalidAssign, true);
                        }
                    }
                }
            }
        }
Esempio n. 7
0
        public override void Visit(NumericUnary node)
        {
            if (node != null)
            {
                // recurse first, then check to see if the unary is still needed
                base.Visit(node);

                // if the operand is a numeric literal
                ConstantWrapper constantWrapper = node.Operand as ConstantWrapper;
                if (constantWrapper != null
                    && constantWrapper.IsNumericLiteral)
                {
                    // get the value of the constant. We've already screened it for numeric, so
                    // we don't have to worry about catching any errors
                    double doubleValue = constantWrapper.ToNumber();

                    // if this is a unary minus...
                    if (node.OperatorToken == JSToken.Minus
                        && m_parser.Settings.IsModificationAllowed(TreeModifications.ApplyUnaryMinusToNumericLiteral))
                    {
                        // negate the value
                        constantWrapper.Value = -doubleValue;

                        // replace us with the negated constant
                        if (node.Parent.ReplaceChild(node, constantWrapper))
                        {
                            // the context for the minus will include the number (its operand),
                            // but the constant will just be the number. Update the context on
                            // the constant to be a copy of the context on the operator
                            constantWrapper.Context = node.Context.Clone();
                        }
                    }
                    else if (node.OperatorToken == JSToken.Plus
                        && m_parser.Settings.IsModificationAllowed(TreeModifications.RemoveUnaryPlusOnNumericLiteral))
                    {
                        // +NEG is still negative, +POS is still positive, and +0 is still 0.
                        // so just get rid of the unary operator altogether
                        if (node.Parent.ReplaceChild(node, constantWrapper))
                        {
                            // the context for the unary will include the number (its operand),
                            // but the constant will just be the number. Update the context on
                            // the constant to be a copy of the context on the operator
                            constantWrapper.Context = node.Context.Clone();
                        }
                    }
                }
            }
        }
Esempio n. 8
0
        //
        // IVisitor implementations
        //
        public override void Visit(BinaryOperator node)
        {
            if (node != null)
            {
                // depth-first
                base.Visit(node);

                if (m_parser.Settings.EvalLiteralExpressions)
                {
                    // if this is an assign operator, an in, or an instanceof, then we won't
                    // try to evaluate it
                    if (!node.IsAssign && node.OperatorToken != JSToken.In && node.OperatorToken != JSToken.InstanceOf)
                    {
                        if (node.OperatorToken == JSToken.StrictEqual || node.OperatorToken == JSToken.StrictNotEqual)
                        {
                            // the operator is a strict equality (or not-equal).
                            // check the primitive types of the two operands -- if they are known but not the same, we can
                            // shortcut the whole process by just replacing this node with a boolean literal.
                            var leftType = node.Operand1.FindPrimitiveType();
                            if (leftType != PrimitiveType.Other)
                            {
                                var rightType = node.Operand2.FindPrimitiveType();
                                if (rightType != PrimitiveType.Other)
                                {
                                    // both sides are known
                                    if (leftType != rightType)
                                    {
                                        // they are not the same type -- replace with a boolean and bail
                                        node.Parent.ReplaceChild(
                                            node,
                                            new ConstantWrapper(node.OperatorToken == JSToken.StrictEqual ? false : true, PrimitiveType.Boolean, node.Context, m_parser));
                                        return;
                                    }

                                    // they are the same type -- we can change the operator to simple equality/not equality
                                    node.OperatorToken = node.OperatorToken == JSToken.StrictEqual ? JSToken.Equal : JSToken.NotEqual;
                                }
                            }
                        }

                        // see if the left operand is a literal number, boolean, string, or null
                        ConstantWrapper left = node.Operand1 as ConstantWrapper;
                        if (left != null)
                        {
                            if (node.OperatorToken == JSToken.Comma)
                            {
                                // the comma operator evaluates the left, then evaluates the right and returns it.
                                // but if the left is a literal, evaluating it doesn't DO anything, so we can replace the
                                // entire operation with the right-hand operand
                                ConstantWrapper rightConstant = node.Operand2 as ConstantWrapper;
                                if (rightConstant != null)
                                {
                                    // we'll replace the operator with the right-hand operand, BUT it's a constant, too.
                                    // first check to see if replacing this node with a constant will result in creating
                                    // a member-bracket operator that can be turned into a member-dot. If it is, then that
                                    // method will handle the replacement. But if it doesn't, then we should just replace
                                    // the comma with the right-hand operand.
                                    if (!ReplaceMemberBracketWithDot(node, rightConstant))
                                    {
                                        node.Parent.ReplaceChild(node, rightConstant);
                                    }
                                }
                                else
                                {
                                    // replace the comma operator with the right-hand operand
                                    node.Parent.ReplaceChild(node, node.Operand2);
                                }
                            }
                            else
                            {
                                // see if the right operand is a literal number, boolean, string, or null
                                ConstantWrapper right = node.Operand2 as ConstantWrapper;
                                if (right != null)
                                {
                                    // then they are both constants and we can evaluate the operation
                                    EvalThisOperator(node, left, right);
                                }
                                else
                                {
                                    // see if the right is a binary operator that can be combined with ours
                                    BinaryOperator rightBinary = node.Operand2 as BinaryOperator;
                                    if (rightBinary != null)
                                    {
                                        ConstantWrapper rightLeft = rightBinary.Operand1 as ConstantWrapper;
                                        if (rightLeft != null)
                                        {
                                            // eval our left and the right-hand binary's left and put the combined operation as
                                            // the child of the right-hand binary
                                            EvalToTheRight(node, left, rightLeft, rightBinary);
                                        }
                                        else
                                        {
                                            ConstantWrapper rightRight = rightBinary.Operand2 as ConstantWrapper;
                                            if (rightRight != null)
                                            {
                                                EvalFarToTheRight(node, left, rightRight, rightBinary);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            // left is not a constantwrapper. See if the right is
                            ConstantWrapper right = node.Operand2 as ConstantWrapper;
                            if (right != null)
                            {
                                // the right is a constant. See if the the left is a binary operator...
                                BinaryOperator leftBinary = node.Operand1 as BinaryOperator;
                                if (leftBinary != null)
                                {
                                    // ...with a constant on the right, and the operators can be combined
                                    ConstantWrapper leftRight = leftBinary.Operand2 as ConstantWrapper;
                                    if (leftRight != null)
                                    {
                                        EvalToTheLeft(node, right, leftRight, leftBinary);
                                    }
                                    else
                                    {
                                        ConstantWrapper leftLeft = leftBinary.Operand1 as ConstantWrapper;
                                        if (leftLeft != null)
                                        {
                                            EvalFarToTheLeft(node, right, leftLeft, leftBinary);
                                        }
                                    }
                                }
                                else if (m_parser.Settings.IsModificationAllowed(TreeModifications.SimplifyStringToNumericConversion))
                                {
                                    // see if it's a lookup and this is a minus operation and the constant is a zero
                                    Lookup lookup = node.Operand1 as Lookup;
                                    if (lookup != null && node.OperatorToken == JSToken.Minus && right.IsIntegerLiteral && right.ToNumber() == 0)
                                    {
                                        // okay, so we have "lookup - 0"
                                        // this is done frequently to force a value to be numeric.
                                        // There is an easier way: apply the unary + operator to it.
                                        NumericUnary unary = new NumericUnary(node.Context, m_parser, lookup, JSToken.Plus);
                                        node.Parent.ReplaceChild(node, unary);
                                    }
                                }
                            }
                            // TODO: shouldn't we check if they BOTH are binary operators? (a*6)*(5*b) ==> a*30*b (for instance)
                        }
                    }
                }
            }
        }
Esempio n. 9
0
        public override void Visit(NumericUnary node)
        {
            if (node != null)
            {
                // depth-first
                base.Visit(node);

                if (m_parser.Settings.IsModificationAllowed(TreeModifications.EvaluateNumericExpressions))
                {
                    // see if our operand is a ConstantWrapper
                    ConstantWrapper literalOperand = node.Operand as ConstantWrapper;
                    if (literalOperand != null)
                    {
                        // must be number, boolean, string, or null
                        switch (node.OperatorToken)
                        {
                            case JSToken.Plus:
                                try
                                {
                                    // replace with a constant representing operand.ToNumber,
                                    node.Parent.ReplaceChild(node, new ConstantWrapper(literalOperand.ToNumber(), PrimitiveType.Number, node.Context, m_parser));
                                }
                                catch (InvalidCastException)
                                {
                                    // some kind of casting in ToNumber caused a situation where we don't want
                                    // to perform the combination on these operands
                                }
                                break;

                            case JSToken.Minus:
                                try
                                {
                                    // replace with a constant representing the negative of operand.ToNumber
                                    node.Parent.ReplaceChild(node, new ConstantWrapper(-literalOperand.ToNumber(), PrimitiveType.Number, node.Context, m_parser));
                                }
                                catch (InvalidCastException)
                                {
                                    // some kind of casting in ToNumber caused a situation where we don't want
                                    // to perform the combination on these operands
                                }
                                break;

                            case JSToken.BitwiseNot:
                                try
                                {
                                    // replace with a constant representing the bitwise-not of operant.ToInt32
                                    node.Parent.ReplaceChild(node, new ConstantWrapper(Convert.ToDouble(~literalOperand.ToInt32()), PrimitiveType.Number, node.Context, m_parser));
                                }
                                catch (InvalidCastException)
                                {
                                    // some kind of casting in ToNumber caused a situation where we don't want
                                    // to perform the combination on these operands
                                }
                                break;

                            case JSToken.LogicalNot:
                                // replace with a constant representing the opposite of operand.ToBoolean
                                try
                                {
                                    node.Parent.ReplaceChild(node, new ConstantWrapper(!literalOperand.ToBoolean(), PrimitiveType.Boolean, node.Context, m_parser));
                                }
                                catch (InvalidCastException)
                                {
                                    // ignore any invalid cast exceptions
                                }
                                break;
                        }
                    }
                }
            }
        }
Esempio n. 10
0
        protected virtual void CreateLiteralShortcuts()
        {
            if (m_literalMap != null)
            {
                // get a reference to the first function scope in the chain
                // might be this, might be a parent
                FunctionScope functionScope = null;
                ActivationObject scope = this;
                while (scope != null && (functionScope = scope as FunctionScope) == null)
                {
                    scope = scope.Parent;
                }

                // if we didn't find a parent function scope, then don't do any combining
                // because the literals are globals
                if (functionScope != null)
                {
                    // for each value in our literal map
                    foreach (string constantString in m_literalMap.Keys)
                    {
                        LiteralReference literalReference = m_literalMap[constantString];

                        // if the child scope isn't null, then we don't reference the literal
                        // and only one of our child scopes does, so we don't want to add the
                        // shortcut here.
                        // OR if there are no constant wrappers left in the list, then we've already
                        // replaced them all and there's nothing left to do.
                        // BUT if the child scope is null, either we reference it, or more than
                        // one child references it. So if there are any constant wrappers in the list,
                        // then we want to add the shortcut and replace all the constants
                        if (literalReference.ChildScope == null && literalReference.ConstantWrapperList.Count > 0)
                        {
                            // AND we only want to do it if it will be worthwhile.
                            // (and a constant of length 1 is never worthwhile)
                            int constantLength = constantString.Length;
                            if (constantLength > 1)
                            {
                                int minCount = (constantLength + 7) / (constantLength - 1);
                                if (literalReference.Count > minCount)
                                {
                                    // create a special name that won't collide with any other variable names
                                    string specialName = string.Format(CultureInfo.InvariantCulture, "[literal:{0}]", ++s_literalCounter);

                                    // add a generated var statement at the top of the function block that
                                    // is equal to the literal value (just use the first constant wrapper as a model)
                                    ConstantWrapper modelConstant = literalReference.ConstantWrapperList[0];

                                    // by default we will use the value of the first instance as the generated variable's value
                                    object generatedValue = modelConstant.Value;

                                    // BUT....
                                    // if this is a numeric value, then we need to determine whether we should use a
                                    // positive or negative version of this value to minimize the number of minus operators in the results
                                    if (modelConstant.IsNumericLiteral)
                                    {
                                        // first we need to go through the existing references and count how many negative values there are
                                        var numberOfNegatives = 0;
                                        foreach (ConstantWrapper constantWrapper in literalReference.ConstantWrapperList)
                                        {
                                            // since the model us numeric, we shouldn't have any problems calling the
                                            // ToNumber method on the others (which should all also be numeric)
                                            if (constantWrapper.ToNumber() < 0)
                                            {
                                                ++numberOfNegatives;
                                            }
                                        }

                                        // now if more than half of the references are negative, we will want the generated value
                                        // to also be negative! Otherwise we want to force it to Positive.
                                        var absoluteValue = Math.Abs((double)generatedValue);
                                        if (numberOfNegatives > literalReference.ConstantWrapperList.Count / 2)
                                        {
                                            // force it to negative
                                            generatedValue = -absoluteValue;
                                        }
                                        else
                                        {
                                            // force it to positive
                                            generatedValue = absoluteValue;
                                        }
                                    }

                                    // add the generated variable to the function scope
                                    functionScope.FunctionObject.AddGeneratedVar(
                                        specialName,
                                        new ConstantWrapper(
                                            generatedValue,
                                            modelConstant.PrimitiveType,
                                            modelConstant.Context,
                                            Parser),
                                        true);

                                    // walk the list of constant wrappers backwards (because we'll be removing them
                                    // as we go along) and replace each one with a lookup for the generated variable.
                                    // Don't forget to analyze the lookup.
                                    for (int ndx = literalReference.ConstantWrapperList.Count - 1; ndx >= 0; --ndx)
                                    {
                                        ConstantWrapper constantWrapper = literalReference.ConstantWrapperList[ndx];

                                        // create the lookup based on the thisliteral context
                                        Lookup lookup = new Lookup(specialName, constantWrapper.Context, Parser);
                                        // indicate this is generated by our code, not the user
                                        lookup.IsGenerated = true;

                                        // by default, we're just going to replace the constant with the lookup
                                        AstNode replacement = lookup;

                                        // if the constant wrapper is a numeric value that is the NEGATIVE of the
                                        // combined numeric value (which would happen if the literal was subsequently
                                        // combined with a unary minus operator), then we need to change this to a unary-minus
                                        // operator on the lookup, not just the lookup.
                                        if (constantWrapper.IsNumericLiteral)
                                        {
                                            // since the constant wrapper is numeric, we shouldn't have any problems
                                            // calling ToNumber
                                            if ((double)generatedValue == -constantWrapper.ToNumber())
                                            {
                                                // it has been negated! Change the replacement to a unary minus operator
                                                // with the lookup as its operand
                                                replacement = new NumericUnary(
                                                    constantWrapper.Context,
                                                    Parser,
                                                    lookup,
                                                    JSToken.Minus);
                                            }
                                        }

                                        // replace the this literal with the appropriate node
                                        constantWrapper.Parent.ReplaceChild(constantWrapper, replacement);

                                        // set up the lookup's outer local field using the scope of the
                                        // original constant wrapper
                                        lookup.SetOuterLocalField(constantWrapper.EnclosingScope);

                                        // and remove it from the list. This is so child scopes don't also try to
                                        // add a shortcut -- the list will be empty.
                                        literalReference.ConstantWrapperList.RemoveAt(ndx);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
Esempio n. 11
0
 public override void Visit(NumericUnary node)
 {
     if (node != null)
     {
         // if this is a unary logical-not operator, then we will just remove the
         // logical-not operation
         if (node.OperatorToken == JSToken.LogicalNot)
         {
             if (m_measure)
             {
                 // measure
                 // removes the not operator character, but also might remove parens that we would
                 // no longer need.
                 --m_delta;
                 if (node.Operand is BinaryOperator || node.Operand is Conditional)
                 {
                     // those operators are lesser-precedence than the logical-not coperator and would've
                     // added parens that we now don't need
                     m_delta -= 2;
                 }
             }
             else
             {
                 // convert
                 // just replace the not with its own operand
                 node.Parent.ReplaceChild(node, node.Operand);
             }
         }
         else
         {
             // same logic as most nodes for the other operators
             TypicalHandler(node);
         }
     }
 }