private static void ResolveLookups(JsActivationObject scope, JsSettings settings)
        {
            // resolve each lookup this scope contains
            foreach (var lookup in scope.ScopeLookups)
            {
                ResolveLookup(scope, lookup, settings);
            }

            // and recurse
            foreach (var childScope in scope.ChildScopes)
            {
                ResolveLookups(childScope, settings);
            }

            // mark any variables defined in this scope that don't have any references
            // so we can throw warnings later. We can't rely on the reference count because
            // we might remove references while optimizing code -- if we throw an error when
            // the count gets to zero, then we would be reporting errors that don't exist.
            // but we DO know right now what isn't referenced at all.
            foreach (var field in scope.NameTable.Values)
            {
                if (field.RefCount == 0)
                {
                    field.HasNoReferences = true;
                }
            }
        }
        protected JsActivationObject(JsActivationObject parent, JsSettings codeSettings)
        {
            m_isKnownAtCompileTime = true;
            m_useStrict            = false;
            m_settings             = codeSettings;

            Parent      = parent;
            NameTable   = new Dictionary <string, JsVariableField>();
            ChildScopes = new List <JsActivationObject>();

            // if our parent is a scope....
            if (parent != null)
            {
                // add us to the parent's list of child scopes
                parent.ChildScopes.Add(this);

                // if the parent is strict, so are we
                UseStrict = parent.UseStrict;
            }

            // create the two lists of declared items for this scope
            ScopeLookups           = new HashSet <JsLookup>();
            VarDeclaredNames       = new HashSet <IJsNameDeclaration>();
            LexicallyDeclaredNames = new HashSet <IJsNameDeclaration>();

            GhostedCatchParameters = new HashSet <JsParameterDeclaration>();
            GhostedFunctions       = new HashSet <JsFunctionObject>();
        }
        private bool m_useStrict; //= false;

        #endregion Fields

        #region Constructors

        protected JsActivationObject(JsActivationObject parent, JsSettings codeSettings)
        {
            m_isKnownAtCompileTime = true;
            m_useStrict = false;
            m_settings = codeSettings;

            Parent = parent;
            NameTable = new Dictionary<string, JsVariableField>();
            ChildScopes = new List<JsActivationObject>();

            // if our parent is a scope....
            if (parent != null)
            {
                // add us to the parent's list of child scopes
                parent.ChildScopes.Add(this);

                // if the parent is strict, so are we
                UseStrict = parent.UseStrict;
            }

            // create the two lists of declared items for this scope
            ScopeLookups = new HashSet<JsLookup>();
            VarDeclaredNames = new HashSet<IJsNameDeclaration>();
            LexicallyDeclaredNames = new HashSet<IJsNameDeclaration>();

            GhostedCatchParameters = new HashSet<JsParameterDeclaration>();
            GhostedFunctions = new HashSet<JsFunctionObject>();
        }
        private static void ResolveGhostedCatchParameter(JsActivationObject scope, JsParameterDeclaration catchParameter)
        {
            // check to see if the name exists in the outer variable scope.
            var ghostField = scope[catchParameter.Name];

            if (ghostField == null)
            {
                // set up a ghost field to keep track of the relationship
                ghostField = new JsVariableField(JsFieldType.GhostCatch, catchParameter.Name, 0, null)
                {
                    OriginalContext = catchParameter.Context
                };

                scope.AddField(ghostField);
            }
            else if (ghostField.FieldType == JsFieldType.GhostCatch)
            {
                // there is, but it's another ghost catch variable. That's fine; just use it.
                // don't even flag it as ambiguous because if someone is later referencing the error variable
                // used in a couple catch variables, we'll say something then because other browsers will have that
                // variable undefined or from an outer scope.
            }
            else
            {
                // there is, and it's NOT another ghosted catch variable. Possible naming
                // collision in IE -- if an error happens, it will clobber the existing field's value,
                // although that MAY be the intention; we don't know for sure. But it IS a cross-
                // browser behavior difference.
                ghostField.IsAmbiguous = true;

                if (ghostField.OuterField != null)
                {
                    // and to make matters worse, it's actually bound to an OUTER field
                    // in modern browsers, but will bind to this catch variable in older
                    // versions of IE! Definitely a cross-browser difference!
                    // throw a cross-browser issue error.
                    catchParameter.Context.HandleError(JsError.AmbiguousCatchVar);
                }
            }

            // link them so they all keep the same name going forward
            // (since they are named the same in the sources)
            catchParameter.VariableField.OuterField = ghostField;

            // TODO: this really should be a LIST of ghosted fields, since multiple
            // elements can ghost to the same field.
            ghostField.GhostedField = catchParameter.VariableField;

            // if the actual field has references, we want to bubble those up
            // since we're now linking those fields
            if (catchParameter.VariableField.RefCount > 0)
            {
                // add the catch parameter's references to the ghost field
                ghostField.AddReferences(catchParameter.VariableField.References);
            }
        }
