Пример #1
0
 public override bool ReplaceChild(AstNode oldNode, AstNode newNode)
 {
     // if the old node isn't our element list, ignore the cal
     if (oldNode == m_elements)
     {
         if (newNode == null)
         {
             // we want to remove the list altogether
             m_elements = null;
             return(true);
         }
         else
         {
             // if the new node isn't an AstNodeList, then ignore the call
             AstNodeList newList = newNode as AstNodeList;
             if (newList != null)
             {
                 // replace it
                 m_elements     = newList;
                 newList.Parent = this;
                 return(true);
             }
         }
     }
     return(false);
 }
Пример #2
0
 public override bool ReplaceChild(AstNode oldNode, AstNode newNode)
 {
     if (m_func == oldNode)
     {
         m_func = newNode;
         if (newNode != null)
         {
             newNode.Parent = this;
         }
         return(true);
     }
     if (m_args == oldNode)
     {
         if (newNode == null)
         {
             // remove it
             m_args = null;
             return(true);
         }
         else
         {
             // if the new node isn't an AstNodeList, ignore it
             AstNodeList newList = newNode as AstNodeList;
             if (newList != null)
             {
                 m_args         = newList;
                 newNode.Parent = this;
                 return(true);
             }
         }
     }
     return(false);
 }
Пример #3
0
 public void Visit(AstNodeList node)
 {
     if (node != null)
     {
         DoesRequire = true;
     }
 }
Пример #4
0
 public void Visit(AstNodeList node)
 {
     // add the names for each item in the list
     // this assumes that the items are name declarations!
     // (like parameter lists)
     node.IfNotNull(n => n.Children.ForEach(i => i.Accept(this)));
 }
Пример #5
0
 public override bool ReplaceChild(AstNode oldNode, AstNode newNode)
 {
     if (m_expression == oldNode)
     {
         m_expression = newNode;
         if (newNode != null) { newNode.Parent = this; }
         return true;
     }
     if (m_cases == oldNode)
     {
         if (newNode == null)
         {
             // remove it
             m_cases = null;
             return true;
         }
         else
         {
             // if the new node isn't an AstNodeList, ignore the call
             AstNodeList newList = newNode as AstNodeList;
             if (newList != null)
             {
                 m_cases = newList;
                 newNode.Parent = this;
                 return true;
             }
         }
     }
     return false;
 }
Пример #6
0
 public Switch(Context context, JSParser parser, AstNode expression, AstNodeList cases)
     : base(context, parser)
 {
     m_expression = expression;
     m_cases = cases;
     if (m_expression != null) m_expression.Parent = this;
     if (m_cases != null) m_cases.Parent = this;
 }
Пример #7
0
 public Switch(Context context, JSParser parser, AstNode expression, AstNodeList cases)
     : base(context, parser)
 {
     Expression = expression;
     Cases = cases;
     if (Expression != null) Expression.Parent = this;
     if (Cases != null) Cases.Parent = this;
 }
Пример #8
0
 public ArrayLiteral(Context context, JSParser parser, AstNodeList elements)
     : base(context, parser)
 {
     m_elements = elements;
     if (m_elements != null)
     {
         m_elements.Parent = this;
     }
 }
Пример #9
0
        public override AstNode Clone()
        {
            // create a new empty list
            AstNodeList newList = new AstNodeList((Context == null ? null : Context.Clone()), Parser);

            // and clone all the items into it (skipping nulls)
            for (int ndx = 0; ndx < m_list.Count; ++ndx)
            {
                if (m_list[ndx] != null)
                {
                    newList.Append(m_list[ndx].Clone());
                }
            }
            return(newList);
        }
Пример #10
0
        public CallNode(Context context, JSParser parser, AstNode function, AstNodeList args, bool inBrackets)
            : base(context, parser)
        {
            m_func       = function;
            m_args       = args;
            m_inBrackets = inBrackets;

            if (m_func != null)
            {
                m_func.Parent = this;
            }
            if (m_args != null)
            {
                m_args.Parent = this;
            }
        }
        private static bool NotJustPrimitives(AstNodeList nodeList)
        {
            // if any node in the list isn't a constant wrapper (boolean, number, string)
            // or a unary (presumably a negative number), then we've got something other than
            // a primitive in the list (array or object)
            foreach (var child in nodeList)
            {
                if (!(child is ConstantWrapper) && !(child is UnaryOperator))
                {
                    return(true);
                }
            }

            // if we get here, then everything is a primitive
            return(false);
        }
Пример #12
0
        public CallNode(Context context, JSParser parser, AstNode function, AstNodeList args, bool inBrackets)
            : base(context, parser)
        {
            m_func = function;
            m_args = args;
            m_inBrackets = inBrackets;

            if (m_func != null)
            {
                m_func.Parent = this;
            }
            if (m_args != null)
            {
                m_args.Parent = this;
            }
        }
Пример #13
0
 public void Visit(AstNodeList node)
 {
     // add the names for each item in the list
     // this assumes that the items are name declarations!
     // (like parameter lists)
     if (node != null)
     {
         var count = node.Count;
         for (var i = 0; i < count; i++)
         {
             var itemNode = node[i];
             if (itemNode != null)
             {
                 itemNode.Accept(this);
             }
         }
     }
 }
Пример #14
0
        public override bool ReplaceChild(AstNode oldNode, AstNode newNode)
        {
            if (Expression == oldNode)
            {
                Expression = newNode;
                return(true);
            }
            if (Cases == oldNode)
            {
                AstNodeList newList = newNode as AstNodeList;
                if (newNode == null || newList != null)
                {
                    // remove it
                    Cases = newList;
                    return(true);
                }
            }

            return(false);
        }
        public void Visit(AstNodeList node)
        {
            if (node != null)
            {
                for (var ndx = 0; ndx < node.Count; ++ndx)
                {
                    if (ndx > 0)
                    {
                        m_writer.Write(',');
                        if (m_settings.OutputMode == OutputMode.MultipleLines)
                        {
                            m_writer.Write(' ');
                        }
                    }

                    if (node[ndx] != null)
                    {
                        node[ndx].Accept(this);
                    }
                }
            }
        }
Пример #16
0
        /// <summary>
        /// an astlist is equivalent to another astlist if they both have the same number of
        /// items, and each item is equivalent to the corresponding item in the other
        /// </summary>
        /// <param name="otherNode"></param>
        /// <returns></returns>
        public override bool IsEquivalentTo(AstNode otherNode)
        {
            bool isEquivalent = false;

            AstNodeList otherList = otherNode as AstNodeList;

            if (otherList != null && m_list.Count == otherList.Count)
            {
                // now assume it's true unless we come across an item that ISN'T
                // equivalent, at which case we'll bail the test.
                isEquivalent = true;
                for (var ndx = 0; ndx < m_list.Count; ++ndx)
                {
                    if (!m_list[ndx].IsEquivalentTo(otherList[ndx]))
                    {
                        isEquivalent = false;
                        break;
                    }
                }
            }

            return(isEquivalent);
        }
