/// <summary>
        /// Executes the specified indexer method.
        /// </summary>
        /// <param name="mdesc">The method descriptor</param>
        /// <param name="script">The script.</param>
        /// <param name="obj">The object.</param>
        /// <param name="index">The indexer parameter</param>
        /// <param name="value">The dynvalue to set on a setter, or null.</param>
        /// <returns></returns>
        /// <exception cref="System.NotImplementedException"></exception>
        protected virtual DynValue ExecuteIndexer(StandardUserDataOverloadedMethodDescriptor mdesc, Script script, object obj, DynValue index, DynValue value)
        {
            var callback = mdesc.GetCallback(script, obj);

            IList <DynValue> values;

            if (index.Type == DataType.Tuple)
            {
                if (value == null)
                {
                    values = index.Tuple;
                }
                else
                {
                    values = new List <DynValue>(index.Tuple);
                    values.Add(value);
                }
            }
            else
            {
                if (value == null)
                {
                    values = new DynValue[] { index };
                }
                else
                {
                    values = new DynValue[] { index, value };
                }
            }

            CallbackArguments      args    = new CallbackArguments(values, false);
            ScriptExecutionContext execCtx = script.CreateDynamicExecutionContext();

            return(callback(execCtx, args));
        }
        /// <summary>
        /// Performs an "index" "get" operation. This tries to resolve minor variations of member names.
        /// </summary>
        /// <param name="script">The script originating the request</param>
        /// <param name="obj">The object (null if a static request is done)</param>
        /// <param name="index">The index.</param>
        /// <param name="isDirectIndexing">If set to true, it's indexed with a name, if false it's indexed through brackets.</param>
        /// <returns></returns>
        public virtual DynValue Index(Script script, object obj, DynValue index, bool isDirectIndexing)
        {
            if (!isDirectIndexing)
            {
                StandardUserDataOverloadedMethodDescriptor mdesc = m_Methods.GetOrDefault(SPECIAL_GETITEM);

                if (mdesc != null)
                {
                    return(ExecuteIndexer(mdesc, script, obj, index, null));
                }
            }

            index = index.ToScalar();

            if (index.Type != DataType.String)
            {
                throw ScriptRuntimeException.BadArgument(1, string.Format("userdata<{0}>.__index", this.Name), "string", index.Type.ToLuaTypeString(), false);
            }

            DynValue v = TryIndex(script, obj, index.String);

            if (v == null)
            {
                v = TryIndex(script, obj, UpperFirstLetter(index.String));
            }
            if (v == null)
            {
                v = TryIndex(script, obj, Camelify(index.String));
            }
            if (v == null)
            {
                v = TryIndex(script, obj, UpperFirstLetter(Camelify(index.String)));
            }

            if (v == null && m_ExtMethodsVersion < UserData.GetExtensionMethodsChangeVersion())
            {
                m_ExtMethodsVersion = UserData.GetExtensionMethodsChangeVersion();

                v = TryIndexOnExtMethod(script, obj, index.String);
                if (v == null)
                {
                    v = TryIndexOnExtMethod(script, obj, UpperFirstLetter(index.String));
                }
                if (v == null)
                {
                    v = TryIndexOnExtMethod(script, obj, Camelify(index.String));
                }
                if (v == null)
                {
                    v = TryIndexOnExtMethod(script, obj, UpperFirstLetter(Camelify(index.String)));
                }
            }

            return(v);
        }
        /// <summary>
        /// Gets a "meta" operation on this userdata. If a descriptor does not support this functionality,
        /// it should return "null" (not a nil).
        /// See <see cref="IUserDataDescriptor.MetaIndex" /> for further details.
        ///
        /// If a method exists marked with <see cref="MoonSharpUserDataMetamethodAttribute" /> for the specific
        /// metamethod requested, that method is returned.
        ///
        /// If the above fails, the following dispatching occur:
        ///
        /// __add, __sub, __mul, __div, __mod and __unm are dispatched to C# operator overloads (if they exist)
        /// __eq is dispatched to System.Object.Equals.
        /// __lt and __le are dispatched IComparable.Compare, if the type implements IComparable or IComparable{object}
        /// __len is dispatched to Length and Count properties, if those exist.
        /// __iterator is handled if the object implements IEnumerable or IEnumerator.
        /// __tonumber is dispatched to implicit or explicit conversion operators to standard numeric types.
        /// __tobool is dispatched to an implicit or explicit conversion operator to bool. If that fails, operator true is used.
        ///
        /// <param name="script">The script originating the request</param>
        /// <param name="obj">The object (null if a static request is done)</param>
        /// <param name="metaname">The name of the metamember.</param>
        /// </summary>
        /// <returns></returns>
        public virtual DynValue MetaIndex(Script script, object obj, string metaname)
        {
            StandardUserDataOverloadedMethodDescriptor desc = m_MetaMethods.GetOrDefault(metaname);

            if (desc != null)
            {
                return(desc.GetCallbackAsDynValue(script, obj));
            }

            switch (metaname)
            {
            case "__add":
                return(DispatchMetaOnMethod(script, obj, "op_Addition"));

            case "__sub":
                return(DispatchMetaOnMethod(script, obj, "op_Subtraction"));

            case "__mul":
                return(DispatchMetaOnMethod(script, obj, "op_Multiply"));

            case "__div":
                return(DispatchMetaOnMethod(script, obj, "op_Division"));

            case "__mod":
                return(DispatchMetaOnMethod(script, obj, "op_Modulus"));

            case "__unm":
                return(DispatchMetaOnMethod(script, obj, "op_UnaryNegation"));

            case "__eq":
                return(MultiDispatchEqual(script, obj));

            case "__lt":
                return(MultiDispatchLessThan(script, obj));

            case "__le":
                return(MultiDispatchLessThanOrEqual(script, obj));

            case "__len":
                return(TryDispatchLength(script, obj));

            case "__tonumber":
                return(TryDispatchToNumber(script, obj));

            case "__tobool":
                return(TryDispatchToBool(script, obj));

            case "__iterator":
                return(ClrToScriptConversions.EnumerationToDynValue(script, obj));

            default:
                return(null);
            }
        }
        private DynValue DispatchMetaOnMethod(Script script, object obj, string methodName)
        {
            StandardUserDataOverloadedMethodDescriptor desc = m_Methods.GetOrDefault(methodName);

            if (desc != null)
            {
                return(desc.GetCallbackAsDynValue(script, obj));
            }
            else
            {
                return(null);
            }
        }
        /// <summary>
        /// Tries to perform an indexing operation by checking newly added extension methods for the given indexName.
        /// </summary>
        /// <param name="script">The script.</param>
        /// <param name="obj">The object.</param>
        /// <param name="indexName">Member name to be indexed.</param>
        /// <returns></returns>
        /// <exception cref="System.NotImplementedException"></exception>
        private DynValue TryIndexOnExtMethod(Script script, object obj, string indexName)
        {
            List <StandardUserDataMethodDescriptor> methods = UserData.GetExtensionMethodsByName(indexName)
                                                              .Where(d => d.ExtensionMethodType != null && d.ExtensionMethodType.IsAssignableFrom(this.Type))
                                                              .ToList();

            if (methods != null && methods.Count > 0)
            {
                var ext = new StandardUserDataOverloadedMethodDescriptor(indexName, this.Type);
                ext.SetExtensionMethodsSnapshot(UserData.GetExtensionMethodsChangeVersion(), methods);
                m_Methods.Add(indexName, ext);
                return(DynValue.NewCallback(ext.GetCallback(script, obj)));
            }

            return(null);
        }
        /// <summary>
        /// Performs an "index" "set" operation. This tries to resolve minor variations of member names.
        /// </summary>
        /// <param name="script">The script originating the request</param>
        /// <param name="obj">The object (null if a static request is done)</param>
        /// <param name="index">The index.</param>
        /// <param name="value">The value to be set</param>
        /// <param name="isDirectIndexing">If set to true, it's indexed with a name, if false it's indexed through brackets.</param>
        /// <returns></returns>
        public virtual bool SetIndex(Script script, object obj, DynValue index, DynValue value, bool isDirectIndexing)
        {
            if (!isDirectIndexing)
            {
                StandardUserDataOverloadedMethodDescriptor mdesc = m_Methods.GetOrDefault(SPECIAL_SETITEM);

                if (mdesc != null)
                {
                    ExecuteIndexer(mdesc, script, obj, index, value);
                    return(true);
                }
            }

            index = index.ToScalar();

            if (index.Type != DataType.String)
            {
                throw ScriptRuntimeException.BadArgument(1, string.Format("userdata<{0}>.__setindex", this.Name), "string", index.Type.ToLuaTypeString(), false);
            }

            bool v = TrySetIndex(script, obj, index.String, value);

            if (!v)
            {
                v = TrySetIndex(script, obj, UpperFirstLetter(index.String), value);
            }
            if (!v)
            {
                v = TrySetIndex(script, obj, Camelify(index.String), value);
            }
            if (!v)
            {
                v = TrySetIndex(script, obj, UpperFirstLetter(Camelify(index.String)), value);
            }

            return(v);
        }
        /// <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);
                    }
                }
            }
        }