瀏覽代碼

Extension methods, debug.upvaluejoin

Xanathar 10 年之前
父節點
當前提交
ed7936367b

+ 31 - 0
src/MoonSharp.Interpreter.Tests/EndToEnd/UserDataOverloadsTests.cs

@@ -6,6 +6,20 @@ using NUnit.Framework;
 
 namespace MoonSharp.Interpreter.Tests.EndToEnd
 {
+	public static class OverloadsExtMethods
+	{
+		public static string Method1(this UserDataOverloadsTests.OverloadsTestClass obj, string x, bool b)
+		{
+			return "X" + obj.Method1();
+		}
+
+		public static string Method3(this UserDataOverloadsTests.OverloadsTestClass obj)
+		{
+			return "X" + obj.Method1(0.17);
+		}
+	}
+
+
 	[TestFixture]
 	public class UserDataOverloadsTests
 	{
@@ -123,6 +137,23 @@ namespace MoonSharp.Interpreter.Tests.EndToEnd
 			RunTestOverload("s:method1(true)", "s");
 		}
 
+		[Test]
+		public void Interop_Overloads_ExtMethods()
+		{
+			UserData.RegisterExtensionType(typeof(OverloadsExtMethods));
+
+			RunTestOverload("o:method1('xx', true)", "X1");
+			RunTestOverload("o:method3()", "X3");
+		}
+
+		[Test]
+		[ExpectedException(typeof(ScriptRuntimeException))]
+		public void Interop_Overloads_ExtMethods2()
+		{
+			UserData.RegisterExtensionType(typeof(OverloadsExtMethods));
+			RunTestOverload("s:method3()", "X3");
+		}
+
 
 
 		[Test]

+ 72 - 0
src/MoonSharp.Interpreter/CoreLib/DebugModule.cs

@@ -161,6 +161,29 @@ namespace MoonSharp.Interpreter.CoreLib
 		}
 
 
+		[MoonSharpModuleMethod]
+		public static DynValue upvaluejoin(ScriptExecutionContext executionContext, CallbackArguments args)
+		{
+			DynValue f1 = args.AsType(0, "upvaluejoin", DataType.Function, false);
+			DynValue f2 = args.AsType(2, "upvaluejoin", DataType.Function, false);
+			int n1 = args.AsInt(1, "upvaluejoin") - 1;
+			int n2 = args.AsInt(3, "upvaluejoin") - 1;
+
+			Closure c1 = f1.Function;
+			Closure c2 = f2.Function;
+
+			if (n1 < 0 || n1 >= c1.ClosureContext.Count)
+				throw ScriptRuntimeException.BadArgument(1, "upvaluejoin", "invalid upvalue index");
+			
+			if (n2 < 0 || n2 >= c2.ClosureContext.Count)
+				throw ScriptRuntimeException.BadArgument(3, "upvaluejoin", "invalid upvalue index");
+
+			c2.ClosureContext[n2] = c1.ClosureContext[n1];
+
+			return DynValue.Void;
+		}
+
+
 		[MoonSharpModuleMethod]
 		public static DynValue traceback(ScriptExecutionContext executionContext, CallbackArguments args)
 		{
@@ -216,6 +239,55 @@ namespace MoonSharp.Interpreter.CoreLib
 			return DynValue.NewString(sb);
 		}
 
+		//[MoonSharpModuleMethod]
+		//public static DynValue getlocal(ScriptExecutionContext executionContext, CallbackArguments args)
+		//{
+		//	Coroutine c;
+		//	int funcIdx;
+		//	Closure f;
+		//	int nextArg = ParseComplexArgs("getlocal", executionContext, args, out c, out f, out funcIdx);
+
+		//	int localIdx = args.AsInt(nextArg, "getlocal");
+
+		//	if (f != null)
+		//	{
+				
+		//	}
+		//	else
+		//	{
+
+		//	}
+
+
+
+		//}
+
+		//private static int ParseComplexArgs(string funcname, ScriptExecutionContext executionContext, CallbackArguments args, out Coroutine c, out Closure f, out int funcIdx)
+		//{
+		//	DynValue arg1 = args[0];
+		//	int argbase = 0;
+		//	c = null;
+
+		//	if (arg1.Type == DataType.Thread)
+		//	{
+		//		c = arg1.Coroutine;
+		//		argbase = 1;
+		//	}
+
+		//	if (args[argbase].Type == DataType.Number)
+		//	{
+		//		funcIdx = (int)args[argbase].Number;
+		//		f = null;
+		//	}
+		//	else
+		//	{
+		//		funcIdx = -1;
+		//		f = args.AsType(argbase, funcname, DataType.Function, false).Function;
+		//	}
+
+		//	return argbase + 1;
+		//}
+
 
 		//[MoonSharpMethod]
 		//public static DynValue getinfo(ScriptExecutionContext executionContext, CallbackArguments args)

