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