public static bool Apply(TextWriter writer, JsAstNode node) { if (node != null) { var visitor = new JsonOutputVisitor(writer); node.Accept(visitor); return(visitor.IsValid); } return(false); }
public override bool IsEquivalentTo(JsAstNode otherNode) { // a binary operator is equivalent to another binary operator if the operator is the same and // both operands are also equivalent var otherBinary = otherNode as JsBinaryOperator; return(otherBinary != null && OperatorToken == otherBinary.OperatorToken && Operand1.IsEquivalentTo(otherBinary.Operand1) && Operand2.IsEquivalentTo(otherBinary.Operand2)); }
public override bool IsEquivalentTo(JsAstNode otherNode) { // a call node is equivalent to another call node if the function and the arguments // are all equivalent (and be sure to check for brackets and constructor) var otherCall = otherNode as JsCallNode; return(otherCall != null && this.InBrackets == otherCall.InBrackets && this.IsConstructor == otherCall.IsConstructor && this.Function.IsEquivalentTo(otherCall.Function) && this.Arguments.IsEquivalentTo(otherCall.Arguments)); }
public override bool ReplaceChild(JsAstNode oldNode, JsAstNode newNode) { if (oldNode == m_properties) { var properties = newNode as JsAstNodeList; if (newNode == null || properties != null) { Properties = properties; } } return(false); }
public override bool ReplaceChild(JsAstNode oldNode, JsAstNode newNode) { if (WithObject == oldNode) { WithObject = newNode; return(true); } if (Body == oldNode) { Body = ForceToBlock(newNode); return(true); } return(false); }
public override bool ReplaceChild(JsAstNode oldNode, JsAstNode newNode) { if (Operand1 == oldNode) { Operand1 = newNode; return(true); } if (Operand2 == oldNode) { Operand2 = newNode; return(true); } return(false); }
private static bool IsIterativeReference(JsAstNode initializer, IJsNameReference reference) { // we only care about array and regular expressions with the global switch at this point. // if it's not one of those types, then go ahead and assume iterative reference doesn't matter. var regExp = initializer as JsRegExpLiteral; if (initializer is JsArrayLiteral || initializer is JsObjectLiteral || (regExp != null && regExp.PatternSwitches != null && regExp.PatternSwitches.IndexOf("g", StringComparison.OrdinalIgnoreCase) >= 0)) { // get the parent block for the initializer. We'll use this as a stopping point in our loop. var parentBlock = GetParentBlock(initializer); // walk up the parent chain from the reference. If we find a while, a for, or a do-while, // then we know this reference is iteratively called. // stop when the parent is null, the same block containing the initializer, or a function object. // (because a function object will step out of scope, and we know we should be in the same scope) var child = reference as JsAstNode; var parent = child.Parent; while (parent != null && parent != parentBlock && !(parent is JsFunctionObject)) { // while or do-while is iterative -- the condition and the body are both called repeatedly. if (parent is JsWhileNode || parent is JsDoWhile) { return(true); } // for-statements call the condition, the incrementer, and the body repeatedly, but not the // initializer. var forNode = parent as JsForNode; if (forNode != null && child != forNode.Initializer) { return(true); } // in forin-statements, only the body is repeated, the collection is evaluated only once. var forInStatement = parent as JsForIn; if (forInStatement != null && child == forInStatement.Body) { return(true); } // go up child = parent; parent = parent.Parent; } } return(false); }
public override bool ReplaceChild(JsAstNode oldNode, JsAstNode newNode) { if (Body == oldNode) { Body = ForceToBlock(newNode); return(true); } if (Condition == oldNode) { Condition = newNode; return(true); } return(false); }
public static JsBlock ForceToBlock(JsAstNode node) { // if the node is null or already a block, then we're // good to go -- just return it. var block = node as JsBlock; if (block == null && node != null) { // it's not a block, so create a new block, append the astnode // and return the block block = new JsBlock(node.Context.Clone(), node.Parser); block.Append(node); } return(block); }
/// <summary> /// Insert a new node into the given position index within the block /// </summary> /// <param name="position">zero-based index into which the new node will be inserted</param> /// <param name="item">new node to insert into the block</param> public void Insert(int position, JsAstNode item) { if (item != null) { var block = item as JsBlock; if (block != null) { InsertRange(position, block.Children); } else { item.Parent = this; m_list.Insert(position, item); } } }
public void MarkSegment(JsAstNode node, int startLine, int startColumn, string name, JsContext context) { if (startLine == int.MaxValue) { throw new ArgumentOutOfRangeException("startLine"); } // add the offsets startLine += m_lineOffset; startColumn += m_columnOffset; // if we have a name, try adding it to the hash set of names. If it already exists, the call to Add // will return false. If it doesn't already exist, Add will return true and we'll append it to the list // of names. That way we have a nice list of names ordered by their first occurrence in the minified file. if (!string.IsNullOrEmpty(name) && m_names.Add(name)) { m_nameList.Add(name); } // if this is a newline, the startline will be bigger than the largest line we've had so far m_maxMinifiedLine = Math.Max(m_maxMinifiedLine, startLine); // save the file context in our list of files if (context != null && context.Document != null && context.Document.FileContext != null) { // if this is the first instance of this file... if (m_sourceFiles.Add(context.Document.FileContext)) { // ...add it to the list, so we end up with a list of unique files // sorted by their first occurence in the minified file. m_sourceFileList.Add(MakeRelative(context.Document.FileContext, m_mapPath)); } } // create the segment object and add it to the list. // the destination line/col numbers are zero-based. The format expects line to be 1-based and col 0-based. // the context line is one-based; col is zero-based. The format expects both to be zero-based. var segment = CreateSegment( startLine + 1, startColumn, context == null || context.StartLineNumber < 1 ? -1 : context.StartLineNumber - 1, context == null || context.StartColumn < 0 ? -1 : context.StartColumn, context.IfNotNull(c => MakeRelative(c.Document.FileContext, m_mapPath)), name); m_segments.Add(segment); }
private void TypicalHandler(JsAstNode node) { if (node != null) { // don't need to recurse -- to logical-not this, we just need to apply // the logical-not operator to it, which will add a character if (m_measure) { // measure ++m_delta; } else { // simple convert WrapWithLogicalNot(node); } } }
/// <summary> /// Return the first Block node in the tree starting from the given node and working up through the parent nodes. /// </summary> /// <param name="node">initial node</param> /// <returns>first block node in the node tree</returns> private static JsBlock GetParentBlock(JsAstNode node) { while (node != null) { // see if the current node is a block, and if so, return it. var block = node as JsBlock; if (block != null) { return(block); } // try the parent node = node.Parent; } // if we get here, we never found a parent block. return(null); }
public override bool IsEquivalentTo(JsAstNode otherNode) { JsVariableField otherField = null; JsLookup otherLookup; var otherVarDecl = otherNode as JsVariableDeclaration; if (otherVarDecl != null) { otherField = otherVarDecl.VariableField; } else if ((otherLookup = otherNode as JsLookup) != null) { otherField = otherLookup.VariableField; } // if we get here, we're not equivalent return(this.VariableField != null && this.VariableField.IsSameField(otherField)); }
public override bool ReplaceChild(JsAstNode oldNode, JsAstNode newNode) { if (CaseValue == oldNode) { CaseValue = newNode; return(true); } if (Statements == oldNode) { var newBlock = newNode as JsBlock; if (newNode == null || newBlock != null) { Statements = newBlock; return(true); } } return(false); }
public override bool ReplaceChild(JsAstNode oldNode, JsAstNode newNode) { if (Body == oldNode) { Body = ForceToBlock(newNode); return(true); } else if (ParameterDeclarations == oldNode) { var newList = newNode as JsAstNodeList; if (newNode == null || newList != null) { ParameterDeclarations = newList; return(true); } } return(false); }
private bool CanBeBroken(JsAstNode node) { JsAstNodeList nodeList; if (!m_statementStart.IsSafe(node)) { // don't break if the next statement is a function or an object literal return(false); } else if ((nodeList = node as JsAstNodeList) != null) { // if there aren't any operands in the list, we can break this, // otherwise check to see if the first item can be broken. return(nodeList.Count == 0 || CanBeBroken(nodeList[0])); } // if we get here, it's okay to break this operation into separate statements return(true); }
public override bool ReplaceChild(JsAstNode oldNode, JsAstNode newNode) { if (Condition == oldNode) { Condition = newNode; return(true); } if (TrueExpression == oldNode) { TrueExpression = newNode; return(true); } if (FalseExpression == oldNode) { FalseExpression = newNode; return(true); } return(false); }
public override bool ReplaceChild(JsAstNode oldNode, JsAstNode newNode) { if (Name == oldNode) { var objectField = newNode as JsObjectLiteralField; if (newNode == null || objectField != null) { Name = objectField; } return(true); } if (Value == oldNode) { Value = newNode; return(true); } return(false); }
public override bool ReplaceChild(JsAstNode oldNode, JsAstNode newNode) { if (Expression == oldNode) { Expression = newNode; return(true); } if (Cases == oldNode) { JsAstNodeList newList = newNode as JsAstNodeList; if (newNode == null || newList != null) { // remove it Cases = newList; return(true); } } return(false); }
public override bool ReplaceChild(JsAstNode oldNode, JsAstNode newNode) { for (int ndx = 0; ndx < m_list.Count; ++ndx) { if (m_list[ndx] == oldNode) { // if the new node isn't a variabledeclaration, ignore the call JsVariableDeclaration newDecl = newNode as JsVariableDeclaration; if (newNode == null || newDecl != null) { m_list[ndx].IfNotNull(n => n.Parent = (n.Parent == this) ? null : n.Parent); m_list[ndx] = newDecl; m_list[ndx].IfNotNull(n => n.Parent = this); return(true); } break; } } return(false); }
/// <summary> /// Insert the given statement node after an existing node in the block. /// </summary> /// <param name="after">exisitng child node of the block</param> /// <param name="item">node to insert after the existing node</param> public void InsertAfter(JsAstNode after, JsAstNode item) { if (item != null) { int index = m_list.IndexOf(after); if (index >= 0) { var block = item as JsBlock; if (block != null) { // don't insert a block into a block -- insert the new block's // children instead (don't want nested blocks) InsertRange(index + 1, block.Children); } else { item.Parent = this; m_list.Insert(index + 1, item); } } } }
public void MarkSegment(JsAstNode node, int startLine, int startColumn, string name, JsContext context) { if (node == null || string.IsNullOrEmpty(name)) { return; } // see if this is within a function object node, // AND if this segment has the same name as the function name // AND this context isn't the same as the entire function context. // this should only be true for the function NAME segment. var functionObject = node as JsFunctionObject; if (functionObject != null && string.CompareOrdinal(name, functionObject.Name) == 0 && context != functionObject.Context) { // adjust the offsets startLine += m_lineOffset; startColumn += m_columnOffset; // it does -- so this is the segment that corresponds to the function object's name, which // for this format we want to output a separate segment for. It used to be its own Lookup // node child of the function object, so we need to create a fake node here, start a new // symbol from it, end the symbol, then write it. var fakeLookup = new JsLookup(context, functionObject.Parser) { Name = name }; var nameSymbol = JavaScriptSymbol.StartNew(fakeLookup, startLine, startColumn, GetSourceFileIndex(functionObject.Context.Document.FileContext)); // the name will never end on a different line -- it's a single unbreakable token. The length is just // the length of the name, so add that number to the column start. And the parent context is the function // name (again) nameSymbol.End(startLine, startColumn + name.Length, name); nameSymbol.WriteTo(m_writer); } }
public override bool IsEquivalentTo(JsAstNode otherNode) { // this one is tricky. If we have a field assigned, then we are equivalent if the // field is the same as the other one. If there is no field, then just check the name var otherLookup = otherNode as JsLookup; if (otherLookup != null) { if (VariableField != null) { // the variable fields should be the same return(VariableField.IsSameField(otherLookup.VariableField)); } else { // otherwise the names should be identical return(string.CompareOrdinal(Name, otherLookup.Name) == 0); } } // if we get here, we're not equivalent return(false); }
public bool Match(JsAstNode node, string identifiers) { // set the match to false m_isMatch = false; // identifiers cannot be null or blank and must match: IDENT(.IDENT)* // since for JS there has to be at least a global object, the dot must be AFTER the first character. if (node != null && !string.IsNullOrEmpty(identifiers)) { // get all the parts var parts = identifiers.Split('.'); // each part must be a valid JavaScript identifier. Assume everything is valid // unless at least one is invalid -- then forget it var isValid = true; foreach (var part in parts) { if (!JsScanner.IsValidIdentifier(part)) { isValid = false; break; } } // must be valid to continue if (isValid) { // save the parts and start the index on the last one, since we'll be walking backwards m_parts = parts; m_index = parts.Length - 1; node.Accept(this); } } return(m_isMatch); }
private static bool MatchesMemberChain(JsAstNode parent, string lookup, int startIndex) { // get the NEXT period var period = lookup.IndexOf('.', startIndex); // loop until we run out of periods while (period > 0) { // if the parent isn't a member, or if the name of the parent doesn't match // the current identifier in the chain, then we're no match and can bail if (!MatchMemberName(parent, lookup, startIndex, period)) { return(false); } // next parent, next segment, and find the next period parent = parent.Parent; startIndex = period + 1; period = lookup.IndexOf('.', startIndex); } // now check the last segment, from start to the end of the string return(MatchMemberName(parent, lookup, startIndex, lookup.Length)); }
/// <summary> /// Replace the existing direct child node of the block with a new node. /// </summary> /// <param name="oldNode">existing statement node to replace.</param> /// <param name="newNode">node with which to replace the existing node.</param> /// <returns>true if the replacement was a succeess; false otherwise</returns> public override bool ReplaceChild(JsAstNode oldNode, JsAstNode newNode) { for (int ndx = m_list.Count - 1; ndx >= 0; --ndx) { if (m_list[ndx] == oldNode) { m_list[ndx].IfNotNull(n => n.Parent = (n.Parent == this) ? null : n.Parent); if (newNode == null) { // just remove it m_list.RemoveAt(ndx); } else { JsBlock newBlock = newNode as JsBlock; if (newBlock != null) { // the new "statement" is a block. That means we need to insert all // the statements from the new block at the location of the old item. m_list.RemoveAt(ndx); InsertRange(ndx, newBlock.m_list); } else { // not a block -- slap it in there m_list[ndx] = newNode; newNode.Parent = this; } } return(true); } } return(false); }
public override bool ReplaceChild(JsAstNode oldNode, JsAstNode newNode) { if (TryBlock == oldNode) { TryBlock = ForceToBlock(newNode); return(true); } if (CatchParameter == oldNode) { CatchParameter = newNode as JsParameterDeclaration; return(true); } if (CatchBlock == oldNode) { CatchBlock = ForceToBlock(newNode); return(true); } if (FinallyBlock == oldNode) { FinallyBlock = ForceToBlock(newNode); return(true); } return(false); }
public static JavaScriptSymbol StartNew(JsAstNode node, int startLine, int startColumn, int sourceFileId) { if (startLine == int.MaxValue) { throw new ArgumentOutOfRangeException("startLine"); } if (startColumn == int.MaxValue) { throw new ArgumentOutOfRangeException("startColumn"); } return(new JavaScriptSymbol { // destination line/col number are fed to us as zero-based, so add one to get to // the one-based values we desire. Context objects store the source line/col as // one-based already. m_startLine = startLine + 1, m_startColumn = startColumn + 1, m_sourceContext = node != null ? node.Context : null, m_symbolType = node != null?node.GetType().Name : "[UNKNOWN]", m_sourceFileId = sourceFileId, }); }
public static void Apply(JsAstNode node, JsActivationObject scope, JsSettings settings) { if (node != null && scope != null) { // create the visitor and run it. This will create all the child // scopes and populate all the scopes with the var-decl, lex-decl, // and lookup references within them. var visitor = new JsResolutionVisitor(scope, settings); node.Accept(visitor); // now that all the scopes are created and they all know what decls // they contains, create all the fields CreateFields(scope); // now that all the fields have been created in all the scopes, // let's go through and resolve all the references ResolveLookups(scope, settings); // now that everything is declared and resolved as per the language specs, // we need to go back and add ghosted fields for older versions of IE that // incorrectly implement catch-variables and named function expressions. AddGhostedFields(scope); } }