+ 22 - 0
src/MoonSharp.Interpreter/DataStructs/Extension_Methods.cs

@@ -29,6 +29,28 @@ namespace MoonSharp.Interpreter
 		}
 
 
+		/// <summary>
+		/// Gets a value from the dictionary or creates it
+		/// </summary>
+		/// <typeparam name="TKey">The type of the key.</typeparam>
+		/// <typeparam name="TValue">The type of the value.</typeparam>
+		/// <param name="dictionary">The dictionary.</param>
+		/// <param name="key">The key.</param>
+		/// <param name="creator">A function which will create the value if it doesn't exist.</param>
+		/// <returns></returns>
+		public static TValue GetOrCreate<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, Func<TValue> creator)
+		{
+			TValue v;
+
+			if (!dictionary.TryGetValue(key, out v))
+			{
+				v = creator();
+				dictionary.Add(key, v);
+			}
+
+			return v;
+		}
+
 
 	}
 }

+ 2 - 1
src/MoonSharp.Interpreter/DataStructs/MultiDictionary.cs

@@ -13,6 +13,7 @@ namespace MoonSharp.Interpreter.DataStructs
 	internal class MultiDictionary<K, V>
 	{
 		Dictionary<K, List<V>> m_Map = new Dictionary<K, List<V>>();
+		V[] m_DefaultRet = new V[0];
 
 		public void Add(K key, V value)
 		{
@@ -35,7 +36,7 @@ namespace MoonSharp.Interpreter.DataStructs
 			if (m_Map.TryGetValue(key, out list))
 				return list;
 			else
-				return new V[0];
+				return m_DefaultRet;
 		}
 
 		public bool ContainsKey(K key)

+ 77 - 8
src/MoonSharp.Interpreter/DataTypes/UserData.cs

@@ -2,8 +2,10 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
+using System.Runtime.CompilerServices;
 using System.Text;
 using System.Threading;
+using MoonSharp.Interpreter.DataStructs;
 using MoonSharp.Interpreter.Interop;
 
 namespace MoonSharp.Interpreter
@@ -38,6 +40,8 @@ namespace MoonSharp.Interpreter
 		private static object s_Lock = new object();
 		private static Dictionary<Type, IUserDataDescriptor> s_Registry = new Dictionary<Type, IUserDataDescriptor>();
 		private static InteropAccessMode s_DefaultAccessMode;
+		private static MultiDictionary<string, StandardUserDataMethodDescriptor> s_ExtensionMethodRegistry = new MultiDictionary<string, StandardUserDataMethodDescriptor>();
+		private static int s_ExtensionMethodChangeVersion = 0;
 
 		static UserData()
 		{
@@ -92,14 +96,29 @@ namespace MoonSharp.Interpreter
 		/// Registers all types marked with a MoonSharpUserDataAttribute that ar contained in an assembly.
 		/// </summary>
 		/// <param name="asm">The assembly.</param>
-		public static void RegisterAssembly(Assembly asm = null)
+		/// <param name="includeExtensionTypes">if set to <c>true</c> extension types are registered to the appropriate registry.</param>
+		public static void RegisterAssembly(Assembly asm = null, bool includeExtensionTypes = false)
 		{
 			asm = asm ?? Assembly.GetCallingAssembly();
 
+			if (includeExtensionTypes)
+			{
+				var extensionTypes = from t in asm.GetTypes()
+									 let attributes = t.GetCustomAttributes(typeof(ExtensionAttribute), true)
+									 where attributes != null && attributes.Length > 0
+									 select new { Attributes = attributes, DataType = t };
+
+				foreach (var extType in extensionTypes)
+				{
+					UserData.RegisterExtensionType(extType.DataType);
+				}
+			}
+
+
 			var userDataTypes = from t in asm.GetTypes()
-				let attributes = t.GetCustomAttributes(typeof(MoonSharpUserDataAttribute), true)
-				where attributes != null && attributes.Length > 0
-				select new { Attributes = attributes, DataType = t };
+								let attributes = t.GetCustomAttributes(typeof(MoonSharpUserDataAttribute), true)
+								where attributes != null && attributes.Length > 0
+								select new { Attributes = attributes, DataType = t };
 
 			foreach (var userDataType in userDataTypes)
 			{
@@ -111,7 +130,7 @@ namespace MoonSharp.Interpreter
 		}
 
 		/// <summary>
-		/// Unregisters a type.
+		/// Unregisters a type
 		/// </summary>
 		/// <typeparam name="T">The type to be unregistered</typeparam>
 		public static void UnregisterType<T>()
@@ -120,14 +139,16 @@ namespace MoonSharp.Interpreter
 		}
 
 		/// <summary>
-		/// Unregisters a type.
+		/// Unregisters a type
 		/// </summary>
 		/// <param name="t">The The type to be unregistered</param>
 		public static void UnregisterType(Type t)
 		{
 			lock (s_Lock)
+			{
 				if (s_Registry.ContainsKey(t))
 					s_Registry.Remove(t);
+			}
 		}
 
 		/// <summary>
@@ -198,6 +219,54 @@ namespace MoonSharp.Interpreter
 			}
 		}
 
