public static void Apply(Block block, JSParser parser) { // create a new instance of the visitor and apply it to the block var visitor = new ReorderScopeVisitor(parser); block.Accept(visitor); // get the first insertion point. Make sure that we skip over any comments and directive prologues. // we do NOT want to insert anything between the start of the scope and any directive prologues. int insertAt = 0; while (insertAt < block.Count && (block[insertAt].IsDirectivePrologue || block[insertAt] is ImportantComment)) { ++insertAt; } // first, we want to move all function declarations to the top of this block if (visitor.m_functionDeclarations != null) { foreach (var funcDecl in visitor.m_functionDeclarations) { insertAt = RelocateFunction(block, insertAt, funcDecl); } } // special case: if there is only one var statement in the entire scope, // then just leave it alone because we will only add bytes by moving it around, // or be byte-neutral at best (no initializers and not in a for-statement). if (visitor.m_varStatements != null && visitor.m_varStatements.Count > 1) { // then we want to move all variable declarations after to the top (after the functions) foreach (var varStatement in visitor.m_varStatements) { insertAt = RelocateVar(block, insertAt, varStatement); } } // then we want to do the same thing for all child functions (declarations AND other) if (visitor.m_functionDeclarations != null) { foreach (var funcDecl in visitor.m_functionDeclarations) { Apply(funcDecl.Body, parser); } } if (visitor.m_functionExpressions != null) { foreach (var funcExpr in visitor.m_functionExpressions) { Apply(funcExpr.Body, parser); } } }
public static void Apply(Block block, JSParser parser) { if (parser == null) { throw new ArgumentNullException("parser"); } if (block != null) { // create a new instance of the visitor and apply it to the block var visitor = new ReorderScopeVisitor(parser); block.Accept(visitor); // if there were any module directive prologues we need to promote, do them first var insertAt = 0; if (visitor.m_moduleDirectives != null) { foreach (var directivePrologue in visitor.m_moduleDirectives) { insertAt = RelocateDirectivePrologue(block, insertAt, directivePrologue); } } // Make sure that we skip over any remaining comments and directive prologues. // we do NOT want to insert anything between the start of the scope and any directive prologues. while (insertAt < block.Count && (block[insertAt] is DirectivePrologue || block[insertAt] is ImportantComment)) { ++insertAt; } // first, we want to move all function declarations to the top of this block if (visitor.m_functionDeclarations != null) { foreach (var funcDecl in visitor.m_functionDeclarations) { insertAt = RelocateFunction(block, insertAt, funcDecl); } } // special case: if there is only one var statement in the entire scope, // then just leave it alone because we will only add bytes by moving it around, // or be byte-neutral at best (no initializers and not in a for-statement). if (visitor.m_varStatements != null && visitor.m_varStatements.Count > 1) { // then we want to move all variable declarations after to the top (after the functions) foreach (var varStatement in visitor.m_varStatements) { insertAt = RelocateVar(block, insertAt, varStatement); } } // then we want to do the same thing for all child functions (declarations AND other) if (visitor.m_functionDeclarations != null) { foreach (var funcDecl in visitor.m_functionDeclarations) { Apply(funcDecl.Body, parser); } } if (visitor.m_functionExpressions != null) { foreach (var funcExpr in visitor.m_functionExpressions) { Apply(funcExpr.Body, parser); } } // and then recurse the modules if (visitor.m_moduleDeclarations != null) { foreach (var moduleDecl in visitor.m_moduleDeclarations) { Apply(moduleDecl.Body, parser); } } } }
private static int RelocateVar(Block block, int insertAt, Var varStatement) { var forInParent = varStatement.Parent as ForIn; if (forInParent != null) { insertAt = ReorderScopeVisitor.RelocateForInVar(block, insertAt, varStatement, forInParent); } else { // if the var statement is at the next position to insert, then we don't need // to do anything. if (block[insertAt] != varStatement) { // check to see if the current position is a var and we are the NEXT statement. // if that's the case, we don't need to break out the initializer, just append all the // vardecls as-is to the current position. ForNode forNode; var existingVar = block[insertAt] as Var; if (existingVar != null && block[insertAt + 1] == varStatement) { // two var-s one right after the other. // just append our vardecls to the insertion point, then delete our statement existingVar.Append(varStatement); block.RemoveAt(insertAt + 1); } else if (existingVar != null && (forNode = varStatement.Parent as ForNode) != null && forNode.Initializer == varStatement && forNode == block[insertAt + 1]) { // this var statement is the initializer of a for-statement, which is // immediately after the var we would insert our vardecls into. // rather than moving our vardecls into the outside var-statement, let's // move the outside var-statement into our for-statement. varStatement.InsertAt(0, existingVar); block.RemoveAt(insertAt); } else { // iterate through the decls and count how many have initializers var initializerCount = 0; for (var ndx = 0; ndx < varStatement.Count; ++ndx) { // count the number of vardecls with initializers if (varStatement[ndx].Initializer != null) { ++initializerCount; } } // if there are more than two decls with initializers, then we won't actually // be gaining anything by moving the var to the top. We'll get rid of the four // bytes for the "var ", but we'll be adding two bytes for the name and comma // because name=init will still need to remain behind. if (initializerCount <= 2) { // first iterate through all the declarations in the var statement, // constructing an expression statement that is made up of assignment // operators for each of the declarations that have initializers (if any) // and removing all the initializers var assignments = new List <AstNode>(); for (var ndx = 0; ndx < varStatement.Count; ++ndx) { var varDecl = varStatement[ndx]; if (varDecl.Initializer != null) { // hold on to the object so we don't lose it to the GC var initializer = varDecl.Initializer; // remove it from the vardecl varDecl.Initializer = null; var reference = BindingTransform.FromBinding(varDecl.Binding); if (varDecl.IsCCSpecialCase) { // we don't want to add the special-case to the binary operator class, // so just create a copy of the vardecl for this location, using a reference // for the "binding" assignments.Add(new VariableDeclaration(varDecl.Context) { Binding = reference, AssignContext = varDecl.AssignContext, Initializer = initializer, IsCCSpecialCase = true, UseCCOn = varDecl.UseCCOn, TerminatingContext = varDecl.TerminatingContext }); } else { assignments.Add(new BinaryOperator(varDecl.Context) { Operand1 = reference, Operand2 = initializer, OperatorToken = JSToken.Assign, OperatorContext = varDecl.AssignContext }); } } // if the vardecl we are moving isn't a binding pattern, we need to // break it down into a simple list of names. if (!(varDecl.Binding is BindingIdentifier)) { // place the original vardecl with the first one var first = true; foreach (var declName in BindingsVisitor.Bindings(varDecl.Binding)) { if (first) { varStatement[ndx] = new VariableDeclaration(declName.Context) { Binding = new BindingIdentifier(declName.Context) { Name = declName.Name, VariableField = declName.VariableField } }; first = false; } else { // otherwise we want to insert a new one at the current position + 1 varStatement.InsertAt(++ndx, new VariableDeclaration(declName.Context) { Binding = new BindingIdentifier(declName.Context) { Name = declName.Name, VariableField = declName.VariableField } }); } } } } // now if there were any initializers... if (assignments.Count > 0) { // we want to create one big expression from all the assignments and replace the // var statement with the assignment(s) expression. Start at position n=1 and create // a binary operator of n-1 as the left, n as the right, and using a comma operator. var expression = assignments[0]; for (var ndx = 1; ndx < assignments.Count; ++ndx) { expression = CommaOperator.CombineWithComma(expression.Context.FlattenToStart(), expression, assignments[ndx]); } // replace the var with the expression. // we still have a pointer to the var, so we can insert it back into the proper // place next. varStatement.Parent.ReplaceChild(varStatement, expression); } else { // no initializers. // just remove the var statement altogether varStatement.Parent.ReplaceChild(varStatement, null); } // if the statement at the insertion point is a var-statement already, // then we just need to append our vardecls to it. Otherwise we'll insert our // var statement at the right point if (existingVar != null) { // append the varstatement we want to move to the existing var, which will // transfer all the vardecls to it. existingVar.Append(varStatement); } else { // move the var to the insert point, incrementing the position or next time block.Insert(insertAt, varStatement); } } } } } return(insertAt); }