public void Visit(CallNode node)
 {
     if (node != null)
     {
         if (node.InBrackets)
         {
             // if this is a member-bracket operation, then *we* don't need parens, but we shoul
             // recurse the function in case something in there does
             node.Function.Accept(this);
         }
         else if (!node.IsConstructor)
         {
             // we have parens for our call arguments, so we definitely
             // need to be wrapped and there's no need to recurse
             m_needsParens = true;
         }
         else
         {
             // we are a new-operator - if we have any arguments then we're good to go
             // because those arguments will be associated with us, not the outer new.
             // but if we don't have any arguments, we might need to be wrapped in parens
             // so any outer arguments don't get associated with us
             if (node.Arguments == null || node.Arguments.Count == 0)
             {
                 m_needsParens = !m_outerHasNoArguments;
             }
         }
     }
     else
     {
         // shouldn't happen, but we're a call so let's wrap in parens
         m_needsParens = true;
     }
 }
 public void Visit(CallNode node)
 {
     if (node != null)
     {
         DoesRequire = true;
     }
 }
Exemple #3
0
 public void Visit(CallNode node)
 {
     // if there's a function node, recurse into it
     if (node != null && node.Function != null)
     {
         node.Function.Accept(this);
     }
 }
Exemple #4
0
            public override void Visit(CallNode node)
            {
                base.Visit(node);

                if (ShouldInsertModulePathArgument(node))
                {
                    insertionIndex = node.Arguments[0].Context.StartPosition;
                }
            }
Exemple #5
0
            public override void Visit(CallNode node)
            {
                base.Visit(node);

                if (ShouldInsertModulePathArgument(node))
                {
                    var modulePathNode = new ConstantWrapper(modulePath, PrimitiveType.String, node.Context, node.Parser);
                    node.Arguments.Insert(0, modulePathNode);
                }
            }
Exemple #6
0
        internal override void AnalyzeNode()
        {
            // check to see if this node is an argument to a RegExp constructor.
            // if it is, we'll want to not use certain string escapes
            AstNode previousNode = null;
            AstNode parentNode   = Parent;

            while (parentNode != null)
            {
                // is this a call node and he previous node was one of the parameters?
                CallNode callNode = parentNode as CallNode;
                if (callNode != null && previousNode == callNode.Arguments)
                {
                    // are we calling a simple lookup for "RegExp"?
                    Lookup lookup = callNode.Function as Lookup;
                    if (lookup != null && lookup.Name == "RegExp")
                    {
                        // we are -- so all string literals passed within this constructor should not use
                        // standard string escape sequences
                        m_isParameterToRegExp = true;
                        // we can stop looking
                        break;
                    }
                }

                // next up the chain, keeping track of this current node as next iteration's "previous" node
                previousNode = parentNode;
                parentNode   = parentNode.Parent;
            }

            // we only need to process the literals IF we are actually going to do
            // anything with them (combine duplicates). So if we aren't, don't call
            // AddLiteral because it hugely inflates the processing time of the application.
            if (Parser.Settings.CombineDuplicateLiterals)
            {
                // add this literal to the scope's literal collection.
                // HOWEVER, we do NOT want to add it for consideration of literal combination
                // if any scope above us is a with-scope -- otherwise the
                // variable we use to combine the literals might be confused with a
                // property on the with-object.
                // AND we don't want to do it if the scope is unknown, for the same reason.
                // we won't really know if the variable we create will interfere with the
                // scope resolution of any variables that me in the eval string.
                ActivationObject thisScope = Parser.ScopeStack.Peek();
                if (thisScope.IsKnownAtCompileTime && !thisScope.IsInWithScope)
                {
                    thisScope.AddLiteral(this, thisScope);
                }
            }

            // this node has no children, so don't bother calling the base
        }
Exemple #7
0
        public override AstNode Clone()
        {
            CallNode newCallNode = new CallNode(
                (Context == null ? null : Context.Clone()),
                Parser,
                (m_func == null ? null : m_func.Clone()),
                (m_args == null ? null : (AstNodeList)m_args.Clone()),
                m_inBrackets
                );

            newCallNode.m_isConstructor = m_isConstructor;
            return(newCallNode);
        }