+		/// <summary>
+		/// Registers an extension Type (that is a type containing extension methods)
+		/// </summary>
+		/// <param name="type">The type.</param>
+		/// <param name="mode">The InteropAccessMode.</param>
+		public static void RegisterExtensionType(Type type, InteropAccessMode mode = InteropAccessMode.Default)
+		{
+			lock (s_Lock)
+			{
+				foreach (MethodInfo mi in type.GetMethods().Where(_mi => _mi.IsStatic))
+				{
+					if (!StandardUserDataMethodDescriptor.CheckMethodIsCompatible(mi, false))
+						continue;
+
+					if (mi.GetCustomAttributes(typeof(ExtensionAttribute), false).Length == 0)
+						continue;
+
+					var desc = new StandardUserDataMethodDescriptor(mi, mode);
+
+					s_ExtensionMethodRegistry.Add(mi.Name, desc);
+
+					++s_ExtensionMethodChangeVersion;
+				}
+			}
+		}
+
+
+		/// <summary>
+		/// Gets all the extension methods which can match a given name
+		/// </summary>
+		/// <param name="name">The name.</param>
+		/// <returns></returns>
+		public static IEnumerable<StandardUserDataMethodDescriptor> GetExtensionMethodsByName(string name)
+		{
+			lock (s_Lock)
+				return s_ExtensionMethodRegistry.Find(name);
+		}
+
+		/// <summary>
+		/// Gets a number which gets incremented everytime the extension methods registry changes.
+		/// Use this to invalidate caches based on extension methods
+		/// </summary>
+		/// <returns></returns>
+		public static int GetExtensionMethodsChangeVersion()
+		{
+			return s_ExtensionMethodChangeVersion;
+		}
+
 
 
 		private static IUserDataDescriptor RegisterType_Impl(Type type, InteropAccessMode accessMode, string friendlyName, IUserDataDescriptor descriptor)
@@ -215,7 +284,7 @@ namespace MoonSharp.Interpreter
 			if (accessMode == InteropAccessMode.Default)
 				accessMode = s_DefaultAccessMode;
 
