private bool IsMinificationHint(ConstantWrapper node) { var isHint = false; if (node.PrimitiveType == PrimitiveType.String) { // try splitting on commas and removing empty items var sections = node.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (var section in sections) { // valid hints are: // name:nomunge don't automatically rename the field defined in this scope named "name" // if name is missing (colon is the first character) or "*", then don't rename ANY // fields defined in the current scope. var ndxColon = section.IndexOf(':'); if (ndxColon >= 0) { // make sure this is a "nomunge" hint. If it is, then the entire node is treated as a hint and // will be removed from the AST. if (string.Compare(section.Substring(ndxColon + 1).Trim(), "nomunge", StringComparison.OrdinalIgnoreCase) == 0) { // it is. isHint = true; // get the name that we don't want to munge. Null means all. Convert "*" // to null. var identifier = section.Substring(0, ndxColon).Trim(); if (string.IsNullOrEmpty(identifier) || string.CompareOrdinal(identifier, "*") == 0) { identifier = null; } // get the current scope and iterate over all the fields within it // looking for just the ones that are defined here (outer is null) var currentScope = node.EnclosingScope ?? m_globalScope; foreach (var field in currentScope.NameTable.Values) { if (field.OuterField == null) { // if the identifier is null or matches exactly, mark it as not crunchable if (identifier == null || string.CompareOrdinal(identifier, field.Name) == 0) { field.CanCrunch = false; } } } } } } } return(isHint); }
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); }
/// <summary> /// Optimized string literal concatenation for repeat usage pattern, avoiding multiple copying and allocation /// </summary> /// <param name="other"></param> /// <returns></returns> public StringList Concat(ConstantWrapper other) { var left = this.Value; if (this.PrimitiveType != PrimitiveType.String) { left = this.ToString(); } object right = null; if (other != null) { right = other.Value; if (other.PrimitiveType != PrimitiveType.String) { right = other.ToString(); } } return(new StringList(left, right)); }
internal override string GetFunctionGuess(AstNode target) { // get our guess from the function call string funcName = m_func.GetFunctionGuess(target); // MSN VOODOO: if this is the addMethod method, then the // name of the function is the first parameter. // The syntax of the add method call is: obj.addMethod("name",function(){...}) // so there should be two parameters.... if (funcName == "addMethod" && m_args.Count == 2) { // the first one should be a string constant.... ConstantWrapper firstParam = m_args[0] as ConstantWrapper; // and the second one should be the function expression we're looking for if ((firstParam != null) && (firstParam.Value is string) && (m_args[1] == target)) { // use that first parameter as the guess funcName = firstParam.ToString(); } } return(funcName); }
private ConstantWrapper Equal(ConstantWrapper left, ConstantWrapper right) { ConstantWrapper newLiteral = null; if (m_parser.Settings.IsModificationAllowed(TreeModifications.EvaluateNumericExpressions)) { PrimitiveType leftType = left.PrimitiveType; if (leftType == right.PrimitiveType) { // the values are the same type switch (leftType) { case PrimitiveType.Null: // null == null is true newLiteral = new ConstantWrapper(true, PrimitiveType.Boolean, null, m_parser); break; case PrimitiveType.Boolean: // compare boolean values newLiteral = new ConstantWrapper(left.ToBoolean() == right.ToBoolean(), PrimitiveType.Boolean, null, m_parser); break; case PrimitiveType.String: // compare string ordinally newLiteral = new ConstantWrapper(string.CompareOrdinal(left.ToString(), right.ToString()) == 0, PrimitiveType.Boolean, null, m_parser); break; case PrimitiveType.Number: try { // compare the values // +0 and -0 are treated as "equal" in C#, so we don't need to test them separately. // and NaN is always unequal to everything else, including itself. if (left.IsOkayToCombine && right.IsOkayToCombine) { newLiteral = new ConstantWrapper(left.ToNumber() == right.ToNumber(), PrimitiveType.Boolean, null, 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; } } else if (left.IsOkayToCombine && right.IsOkayToCombine) { try { // numeric comparison // +0 and -0 are treated as "equal" in C#, so we don't need to test them separately. // and NaN is always unequal to everything else, including itself. newLiteral = new ConstantWrapper(left.ToNumber() == right.ToNumber(), PrimitiveType.Boolean, null, 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 } } } return newLiteral; }
private ConstantWrapper StringConcat(ConstantWrapper left, ConstantWrapper right) { ConstantWrapper newLiteral = null; // if we don't want to combine adjacent string literals, then we know we don't want to do // anything here. if (m_parser.Settings.IsModificationAllowed(TreeModifications.CombineAdjacentStringLiterals)) { // if either one of the operands is not a string literal, then check to see if we allow // evaluation of numeric expression; if not, then no-go. IF they are both string literals, // then it doesn't matter what the numeric flag says. if ((left.IsStringLiteral && right.IsStringLiteral) || m_parser.Settings.IsModificationAllowed(TreeModifications.EvaluateNumericExpressions)) { // if either value is a floating-point number (a number, not NaN, not Infinite, not an Integer), // then we won't do the string concatenation because different browsers may have subtle differences // in their double-to-string conversion algorithms. // so if neither is a numeric literal, or if one or both are, if they are both integer literals // in the range that we can EXACTLY represent them in a double, then we can proceed. // NaN, +Infinity and -Infinity are also acceptable if ((!left.IsNumericLiteral || left.IsExactInteger || left.IsNaN || left.IsInfinity) && (!right.IsNumericLiteral || right.IsExactInteger || right.IsNaN || right.IsInfinity)) { // they are both either string, bool, null, or integer newLiteral = new ConstantWrapper(left.ToString() + right.ToString(), PrimitiveType.String, null, m_parser); } } } return newLiteral; }
/// <summary> /// If the new literal is a string literal, then we need to check to see if our /// parent is a CallNode. If it is, and if the string literal can be an identifier, /// we'll replace it with a Member-Dot operation. /// </summary> /// <param name="newLiteral">newLiteral we intend to replace this binaryop node with</param> /// <returns>true if we replaced the parent callnode with a member-dot operation</returns> private bool ReplaceMemberBracketWithDot(BinaryOperator node, ConstantWrapper newLiteral) { if (newLiteral.IsStringLiteral) { // see if this newly-combined string is the sole argument to a // call-brackets node. If it is and the combined string is a valid // identifier (and not a keyword), then we can replace the call // with a member operator. // remember that the parent of the argument won't be the call node -- it // will be the ast node list representing the arguments, whose parent will // be the node list. CallNode parentCall = (node.Parent is AstNodeList ? node.Parent.Parent as CallNode : null); if (parentCall != null && parentCall.InBrackets) { // get the newly-combined string string combinedString = newLiteral.ToString(); // see if this new string is the target of a replacement operation string newName; if (m_parser.Settings.HasRenamePairs && m_parser.Settings.ManualRenamesProperties && m_parser.Settings.IsModificationAllowed(TreeModifications.PropertyRenaming) && !string.IsNullOrEmpty(newName = m_parser.Settings.GetNewName(combinedString))) { // yes, it is. Now see if the new name is safe to be converted to a dot-operation. if (m_parser.Settings.IsModificationAllowed(TreeModifications.BracketMemberToDotMember) && JSScanner.IsSafeIdentifier(newName) && !JSScanner.IsKeyword(newName, parentCall.EnclosingScope.UseStrict)) { // we want to replace the call with operator with a new member dot operation, and // since we won't be analyzing it (we're past the analyze phase, we're going to need // to use the new string value Member replacementMember = new Member(parentCall.Context, m_parser, parentCall.Function, newName, parentCall.Arguments[0].Context); parentCall.Parent.ReplaceChild(parentCall, replacementMember); return true; } else { // nope, can't be changed to a dot-operator for whatever reason. // just replace the value on this new literal. The old operation will // get replaced with this new literal newLiteral.Value = newName; // and make sure it's type is string newLiteral.PrimitiveType = PrimitiveType.String; } } else if (m_parser.Settings.IsModificationAllowed(TreeModifications.BracketMemberToDotMember)) { // our parent is a call-bracket -- now we just need to see if the newly-combined // string can be an identifier if (JSScanner.IsSafeIdentifier(combinedString) && !JSScanner.IsKeyword(combinedString, parentCall.EnclosingScope.UseStrict)) { // yes -- replace the parent call with a new member node using the newly-combined string Member replacementMember = new Member(parentCall.Context, m_parser, parentCall.Function, combinedString, parentCall.Arguments[0].Context); parentCall.Parent.ReplaceChild(parentCall, replacementMember); return true; } } } } return false; }
private ConstantWrapper LessThanOrEqual(ConstantWrapper left, ConstantWrapper right) { ConstantWrapper newLiteral = null; if (m_parser.Settings.IsModificationAllowed(TreeModifications.EvaluateNumericExpressions)) { if (left.IsStringLiteral && right.IsStringLiteral) { // do a straight ordinal comparison of the strings newLiteral = new ConstantWrapper(string.CompareOrdinal(left.ToString(), right.ToString()) <= 0, PrimitiveType.Boolean, null, m_parser); } else { try { // either one or both are NOT a string -- numeric comparison if (left.IsOkayToCombine && right.IsOkayToCombine) { newLiteral = new ConstantWrapper(left.ToNumber() <= right.ToNumber(), PrimitiveType.Boolean, null, 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 } } } return newLiteral; }