Beispiel #1
0
        private static int RelocateFunction(Block block, int insertAt, FunctionObject 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);
        }
        internal FunctionScope(ActivationObject parent, bool isExpression, CodeSettings settings, FunctionObject funcObj)
            : base(parent, settings)
        {
            m_refScopes = new HashSet<ActivationObject>();
            if (isExpression)
            {
                // parent scopes automatically reference enclosed function expressions
                AddReference(Parent);
            }

            FunctionObject = funcObj;
        }
Beispiel #3
0
        internal FunctionScope(ActivationObject parent, bool isExpression, CodeSettings settings, FunctionObject funcObj)
            : base(parent, settings)
        {
            m_refScopes = new HashSet <ActivationObject>();
            if (isExpression)
            {
                // parent scopes automatically reference enclosed function expressions
                AddReference(Parent);
            }

            FunctionObject = funcObj;
        }
Beispiel #4
0
        public override void Visit(FunctionObject 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 == FunctionType.Declaration && m_conditionalCommentLevel == 0)
                    {
                        if (m_functionDeclarations == null)
                        {
                            m_functionDeclarations = new List <FunctionObject>();
                        }

                        m_functionDeclarations.Add(node);
                    }
                    else
                    {
                        if (m_functionExpressions == null)
                        {
                            m_functionExpressions = new List <FunctionObject>();
                        }

                        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, FunctionObject 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 != FieldType.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 BlockScope))
                    {
                        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;
                }
            }
        }
        private static int RelocateFunction(Block block, int insertAt, FunctionObject 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(FunctionObject 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 == FunctionType.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 FunctionScope(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 FunctionScope(parentScope, node.FunctionType != FunctionType.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 == FunctionType.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 void DefineField(INameDeclaration nameDecl, FunctionObject fieldValue)
        {
            var field = this[nameDecl.Name];
            if (nameDecl is ParameterDeclaration)
            {
                // 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(FieldType.CatchError, nameDecl.Name, 0, null)
                    {
                        OriginalContext = nameDecl.NameContext.Clone(),
                        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.Clone();
                    field.IsDeclared = true;
                    field.IsFunction = (nameDecl is FunctionObject);
                    field.FieldValue = fieldValue;

                    // if this field is a constant, mark it now
                    var lexDeclaration = nameDecl.Parent as LexicalDeclaration;
                    field.InitializationOnly = nameDecl.Parent is ConstStatement
                        || (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 LexicalDeclaration)
                    {
                        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 INameReference;
                        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(FunctionObject node)
 {
     // invalid! ignore
     IsValid = false;
 }
 public void Visit(FunctionObject node)
 {
     // we're good
 }
Beispiel #11
0
        /// <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 Block Parse(CodeSettings 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;

            Block scriptBlock;
            Block returnBlock;
            switch (m_settings.SourceMode)
            {
                case JavaScriptSourceMode.Program:
                    // simply parse a block of statements
                    returnBlock = scriptBlock = ParseStatements();
                    break;
                    
                case JavaScriptSourceMode.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 Block(CurrentPositionContext(), this);
                    GetNextToken();
                    try
                    {
                        var expr = ParseExpression();
                        if (expr != null)
                        {
                            scriptBlock.Append(expr);
                            scriptBlock.UpdateWith(expr.Context);
                        }
                    }
                    catch (EndOfFileException)
                    {
                        Debug.WriteLine("EOF");
                    }
                    break;

                case JavaScriptSourceMode.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 Block(null, this);

                    var parameters = new AstNodeList(null, this);
                    parameters.Append(new ParameterDeclaration(null, this)
                        {
                            Name = "event",
                            RenameNotAllowed = true
                        });

                    var funcExpression = new FunctionObject(null, this)
                        {
                            FunctionType = FunctionType.Expression, 
                            ParameterDeclarations = parameters
                        };
                    scriptBlock.Append(funcExpression);

                    returnBlock = ParseStatements();
                    funcExpression.Body = returnBlock;
                    break;

                default:
                    Debug.Fail("Unexpected source mode enumeration");
                    return null;
            }

            // resolve everything
            ResolutionVisitor.Apply(scriptBlock, GlobalScope, m_settings);

            if (scriptBlock != null && Settings.MinifyCode)
            {
                // 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. 
                ReorderScopeVisitor.Apply(scriptBlock, this);

                // analyze the entire node tree (needed for hypercrunch)
                // root to leaf (top down)
                var analyzeVisitor = new AnalyzeNodeVisitor(this);
                scriptBlock.Accept(analyzeVisitor);

                // analyze the scope chain (also needed for hypercrunch)
                // root to leaf (top down)
                m_globalScope.AnalyzeScope();

                // if we want to crunch any names....
                if (m_settings.LocalRenaming != LocalRenaming.KeepAll
                    && m_settings.IsModificationAllowed(TreeModifications.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.
                    m_globalScope.AutoRenameFields();
                }

                // if we want to evaluate literal expressions, do so now
                if (m_settings.EvalLiteralExpressions)
                {
                    var visitor = new EvaluateLiteralVisitor(this);
                    scriptBlock.Accept(visitor);
                }

                // if any of the conditions we check for in the final pass are available, then
                // make the final pass
                if (m_settings.IsModificationAllowed(TreeModifications.BooleanLiteralsToNotOperators))
                {
                    var visitor = new FinalPassVisitor(this);
                    scriptBlock.Accept(visitor);
                }

                // 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.
                m_globalScope.ValidateGeneratedNames();
            }

            if (returnBlock.Parent != null)
            {
                returnBlock.Parent = null;
            }

            return returnBlock;
        }
 public void Visit(FunctionObject node)
 {
     // not applicable; terminate
 }
 private static string GetPropertyType(FunctionObject funcObj)
 {
     // should never be a function declaration....
     return funcObj == null || funcObj.FunctionType == FunctionType.Expression
         ? "data"
         : funcObj.FunctionType == FunctionType.Getter ? "get" : "set";
 }
        public override void Visit(FunctionObject 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(TreeModifications.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 ParameterDeclaration).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 ParameterDeclaration).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(FunctionObject 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 == FunctionType.Declaration && m_conditionalCommentLevel == 0)
                    {
                        if (m_functionDeclarations == null)
                        {
                            m_functionDeclarations = new List<FunctionObject>();
                        }

                        m_functionDeclarations.Add(node);
                    }
                    else
                    {
                        if (m_functionExpressions == null)
                        {
                            m_functionExpressions = new List<FunctionObject>();
                        }

                        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 DefineField(INameDeclaration nameDecl, FunctionObject fieldValue)
        {
            var field = this[nameDecl.Name];

            if (nameDecl is ParameterDeclaration)
            {
                // 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(FieldType.CatchError, nameDecl.Name, 0, null)
                    {
                        OriginalContext = nameDecl.NameContext.Clone(),
                        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.Clone();
                    field.IsDeclared      = true;
                    field.IsFunction      = (nameDecl is FunctionObject);
                    field.FieldValue      = fieldValue;

                    // if this field is a constant, mark it now
                    var lexDeclaration = nameDecl.Parent as LexicalDeclaration;
                    field.InitializationOnly = nameDecl.Parent is ConstStatement ||
                                               (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 LexicalDeclaration)
                    {
                        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 INameReference;
                        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;
            }
        }
        private static void ResolveGhostedFunctions(ActivationObject scope, FunctionObject 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(FieldType.GhostFunction, funcObject.Name, 0, funcObject)
                {
                    OriginalContext = functionField.OriginalContext.Clone(),
                    CanCrunch = funcObject.VariableField.IfNotNull(v => v.CanCrunch)
                };

                scope.AddField(ghostField);
            }
            else if (ghostField.FieldType == FieldType.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 VariableDeclaration;
                    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.
                        Lookup lookup;
                        var parentAssignment = funcObject.Parent as BinaryOperator;
                        if (parentAssignment == null || parentAssignment.OperatorToken != JSToken.Assign
                            || parentAssignment.Operand2 != funcObject
                            || (lookup = parentAssignment.Operand1 as Lookup) == 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);
            }
        }
 public void Visit(FunctionObject node)
 {
     // we're good
 }
        public void Visit(FunctionObject 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 == FunctionType.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 FunctionScope(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 FunctionScope(parentScope, node.FunctionType != FunctionType.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 == FunctionType.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);
                    }
                }
            }
        }
Beispiel #20
0
 public void Visit(FunctionObject node)
 {
     // invalid! ignore
     IsValid = false;
 }
Beispiel #21
0
 public void Visit(FunctionObject node)
 {
     // not applicable; terminate
 }
        private void UnreferencedFunction(JSVariableField variableField, FunctionObject 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 != FieldType.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 BlockScope))
                    {
                        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;
                }
            }
        }
        private static void ResolveGhostedFunctions(ActivationObject scope, FunctionObject 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(FieldType.GhostFunction, funcObject.Name, 0, funcObject)
                {
                    OriginalContext = functionField.OriginalContext.Clone(),
                    CanCrunch       = funcObject.VariableField.IfNotNull(v => v.CanCrunch)
                };

                scope.AddField(ghostField);
            }
            else if (ghostField.FieldType == FieldType.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 VariableDeclaration;
                    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.
                        Lookup lookup;
                        var    parentAssignment = funcObject.Parent as BinaryOperator;
                        if (parentAssignment == null || parentAssignment.OperatorToken != JSToken.Assign ||
                            parentAssignment.Operand2 != funcObject ||
                            (lookup = parentAssignment.Operand1 as Lookup) == 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);
            }
        }