-			lock(s_Lock)
+			lock (s_Lock)
 			{
 				if (!s_Registry.ContainsKey(type))
 				{
@@ -257,7 +326,7 @@ namespace MoonSharp.Interpreter
 
 		private static IUserDataDescriptor GetDescriptorForType(Type type, bool searchInterfaces)
 		{
-			lock(s_Lock)
+			lock (s_Lock)
 			{
 				IUserDataDescriptor typeDescriptor = null;
 

+ 4 - 0
src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs

@@ -1195,6 +1195,7 @@ namespace MoonSharp.Interpreter.Execution.VM
 
 					if (!ud.Descriptor.SetIndex(this.GetScript(), ud.Object, originalIdx, value, isNameIndex))
 					{
+						System.Diagnostics.Debug.WriteLine(string.Format("SETINDEX !!! Missing UD : {0} - {1} ", ud.Descriptor.Name, idx.String));
 						throw ScriptRuntimeException.UserDataMissingField(ud.Descriptor.Name, idx.String);
 					}
 
@@ -1277,7 +1278,10 @@ namespace MoonSharp.Interpreter.Execution.VM
 					var v = ud.Descriptor.Index(this.GetScript(), ud.Object, originalIdx, isNameIndex);
 
 					if (v == null)
+					{
+						System.Diagnostics.Debug.WriteLine(string.Format("INDEX !!! Missing UD : {0} - {1} ", ud.Descriptor.Name, idx.String));
 						throw ScriptRuntimeException.UserDataMissingField(ud.Descriptor.Name, idx.String);
+					}
 
 					m_ValueStack.Push(v.AsReadOnly());
 					return instructionPtr;

+ 76 - 16
src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardUserDataDescriptor.cs

@@ -6,7 +6,6 @@ using System.Text;
 using System.Threading;
 using MoonSharp.Interpreter.Execution;
 using MoonSharp.Interpreter.Interop.Converters;
-using MoonSharp.Interpreter.Interop.StandardDescriptors;
 
 namespace MoonSharp.Interpreter.Interop
 {
@@ -32,6 +31,8 @@ namespace MoonSharp.Interpreter.Interop
 		/// </summary>
 		public string FriendlyName { get; private set; }
 
+		private object m_Lock = new object();
+		private int m_ExtMethodsVersion = 0;
 		private Dictionary<string, StandardUserDataOverloadedMethodDescriptor> m_MetaMethods = new Dictionary<string, StandardUserDataOverloadedMethodDescriptor>();
 		private Dictionary<string, StandardUserDataOverloadedMethodDescriptor> m_Methods = new Dictionary<string, StandardUserDataOverloadedMethodDescriptor>();
 		private Dictionary<string, StandardUserDataPropertyDescriptor> m_Properties = new Dictionary<string, StandardUserDataPropertyDescriptor>();
@@ -72,7 +73,7 @@ namespace MoonSharp.Interpreter.Interop
 					if (CheckVisibility(ci.GetCustomAttributes(true), ci.IsPublic))
 					{
 						var md = new StandardUserDataMethodDescriptor(ci, this.AccessMode);
-						if (constructors == null) constructors = new StandardUserDataOverloadedMethodDescriptor();
+						if (constructors == null) constructors = new StandardUserDataOverloadedMethodDescriptor("__new", this.Type) { IgnoreExtensionMethods = true };
 						constructors.AddOverload(md);
 					}
 				}
@@ -103,7 +104,7 @@ namespace MoonSharp.Interpreter.Interop
 						}
 						else
 						{
-							m_Methods.Add(name, new StandardUserDataOverloadedMethodDescriptor(md));
+							m_Methods.Add(name, new StandardUserDataOverloadedMethodDescriptor(name, this.Type, md));
 						}
 
 						foreach(string metaname in GetMetaNamesFromAttributes(mi))
@@ -114,7 +115,7 @@ namespace MoonSharp.Interpreter.Interop
 							}
 							else
 							{
-								m_MetaMethods.Add(metaname, new StandardUserDataOverloadedMethodDescriptor(md));
+								m_MetaMethods.Add(metaname, new StandardUserDataOverloadedMethodDescriptor(metaname, this.Type, md) { IgnoreExtensionMethods = true });
 							}
 						}
 					}
