Example #1
0
        /// <summary>
        /// Resolves which function is being called where possible.
        /// </summary>
        internal override void ResolveVariables(OptimizationInfo optimizationInfo)
        {
            if (ResolvedMethod != null || UserDefined != null)
            {
                // Already resolved.
                return;
            }

            // Grab if this is a 'new' call:
            bool isConstructor = optimizationInfo.IsConstructCall;

            optimizationInfo.IsConstructCall = false;

            // Resolve kids:
            base.ResolveVariables(optimizationInfo);

            object resolvedMethod = null;

            if (this.Target is MemberAccessExpression)
            {
                // The function is a member access expression (e.g. "Math.cos()").

                // Get the parent of the member (e.g. Math):
                MemberAccessExpression baseExpression = ((MemberAccessExpression)this.Target);

                // Get the property:
                Nitrassic.Library.PropertyVariable property = baseExpression.GetProperty(optimizationInfo);

                // It should be a callable method:
                if (property.Type == typeof(MethodGroup) || typeof(System.Reflection.MethodBase).IsAssignableFrom(property.Type))
                {
                    if (property.IsConstant)
                    {
                        // Great, grab the value:
                        resolvedMethod = property.ConstantValue;
                    }
                    else
                    {
                        // This occurs when the method has collapsed.
                        // The property is still a method though, so we know for sure it can be invoked.
                        // It's now an instance property on the object.
                        optimizationInfo.TypeError("Runtime method in use (not supported at the moment). Internal: #T1");
                    }
                }
                else if (property.Type == typeof(FunctionMethodGenerator))
                {
                    FunctionMethodGenerator fm = property.ConstantValue as FunctionMethodGenerator;

                    if (fm != null)
                    {
                        // Get the arg types being passed into the method, including 'this':
                        Type[] argTypes = GetArgumentTypes(optimizationInfo, true, isConstructor?fm:null);

                        // Get a specific overload:
                        Library.UserDefinedFunction udm = fm.GetCompiled(argTypes, optimizationInfo.Engine, isConstructor);

                        resolvedMethod = udm.body;
                        UserDefined    = udm;
                    }
                    else
                    {
                        // Runtime resolve (property)
                        optimizationInfo.TypeError("Runtime property in use (not supported at the moment). Internal: #T4");
                    }
                }
                else if (property.Type != typeof(object))
                {
                    throw new JavaScriptException(
                              optimizationInfo.Engine,
                              "TypeError",
                              "Cannot run '" + property.Name + "' as a method because it's known to be a " + property.Type + "."
                              );
                }
                else
                {
                    // Similar to above, but this time its something that might not even be a method
                    optimizationInfo.TypeError("Runtime method in use (not supported at the moment). Internal: #T2");
                }
            }

            if (resolvedMethod == null)
            {
                // Get target as a name expression:
                NameExpression nameExpr = Target as NameExpression;

                if (nameExpr != null && nameExpr.Variable != null)
                {
                    if (nameExpr.Variable.IsConstant)
                    {
                        FunctionMethodGenerator fm = nameExpr.Variable.ConstantValue as FunctionMethodGenerator;

                        if (fm != null)
                        {
                            // Get the arg types being passed into the method, including 'this':
                            Type[] argTypes = GetArgumentTypes(optimizationInfo, true, isConstructor?fm:null);

                            // Get a specific overload:
                            Library.UserDefinedFunction udm = fm.GetCompiled(argTypes, optimizationInfo.Engine, isConstructor);

                            resolvedMethod = udm.body;
                            UserDefined    = udm;
                        }
                        else
                        {
                            // This is a constructor for a built-in type.

                            // Get the return type:
                            Type returnType = Target.GetResultType(optimizationInfo);

                            // Get the proto for it:
                            Nitrassic.Library.Prototype proto = optimizationInfo.Engine.Prototypes.Get(returnType);

                            // Note that these two special methods are always methods
                            // or method groups so no checking is necessary.
                            if (isConstructor)
                            {
                                resolvedMethod = proto.OnConstruct;
                            }
                            else
                            {
                                resolvedMethod = proto.OnCall;
                            }
                        }
                    }
                    else
                    {
                                                #warning runtime resolve here.
                        // -> E.g. new varName() or varName()
                        optimizationInfo.TypeError("Runtime resolve in use (not supported at the moment). Internal: #T5");
                    }
                }
                else
                {
                    // Something else (e.g. "eval()")

                    // Get the return type:
                    Type returnType = Target.GetResultType(optimizationInfo);

                    // Get the proto for it:
                    Nitrassic.Library.Prototype proto = optimizationInfo.Engine.Prototypes.Get(returnType);

                    // Note that these two special methods are always methods
                    // or method groups so no checking is necessary.
                    if (isConstructor)
                    {
                        resolvedMethod = proto.OnConstruct;
                    }
                    else
                    {
                        resolvedMethod = proto.OnCall;
                    }
                }
            }

            if (resolvedMethod == null)
            {
                // Runtime resolve only.
                ResolvedMethod = null;
                return;
            }

            // Note that it may be a MethodGroup, so let's resolve it further if needed.
            MethodGroup group = resolvedMethod as MethodGroup;

            if (group == null)
            {
                // It must be MethodBase - it can't be anything else:
                ResolvedMethod = resolvedMethod as System.Reflection.MethodBase;
            }
            else
            {
                // We have a group! Find the overload that we're after (excluding 'this' and it's never a constructor either):
                ResolvedMethod = group.Match(GetArgumentTypes(optimizationInfo, false, null));
            }
        }
