internal override void AnalyzeNode() { // recurse first, then check to see if the unary is still needed base.AnalyzeNode(); // if the operand is a numeric literal ConstantWrapper constantWrapper = 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 (OperatorToken == JSToken.Minus && Parser.Settings.IsModificationAllowed(TreeModifications.ApplyUnaryMinusToNumericLiteral)) { // negate the value constantWrapper.Value = -doubleValue; // replace us with the negated constant if (Parent.ReplaceChild(this, 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 = Context.Clone(); return; } } else if (OperatorToken == JSToken.Plus && 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 (Parent.ReplaceChild(this, 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 = Context.Clone(); return; } } } }
protected virtual void CreateLiteralShortcuts() { if (m_literalMap != null) { // get a reference to the first function scope in the chain // might be this, might be a parent FunctionScope functionScope = null; ActivationObject scope = this; while (scope != null && (functionScope = scope as FunctionScope) == null) { scope = scope.Parent; } // if we didn't find a parent function scope, then don't do any combining // because the literals are globals if (functionScope != null) { // for each value in our literal map foreach (string constantString in m_literalMap.Keys) { LiteralReference literalReference = m_literalMap[constantString]; // if the child scope isn't null, then we don't reference the literal // and only one of our child scopes does, so we don't want to add the // shortcut here. // OR if there are no constant wrappers left in the list, then we've already // replaced them all and there's nothing left to do. // BUT if the child scope is null, either we reference it, or more than // one child references it. So if there are any constant wrappers in the list, // then we want to add the shortcut and replace all the constants if (literalReference.ChildScope == null && literalReference.ConstantWrapperList.Count > 0) { // AND we only want to do it if it will be worthwhile. // (and a constant of length 1 is never worthwhile) int constantLength = constantString.Length; if (constantLength > 1) { int minCount = (constantLength + 7) / (constantLength - 1); if (literalReference.Count > minCount) { // create a special name that won't collide with any other variable names string specialName = string.Format(CultureInfo.InvariantCulture, "[literal:{0}]", ++s_literalCounter); // add a generated var statement at the top of the function block that // is equal to the literal value (just use the first constant wrapper as a model) ConstantWrapper modelConstant = literalReference.ConstantWrapperList[0]; // by default we will use the value of the first instance as the generated variable's value object generatedValue = modelConstant.Value; // BUT.... // if this is a numeric value, then we need to determine whether we should use a // positive or negative version of this value to minimize the number of minus operators in the results if (modelConstant.IsNumericLiteral) { // first we need to go through the existing references and count how many negative values there are var numberOfNegatives = 0; foreach (ConstantWrapper constantWrapper in literalReference.ConstantWrapperList) { // since the model us numeric, we shouldn't have any problems calling the // ToNumber method on the others (which should all also be numeric) if (constantWrapper.ToNumber() < 0) { ++numberOfNegatives; } } // now if more than half of the references are negative, we will want the generated value // to also be negative! Otherwise we want to force it to Positive. var absoluteValue = Math.Abs((double)generatedValue); if (numberOfNegatives > literalReference.ConstantWrapperList.Count / 2) { // force it to negative generatedValue = -absoluteValue; } else { // force it to positive generatedValue = absoluteValue; } } // add the generated variable to the function scope functionScope.FunctionObject.AddGeneratedVar( specialName, new ConstantWrapper( generatedValue, modelConstant.PrimitiveType, modelConstant.Context, Parser), true); // walk the list of constant wrappers backwards (because we'll be removing them // as we go along) and replace each one with a lookup for the generated variable. // Don't forget to analyze the lookup. for (int ndx = literalReference.ConstantWrapperList.Count - 1; ndx >= 0; --ndx) { ConstantWrapper constantWrapper = literalReference.ConstantWrapperList[ndx]; // create the lookup based on the thisliteral context Lookup lookup = new Lookup(specialName, constantWrapper.Context, Parser); // indicate this is generated by our code, not the user lookup.IsGenerated = true; // by default, we're just going to replace the constant with the lookup AstNode replacement = lookup; // if the constant wrapper is a numeric value that is the NEGATIVE of the // combined numeric value (which would happen if the literal was subsequently // combined with a unary minus operator), then we need to change this to a unary-minus // operator on the lookup, not just the lookup. if (constantWrapper.IsNumericLiteral) { // since the constant wrapper is numeric, we shouldn't have any problems // calling ToNumber if ((double)generatedValue == -constantWrapper.ToNumber()) { // it has been negated! Change the replacement to a unary minus operator // with the lookup as its operand replacement = new NumericUnary( constantWrapper.Context, Parser, lookup, JSToken.Minus); } } // replace the this literal with the appropriate node constantWrapper.Parent.ReplaceChild(constantWrapper, replacement); // set up the lookup's outer local field using the scope of the // original constant wrapper lookup.SetOuterLocalField(constantWrapper.EnclosingScope); // and remove it from the list. This is so child scopes don't also try to // add a shortcut -- the list will be empty. literalReference.ConstantWrapperList.RemoveAt(ndx); } } } } } } } }
public override void CleanupNodes() { base.CleanupNodes(); if (Parser.Settings.EvalLiteralExpressions && Parser.Settings.IsModificationAllowed(TreeModifications.EvaluateNumericExpressions)) { // see if our operand is a ConstantWrapper ConstantWrapper literalOperand = Operand as ConstantWrapper; if (literalOperand != null) { // must be number, boolean, string, or null switch (OperatorToken) { case JSToken.Plus: try { // replace with a constant representing operand.ToNumber, Parent.ReplaceChild(this, new ConstantWrapper(literalOperand.ToNumber(), PrimitiveType.Number, Context, Parser)); } catch (InvalidCastException) { // some kind of casting in ToNumber caused a situation where we don't want // to perform the combination on these operands } break; case JSToken.Minus: try { // replace with a constant representing the negative of operand.ToNumber Parent.ReplaceChild(this, new ConstantWrapper(-literalOperand.ToNumber(), PrimitiveType.Number, Context, Parser)); } catch (InvalidCastException) { // some kind of casting in ToNumber caused a situation where we don't want // to perform the combination on these operands } break; case JSToken.BitwiseNot: try { // replace with a constant representing the bitwise-not of operant.ToInt32 Parent.ReplaceChild(this, new ConstantWrapper(Convert.ToDouble(~literalOperand.ToInt32()), PrimitiveType.Number, Context, Parser)); } catch (InvalidCastException) { // some kind of casting in ToNumber caused a situation where we don't want // to perform the combination on these operands } break; case JSToken.LogicalNot: // replace with a constant representing the opposite of operand.ToBoolean try { Parent.ReplaceChild(this, new ConstantWrapper(!literalOperand.ToBoolean(), PrimitiveType.Boolean, Context, Parser)); } catch (InvalidCastException) { // ignore any invalid cast exceptions } break; } } } }
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 Divide(ConstantWrapper left, ConstantWrapper right) { ConstantWrapper newLiteral = null; if (left.IsOkayToCombine && right.IsOkayToCombine && m_parser.Settings.IsModificationAllowed(TreeModifications.EvaluateNumericExpressions)) { try { double leftValue = left.ToNumber(); double rightValue = right.ToNumber(); double result = leftValue / rightValue; if (ConstantWrapper.NumberIsOkayToCombine(result)) { newLiteral = new ConstantWrapper(result, PrimitiveType.Number, null, m_parser); } else { if (!left.IsNumericLiteral && ConstantWrapper.NumberIsOkayToCombine(leftValue)) { left.Parent.ReplaceChild(left, new ConstantWrapper(leftValue, PrimitiveType.Number, left.Context, m_parser)); } if (!right.IsNumericLiteral && ConstantWrapper.NumberIsOkayToCombine(rightValue)) { right.Parent.ReplaceChild(right, new ConstantWrapper(rightValue, PrimitiveType.Number, right.Context, m_parser)); } } } catch (InvalidCastException) { // some kind of casting in ToNumber caused a situation where we don't want // to perform the combination on these operands } } return newLiteral; }
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; }