@@ -184,7 +185,11 @@ namespace MoonSharp.Interpreter.Interop
 		{
 			if (!isDirectIndexing)
 			{
-				StandardUserDataOverloadedMethodDescriptor mdesc = m_Methods.GetOrDefault(SPECIAL_GETITEM);
+				StandardUserDataOverloadedMethodDescriptor mdesc;
+
+				lock (m_Lock) 
+					mdesc = m_Methods.GetOrDefault(SPECIAL_GETITEM);
+
 				if (mdesc != null)
 					return ExecuteIndexer(mdesc, script, obj, index, null);
 			}
@@ -194,14 +199,54 @@ namespace MoonSharp.Interpreter.Interop
 			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)));
+			DynValue v = null;
+
+			lock (m_Lock)
+			{
+				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>
+		/// 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>
 		/// Tries to perform an indexing operation by checking methods and properties for the given indexName
 		/// </summary>
@@ -237,7 +282,11 @@ namespace MoonSharp.Interpreter.Interop
 		{
 			if (!isDirectIndexing)
 			{
-				StandardUserDataOverloadedMethodDescriptor mdesc = m_Methods.GetOrDefault(SPECIAL_SETITEM);
+				StandardUserDataOverloadedMethodDescriptor mdesc;
+				
+				lock(m_Lock)
+					mdesc = m_Methods.GetOrDefault(SPECIAL_SETITEM);
+
 				if (mdesc != null)
 				{
 					ExecuteIndexer(mdesc, script, obj, index, value);
@@ -250,10 +299,15 @@ namespace MoonSharp.Interpreter.Interop
 			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);
+			bool v = false;
+
+			lock (m_Lock)
+			{
+				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;
 		}
@@ -270,7 +324,10 @@ namespace MoonSharp.Interpreter.Interop
 		{
 			StandardUserDataPropertyDescriptor pdesc;
 
-			if (m_Properties.TryGetValue(indexName, out pdesc))
+			lock(m_Lock)
+				pdesc = m_Properties.GetOrDefault(indexName);
+
+			if (pdesc != null)
 			{
 				pdesc.SetValue(script, obj, value);
 				return true;
@@ -420,7 +477,10 @@ namespace MoonSharp.Interpreter.Interop
 		/// <returns></returns>
 		public virtual DynValue MetaIndex(Script script, object obj, string metaname)
 		{
-			StandardUserDataOverloadedMethodDescriptor desc = m_MetaMethods.GetOrDefault(metaname);
+			StandardUserDataOverloadedMethodDescriptor desc;
+			
+			lock(m_Lock)
+				desc = m_MetaMethods.GetOrDefault(metaname);
 
 			if (desc != null)
 				return desc.GetCallbackAsDynValue(script, obj);

+ 22 - 2
src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardUserDataMethodDescriptor.cs

@@ -3,13 +3,13 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Linq.Expressions;
 using System.Reflection;
+using System.Runtime.CompilerServices;
 using System.Text;
 using System.Threading;
 using MoonSharp.Interpreter;
 using MoonSharp.Interpreter.Diagnostics;
 using MoonSharp.Interpreter.Execution;
 using MoonSharp.Interpreter.Interop.Converters;
-using MoonSharp.Interpreter.Interop.StandardDescriptors;
 
 namespace MoonSharp.Interpreter.Interop
 {
@@ -39,6 +39,10 @@ namespace MoonSharp.Interpreter.Interop
 		/// </summary>
 		public bool IsConstructor { get; private set; }
 		/// <summary>
+		/// Gets the type which this extension method extends, null if this is not an extension method.
+		/// </summary>
+		public Type ExtensionMethodType { get; private set; }
+		/// <summary>
 		/// Gets a sort discriminant to give consistent overload resolution matching in case of perfectly equal scores
 		/// </summary>
 		public string SortDiscriminant { get; private set; }
@@ -77,6 +81,12 @@ namespace MoonSharp.Interpreter.Interop
 
 			Parameters = methodBase.GetParameters();
 
+			if (methodBase.IsStatic && Parameters.Length > 0 && methodBase.GetCustomAttributes(typeof(ExtensionAttribute), false).Any())
+			{
+				this.ExtensionMethodType = Parameters[0].ParameterType;
+			}
+
+
 			// adjust access mode
 			if (Script.GlobalOptions.Platform.IsRunningOnAOT())
 				accessMode = InteropAccessMode.Reflection;
@@ -213,13 +223,21 @@ namespace MoonSharp.Interpreter.Interop
 
 			for (int i = 0; i < pars.Length; i++)
 			{
+				// keep track of out and ref params
 				if (Parameters[i].ParameterType.IsByRef)
 				{
 					if (outParams == null) outParams = new List<int>();
 					outParams.Add(i);
 				}
 
-				if (Parameters[i].ParameterType == typeof(Script))
+				// if an ext method, we have an obj -> fill the first param
+				if (ExtensionMethodType != null && obj != null && i == 0)
+				{
+					pars[i] = obj;
+					continue;
+				}
+				// else, fill types with a supported type
+				else if (Parameters[i].ParameterType == typeof(Script))
 				{
 					pars[i] = script;
 				}
@@ -231,10 +249,12 @@ namespace MoonSharp.Interpreter.Interop
 				{
 					pars[i] = args.SkipMethodCall();
 				}
+				// else, ignore out params
 				else if (Parameters[i].IsOut)
 				{
 					pars[i] = null;
 				}
+				// else, convert it
 				else
 				{
 					var arg = args.RawGet(j, false) ?? DynValue.Void;

+ 81 - 21
src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardUserDataOverloadedMethodDescriptor.cs

@@ -4,7 +4,7 @@ using System.Linq;
 using System.Text;
 using MoonSharp.Interpreter.Interop.Converters;
 
-namespace MoonSharp.Interpreter.Interop.StandardDescriptors
+namespace MoonSharp.Interpreter.Interop
 {
 	/// <summary>
 	/// Class providing easier marshalling of overloaded CLR functions
@@ -22,23 +22,33 @@ namespace MoonSharp.Interpreter.Interop.StandardDescriptors
 		}
 
 		private List<StandardUserDataMethodDescriptor> m_Overloads = new List<StandardUserDataMethodDescriptor>();
+		private List<StandardUserDataMethodDescriptor> m_ExtOverloads = new List<StandardUserDataMethodDescriptor>();
 		private bool m_Unsorted = true;
 		private OverloadCacheItem[] m_Cache = new OverloadCacheItem[CACHE_SIZE];
 		private int m_CacheHits = 0;
+		private int m_ExtensionMethodVersion = 0;
+
+		/// <summary>
+		/// Gets or sets a value indicating whether this instance ignores extension methods.
+		/// </summary>
+		public bool IgnoreExtensionMethods { get; set; }
+
 
 		/// <summary>
 		/// Initializes a new instance of the <see cref="StandardUserDataOverloadedMethodDescriptor"/> class.
 		/// </summary>
-		public StandardUserDataOverloadedMethodDescriptor()
+		public StandardUserDataOverloadedMethodDescriptor(string name, Type declaringType)
 		{
-
+			Name = name;
+			DeclaringType = declaringType;
 		}
 
 		/// <summary>
 		/// Initializes a new instance of the <see cref="StandardUserDataOverloadedMethodDescriptor"/> class.
 		/// </summary>
 		/// <param name="descriptor">The descriptor of the first overloaded method.</param>
-		public StandardUserDataOverloadedMethodDescriptor(StandardUserDataMethodDescriptor descriptor)
+		public StandardUserDataOverloadedMethodDescriptor(string name, Type declaringType, StandardUserDataMethodDescriptor descriptor)
+			: this(name, declaringType)
 		{
 			m_Overloads.Add(descriptor);
 		}
@@ -46,24 +56,42 @@ namespace MoonSharp.Interpreter.Interop.StandardDescriptors
 		/// <summary>
 		/// Initializes a new instance of the <see cref="StandardUserDataOverloadedMethodDescriptor"/> class.
 		/// </summary>
-		/// <param name="descriptor">The descriptors of the overloaded methods.</param>
-		public StandardUserDataOverloadedMethodDescriptor(IEnumerable<StandardUserDataMethodDescriptor> descriptors)
+		/// <param name="descriptors">The descriptors of the overloaded methods.</param>
+		public StandardUserDataOverloadedMethodDescriptor(string name, Type declaringType, IEnumerable<StandardUserDataMethodDescriptor> descriptors)
+			: this(name, declaringType)
 		{
 			m_Overloads.AddRange(descriptors);
 		}
 
+		/// <summary>
+		/// Sets the extension methods snapshot.
+		/// </summary>
+		/// <param name="version">The version.</param>
+		/// <param name="extMethods">The ext methods.</param>
+		internal void SetExtensionMethodsSnapshot(int version, List<StandardUserDataMethodDescriptor> extMethods)
+		{
+			m_ExtOverloads = extMethods;
+			m_ExtensionMethodVersion = version;
+		}
+
+
+
 		/// <summary>
 		/// Gets the name of the first described overload
 		/// </summary>
 		public string Name
 		{
-			get
-			{
-				if (m_Overloads.Count > 0)
-					return m_Overloads[0].Name;
+			get;
+			private set;
+		}
 
-				return null;
-			}
+		/// <summary>
+		/// Gets the name of the first described overload
+		/// </summary>
+		public Type DeclaringType
+		{
+			get;
+			private set;
 		}
 
 		/// <summary>
@@ -98,7 +126,10 @@ namespace MoonSharp.Interpreter.Interop.StandardDescriptors
 		/// <exception cref="ScriptRuntimeException">function call doesn't match any overload</exception>
 		private DynValue PerformOverloadedCall(Script script, object obj, ScriptExecutionContext context, CallbackArguments args)
 		{
-			if (m_Overloads.Count == 1)
+			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)
@@ -107,16 +138,19 @@ namespace MoonSharp.Interpreter.Interop.StandardDescriptors
 				m_Unsorted = false;
 			}
 
-			for (int i = 0; i < m_Cache.Length; i++)
+			if (extMethodCacheNotExpired)
 			{
-				if (m_Cache[i] != null && CheckMatch(obj != null, args, m_Cache[i]))
+				for (int i = 0; i < m_Cache.Length; i++)
 				{
-					System.Diagnostics.Debug.WriteLine(string.Format("[OVERLOAD] : CACHED! slot {0}, hits: {1}", i, m_CacheHits));
-					return m_Cache[i].Method.Callback(script, obj, context, args);
+					if (m_Cache[i] != null && CheckMatch(obj != null, args, m_Cache[i]))
+					{
+						System.Diagnostics.Debug.WriteLine(string.Format("[OVERLOAD] : CACHED! slot {0}, hits: {1}", i, m_CacheHits));
+						return m_Cache[i].Method.Callback(script, obj, context, args);
+					}
 				}
 			}
 
-
+			// resolve on overloads first
 			int maxScore = 0;
 			StandardUserDataMethodDescriptor bestOverload = null;
 
@@ -124,7 +158,7 @@ namespace MoonSharp.Interpreter.Interop.StandardDescriptors
 			{
 				if (obj != null || m_Overloads[i].IsStatic)
 				{
-					int score = CalcScoreForOverload(context, args, m_Overloads[i]);
+					int score = CalcScoreForOverload(context, args, m_Overloads[i], false);
 
 					if (score > maxScore)
 					{
@@ -134,6 +168,29 @@ namespace MoonSharp.Interpreter.Interop.StandardDescriptors
 				}
 			}
 
+			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);
@@ -208,7 +265,7 @@ namespace MoonSharp.Interpreter.Interop.StandardDescriptors
 		/// <param name="args">The arguments.</param>
 		/// <param name="method">The method.</param>
 		/// <returns></returns>
-		private int CalcScoreForOverload(ScriptExecutionContext context, CallbackArguments args, StandardUserDataMethodDescriptor method)
+		private int CalcScoreForOverload(ScriptExecutionContext context, CallbackArguments args, StandardUserDataMethodDescriptor method, bool isExtMethod)
 		{
 			int totalScore = ScriptToClrConversions.WEIGHT_EXACT_MATCH;
 			int argsBase = args.IsMethodCall ? 1 : 0;
@@ -216,6 +273,9 @@ namespace MoonSharp.Interpreter.Interop.StandardDescriptors
 
 			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)))
@@ -250,7 +310,7 @@ namespace MoonSharp.Interpreter.Interop.StandardDescriptors
 			}
 
 			System.Diagnostics.Debug.WriteLine(string.Format("[OVERLOAD] : Score {0} for method {1}", totalScore, method.SortDiscriminant));
-	
+
 			return totalScore;
 		}