Example #1
0
        protected internal StandardUserDataDescriptor(Type type, InteropAccessMode accessMode, string friendlyName)
        {
            if (accessMode == InteropAccessMode.Default)
            {
                accessMode = UserData.DefaultAccessMode;
            }

            Type         = type;
            Name         = type.FullName;
            AccessMode   = accessMode;
            FriendlyName = friendlyName;

            if (AccessMode != InteropAccessMode.HideMembers)
            {
                foreach (MethodInfo mi in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Static))
                {
                    if (CheckVisibility(mi.GetCustomAttributes(true), mi.IsPublic))
                    {
                        if (mi.IsSpecialName)
                        {
                            continue;
                        }

                        var md = new StandardUserDataMethodDescriptor(mi, this.AccessMode);

                        if (m_Methods.ContainsKey(md.Name))
                        {
                            continue;
                        }
                        //throw new ArgumentException(string.Format("{0}.{1} has overloads", Name, md.Name));

                        m_Methods.Add(md.Name, md);
                    }
                }

                foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
                {
                    if (CheckVisibility(pi.GetCustomAttributes(true), IsPropertyInfoPublic(pi)))
                    {
                        var pd = new StandardUserDataPropertyDescriptor(pi, this.AccessMode);
                        m_Properties.Add(pd.Name, pd);
                    }
                }
            }
        }
        private void Cache(bool hasObject, CallbackArguments args, StandardUserDataMethodDescriptor bestOverload)
        {
            int lowestHits          = int.MaxValue;
            OverloadCacheItem found = null;

            for (int i = 0; i < m_Cache.Length; i++)
            {
                if (m_Cache[i] == null)
                {
                    found = new OverloadCacheItem()
                    {
                        ArgsDataType = new List <DataType>()
                    };
                    m_Cache[i] = found;
                    break;
                }
                else if (m_Cache[i].HitIndexAtLastHit < lowestHits)
                {
                    lowestHits = m_Cache[i].HitIndexAtLastHit;
                    found      = m_Cache[i];
                }
            }

            if (found == null)
            {
                // overflow..
                m_Cache = new OverloadCacheItem[CACHE_SIZE];
                found   = new OverloadCacheItem()
                {
                    ArgsDataType = new List <DataType>()
                };
                m_Cache[0]  = found;
                m_CacheHits = 0;
            }

            found.Method            = bestOverload;
            found.HitIndexAtLastHit = ++m_CacheHits;
            found.ArgsDataType.Clear();
            found.HasObject = hasObject;

            for (int i = 0; i < args.Count; i++)
            {
                found.ArgsDataType.Add(args[i].Type);
            }
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="StandardUserDataOverloadedMethodDescriptor" /> class.
 /// </summary>
 /// <param name="name">The name.</param>
 /// <param name="declaringType">The declaring type.</param>
 /// <param name="descriptor">The descriptor of the first overloaded method.</param>
 public StandardUserDataOverloadedMethodDescriptor(string name, Type declaringType, StandardUserDataMethodDescriptor descriptor)
     : this(name, declaringType)
 {
     m_Overloads.Add(descriptor);
 }
        /// <summary>
        /// Calculates the score for the overload.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="args">The arguments.</param>
        /// <param name="method">The method.</param>
        /// <param name="isExtMethod">if set to <c>true</c>, is an extension method.</param>
        /// <returns></returns>
        private int CalcScoreForOverload(ScriptExecutionContext context, CallbackArguments args, StandardUserDataMethodDescriptor method, bool isExtMethod)
        {
            int  totalScore  = ScriptToClrConversions.WEIGHT_EXACT_MATCH;
            int  argsBase    = args.IsMethodCall ? 1 : 0;
            int  argsCnt     = argsBase;
            bool varArgsUsed = false;

            for (int i = 0; i < method.Parameters.Length; i++)
            {
                if (isExtMethod && i == 0)
                {
                    continue;
                }

                Type parameterType = method.Parameters[i].ParameterType;

                if ((parameterType == typeof(Script)) || (parameterType == typeof(ScriptExecutionContext)) || (parameterType == typeof(CallbackArguments)))
                {
                    continue;
                }

                if (i == method.Parameters.Length - 1 && method.VarArgsArrayType != null)
                {
                    int      varargCnt          = 0;
                    DynValue firstArg           = null;
                    int      scoreBeforeVargars = totalScore;

                    // update score for varargs
                    while (true)
                    {
                        var arg = args.RawGet(argsCnt, false);
                        if (arg == null)
                        {
                            break;
                        }

                        if (firstArg == null)
                        {
                            firstArg = arg;
                        }

                        argsCnt += 1;

                        varargCnt += 1;

                        int score = CalcScoreForSingleArgument(method.VarArgsElementType, arg, isOptional: false);
                        totalScore = Math.Min(totalScore, score);
                    }

                    // check if exact-match
                    if (varargCnt == 1)
                    {
                        if (firstArg.Type == DataType.UserData && firstArg.UserData.Object != null)
                        {
                            if (method.VarArgsArrayType.IsAssignableFrom(firstArg.UserData.Object.GetType()))
                            {
                                totalScore = scoreBeforeVargars;
                                continue;
                            }
                        }
                    }

                    // apply varargs penalty to score
                    if (varargCnt == 0)
                    {
                        totalScore = Math.Min(totalScore, ScriptToClrConversions.WEIGHT_VARARGS_EMPTY);
                    }

                    varArgsUsed = true;
                }
                else
                {
                    var arg = args.RawGet(argsCnt, false) ?? DynValue.Void;

                    int score = CalcScoreForSingleArgument(parameterType, arg, !(method.Parameters[i].DefaultValue.IsDbNull()));

                    totalScore = Math.Min(totalScore, score);

                    argsCnt += 1;
                }
            }

            if (totalScore > 0)
            {
                if ((args.Count - argsBase) <= method.Parameters.Length)
                {
                    totalScore += ScriptToClrConversions.WEIGHT_NO_EXTRA_PARAMS_BONUS;
                    totalScore *= 1000;
                }
                else if (varArgsUsed)
                {
                    totalScore -= ScriptToClrConversions.WEIGHT_VARARGS_MALUS;
                    totalScore *= 1000;
                }
                else
                {
                    totalScore *= 1000;
                    totalScore -= ScriptToClrConversions.WEIGHT_EXTRA_PARAMS_MALUS * ((args.Count - argsBase) - method.Parameters.Length);
                    totalScore  = Math.Max(1, totalScore);
                }
            }

#if DEBUG_OVERLOAD_RESOLVER
            System.Diagnostics.Debug.WriteLine(string.Format("[OVERLOAD] : Score {0} for method {1}", totalScore, method.SortDiscriminant));
#endif
            return(totalScore);
        }
        /// <summary>
        /// Performs the overloaded call.
        /// </summary>
        /// <param name="script">The script.</param>
        /// <param name="obj">The object.</param>
        /// <param name="context">The context.</param>
        /// <param name="args">The arguments.</param>
        /// <returns></returns>
        /// <exception cref="ScriptRuntimeException">function call doesn't match any overload</exception>
        private DynValue PerformOverloadedCall(Script script, object obj, ScriptExecutionContext context, CallbackArguments args)
        {
            bool extMethodCacheNotExpired = IgnoreExtensionMethods || m_ExtensionMethodVersion == UserData.GetExtensionMethodsChangeVersion();

            // common case, let's optimize for it
            if (m_Overloads.Count == 1 && m_ExtOverloads.Count == 0 && extMethodCacheNotExpired)
            {
                return(m_Overloads[0].Callback(script, obj, context, args));
            }

            if (m_Unsorted)
            {
                m_Overloads.Sort();
                m_Unsorted = false;
            }

            if (extMethodCacheNotExpired)
            {
                for (int i = 0; i < m_Cache.Length; i++)
                {
                    if (m_Cache[i] != null && CheckMatch(obj != null, args, m_Cache[i]))
                    {
#if DEBUG_OVERLOAD_RESOLVER
                        System.Diagnostics.Debug.WriteLine(string.Format("[OVERLOAD] : CACHED! slot {0}, hits: {1}", i, m_CacheHits));
#endif
                        return(m_Cache[i].Method.Callback(script, obj, context, args));
                    }
                }
            }

            // resolve on overloads first
            int maxScore = 0;
            StandardUserDataMethodDescriptor bestOverload = null;

            for (int i = 0; i < m_Overloads.Count; i++)
            {
                if (obj != null || m_Overloads[i].IsStatic)
                {
                    int score = CalcScoreForOverload(context, args, m_Overloads[i], false);

                    if (score > maxScore)
                    {
                        maxScore     = score;
                        bestOverload = m_Overloads[i];
                    }
                }
            }

            if (!IgnoreExtensionMethods && (obj != null))
            {
                if (!extMethodCacheNotExpired)
                {
                    m_ExtensionMethodVersion = UserData.GetExtensionMethodsChangeVersion();
                    m_ExtOverloads           = UserData.GetExtensionMethodsByName(this.Name)
                                               .Where(d => d.ExtensionMethodType != null && d.ExtensionMethodType.IsAssignableFrom(this.DeclaringType))
                                               .ToList();
                }

                for (int i = 0; i < m_ExtOverloads.Count; i++)
                {
                    int score = CalcScoreForOverload(context, args, m_ExtOverloads[i], true);

                    if (score > maxScore)
                    {
                        maxScore     = score;
                        bestOverload = m_ExtOverloads[i];
                    }
                }
            }


            if (bestOverload != null)
            {
                Cache(obj != null, args, bestOverload);
                return(bestOverload.Callback(script, obj, context, args));
            }

            throw new ScriptRuntimeException("function call doesn't match any overload");
        }
 /// <summary>
 /// Adds an overload.
 /// </summary>
 /// <param name="overload">The overload.</param>
 public void AddOverload(StandardUserDataMethodDescriptor overload)
 {
     m_Overloads.Add(overload);
     m_Unsorted = true;
 }
Example #7
0
        public static DynValue CreateCallbackDynValue(Script script, MethodInfo mi, object obj = null)
        {
            var desc = new StandardUserDataMethodDescriptor(mi);

            return(desc.GetCallbackAsDynValue(script, obj));
        }
        /// <summary>
        /// Checks if the event is compatible with a standard descriptor
        /// </summary>
        /// <param name="ei">The EventInfo.</param>
        /// <param name="throwException">if set to <c>true</c> an exception with the proper error message is thrown if not compatible.</param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentException">
        /// Thrown if throwException is <c>true</c> and one of this applies:
        /// The event is declared in a value type
        /// or
        /// The event does not have both add and remove methods
        /// or
        /// The event handler type doesn't implement a public Invoke method
        /// or
        /// The event handler has a return type which is not System.Void
        /// or
        /// The event handler has more than MAX_ARGS_IN_DELEGATE parameters
        /// or
        /// The event handler has a value type parameter or a by ref parameter
        /// or
        /// The event handler signature is not a valid method according to <see cref="StandardUserDataMethodDescriptor.CheckMethodIsCompatible"/>
        /// </exception>
        public static bool CheckEventIsCompatible(EventInfo ei, bool throwException)
        {
            if (ei.DeclaringType.IsValueType)
            {
                if (throwException)
                {
                    throw new ArgumentException("Events are not supported on value types");
                }
                return(false);
            }

            if ((ei.GetAddMethod(true) == null) || (ei.GetRemoveMethod(true) == null))
            {
                if (throwException)
                {
                    throw new ArgumentException("Event must have add and remove methods");
                }
                return(false);
            }

            MethodInfo invoke = ei.EventHandlerType.GetMethod("Invoke");

            if (invoke == null)
            {
                if (throwException)
                {
                    throw new ArgumentException("Event handler type doesn't seem to be a delegate");
                }
                return(false);
            }

            if (!StandardUserDataMethodDescriptor.CheckMethodIsCompatible(invoke, throwException))
            {
                return(false);
            }

            if (invoke.ReturnType != typeof(void))
            {
                if (throwException)
                {
                    throw new ArgumentException("Event handler cannot have a return type");
                }
                return(false);
            }

            ParameterInfo[] pars = invoke.GetParameters();

            if (pars.Length > MAX_ARGS_IN_DELEGATE)
            {
                if (throwException)
                {
                    throw new ArgumentException(string.Format("Event handler cannot have more than {0} parameters", MAX_ARGS_IN_DELEGATE));
                }
                return(false);
            }

            foreach (ParameterInfo pi in pars)
            {
                if (pi.ParameterType.IsValueType)
                {
                    if (throwException)
                    {
                        throw new ArgumentException("Event handler cannot have value type parameters");
                    }
                    return(false);
                }
                else if (pi.ParameterType.IsByRef)
                {
                    if (throwException)
                    {
                        throw new ArgumentException("Event handler cannot have by-ref type parameters");
                    }
                    return(false);
                }
            }

            return(true);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="StandardUserDataDescriptor"/> class.
        /// </summary>
        /// <param name="type">The type this descriptor refers to.</param>
        /// <param name="accessMode">The interop access mode this descriptor uses for members access</param>
        /// <param name="friendlyName">A human readable friendly name of the descriptor.</param>
        protected internal StandardUserDataDescriptor(Type type, InteropAccessMode accessMode, string friendlyName)
        {
            if (Script.GlobalOptions.Platform.IsRunningOnAOT())
            {
                accessMode = InteropAccessMode.Reflection;
            }

            if (accessMode == InteropAccessMode.Default)
            {
                accessMode = UserData.DefaultAccessMode;
            }

            Type         = type;
            Name         = type.FullName;
            AccessMode   = accessMode;
            FriendlyName = friendlyName;

            if (AccessMode != InteropAccessMode.HideMembers)
            {
                // get first constructor
                StandardUserDataOverloadedMethodDescriptor constructors = null;

                foreach (ConstructorInfo ci in type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
                {
                    StandardUserDataMethodDescriptor md = StandardUserDataMethodDescriptor.TryCreateIfVisible(ci, this.AccessMode);

                    if (md != null)
                    {
                        if (constructors == null)
                        {
                            constructors = new StandardUserDataOverloadedMethodDescriptor("__new", this.Type)
                            {
                                IgnoreExtensionMethods = true
                            }
                        }
                        ;
                        constructors.AddOverload(md);
                    }
                }

                // valuetypes don't reflect their empty ctor.. actually empty ctors are a perversion, we don't care and implement ours
                if (type.IsValueType)
                {
                    if (constructors == null)
                    {
                        constructors = new StandardUserDataOverloadedMethodDescriptor("__new", this.Type);
                    }

                    constructors.AddOverload(new StandardUserDataMethodDescriptor(type, accessMode));
                }

                if (constructors != null)
                {
                    m_Methods.Add("__new", constructors);
                }

                // get methods
                foreach (MethodInfo mi in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
                {
                    StandardUserDataMethodDescriptor md = StandardUserDataMethodDescriptor.TryCreateIfVisible(mi, this.AccessMode);

                    if (md != null)
                    {
                        if (!StandardUserDataMethodDescriptor.CheckMethodIsCompatible(mi, false))
                        {
                            continue;
                        }

                        Dictionary <string, StandardUserDataOverloadedMethodDescriptor> dic = m_Methods;

                        string name = mi.Name;
                        if (mi.IsSpecialName && (mi.Name == CASTINGS_EXPLICIT || mi.Name == CASTINGS_IMPLICIT))
                        {
                            name = GetConversionMethodName(mi.ReturnType);
                        }

                        if (m_Methods.ContainsKey(name))
                        {
                            m_Methods[name].AddOverload(md);
                        }
                        else
                        {
                            m_Methods.Add(name, new StandardUserDataOverloadedMethodDescriptor(name, this.Type, md));
                        }

                        foreach (string metaname in GetMetaNamesFromAttributes(mi))
                        {
                            if (m_MetaMethods.ContainsKey(metaname))
                            {
                                m_MetaMethods[metaname].AddOverload(md);
                            }
                            else
                            {
                                m_MetaMethods.Add(metaname, new StandardUserDataOverloadedMethodDescriptor(metaname, this.Type, md)
                                {
                                    IgnoreExtensionMethods = true
                                });
                            }
                        }
                    }
                }

                // get properties
                foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
                {
                    if (pi.IsSpecialName || pi.GetIndexParameters().Any())
                    {
                        continue;
                    }

                    var pd = StandardUserDataPropertyDescriptor.TryCreateIfVisible(pi, this.AccessMode);
                    if (pd != null)
                    {
                        m_Properties.Add(pd.Name, pd);
                    }
                }

                // get fields
                foreach (FieldInfo fi in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
                {
                    if (fi.IsSpecialName)
                    {
                        continue;
                    }

                    var fd = StandardUserDataFieldDescriptor.TryCreateIfVisible(fi, this.AccessMode);
                    if (fd != null)
                    {
                        m_Fields.Add(fd.Name, fd);
                    }
                }

                // get events
                foreach (EventInfo ei in type.GetEvents(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
                {
                    if (ei.IsSpecialName)
                    {
                        continue;
                    }

                    var ed = StandardUserDataEventDescriptor.TryCreateIfVisible(ei, this.AccessMode);
                    if (ed != null)
                    {
                        m_Events.Add(ei.Name, ed);
                    }
                }
            }
        }