Example #2
0
        /// <summary>
        /// Generates CIL for the expression.
        /// </summary>
        /// <param name="generator"> The generator to output the CIL to. </param>
        /// <param name="optimizationInfo"> Information about any optimizations that should be performed. </param>
        public override void GenerateCode(ILGenerator generator, OptimizationInfo optimizationInfo)
        {
            // Get the target as a name expression:
            NameExpression nameExpr = Target as NameExpression;

            // Grab if this is a 'new' call:
            bool isConstructor = optimizationInfo.IsConstructCall;

            optimizationInfo.IsConstructCall = false;

            if (ResolvedMethod != null)
            {
                // We have a known method!

                if (UserDefined != null)
                {
                    if (isConstructor)
                    {
                        // Generate code to produce the "this" value.
                        Library.Prototype proto = UserDefined.GetInstancePrototype(optimizationInfo.Engine);

                        // Create the object now:
                        generator.NewObject(proto.TypeConstructor);

                        // Duplicate (which will act as our return value):
                        if (optimizationInfo.RootExpression != this)
                        {
                            generator.Duplicate();
                        }
                    }
                    else
                    {
                        // There are three cases for non-constructor calls.
                        if (this.Target is NameExpression)
                        {
                            // 1. The function is a name expression (e.g. "parseInt()").
                            //	In this case this = scope.ImplicitThisValue, if there is one, otherwise undefined.
                            ((NameExpression)this.Target).GenerateThis(generator);
                        }
                        else if (this.Target is MemberAccessExpression)
                        {
                            // 2. The function is a member access expression (e.g. "Math.cos()").
                            //	In this case this = Math.
                            var baseExpression = ((MemberAccessExpression)this.Target).Base;
                            baseExpression.GenerateCode(generator, optimizationInfo);
                            EmitConversion.ToAny(generator, baseExpression.GetResultType(optimizationInfo));
                        }
                        else
                        {
                            // 3. Neither of the above (e.g. "(function() { return 5 })()")
                            //	In this case this = undefined.
                            EmitHelpers.EmitUndefined(generator);
                        }
                    }
                }

                // Emit the rest of the args:
                EmitArguments(generator, optimizationInfo);

                // Got a return type?
                Type returnType = GetResultType(optimizationInfo);

                // Then the call!
                if (typeof(System.Reflection.ConstructorInfo).IsAssignableFrom(ResolvedMethod.GetType()))
                {
                    // Actual constructor call:
                    generator.NewObject(ResolvedMethod as System.Reflection.ConstructorInfo);
                }
                else
                {
                    // Ordinary method:
                    generator.Call(ResolvedMethod);
                }

                if (isConstructor)
                {
                    // Always a returned value here. Needed?
                    if (optimizationInfo.RootExpression == this)
                    {
                        // Remove the return value:
                        generator.Pop();
                    }
                }
                else
                {
                    if (returnType == typeof(Nitrassic.Undefined))
                    {
                        if (optimizationInfo.RootExpression != this)
                        {
                            // Put undef on the stack:
                            EmitHelpers.EmitUndefined(generator);
                        }
                    }
                    else if (optimizationInfo.RootExpression == this)
                    {
                        // Remove the return value:
                        generator.Pop();
                    }
                }
            }
            else
            {
                // Either runtime resolve it or it's not actually a callable function
                throw new NotImplementedException("A function was called which was not supported (" + ToString() + ")");
            }
        }