Пример #5
0
        public JsBlockScope(JsActivationObject parent, JsContext context, JsSettings settings)
            : base(parent, settings)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            m_context = context.Clone();
        }
Пример #6
0
        private JsContext m_context; // = null;

        #endregion Fields

        #region Constructors

        public JsBlockScope(JsActivationObject parent, JsContext context, JsSettings settings)
            : base(parent, settings)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            m_context = context.Clone();
        }
        private JsResolutionVisitor(JsActivationObject rootScope, JsSettings settings)
        {
            // create the lexical and variable scope stacks and push the root scope onto them
            m_lexicalStack = new Stack <JsActivationObject>();
            m_lexicalStack.Push(rootScope);

            m_variableStack = new Stack <JsActivationObject>();
            m_variableStack.Push(rootScope);

            m_settings = settings;
        }
        private static void CreateFields(JsActivationObject scope)
        {
            // declare this scope
            scope.DeclareScope();

            // and recurse
            foreach (var childScope in scope.ChildScopes)
            {
                CreateFields(childScope);
            }
        }
        private JsResolutionVisitor(JsActivationObject rootScope, JsSettings settings)
        {
            // create the lexical and variable scope stacks and push the root scope onto them
            m_lexicalStack = new Stack<JsActivationObject>();
            m_lexicalStack.Push(rootScope);

            m_variableStack = new Stack<JsActivationObject>();
            m_variableStack.Push(rootScope);

            m_settings = settings;
        }
Пример #10
0
        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;
        }
        private static void CollapseBlockScope(JsActivationObject blockScope)
        {
            // copy over the stuff we want to carry over to the parent
            blockScope.ScopeLookups.CopyItemsTo(blockScope.Parent.ScopeLookups);
            blockScope.VarDeclaredNames.CopyItemsTo(blockScope.Parent.VarDeclaredNames);
            blockScope.ChildScopes.CopyItemsTo(blockScope.Parent.ChildScopes);
            blockScope.GhostedCatchParameters.CopyItemsTo(blockScope.Parent.GhostedCatchParameters);
            blockScope.GhostedFunctions.CopyItemsTo(blockScope.Parent.GhostedFunctions);

            // remove it from its parent's collection of child scopes
            blockScope.Parent.ChildScopes.Remove(blockScope);
        }
        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;
        }
Пример #13
0
        internal void AddReference(JsActivationObject scope)
        {
            // we don't want to include block scopes or with scopes -- they are really
            // contained within their parents
            while (scope != null && scope is JsBlockScope)
            {
                scope = scope.Parent;
            }

            if (scope != null)
            {
                // add the scope to the hash
                m_refScopes.Add(scope);
            }
        }
        private static void AddGhostedFields(JsActivationObject scope)
        {
            foreach (var catchParameter in scope.GhostedCatchParameters)
            {
                ResolveGhostedCatchParameter(scope, catchParameter);
            }

            foreach (var ghostFunc in scope.GhostedFunctions)
            {
                ResolveGhostedFunctions(scope, ghostFunc);
            }

            // recurse
            foreach (var childScope in scope.ChildScopes)
            {
                AddGhostedFields(childScope);
            }
        }
        public static void Apply(JsAstNode node, JsActivationObject scope, JsSettings settings)
        {
            if (node != null && scope != null)
            {
                // create the visitor and run it. This will create all the child
                // scopes and populate all the scopes with the var-decl, lex-decl,
                // and lookup references within them.
                var visitor = new JsResolutionVisitor(scope, settings);
                node.Accept(visitor);

                // now that all the scopes are created and they all know what decls
                // they contains, create all the fields
                CreateFields(scope);

                // now that all the fields have been created in all the scopes,
                // let's go through and resolve all the references
                ResolveLookups(scope, settings);

                // now that everything is declared and resolved as per the language specs,
                // we need to go back and add ghosted fields for older versions of IE that
                // incorrectly implement catch-variables and named function expressions.
                AddGhostedFields(scope);
            }
        }
