Esempio n. 1
0
        internal static CodeNode Parse(ParseInfo state, ref int index)
        {
            if (state.Code[index] != '{')
            {
                throw new ArgumentException("Invalid JSON definition");
            }

            var flds = new Dictionary <string, Expression>();
            var computedProperties = new List <KeyValuePair <Expression, Expression> >();
            int i = index;

            while (state.Code[i] != '}')
            {
                i++;
                Tools.SkipSpaces(state.Code, ref i);
                int start = i;
                if (state.Code[i] == '}')
                {
                    break;
                }

                bool getOrSet = Parser.Validate(state.Code, "get", ref i) || Parser.Validate(state.Code, "set", ref i);
                Tools.SkipSpaces(state.Code, ref i);
                if (getOrSet && state.Code[i] == '(')  // function with name 'get' or 'set'
                {
                    getOrSet = false;
                    i        = start;
                }

                var asterisk = state.Code[i] == '*';
                Tools.SkipSpaces(state.Code, ref i);

                var async = false;
                if (!asterisk)
                {
                    async = Parser.Validate(state.Code, "async", ref i);
                    Tools.SkipSpaces(state.Code, ref i);
                }

                if (Parser.Validate(state.Code, "[", ref i))
                {
                    var name = ExpressionTree.Parse(state, ref i, false, false, false, true, false);
                    while (Tools.IsWhiteSpace(state.Code[i]))
                    {
                        i++;
                    }
                    if (state.Code[i] != ']')
                    {
                        ExceptionHelper.ThrowSyntaxError("Expected ']'", state.Code, i);
                    }
                    do
                    {
                        i++;
                    }while (Tools.IsWhiteSpace(state.Code[i]));

                    Tools.SkipSpaces(state.Code, ref i);
                    CodeNode initializer;
                    if (state.Code[i] == '(')
                    {
                        initializer = FunctionDefinition.Parse(state, ref i, asterisk ? FunctionKind.AnonymousGenerator : async ? FunctionKind.AsyncAnonymousFunction : FunctionKind.AnonymousFunction);
                    }
                    else
                    {
                        if (!Parser.Validate(state.Code, ":", ref i))
                        {
                            ExceptionHelper.ThrowSyntaxError(Strings.UnexpectedToken, state.Code, i);
                        }
                        initializer = ExpressionTree.Parse(state, ref i);
                    }

                    switch (state.Code[start])
                    {
                    case 'g':
                    {
                        computedProperties.Add(new KeyValuePair <Expression, Expression>(name, new PropertyPair((Expression)initializer, null)));
                        break;
                    }

                    case 's':
                    {
                        computedProperties.Add(new KeyValuePair <Expression, Expression>(name, new PropertyPair(null, (Expression)initializer)));
                        break;
                    }

                    default:
                    {
                        computedProperties.Add(new KeyValuePair <Expression, Expression>(name, (Expression)initializer));
                        break;
                    }
                    }
                }
                else if (getOrSet && state.Code[i] != ':')
                {
                    i = start;
                    var mode             = state.Code[i] == 's' ? FunctionKind.Setter : FunctionKind.Getter;
                    var propertyAccessor = FunctionDefinition.Parse(state, ref i, mode) as FunctionDefinition;
                    var accessorName     = propertyAccessor._name;
                    if (!flds.ContainsKey(accessorName))
                    {
                        var propertyPair = new PropertyPair
                                           (
                            mode == FunctionKind.Getter ? propertyAccessor : null,
                            mode == FunctionKind.Setter ? propertyAccessor : null
                                           );
                        flds.Add(accessorName, propertyPair);
                    }
                    else
                    {
                        var vle = flds[accessorName] as PropertyPair;

                        if (vle == null)
                        {
                            ExceptionHelper.ThrowSyntaxError("Try to define " + mode.ToString().ToLowerInvariant() + " for defined field", state.Code, start);
                        }

                        do
                        {
                            if (mode == FunctionKind.Getter)
                            {
                                if (vle.Getter == null)
                                {
                                    vle.Getter = propertyAccessor;
                                    break;
                                }
                            }
                            else
                            {
                                if (vle.Setter == null)
                                {
                                    vle.Setter = propertyAccessor;
                                    break;
                                }
                            }

                            ExceptionHelper.ThrowSyntaxError("Try to redefine " + mode.ToString().ToLowerInvariant() + " of " + propertyAccessor.Name, state.Code, start);
                        }while (false);
                    }
                }
                else
                {
                    if (asterisk)
                    {
                        do
                        {
                            i++;
                        }while (Tools.IsWhiteSpace(state.Code[i]));
                    }

                    i = start;
                    var fieldName = "";
                    if (Parser.ValidateName(state.Code, ref i, false, true, state.Strict))
                    {
                        fieldName = Tools.Unescape(state.Code.Substring(start, i - start), state.Strict);
                    }
                    else if (Parser.ValidateValue(state.Code, ref i))
                    {
                        if (state.Code[start] == '-')
                        {
                            ExceptionHelper.Throw(new SyntaxError("Invalid char \"-\" at " + CodeCoordinates.FromTextPosition(state.Code, start, 1)));
                        }

                        int n = start;
                        if (Tools.ParseJsNumber(state.Code, ref n, out double d))
                        {
                            fieldName = NumberUtils.DoubleToString(d);
                        }
                        else if (state.Code[start] == '\'' || state.Code[start] == '"')
                        {
                            fieldName = Tools.Unescape(state.Code.Substring(start + 1, i - start - 2), state.Strict);
                        }
                        else if (flds.Count != 0)
                        {
                            ExceptionHelper.Throw((new SyntaxError("Invalid field name at " + CodeCoordinates.FromTextPosition(state.Code, start, i - start))));
                        }
                        else
                        {
                            return(null);
                        }
                    }
                    else
                    {
                        return(null);
                    }

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

                    Expression initializer = null;

                    if (state.Code[i] == '(')
                    {
                        i           = start;
                        initializer = FunctionDefinition.Parse(state, ref i, asterisk ? FunctionKind.MethodGenerator : async ? FunctionKind.AsyncMethod : FunctionKind.Method);
                    }
                    else
                    {
                        if (asterisk || (async && state.Code[i] != ':'))
                        {
                            ExceptionHelper.ThrowSyntaxError("Unexpected token", state.Code, i);
                        }

                        if (state.Code[i] != ':' && state.Code[i] != ',' && state.Code[i] != '}')
                        {
                            ExceptionHelper.ThrowSyntaxError("Expected ',', ';' or '}'", state.Code, i);
                        }

                        if (flds.TryGetValue(fieldName, out Expression aei))
                        {
                            if (state.Strict ? (!(aei is Constant constant) || constant.value != JSValue.undefined) : aei is PropertyPair)
                            {
                                ExceptionHelper.ThrowSyntaxError("Try to redefine field \"" + fieldName + "\"", state.Code, start, i - start);
                            }

                            if (state.Message != null)
                            {
                                state.Message(MessageLevel.Warning, i, 0, "Duplicate key \"" + fieldName + "\"");
                            }
                        }

                        if (state.Code[i] == ',' || state.Code[i] == '}')
                        {
                            if (!Parser.ValidateName(fieldName, 0))
                            {
                                ExceptionHelper.ThrowSyntaxError("Invalid variable name", state.Code, i);
                            }

                            var variable = new Variable(fieldName, state.LexicalScopeLevel)
                            {
                                Position = start,
                                Length   = fieldName.Length,
                            };

                            initializer = variable;
                        }
                        else
                        {
                            i++;
                            Tools.SkipSpaces(state.Code, ref i);

                            initializer = ExpressionTree.Parse(state, ref i, false, false);
                        }
                    }

                    flds[fieldName] = initializer;
                }

                while (Tools.IsWhiteSpace(state.Code[i]))
                {
                    i++;
                }

                if ((state.Code[i] != ',') && (state.Code[i] != '}'))
                {
                    return(null);
                }
            }

            i++;
            var pos = index;

            index = i;
            return(new ObjectDefinition(flds, computedProperties.ToArray())
            {
                Position = pos,
                Length = index - pos
            });
        }