Пример #17
0
        //---------------------------------------------------------------------------------------
        // ParseSwitchStatement
        //
        //  SwitchStatement :
        //    'switch' '(' Expression ')' '{' CaseBlock '}'
        //
        //  CaseBlock :
        //    CaseList DefaultCaseClause CaseList
        //
        //  CaseList :
        //    <empty> |
        //    CaseClause CaseList
        //
        //  CaseClause :
        //    'case' Expression ':' OptionalStatements
        //
        //  DefaultCaseClause :
        //    <empty> |
        //    'default' ':' OptionalStatements
        //---------------------------------------------------------------------------------------
        private AstNode ParseSwitchStatement()
        {
            Context switchCtx = m_currentToken.Clone();
            AstNode expr = null;
            AstNodeList cases = null;
            m_blockType.Add(BlockType.Switch);
            try
            {
                // read switch(expr)
                GetNextToken();
                if (JSToken.LeftParenthesis != m_currentToken.Token)
                    ReportError(JSError.NoLeftParenthesis);
                GetNextToken();
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_SwitchNoSkipTokenSet);
                try
                {
                    expr = ParseExpression();

                    if (JSToken.RightParenthesis != m_currentToken.Token)
                    {
                        ReportError(JSError.NoRightParenthesis);
                    }

                    GetNextToken();
                    if (JSToken.LeftCurly != m_currentToken.Token)
                    {
                        ReportError(JSError.NoLeftCurly);
                    }
                    GetNextToken();

                }
                catch (RecoveryTokenException exc)
                {
                    if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) == -1
                          && IndexOfToken(NoSkipTokenSet.s_SwitchNoSkipTokenSet, exc) == -1)
                    {
                        // give up
                        exc._partiallyComputedNode = null;
                        throw;
                    }
                    else
                    {
                        if (exc._partiallyComputedNode == null)
                            expr = new ConstantWrapper(true, PrimitiveType.Boolean, CurrentPositionContext(), this);
                        else
                            expr = exc._partiallyComputedNode;

                        if (IndexOfToken(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet, exc) != -1)
                        {
                            if (exc._token == JSToken.RightParenthesis)
                                GetNextToken();

                            if (JSToken.LeftCurly != m_currentToken.Token)
                            {
                                ReportError(JSError.NoLeftCurly);
                            }
                            GetNextToken();
                        }

                    }
                }
                finally
                {
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_SwitchNoSkipTokenSet);
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockConditionNoSkipTokenSet);
                }

                // parse the switch body
                cases = new AstNodeList(m_currentToken.Clone(), this);
                bool defaultStatement = false;
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet);
                try
                {
                    while (JSToken.RightCurly != m_currentToken.Token)
                    {
                        SwitchCase caseClause = null;
                        AstNode caseValue = null;
                        Context caseCtx = m_currentToken.Clone();
                        m_noSkipTokenSet.Add(NoSkipTokenSet.s_CaseNoSkipTokenSet);
                        try
                        {
                            if (JSToken.Case == m_currentToken.Token)
                            {
                                // get the case
                                GetNextToken();
                                caseValue = ParseExpression();
                            }
                            else if (JSToken.Default == m_currentToken.Token)
                            {
                                // get the default
                                if (defaultStatement)
                                {
                                    // we report an error but we still accept the default
                                    ReportError(JSError.DupDefault, true);
                                }
                                else
                                {
                                    defaultStatement = true;
                                }
                                GetNextToken();
                            }
                            else
                            {
                                // This is an error, there is no case or default. Assume a default was missing and keep going
                                defaultStatement = true;
                                ReportError(JSError.BadSwitch);
                            }
                            if (JSToken.Colon != m_currentToken.Token)
                            {
                                ReportError(JSError.NoColon);
                            }

                            // read the statements inside the case or default
                            GetNextToken();
                        }
                        catch (RecoveryTokenException exc)
                        {
                            // right now we can only get here for the 'case' statement
                            if (IndexOfToken(NoSkipTokenSet.s_CaseNoSkipTokenSet, exc) == -1)
                            {
                                // ignore the current case or default
                                exc._partiallyComputedNode = null;
                                throw;
                            }
                            else
                            {
                                caseValue = exc._partiallyComputedNode;

                                if (exc._token == JSToken.Colon)
                                {
                                    GetNextToken();
                                }
                            }
                        }
                        finally
                        {
                            m_noSkipTokenSet.Remove(NoSkipTokenSet.s_CaseNoSkipTokenSet);
                        }

                        m_blockType.Add(BlockType.Block);
                        try
                        {
                            Block statements = new Block(m_currentToken.Clone(), this);
                            m_noSkipTokenSet.Add(NoSkipTokenSet.s_SwitchNoSkipTokenSet);
                            m_noSkipTokenSet.Add(NoSkipTokenSet.s_StartStatementNoSkipTokenSet);
                            try
                            {
                                while (JSToken.RightCurly != m_currentToken.Token && JSToken.Case != m_currentToken.Token && JSToken.Default != m_currentToken.Token)
                                {
                                    try
                                    {
                                        // parse a Statement, not a SourceElement
                                        statements.Append(ParseStatement(false));
                                    }
                                    catch (RecoveryTokenException exc)
                                    {
                                        if (exc._partiallyComputedNode != null)
                                        {
                                            statements.Append(exc._partiallyComputedNode);
                                            exc._partiallyComputedNode = null;
                                        }
                                        if (IndexOfToken(NoSkipTokenSet.s_StartStatementNoSkipTokenSet, exc) == -1)
                                        {
                                            throw;
                                        }
                                    }
                                }
                            }
                            catch (RecoveryTokenException exc)
                            {
                                if (IndexOfToken(NoSkipTokenSet.s_SwitchNoSkipTokenSet, exc) == -1)
                                {
                                    caseClause = new SwitchCase(caseCtx, this, caseValue, statements);
                                    cases.Append(caseClause);
                                    throw;
                                }
                            }
                            finally
                            {
                                m_noSkipTokenSet.Remove(NoSkipTokenSet.s_StartStatementNoSkipTokenSet);
                                m_noSkipTokenSet.Remove(NoSkipTokenSet.s_SwitchNoSkipTokenSet);
                            }
                            if (JSToken.RightCurly == m_currentToken.Token)
                            {
                                statements.Context.UpdateWith(m_currentToken);
                            }
                            caseCtx.UpdateWith(statements.Context);
                            caseClause = new SwitchCase(caseCtx, this, caseValue, statements);
                            cases.Append(caseClause);
                        }
                        finally
                        {
                            m_blockType.RemoveAt(m_blockType.Count - 1);
                        }
                    }
                }
                catch (RecoveryTokenException exc)
                {
                    if (IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exc) == -1)
                    {
                        //save what you can a rethrow
                        switchCtx.UpdateWith(CurrentPositionContext());
                        exc._partiallyComputedNode = new Switch(switchCtx, this, expr, cases);
                        throw;
                    }
                }
                finally
                {
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet);
                }
                switchCtx.UpdateWith(m_currentToken);
                GetNextToken();
            }
            finally
            {
                m_blockType.RemoveAt(m_blockType.Count - 1);
            }

            return new Switch(switchCtx, this, expr, cases);
        }
