public override void Visit(Var node) { if (node != null) { // don't bother creating a list of var-statements if we're not going to move them if (m_moveVarStatements) { if (m_varStatements == null) { m_varStatements = new List<Var>(); } // add the node to the list of variable declarations m_varStatements.Add(node); } // and recurse base.Visit(node); } }
private static int RelocateVar(Block block, int insertAt, Var varStatement) { // if the var statement is at the next position to insert, then we don't need // to do anything. if (block[insertAt] != varStatement && !InsideConditionalComment(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. var existingVar = block[insertAt] as Var; if (existingVar != null && block[insertAt + 1] == varStatement) { // just append our vardecls to the insertion point, then delete our statement existingVar.Append(varStatement); block.RemoveAt(insertAt + 1); } else { // iterate through the decls and count how many have initializers var initializerCount = 0; for (var ndx = 0; ndx < varStatement.Count; ++ndx) { 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) { if (varDecl.IsCCSpecialCase) { // create a vardecl with the same name and no initializer var copyDecl = new VariableDeclaration( varDecl.Context, varDecl.Parser, varDecl.Identifier, varDecl.Field.OriginalContext, null, 0, true); // replace the special vardecl with the copy varStatement.ReplaceChild(varDecl, copyDecl); // add the original vardecl to the list of "assignments" assignments.Add(varDecl); } else { // hold on to the object so we don't lose it to the GC var initializer = varDecl.Initializer; // remove it from the vardecl varDecl.ReplaceChild(initializer, null); // create an assignment operator for a lookup to the name // as the left, and the initializer as the right, and add it to the list assignments.Add(new BinaryOperator( varDecl.Context, varDecl.Parser, new Lookup(varDecl.Identifier, varDecl.Field.OriginalContext, varDecl.Parser), initializer, JSToken.Assign)); } } } // 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 = new BinaryOperator( null, expression.Parser, expression, assignments[ndx], JSToken.Comma); } // 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. // if the parent is a for-in statement... var forInParent = varStatement.Parent as ForIn; if (forInParent != null) { // we want to replace the var statement with a lookup for the var // there should be only one vardecl var varDecl = varStatement[0]; varStatement.Parent.ReplaceChild( varStatement, new Lookup(varDecl.Identifier, varDecl.Field.OriginalContext, varStatement.Parser)); } else { // 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; }
//--------------------------------------------------------------------------------------- // ParseForStatement // // ForStatement : // 'for' '(' OptionalExpressionNoIn ';' OptionalExpression ';' OptionalExpression ')' // 'for' '(' 'var' VariableDeclarationListNoIn ';' OptionalExpression ';' OptionalExpression ')' // 'for' '(' LeftHandSideExpression 'in' Expression')' // 'for' '(' 'var' Identifier OptionalInitializerNoIn 'in' Expression')' // // OptionalExpressionNoIn : // <empty> | // ExpressionNoIn // same as Expression but does not process 'in' as an operator // // OptionalInitializerNoIn : // <empty> | // InitializerNoIn // same as initializer but does not process 'in' as an operator //--------------------------------------------------------------------------------------- private AstNode ParseForStatement() { m_blockType.Add(BlockType.Loop); AstNode forNode = null; try { Context forCtx = m_currentToken.Clone(); GetNextToken(); if (JSToken.LeftParenthesis != m_currentToken.Token) ReportError(JSError.NoLeftParenthesis); GetNextToken(); bool isForIn = false, recoveryInForIn = false; AstNode lhs = null, initializer = null, condOrColl = null, increment = null; try { if (JSToken.Var == m_currentToken.Token) { isForIn = true; Var varList = new Var(m_currentToken.Clone(), this); varList.Append(ParseIdentifierInitializer(JSToken.In, (FieldAttributes)0)); // a list of variable initializers is allowed only in a for(;;) while (JSToken.Comma == m_currentToken.Token) { isForIn = false; varList.Append(ParseIdentifierInitializer(JSToken.In, (FieldAttributes)0)); //initializer = new Comma(initializer.context.CombineWith(var.context), initializer, var); } initializer = varList; // if it could still be a for..in, now it's time to get the 'in' if (isForIn) { if (JSToken.In == m_currentToken.Token) { GetNextToken(); condOrColl = ParseExpression(); } else isForIn = false; } } else { if (JSToken.Semicolon != m_currentToken.Token) { bool isLHS; initializer = ParseUnaryExpression(out isLHS, false); if (isLHS && JSToken.In == m_currentToken.Token) { isForIn = true; lhs = initializer; initializer = null; GetNextToken(); m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); try { condOrColl = ParseExpression(); } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1) { exc._partiallyComputedNode = null; throw; } else { if (exc._partiallyComputedNode == null) condOrColl = new ConstantWrapper(true, PrimitiveType.Boolean, CurrentPositionContext(), this); // what could we put here? else condOrColl = exc._partiallyComputedNode; } if (exc._token == JSToken.RightParenthesis) { GetNextToken(); recoveryInForIn = true; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); } } else initializer = ParseExpression(initializer, false, isLHS, JSToken.In); } } } catch (RecoveryTokenException exc) { // error is too early abort for exc._partiallyComputedNode = null; throw; } // at this point we know whether or not is a for..in if (isForIn) { if (!recoveryInForIn) { if (JSToken.RightParenthesis != m_currentToken.Token) ReportError(JSError.NoRightParenthesis); forCtx.UpdateWith(m_currentToken); GetNextToken(); } AstNode body = null; // if the statements aren't withing curly-braces, throw a possible error if (JSToken.LeftCurly != m_currentToken.Token) { ReportError(JSError.StatementBlockExpected, forCtx, true); } try { // parse a Statement, not a SourceElement body = ParseStatement(false); } catch (RecoveryTokenException exc) { if (exc._partiallyComputedNode == null) body = new Block(CurrentPositionContext(), this); else body = exc._partiallyComputedNode; exc._partiallyComputedNode = new ForIn(forCtx, this, (lhs != null ? lhs : initializer), condOrColl, body); throw; } // for (a in b) // lhs = a, initializer = null // for (var a in b) // lhs = null, initializer = var a forNode = new ForIn(forCtx, this, (lhs != null ? lhs : initializer), condOrColl, body); } else { m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); try { if (JSToken.Semicolon != m_currentToken.Token) { ReportError(JSError.NoSemicolon); if (JSToken.Colon == m_currentToken.Token) { m_noSkipTokenSet.Add(NoSkipTokenSet.s_VariableDeclNoSkipTokenSet); try { SkipTokensAndThrow(); } catch (RecoveryTokenException) { if (JSToken.Semicolon == m_currentToken.Token) m_errorToken = null; else throw; } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_VariableDeclNoSkipTokenSet); } } } GetNextToken(); if (JSToken.Semicolon != m_currentToken.Token) { condOrColl = ParseExpression(); if (JSToken.Semicolon != m_currentToken.Token) ReportError(JSError.NoSemicolon); } GetNextToken(); if (JSToken.RightParenthesis != m_currentToken.Token) increment = ParseExpression(); if (JSToken.RightParenthesis != m_currentToken.Token) ReportError(JSError.NoRightParenthesis); forCtx.UpdateWith(m_currentToken); GetNextToken(); } catch (RecoveryTokenException exc) { if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1) { exc._partiallyComputedNode = null; throw; } else { // discard any partial info, just genrate empty condition and increment and keep going exc._partiallyComputedNode = null; if (condOrColl == null) condOrColl = new ConstantWrapper(true, PrimitiveType.Boolean, CurrentPositionContext(), this); } if (exc._token == JSToken.RightParenthesis) { GetNextToken(); } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet); } AstNode body = null; // if the statements aren't withing curly-braces, throw a possible error if (JSToken.LeftCurly != m_currentToken.Token) { ReportError(JSError.StatementBlockExpected, forCtx, true); } try { // parse a Statement, not a SourceElement body = ParseStatement(false); } catch (RecoveryTokenException exc) { if (exc._partiallyComputedNode == null) body = new Block(CurrentPositionContext(), this); else body = exc._partiallyComputedNode; exc._partiallyComputedNode = new ForNode(forCtx, this, initializer, condOrColl, increment, body); throw; } forNode = new ForNode(forCtx, this, initializer, condOrColl, increment, body); } } finally { m_blockType.RemoveAt(m_blockType.Count - 1); } return forNode; }
//--------------------------------------------------------------------------------------- // ParseVariableStatement // // VariableStatement : // 'var' VariableDeclarationList // // VariableDeclarationList : // VariableDeclaration | // VariableDeclaration ',' VariableDeclarationList // // VariableDeclaration : // Identifier Initializer // // Initializer : // <empty> | // '=' AssignmentExpression //--------------------------------------------------------------------------------------- private AstNode ParseVariableStatement(FieldAttributes visibility) { Var varList = new Var(m_currentToken.Clone(), this); bool single = true; AstNode vdecl = null; AstNode identInit = null; for (; ; ) { m_noSkipTokenSet.Add(NoSkipTokenSet.s_EndOfLineToken); try { identInit = ParseIdentifierInitializer(JSToken.None, visibility); } catch (RecoveryTokenException exc) { // an exception is passing by, possibly bringing some info, save the info if any if (exc._partiallyComputedNode != null) { if (!single) { varList.Append(exc._partiallyComputedNode); varList.Context.UpdateWith(exc._partiallyComputedNode.Context); exc._partiallyComputedNode = varList; } } if (IndexOfToken(NoSkipTokenSet.s_EndOfLineToken, exc) == -1) throw; else { if (single) identInit = exc._partiallyComputedNode; } } finally { m_noSkipTokenSet.Remove(NoSkipTokenSet.s_EndOfLineToken); } if (identInit != null) { vdecl = identInit; varList.Append(vdecl); } if (JSToken.Semicolon == m_currentToken.Token || JSToken.RightCurly == m_currentToken.Token) { if (JSToken.Semicolon == m_currentToken.Token) { vdecl.Context.UpdateWith(m_currentToken); GetNextToken(); } break; } if (JSToken.Comma == m_currentToken.Token) { single = false; continue; } if (m_scanner.GotEndOfLine) { break; } // assume the variable statement was terminated and move on ReportError(JSError.NoSemicolon, true); break; } if (vdecl != null) { varList.Context.UpdateWith(vdecl.Context); } return varList; }
private static bool VarDeclExists(Var node, int ndx, string name) { // only need to look forward from the index passed for (; ndx < node.Count; ++ndx) { // string must be exact match if (string.CompareOrdinal(node[ndx].Identifier, name) == 0) { // there is at least one -- we can bail return true; } } // if we got here, we didn't find any matches return false; }
private static void DeleteNoInits(Var node, int min, string name) { // walk backwards from the end of the list down to (and including) the minimum index for (int ndx = node.Count - 1; ndx >= min; --ndx) { // if the name matches and there is no initializer... if (string.CompareOrdinal(node[ndx].Identifier, name) == 0 && node[ndx].Initializer == null) { // ...remove it from the list node.RemoveAt(ndx); } } }
private static bool AreAssignmentsInVar(BinaryOperator binaryOp, Var varStatement) { bool areAssignmentsInVar = false; if (binaryOp != null) { // we only want to pop positive for the simple assign (=). If it's any of the // complex assigns (+=, -=, etc) then we don't want to combine them. if (binaryOp.OperatorToken == JSToken.Assign) { // see if the left-hand side is a simple lookup Lookup lookup = binaryOp.Operand1 as Lookup; if (lookup != null) { // it is. see if that variable is in the previous var statement areAssignmentsInVar = varStatement.Contains(lookup.Name); } } else if (binaryOp.OperatorToken == JSToken.Comma) { // this is a comma operator, so we will return true only if both // left and right operators are assignments to vars defined in the // var statement areAssignmentsInVar = AreAssignmentsInVar(binaryOp.Operand1 as BinaryOperator, varStatement) && AreAssignmentsInVar(binaryOp.Operand2 as BinaryOperator, varStatement); } } return areAssignmentsInVar; }
public override void Visit(Var node) { if (node != null) { // first we want to weed out duplicates that don't have initializers // var a=1, a=2 is okay, but var a, a=2 and var a=2, a should both be just var a=2, // and var a, a should just be var a if (m_parser.Settings.IsModificationAllowed(TreeModifications.RemoveDuplicateVar)) { // first we want to weed out duplicates that don't have initializers // var a=1, a=2 is okay, but var a, a=2 and var a=2, a should both be just var a=2, // and var a, a should just be var a int ndx = 0; while (ndx < node.Count) { string thisName = node[ndx].Identifier; // handle differently if we have an initializer or not if (node[ndx].Initializer != null) { // the current vardecl has an initializer, so we want to delete any other // vardecls of the same name in the rest of the list with no initializer // and move on to the next item afterwards DeleteNoInits(node, ++ndx, thisName); } else { // this vardecl has no initializer, so we can delete it if there is ANY // other vardecl with the same name (whether or not it has an initializer) if (VarDeclExists(node, ndx + 1, thisName)) { node.RemoveAt(ndx); // don't increment the index; we just deleted the current item, // so the next item just slid into this position } else { // nope -- it's the only one. Move on to the next ++ndx; } } } } // recurse the analyze base.Visit(node); } }
internal void AddGeneratedVar(string name, AstNode initializer, bool isLiteral) { // if the body is empty, create one now if (Body == null) { Body = new Block(null, Parser); } // see if the first statement in the body (if any) is a var already Var var = null; if (Body.Count > 0) { var = Body[0] as Var; } VariableDeclaration varDecl = new VariableDeclaration( null, Parser, name, new Context(Parser), initializer, (isLiteral ? FieldAttributes.Literal : 0) ); varDecl.IsGenerated = true; // make sure we set the crunchability of this field to TRUE. Doesn't matter // whether it's a global or within a with-scope or what-have-you. It didn't // exist in the sources (we are generating it now) so we can rename it whatever // the heck we want. varDecl.Field.CanCrunch = true; if (var != null) { // the first statement is a var; just add a new declaration to the front var.InsertAt(0, varDecl); } else { // not a var; create a new one var = new Var(null, Parser); var.Append(varDecl); Body.Insert(0, var); } }