/*
 *  public MetaObjectBase FindMetaObject(object target) {
 *    //built-in objects - lookup in MetaObjects table
 *    MetaObjectBase meta;
 *    if (_runtime.MetaObjects.TryGetValue(target.GetType(), out meta))
 *      return meta;
 *    //Check scriptObject
 *    var scriptObj = target as ScriptObjectBase;
 *    if (scriptObj != null)
 *      return scriptObj.MetaObject;
 *    return null;
 *  }
 *
 *  public MethodImp FindMethod(object target, string methodName, int argCount) {
 *    var metaObj = FindMetaObject(target);
 *    if (metaObj == null)
 *      throw new RuntimeException(String.Format("MetaObject not found for object {0}, method {1}", target, methodName));
 *    return metaObj.FindMethod(_context, target, methodName, argCount);
 *  }
 *
 *  public void ExecuteMethod(object target, string methodName, int argCount) {
 *    var method = FindMethod(target, methodName, argCount);
 *    if (method == null)
 *      throw new Exception("Method not found: " + methodName);
 *    _context.CurrentFrame = new StackFrame(method, _context.CurrentFrame, method.OwnerFrame, _context.Data, argCount);
 *    method.Execute(_context, target, argCount);
 *    //return back new frame
 *    _context.PopFrame();
 *    object result = _context.LastResult;
 *    _context.Data.Replace(argCount + 1, result);
 *  }//method
 */
        //node parameter is used for error reporting
        public void ExecuteBinaryOperator(string op)
        {
            var arg1 = _context.Data[1];
            var arg2 = _context.Data[0];
            var key  = OperatorDispatchKey.CreateFromArgs(op, arg1, arg2);
            OperatorImplementation opImpl;

            if (!OperatorImplementations.TryGetValue(key, out opImpl))
            {
                opImpl = _runtime.AddOperatorImplementation(OperatorImplementations, key);
            }
            if (opImpl != null)
            {
                try {
                    var result = opImpl.Evaluate(arg1, arg2);
                    _context.Data.Replace(2, result);
                    return;
                } catch (OverflowException) {
                    if (TryConvertArgsOnOverflow(opImpl.BaseType))
                    {
                        ExecuteBinaryOperator(op); //call self recursively, now with new arg types
                        return;
                    }
                    throw;
                } //catch
            }     //if

            //Treating as normal call - first comes implementor (arg1), then argument (ag2); something like:
            // a + b  =>   a._add(b)
            //ExecuteMethod(arg1, op, 1);
            _context.ThrowError(Resources.ErrOpNotImplemented, op);
        }//method
        private OperatorImplementation FindBaseImplementation(ExpressionType op, Type commonType)
        {
            var baseKey = new OperatorDispatchKey(op, commonType, commonType);

            OperatorImplementations.TryGetValue(baseKey, out OperatorImplementation baseImpl);
            return(baseImpl);
        }
        protected void AddImplementation(string op, Type baseType, BinaryOperatorMethod baseMethod, TypeConverter resultConverter)
        {
            var key = OperatorDispatchKey.CreateFromTypes(op, baseType, baseType);
            var imp = new OperatorImplementation(key, baseType, baseMethod, null, null, resultConverter);

            _baseOperatorImplementations.Add(key, imp);
        }
        protected OperatorImplementation AddUnary(ExpressionType op, Type commonType, UnaryOperatorMethod unaryMethod)
        {
            var key  = new OperatorDispatchKey(op, commonType);
            var impl = new OperatorImplementation(key, commonType, null, unaryMethod, null, null);

            OperatorImplementations[key] = impl;
            return(impl);
        }
        protected OperatorImplementation AddConverter(Type fromType, Type toType, UnaryOperatorMethod method)
        {
            var key  = new OperatorDispatchKey(ExpressionType.ConvertChecked, fromType, toType);
            var impl = new OperatorImplementation(key, toType, method);

            OperatorImplementations[key] = impl;
            return(impl);
        }
        protected OperatorImplementation AddBinary(ExpressionType op, Type commonType,
                                                   BinaryOperatorMethod binaryMethod, UnaryOperatorMethod resultConverter)
        {
            var key  = new OperatorDispatchKey(op, commonType, commonType);
            var impl = new OperatorImplementation(key, commonType, binaryMethod, null, null, resultConverter);

            OperatorImplementations[key] = impl;
            return(impl);
        }
 //Constructor for unary operators and type converters
 public OperatorImplementation(OperatorDispatchKey key, Type type, UnaryOperatorMethod method)
 {
     Key              = key;
     CommonType       = type;
     Arg1Converter    = method;
     Arg2Converter    = null;
     ResultConverter  = null;
     BaseBinaryMethod = null;
 }
        protected virtual OperatorImplementation CreateBinaryOperatorImplementation(ExpressionType op, Type arg1Type, Type arg2Type,
                                                                                    Type commonType, BinaryOperatorMethod method, UnaryOperatorMethod resultConverter)
        {
            var key           = new OperatorDispatchKey(op, arg1Type, arg2Type);
            var arg1Converter = arg1Type == commonType ? null : GetConverter(arg1Type, commonType);
            var arg2Converter = arg2Type == commonType ? null : GetConverter(arg2Type, commonType);
            var impl          = new OperatorImplementation(key, commonType, method, arg1Converter, arg2Converter, resultConverter);

            return(impl);
        }
 public OperatorImplementation(OperatorDispatchKey key, Type baseType, BinaryOperatorMethod baseMethod,
                               TypeConverter arg1Converter, TypeConverter arg2Converter, TypeConverter resultConverter)
 {
     Key             = key;
     BaseType        = baseType;
     Arg1Converter   = arg1Converter;
     Arg2Converter   = arg2Converter;
     ResultConverter = resultConverter;
     BaseMethod      = baseMethod;
     SetupEvaluationMethod();
 }
        // Important: returns null if fromType == toType
        public virtual UnaryOperatorMethod GetConverter(Type fromType, Type toType)
        {
            if (fromType == toType)
            {
                return(x => x);
            }

            var key = new OperatorDispatchKey(ExpressionType.ConvertChecked, fromType, toType);

            return(!OperatorImplementations.TryGetValue(key, out OperatorImplementation impl) ? null : impl.Arg1Converter);
        }
 //Constructor for binary operators
 public OperatorImplementation(OperatorDispatchKey key, Type resultType, BinaryOperatorMethod baseBinaryMethod,
                               UnaryOperatorMethod arg1Converter, UnaryOperatorMethod arg2Converter, UnaryOperatorMethod resultConverter)
 {
     Key              = key;
     CommonType       = resultType;
     Arg1Converter    = arg1Converter;
     Arg2Converter    = arg2Converter;
     ResultConverter  = resultConverter;
     BaseBinaryMethod = baseBinaryMethod;
     SetupEvaluationMethod();
 }
        private object ConvertValue(object value, Type toType)
        {
            var           key = OperatorDispatchKey.CreateForTypeConverter(value.GetType(), toType);
            TypeConverter converter;

            if (_runtime.TypeConverters.TryGetValue(key, out converter))
            {
                var result = converter.Invoke(value);
                return(result);
            }
            throw new Exception(string.Format("Failed to convert value '%1' to type %2.", value, toType));
        }
        public object ExecuteUnaryOperator(ExpressionType op, object arg1, ref OperatorImplementation previousUsed)
        {
            // 1. Get arg type
            Type arg1Type;

            try {
                arg1Type = arg1.GetType();
            } catch (NullReferenceException) {
                CheckUnassigned(arg1);
                throw;
            }

            // 2. If we had prev impl, check if current args types match it; first copy it into local variable
            OperatorDispatchKey key;
            var currentImpl = previousUsed;

            if (currentImpl != null && arg1Type != currentImpl.Key.Arg1Type)
            {
                currentImpl = null;
            }

            // 3. Find implementation for arg type
            if (currentImpl == null)
            {
                key = new OperatorDispatchKey(op, arg1Type);
                if (!OperatorImplementations.TryGetValue(key, out currentImpl))
                {
                    ThrowError(Resources.ErrOpNotDefinedForType, op, arg1Type);
                }
            }

            // 4. Actually call
            try {
                previousUsed = currentImpl; //set previousUsed so next time we'll try this impl first
                return(currentImpl.Arg1Converter(arg1));
            } catch (OverflowException) {
                if (currentImpl.OverflowHandler == null)
                {
                    throw;
                }

                previousUsed = currentImpl.OverflowHandler;               //set previousUsed to overflowHandler, so it will be used next time
                return(ExecuteUnaryOperator(op, arg1, ref previousUsed)); //call self recursively
            }
        }
