public override void Visit(Lookup node)
 {
     // only lookups need to be detached.
     if (node != null)
     {
         // if the node's vairable field is set, remove it from the
         // field's list of references.
         var variableField = node.VariableField;
         if (variableField != null)
         {
             variableField.References.Remove(node);
         }
     }
 }
 internal UndefinedReferenceException(Lookup lookup, Context context)
 {
     m_lookup = lookup;
     m_name = lookup.Name;
     m_type = lookup.RefType;
     m_context = context;
 }
        public void MarkSegment(AstNode node, int startLine, int startColumn, string name, Context context)
        {
            if (node == null || string.IsNullOrEmpty(name))
            {
                return;
            }

            // see if this is within a function object node, 
            // AND if this segment has the same name as the function name
            // AND this context isn't the same as the entire function context.
            // this should only be true for the function NAME segment.
            var functionObject = node as FunctionObject;
            if (functionObject != null 
                && string.CompareOrdinal(name, functionObject.Name) == 0
                && context != functionObject.Context)
            {
                // it does -- so this is the segment that corresponds to the function object's name, which
                // for this format we want to output a separate segment for. It used to be its own Lookup
                // node child of the function object, so we need to create a fake node here, start a new 
                // symbol from it, end the symbol, then write it.
                var fakeLookup = new Lookup(context, functionObject.Parser) { Name = name };
                var nameSymbol = JavaScriptSymbol.StartNew(fakeLookup, startLine, startColumn, GetSourceFileIndex(functionObject.Context.Document.FileContext));

                // the name will never end on a different line -- it's a single unbreakable token. The length is just
                // the length of the name, so add that number to the column start. And the parent context is the function
                // name (again)
                nameSymbol.End(startLine, startColumn + name.Length, name);
                nameSymbol.WriteTo(m_writer);
            }
        }
 public void Visit(Lookup node)
 {
     // invalid! ignore
     IsValid = false;
 }
        private static int RelocateVar(Block block, int insertAt, Var varStatement)
        {
            // if the var statement is at the next position to insert, then we don't need
            // to do anything.
            if (block[insertAt] != varStatement)
            {
                // check to see if the current position is a var and we are the NEXT statement.
                // if that's the case, we don't need to break out the initializer, just append all the
                // vardecls as-is to the current position.
                var existingVar = block[insertAt] as Var;
                if (existingVar != null && block[insertAt + 1] == varStatement)
                {
                    // just append our vardecls to the insertion point, then delete our statement
                    existingVar.Append(varStatement);
                    block.RemoveAt(insertAt + 1);
                }
                else
                {
                    // iterate through the decls and count how many have initializers
                    var initializerCount = 0;
                    for (var ndx = 0; ndx < varStatement.Count; ++ndx)
                    {
                        if (varStatement[ndx].Initializer != null)
                        {
                            ++initializerCount;
                        }
                    }

                    // if there are more than two decls with initializers, then we won't actually
                    // be gaining anything by moving the var to the top. We'll get rid of the four
                    // bytes for the "var ", but we'll be adding two bytes for the name and comma
                    // because name=init will still need to remain behind.
                    if (initializerCount <= 2)
                    {
                        // first iterate through all the declarations in the var statement,
                        // constructing an expression statement that is made up of assignment
                        // operators for each of the declarations that have initializers (if any)
                        // and removing all the initializers
                        var assignments = new List<AstNode>();
                        for (var ndx = 0; ndx < varStatement.Count; ++ndx)
                        {
                            var varDecl = varStatement[ndx];
                            if (varDecl.Initializer != null)
                            {
                                if (varDecl.IsCCSpecialCase)
                                {
                                    // create a vardecl with the same name and no initializer
                                    var copyDecl = new VariableDeclaration(varDecl.Context, varDecl.Parser)
                                        {
                                            Identifier = varDecl.Identifier,
                                            NameContext = varDecl.VariableField.OriginalContext,
                                            VariableField = varDecl.VariableField
                                        };

                                    // replace the special vardecl with the copy
                                    varStatement[ndx] = copyDecl;

                                    // add the original vardecl to the list of "assignments"
                                    assignments.Add(varDecl);

                                    // add the new decl to the field's declaration list, and remove the old one
                                    // because we're going to change that to an assignment.
                                    varDecl.VariableField.Declarations.Add(copyDecl);
                                    varDecl.VariableField.Declarations.Remove(varDecl);
                                }
                                else
                                {
                                    // hold on to the object so we don't lose it to the GC
                                    var initializer = varDecl.Initializer;

                                    // remove it from the vardecl
                                    varDecl.Initializer = null;

                                    // create an assignment operator for a lookup to the name
                                    // as the left, and the initializer as the right, and add it to the list
                                    var lookup = new Lookup(varDecl.VariableField.OriginalContext, varDecl.Parser)
                                        {
                                            Name = varDecl.Identifier,
                                            VariableField = varDecl.VariableField,
                                        };
                                    assignments.Add(new BinaryOperator(varDecl.Context, varDecl.Parser)
                                        {
                                            Operand1 = lookup,
                                            Operand2 = initializer,
                                            OperatorToken = JSToken.Assign,
                                            OperatorContext = varDecl.AssignContext
                                        });

                                    // add the new lookup to the field's references
                                    varDecl.VariableField.References.Add(lookup);
                                }
                            }
                        }

                        // now if there were any initializers...
                        if (assignments.Count > 0)
                        {
                            // we want to create one big expression from all the assignments and replace the
                            // var statement with the assignment(s) expression. Start at position n=1 and create
                            // a binary operator of n-1 as the left, n as the right, and using a comma operator.
                            var expression = assignments[0];
                            for (var ndx = 1; ndx < assignments.Count; ++ndx)
                            {
                                expression = CommaOperator.CombineWithComma(null, expression.Parser, expression, assignments[ndx]);
                            }

                            // replace the var with the expression.
                            // we still have a pointer to the var, so we can insert it back into the proper
                            // place next.
                            varStatement.Parent.ReplaceChild(varStatement, expression);
                        }
                        else
                        {
                            // no initializers.
                            // if the parent is a for-in statement...
                            var forInParent = varStatement.Parent as ForIn;
                            if (forInParent != null)
                            {
                                // we want to replace the var statement with a lookup for the var
                                // there should be only one vardecl
                                var varDecl = varStatement[0];
                                var lookup = new Lookup(varDecl.VariableField.OriginalContext, varStatement.Parser)
                                    {
                                        Name = varDecl.Identifier,
                                        VariableField = varDecl.VariableField
                                    };
                                varStatement.Parent.ReplaceChild(varStatement, lookup);
                                varDecl.VariableField.References.Add(lookup);
                            }
                            else
                            {
                                // just remove the var statement altogether
                                varStatement.Parent.ReplaceChild(varStatement, null);
                            }
                        }

                        // if the statement at the insertion point is a var-statement already,
                        // then we just need to append our vardecls to it. Otherwise we'll insert our
                        // var statement at the right point
                        if (existingVar != null)
                        {
                            // append the varstatement we want to move to the existing var, which will
                            // transfer all the vardecls to it.
                            existingVar.Append(varStatement);
                        }
                        else
                        {
                            // move the var to the insert point, incrementing the position or next time
                            block.Insert(insertAt, varStatement);
                        }
                    }
                }
            }

            return insertAt;
        }