Пример #18
0
        //
        // expression elements we shouldn't get to
        //

        public void Visit(AstNodeList node)
        {
            Debug.Fail("shouldn't get here");
        }
Пример #19
0
        internal override void AnalyzeNode()
        {
            // see if this is a member (we'll need it for a couple checks)
            Member member = m_func as Member;

            if (Parser.Settings.StripDebugStatements && Parser.Settings.IsModificationAllowed(TreeModifications.StripDebugStatements))
            {
                // if this is a member, and it's a debugger object, and it's a constructor....
                if (member != null && member.IsDebuggerStatement && m_isConstructor)
                {
                    // we need to replace our debugger object with a generic Object
                    m_func = new Lookup("Object", m_func.Context, Parser);
                    // and make sure the node list is empty
                    if (m_args != null && m_args.Count > 0)
                    {
                        m_args = new AstNodeList(m_args.Context, Parser);
                    }
                }
            }

            // if this is a constructor and we want to collapse
            // some of them to literals...
            if (m_isConstructor && Parser.Settings.CollapseToLiteral)
            {
                // see if this is a lookup, and if so, if it's pointing to one
                // of the two constructors we want to collapse
                Lookup lookup = m_func as Lookup;
                if (lookup != null)
                {
                    if (lookup.Name == "Object" &&
                        Parser.Settings.IsModificationAllowed(TreeModifications.NewObjectToObjectLiteral))
                    {
                        // no arguments -- the Object constructor with no arguments is the exact same as an empty
                        // object literal
                        if (m_args == null || m_args.Count == 0)
                        {
                            // replace our node with an object literal
                            ObjectLiteral objLiteral = new ObjectLiteral(Context, Parser, null, null);
                            if (Parent.ReplaceChild(this, objLiteral))
                            {
                                // and bail now. No need to recurse -- it's an empty literal
                                return;
                            }
                        }
                        else if (m_args.Count == 1)
                        {
                            // one argument
                            // check to see if it's an object literal.
                            ObjectLiteral objectLiteral = m_args[0] as ObjectLiteral;
                            if (objectLiteral != null)
                            {
                                // the Object constructor with an argument that is a JavaScript object merely returns the
                                // argument. Since the argument is an object literal, it is by definition a JavaScript object
                                // and therefore we can replace the constructor call with the object literal
                                Parent.ReplaceChild(this, objectLiteral);

                                // don't forget to recurse the object now
                                objectLiteral.AnalyzeNode();

                                // and then bail -- we don't want to process this call
                                // operation any more; we've gotten rid of it
                                return;
                            }
                        }
                    }
                    else if (lookup.Name == "Array" &&
                             Parser.Settings.IsModificationAllowed(TreeModifications.NewArrayToArrayLiteral))
                    {
                        // Array is trickier.
                        // If there are no arguments, then just use [].
                        // if there are multiple arguments, then use [arg0,arg1...argN].
                        // but if there is one argument and it's numeric, we can't crunch it.
                        // also can't crunch if it's a function call or a member or something, since we won't
                        // KNOW whether or not it's numeric.
                        //
                        // so first see if it even is a single-argument constant wrapper.
                        ConstantWrapper constWrapper = (m_args != null && m_args.Count == 1 ? m_args[0] as ConstantWrapper : null);

                        // if the argument count is not one, then we crunch.
                        // if the argument count IS one, we only crunch if we have a constant wrapper,
                        // AND it's not numeric.
                        if (m_args == null ||
                            m_args.Count != 1 ||
                            (constWrapper != null && !constWrapper.IsNumericLiteral))
                        {
                            // create the new array literal object
                            ArrayLiteral arrayLiteral = new ArrayLiteral(Context, Parser, m_args);
                            // replace ourself within our parent
                            if (Parent.ReplaceChild(this, arrayLiteral))
                            {
                                // recurse
                                arrayLiteral.AnalyzeNode();
                                // and bail -- we don't want to recurse this node any more
                                return;
                            }
                        }
                    }
                }
            }

            // if we are replacing resource references with strings generated from resource files
            // and this is a brackets call: lookup[args]
            ResourceStrings resourceStrings = Parser.ResourceStrings;

            if (m_inBrackets && resourceStrings != null && resourceStrings.Count > 0)
            {
                // see if the root object is a lookup that corresponds to the
                // global value (not a local field) for our resource object
                // (same name)
                Lookup rootLookup = m_func as Lookup;
                if (rootLookup != null &&
                    rootLookup.LocalField == null &&
                    string.CompareOrdinal(rootLookup.Name, resourceStrings.Name) == 0)
                {
                    // we're going to replace this node with a string constant wrapper
                    // but first we need to make sure that this is a valid lookup.
                    // if the parameter contains anything that would vary at run-time,
                    // then we need to throw an error.
                    // the parser will always have either one or zero nodes in the arguments
                    // arg list. We're not interested in zero args, so just make sure there is one
                    if (m_args.Count == 1)
                    {
                        // must be a constant wrapper
                        ConstantWrapper argConstant = m_args[0] as ConstantWrapper;
                        if (argConstant != null)
                        {
                            string resourceName = argConstant.Value.ToString();

                            // get the localized string from the resources object
                            ConstantWrapper resourceLiteral = new ConstantWrapper(
                                resourceStrings[resourceName],
                                PrimitiveType.String,
                                Context,
                                Parser);

                            // replace this node with localized string, analyze it, and bail
                            // so we don't anaylze the tree we just replaced
                            Parent.ReplaceChild(this, resourceLiteral);
                            resourceLiteral.AnalyzeNode();
                            return;
                        }
                        else
                        {
                            // error! must be a constant
                            Context.HandleError(
                                JSError.ResourceReferenceMustBeConstant,
                                true);
                        }
                    }
                    else
                    {
                        // error! can only be a single constant argument to the string resource object.
                        // the parser will only have zero or one arguments, so this must be zero
                        // (since the parser won't pass multiple args to a [] operator)
                        Context.HandleError(
                            JSError.ResourceReferenceMustBeConstant,
                            true);
                    }
                }
            }

            // and finally, if this is a backets call and the argument is a constantwrapper that can
            // be an identifier, just change us to a member node:  obj["prop"] to obj.prop.
            // but ONLY if the string value is "safe" to be an identifier. Even though the ECMA-262
            // spec says certain Unicode categories are okay, in practice the various major browsers
            // all seem to have problems with certain characters in identifiers. Rather than risking
            // some browsers breaking when we change this syntax, don't do it for those "danger" categories.
            if (m_inBrackets && m_args != null)
            {
                // see if there is a single, constant argument
                string argText = m_args.SingleConstantArgument;
                if (argText != null)
                {
                    // see if we want to replace the name
                    string newName;
                    if (Parser.Settings.HasRenamePairs && Parser.Settings.ManualRenamesProperties &&
                        Parser.Settings.IsModificationAllowed(TreeModifications.PropertyRenaming) &&
                        !string.IsNullOrEmpty(newName = Parser.Settings.GetNewName(argText)))
                    {
                        // yes -- we are going to replace the name, either as a string literal, or by converting
                        // to a member-dot operation.
                        // See if we can't turn it into a dot-operator. If we can't, then we just want to replace the operator with
                        // a new constant wrapper. Otherwise we'll just replace the operator with a new constant wrapper.
                        if (Parser.Settings.IsModificationAllowed(TreeModifications.BracketMemberToDotMember) &&
                            JSScanner.IsSafeIdentifier(newName) &&
                            !JSScanner.IsKeyword(newName))
                        {
                            // the new name is safe to convert to a member-dot operator.
                            // but we don't want to convert the node to the NEW name, because we still need to Analyze the
                            // new member node -- and it might convert the new name to something else. So instead we're
                            // just going to convert this existing string to a member node WITH THE OLD STRING,
                            // and THEN analyze it (which will convert the old string to newName)
                            Member replacementMember = new Member(Context, Parser, m_func, argText);
                            Parent.ReplaceChild(this, replacementMember);

                            // this analyze call will convert the old-name member to the newName value
                            replacementMember.AnalyzeNode();
                            return;
                        }
                        else
                        {
                            // nope; can't convert to a dot-operator.
                            // we're just going to replace the first argument with a new string literal
                            // and continue along our merry way.
                            m_args.ReplaceChild(m_args[0], new ConstantWrapper(newName, PrimitiveType.String, m_args[0].Context, Parser));
                        }
                    }
                    else if (Parser.Settings.IsModificationAllowed(TreeModifications.BracketMemberToDotMember) &&
                             JSScanner.IsSafeIdentifier(argText) &&
                             !JSScanner.IsKeyword(argText))
                    {
                        // not a replacement, but the string literal is a safe identifier. So we will
                        // replace this call node with a Member-dot operation
                        Member replacementMember = new Member(Context, Parser, m_func, argText);
                        Parent.ReplaceChild(this, replacementMember);
                        replacementMember.AnalyzeNode();
                        return;
                    }
                }
            }

            // call the base class to recurse
            base.AnalyzeNode();

            // call this AFTER recursing to give the fields a chance to resolve, because we only
            // want to make this replacement if we are working on the global Date object.
            if (!m_inBrackets && !m_isConstructor &&
                (m_args == null || m_args.Count == 0) &&
                member != null && string.CompareOrdinal(member.Name, "getTime") == 0 &&
                Parser.Settings.IsModificationAllowed(TreeModifications.DateGetTimeToUnaryPlus))
            {
                // this is not a constructor and it's not a brackets call, and there are no arguments.
                // if the function is a member operation to "getTime" and the object of the member is a
                // constructor call to the global "Date" object (not a local), then we want to replace the call
                // with a unary plus on the Date constructor. Converting to numeric type is the same as
                // calling getTime, so it's the equivalent with much fewer bytes.
                CallNode dateConstructor = member.Root as CallNode;
                if (dateConstructor != null &&
                    dateConstructor.IsConstructor)
                {
                    // lookup for the predifined (not local) "Date" field
                    Lookup lookup = dateConstructor.Function as Lookup;
                    if (lookup != null && string.CompareOrdinal(lookup.Name, "Date") == 0 &&
                        lookup.LocalField == null)
                    {
                        // this is in the pattern: (new Date()).getTime()
                        // we want to replace it with +new Date
                        // use the same date constructor node as the operand
                        NumericUnary unary = new NumericUnary(Context, Parser, dateConstructor, JSToken.Plus);

                        // replace us (the call to the getTime method) with this unary operator
                        Parent.ReplaceChild(this, unary);

                        // don't need to AnalyzeNode on the unary operator. The operand has already
                        // been analyzed when we recursed, and the unary operator wouldn't do anything
                        // special anyway (since the operand is not a numeric constant)
                    }
                }
            }
            else if (Parser.Settings.EvalTreatment != EvalTreatment.Ignore)
            {
                // if this is a window.eval call, then we need to mark this scope as unknown just as
                // we would if this was a regular eval call.
                // (unless, of course, the parser settings say evals are safe)
                // call AFTER recursing so we know the left-hand side properties have had a chance to
                // lookup their fields to see if they are local or global
                if (member != null && string.CompareOrdinal(member.Name, "eval") == 0)
                {
                    if (member.LeftHandSide.IsWindowLookup)
                    {
                        // this is a call to window.eval()
                        // mark this scope as unknown so we don't crunch out locals
                        // we might reference in the eval at runtime
                        ScopeStack.Peek().IsKnownAtCompileTime = false;
                    }
                }
                else
                {
                    CallNode callNode = m_func as CallNode;
                    if (callNode != null &&
                        callNode.InBrackets &&
                        callNode.LeftHandSide.IsWindowLookup &&
                        callNode.Arguments.IsSingleConstantArgument("eval"))
                    {
                        // this is a call to window["eval"]
                        // mark this scope as unknown so we don't crunch out locals
                        // we might reference in the eval at runtime
                        ScopeStack.Peek().IsKnownAtCompileTime = false;
                    }
                }
            }

            /* REVIEW: may be too late. lookups may alread have been analyzed and
             * found undefined
             * // check to see if this is an assignment to a window["prop"] structure
             * BinaryOperator binaryOp = Parent as BinaryOperator;
             * if (binaryOp != null && binaryOp.IsAssign
             *  && m_inBrackets
             *  && m_func.IsWindowLookup
             *  && m_args != null)
             * {
             *  // and IF the property is a non-empty constant that isn't currently
             *  // a global field...
             *  string propertyName = m_args.SingleConstantArgument;
             *  if (!string.IsNullOrEmpty(propertyName)
             *      && Parser.GlobalScope[propertyName] == null)
             *  {
             *      // we want to also add it to the global fields so it's not undefined
             *      Parser.GlobalScope.DeclareField(propertyName, null, 0);
             *  }
             * }
             */
        }