Example #3
0
        public override Statement ParseNoNewContext(Parser parser)
        {
            // Consume the for keyword.
            parser.Expect(KeywordToken.For);

            // Read the left parenthesis.
            parser.Expect(PunctuatorToken.LeftParenthesis);

            // The initialization statement.
            Statement initializationStatement = null;

            // The type of for statement.
            ForStatementType type = ForStatementType.Unknown;

            // The for-in and for-of expressions need a variable to assign to.  Is null for a regular for statement.
            IReferenceExpression forInOfReference = null;

            if (parser.nextToken == KeywordToken.Var || parser.nextToken == KeywordToken.Let || parser.nextToken == KeywordToken.Const)
            {
                bool isVar = (parser.nextToken == KeywordToken.Var);

                // Read past the var/let/const token.
                parser.Expect(parser.nextToken);

                Scope scope = isVar?parser.currentVarScope : parser.currentLetScope;

                // There can be multiple initializers (but not for for-in statements).
                var varLetConstStatement = new VarStatement(scope);
                initializationStatement = parser.Labels(varLetConstStatement);

                while (true)
                {
                    var declaration = new VariableDeclaration();

                    // The next token must be a variable name.
                    declaration.VariableName = parser.ExpectIdentifier();
                    parser.ValidateVariableName(declaration.VariableName);

                    // Add the variable to the current function's list of local variables.
                    parser.currentVarScope.AddVariable(declaration.VariableName, null,
                                                       parser.context == CodeContext.Function ? null : new LiteralExpression(Undefined.Value));

                    // The next token is either an equals sign (=), a semi-colon, a comma, or the "in" keyword.
                    if (parser.nextToken == PunctuatorToken.Assignment)
                    {
                        // Read past the equals token (=).
                        parser.Expect(PunctuatorToken.Assignment);

                        // Read the setter expression.
                        declaration.InitExpression = parser.ParseExpression(PunctuatorToken.Semicolon, PunctuatorToken.Comma);

                        // This must be a regular for statement.
                        type = ForStatementType.For;
                    }

                    // Add the declaration to the initialization statement.
                    varLetConstStatement.Declarations.Add(declaration);

                    if (parser.nextToken == PunctuatorToken.Semicolon)
                    {
                        // This is a regular for statement.
                        break;
                    }
                    else if (parser.nextToken == KeywordToken.In && type == ForStatementType.Unknown)
                    {
                        // This is a for-in statement.
                        forInOfReference = new NameExpression(scope, declaration.VariableName);
                        type             = ForStatementType.ForIn;
                        break;
                    }
                    else if (parser.nextToken == IdentifierToken.Of && type == ForStatementType.Unknown)
                    {
                        // This is a for-of statement.
                        forInOfReference = new NameExpression(scope, declaration.VariableName);
                        type             = ForStatementType.ForOf;
                        break;
                    }
                    else if (parser.nextToken != PunctuatorToken.Comma)
                    {
                        throw new JavaScriptException(parser.engine, "SyntaxError", string.Format("Unexpected token {0}", Token.ToText(parser.nextToken)), parser.LineNumber, parser.SourcePath);
                    }

                    // Read past the comma token.
                    parser.Expect(PunctuatorToken.Comma);

                    // Multiple initializers are not allowed in for-in statements.
                    type = ForStatementType.For;
                }
            }
            else
            {
                // Not a var initializer - can be a simple variable name then "in" or any expression ending with a semi-colon.
                // The expression can be empty.
                if (parser.nextToken != PunctuatorToken.Semicolon)
                {
                    // Parse an expression.
                    var initializationExpression = parser.ParseExpression(PunctuatorToken.Semicolon, KeywordToken.In, IdentifierToken.Of);

                    // Record debug info for the expression.
                    initializationStatement = new ExpressionStatement(initializationExpression);

                    if (parser.nextToken == KeywordToken.In)
                    {
                        // This is a for-in statement.
                        if ((initializationExpression is IReferenceExpression) == false)
                        {
                            throw new JavaScriptException(parser.engine, "SyntaxError", "Invalid left-hand side in for-in", parser.LineNumber, parser.SourcePath);
                        }
                        forInOfReference = (IReferenceExpression)initializationExpression;
                    }
                    else if (parser.nextToken == IdentifierToken.Of)
                    {
                        // This is a for-of statement.
                        if ((initializationExpression is IReferenceExpression) == false)
                        {
                            throw new JavaScriptException(parser.engine, "SyntaxError", "Invalid left-hand side in for-of", parser.LineNumber, parser.SourcePath);
                        }
                        forInOfReference = (IReferenceExpression)initializationExpression;
                        type             = ForStatementType.ForOf;
                    }
                }
            }

            if (type == ForStatementType.ForIn)
            {
                // for (x in y)
                // for (var x in y)
                var result   = new ForInStatement();
                var labelled = parser.Labels(result);

                result.Variable = forInOfReference;

                // Consume the "in".
                parser.Expect(KeywordToken.In);

                // Parse the right-hand-side expression.
                result.TargetObject = parser.ParseExpression(PunctuatorToken.RightParenthesis);

                // Read the right parenthesis.
                parser.Expect(PunctuatorToken.RightParenthesis);

                // Read the statements that will be executed in the loop body.
                result.Body = parser.ParseStatement();

                return(labelled);
            }
            else if (type == ForStatementType.ForOf)
            {
                // for (x of y)
                // for (var x of y)
                var result   = new ForOfStatement();
                var labelled = parser.Labels(result);

                result.Variable = forInOfReference;

                // Consume the "of".
                parser.Expect(IdentifierToken.Of);

                // Parse the right-hand-side expression.
                result.TargetObject = parser.ParseExpression(PunctuatorToken.RightParenthesis, PunctuatorToken.Comma); // Comma is not allowed.

                // Read the right parenthesis.
                parser.Expect(PunctuatorToken.RightParenthesis);

                // Read the statements that will be executed in the loop body.
                result.Body = parser.ParseStatement();

                return(labelled);
            }
            else
            {
                var result   = new ForStatement();
                var labelled = parser.Labels(result);

                // Set the initialization statement.
                if (initializationStatement != null)
                {
                    result.InitStatement = initializationStatement;
                }

                // Read the semicolon.
                parser.Expect(PunctuatorToken.Semicolon);

                // Parse the optional condition expression.
                // Note: if the condition is omitted then it is considered to always be true.
                if (parser.nextToken != PunctuatorToken.Semicolon)
                {
                    result.ConditionStatement = new ExpressionStatement(parser.ParseExpression(PunctuatorToken.Semicolon));
                }

                // Read the semicolon.
                // Note: automatic semicolon insertion never inserts a semicolon in the header of a
                // for statement.
                parser.Expect(PunctuatorToken.Semicolon);

                // Parse the optional increment expression.
                if (parser.nextToken != PunctuatorToken.RightParenthesis)
                {
                    result.IncrementStatement = new ExpressionStatement(parser.ParseExpression(PunctuatorToken.RightParenthesis));
                }

                // Read the right parenthesis.
                parser.Expect(PunctuatorToken.RightParenthesis);

                // Read the statements that will be executed in the loop body.
                result.Body = parser.ParseStatement();

                return(labelled);
            }
        }