Пример #16
0
 public JsWithScope(JsActivationObject parent, JsContext context, JsSettings settings)
     : base(parent, context, settings)
 {
     IsInWithScope = true;
 }
Пример #17
0
 public JsWithScope(JsActivationObject parent, JsContext context, JsSettings settings)
     : base(parent, context, settings)
 {
     IsInWithScope = true;
 }
Пример #18
0
 internal JsCatchScope(JsActivationObject parent, JsContext catchContext, JsSettings settings, JsParameterDeclaration catchParameter)
     : base(parent, catchContext, settings)
 {
     CatchParameter = catchParameter;
 }
        private static void ResolveLookups(JsActivationObject scope, JsSettings settings)
        {
            // resolve each lookup this scope contains
            foreach (var lookup in scope.ScopeLookups)
            {
                ResolveLookup(scope, lookup, settings);
            }

            // and recurse
            foreach (var childScope in scope.ChildScopes)
            {
                ResolveLookups(childScope, settings);
            }

            // mark any variables defined in this scope that don't have any references
            // so we can throw warnings later. We can't rely on the reference count because
            // we might remove references while optimizing code -- if we throw an error when
            // the count gets to zero, then we would be reporting errors that don't exist.
            // but we DO know right now what isn't referenced at all.
            foreach (var field in scope.NameTable.Values)
            {
                if (field.RefCount == 0)
                {
                    field.HasNoReferences = true;
                }
            }
        }
        private static void ResolveLookup(JsActivationObject scope, JsLookup lookup, JsSettings settings)
        {
            // resolve lookup via the lexical scope
            lookup.VariableField = scope.FindReference(lookup.Name);
            if (lookup.VariableField.FieldType == JsFieldType.UndefinedGlobal)
            {
                // couldn't find it.
                // if the lookup isn't generated and isn't the object of a typeof operator,
                // then we want to throw an error.
                if (!lookup.IsGenerated)
                {
                    var parentUnaryOp = lookup.Parent as JsUnaryOperator;
                    if (parentUnaryOp != null && parentUnaryOp.OperatorToken == JsToken.TypeOf)
                    {
                        // this undefined lookup is the target of a typeof operator.
                        // I think it's safe to assume we're going to use it. Don't throw an error
                        // and instead add it to the "known" expected globals of the global scope
                        MakeExpectedGlobal(lookup.VariableField);
                    }
                    else
                    {
                        // report this undefined reference
                        lookup.Context.ReportUndefined(lookup);

                        // possibly undefined global (but definitely not local).
                        // see if this is a function or a variable.
                        var callNode = lookup.Parent as JsCallNode;
                        var isFunction = callNode != null && callNode.Function == lookup;
                        lookup.Context.HandleError((isFunction ? JsError.UndeclaredFunction : JsError.UndeclaredVariable), false);
                    }
                }
            }
            else if (lookup.VariableField.FieldType == JsFieldType.Predefined)
            {
                if (string.CompareOrdinal(lookup.Name, "window") == 0)
                {
                    // it's the global window object
                    // see if it's the child of a member or call-brackets node
                    var member = lookup.Parent as JsMember;
                    if (member != null)
                    {
                        // we have window.XXXX. Add XXXX to the known globals if it
                        // isn't already a known item.
                        scope.AddGlobal(member.Name);
                    }
                    else
                    {
                        var callNode = lookup.Parent as JsCallNode;
                        if (callNode != null && callNode.InBrackets
                            && callNode.Arguments.Count == 1
                            && callNode.Arguments[0] is JsConstantWrapper
                            && callNode.Arguments[0].FindPrimitiveType() == JsPrimitiveType.String)
                        {
                            // we have window["XXXX"]. See if XXXX is a valid identifier.
                            // TODO: we could get rid of the ConstantWrapper restriction and use an Evaluate visitor
                            // to evaluate the argument, since we know for sure that it's a string.
                            var identifier = callNode.Arguments[0].ToString();
                            if (JsScanner.IsValidIdentifier(identifier))
                            {
                                // Add XXXX to the known globals if it isn't already a known item.
                                scope.AddGlobal(identifier);
                            }
                        }
                    }
                }
                else if (settings.EvalTreatment != JsEvalTreatment.Ignore
                    && string.CompareOrdinal(lookup.Name, "eval") == 0)
                {
                    // it's an eval -- but are we calling it?
                    // TODO: what if we are assigning it to a variable? Should we track that variable and see if we call it?
                    // What about passing it as a parameter to a function? track that as well in case the function calls it?
                    var parentCall = lookup.Parent as JsCallNode;
                    if (parentCall != null && parentCall.Function == lookup)
                    {
                        scope.IsKnownAtCompileTime = false;
                    }
                }
            }

            // add the reference
            lookup.VariableField.AddReference(lookup);

            // we are actually referencing this field, so it's no longer a placeholder field if it
            // happens to have been one.
            lookup.VariableField.IsPlaceholder = false;
        }
        private static void ResolveGhostedCatchParameter(JsActivationObject scope, JsParameterDeclaration catchParameter)
        {
            // check to see if the name exists in the outer variable scope.
            var ghostField = scope[catchParameter.Name];
            if (ghostField == null)
            {
                // set up a ghost field to keep track of the relationship
                ghostField = new JsVariableField(JsFieldType.GhostCatch, catchParameter.Name, 0, null)
                {
                    OriginalContext = catchParameter.Context
                };

                scope.AddField(ghostField);
            }
            else if (ghostField.FieldType == JsFieldType.GhostCatch)
            {
                // there is, but it's another ghost catch variable. That's fine; just use it.
                // don't even flag it as ambiguous because if someone is later referencing the error variable
                // used in a couple catch variables, we'll say something then because other browsers will have that
                // variable undefined or from an outer scope.
            }
            else
            {
                // there is, and it's NOT another ghosted catch variable. Possible naming
                // collision in IE -- if an error happens, it will clobber the existing field's value,
                // although that MAY be the intention; we don't know for sure. But it IS a cross-
                // browser behavior difference.
                ghostField.IsAmbiguous = true;

                if (ghostField.OuterField != null)
                {
                    // and to make matters worse, it's actually bound to an OUTER field
                    // in modern browsers, but will bind to this catch variable in older
                    // versions of IE! Definitely a cross-browser difference!
                    // throw a cross-browser issue error.
                    catchParameter.Context.HandleError(JsError.AmbiguousCatchVar);
                }
            }

            // link them so they all keep the same name going forward
            // (since they are named the same in the sources)
            catchParameter.VariableField.OuterField = ghostField;

            // TODO: this really should be a LIST of ghosted fields, since multiple
            // elements can ghost to the same field.
            ghostField.GhostedField = catchParameter.VariableField;

            // if the actual field has references, we want to bubble those up
            // since we're now linking those fields
            if (catchParameter.VariableField.RefCount > 0)
            {
                // add the catch parameter's references to the ghost field
                ghostField.AddReferences(catchParameter.VariableField.References);
            }
        }
        private static void CreateFields(JsActivationObject scope)
        {
            // declare this scope
            scope.DeclareScope();

            // and recurse
            foreach (var childScope in scope.ChildScopes)
            {
                CreateFields(childScope);
            }
        }
        public static void Apply(JsAstNode node, JsActivationObject scope, JsSettings settings)
        {
            if (node != null && scope != null)
            {
                // create the visitor and run it. This will create all the child
                // scopes and populate all the scopes with the var-decl, lex-decl,
                // and lookup references within them.
                var visitor = new JsResolutionVisitor(scope, settings);
                node.Accept(visitor);

                // now that all the scopes are created and they all know what decls
                // they contains, create all the fields
                CreateFields(scope);

                // now that all the fields have been created in all the scopes,
                // let's go through and resolve all the references
                ResolveLookups(scope, settings);

                // now that everything is declared and resolved as per the language specs,
                // we need to go back and add ghosted fields for older versions of IE that
                // incorrectly implement catch-variables and named function expressions.
                AddGhostedFields(scope);
            }
        }
        private static void CollapseBlockScope(JsActivationObject blockScope)
        {
            // copy over the stuff we want to carry over to the parent
            blockScope.ScopeLookups.CopyItemsTo(blockScope.Parent.ScopeLookups);
            blockScope.VarDeclaredNames.CopyItemsTo(blockScope.Parent.VarDeclaredNames);
            blockScope.ChildScopes.CopyItemsTo(blockScope.Parent.ChildScopes);
            blockScope.GhostedCatchParameters.CopyItemsTo(blockScope.Parent.GhostedCatchParameters);
            blockScope.GhostedFunctions.CopyItemsTo(blockScope.Parent.GhostedFunctions);

            // remove it from its parent's collection of child scopes
            blockScope.Parent.ChildScopes.Remove(blockScope);
        }
        private static void AddGhostedFields(JsActivationObject scope)
        {
            foreach (var catchParameter in scope.GhostedCatchParameters)
            {
                ResolveGhostedCatchParameter(scope, catchParameter);
            }

            foreach (var ghostFunc in scope.GhostedFunctions)
            {
                ResolveGhostedFunctions(scope, ghostFunc);
            }

            // recurse
            foreach (var childScope in scope.ChildScopes)
            {
                AddGhostedFields(childScope);
            }
        }
        private static void ResolveLookup(JsActivationObject scope, JsLookup lookup, JsSettings settings)
        {
            // resolve lookup via the lexical scope
            lookup.VariableField = scope.FindReference(lookup.Name);
            if (lookup.VariableField.FieldType == JsFieldType.UndefinedGlobal)
            {
                // couldn't find it.
                // if the lookup isn't generated and isn't the object of a typeof operator,
                // then we want to throw an error.
                if (!lookup.IsGenerated)
                {
                    var parentUnaryOp = lookup.Parent as JsUnaryOperator;
                    if (parentUnaryOp != null && parentUnaryOp.OperatorToken == JsToken.TypeOf)
                    {
                        // this undefined lookup is the target of a typeof operator.
                        // I think it's safe to assume we're going to use it. Don't throw an error
                        // and instead add it to the "known" expected globals of the global scope
                        MakeExpectedGlobal(lookup.VariableField);
                    }
                    else
                    {
                        // report this undefined reference
                        lookup.Context.ReportUndefined(lookup);

                        // possibly undefined global (but definitely not local).
                        // see if this is a function or a variable.
                        var callNode   = lookup.Parent as JsCallNode;
                        var isFunction = callNode != null && callNode.Function == lookup;
                        lookup.Context.HandleError((isFunction ? JsError.UndeclaredFunction : JsError.UndeclaredVariable), false);
                    }
                }
            }
            else if (lookup.VariableField.FieldType == JsFieldType.Predefined)
            {
                if (string.CompareOrdinal(lookup.Name, "window") == 0)
                {
                    // it's the global window object
                    // see if it's the child of a member or call-brackets node
                    var member = lookup.Parent as JsMember;
                    if (member != null)
                    {
                        // we have window.XXXX. Add XXXX to the known globals if it
                        // isn't already a known item.
                        scope.AddGlobal(member.Name);
                    }
                    else
                    {
                        var callNode = lookup.Parent as JsCallNode;
                        if (callNode != null && callNode.InBrackets &&
                            callNode.Arguments.Count == 1 &&
                            callNode.Arguments[0] is JsConstantWrapper &&
                            callNode.Arguments[0].FindPrimitiveType() == JsPrimitiveType.String)
                        {
                            // we have window["XXXX"]. See if XXXX is a valid identifier.
                            // TODO: we could get rid of the ConstantWrapper restriction and use an Evaluate visitor
                            // to evaluate the argument, since we know for sure that it's a string.
                            var identifier = callNode.Arguments[0].ToString();
                            if (JsScanner.IsValidIdentifier(identifier))
                            {
                                // Add XXXX to the known globals if it isn't already a known item.
                                scope.AddGlobal(identifier);
                            }
                        }
                    }
                }
                else if (settings.EvalTreatment != JsEvalTreatment.Ignore &&
                         string.CompareOrdinal(lookup.Name, "eval") == 0)
                {
                    // it's an eval -- but are we calling it?
                    // TODO: what if we are assigning it to a variable? Should we track that variable and see if we call it?
                    // What about passing it as a parameter to a function? track that as well in case the function calls it?
                    var parentCall = lookup.Parent as JsCallNode;
                    if (parentCall != null && parentCall.Function == lookup)
                    {
                        scope.IsKnownAtCompileTime = false;
                    }
                }
            }

            // add the reference
            lookup.VariableField.AddReference(lookup);

            // we are actually referencing this field, so it's no longer a placeholder field if it
            // happens to have been one.
            lookup.VariableField.IsPlaceholder = false;
        }
        internal void AddReference(JsActivationObject scope)
        {
            // we don't want to include block scopes or with scopes -- they are really
            // contained within their parents
            while (scope != null && scope is JsBlockScope)
            {
                scope = scope.Parent;
            }

            if (scope != null)
            {
                // add the scope to the hash
                m_refScopes.Add(scope);
            }
        }
        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);
            }
        }
Пример #29
0
 internal JsCatchScope(JsActivationObject parent, JsContext catchContext, JsSettings settings, JsParameterDeclaration catchParameter)
     : base(parent, catchContext, settings)
 {
     CatchParameter = catchParameter;
 }
        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);
            }
        }