Beispiel #6
0
 internal void ReportUndefined(Lookup lookup)
 {
     UndefinedReferenceException ex = new UndefinedReferenceException(lookup, this);
     Document.ReportUndefined(ex);
 }
        private static void ResolveLookup(ActivationObject scope, Lookup lookup, CodeSettings settings)
        {
            // resolve lookup via the lexical scope
            lookup.VariableField = scope.FindReference(lookup.Name);
            if (lookup.VariableField.FieldType == FieldType.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 UnaryOperator;
                    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 CallNode;
                        var isFunction = callNode != null && callNode.Function == lookup;
                        lookup.Context.HandleError((isFunction ? JSError.UndeclaredFunction : JSError.UndeclaredVariable), false);
                    }
                }
            }
            else if (lookup.VariableField.FieldType == FieldType.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 Member;
                    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 CallNode;
                        if (callNode != null && callNode.InBrackets
                            && callNode.Arguments.Count == 1
                            && callNode.Arguments[0] is ConstantWrapper
                            && callNode.Arguments[0].FindPrimitiveType() == PrimitiveType.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 != EvalTreatment.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 CallNode;
                    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;
        }
        public void Visit(Lookup node)
        {
            if (node != null)
            {
                node.Index = NextOrderIndex;

                // add the lookup node to the current lexical scope, because
                // that's the starting point for this node's lookup resolution.
                CurrentLexicalScope.ScopeLookups.Add(node);
            }
        }
 public void Visit(Lookup node)
 {
     // we're good
 }
Beispiel #10
0
        private AstNode ParseLeftHandSideExpression(bool isMinus)
        {
            AstNode ast = null;
            bool skipToken = true;
            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_currentToken.Clone(), this)
                        {
                            Name = m_scanner.Identifier
                        };
                    break;

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

                        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.
                            CCTooComplicated(null);
                            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.StringLiteralValue, PrimitiveType.String, m_currentToken.Clone(), this)
                        {
                            MayHaveIssues = m_scanner.LiteralHasIssues
                        };
                    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
                            var mayHaveIssues = m_scanner.LiteralHasIssues;
                            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)
                                {
                                    MayHaveIssues = mayHaveIssues
                                };
                        }
                        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)
                            {
                                MayHaveIssues = true
                            };
                        }
                        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.ConditionalCompilationVariable:
                    ast = new ConstantWrapperPP(m_currentToken.Clone(), this)
                        {
                            VarName = m_currentToken.Code,
                            ForceComments = false
                        };
                    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(m_currentToken.Clone(), this)
                            {
                                Pattern = source,
                                PatternSwitches = flags
                            };
                        break;
                    }
                    goto default;

                // expression
                case JSToken.LeftParenthesis:
                    {
                        var groupingOp = new GroupingOperator(m_currentToken.Clone(), this);
                        ast = groupingOp;
                        GetNextToken();
                        m_noSkipTokenSet.Add(NoSkipTokenSet.s_ParenExpressionNoSkipToken);
                        try
                        {
                            // parse an expression
                            groupingOp.Operand = ParseExpression();
                            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
                                groupingOp.Operand = exc._partiallyComputedNode;
                        }
                        finally
                        {
                            m_noSkipTokenSet.Remove(NoSkipTokenSet.s_ParenExpressionNoSkipToken);
                        }
                    }
                    break;

                // array initializer
                case JSToken.LeftBracket:
                    Context listCtx = m_currentToken.Clone();
                    GetNextToken();
                    AstNodeList list = new AstNodeList(CurrentPositionContext(), this);
                    while (JSToken.RightBracket != m_currentToken.Token)
                    {
                        if (JSToken.Comma != m_currentToken.Token)
                        {
                            m_noSkipTokenSet.Add(NoSkipTokenSet.s_ArrayInitNoSkipTokenSet);
                            try
                            {
                                var expression = ParseExpression(true);
                                list.Append(expression);
                                if (JSToken.Comma != m_currentToken.Token)
                                {
                                    if (JSToken.RightBracket != m_currentToken.Token)
                                    {
                                        ReportError(JSError.NoRightBracket);
                                    }

                                    break;
                                }
                                else
                                {
                                    // we have a comma -- skip it after adding it as a terminator
                                    // on the previous expression
                                    expression.IfNotNull(e => e.TerminatingContext = m_currentToken.Clone());
                                    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)
                                        {
                                            Elements = 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)
                                {
                                    TerminatingContext = m_currentToken.Clone()
                                });

                            // 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)
                        {
                            Elements = list
                        };
                    break;

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

                    var propertyList = new AstNodeList(CurrentPositionContext(), this);

                    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.Identifier, PrimitiveType.String, m_currentToken.Clone(), this);
                                    break;

                                case JSToken.StringLiteral:
                                    field = new ObjectLiteralField(m_scanner.StringLiteralValue, PrimitiveType.String, m_currentToken.Clone(), this)
                                        {
                                            MayHaveIssues = m_scanner.LiteralHasIssues
                                        };
                                    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 (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 token, string, number, or getter/setter.
                                    // see if it's a token that COULD be an identifierName.
                                    ident = m_scanner.Identifier;
                                    if (JSScanner.IsValidIdentifier(ident))
                                    {
                                        // BY THE SPEC, if it's a valid identifierName -- which includes reserved words -- then it's
                                        // okay for object literal syntax. However, reserved words here won't work in all browsers,
                                        // so if it is a reserved word, let's throw a low-sev cross-browser warning on the code.
                                        if (JSKeyword.CanBeIdentifier(m_currentToken.Token) == null)
                                        {
                                            ReportError(JSError.ObjectLiteralKeyword, m_currentToken.Clone(), true);
                                        }

                                        field = new ObjectLiteralField(ident, PrimitiveType.String, m_currentToken.Clone(), this);
                                    }
                                    else
                                    {
                                        // throw an error but use it anyway, since that's what the developer has going on
                                        ReportError(JSError.NoMemberIdentifier, m_currentToken.Clone(), true);
                                        field = new ObjectLiteralField(m_currentToken.Code, PrimitiveType.String, m_currentToken.Clone(), 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
                                        {
                                            field.ColonContext = m_currentToken.Clone();
                                            GetNextToken();
                                            value = ParseExpression(true);
                                        }
                                    }

                                    // put the pair into the list of fields
                                    var propCtx = field.Context.Clone().CombineWith(value.IfNotNull(v => v.Context));
                                    var property = new ObjectLiteralProperty(propCtx, this)
                                        {
                                            Name = field,
                                            Value = value
                                        };

                                    propertyList.Append(property);

                                    if (JSToken.RightCurly == m_currentToken.Token)
                                    {
                                        break;
                                    }
                                    else
                                    {
                                        if (JSToken.Comma == m_currentToken.Token)
                                        {
                                            // skip the comma after adding it to the property as a terminating context
                                            property.IfNotNull(p => p.TerminatingContext = m_currentToken.Clone());
                                            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_foundEndOfLine)
                                            {
                                                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;

                                        var propCtx = field.Context.Clone().CombineWith(value.IfNotNull(v => v.Context));
                                        var property = new ObjectLiteralProperty(propCtx, this)
                                        {
                                            Name = field,
                                            Value = value
                                        };

                                        propertyList.Append(property);
                                    }

                                    if (IndexOfToken(NoSkipTokenSet.s_ObjectInitNoSkipTokenSet, exc) == -1)
                                    {
                                        exc._partiallyComputedNode = new ObjectLiteral(objCtx, this)
                                            {
                                                Properties = propertyList
                                            };
                                        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)
                        {
                            Properties = propertyList
                        };
                    break;

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

                case JSToken.AspNetBlock:
                    ast = new AspNetBlockNode(m_currentToken.Clone(), this)
                        {
                            AspNetBlockText = m_currentToken.Code
                        };
                    break;

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

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

            return MemberExpression(ast, newContexts);
        }
Beispiel #11
0
        private FunctionObject ParseFunction(FunctionType functionType, Context fncCtx)
        {
            Lookup name = null;
            AstNodeList formalParameters = null;
            Block body = null;
            bool inExpression = (functionType == FunctionType.Expression);
            Context paramsContext = null;

            GetNextToken();

            // get the function name or make an anonymous function if in expression "position"
            if (JSToken.Identifier == m_currentToken.Token)
            {
                name = new Lookup(m_currentToken.Clone(), this)
                    {
                        Name = m_scanner.Identifier
                    };
                GetNextToken();
            }
            else
            {
                string identifier = JSKeyword.CanBeIdentifier(m_currentToken.Token);
                if (null != identifier)
                {
                    name = new Lookup(m_currentToken.Clone(), this)
                        {
                            Name = identifier
                        };
                    GetNextToken();
                }
                else
                {
                    if (!inExpression)
                    {
                        // if this isn't a function expression, then we need to throw an error because
                        // function DECLARATIONS always need a valid identifier name
                        ReportError(JSError.NoIdentifier, m_currentToken.Clone(), true);

                        // BUT if the current token is a left paren, we don't want to use it as the name.
                        // (fix for issue #14152)
                        if (m_currentToken.Token != JSToken.LeftParenthesis
                            && m_currentToken.Token != JSToken.LeftCurly)
                        {
                            identifier = m_currentToken.Code;
                            name = new Lookup(CurrentPositionContext(), this)
                                {
                                    Name = identifier
                                };
                            GetNextToken();
                        }
                    }
                }
            }

            // make a new state and save the old one
            List<BlockType> blockType = m_blockType;
            m_blockType = new List<BlockType>(16);
            Dictionary<string, LabelInfo> labelTable = m_labelTable;
            m_labelTable = new Dictionary<string, LabelInfo>();

            try
            {
                // get the formal parameters
                if (JSToken.LeftParenthesis != m_currentToken.Token)
                {
                    // we expect a left paren at this point for standard cross-browser support.
                    // BUT -- some versions of IE allow an object property expression to be a function name, like window.onclick. 
                    // we still want to throw the error, because it syntax errors on most browsers, but we still want to
                    // be able to parse it and return the intended results. 
                    // Skip to the open paren and use whatever is in-between as the function name. Doesn't matter that it's 
                    // an invalid identifier; it won't be accessible as a valid field anyway.
                    bool expandedIndentifier = false;
                    while (m_currentToken.Token != JSToken.LeftParenthesis
                        && m_currentToken.Token != JSToken.LeftCurly
                        && m_currentToken.Token != JSToken.Semicolon
                        && m_currentToken.Token != JSToken.EndOfFile)
                    {
                        name.Context.UpdateWith(m_currentToken);
                        GetNextToken();
                        expandedIndentifier = true;
                    }

                    // if we actually expanded the identifier context, then we want to report that
                    // the function name needs to be an identifier. Otherwise we didn't expand the 
                    // name, so just report that we expected an open paren at this point.
                    if (expandedIndentifier)
                    {
                        name.Name = name.Context.Code;
                        name.Context.HandleError(JSError.FunctionNameMustBeIdentifier, false);
                    }
                    else
                    {
                        ReportError(JSError.NoLeftParenthesis, true);
                    }
                }

                if (m_currentToken.Token == JSToken.LeftParenthesis)
                {
                    // create the parameter list
                    formalParameters = new AstNodeList(m_currentToken.Clone(), this);
                    paramsContext = m_currentToken.Clone();

                    // skip the open paren
                    GetNextToken();

                    // create the list of arguments and update the context
                    while (JSToken.RightParenthesis != m_currentToken.Token)
                    {
                        String id = null;
                        m_noSkipTokenSet.Add(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet);
                        try
                        {
                            ParameterDeclaration paramDecl = null;
                            if (JSToken.Identifier != m_currentToken.Token && (id = JSKeyword.CanBeIdentifier(m_currentToken.Token)) == null)
                            {
                                if (JSToken.LeftCurly == m_currentToken.Token)
                                {
                                    ReportError(JSError.NoRightParenthesis);
                                    break;
                                }
                                else if (JSToken.Comma == m_currentToken.Token)
                                {
                                    // We're missing an argument (or previous argument was malformed and
                                    // we skipped to the comma.)  Keep trying to parse the argument list --
                                    // we will skip the comma below.
                                    ReportError(JSError.SyntaxError, true);
                                }
                                else
                                {
                                    ReportError(JSError.SyntaxError, true);
                                    SkipTokensAndThrow();
                                }
                            }
                            else
                            {
                                if (null == id)
                                {
                                    id = m_scanner.Identifier;
                                }

                                paramDecl = new ParameterDeclaration(m_currentToken.Clone(), this)
                                    {
                                        Name = id,
                                        Position = formalParameters.Count
                                    };
                                formalParameters.Append(paramDecl);
                                GetNextToken();
                            }

                            // got an arg, it should be either a ',' or ')'
                            if (JSToken.RightParenthesis == m_currentToken.Token)
                            {
                                break;
                            }
                            else if (JSToken.Comma == m_currentToken.Token)
                            {
                                // append the comma context as the terminator for the parameter
                                paramDecl.IfNotNull(p => p.TerminatingContext = m_currentToken.Clone());
                            }
                            else
                            {
                                // deal with error in some "intelligent" way
                                if (JSToken.LeftCurly == m_currentToken.Token)
                                {
                                    ReportError(JSError.NoRightParenthesis);
                                    break;
                                }
                                else
                                {
                                    if (JSToken.Identifier == m_currentToken.Token)
                                    {
                                        // it's possible that the guy was writing the type in C/C++ style (i.e. int x)
                                        ReportError(JSError.NoCommaOrTypeDefinitionError);
                                    }
                                    else
                                        ReportError(JSError.NoComma);
                                }
                            }

                            GetNextToken();
                        }
                        catch (RecoveryTokenException exc)
                        {
                            if (IndexOfToken(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet, exc) == -1)
                                throw;
                        }
                        finally
                        {
                            m_noSkipTokenSet.Remove(NoSkipTokenSet.s_FunctionDeclNoSkipTokenSet);
                        }
                    }

                    fncCtx.UpdateWith(m_currentToken);
                    GetNextToken();
                }

                // read the function body of non-abstract functions.
                if (JSToken.LeftCurly != m_currentToken.Token)
                    ReportError(JSError.NoLeftCurly, true);

                m_blockType.Add(BlockType.Block);
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_BlockNoSkipTokenSet);
                m_noSkipTokenSet.Add(NoSkipTokenSet.s_StartStatementNoSkipTokenSet);
                try
                {
                    // parse the block locally to get the exact end of function
                    body = new Block(m_currentToken.Clone(), this);
                    body.BraceOnNewLine = m_foundEndOfLine;
                    GetNextToken();

                    var possibleDirectivePrologue = true;
                    while (JSToken.RightCurly != m_currentToken.Token)
                    {
                        try
                        {
                            // function body's are SourceElements (Statements + FunctionDeclarations)
                            var statement = ParseStatement(true);
                            if (possibleDirectivePrologue)
                            {
                                var constantWrapper = statement as ConstantWrapper;
                                if (constantWrapper != null && constantWrapper.PrimitiveType == PrimitiveType.String)
                                {
                                    // if it's already a directive prologues, we're good to go
                                    if (!(constantWrapper is DirectivePrologue))
                                    {
                                        // make the statement a directive prologue instead of a constant wrapper
                                        statement = new DirectivePrologue(constantWrapper.Value.ToString(), constantWrapper.Context, constantWrapper.Parser)
                                            {
                                                MayHaveIssues = constantWrapper.MayHaveIssues
                                            };
                                    }
                                }
                                else if (!m_newModule)
                                {
                                    // no longer considering constant wrappers
                                    possibleDirectivePrologue = false;
                                }
                            }
                            else if (m_newModule)
                            {
                                // we scanned into a new module -- we might find directive prologues again
                                possibleDirectivePrologue = true;
                            }

                            // add it to the body
                            body.Append(statement);
                        }
                        catch (RecoveryTokenException exc)
                        {
                            if (exc._partiallyComputedNode != null)
                            {
                                body.Append(exc._partiallyComputedNode);
                            }
                            if (IndexOfToken(NoSkipTokenSet.s_StartStatementNoSkipTokenSet, exc) == -1)
                                throw;
                        }
                    }

                    // make sure any important comments before the closing brace are kept
                    if (m_importantComments.Count > 0
                        && m_settings.PreserveImportantComments
                        && m_settings.IsModificationAllowed(TreeModifications.PreserveImportantComments))
                    {
                        // we have important comments before the EOF. Add the comment(s) to the program.
                        foreach (var importantComment in m_importantComments)
                        {
                            body.Append(new ImportantComment(importantComment, this));
                        }

                        m_importantComments.Clear();
                    }

                    body.Context.UpdateWith(m_currentToken);
                    fncCtx.UpdateWith(m_currentToken);
                }
                catch (EndOfFileException)
                {
                    // if we get an EOF here, we never had a chance to find the closing curly-brace
                    fncCtx.HandleError(JSError.UnclosedFunction, true);
                }
                catch (RecoveryTokenException exc)
                {
                    if (IndexOfToken(NoSkipTokenSet.s_BlockNoSkipTokenSet, exc) == -1)
                    {
                        exc._partiallyComputedNode = new FunctionObject(fncCtx, this)
                            {
                                FunctionType = (inExpression ? FunctionType.Expression : FunctionType.Declaration),
                                IdContext = name.IfNotNull(n => n.Context),
                                Name = name.IfNotNull(n => n.Name),
                                ParameterDeclarations = formalParameters,
                                ParametersContext = paramsContext,
                                Body = body
                            };
                        throw;
                    }
                }
                finally
                {
                    m_blockType.RemoveAt(m_blockType.Count - 1);
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_StartStatementNoSkipTokenSet);
                    m_noSkipTokenSet.Remove(NoSkipTokenSet.s_BlockNoSkipTokenSet);
                }

                GetNextToken();
            }
            finally
            {
                // restore state
                m_blockType = blockType;
                m_labelTable = labelTable;
            }

            return new FunctionObject(fncCtx, this)
                {
                    FunctionType = functionType,
                    IdContext = name.IfNotNull(n => n.Context),
                    Name = name.IfNotNull(n => n.Name),
                    ParameterDeclarations = formalParameters,
                    ParametersContext = paramsContext,
                    Body = body
                };
        }
 public override void Visit(Lookup node)
 {
     // same logic for most nodes
     TypicalHandler(node);
 }
 public void Visit(Lookup node)
 {
     // we are only a match if we are looking for the first part
     if (node != null && m_index == 0)
     {
         // see if the name matches; and if there is a field, it should be a global
         if (string.CompareOrdinal(node.Name, m_parts[0]) == 0
             && (node.VariableField == null || node.VariableField.FieldType == FieldType.UndefinedGlobal 
             || node.VariableField.FieldType == FieldType.Global))
         {
             // match!
             m_isMatch = true;
         }
     }
 }
        public override void Visit(Lookup node)
        {
            if (node != null)
            {
                // figure out if our reference type is a function or a constructor
                if (node.Parent is CallNode)
                {
                    node.RefType = (
                      ((CallNode)(node.Parent)).IsConstructor
                      ? ReferenceType.Constructor
                      : ReferenceType.Function
                      );
                }

                // check the name of the variable for reserved words that aren't allowed
                ActivationObject scope = m_scopeStack.Peek();
                if (JSScanner.IsKeyword(node.Name, scope.UseStrict))
                {
                    node.Context.HandleError(JSError.KeywordUsedAsIdentifier, true);
                }

                // no variable field means ignore it
                if (node.VariableField != null && node.VariableField.FieldType == FieldType.Predefined)
                {
                    // this is a predefined field. If it's Nan or Infinity, we should
                    // replace it with the numeric value in case we need to later combine
                    // some literal expressions.
                    if (string.CompareOrdinal(node.Name, "NaN") == 0)
                    {
                        // don't analyze the new ConstantWrapper -- we don't want it to take part in the
                        // duplicate constant combination logic should it be turned on.
                        node.Parent.ReplaceChild(node, new ConstantWrapper(double.NaN, PrimitiveType.Number, node.Context, m_parser));
                    }
                    else if (string.CompareOrdinal(node.Name, "Infinity") == 0)
                    {
                        // don't analyze the new ConstantWrapper -- we don't want it to take part in the
                        // duplicate constant combination logic should it be turned on.
                        node.Parent.ReplaceChild(node, new ConstantWrapper(double.PositiveInfinity, PrimitiveType.Number, node.Context, m_parser));
                    }
                }
            }
        }
        private static void ResolveLookup(ActivationObject scope, Lookup lookup, CodeSettings settings)
        {
            // resolve lookup via the lexical scope
            lookup.VariableField = scope.FindReference(lookup.Name);
            if (lookup.VariableField.FieldType == FieldType.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 UnaryOperator;
                    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 CallNode;
                        var isFunction = callNode != null && callNode.Function == lookup;
                        lookup.Context.HandleError((isFunction ? JSError.UndeclaredFunction : JSError.UndeclaredVariable), false);
                    }
                }
            }
            else if (lookup.VariableField.FieldType == FieldType.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 Member;
                    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 CallNode;
                        if (callNode != null && callNode.InBrackets &&
                            callNode.Arguments.Count == 1 &&
                            callNode.Arguments[0] is ConstantWrapper &&
                            callNode.Arguments[0].FindPrimitiveType() == PrimitiveType.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 != EvalTreatment.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 CallNode;
                    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;
        }