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();
}
}
}
}
}
}
}