Exemple #14
0
/*
 *  public virtual FunctionBindingInfo GetFunctionBindingInfo(string name, AstNodeList  parameters) {
 *    return FunctionBindings.Find(name, parameters.Count);
 *  }
 *  //Utility methods for adding library functions
 *  public FunctionBindingInfo AddFunction(string name, int paramCount) {
 *    return null;
 *  }
 *  public FunctionBindingInfo AddFunction(string name, int paramCount, FunctionFlags flags) {
 *    FunctionBindingInfo info = new FunctionBindingInfo(name, paramCount, null, flags);
 *    FunctionBindings.Add(name, info);
 *    return info;
 *  }
 */

        #region Operator implementations
        // When an implementation for exact type pair is not found, we find implementation for base type and create
        // implementation for exact types using type converters
        public virtual OperatorImplementation AddOperatorImplementation(OperatorImplementationTable implementations, OperatorDispatchKey forKey)
        {
            Type baseType = GetBaseTypeForExpression(forKey.OpSymbol, forKey.Arg1Type, forKey.Arg2Type);

            if (baseType == null)
            {
                return(null);
            }
            TypeConverter arg1Converter = GetConverter(forKey.Arg1Type, baseType);
            TypeConverter arg2Converter = GetConverter(forKey.Arg2Type, baseType);
            //Get base method for the operator and common type
            var baseKey = OperatorDispatchKey.CreateFromTypes(forKey.OpSymbol, baseType, baseType);
            OperatorImplementation baseImpl;

            if (!_baseOperatorImplementations.TryGetValue(baseKey, out baseImpl))
            {
                throw new Exception(string.Format(Resources.ErrOpNotDefinedForTypes, forKey.OpSymbol, forKey.Arg1Type, forKey.Arg2Type));
            }
            var impl = new OperatorImplementation(forKey, baseType, baseImpl.BaseMethod, arg1Converter, arg2Converter, baseImpl.ResultConverter);

            implementations[forKey] = impl;
            return(impl);
        }
        public object ExecuteBinaryOperator(ExpressionType op, object arg1, object arg2, ref OperatorImplementation previousUsed)
        {
            // 1. Get arg types
            Type arg1Type, arg2Type;

            try {
                arg1Type = arg1.GetType();
                arg2Type = arg2.GetType();
            } catch (NullReferenceException) {
                // arg1 or arg2 is null - which means never assigned.
                CheckUnassigned(arg1);
                CheckUnassigned(arg2);
                throw;
            }

            // 2. If we had prev impl, check if current args types match it; first copy it into local variable
            // Note: BinaryExpression node might already have tried it directly, without any checks, and
            // apparently failed. At some point this attempt in BinaryExpressionNode can become disabled.
            // But we might still try it here, with proper checks
            var currentImpl = previousUsed;

            if (currentImpl != null && (arg1Type != currentImpl.Key.Arg1Type || arg2Type != currentImpl.Key.Arg2Type))
            {
                currentImpl = null;
            }

            // 3. Find implementation for arg types
            OperatorDispatchKey key;

            if (currentImpl == null)
            {
                key = new OperatorDispatchKey(op, arg1Type, arg2Type);
                if (!OperatorImplementations.TryGetValue(key, out currentImpl))
                {
                    ThrowScriptError(Resources.ErrOpNotDefinedForTypes, op, arg1Type, arg2Type);
                }
            }

            // 4. Actually call
            try {
                previousUsed = currentImpl;
                return(currentImpl.EvaluateBinary(arg1, arg2));
            } catch (OverflowException) {
                if (currentImpl.OverflowHandler == null)
                {
                    throw;
                }
                previousUsed = currentImpl.OverflowHandler;                      //set previousUsed to overflowHandler, so it will be used next time
                return(ExecuteBinaryOperator(op, arg1, arg2, ref previousUsed)); //call self recursively
            } catch (IndexOutOfRangeException) {
                //We can get here only if we use SmartBoxing - the result is out of range of pre-allocated boxes,
                // so attempt to lookup a boxed value in _boxes dictionary fails with outOfRange exc
                if (currentImpl.NoBoxImplementation == null)
                {
                    throw;
                }
                // If NoBoxImpl is not null, then it is implementation with auto-boxing.
                // Auto-boxing failed - the result is outside the range of our boxes array. Let's call no-box version.
                // we also set previousUsed to no-box implementation, so we use it in the future calls
                previousUsed = currentImpl.NoBoxImplementation;
                return(ExecuteBinaryOperator(op, arg1, arg2, ref previousUsed)); //call self recursively
            }
        }//method