Пример #20
0
        //---------------------------------------------------------------------------------------
        // MemberExpression
        //
        // Accessor :
        //  <empty> |
        //  Arguments Accessor
        //  '[' Expression ']' Accessor |
        //  '.' Identifier Accessor |
        //
        //  Don't have this function throwing an exception without checking all the calling sites.
        //  There is state in instance variable that is saved on the calling stack in some function
        //  (i.e ParseFunction and ParseClass) and you don't want to blow up the stack
        //---------------------------------------------------------------------------------------
        private AstNode MemberExpression(AstNode expression, List<Context> newContexts)
        {
            for (; ; )
            {
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_MemberExprNoSkipTokenSet);
                try
                {
                    switch (m_currentToken.Token)
                    {
                        case JSToken.LeftParenthesis:
                            AstNodeList args = null;
                            RecoveryTokenException callError = null;
                            m_noSkipTokenSet.Add(NoSkipTokenSet.s_ParenToken);
                            try
                            {
                                args = ParseExpressionList(JSToken.RightParenthesis);
                            }
                            catch (RecoveryTokenException exc)
                            {
                                args = (AstNodeList)exc._partiallyComputedNode;
                                if (IndexOfToken(NoSkipTokenSet.s_ParenToken, exc) == -1)
                                    callError = exc; // thrown later on
                            }
                            finally
                            {
                                m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ParenToken);
                            }

                            expression = new CallNode(expression.Context.CombineWith(args.Context), this, expression, args, false);

                            if (null != newContexts && newContexts.Count > 0)
                            {
                                (newContexts[newContexts.Count - 1]).UpdateWith(expression.Context);
                                if (!(expression is CallNode))
                                    expression = new CallNode(newContexts[newContexts.Count - 1], this, expression, new AstNodeList(CurrentPositionContext(), this), false);
                                else
                                    expression.Context = newContexts[newContexts.Count - 1];
                                ((CallNode)expression).IsConstructor = true;
                                newContexts.RemoveAt(newContexts.Count - 1);
                            }

                            if (callError != null)
                            {
                                callError._partiallyComputedNode = expression;
                                throw callError;
                            }

                            GetNextToken();
                            break;

                        case JSToken.LeftBracket:
                            m_noSkipTokenSet.Add(NoSkipTokenSet.s_BracketToken);
                            try
                            {
                                //
                                // ROTOR parses a[b,c] as a call to a, passing in the arguments b and c.
                                // the correct parse is a member lookup on a of c -- the "b,c" should be
                                // a single expression with a comma operator that evaluates b but only
                                // returns c.
                                // So we'll change the default behavior from parsing an expression list to
                                // parsing a single expression, but returning a single-item list (or an empty
                                // list if there is no expression) so the rest of the code will work.
                                //
                                //args = ParseExpressionList(JSToken.RightBracket);
                                GetNextToken();
                                args = new AstNodeList(m_currentToken.Clone(), this);

                                AstNode accessor = ParseExpression();
                                if (accessor != null)
                                {
                                    args.Append(accessor);
                                }
                            }
                            catch (RecoveryTokenException exc)
                            {
                                if (IndexOfToken(NoSkipTokenSet.s_BracketToken, exc) == -1)
                                {
                                    if (exc._partiallyComputedNode != null)
                                    {
                                        exc._partiallyComputedNode =
                                           new CallNode(expression.Context.CombineWith(m_currentToken.Clone()), this, expression, (AstNodeList)exc._partiallyComputedNode, true);
                                    }
                                    else
                                    {
                                        exc._partiallyComputedNode = expression;
                                    }
                                    throw;
                                }
                                else
                                    args = (AstNodeList)exc._partiallyComputedNode;
                            }
                            finally
                            {
                                m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BracketToken);
                            }
                            expression = new CallNode(expression.Context.CombineWith(m_currentToken.Clone()), this, expression, args, true);

                            // there originally was code here in the ROTOR sources that checked the new context list and
                            // changed this member call to a constructor call, effectively combining the two. I believe they
                            // need to remain separate.

                            // remove the close bracket token
                            GetNextToken();
                            break;

                        case JSToken.AccessField:
                            ConstantWrapper id = null;
                            GetNextToken();
                            if (JSToken.Identifier != m_currentToken.Token)
                            {
                                string identifier = JSKeyword.CanBeIdentifier(m_currentToken.Token);
                                if (null != identifier)
                                {
                                    // don't report an error here -- it's actually okay to have a property name
                                    // that is a keyword which is okay to be an identifier. For instance,
                                    // jQuery has a commonly-used method named "get" to make an ajax request
                                    //ForceReportInfo(JSError.KeywordUsedAsIdentifier);
                                    id = new ConstantWrapper(identifier, PrimitiveType.String, m_currentToken.Clone(), this);
                                }
                                else if (JSScanner.IsValidIdentifier(m_currentToken.Code))
                                {
                                    // it must be a keyword, because it can't technically be an indentifier,
                                    // but it IS a valid identifier format. Throw the error but still
                                    // create the constant wrapper so we can output it as-is
                                    ReportError(JSError.NoIdentifier, m_currentToken.Clone(), true);
                                    id = new ConstantWrapper(m_currentToken.Code, PrimitiveType.String, m_currentToken.Clone(), this);
                                }
                                else
                                {
                                    ReportError(JSError.NoIdentifier);
                                    SkipTokensAndThrow(expression);
                                }
                            }
                            else
                            {
                                id = new ConstantWrapper(m_scanner.GetIdentifier(), PrimitiveType.String, m_currentToken.Clone(), this);
                            }
                            GetNextToken();
                            expression = new Member(expression.Context.CombineWith(id.Context), this, expression, id.Context.Code, id.Context);
                            break;
                        default:
                            if (null != newContexts)
                            {
                                while (newContexts.Count > 0)
                                {
                                    (newContexts[newContexts.Count - 1]).UpdateWith(expression.Context);
                                    expression = new CallNode(newContexts[newContexts.Count - 1],
                                                          this,
                                                          expression,
                                                          new AstNodeList(CurrentPositionContext(), this),
                                                          false);
                                    ((CallNode)expression).IsConstructor = true;
                                    newContexts.RemoveAt(newContexts.Count - 1);
                                }
                            }
                            return expression;
                    }
                }
                catch (RecoveryTokenException exc)
                {
                    if (IndexOfToken(NoSkipTokenSet.s_MemberExprNoSkipTokenSet, exc) != -1)
                        expression = exc._partiallyComputedNode;
                    else
                    {
                        Debug.Assert(exc._partiallyComputedNode == expression);
                        throw;
                    }
                }
                finally
                {
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_MemberExprNoSkipTokenSet);
                }
            }
        }
