public override void Visit()

in AjaxMinDll/JavaScript/AnalyzeNodeVisitor.cs [3832:4039]


        public override void Visit(UnaryOperator node)
        {
            if (node != null)
            {
                base.Visit(node);

                if (node.Operand != null && node.Operand.IsDebugOnly)
                {
                    node.IsDebugOnly = true;
                    switch (node.OperatorToken)
                    {
                        case JSToken.Void:
                            // void *anything* is the same as void 0
                            node.Operand = new ConstantWrapper(0, PrimitiveType.Number, node.Operand.Context);
                            break;

                        case JSToken.TypeOf:
                            // typeof null is "object"
                            node.Parent.ReplaceChild(node, new ConstantWrapper("object", PrimitiveType.String, node.Context)
                                {
                                    IsDebugOnly = true
                                });
                            break;

                        case JSToken.Delete:
                            // delete null is true
                            node.Operand = new ConstantWrapper(true, PrimitiveType.Boolean, node.Context);
                            break;

                        case JSToken.Increment:
                        case JSToken.Decrement:
                            // ++ and -- result in a number, so just replace with 0
                            node.Parent.ReplaceChild(node, new ConstantWrapper(0, PrimitiveType.Number, node.Context)
                                {
                                    IsDebugOnly = true
                                });
                            break;

                        case JSToken.LogicalNot:
                            // !null is true
                            node.Parent.ReplaceChild(node, new ConstantWrapper(true, PrimitiveType.Boolean, node.Context)
                                {
                                    IsDebugOnly = true
                                });
                            break;

                        case JSToken.BitwiseNot:
                            // ~null is -1
                            node.Parent.ReplaceChild(node, new ConstantWrapper(-1, PrimitiveType.Number, node.Context)
                                {
                                    IsDebugOnly = true
                                });
                            break;

                        case JSToken.Plus:
                            // +null is zero
                            node.Parent.ReplaceChild(node, new ConstantWrapper(0, PrimitiveType.Number, node.Context)
                                {
                                    IsDebugOnly = true
                                });
                            break;

                        case JSToken.Minus:
                            // -null is negative zero
                            node.Parent.ReplaceChild(node, new ConstantWrapper(-0, PrimitiveType.Number, node.Context)
                                {
                                    IsDebugOnly = true
                                });
                            break;

                        default:
                            node.Operand = ClearDebugExpression(node.Operand);
                            break;
                    }
                }
                else
                {
                    // strict mode has some restrictions
                    if (node.OperatorToken == JSToken.Delete)
                    {
                        if (m_scopeStack.Peek().UseStrict)
                        {
                            // operand of a delete operator cannot be a variable name, argument name, or function name
                            // which means it can't be a lookup
                            if (node.Operand is Lookup)
                            {
                                node.Context.HandleError(JSError.StrictModeInvalidDelete, true);
                            }
                        }
                    }
                    else if (node.OperatorToken == JSToken.Increment || node.OperatorToken == JSToken.Decrement)
                    {
                        var lookup = node.Operand as Lookup;
                        if (lookup != null)
                        {
                            if (lookup.VariableField != null && lookup.VariableField.InitializationOnly)
                            {
                                // can't increment or decrement a constant!
                                lookup.Context.HandleError(JSError.AssignmentToConstant, true);
                            }

                            // and strict mode has some restrictions we want to check now
                            if (m_scopeStack.Peek().UseStrict)
                            {
                                // the operator cannot be the eval function or arguments object.
                                // that means the operator is a lookup, and the field for that lookup
                                // is the arguments object or the predefined "eval" object.
                                if (lookup.VariableField == null
                                    || lookup.VariableField.FieldType == FieldType.UndefinedGlobal
                                    || lookup.VariableField.FieldType == FieldType.Arguments
                                    || (lookup.VariableField.FieldType == FieldType.Predefined 
                                    && string.CompareOrdinal(lookup.Name, "eval") == 0))
                                {
                                    node.Operand.Context.HandleError(JSError.StrictModeInvalidPreOrPost, true);
                                }
                            }
                        }
                    }
                    else if (node.OperatorToken == JSToken.TypeOf)
                    {
                        if (m_parser.Settings.RemoveUnneededCode
                            && m_parser.Settings.IsModificationAllowed(TreeModifications.RemoveWindowDotFromTypeOf))
                        {
                            // we want to see if the typeof operand is window.name -- which is getting the type string of 
                            // a potential global variable. If "name" would otherwise resolve to the global namespace (either
                            // defined or undefined), then we can really get rid of the "window." part because the typeof
                            // operator will work just fine if the operand is undefined (it won't throw a reference error).
                            var member = node.Operand as Member;
                            if (member != null)
                            {
                                var lookup = member.Root as Lookup;
                                if (lookup != null
                                    && lookup.VariableField != null
                                    && lookup.VariableField.FieldType == FieldType.Predefined
                                    && lookup.Name == "window")
                                {
                                    // we have window.name
                                    // now check to see if the name part of our member would resolve to something in
                                    // the global namespace.
                                    var name = member.Name;
                                    var enclosingScope = member.EnclosingScope;
                                    var existingField = enclosingScope.CanReference(name);
                                    if (existingField == null
                                        || existingField.FieldType == FieldType.Predefined
                                        || existingField.FieldType == FieldType.Global
                                        || existingField.FieldType == FieldType.UndefinedGlobal)
                                    {
                                        // replace the member with a lookup on the name.
                                        // first, detach the reference to window
                                        DetachReferences.Apply(lookup);

                                        // (just reuse the lookup for "window" by changing the name and doing
                                        // a formal reference lookup on it (which will generate fields if needed)
                                        lookup.Name = name;
                                        lookup.VariableField = enclosingScope.FindReference(name);
                                        node.Operand = lookup;

                                        // and make sure we increment the new reference
                                        lookup.VariableField.AddReference(lookup);
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        // 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();
                                }
                            }
                        }
                    }
                }
            }
        }