Exemple #1
0
        internal static Expression Parse(ParseInfo state, ref int index, FunctionKind kind)
        {
            string code     = state.Code;
            int    position = index;

            switch (kind)
            {
            case FunctionKind.AnonymousFunction:
            case FunctionKind.AnonymousGenerator:
            {
                break;
            }

            case FunctionKind.Function:
            {
                if (!Parser.Validate(code, "function", ref position))
                {
                    return(null);
                }

                if (code[position] == '*')
                {
                    kind = FunctionKind.Generator;
                    position++;
                }
                else if ((code[position] != '(') && (!Tools.IsWhiteSpace(code[position])))
                {
                    return(null);
                }

                break;
            }

            case FunctionKind.Getter:
            {
                if (!Parser.Validate(code, "get ", ref position))
                {
                    return(null);
                }

                break;
            }

            case FunctionKind.Setter:
            {
                if (!Parser.Validate(code, "set ", ref position))
                {
                    return(null);
                }

                break;
            }

            case FunctionKind.MethodGenerator:
            case FunctionKind.Method:
            {
                if (code[position] == '*')
                {
                    kind = FunctionKind.MethodGenerator;
                    position++;
                }
                else if (kind == FunctionKind.MethodGenerator)
                {
                    throw new ArgumentException("mode");
                }

                break;
            }

            case FunctionKind.Arrow:
            {
                break;
            }

            case FunctionKind.AsyncFunction:
            {
                if (!Parser.Validate(code, "async", ref position))
                {
                    return(null);
                }

                Tools.SkipSpaces(code, ref position);

                if (!Parser.Validate(code, "function", ref position))
                {
                    return(null);
                }

                break;
            }

            default:
                throw new NotImplementedException(kind.ToString());
            }

            Tools.SkipSpaces(state.Code, ref position);

            var       parameters                = new List <ParameterDescriptor>();
            CodeBlock body                      = null;
            string    name                      = null;
            bool      arrowWithSunglePrm        = false;
            int       nameStartPos              = 0;
            bool      containsDestructuringPrms = false;

            if (kind != FunctionKind.Arrow)
            {
                if (code[position] != '(')
                {
                    nameStartPos = position;
                    if (Parser.ValidateName(code, ref position, false, true, state.strict))
                    {
                        name = Tools.Unescape(code.Substring(nameStartPos, position - nameStartPos), state.strict);
                    }
                    else if ((kind == FunctionKind.Getter || kind == FunctionKind.Setter) && Parser.ValidateString(code, ref position, false))
                    {
                        name = Tools.Unescape(code.Substring(nameStartPos + 1, position - nameStartPos - 2), state.strict);
                    }
                    else if ((kind == FunctionKind.Getter || kind == FunctionKind.Setter) && Parser.ValidateNumber(code, ref position))
                    {
                        name = Tools.Unescape(code.Substring(nameStartPos, position - nameStartPos), state.strict);
                    }
                    else
                    {
                        ExceptionHelper.ThrowSyntaxError("Invalid function name", code, nameStartPos, position - nameStartPos);
                    }

                    Tools.SkipSpaces(code, ref position);

                    if (code[position] != '(')
                    {
                        ExceptionHelper.ThrowUnknownToken(code, position);
                    }
                }
                else if (kind == FunctionKind.Getter || kind == FunctionKind.Setter)
                {
                    ExceptionHelper.ThrowSyntaxError("Getter and Setter must have name", code, index);
                }
                else if (kind == FunctionKind.Method || kind == FunctionKind.MethodGenerator)
                {
                    ExceptionHelper.ThrowSyntaxError("Method must have name", code, index);
                }

                position++;
            }
            else if (code[position] != '(')
            {
                arrowWithSunglePrm = true;
            }
            else
            {
                position++;
            }

            Tools.SkipSpaces(code, ref position);

            if (code[position] == ',')
            {
                ExceptionHelper.ThrowSyntaxError(Strings.UnexpectedToken, code, position);
            }

            while (code[position] != ')')
            {
                if (parameters.Count == 255 || (kind == FunctionKind.Setter && parameters.Count == 1) || kind == FunctionKind.Getter)
                {
                    ExceptionHelper.ThrowSyntaxError(string.Format(Strings.TooManyArgumentsForFunction, name), code, index);
                }

                bool rest = Parser.Validate(code, "...", ref position);

                Expression destructor = null;
                int        n          = position;
                if (!Parser.ValidateName(code, ref position, state.strict))
                {
                    if (code[position] == '{')
                    {
                        destructor = (Expression)ObjectDefinition.Parse(state, ref position);
                    }
                    else if (code[position] == '[')
                    {
                        destructor = (Expression)ArrayDefinition.Parse(state, ref position);
                    }

                    if (destructor == null)
                    {
                        ExceptionHelper.ThrowUnknownToken(code, nameStartPos);
                    }

                    containsDestructuringPrms = true;
                }

                var pname     = Tools.Unescape(code.Substring(n, position - n), state.strict);
                var reference = new ParameterReference(pname, rest, state.lexicalScopeLevel + 1)
                {
                    Position = n,
                    Length   = position - n
                };
                var desc = reference.Descriptor as ParameterDescriptor;

                if (destructor != null)
                {
                    desc.Destructor = new ObjectDesctructor(destructor);
                }

                parameters.Add(desc);

                Tools.SkipSpaces(state.Code, ref position);
                if (arrowWithSunglePrm)
                {
                    position--;
                    break;
                }

                if (code[position] == '=')
                {
                    if (kind == FunctionKind.Arrow)
                    {
                        ExceptionHelper.ThrowSyntaxError("Parameters of arrow-function cannot have an initializer", code, position);
                    }

                    if (rest)
                    {
                        ExceptionHelper.ThrowSyntaxError("Rest parameters can not have an initializer", code, position);
                    }
                    do
                    {
                        position++;
                    }while (Tools.IsWhiteSpace(code[position]));
                    desc.initializer = ExpressionTree.Parse(state, ref position, false, false) as Expression;
                }

                if (code[position] == ',')
                {
                    if (rest)
                    {
                        ExceptionHelper.ThrowSyntaxError("Rest parameters must be the last in parameters list", code, position);
                    }
                    do
                    {
                        position++;
                    }while (Tools.IsWhiteSpace(code[position]));
                }
            }

            if (kind == FunctionKind.Setter)
            {
                if (parameters.Count != 1)
                {
                    ExceptionHelper.ThrowSyntaxError("Setter must have only one argument", code, index);
                }
            }

            position++;
            Tools.SkipSpaces(code, ref position);

            if (kind == FunctionKind.Arrow)
            {
                if (!Parser.Validate(code, "=>", ref position))
                {
                    ExceptionHelper.ThrowSyntaxError("Expected \"=>\"", code, position);
                }
                Tools.SkipSpaces(code, ref position);
            }

            if (code[position] != '{')
            {
                var oldFunctionScopeLevel = state.functionScopeLevel;
                state.functionScopeLevel = ++state.lexicalScopeLevel;

                try
                {
                    if (kind == FunctionKind.Arrow)
                    {
                        body = new CodeBlock(new CodeNode[]
                        {
                            new Return(ExpressionTree.Parse(state, ref position, processComma: false) as Expression)
                        })
                        {
                            _variables = new VariableDescriptor[0]
                        };

                        body.Position = body._lines[0].Position;
                        body.Length   = body._lines[0].Length;
                    }
                    else
                    {
                        ExceptionHelper.ThrowUnknownToken(code, position);
                    }
                }
                finally
                {
                    state.functionScopeLevel = oldFunctionScopeLevel;
                    state.lexicalScopeLevel--;
                }
            }
            else
            {
                var oldCodeContext = state.CodeContext;
                state.CodeContext &= ~(CodeContext.InExpression | CodeContext.Conditional | CodeContext.InEval);
                if (kind == FunctionKind.Generator || kind == FunctionKind.MethodGenerator || kind == FunctionKind.AnonymousGenerator)
                {
                    state.CodeContext |= CodeContext.InGenerator;
                }
                else if (kind == FunctionKind.AsyncFunction)
                {
                    state.CodeContext |= CodeContext.InAsync;
                }
                state.CodeContext |= CodeContext.InFunction;

                var labels = state.Labels;
                state.Labels = new List <string>();
                state.AllowReturn++;
                try
                {
                    state.AllowBreak.Push(false);
                    state.AllowContinue.Push(false);
                    state.AllowDirectives = true;
                    body = CodeBlock.Parse(state, ref position) as CodeBlock;
                    if (containsDestructuringPrms)
                    {
                        var destructuringTargets = new List <VariableDescriptor>();
                        var assignments          = new List <Expression>();
                        for (var i = 0; i < parameters.Count; i++)
                        {
                            if (parameters[i].Destructor != null)
                            {
                                var targets = parameters[i].Destructor.GetTargetVariables();
                                for (var j = 0; j < targets.Count; j++)
                                {
                                    destructuringTargets.Add(new VariableDescriptor(targets[j].Name, state.functionScopeLevel));
                                }

                                assignments.Add(new Assignment(parameters[i].Destructor, parameters[i].references[0]));
                            }
                        }

                        var newLines = new CodeNode[body._lines.Length + 1];
                        System.Array.Copy(body._lines, 0, newLines, 1, body._lines.Length);
                        newLines[0] = new VariableDefinition(destructuringTargets.ToArray(), assignments.ToArray(), VariableKind.AutoGeneratedParameters);
                        body._lines = newLines;
                    }
                }
                finally
                {
                    state.CodeContext = oldCodeContext;
                    state.AllowBreak.Pop();
                    state.AllowContinue.Pop();
                    state.AllowDirectives = false;
                    state.Labels          = labels;
                    state.AllowReturn--;
                }

                if (kind == FunctionKind.Function && string.IsNullOrEmpty(name))
                {
                    kind = FunctionKind.AnonymousFunction;
                }
            }

            if (body._strict || (parameters.Count > 0 && parameters[parameters.Count - 1].IsRest) || kind == FunctionKind.Arrow)
            {
                for (var j = parameters.Count; j-- > 1;)
                {
                    for (var k = j; k-- > 0;)
                    {
                        if (parameters[j].Name == parameters[k].Name)
                        {
                            ExceptionHelper.ThrowSyntaxError("Duplicate names of function parameters not allowed in strict mode", code, index);
                        }
                    }
                }

                if (name == "arguments" || name == "eval")
                {
                    ExceptionHelper.ThrowSyntaxError("Functions name can not be \"arguments\" or \"eval\" in strict mode at", code, index);
                }

                for (int j = parameters.Count; j-- > 0;)
                {
                    if (parameters[j].Name == "arguments" || parameters[j].Name == "eval")
                    {
                        ExceptionHelper.ThrowSyntaxError("Parameters name cannot be \"arguments\" or \"eval\" in strict mode at", code, parameters[j].references[0].Position, parameters[j].references[0].Length);
                    }
                }
            }

            var func = new FunctionDefinition(name)
            {
                parameters = parameters.ToArray(),
                _body      = body,
                kind       = kind,
                Position   = index,
                Length     = position - index,
#if DEBUG
                trace = body.directives != null?body.directives.Contains("debug trace") : false
#endif
            };

            if (!string.IsNullOrEmpty(name))
            {
                func.Reference.ScopeLevel = state.lexicalScopeLevel;
                func.Reference.Position   = nameStartPos;
                func.Reference.Length     = name.Length;

                func.reference._descriptor.definitionScopeLevel = func.reference.ScopeLevel;
            }

            if (parameters.Count != 0)
            {
                var newVariablesCount = body._variables.Length + parameters.Count;

                for (var j = 0; j < body._variables.Length; j++)
                {
                    for (var k = 0; k < parameters.Count; k++)
                    {
                        if (body._variables[j].name == parameters[k].name)
                        {
                            newVariablesCount--;
                            break;
                        }
                    }
                }

                var newVariables = new VariableDescriptor[newVariablesCount];
                for (var j = 0; j < parameters.Count; j++)
                {
                    newVariables[j] = parameters[parameters.Count - j - 1]; // порядок определяет приоритет
                    for (var k = 0; k < body._variables.Length; k++)
                    {
                        if (body._variables[k] != null && body._variables[k].name == parameters[j].name)
                        {
                            if (body._variables[k].initializer != null)
                            {
                                newVariables[j] = body._variables[k];
                            }
                            else
                            {
                                body._variables[k].lexicalScope = false;
                            }
                            body._variables[k] = null;
                            break;
                        }
                    }
                }

                for (int j = 0, k = parameters.Count; j < body._variables.Length; j++)
                {
                    if (body._variables[j] != null)
                    {
                        newVariables[k++] = body._variables[j];
                    }
                }

                body._variables = newVariables;
            }

            if ((state.CodeContext & CodeContext.InExpression) == 0 && kind == FunctionKind.Function)
            // Позволяет делать вызов сразу при объявлении функции
            // (в таком случае функция не добавляется в контекст).
            // Если убрать проверку, то в тех сулчаях,
            // когда определение и вызов стоят внутри выражения,
            // будет выдано исключение, потому,
            // что тогда это уже не определение и вызов функции,
            // а часть выражения, которые не могут начинаться со слова "function".
            // За красивыми словами "может/не может" кроется другая хрень: если бы это было выражение,
            // то прямо тут надо было бы разбирать тот оператор, который стоит после определения функции,
            // что не разумно
            {
                var tindex = position;
                while (position < code.Length && Tools.IsWhiteSpace(code[position]) && !Tools.IsLineTerminator(code[position]))
                {
                    position++;
                }

                if (position < code.Length && code[position] == '(')
                {
                    var args = new List <Expression>();
                    position++;
                    for (;;)
                    {
                        while (Tools.IsWhiteSpace(code[position]))
                        {
                            position++;
                        }
                        if (code[position] == ')')
                        {
                            break;
                        }
                        else if (code[position] == ',')
                        {
                            do
                            {
                                position++;
                            }while (Tools.IsWhiteSpace(code[position]));
                        }
                        args.Add(ExpressionTree.Parse(state, ref position, false, false));
                    }

                    position++;
                    index = position;
                    while (position < code.Length && Tools.IsWhiteSpace(code[position]))
                    {
                        position++;
                    }

                    if (position < code.Length && code[position] == ';')
                    {
                        ExceptionHelper.Throw((new SyntaxError("Expression can not start with word \"function\"")));
                    }

                    return(new Call(func, args.ToArray()));
                }
                else
                {
                    position = tindex;
                }
            }

            if ((state.CodeContext & CodeContext.InExpression) == 0 &&
                (kind != FunctionKind.Arrow || (state.CodeContext & CodeContext.InEval) == 0))
            {
                if (string.IsNullOrEmpty(name))
                {
                    ExceptionHelper.ThrowSyntaxError("Function must have name", state.Code, index);
                }

                state.Variables.Add(func.reference._descriptor);
            }

            index = position;
            return(func);
        }
Exemple #2
0
 protected override EP_VP1 Visit(NiL.JS.Expressions.ObjectDefinition node)
 {
     return(Visit(node as Expression));
 }