Пример #21
0
        //---------------------------------------------------------------------------------------
        // ParseExpressionList
        //
        //  Given a starting this.currentToken '(' or '[', parse a list of expression separated by
        //  ',' until matching ')' or ']'
        //---------------------------------------------------------------------------------------
        private AstNodeList ParseExpressionList(JSToken terminator)
        {
            Context listCtx = m_currentToken.Clone();
            GetNextToken();
            AstNodeList list = new AstNodeList(listCtx, this);
            if (terminator != m_currentToken.Token)
            {
                for (; ; )
                {
                    m_noSkipTokenSet.Add(NoSkipTokenSet.s_ExpressionListNoSkipTokenSet);
                    try
                    {
                        if (JSToken.Comma == m_currentToken.Token)
                        {
                            list.Append(new ConstantWrapper(Missing.Value, PrimitiveType.Other, m_currentToken.Clone(), this));
                        }
                        else if (terminator == m_currentToken.Token)
                        {
                            break;
                        }
                        else
                            list.Append(ParseExpression(true));

                        if (terminator == m_currentToken.Token)
                            break;
                        else
                        {
                            if (JSToken.Comma != m_currentToken.Token)
                            {
                                if (terminator == JSToken.RightParenthesis)
                                {
                                    //  in ASP+ it's easy to write a semicolon at the end of an expression
                                    //  not realizing it is going to go inside a function call
                                    //  (ie. Response.Write()), so make a special check here
                                    if (JSToken.Semicolon == m_currentToken.Token)
                                    {
                                        if (JSToken.RightParenthesis == m_scanner.PeekToken())
                                        {
                                            ReportError(JSError.UnexpectedSemicolon, true);
                                            GetNextToken();
                                            break;
                                        }
                                    }
                                    ReportError(JSError.NoRightParenthesisOrComma);
                                }
                                else
                                    ReportError(JSError.NoRightBracketOrComma);
                                SkipTokensAndThrow();
                            }
                        }
                    }
                    catch (RecoveryTokenException exc)
                    {
                        if (exc._partiallyComputedNode != null)
                            list.Append(exc._partiallyComputedNode);
                        if (IndexOfToken(NoSkipTokenSet.s_ExpressionListNoSkipTokenSet, exc) == -1)
                        {
                            exc._partiallyComputedNode = list;
                            throw;
                        }
                    }
                    finally
                    {
                        m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ExpressionListNoSkipTokenSet);
                    }
                    GetNextToken();
                }
            }
            listCtx.UpdateWith(m_currentToken);
            return list;
        }