Exemple #8
0
        //code in parser relies on the member string (x.y.z...) being returned from here
        public override string ToCode(ToCodeFormat format)
        {
            // pass P to the root object so it knows we might want parentheses
            string rootCrunched = Root.ToCode();

            // these tests are for items that DON'T need parens, and then we NOT the results.
            // non-numeric constant wrappers don't need parens (boolean, string, null).
            // numeric constant wrappers need parens IF there is no decimal point.
            // function expressions will take care of their own parens.
            bool needParen = !(
                (Root is Lookup) ||
                (Root is Member) ||
                (Root is CallNode) ||
                (Root is ThisLiteral) ||
                (Root is ArrayLiteral) ||
                (Root is ObjectLiteral) ||
                (Root is RegExpLiteral) ||
                (Root is FunctionObject) ||
                (Root is ConstantWrapper && !((ConstantWrapper)Root).IsNumericLiteral) ||
                (Root is ConstantWrapper && ((ConstantWrapper)Root).IsNumericLiteral && rootCrunched.Contains("."))
                );

            // if the root is a constructor with no arguments, we'll need to wrap it in parens so the
            // member-dot comes out with the right precedence.
            // (don't bother checking if we already are already going to use parens)
            if (!needParen)
            {
                CallNode callNode = Root as CallNode;
                if (callNode != null && callNode.IsConstructor && (callNode.Arguments == null || callNode.Arguments.Count == 0))
                {
                    needParen = true;
                }
            }

            StringBuilder sb = new StringBuilder();

            if (needParen)
            {
                sb.Append('(');
                sb.Append(rootCrunched);
                sb.Append(')');
            }
            else
            {
                sb.Append(rootCrunched);
            }
            sb.Append('.');
            sb.Append(Name);
            return(sb.ToString());
        }
        public override void Visit(CallNode node)
        {
            if (IsDefineFunction(node) && node.Arguments.Count > 0)
            {
                FoundModuleDefinition = true;

                // If first argument is a string, then it is the module path.
                var constWrapper = node.Arguments[0] as ConstantWrapper;
                if (constWrapper != null && constWrapper.PrimitiveType == PrimitiveType.String)
                {
                    ModulePath = (string)constWrapper.Value;
                }

                return;
            }

            base.Visit(node);
        }
Exemple #10
0
 internal override string GetFunctionGuess(AstNode target)
 {
     // MSN VOODOO: treat the as and ns methods as special if the expression is the root,
     // the parent is the call, and there is one string parameter -- use the string parameter
     if (Root == target && (Name == "as" || Name == "ns"))
     {
         CallNode call = Parent as CallNode;
         if (call != null && call.Arguments.Count == 1)
         {
             ConstantWrapper firstParam = call.Arguments[0] as ConstantWrapper;
             if (firstParam != null)
             {
                 return(firstParam.ToString());
             }
         }
     }
     return(Name);
 }
Exemple #11
0
        private string GuessAtName()
        {
            AstNode parent = Parent;

            if (parent != null)
            {
                if (parent is AstNodeList)
                {
                    // if the parent is an ASTList, then we're really interested
                    // in our parent's parent (probably a call)
                    parent = parent.Parent;
                }
                CallNode call = parent as CallNode;
                if (call != null && call.IsConstructor)
                {
                    // if this function expression is the object of a new, then we want the parent
                    parent = parent.Parent;
                }

                string guess = parent.GetFunctionGuess(this);
                if (guess != null && guess.Length > 0)
                {
                    if (guess.StartsWith("\"", StringComparison.Ordinal) &&
                        guess.EndsWith("\"", StringComparison.Ordinal))
                    {
                        // don't need to wrap it in quotes -- it already is
                        return(guess);
                    }
                    // wrap the guessed name in quotes
                    return(string.Format(CultureInfo.InvariantCulture, "\"{0}\"", guess));
                }
                else
                {
                    return(string.Format(CultureInfo.InvariantCulture, "anonymous_{0}", UniqueNumber));
                }
            }
            return(string.Empty);
        }
Exemple #12
0
        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);
                    }
                }
            }
        }
 public void Visit(CallNode node)
 {
     // invalid! ignore
     IsValid = false;
 }
Exemple #14
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);
             *  }
             * }
             */
        }
