private static int RelocateFunction(JsBlock block, int insertAt, JsFunctionObject funcDecl) { if (block[insertAt] != funcDecl) { // technically function declarations can only be direct children of the program or a function block. // and since we are passing in such a block, the parent of the function declaration better be that // block. If it isn't, we don't want to move it because it's not in an allowed place, and different // browsers treat that situation differently. Some browsers would process such funcdecls as if // they were a direct child of the main block. Others will treat it like a function expression with // an external name, and only assign the function to the name if that line of code is actually // executed. So since there's a difference, just leave them as-is and only move valid funcdecls. if (funcDecl.Parent == block) { // remove the function from it's parent, which will take it away from where it is right now. funcDecl.Parent.ReplaceChild(funcDecl, null); // now insert it into the block at the new location, incrementing the location so the next function // will be inserted after it. It is important that they be in the same order as the source, or the semantics // will change when there are functions with the same name. block.Insert(insertAt++, funcDecl); } } else { // we're already in the right place. Just increment the pointer to move to the next position // for next time ++insertAt; } // return the new position return(insertAt); }
public void Visit(JsFunctionObject node) { // this shouldn't be called for anything but a function expression, // which is definitely NOT safe to start a statement off because it would // then be interpreted as a function *declaration*. Debug.Assert(node == null || node.FunctionType == JsFunctionType.Expression); m_isSafe = false; }
internal JsFunctionScope(JsActivationObject parent, bool isExpression, JsSettings settings, JsFunctionObject funcObj) : base(parent, settings) { m_refScopes = new HashSet<JsActivationObject>(); if (isExpression) { // parent scopes automatically reference enclosed function expressions AddReference(Parent); } FunctionObject = funcObj; }
internal JsFunctionScope(JsActivationObject parent, bool isExpression, JsSettings settings, JsFunctionObject funcObj) : base(parent, settings) { m_refScopes = new HashSet <JsActivationObject>(); if (isExpression) { // parent scopes automatically reference enclosed function expressions AddReference(Parent); } FunctionObject = funcObj; }
public override void Visit(JsFunctionObject node) { if (node != null) { // if we are reordering ANYTHING, then we need to do the reordering on a scope level. // so if that's the case, we need to create a list of all the child functions and NOT // recurse at this point. Then we'll reorder, then we'll use the lists to recurse. // BUT if we are not reordering anything, no sense making the lists and recursing later. // if that's the case, we can just recurse now and not have to worry about anything later. if (m_moveVarStatements || m_moveFunctionDecls) { // add the node to the appropriate list: either function expression or function declaration. // assume if it's not a function declaration, it must be an expression since the other types // are not declaration (getter, setter) and we want to treat declarations special. // if the conditional comment level isn't zero, then something funky is going on with // the conditional-compilation statements, and we don't want to move the declarations, so // don't add them to the declaration list. But we still want to recurse them, so add them // to the expression list (which get recursed but not moved). if (node.FunctionType == JsFunctionType.Declaration && m_conditionalCommentLevel == 0) { if (m_functionDeclarations == null) { m_functionDeclarations = new List <JsFunctionObject>(); } m_functionDeclarations.Add(node); } else { if (m_functionExpressions == null) { m_functionExpressions = new List <JsFunctionObject>(); } m_functionExpressions.Add(node); } // BUT DO NOT RECURSE!!!! // we only want the functions and variables in THIS scope, not child function scopes. //base.Visit(node); } else { // we're not reordering, so just recurse now to save the hassle base.Visit(node); } } }
private void UnreferencedFunction(JsVariableField variableField, JsFunctionObject functionObject) { // if there is no name, then ignore this declaration because it's malformed. // (won't be a function expression because those are automatically referenced). // also ignore ghosted function fields. if (functionObject.Name != null && variableField.FieldType != JsFieldType.GhostFunction) { // if the function name isn't a simple identifier, then leave it there and mark it as // not renamable because it's probably one of those darn IE-extension event handlers or something. if (JsScanner.IsValidIdentifier(functionObject.Name)) { // unreferenced function declaration. fire a warning. var ctx = functionObject.IdContext ?? variableField.OriginalContext; ctx.HandleError(JsError.FunctionNotReferenced, false); // hide it from the output if our settings say we can. // we don't want to delete it, per se, because we still want it to // show up in the scope report so the user can see that it was unreachable // in case they are wondering where it went. // ES6 has the notion of block-scoped function declarations. ES5 says functions can't // be defined inside blocks -- only at the root level of the global scope or function scopes. // so if this is a block scope, don't hide the function, even if it is unreferenced because // of the cross-browser difference. if (this.IsKnownAtCompileTime && m_settings.MinifyCode && m_settings.RemoveUnneededCode && !(this is JsBlockScope)) { functionObject.HideFromOutput = true; } } else { // not a valid identifier name for this function. Don't rename it because it's // malformed and we don't want to mess up the developer's intent. variableField.CanCrunch = false; } } }
public void Visit(JsFunctionObject node) { // not applicable; terminate }
public void Visit(JsFunctionObject node) { // invalid! ignore IsValid = false; }
public void Visit(JsFunctionObject node) { if (node != null) { // it's a declaration; put the index to -1. node.Index = -1; // create a function scope, assign it to the function object, // and push it on the stack var parentScope = CurrentLexicalScope; if (node.FunctionType == JsFunctionType.Expression && !string.IsNullOrEmpty(node.Name)) { // function expressions have an intermediate scope between the parent and the // function's scope that contains just the function name so the function can // be self-referencing without the function expression polluting the parent scope. // don't add the function name field yet, because it's not a decl per se. parentScope = new JsFunctionScope(parentScope, true, m_settings, node) { IsInWithScope = m_withDepth > 0 }; // add this function object to the list of function objects the variable scope // will need to ghost later CurrentVariableScope.GhostedFunctions.Add(node); } node.FunctionScope = new JsFunctionScope(parentScope, node.FunctionType != JsFunctionType.Declaration, m_settings, node) { IsInWithScope = m_withDepth > 0 }; m_lexicalStack.Push(node.FunctionScope); m_variableStack.Push(node.FunctionScope); var savedIndex = m_orderIndex; try { // recurse into the function to handle it after saving the current index and resetting it if (node.Body != null) { m_orderIndex = 0; node.Body.Accept(this); } } finally { Debug.Assert(CurrentLexicalScope == node.FunctionScope); m_lexicalStack.Pop(); m_variableStack.Pop(); m_orderIndex = savedIndex; } // nothing to add to the var-decl list. // but add the function name to the current lex-decl list // IF it is a declaration and it has a name (and it SHOULD unless there was an error) if (node.FunctionType == JsFunctionType.Declaration && !string.IsNullOrEmpty(node.Name)) { var lexicalScope = CurrentLexicalScope; lexicalScope.LexicallyDeclaredNames.Add(node); if (lexicalScope != CurrentVariableScope) { // the current lexical scope is the variable scope. // this is ES6 syntax: a function declaration inside a block scope. Not allowed // in ES5 code, so throw a warning and ghost this function in the outer variable scope // to make sure that we don't generate any naming collisions. node.NameContext.HandleError(JsError.MisplacedFunctionDeclaration, false); CurrentVariableScope.GhostedFunctions.Add(node); } } } }
private static void ResolveGhostedFunctions(JsActivationObject scope, JsFunctionObject funcObject) { var functionField = funcObject.VariableField; // let's check on ghosted names in the outer variable scope var ghostField = scope[funcObject.Name]; if (ghostField == null) { // nothing; good to go. Add a ghosted field to keep track of it. ghostField = new JsVariableField(JsFieldType.GhostFunction, funcObject.Name, 0, funcObject) { OriginalContext = functionField.OriginalContext, CanCrunch = funcObject.VariableField.IfNotNull(v => v.CanCrunch) }; scope.AddField(ghostField); } else if (ghostField.FieldType == JsFieldType.GhostFunction) { // there is, but it's another ghosted function expression. // what if a lookup is resolved to this field later? We probably still need to // at least flag it as ambiguous. We will only need to throw an error, though, // if someone actually references the outer ghost variable. ghostField.IsAmbiguous = true; } else { // something already exists. Could be a naming collision for IE or at least a // a cross-browser behavior difference if it's not coded properly. // mark this field as a function, even if it wasn't before ghostField.IsFunction = true; if (ghostField.OuterField != null) { // if the pre-existing field we are ghosting is a reference to // an OUTER field, then we actually have a problem that creates a BIG // difference between older IE browsers and everything else. // modern browsers will have the link to the outer field, but older // IE browsers will link to this function expression! // fire a cross-browser error warning ghostField.IsAmbiguous = true; funcObject.IdContext.HandleError(JsError.AmbiguousNamedFunctionExpression); } else if (ghostField.IsReferenced) { // if the ghosted field isn't even referenced, then who cares? // but it is referenced. Let's see if it matters. // something like: var nm = function nm() {} // is TOTALLY cool common cross-browser syntax. var parentVarDecl = funcObject.Parent as JsVariableDeclaration; if (parentVarDecl == null || parentVarDecl.Name != funcObject.Name) { // see if it's a simple assignment. // something like: var nm; nm = function nm(){}, // would also be cool, although less-common than the vardecl version. JsLookup lookup; var parentAssignment = funcObject.Parent as JsBinaryOperator; if (parentAssignment == null || parentAssignment.OperatorToken != JsToken.Assign || parentAssignment.Operand2 != funcObject || (lookup = parentAssignment.Operand1 as JsLookup) == null || lookup.Name != funcObject.Name) { // something else. Flag it as ambiguous. ghostField.IsAmbiguous = true; } } } } // link them so they all keep the same name going forward // (since they are named the same in the sources) functionField.OuterField = ghostField; // TODO: this really should be a LIST of ghosted fields, since multiple // elements can ghost to the same field. ghostField.GhostedField = functionField; // if the actual field has references, we want to bubble those up // since we're now linking those fields if (functionField.RefCount > 0) { // add the function's references to the ghost field ghostField.AddReferences(functionField.References); } }
private static int RelocateFunction(JsBlock block, int insertAt, JsFunctionObject funcDecl) { if (block[insertAt] != funcDecl) { // technically function declarations can only be direct children of the program or a function block. // and since we are passing in such a block, the parent of the function declaration better be that // block. If it isn't, we don't want to move it because it's not in an allowed place, and different // browsers treat that situation differently. Some browsers would process such funcdecls as if // they were a direct child of the main block. Others will treat it like a function expression with // an external name, and only assign the function to the name if that line of code is actually // executed. So since there's a difference, just leave them as-is and only move valid funcdecls. if (funcDecl.Parent == block) { // remove the function from it's parent, which will take it away from where it is right now. funcDecl.Parent.ReplaceChild(funcDecl, null); // now insert it into the block at the new location, incrementing the location so the next function // will be inserted after it. It is important that they be in the same order as the source, or the semantics // will change when there are functions with the same name. block.Insert(insertAt++, funcDecl); } } else { // we're already in the right place. Just increment the pointer to move to the next position // for next time ++insertAt; } // return the new position return insertAt; }
public void Visit(JsFunctionObject node) { if (node != null) { var symbol = StartSymbol(node); var isNoIn = m_noIn; m_noIn = false; var encloseInParens = node.IsExpression && m_startOfStatement; if (encloseInParens) { OutputPossibleLineBreak('('); } // get the function name we will use for symbol references. // use the function's real name if: // 1. there is one AND // 2a. the function is a declaration OR // 2b. the refcount is greater than zero OR // 2c. we aren't going to remove function expression names // otherwise use the name guess. var hasName = !node.Name.IsNullOrWhiteSpace() && (!node.IsExpression || node.RefCount > 0 || !m_settings.RemoveFunctionExpressionNames || !m_settings.IsModificationAllowed(JsTreeModifications.RemoveFunctionExpressionNames)); var fullFunctionName = hasName ? node.Name : node.NameGuess; Output("function"); MarkSegment(node, fullFunctionName, node.Context); SetContextOutputPosition(node.Context); m_startOfStatement = false; bool isAnonymous = true; if (!node.Name.IsNullOrWhiteSpace()) { isAnonymous = false; var minFunctionName = node.VariableField != null ? node.VariableField.ToString() : node.Name; if (m_settings.SymbolsMap != null) { m_functionStack.Push(minFunctionName); } if (hasName) { // all identifier should be treated as if they start with a valid // identifier character. That might not always be the case, like when // we consider an ASP.NET block to output the start of an identifier. // so let's FORCE the insert-space logic here. if (JsScanner.IsValidIdentifierPart(m_lastCharacter)) { Output(' '); } Output(minFunctionName); MarkSegment(node, node.Name, node.IdContext); SetContextOutputPosition(node.NameContext); } } if (m_settings.SymbolsMap != null && isAnonymous) { JsBinaryOperator binaryOperator = node.Parent as JsBinaryOperator; if (binaryOperator != null && binaryOperator.Operand1 is JsLookup) { m_functionStack.Push("(anonymous) [{0}]".FormatInvariant(binaryOperator.Operand1)); } else { m_functionStack.Push("(anonymous)"); } } OutputFunctionArgsAndBody(node, m_settings.RemoveUnneededCode && node.EnclosingScope.IsKnownAtCompileTime && m_settings.MinifyCode && m_settings.IsModificationAllowed(JsTreeModifications.RemoveUnusedParameters)); if (encloseInParens) { OutputPossibleLineBreak(')'); } m_noIn = isNoIn; EndSymbol(symbol); if (m_settings.SymbolsMap != null) { m_functionStack.Pop(); } } }
private static string GetPropertyType(JsFunctionObject funcObj) { // should never be a function declaration.... return funcObj == null || funcObj.FunctionType == JsFunctionType.Expression ? "data" : funcObj.FunctionType == JsFunctionType.Getter ? "get" : "set"; }
private void DefineField(IJsNameDeclaration nameDecl, JsFunctionObject fieldValue) { var field = this[nameDecl.Name]; if (nameDecl is JsParameterDeclaration) { // function parameters are handled separately, so if this is a parameter declaration, // then it must be a catch variable. if (field == null) { // no collision - create the catch-error field field = new JsVariableField(JsFieldType.CatchError, nameDecl.Name, 0, null) { OriginalContext = nameDecl.NameContext, IsDeclared = true }; this.AddField(field); } else { // it's an error to declare anything in the catch scope with the same name as the // error variable field.OriginalContext.HandleError(JsError.DuplicateCatch, true); } } else { if (field == null) { // could be global or local depending on the scope, so let the scope create it. field = this.CreateField(nameDecl.Name, null, 0); field.OriginalContext = nameDecl.NameContext; field.IsDeclared = true; field.IsFunction = (nameDecl is JsFunctionObject); field.FieldValue = fieldValue; // if this field is a constant, mark it now var lexDeclaration = nameDecl.Parent as JsLexicalDeclaration; field.InitializationOnly = nameDecl.Parent is JsConstStatement || (lexDeclaration != null && lexDeclaration.StatementToken == JsToken.Const); this.AddField(field); } else { // already defined! // if this is a lexical declaration, then it's an error because we have two // lexical declarations with the same name in the same scope. if (nameDecl.Parent is JsLexicalDeclaration) { nameDecl.NameContext.HandleError(JsError.DuplicateLexicalDeclaration, true); } if (nameDecl.Initializer != null) { // if this is an initialized declaration, then the var part is // superfluous and the "initializer" is really a lookup assignment. // So bump up the ref-count for those cases. var nameReference = nameDecl as IJsNameReference; if (nameReference != null) { field.AddReference(nameReference); } } // don't clobber an existing field value with null. For instance, the last // function declaration is the winner, so always set the value if we have something, // but a var following a function shouldn't reset it to null. if (fieldValue != null) { field.FieldValue = fieldValue; } } } nameDecl.VariableField = field; field.Declarations.Add(nameDecl); // if this scope is within a with-statement, or if the declaration was flagged // as not being renamable, then mark the field as not crunchable if (IsInWithScope || nameDecl.RenameNotAllowed) { field.CanCrunch = false; } }
public void Visit(JsFunctionObject node) { // we're good }
/// <summary> /// Parse the source code using the given settings, getting back an abstract syntax tree Block node as the root /// representing the list of statements in the source code. /// </summary> /// <param name="settings">code settings to use to process the source code</param> /// <returns>root Block node representing the top-level statements</returns> public JsBlock Parse(JsSettings settings) { // initialize the scanner with our settings // make sure the RawTokens setting is OFF or we won't be able to create our AST InitializeScanner(settings); // make sure we initialize the global scope's strict mode to our flag, whether or not it // is true. This means if the setting is false, we will RESET the flag to false if we are // reusing the scope and a previous Parse call had code that set it to strict with a // program directive. GlobalScope.UseStrict = m_settings.StrictMode; // make sure the global scope knows about our known global names GlobalScope.SetAssumedGlobals(m_settings); // start of a new module m_newModule = true; var timePoints = m_timingPoints = new long[9]; var timeIndex = timePoints.Length; var stopWatch = new Stopwatch(); stopWatch.Start(); JsBlock scriptBlock = null; JsBlock returnBlock = null; try { switch (m_settings.SourceMode) { case JsSourceMode.Program: // simply parse a block of statements returnBlock = scriptBlock = ParseStatements(); break; case JsSourceMode.Expression: // create a block, get the first token, add in the parse of a single expression, // and we'll go fron there. returnBlock = scriptBlock = new JsBlock(CurrentPositionContext(), this); GetNextToken(); try { var expr = ParseExpression(); if (expr != null) { scriptBlock.Append(expr); scriptBlock.UpdateWith(expr.Context); } } catch (EndOfStreamException) { Debug.WriteLine("EOF"); } break; case JsSourceMode.EventHandler: // we're going to create the global block, add in a function expression with a single // parameter named "event", and then we're going to parse the input as the body of that // function expression. We're going to resolve the global block, but only return the body // of the function. scriptBlock = new JsBlock(null, this); var parameters = new JsAstNodeList(null, this); parameters.Append(new JsParameterDeclaration(null, this) { Name = "event", RenameNotAllowed = true }); var funcExpression = new JsFunctionObject(null, this) { FunctionType = JsFunctionType.Expression, ParameterDeclarations = parameters }; scriptBlock.Append(funcExpression); funcExpression.Body = returnBlock = ParseStatements(); break; default: Debug.Fail("Unexpected source mode enumeration"); return null; } } catch (RecoveryTokenException) { // this should never happen but let's make SURE we don't expose our // private exception object to the outside world m_currentToken.HandleError(JsError.ApplicationError, true); } timePoints[--timeIndex] = stopWatch.ElapsedTicks; if (scriptBlock != null) { // resolve everything JsResolutionVisitor.Apply(scriptBlock, GlobalScope, m_settings); } timePoints[--timeIndex] = stopWatch.ElapsedTicks; if (scriptBlock != null && Settings.MinifyCode && !Settings.PreprocessOnly) { // this visitor doesn't just reorder scopes. It also combines the adjacent var variables, // unnests blocks, identifies prologue directives, and sets the strict mode on scopes. JsReorderScopeVisitor.Apply(scriptBlock, this); timePoints[--timeIndex] = stopWatch.ElapsedTicks; // analyze the entire node tree (needed for hypercrunch) // root to leaf (top down) var analyzeVisitor = new JsAnalyzeNodeVisitor(this); scriptBlock.Accept(analyzeVisitor); timePoints[--timeIndex] = stopWatch.ElapsedTicks; // analyze the scope chain (also needed for hypercrunch) // root to leaf (top down) GlobalScope.AnalyzeScope(); timePoints[--timeIndex] = stopWatch.ElapsedTicks; // if we want to crunch any names.... if (m_settings.LocalRenaming != JsLocalRenaming.KeepAll && m_settings.IsModificationAllowed(JsTreeModifications.LocalRenaming)) { // then do a top-down traversal of the scope tree. For each field that had not // already been crunched (globals and outers will already be crunched), crunch // the name with a crunch iterator that does not use any names in the verboten set. GlobalScope.AutoRenameFields(); } timePoints[--timeIndex] = stopWatch.ElapsedTicks; // if we want to evaluate literal expressions, do so now if (m_settings.EvalLiteralExpressions) { var visitor = new JsEvaluateLiteralVisitor(this); scriptBlock.Accept(visitor); } timePoints[--timeIndex] = stopWatch.ElapsedTicks; // make the final cleanup pass JsFinalPassVisitor.Apply(scriptBlock, this); timePoints[--timeIndex] = stopWatch.ElapsedTicks; // we want to walk all the scopes to make sure that any generated // variables that haven't been crunched have been assigned valid // variable names that don't collide with any existing variables. GlobalScope.ValidateGeneratedNames(); timePoints[--timeIndex] = stopWatch.ElapsedTicks; } if (returnBlock != null && returnBlock.Parent != null) { returnBlock.Parent = null; } return returnBlock; }
/// <summary> /// Output everything for a function except the initial keyword /// </summary> /// <param name="node"></param> /// <param name="removeUnused"></param> private void OutputFunctionArgsAndBody(JsFunctionObject node, bool removeUnused) { if (node != null) { m_startOfStatement = false; if (node.ParameterDeclarations != null) { Indent(); OutputPossibleLineBreak('('); MarkSegment(node, null, node.ParametersContext); // figure out the last referenced argument so we can skip // any that aren't actually referenced int lastRef = node.ParameterDeclarations.Count - 1; // if we're not known at compile time, then we can't leave off unreferenced parameters // (also don't leave things off if we're not hypercrunching) // (also check the kill flag for removing unused parameters) if (removeUnused) { while (lastRef >= 0) { // we want to loop backwards until we either find a parameter that is referenced. // at that point, lastRef will be the index of the last referenced parameter so // we can output from 0 to lastRef var argumentField = (node.ParameterDeclarations[lastRef] as JsParameterDeclaration).IfNotNull(p => p.VariableField); if (argumentField != null && !argumentField.IsReferenced) { --lastRef; } else { // found a referenced parameter, or something weird -- stop looking break; } } } JsAstNode paramDecl = null; for (var ndx = 0; ndx <= lastRef; ++ndx) { if (ndx > 0) { OutputPossibleLineBreak(','); MarkSegment(node, null, paramDecl.IfNotNull(p => p.TerminatingContext) ?? node.ParametersContext); if (m_settings.OutputMode == MinifierOutputMode.MultipleLines) { OutputPossibleLineBreak(' '); } } paramDecl = node.ParameterDeclarations[ndx]; if (paramDecl != null) { paramDecl.Accept(this); } } Unindent(); OutputPossibleLineBreak(')'); MarkSegment(node, null, node.ParametersContext); } if (node.Body == null || node.Body.Count == 0) { Output("{}"); MarkSegment(node, null, node.Body.IfNotNull(b => b.Context)); BreakLine(false); } else { if (m_settings.BlocksStartOnSameLine == MinifierBlockStart.NewLine || (m_settings.BlocksStartOnSameLine == MinifierBlockStart.UseSource && node.Body.BraceOnNewLine)) { NewLine(); } else if (m_settings.OutputMode == MinifierOutputMode.MultipleLines) { OutputPossibleLineBreak(' '); } node.Body.Accept(this); } } }
public override void Visit(JsFunctionObject node) { if (node != null) { // get the name of this function, calculate something if it's anonymous or if // the name isn't actually referenced if (node.Name.IsNullOrWhiteSpace() || (node.IsExpression && node.RefCount == 0 && m_parser.Settings.RemoveFunctionExpressionNames && m_parser.Settings.IsModificationAllowed(JsTreeModifications.RemoveFunctionExpressionNames))) { node.NameGuess = GuessAtName(node); } // don't analyze the identifier or we'll add an extra reference to it. // and we don't need to analyze the parameters because they were fielded-up // back when the function object was created, too if (m_scopeStack.Peek().UseStrict) { // if this is a function delcaration, it better be a source element. // if not, we want to throw a warning that different browsers will treat this function declaration // differently. Technically, this location is not allowed. IE and most other browsers will // simply treat it like every other function declaration in this scope. Firefox, however, won't // add this function declaration's name to the containing scope until the function declaration // is actually "executed." So if you try to call it BEFORE, you will get a "not defined" error. // TODO: take this out for now, because we will throw this error in the resolution process // until we can get this worked out properly. //if (!node.IsSourceElement && node.FunctionType == FunctionType.Declaration) //{ // (node.NameContext ?? node.Context).HandleError(JSError.MisplacedFunctionDeclaration, true); //} // we need to make sure the function isn't named "eval" or "arguments" if (string.CompareOrdinal(node.Name, "eval") == 0 || string.CompareOrdinal(node.Name, "arguments") == 0) { if (node.IdContext != null) { node.IdContext.HandleError(JsError.StrictModeFunctionName, true); } else if (node.Context != null) { node.Context.HandleError(JsError.StrictModeFunctionName, true); } } // we need to make sure: // 1. there are no duplicate argument names, and // 2. none of them are named "eval" or "arguments" // create map that we'll use to determine if there are any dups if (node.ParameterDeclarations != null && node.ParameterDeclarations.Count > 0) { var parameterMap = new HashSet<string>(); foreach (var parameter in node.ParameterDeclarations) { // if it already exists in the map, then it's a dup var parameterName = (parameter as JsParameterDeclaration).IfNotNull(p => p.Name); if (parameterMap.Add(parameterName)) { // now check to see if it's one of the two forbidden names if (string.CompareOrdinal(parameterName, "eval") == 0 || string.CompareOrdinal(parameterName, "arguments") == 0) { parameter.Context.HandleError(JsError.StrictModeArgumentName, true); } } else { // already exists -- throw an error parameter.Context.HandleError(JsError.StrictModeDuplicateArgument, true); } } } } else if (node.ParameterDeclarations != null && node.ParameterDeclarations.Count > 0) { // not strict // if there are duplicate parameter names, throw a warning var parameterMap = new HashSet<string>(); foreach (var parameter in node.ParameterDeclarations) { // if it already exists in the map, then it's a dup var parameterName = (parameter as JsParameterDeclaration).IfNotNull(p => p.Name); if (!parameterMap.Add(parameterName)) { // already exists -- throw an error parameter.Context.HandleError(JsError.DuplicateName, false); } } } if (node.Body != null) { // push the stack and analyze the body m_scopeStack.Push(node.FunctionScope); try { // recurse the body node.Body.Accept(this); } finally { m_scopeStack.Pop(); } } } }
public override void Visit(JsFunctionObject node) { if (node != null) { // if we are reordering ANYTHING, then we need to do the reordering on a scope level. // so if that's the case, we need to create a list of all the child functions and NOT // recurse at this point. Then we'll reorder, then we'll use the lists to recurse. // BUT if we are not reordering anything, no sense making the lists and recursing later. // if that's the case, we can just recurse now and not have to worry about anything later. if (m_moveVarStatements || m_moveFunctionDecls) { // add the node to the appropriate list: either function expression or function declaration. // assume if it's not a function declaration, it must be an expression since the other types // are not declaration (getter, setter) and we want to treat declarations special. // if the conditional comment level isn't zero, then something funky is going on with // the conditional-compilation statements, and we don't want to move the declarations, so // don't add them to the declaration list. But we still want to recurse them, so add them // to the expression list (which get recursed but not moved). if (node.FunctionType == JsFunctionType.Declaration && m_conditionalCommentLevel == 0) { if (m_functionDeclarations == null) { m_functionDeclarations = new List<JsFunctionObject>(); } m_functionDeclarations.Add(node); } else { if (m_functionExpressions == null) { m_functionExpressions = new List<JsFunctionObject>(); } m_functionExpressions.Add(node); } // BUT DO NOT RECURSE!!!! // we only want the functions and variables in THIS scope, not child function scopes. //base.Visit(node); } else { // we're not reordering, so just recurse now to save the hassle base.Visit(node); } } }