public void Visit(JsUnaryOperator node) { // if this is a postfix operator and there is an operand, recurse into it if (node != null && node.IsPostfix && node.Operand != null) { node.Operand.Accept(this); } }
public void Visit(JsUnaryOperator node) { if (node != null) { if (node.Operand != null) { node.Operand.Accept(this); } node.Index = NextOrderIndex; } }
public override void Visit(JsUnaryOperator node) { if (node != null && !node.OperatorInConditionalCompilationComment) { // if this is a unary logical-not operator, then we will just remove the // logical-not operation if (node.OperatorToken == JsToken.LogicalNot) { if (m_measure) { // measure // removes the not operator character, but also might remove parens that we would // no longer need. --m_delta; if (node.Operand is JsBinaryOperator || node.Operand is JsConditional || node.Operand is JsGroupingOperator) { // those operators are lesser-precedence than the logical-not coperator and would've // added parens that we now don't need m_delta -= 2; } } else { // convert // just replace the not with its own operand, unless the child // itself is a grouping operator, in which case we will replace it // with the grouping operand to get rid of the parens var grouping = node.Operand as JsGroupingOperator; if (grouping != null) { node.Parent.ReplaceChild(node, grouping.Operand); } else { node.Parent.ReplaceChild(node, node.Operand); } } } else { // same logic as most nodes for the other operators TypicalHandler(node); } } }
public void Visit(JsUnaryOperator node) { if (node != null) { // only a negation is allowed -- and even then, I'm not sure // if it has already been integrated into the numeric value yet. if (node.OperatorToken == JsToken.Minus) { m_writer.Write('-'); if (node.Operand != null) { node.Operand.Accept(this); } } else { // invalid! ignore IsValid = false; } } }
public void Visit(JsUnaryOperator node) { // lesser precedence than the new operator; use parens m_needsParens = true; }
//--------------------------------------------------------------------------------------- // ParseUnaryExpression // // UnaryExpression : // PostfixExpression | // 'delete' UnaryExpression | // 'void' UnaryExpression | // 'typeof' UnaryExpression | // '++' UnaryExpression | // '--' UnaryExpression | // '+' UnaryExpression | // '-' UnaryExpression | // '~' UnaryExpression | // '!' UnaryExpression // //--------------------------------------------------------------------------------------- private JsAstNode ParseUnaryExpression(out bool isLeftHandSideExpr, bool isMinus) { isLeftHandSideExpr = false; bool dummy = false; JsContext exprCtx = null; JsAstNode expr = null; TryItAgain: JsAstNode ast = null; switch (m_currentToken.Token) { case JsToken.Void: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JsToken.Void }; break; case JsToken.TypeOf: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JsToken.TypeOf }; break; case JsToken.Plus: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JsToken.Plus }; break; case JsToken.Minus: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, true); ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JsToken.Minus }; break; case JsToken.BitwiseNot: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JsToken.BitwiseNot }; break; case JsToken.LogicalNot: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JsToken.LogicalNot }; break; case JsToken.Delete: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JsToken.Delete }; break; case JsToken.Increment: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JsToken.Increment }; break; case JsToken.Decrement: exprCtx = m_currentToken.Clone(); GetNextToken(); expr = ParseUnaryExpression(out dummy, false); ast = new JsUnaryOperator(exprCtx.Clone().UpdateWith(expr.Context), this) { Operand = expr, OperatorContext = exprCtx, OperatorToken = JsToken.Decrement }; break; case JsToken.ConditionalCommentStart: // skip past the start to the next token exprCtx = m_currentToken.Clone(); GetNextToken(); if (m_currentToken.Token == JsToken.ConditionalCommentEnd) { // empty conditional-compilation comment -- ignore GetNextToken(); goto TryItAgain; } else if (m_currentToken.Token == JsToken.ConditionalCompilationOn) { // /*@cc_on -- check for @IDENT@*/ or !@*/ GetNextToken(); if (m_currentToken.Token == JsToken.ConditionalCompilationVariable) { // /*@cc_on@IDENT -- check for @*/ ast = new JsConstantWrapperPP(m_currentToken.Clone(), this) { VarName = m_currentToken.Code, ForceComments = true }; GetNextToken(); if (m_currentToken.Token == JsToken.ConditionalCommentEnd) { // skip the close and keep going GetNextToken(); } else { // too complicated CCTooComplicated(null); goto TryItAgain; } } else if (m_currentToken.Token == JsToken.LogicalNot) { // /*@cc_on! -- check for @*/ var operatorContext = m_currentToken.Clone(); GetNextToken(); if (m_currentToken.Token == JsToken.ConditionalCommentEnd) { // we have /*@cc_on!@*/ GetNextToken(); expr = ParseUnaryExpression(out dummy, false); exprCtx.UpdateWith(expr.Context); var unary = new JsUnaryOperator(exprCtx, this) { Operand = expr, OperatorContext = operatorContext, OperatorToken = JsToken.LogicalNot }; unary.OperatorInConditionalCompilationComment = true; unary.ConditionalCommentContainsOn = true; ast = unary; } else { // too complicated CCTooComplicated(null); goto TryItAgain; } } else { // too complicated CCTooComplicated(null); goto TryItAgain; } } else if (m_currentToken.Token == JsToken.LogicalNot) { // /*@! -- check for @*/ var operatorContext = m_currentToken.Clone(); GetNextToken(); if (m_currentToken.Token == JsToken.ConditionalCommentEnd) { // we have /*@!@*/ GetNextToken(); expr = ParseUnaryExpression(out dummy, false); exprCtx.UpdateWith(expr.Context); var unary = new JsUnaryOperator(exprCtx, this) { Operand = expr, OperatorContext = operatorContext, OperatorToken = JsToken.LogicalNot }; unary.OperatorInConditionalCompilationComment = true; ast = unary; } else { // too complicated CCTooComplicated(null); goto TryItAgain; } } else if (m_currentToken.Token == JsToken.ConditionalCompilationVariable) { // @IDENT -- check for @*/ ast = new JsConstantWrapperPP(m_currentToken.Clone(), this) { VarName = m_currentToken.Code, ForceComments = true }; GetNextToken(); if (m_currentToken.Token == JsToken.ConditionalCommentEnd) { // skip the close and keep going GetNextToken(); } else { // too complicated CCTooComplicated(null); goto TryItAgain; } } else { // we ONLY support /*@id@*/ or /*@cc_on@id@*/ or /*@!@*/ or /*@cc_on!@*/ in expressions right now. // throw an error, skip to the end of the comment, then ignore it and start // looking for the next token. CCTooComplicated(null); goto TryItAgain; } break; default: m_noSkipTokenSet.Add(NoSkipTokenSet.s_PostfixExpressionNoSkipTokenSet); try { ast = ParseLeftHandSideExpression(isMinus); } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_PostfixExpressionNoSkipTokenSet, exc) == -1) { throw; } else { if (exc._partiallyComputedNode == null) SkipTokensAndThrow(); else ast = exc._partiallyComputedNode; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_PostfixExpressionNoSkipTokenSet); } ast = ParsePostfixExpression(ast, out isLeftHandSideExpr); break; } return ast; }
public override void Visit(JsBinaryOperator node) { if (node != null) { base.Visit(node); // see if this operation is subtracting zero from a lookup -- that is typically done to // coerce a value to numeric. There's a simpler way: unary plus operator. if (node.OperatorToken == JsToken.Minus && m_parser.Settings.IsModificationAllowed(JsTreeModifications.SimplifyStringToNumericConversion)) { JsLookup lookup = node.Operand1 as JsLookup; if (lookup != null) { JsConstantWrapper right = node.Operand2 as JsConstantWrapper; if (right != null && right.IsIntegerLiteral && right.ToNumber() == 0) { // okay, so we have "lookup - 0" // this is done frequently to force a value to be numeric. // There is an easier way: apply the unary + operator to it. // transform: lookup - 0 => +lookup var unary = new JsUnaryOperator(node.Context, m_parser) { Operand = lookup, OperatorToken = JsToken.Plus }; node.Parent.ReplaceChild(node, unary); // because we recursed at the top of this function, we don't need to Analyze // the new Unary node. This visitor's method for UnaryOperator only does something // if the operand is a constant -- and this one is a Lookup. And we already analyzed // the lookup. } } } else if ((node.OperatorToken == JsToken.StrictEqual || node.OperatorToken == JsToken.StrictNotEqual) && m_parser.Settings.IsModificationAllowed(JsTreeModifications.ReduceStrictOperatorIfTypesAreSame)) { JsPrimitiveType leftType = node.Operand1.FindPrimitiveType(); if (leftType != JsPrimitiveType.Other) { JsPrimitiveType rightType = node.Operand2.FindPrimitiveType(); if (leftType == rightType) { // the are the same known types. We can reduce the operators node.OperatorToken = node.OperatorToken == JsToken.StrictEqual ? JsToken.Equal : JsToken.NotEqual; } else if (rightType != JsPrimitiveType.Other) { // they are not the same, but they are both known. We can completely remove the operator // and replace it with true (!==) or false (===). // transform: x !== y => true // transform: x === y => false node.Context.HandleError(JsError.StrictComparisonIsAlwaysTrueOrFalse, false); node.Parent.ReplaceChild( node, new JsConstantWrapper(node.OperatorToken == JsToken.StrictNotEqual, JsPrimitiveType.Boolean, node.Context, m_parser)); // because we are essentially removing the node from the AST, be sure to detach any references JsDetachReferences.Apply(node); } } } else if (node.IsAssign) { var lookup = node.Operand1 as JsLookup; if (lookup != null) { if (lookup.VariableField != null && lookup.VariableField.InitializationOnly) { // the field is an initialization-only field -- we should NOT be assigning to it lookup.Context.HandleError(JsError.AssignmentToConstant, true); } else if (m_scopeStack.Peek().UseStrict) { if (lookup.VariableField == null || lookup.VariableField.FieldType == JsFieldType.UndefinedGlobal) { // strict mode cannot assign to undefined fields node.Operand1.Context.HandleError(JsError.StrictModeUndefinedVariable, true); } else if(lookup.VariableField.FieldType == JsFieldType.Arguments || (lookup.VariableField.FieldType == JsFieldType.Predefined && string.CompareOrdinal(lookup.Name, "eval") == 0)) { // strict mode cannot assign to lookup "eval" or "arguments" node.Operand1.Context.HandleError(JsError.StrictModeInvalidAssign, true); } } } } else if ((node.Parent is JsBlock || (node.Parent is JsCommaOperator && node.Parent.Parent is JsBlock)) && (node.OperatorToken == JsToken.LogicalOr || node.OperatorToken == JsToken.LogicalAnd)) { // this is an expression statement where the operator is || or && -- basically // it's a shortcut for an if-statement: // expr1&&expr2; ==> if(expr1)expr2; // expr1||expr2; ==> if(!expr1)expr2; // let's check to see if the not of expr1 is smaller. If so, we can not the expression // and change the operator var logicalNot = new JsLogicalNot(node.Operand1, node.Parser); if (logicalNot.Measure() < 0) { // it would be smaller! Change it. // transform: expr1&&expr2 => !expr1||expr2 // transform: expr1||expr2 => !expr1&&expr2 logicalNot.Apply(); node.OperatorToken = node.OperatorToken == JsToken.LogicalAnd ? JsToken.LogicalOr : JsToken.LogicalAnd; } } } }
public void Visit(JsUnaryOperator node) { if (node != null) { var symbol = StartSymbol(node); var isNoIn = m_noIn; m_noIn = false; if (node.IsPostfix) { if (node.Operand != null) { AcceptNodeWithParens(node.Operand, node.Operand.Precedence < node.Precedence); SetContextOutputPosition(node.Context, node.Operand.Context); } // the only postfix unary operators are ++ and --, and when in the postfix position, // line breaks are NOT allowed between the operand and the operator. // doesn't seem to need this flag set here, but set it anyways just in case. m_noLineBreaks = true; Output(OperatorString(node.OperatorToken)); MarkSegment(node, null, node.OperatorContext); m_startOfStatement = false; } else { if (node.OperatorInConditionalCompilationComment) { // if we haven't output a cc_on yet, we ALWAYS want to do it now, whether or not the // sources had one. Otherwise, we only only want to output one if we had one and we aren't // removing unneccesary ones. if (!m_outputCCOn || (node.ConditionalCommentContainsOn && !m_settings.IsModificationAllowed(JsTreeModifications.RemoveUnnecessaryCCOnStatements))) { // output it now and set the flag that we have output them Output("/*@cc_on"); m_outputCCOn = true; } else { Output("/*@"); } Output(OperatorString(node.OperatorToken)); MarkSegment(node, null, node.OperatorContext); SetContextOutputPosition(node.Context); Output("@*/"); } else { Output(OperatorString(node.OperatorToken)); MarkSegment(node, null, node.OperatorContext ?? node.Context); SetContextOutputPosition(node.Context); } m_startOfStatement = false; if (node.Operand != null) { AcceptNodeWithParens(node.Operand, node.Operand.Precedence < node.Precedence); } } m_noIn = isNoIn; EndSymbol(symbol); } }
private void DoUnaryNode(JsUnaryOperator node) { if (!node.OperatorInConditionalCompilationComment && m_parser.Settings.IsModificationAllowed(JsTreeModifications.EvaluateNumericExpressions)) { var literalOperand = node.Operand as JsConstantWrapper; switch(node.OperatorToken) { case JsToken.Void: // see if our operand is a ConstantWrapper if (literalOperand != null) { // either number, string, boolean, or null. // the void operator evaluates its operand and returns undefined. Since evaluating a literal // does nothing, then it doesn't matter what the heck it is. Replace it with a zero -- a one- // character literal. node.Operand = new JsConstantWrapper(0, JsPrimitiveType.Number, node.Context, m_parser); } break; case JsToken.TypeOf: if (literalOperand != null) { // either number, string, boolean, or null. // the operand is a literal. Therefore we already know what the typeof // operator will return. Just short-circuit that behavior now and replace the operator // with a string literal of the proper value string typeName = null; if (literalOperand.IsStringLiteral) { // "string" typeName = "string"; } else if (literalOperand.IsNumericLiteral) { // "number" typeName = "number"; } else if (literalOperand.IsBooleanLiteral) { // "boolean" typeName = "boolean"; } else if (literalOperand.Value == null) { // "object" typeName = "object"; } if (!string.IsNullOrEmpty(typeName)) { ReplaceNodeWithLiteral(node, new JsConstantWrapper(typeName, JsPrimitiveType.String, node.Context, m_parser)); } } else if (node.Operand is JsObjectLiteral) { ReplaceNodeWithLiteral(node, new JsConstantWrapper("object", JsPrimitiveType.String, node.Context, m_parser)); } break; case JsToken.Plus: if (literalOperand != null) { try { // replace with a constant representing operand.ToNumber, ReplaceNodeWithLiteral(node, new JsConstantWrapper(literalOperand.ToNumber(), JsPrimitiveType.Number, node.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 } } break; case JsToken.Minus: if (literalOperand != null) { try { // replace with a constant representing the negative of operand.ToNumber ReplaceNodeWithLiteral(node, new JsConstantWrapper(-literalOperand.ToNumber(), JsPrimitiveType.Number, node.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 } } break; case JsToken.BitwiseNot: if (literalOperand != null) { try { // replace with a constant representing the bitwise-not of operant.ToInt32 ReplaceNodeWithLiteral(node, new JsConstantWrapper(Convert.ToDouble(~literalOperand.ToInt32()), JsPrimitiveType.Number, node.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 } } break; case JsToken.LogicalNot: if (literalOperand != null) { // replace with a constant representing the opposite of operand.ToBoolean try { ReplaceNodeWithLiteral(node, new JsConstantWrapper(!literalOperand.ToBoolean(), JsPrimitiveType.Boolean, node.Context, m_parser)); } catch (InvalidCastException) { // ignore any invalid cast exceptions } } break; } } }
private void DoBinaryOperator(JsBinaryOperator node) { if (m_parser.Settings.EvalLiteralExpressions) { // if this is an assign operator, an in, or an instanceof, then we won't // try to evaluate it if (!node.IsAssign && node.OperatorToken != JsToken.In && node.OperatorToken != JsToken.InstanceOf) { if (node.OperatorToken == JsToken.StrictEqual || node.OperatorToken == JsToken.StrictNotEqual) { // the operator is a strict equality (or not-equal). // check the primitive types of the two operands -- if they are known but not the same, we can // shortcut the whole process by just replacing this node with a boolean literal. var leftType = node.Operand1.FindPrimitiveType(); if (leftType != JsPrimitiveType.Other) { var rightType = node.Operand2.FindPrimitiveType(); if (rightType != JsPrimitiveType.Other) { // both sides are known if (leftType != rightType) { // they are not the same type -- replace with a boolean and bail ReplaceNodeWithLiteral( node, new JsConstantWrapper(node.OperatorToken == JsToken.StrictEqual ? false : true, JsPrimitiveType.Boolean, node.Context, m_parser)); return; } // they are the same type -- we can change the operator to simple equality/not equality node.OperatorToken = node.OperatorToken == JsToken.StrictEqual ? JsToken.Equal : JsToken.NotEqual; } } } // see if the left operand is a literal number, boolean, string, or null JsConstantWrapper left = node.Operand1 as JsConstantWrapper; if (left != null) { if (node.OperatorToken == JsToken.Comma) { // the comma operator evaluates the left, then evaluates the right and returns it. // but if the left is a literal, evaluating it doesn't DO anything, so we can replace the // entire operation with the right-hand operand JsConstantWrapper rightConstant = node.Operand2 as JsConstantWrapper; if (rightConstant != null) { // we'll replace the operator with the right-hand operand, BUT it's a constant, too. // first check to see if replacing this node with a constant will result in creating // a member-bracket operator that can be turned into a member-dot. If it is, then that // method will handle the replacement. But if it doesn't, then we should just replace // the comma with the right-hand operand. if (!ReplaceMemberBracketWithDot(node, rightConstant)) { ReplaceNodeWithLiteral(node, rightConstant); } } else if (node is JsCommaOperator) { // this is a collection of expression statements that we've joined together as // an extended comma operator. var list = node.Operand2 as JsAstNodeList; if (list == null) { // not a list, just a single item, so we can just // replace this entire node with the one element ReplaceNodeCheckParens(node, node.Operand2); } else if (list.Count == 1) { // If the list has a single element, then we can just // replace this entire node with the one element ReplaceNodeCheckParens(node, list[0]); } else if (list.Count == 0) { // the recursion ended up emptying the list, so we can just delete // this node altogether ReplaceNodeCheckParens(node, null); } else { // more than one item in the list // move the first item from the list to the left-hand side var firstItem = list[0]; list.RemoveAt(0); node.Operand1 = firstItem; // if there's only one item left in the list, we can get rid of the // extra list node and make it just the remaining node if (list.Count == 1) { firstItem = list[0]; list.RemoveAt(0); node.Operand2 = firstItem; } } } else { // replace the comma operator with the right-hand operand ReplaceNodeCheckParens(node, node.Operand2); } } else { // see if the right operand is a literal number, boolean, string, or null JsConstantWrapper right = node.Operand2 as JsConstantWrapper; if (right != null) { // then they are both constants and we can evaluate the operation EvalThisOperator(node, left, right); } else { // see if the right is a binary operator that can be combined with ours JsBinaryOperator rightBinary = node.Operand2 as JsBinaryOperator; if (rightBinary != null) { JsConstantWrapper rightLeft = rightBinary.Operand1 as JsConstantWrapper; if (rightLeft != null) { // eval our left and the right-hand binary's left and put the combined operation as // the child of the right-hand binary EvalToTheRight(node, left, rightLeft, rightBinary); } else { JsConstantWrapper rightRight = rightBinary.Operand2 as JsConstantWrapper; if (rightRight != null) { EvalFarToTheRight(node, left, rightRight, rightBinary); } } } } } } else { // left is not a constantwrapper. See if the right is JsConstantWrapper right = node.Operand2 as JsConstantWrapper; if (right != null) { // the right is a constant. See if the the left is a binary operator... JsBinaryOperator leftBinary = node.Operand1 as JsBinaryOperator; if (leftBinary != null) { // ...with a constant on the right, and the operators can be combined JsConstantWrapper leftRight = leftBinary.Operand2 as JsConstantWrapper; if (leftRight != null) { EvalToTheLeft(node, right, leftRight, leftBinary); } else { JsConstantWrapper leftLeft = leftBinary.Operand1 as JsConstantWrapper; if (leftLeft != null) { EvalFarToTheLeft(node, right, leftLeft, leftBinary); } } } else if (m_parser.Settings.IsModificationAllowed(JsTreeModifications.SimplifyStringToNumericConversion)) { // see if it's a lookup and this is a minus operation and the constant is a zero JsLookup lookup = node.Operand1 as JsLookup; if (lookup != null && node.OperatorToken == JsToken.Minus && right.IsIntegerLiteral && right.ToNumber() == 0) { // okay, so we have "lookup - 0" // this is done frequently to force a value to be numeric. // There is an easier way: apply the unary + operator to it. var unary = new JsUnaryOperator(node.Context, m_parser) { Operand = lookup, OperatorToken = JsToken.Plus }; ReplaceNodeCheckParens(node, unary); } } } // TODO: shouldn't we check if they BOTH are binary operators? (a*6)*(5*b) ==> a*30*b (for instance) } } } }
public override void Visit(JsUnaryOperator node) { if (node != null) { // depth-first base.Visit(node); DoUnaryNode(node); } }
public void Visit(JsUnaryOperator node) { // not applicable; terminate }
//--------------------------------------------------------------------------------------- // ParsePostfixExpression // // PostfixExpression: // LeftHandSideExpression | // LeftHandSideExpression '++' | // LeftHandSideExpression '--' // //--------------------------------------------------------------------------------------- private JsAstNode ParsePostfixExpression(JsAstNode ast, out bool isLeftHandSideExpr) { isLeftHandSideExpr = true; JsContext exprCtx = null; if (null != ast) { if (!m_foundEndOfLine) { if (JsToken.Increment == m_currentToken.Token) { isLeftHandSideExpr = false; exprCtx = ast.Context.Clone(); exprCtx.UpdateWith(m_currentToken); ast = new JsUnaryOperator(exprCtx, this) { Operand = ast, OperatorToken = m_currentToken.Token, OperatorContext = m_currentToken.Clone(), IsPostfix = true }; GetNextToken(); } else if (JsToken.Decrement == m_currentToken.Token) { isLeftHandSideExpr = false; exprCtx = ast.Context.Clone(); exprCtx.UpdateWith(m_currentToken); ast = new JsUnaryOperator(exprCtx, this) { Operand = ast, OperatorToken = m_currentToken.Token, OperatorContext = m_currentToken.Clone(), IsPostfix = true }; GetNextToken(); } } } return ast; }
public override void Visit(JsUnaryOperator node) { if (node != null) { base.Visit(node); // 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 JsLookup) { node.Context.HandleError(JsError.StrictModeInvalidDelete, true); } } } else if (node.OperatorToken == JsToken.Increment || node.OperatorToken == JsToken.Decrement) { var lookup = node.Operand as JsLookup; 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 == JsFieldType.UndefinedGlobal || lookup.VariableField.FieldType == JsFieldType.Arguments || (lookup.VariableField.FieldType == JsFieldType.Predefined && string.CompareOrdinal(lookup.Name, "eval") == 0)) { node.Operand.Context.HandleError(JsError.StrictModeInvalidPreOrPost, true); } } } } else { // if the operand is a numeric literal JsConstantWrapper constantWrapper = node.Operand as JsConstantWrapper; 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(JsTreeModifications.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(JsTreeModifications.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(); } } } } } }