Esempio n. 2
0
        internal static CodeNode Parse(ParseInfo state, ref int index)
        {
            string code = state.Code;
            int    i    = index;

            if (!Parser.Validate(code, "class", ref i))
            {
                return(null);
            }
            while (Tools.IsWhiteSpace(code[i]))
            {
                i++;
            }
            string     name     = null;
            Expression baseType = null;

            if (!Parser.Validate(code, "extends ", i))
            {
                var n = i;
                if (Parser.ValidateName(code, ref i, true))
                {
                    name = code.Substring(n, i - n);
                }

                while (Tools.IsWhiteSpace(code[i]))
                {
                    i++;
                }
            }
            if (Parser.Validate(code, "extends ", ref i))
            {
                var n = i;
                if (!Parser.ValidateName(code, ref i, true) && !Parser.Validate(code, "null", ref i))
                {
                    ExceptionHelper.ThrowSyntaxError("Invalid base class name", state.Code, i);
                }

                var baseClassName = code.Substring(n, i - n);
                if (baseClassName == "null")
                {
                    baseType = new Constant(JSValue.@null);
                }
                else
                {
                    baseType = new Variable(baseClassName, 1);
                }

                baseType.Position = n;
                baseType.Length   = i - n;

                while (Tools.IsWhiteSpace(code[i]))
                {
                    i++;
                }
            }
            if (code[i] != '{')
            {
                ExceptionHelper.ThrowSyntaxError(Strings.UnexpectedToken, code, i);
            }

            FunctionDefinition ctor   = null;
            ClassDefinition    result = null;
            var oldStrict             = state.strict;

            state.strict = true;
            var flds = new Dictionary <string, MemberDescriptor>();
            var computedProperties = new List <MemberDescriptor>();
            var oldCodeContext     = state.CodeContext;

            state.CodeContext |= CodeContext.InExpression;

            try
            {
                while (code[i] != '}')
                {
                    do
                    {
                        i++;
                    }while (Tools.IsWhiteSpace(code[i]) || code[i] == ';');
                    int s = i;
                    if (state.Code[i] == '}')
                    {
                        break;
                    }

                    bool @static = Parser.Validate(state.Code, "static", ref i);
                    if (@static)
                    {
                        Tools.SkipSpaces(state.Code, ref i);
                        s = i;
                    }

                    bool getOrSet = Parser.Validate(state.Code, "get", ref i) || Parser.Validate(state.Code, "set", ref i);
                    if (getOrSet)
                    {
                        Tools.SkipSpaces(state.Code, ref i);
                    }

                    var asterisk = state.Code[i] == '*';
                    if (asterisk)
                    {
                        do
                        {
                            i++;
                        }while (Tools.IsWhiteSpace(state.Code[i]));
                    }

                    if (Parser.Validate(state.Code, "[", ref i))
                    {
                        var propertyName = ExpressionTree.Parse(state, ref i, false, false, false, true, false);
                        while (Tools.IsWhiteSpace(state.Code[i]))
                        {
                            i++;
                        }
                        if (state.Code[i] != ']')
                        {
                            ExceptionHelper.ThrowSyntaxError("Expected ']'", state.Code, i);
                        }
                        do
                        {
                            i++;
                        }while (Tools.IsWhiteSpace(state.Code[i]));

                        CodeNode initializer;
                        if (state.Code[i] == '(')
                        {
                            initializer = FunctionDefinition.Parse(state, ref i, asterisk ? FunctionKind.AnonymousGenerator : FunctionKind.AnonymousFunction);
                        }
                        else
                        {
                            initializer = ExpressionTree.Parse(state, ref i);
                        }

                        switch (state.Code[s])
                        {
                        case 'g':
                        {
                            computedProperties.Add(new MemberDescriptor((Expression)propertyName, new PropertyPair((Expression)initializer, null), @static));
                            break;
                        }

                        case 's':
                        {
                            computedProperties.Add(new MemberDescriptor((Expression)propertyName, new PropertyPair(null, (Expression)initializer), @static));
                            break;
                        }

                        default:
                        {
                            computedProperties.Add(new MemberDescriptor((Expression)propertyName, (Expression)initializer, @static));
                            break;
                        }
                        }
                    }
                    else if (getOrSet)
                    {
                        i = s;
                        var mode             = state.Code[i] == 's' ? FunctionKind.Setter : FunctionKind.Getter;
                        var propertyAccessor = FunctionDefinition.Parse(state, ref i, mode) as FunctionDefinition;
                        var accessorName     = (@static ? "static " : "") + propertyAccessor._name;
                        if (!flds.ContainsKey(accessorName))
                        {
                            var propertyPair = new PropertyPair
                                               (
                                mode == FunctionKind.Getter ? propertyAccessor : null,
                                mode == FunctionKind.Setter ? propertyAccessor : null
                                               );
                            flds.Add(accessorName, new MemberDescriptor(new Constant(propertyAccessor._name), propertyPair, @static));
                        }
                        else
                        {
                            var vle = flds[accessorName].Value as PropertyPair;

                            if (vle == null)
                            {
                                ExceptionHelper.Throw((new SyntaxError("Try to define " + mode.ToString().ToLowerInvariant() + " for defined field at " + CodeCoordinates.FromTextPosition(state.Code, s, 0))));
                            }

                            do
                            {
                                if (mode == FunctionKind.Getter)
                                {
                                    if (vle.Getter == null)
                                    {
                                        vle.Getter = propertyAccessor;
                                        break;
                                    }
                                }
                                else
                                {
                                    if (vle.Setter == null)
                                    {
                                        vle.Setter = propertyAccessor;
                                        break;
                                    }
                                }

                                ExceptionHelper.ThrowSyntaxError("Try to redefine " + mode.ToString().ToLowerInvariant() + " of " + propertyAccessor.Name, state.Code, s);
                            }while (false);
                        }
                    }
                    else
                    {
                        i = s;
                        string fieldName = null;
                        if (state.Code[i] == '*')
                        {
                            do
                            {
                                i++;
                            }while (Tools.IsWhiteSpace(code[i]));
                        }

                        if (Parser.ValidateName(state.Code, ref i, false, true, state.strict))
                        {
                            fieldName = Tools.Unescape(state.Code.Substring(s, i - s), state.strict);
                        }
                        else if (Parser.ValidateValue(state.Code, ref i))
                        {
                            double d = 0.0;
                            int    n = s;
                            if (Tools.ParseNumber(state.Code, ref n, out d))
                            {
                                fieldName = Tools.DoubleToString(d);
                            }
                            else if (state.Code[s] == '\'' || state.Code[s] == '"')
                            {
                                fieldName = Tools.Unescape(state.Code.Substring(s + 1, i - s - 2), state.strict);
                            }
                        }

                        if (fieldName == null)
                        {
                            ExceptionHelper.Throw((new SyntaxError("Invalid member name at " + CodeCoordinates.FromTextPosition(state.Code, s, i - s))));
                        }

                        if (fieldName == "constructor")
                        {
                            if (@static)
                            {
                                ExceptionHelper.ThrowSyntaxError(Strings.ConstructorCannotBeStatic, state.Code, s);
                            }
                            if (ctor != null)
                            {
                                ExceptionHelper.ThrowSyntaxError("Trying to redefinition constructor", state.Code, s);
                            }

                            state.CodeContext |= CodeContext.InClassConstructor;
                        }
                        else if (@static)
                        {
                            fieldName          = "static " + fieldName;
                            state.CodeContext |= CodeContext.InStaticMember;
                        }
                        if (flds.ContainsKey(fieldName))
                        {
                            ExceptionHelper.Throw(new SyntaxError("Trying to redefinition member \"" + fieldName + "\" at " + CodeCoordinates.FromTextPosition(state.Code, s, i - s)));
                        }

                        state.CodeContext |= CodeContext.InClassDefenition;
                        state.CodeContext &= ~CodeContext.InGenerator;

                        i = s;
                        var method = FunctionDefinition.Parse(state, ref i, FunctionKind.Method) as FunctionDefinition;
                        if (method == null)
                        {
                            ExceptionHelper.ThrowSyntaxError("Unable to parse constructor", state.Code, i);
                        }

                        if (fieldName == "constructor")
                        {
                            ctor = method;
                        }
                        else
                        {
                            flds[fieldName] = new MemberDescriptor(new Constant(method._name), method, @static);
                        }
                    }

                    code = state.Code;
                }

                if (ctor == null)
                {
                    string ctorCode;
                    int    ctorIndex = 0;
                    if (baseType != null && !(baseType is Constant))
                    {
                        ctorCode = "constructor(...args) { super(...args); }";
                    }
                    else
                    {
                        ctorCode = "constructor(...args) { }";
                    }
                    ctor = (FunctionDefinition)FunctionDefinition.Parse(
                        new ParseInfo(ctorCode, ctorCode, null)
                    {
                        strict      = true,
                        CodeContext = CodeContext.InClassConstructor | CodeContext.InClassDefenition
                    },
                        ref ctorIndex,
                        FunctionKind.Method);
                }

                result = new ClassDefinition(name, baseType, new List <MemberDescriptor>(flds.Values).ToArray(), ctor as FunctionDefinition, computedProperties.ToArray());

                if ((oldCodeContext & CodeContext.InExpression) == 0)
                {
                    if (string.IsNullOrEmpty(name))
                    {
                        ExceptionHelper.ThrowSyntaxError("Class must have name", state.Code, index);
                    }
                    if (state.strict && state.functionScopeLevel != state.lexicalScopeLevel)
                    {
                        ExceptionHelper.ThrowSyntaxError("In strict mode code, class can only be declared at top level or immediately within other function.", state.Code, index);
                    }

                    state.Variables.Add(result.reference._descriptor);
                }
            }
            finally
            {
                state.CodeContext = oldCodeContext;
                state.strict      = oldStrict;
            }
            index = i + 1;
            return(result);
        }