Exemple #15
0
        public override string ToCode(ToCodeFormat format)
        {
            StringBuilder sb = new StringBuilder();

            // we will only add a space if we KNOW we might need one
            bool needsSpace = false;

            // normal processing...
            if (m_isConstructor)
            {
                sb.Append("new");
                // the new operator might need to be followed by a space, depending
                // on what will actually follow
                needsSpace = true;
            }

            // list of items that DON'T need parens.
            // lookup, call or member don't need parens. All other items
            // are lower-precedence and therefore need to be wrapped in
            // parentheses to keep the right order.
            // function objects take care of their own parentheses.
            CallNode funcCall        = m_func as CallNode;
            bool     encloseInParens = !(
                (m_func is Lookup) ||
                (m_func is Member) ||
                (funcCall != null) ||
                (m_func is ThisLiteral) ||
                (m_func is FunctionObject)
                );

            // because if the new-operator associates to the right and the ()-operator associates
            // to the left, we need to be careful that we don't change the precedence order when the
            // function of a new operator is itself a call. In that case, the call will have it's own
            // parameters (and therefore parentheses) that will need to be associated with the call
            // and NOT the new -- the call will need to be surrounded with parens to keep that association.
            if (m_isConstructor && funcCall != null && !funcCall.InBrackets)
            {
                encloseInParens = true;
            }


            // if the root is a constructor with no arguments, we'll need to wrap it in parens so the
            // member-dot comes out with the right precedence.
            // (don't bother checking if we already are already going to use parens)
            if (!encloseInParens && funcCall != null && funcCall.IsConstructor &&
                (funcCall.Arguments == null || funcCall.Arguments.Count == 0))
            {
                encloseInParens = true;
            }

            if (encloseInParens)
            {
                // we're adding a parenthesis, so no -- we won't need to
                // add a space
                needsSpace = false;
                sb.Append('(');
            }

            string functionString = m_func.ToCode();

            // if we still think we might need a space, check the function we just
            // formatted. If it starts with an identifier part, then we need the space.
            if (needsSpace && JSScanner.StartsWithIdentifierPart(functionString))
            {
                sb.Append(' ');
            }
            sb.Append(functionString);

            if (encloseInParens)
            {
                sb.Append(')');
            }
            // if this isn't a constructor, or if it is and there are parameters,
            // then we want to output the parameters. But if this is a constructor with
            // no parameters, we can skip the whole empty-argument-parens thing altogether.
            if (!m_isConstructor || (m_args != null && m_args.Count > 0))
            {
                sb.Append((m_inBrackets ? '[' : '('));
                if (m_args != null)
                {
                    sb.Append(m_args.ToCode(ToCodeFormat.Commas));
                }
                sb.Append((m_inBrackets ? ']' : ')'));
            }

            return(sb.ToString());
        }
Exemple #16
0
 bool ShouldInsertModulePathArgument(CallNode node)
 {
     return IsDefineFunction(node)
         && DefineCallIsAnonymous(node);
 }
Exemple #17
0
 bool IsDefineFunction(CallNode node)
 {
     return node.Function.ToCode() == "define";
 }