Пример #22
0
 public override bool ReplaceChild(AstNode oldNode, AstNode newNode)
 {
     if (m_func == oldNode)
     {
         m_func = newNode;
         if (newNode != null) { newNode.Parent = this; }
         return true;
     }
     if (m_args == oldNode)
     {
         if (newNode == null)
         {
             // remove it
             m_args = null;
             return true;
         }
         else
         {
             // if the new node isn't an AstNodeList, ignore it
             AstNodeList newList = newNode as AstNodeList;
             if (newList != null)
             {
                 m_args = newList;
                 newNode.Parent = this;
                 return true;
             }
         }
     }
     return false;
 }
Пример #23
0
 public void Visit(AstNodeList node)
 {
     // shoudn't get here
     DebugEx.Fail("shouldn't get here");
 }
Пример #24
0
        //---------------------------------------------------------------------------------------
        // ParseLeftHandSideExpression
        //
        //  LeftHandSideExpression :
        //    PrimaryExpression Accessor  |
        //    'new' LeftHandSideExpression |
        //    FunctionExpression
        //
        //  PrimaryExpression :
        //    'this' |
        //    Identifier |
        //    Literal |
        //    '(' Expression ')'
        //
        //  FunctionExpression :
        //    'function' OptionalFuncName '(' FormalParameterList ')' { FunctionBody }
        //
        //  OptionalFuncName :
        //    <empty> |
        //    Identifier
        //---------------------------------------------------------------------------------------
        private AstNode ParseLeftHandSideExpression(bool isMinus)
        {
            AstNode ast = null;
            bool isFunction = false;
            List<Context> newContexts = null;

            TryItAgain:

            // new expression
            while (JSToken.New == m_currentToken.Token)
            {
                if (null == newContexts)
                    newContexts = new List<Context>(4);
                newContexts.Add(m_currentToken.Clone());
                GetNextToken();
            }
            JSToken token = m_currentToken.Token;
            switch (token)
            {
                // primary expression
                case JSToken.Identifier:
                    ast = new Lookup(m_scanner.GetIdentifier(), m_currentToken.Clone(), this);
                    break;

                case JSToken.ConditionalCommentStart:
                    // skip past the start to the next token
                    GetNextToken();
                    if (m_currentToken.Token == JSToken.PreprocessorConstant)
                    {
                        // we have /*@id
                        ast = new ConstantWrapperPP(m_currentToken.Code, true, m_currentToken.Clone(), this);
                        GetNextToken();

                        if (m_currentToken.Token == JSToken.ConditionalCommentEnd)
                        {
                            // skip past the closing comment
                            GetNextToken();
                        }
                        else
                        {
                            // we ONLY support /*@id@*/ in expressions right now. If there's not
                            // a closing comment after the ID, then we don't support it.
                            // throw an error, skip to the end of the comment, then ignore it and start
                            // looking for the next token.
                            m_currentToken.HandleError(JSError.ConditionalCompilationTooComplex);

                            // skip to end of conditional comment
                            while (m_currentToken.Token != JSToken.EndOfFile && m_currentToken.Token != JSToken.ConditionalCommentEnd)
                            {
                                GetNextToken();
                            }
                            GetNextToken();
                            goto TryItAgain;
                        }
                    }
                    else if (m_currentToken.Token == JSToken.ConditionalCommentEnd)
                    {
                        // empty conditional comment! Ignore.
                        GetNextToken();
                        goto TryItAgain;
                    }
                    else
                    {
                        // we DON'T have "/*@IDENT". We only support "/*@IDENT @*/", so since this isn't
                        // and id, throw the error, skip to the end of the comment, and ignore it
                        // by looping back and looking for the NEXT token.
                        m_currentToken.HandleError(JSError.ConditionalCompilationTooComplex);

                        // skip to end of conditional comment
                        while (m_currentToken.Token != JSToken.EndOfFile && m_currentToken.Token != JSToken.ConditionalCommentEnd)
                        {
                            GetNextToken();
                        }
                        GetNextToken();
                        goto TryItAgain;
                    }
                    break;

                case JSToken.This:
                    ast = new ThisLiteral(m_currentToken.Clone(), this);
                    break;

                case JSToken.StringLiteral:
                    ast = new ConstantWrapper(m_scanner.StringLiteral, PrimitiveType.String, m_currentToken.Clone(), this);
                    break;

                case JSToken.IntegerLiteral:
                case JSToken.NumericLiteral:
                    {
                        Context numericContext = m_currentToken.Clone();
                        double doubleValue;
                        if (ConvertNumericLiteralToDouble(m_currentToken.Code, (token == JSToken.IntegerLiteral), out doubleValue))
                        {
                            // conversion worked fine
                            // check for some boundary conditions
                            if (doubleValue == double.MaxValue)
                            {
                                ReportError(JSError.NumericMaximum, numericContext, true);
                            }
                            else if (isMinus && -doubleValue == double.MinValue)
                            {
                                ReportError(JSError.NumericMinimum, numericContext, true);
                            }

                            // create the constant wrapper from the value
                            ast = new ConstantWrapper(doubleValue, PrimitiveType.Number, numericContext, this);
                        }
                        else
                        {
                            // check to see if we went overflow
                            if (double.IsInfinity(doubleValue))
                            {
                                ReportError(JSError.NumericOverflow, numericContext, true);
                            }

                            // regardless, we're going to create a special constant wrapper
                            // that simply echos the input as-is
                            ast = new ConstantWrapper(m_currentToken.Code, PrimitiveType.Other, numericContext, this);
                        }
                        break;
                    }

                case JSToken.True:
                    ast = new ConstantWrapper(true, PrimitiveType.Boolean, m_currentToken.Clone(), this);
                    break;

                case JSToken.False:
                    ast = new ConstantWrapper(false, PrimitiveType.Boolean, m_currentToken.Clone(), this);
                    break;

                case JSToken.Null:
                    ast = new ConstantWrapper(null, PrimitiveType.Null, m_currentToken.Clone(), this);
                    break;

                case JSToken.PreprocessorConstant:
                    ast = new ConstantWrapperPP(m_currentToken.Code, false, m_currentToken.Clone(), this);
                    break;

                case JSToken.DivideAssign:
                // normally this token is not allowed on the left-hand side of an expression.
                // BUT, this might be the start of a regular expression that begins with an equals sign!
                // we need to test to see if we can parse a regular expression, and if not, THEN
                // we can fail the parse.

                case JSToken.Divide:
                    // could it be a regexp?
                    String source = m_scanner.ScanRegExp();
                    if (source != null)
                    {
                        // parse the flags (if any)
                        String flags = m_scanner.ScanRegExpFlags();
                        // create the literal
                        ast = new RegExpLiteral(source, flags, m_currentToken.Clone(), this);
                        break;
                    }
                    goto default;

                // expression
                case JSToken.LeftParenthesis:
                    {
                        // save the current context reference
                        Context openParenContext = m_currentToken.Clone();
                        GetNextToken();
                        m_noSkipTokenSet.Add(NoSkipTokenSet.s_ParenExpressionNoSkipToken);
                        try
                        {
                            // parse an expression
                            ast = ParseExpression();
                            
                            // update the expression's context with the context of the open paren
                            ast.Context.UpdateWith(openParenContext);
                            if (JSToken.RightParenthesis != m_currentToken.Token)
                            {
                                ReportError(JSError.NoRightParenthesis);
                            }
                            else
                            {
                                // add the closing paren to the expression context
                                ast.Context.UpdateWith(m_currentToken);
                            }
                        }
                        catch (RecoveryTokenException exc)
                        {
                            if (IndexOfToken(NoSkipTokenSet.s_ParenExpressionNoSkipToken, exc) == -1)
                                throw;
                            else
                                ast = exc._partiallyComputedNode;
                        }
                        finally
                        {
                            m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ParenExpressionNoSkipToken);
                        }
                        if (ast == null) //this can only happen when catching the exception and nothing was sent up by the caller
                            SkipTokensAndThrow();
                    }
                    break;

                // array initializer
                case JSToken.LeftBracket:
                    Context listCtx = m_currentToken.Clone();
                    AstNodeList list = new AstNodeList(m_currentToken.Clone(), this);
                    GetNextToken();
                    while (JSToken.RightBracket != m_currentToken.Token)
                    {
                        if (JSToken.Comma != m_currentToken.Token)
                        {
                            m_noSkipTokenSet.Add(NoSkipTokenSet.s_ArrayInitNoSkipTokenSet);
                            try
                            {
                                list.Append(ParseExpression(true));
                                if (JSToken.Comma != m_currentToken.Token)
                                {
                                    if (JSToken.RightBracket != m_currentToken.Token)
                                        ReportError(JSError.NoRightBracket);
                                    break;
                                }
                                else
                                {
                                    // we have a comma -- skip it
                                    GetNextToken();

                                    // if the next token is the closing brackets, then we need to
                                    // add a missing value to the array because we end in a comma and
                                    // we need to keep it for cross-platform compat.
                                    // TECHNICALLY, that puts an extra item into the array for most modern browsers, but not ALL.
                                    if (m_currentToken.Token == JSToken.RightBracket)
                                    {
                                        list.Append(new ConstantWrapper(Missing.Value, PrimitiveType.Other, m_currentToken.Clone(), this));
                                    }
                                }
                            }
                            catch (RecoveryTokenException exc)
                            {
                                if (exc._partiallyComputedNode != null)
                                    list.Append(exc._partiallyComputedNode);
                                if (IndexOfToken(NoSkipTokenSet.s_ArrayInitNoSkipTokenSet, exc) == -1)
                                {
                                    listCtx.UpdateWith(CurrentPositionContext());
                                    exc._partiallyComputedNode = new ArrayLiteral(listCtx, this, list);
                                    throw;
                                }
                                else
                                {
                                    if (JSToken.RightBracket == m_currentToken.Token)
                                        break;
                                }
                            }
                            finally
                            {
                                m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ArrayInitNoSkipTokenSet);
                            }
                        }
                        else
                        {
                            // comma -- missing array item in the list
                            list.Append(new ConstantWrapper(Missing.Value, PrimitiveType.Other, m_currentToken.Clone(), this));

                            // skip over the comma
                            GetNextToken();

                            // if the next token is the closing brace, then we end with a comma -- and we need to
                            // add ANOTHER missing value to make sure this last comma doesn't get left off.
                            // TECHNICALLY, that puts an extra item into the array for most modern browsers, but not ALL.
                            if (m_currentToken.Token == JSToken.RightBracket)
                            {
                                list.Append(new ConstantWrapper(Missing.Value, PrimitiveType.Other, m_currentToken.Clone(), this));
                            }
                        }
                    }
                    listCtx.UpdateWith(m_currentToken);
                    ast = new ArrayLiteral(listCtx, this, list);
                    break;

                // object initializer
                case JSToken.LeftCurly:
                    Context objCtx = m_currentToken.Clone();
                    GetNextToken();

                    // we're going to keep the keys and values in separate lists, but make sure
                    // that the indexes correlate (keyList[n] is associated with valueList[n])
                    List<ObjectLiteralField> keyList = new List<ObjectLiteralField>();
                    List<AstNode> valueList = new List<AstNode>();

                    if (JSToken.RightCurly != m_currentToken.Token)
                    {
                        for (; ; )
                        {
                            ObjectLiteralField field = null;
                            AstNode value = null;
                            bool getterSetter = false;
                            string ident;

                            switch (m_currentToken.Token)
                            {
                                case JSToken.Identifier:
                                    field = new ObjectLiteralField(m_scanner.GetIdentifier(), PrimitiveType.String, m_currentToken.Clone(), this);
                                    break;

                                case JSToken.StringLiteral:
                                    field = new ObjectLiteralField(m_scanner.StringLiteral, PrimitiveType.String, m_currentToken.Clone(), this);
                                    break;

                                case JSToken.IntegerLiteral:
                                case JSToken.NumericLiteral:
                                    {
                                        double doubleValue;
                                        if (ConvertNumericLiteralToDouble(m_currentToken.Code, (m_currentToken.Token == JSToken.IntegerLiteral), out doubleValue))
                                        {
                                            // conversion worked fine
                                            field = new ObjectLiteralField(
                                              doubleValue,
                                              PrimitiveType.Number,
                                              m_currentToken.Clone(),
                                              this
                                              );
                                        }
                                        else
                                        {
                                            // something went wrong and we're not sure the string representation in the source is 
                                            // going to convert to a numeric value well
                                            if (double.IsInfinity(doubleValue))
                                            {
                                                ReportError(JSError.NumericOverflow, m_currentToken.Clone(), true);
                                            }

                                            // use the source as the field name, not the numeric value
                                            field = new ObjectLiteralField(
                                                m_currentToken.Code,
                                                PrimitiveType.Other,
                                                m_currentToken.Clone(),
                                                this);
                                        }
                                        break;
                                    }

                                case JSToken.Get:
                                case JSToken.Set:
                                    if (m_scanner.PeekToken() == JSToken.Colon)
                                    {
                                        // the field is either "get" or "set" and isn't the special Mozilla getter/setter
                                        field = new ObjectLiteralField(m_currentToken.Code, PrimitiveType.String, m_currentToken.Clone(), this);
                                    }
                                    else
                                    {
                                        // ecma-script get/set property construct
                                        getterSetter = true;
                                        bool isGet = (m_currentToken.Token == JSToken.Get);
                                        value = ParseFunction(
                                          (JSToken.Get == m_currentToken.Token ? FunctionType.Getter : FunctionType.Setter),
                                          m_currentToken.Clone()
                                          );
                                        FunctionObject funcExpr = value as FunctionObject;
                                        if (funcExpr != null)
                                        {
                                            // getter/setter is just the literal name with a get/set flag
                                            field = new GetterSetter(
                                              funcExpr.Name,
                                              isGet,
                                              funcExpr.IdContext.Clone(),
                                              this
                                              );
                                        }
                                        else
                                        {
                                            ReportError(JSError.FunctionExpressionExpected);
                                        }
                                    }
                                    break;

                                default:
                                    // NOT: identifier, string, number, or getter/setter.
                                    // see if it's a token that COULD be an identifier.
                                    ident = JSKeyword.CanBeIdentifier(m_currentToken.Token);
                                    if (ident != null)
                                    {
                                        // don't throw a warning -- it's okay to have a keyword that
                                        // can be an identifier here.
                                        field = new ObjectLiteralField(ident, PrimitiveType.String, m_currentToken.Clone(), this);
                                    }
                                    else
                                    {
                                        ReportError(JSError.NoMemberIdentifier);
                                        field = new ObjectLiteralField("_#Missing_Field#_" + s_cDummyName++, PrimitiveType.String, CurrentPositionContext(), this);
                                    }
                                    break;
                            }

                            if (field != null)
                            {
                                if (!getterSetter)
                                {
                                    GetNextToken();
                                }

                                m_noSkipTokenSet.Add(NoSkipTokenSet.s_ObjectInitNoSkipTokenSet);
                                try
                                {
                                    if (!getterSetter)
                                    {
                                        // get the value
                                        if (JSToken.Colon != m_currentToken.Token)
                                        {
                                            ReportError(JSError.NoColon, true);
                                            value = ParseExpression(true);
                                        }
                                        else
                                        {
                                            GetNextToken();
                                            value = ParseExpression(true);
                                        }
                                    }

                                    // put the pair into the list of fields
                                    keyList.Add(field);
                                    valueList.Add(value);

                                    if (JSToken.RightCurly == m_currentToken.Token)
                                        break;
                                    else
                                    {
                                        if (JSToken.Comma == m_currentToken.Token)
                                        {
                                            // skip the comma
                                            GetNextToken();

                                            // if the next token is the right-curly brace, then we ended 
                                            // the list with a comma, which is perfectly fine
                                            if (m_currentToken.Token == JSToken.RightCurly)
                                            {
                                                break;
                                            }
                                        }
                                        else
                                        {
                                            if (m_scanner.GotEndOfLine)
                                            {
                                                ReportError(JSError.NoRightCurly);
                                            }
                                            else
                                                ReportError(JSError.NoComma, true);
                                            SkipTokensAndThrow();
                                        }
                                    }
                                }
                                catch (RecoveryTokenException exc)
                                {
                                    if (exc._partiallyComputedNode != null)
                                    {
                                        // the problem was in ParseExpression trying to determine value
                                        value = exc._partiallyComputedNode;
                                        keyList.Add(field);
                                        valueList.Add(value);
                                    }
                                    if (IndexOfToken(NoSkipTokenSet.s_ObjectInitNoSkipTokenSet, exc) == -1)
                                    {
                                        exc._partiallyComputedNode = new ObjectLiteral(objCtx, this, keyList.ToArray(), valueList.ToArray());
                                        throw;
                                    }
                                    else
                                    {
                                        if (JSToken.Comma == m_currentToken.Token)
                                            GetNextToken();
                                        if (JSToken.RightCurly == m_currentToken.Token)
                                            break;
                                    }
                                }
                                finally
                                {
                                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ObjectInitNoSkipTokenSet);
                                }
                            }
                        }
                    }
                    objCtx.UpdateWith(m_currentToken);
                    ast = new ObjectLiteral(objCtx, this, keyList.ToArray(), valueList.ToArray());
                    break;

                // function expression
                case JSToken.Function:
                    ast = ParseFunction(FunctionType.Expression, m_currentToken.Clone());
                    isFunction = true;
                    break;

                case JSToken.AspNetBlock:
                    ast = ParseAspNetBlock(consumeSemicolonIfPossible: false);
                    break;

                default:
                    string identifier = JSKeyword.CanBeIdentifier(m_currentToken.Token);
                    if (null != identifier)
                    {
                        ast = new Lookup(identifier, m_currentToken.Clone(), this);
                    }
                    else
                    {
                        ReportError(JSError.ExpressionExpected);
                        SkipTokensAndThrow();
                    }
                    break;
            }

            // can be a CallExpression, that is, followed by '.' or '(' or '['
            if (!isFunction)
                GetNextToken();

            return MemberExpression(ast, newContexts);
        }