示例#1
0
        /// <summary>
        /// Gets the type that results from evaluating this expression.
        /// </summary>
        public override Type GetResultType(OptimizationInfo optimizationInfo)
        {
            var type = this.OperatorType;

            switch (this.OperatorType)
            {
            // Add
            case OperatorType.Add:
            {
                var lhs = this.Left.GetResultType(optimizationInfo);
                var rhs = this.Right.GetResultType(optimizationInfo);
                if (lhs == typeof(string) || rhs == typeof(string))
                {
                    return(typeof(ConcatenatedString));
                }
                if (lhs == typeof(ConcatenatedString) || rhs == typeof(ConcatenatedString))
                {
                    return(typeof(ConcatenatedString));
                }

                // If the two types are numeric integers, retain the one with the most accuracy.
                Type numeric = TypeConverter.MostAccurateInteger(lhs, rhs);

                if (numeric != null)
                {
                    return(numeric);
                }

                if (lhs == typeof(object) || lhs == typeof(Library.ObjectInstance) ||
                    rhs == typeof(object) || rhs == typeof(Library.ObjectInstance))
                {
                    return(typeof(object));
                }
                return(typeof(double));
            }

            // Arithmetic operations.
            case OperatorType.Subtract:
            case OperatorType.Multiply:
            case OperatorType.Divide:
            case OperatorType.Modulo:
                return(typeof(double));

            // Bitwise operations.
            case OperatorType.BitwiseAnd:
            case OperatorType.BitwiseOr:
            case OperatorType.BitwiseXor:
            case OperatorType.LeftShift:
            case OperatorType.SignedRightShift:
                return(typeof(int));

            case OperatorType.UnsignedRightShift:
                return(typeof(double));

            // Relational operations.
            case OperatorType.LessThan:
            case OperatorType.LessThanOrEqual:
            case OperatorType.GreaterThan:
            case OperatorType.GreaterThanOrEqual:
                return(typeof(bool));

            // Equality operations.
            case OperatorType.Equal:
            case OperatorType.StrictlyEqual:
            case OperatorType.NotEqual:
            case OperatorType.StrictlyNotEqual:
                return(typeof(bool));

            // Logical operations.
            case OperatorType.LogicalAnd:
            {
                var lhs = this.Left.GetResultType(optimizationInfo);
                var rhs = this.Right.GetResultType(optimizationInfo);

                // If lhs is true (regardless of whatever it's type is) then we respond with rhs.
                // If it's false, we respond with typeof(bool).

                // Try and eval left statically:
                object leftEval = Left.Evaluate();

                if (leftEval == null)
                {
                    // Can't statically eval it.

                    // If rhs is a bool then great, we know for sure we're returning a bool.
                    if (rhs == typeof(bool))
                    {
                        return(typeof(bool));
                    }

                    // Either a bool or rhs. For now this is an error condition.
                    optimizationInfo.TypeError(
                        "Ambiguous type && statement. It returns either a boolean or '" + rhs +
                        "'. Add ==true to the right hand side to make sure it only returns a boolean."
                        );

                    return(typeof(object));
                }

                if (leftEval != null && TypeConverter.ToBoolean(leftEval))
                {
                    // Ok! We'll be returning rhs no matter what.
                    return(rhs);
                }

                // Left eval was false so we'll be returning false.
                return(typeof(bool));
            }

            case OperatorType.LogicalOr:
            {
                // The result is either the left-hand side or the right-hand side.
                var lhs = this.Left.GetResultType(optimizationInfo);
                var rhs = this.Right.GetResultType(optimizationInfo);
                if (lhs == rhs)
                {
                    return(lhs);
                }
                if (PrimitiveTypeUtilities.IsNumeric(lhs) && PrimitiveTypeUtilities.IsNumeric(rhs))
                {
                    return(typeof(double));
                }

                // If either evaluates statically to 0 then we also know the correct return type.
                object leftEval = Left.Evaluate();

                if (leftEval != null && !TypeConverter.ToBoolean(leftEval))
                {
                    return(rhs);
                }

                object rightEval = Right.Evaluate();

                if (rightEval != null && !TypeConverter.ToBoolean(rightEval))
                {
                    return(lhs);
                }

                return(typeof(object));
            }

            // Misc
            case OperatorType.InstanceOf:
            case OperatorType.In:
                return(typeof(bool));
            }
            throw new NotImplementedException();
        }
示例#2
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));
            }
        }