Exemple #18
0
        //---------------------------------------------------------------------------------------
        // MemberExpression
        //
        // Accessor :
        //  <empty> |
        //  Arguments Accessor
        //  '[' Expression ']' Accessor |
        //  '.' Identifier Accessor |
        //
        //  Don't have this function throwing an exception without checking all the calling sites.
        //  There is state in instance variable that is saved on the calling stack in some function
        //  (i.e ParseFunction and ParseClass) and you don't want to blow up the stack
        //---------------------------------------------------------------------------------------
        private AstNode MemberExpression(AstNode expression, List<Context> newContexts)
        {
            for (; ; )
            {
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_MemberExprNoSkipTokenSet);
                try
                {
                    switch (m_currentToken.Token)
                    {
                        case JSToken.LeftParenthesis:
                            AstNodeList args = null;
                            RecoveryTokenException callError = null;
                            m_noSkipTokenSet.Add(NoSkipTokenSet.s_ParenToken);
                            try
                            {
                                args = ParseExpressionList(JSToken.RightParenthesis);
                            }
                            catch (RecoveryTokenException exc)
                            {
                                args = (AstNodeList)exc._partiallyComputedNode;
                                if (IndexOfToken(NoSkipTokenSet.s_ParenToken, exc) == -1)
                                    callError = exc; // thrown later on
                            }
                            finally
                            {
                                m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ParenToken);
                            }

                            expression = new CallNode(expression.Context.CombineWith(args.Context), this, expression, args, false);

                            if (null != newContexts && newContexts.Count > 0)
                            {
                                (newContexts[newContexts.Count - 1]).UpdateWith(expression.Context);
                                if (!(expression is CallNode))
                                    expression = new CallNode(newContexts[newContexts.Count - 1], this, expression, new AstNodeList(CurrentPositionContext(), this), false);
                                else
                                    expression.Context = newContexts[newContexts.Count - 1];
                                ((CallNode)expression).IsConstructor = true;
                                newContexts.RemoveAt(newContexts.Count - 1);
                            }

                            if (callError != null)
                            {
                                callError._partiallyComputedNode = expression;
                                throw callError;
                            }

                            GetNextToken();
                            break;

                        case JSToken.LeftBracket:
                            m_noSkipTokenSet.Add(NoSkipTokenSet.s_BracketToken);
                            try
                            {
                                //
                                // ROTOR parses a[b,c] as a call to a, passing in the arguments b and c.
                                // the correct parse is a member lookup on a of c -- the "b,c" should be
                                // a single expression with a comma operator that evaluates b but only
                                // returns c.
                                // So we'll change the default behavior from parsing an expression list to
                                // parsing a single expression, but returning a single-item list (or an empty
                                // list if there is no expression) so the rest of the code will work.
                                //
                                //args = ParseExpressionList(JSToken.RightBracket);
                                GetNextToken();
                                args = new AstNodeList(m_currentToken.Clone(), this);

                                AstNode accessor = ParseExpression();
                                if (accessor != null)
                                {
                                    args.Append(accessor);
                                }
                            }
                            catch (RecoveryTokenException exc)
                            {
                                if (IndexOfToken(NoSkipTokenSet.s_BracketToken, exc) == -1)
                                {
                                    if (exc._partiallyComputedNode != null)
                                    {
                                        exc._partiallyComputedNode =
                                           new CallNode(expression.Context.CombineWith(m_currentToken.Clone()), this, expression, (AstNodeList)exc._partiallyComputedNode, true);
                                    }
                                    else
                                    {
                                        exc._partiallyComputedNode = expression;
                                    }
                                    throw;
                                }
                                else
                                    args = (AstNodeList)exc._partiallyComputedNode;
                            }
                            finally
                            {
                                m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BracketToken);
                            }
                            expression = new CallNode(expression.Context.CombineWith(m_currentToken.Clone()), this, expression, args, true);

                            // there originally was code here in the ROTOR sources that checked the new context list and
                            // changed this member call to a constructor call, effectively combining the two. I believe they
                            // need to remain separate.

                            // remove the close bracket token
                            GetNextToken();
                            break;

                        case JSToken.AccessField:
                            ConstantWrapper id = null;
                            GetNextToken();
                            if (JSToken.Identifier != m_currentToken.Token)
                            {
                                string identifier = JSKeyword.CanBeIdentifier(m_currentToken.Token);
                                if (null != identifier)
                                {
                                    // don't report an error here -- it's actually okay to have a property name
                                    // that is a keyword which is okay to be an identifier. For instance,
                                    // jQuery has a commonly-used method named "get" to make an ajax request
                                    //ForceReportInfo(JSError.KeywordUsedAsIdentifier);
                                    id = new ConstantWrapper(identifier, PrimitiveType.String, m_currentToken.Clone(), this);
                                }
                                else if (JSScanner.IsValidIdentifier(m_currentToken.Code))
                                {
                                    // it must be a keyword, because it can't technically be an indentifier,
                                    // but it IS a valid identifier format. Throw the error but still
                                    // create the constant wrapper so we can output it as-is
                                    ReportError(JSError.NoIdentifier, m_currentToken.Clone(), true);
                                    id = new ConstantWrapper(m_currentToken.Code, PrimitiveType.String, m_currentToken.Clone(), this);
                                }
                                else
                                {
                                    ReportError(JSError.NoIdentifier);
                                    SkipTokensAndThrow(expression);
                                }
                            }
                            else
                            {
                                id = new ConstantWrapper(m_scanner.GetIdentifier(), PrimitiveType.String, m_currentToken.Clone(), this);
                            }
                            GetNextToken();
                            expression = new Member(expression.Context.CombineWith(id.Context), this, expression, id.Context.Code, id.Context);
                            break;
                        default:
                            if (null != newContexts)
                            {
                                while (newContexts.Count > 0)
                                {
                                    (newContexts[newContexts.Count - 1]).UpdateWith(expression.Context);
                                    expression = new CallNode(newContexts[newContexts.Count - 1],
                                                          this,
                                                          expression,
                                                          new AstNodeList(CurrentPositionContext(), this),
                                                          false);
                                    ((CallNode)expression).IsConstructor = true;
                                    newContexts.RemoveAt(newContexts.Count - 1);
                                }
                            }
                            return expression;
                    }
                }
                catch (RecoveryTokenException exc)
                {
                    if (IndexOfToken(NoSkipTokenSet.s_MemberExprNoSkipTokenSet, exc) != -1)
                        expression = exc._partiallyComputedNode;
                    else
                    {
                        Debug.Assert(exc._partiallyComputedNode == expression);
                        throw;
                    }
                }
                finally
                {
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_MemberExprNoSkipTokenSet);
                }
            }
        }
Exemple #19
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;
                        }
                    }
                }
            }
        }
Exemple #20
0
 public void Visit(CallNode node)
 {
     ReportError(node);
 }
Exemple #21
0
 bool DefineCallIsAnonymous(CallNode node)
 {
     // e.g. define( [...], function() {} )
     // or   define( function() {} )
     return node.Arguments.Count < 3;
 }
Exemple #22
0
 public override void Visit(CallNode node)
 {
     // same logic for most nodes
     TypicalHandler(node);
 }