浏览代码

Global Options and custom converters and TCO

Xanathar 10 年之前
父节点
当前提交
3cfbb4245b
共有 29 个文件被更改,包括 692 次插入59 次删除
  1. 1 0
      src/DevTools/MoonSharp.VmDebugger/MainForm.cs
  2. 48 0
      src/MoonSharp.Interpreter.Tests/EndToEnd/TailCallTests.cs
  3. 205 0
      src/MoonSharp.Interpreter.Tests/EndToEnd/UserDataMethodsTests.cs
  4. 1 1
      src/MoonSharp.Interpreter.Tests/TestMoreTests.cs
  5. 1 1
      src/MoonSharp.Interpreter.Tests/TestRunner.cs
  6. 1 1
      src/MoonSharp.Interpreter/CoreLib/IO/FileUserData.cs
  7. 3 3
      src/MoonSharp.Interpreter/CoreLib/IoModule.cs
  8. 8 8
      src/MoonSharp.Interpreter/CoreLib/OsSystemModule.cs
  9. 10 3
      src/MoonSharp.Interpreter/DataTypes/CallbackFunction.cs
  10. 4 0
      src/MoonSharp.Interpreter/DataTypes/DataType.cs
  11. 3 0
      src/MoonSharp.Interpreter/Execution/VM/CallStackItemFlags.cs
  12. 11 4
      src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_BinaryDump.cs
  13. 61 15
      src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs
  14. 106 0
      src/MoonSharp.Interpreter/Interop/Converters/ConversionHelper.cs
  15. 115 0
      src/MoonSharp.Interpreter/Interop/CustomConvertersCollection.cs
  16. 1 1
      src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardUserDataDescriptor.cs
  17. 1 1
      src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardUserDataMethodDescriptor.cs
  18. 1 1
      src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardUserDataPropertyDescriptor.cs
  19. 2 2
      src/MoonSharp.Interpreter/Loaders/ScriptLoaderBase.cs
  20. 17 2
      src/MoonSharp.Interpreter/Modules/ModuleRegister.cs
  21. 2 0
      src/MoonSharp.Interpreter/MoonSharp.Interpreter.net35-client.csproj
  22. 17 0
      src/MoonSharp.Interpreter/Properties/AssemblyInfo.cs
  23. 9 13
      src/MoonSharp.Interpreter/Script.cs
  24. 33 0
      src/MoonSharp.Interpreter/ScriptGlobalOptions.cs
  25. 17 1
      src/MoonSharp.Interpreter/ScriptOptions.cs
  26. 6 0
      src/MoonSharp.Interpreter/_Projects/MoonSharp.Interpreter.net40-client/MoonSharp.Interpreter.net40-client.csproj
  27. 6 0
      src/MoonSharp.Interpreter/_Projects/MoonSharp.Interpreter.portable40/MoonSharp.Interpreter.portable40.csproj
  28. 1 1
      src/MoonSharp/Program.cs
  29. 1 1
      src/TestRunners/ConsoleTestRunner/Program.cs

+ 1 - 0
src/DevTools/MoonSharp.VmDebugger/MainForm.cs

@@ -31,6 +31,7 @@ namespace MoonSharp.Debugger
 		{
 			m_Ctx = SynchronizationContext.Current;
 			Script.WarmUp();
+			Script.DefaultOptions.TailCallOptimizationThreshold = 1;
 		}
 
 		private void openToolStripMenuItem_Click(object sender, EventArgs e)

+ 48 - 0
src/MoonSharp.Interpreter.Tests/EndToEnd/TailCallTests.cs

@@ -10,6 +10,54 @@ namespace MoonSharp.Interpreter.Tests.EndToEnd
 	[TestFixture]
 	public class TailCallTests
 	{
+		[Test]
+		public void TcoTest_Pre()
+		{
+			// this just verifies the algorithm for TcoTest_Big
+			string script = @"
+				function recsum(num, partial)
+					if (num == 0) then
+						return partial
+					else
+						return recsum(num - 1, partial + num)
+					end
+				end
+				
+				return recsum(10, 0)";
+
+
+			Script S = new Script();
+			var res = S.DoString(script);
+
+			Assert.AreEqual(DataType.Number, res.Type);
+			Assert.AreEqual(55, res.Number);
+		}
+
+		[Test]
+		public void TcoTest_Big()
+		{
+			// calc the sum of the first N numbers in the most stupid way ever to waste stack and trigger TCO..
+			// (this could be a simple X*(X+1) / 2... )
+			string script = @"
+				function recsum(num, partial)
+					if (num == 0) then
+						return partial
+					else
+						return recsum(num - 1, partial + num)
+					end
+				end
+				
+				return recsum(70000, 0)";
+
+
+			Script S = new Script();
+			var res = S.DoString(script);
+
+			Assert.AreEqual(DataType.Number, res.Type);
+			Assert.AreEqual(2450035000.0, res.Number);
+		}
+
+
 		[Test]
 		public void TailCallFromCLR()
 		{

+ 205 - 0
src/MoonSharp.Interpreter.Tests/EndToEnd/UserDataMethodsTests.cs

@@ -22,6 +22,49 @@ namespace MoonSharp.Interpreter.Tests.EndToEnd
 				return i * 2;
 			}
 
+			public static StringBuilder SetComplexRecursive(List<int[]> intList)
+			{
+				StringBuilder sb = new StringBuilder();
+
+				foreach (int[] arr in intList)
+				{
+					sb.Append(string.Join(",", arr.Select(s => s.ToString()).ToArray()));
+					sb.Append("|");
+				}
+
+				return sb;
+			}
+
+			public static StringBuilder SetComplexTypes(List<string> strlist, IList<int> intlist, Dictionary<string, int> map,
+				string[] strarray, int[] intarray)
+			{
+				StringBuilder sb = new StringBuilder();
+
+				sb.Append(string.Join(",", strlist.ToArray()));
+
+				sb.Append("|");
+
+				sb.Append(string.Join(",", intlist.Select(i => i.ToString()).ToArray()));
+
+				sb.Append("|");
+
+				sb.Append(string.Join(",", map.Keys.OrderBy(x => x).Select(i => i.ToString()).ToArray()));
+
+				sb.Append("|");
+
+				sb.Append(string.Join(",", map.Values.OrderBy(x => x).Select(i => i.ToString()).ToArray()));
+
+				sb.Append("|");
+
+				sb.Append(string.Join(",", strarray));
+
+				sb.Append("|");
+
+				sb.Append(string.Join(",", intarray.Select(i => i.ToString()).ToArray()));
+
+				return sb;
+			}
+
 
 			public static StringBuilder ConcatS(int p1, string p2, IComparable p3, bool p4, List<object> p5, IEnumerable<object> p6,
 				StringBuilder p7, Dictionary<object, object> p8, SomeClass p9, int p10 = 1994)
@@ -170,6 +213,116 @@ namespace MoonSharp.Interpreter.Tests.EndToEnd
 			}
 		}
 
+		public void Test_ConcatMethodStaticComplexCustomConv(InteropAccessMode opt)
+		{
+			try
+			{
+				UserData.UnregisterType<SomeClass>();
+
+				string script = @"    
+				strlist = { 'ciao', 'hello', 'aloha' };
+				intlist = {  };
+				dictry = { ciao = 39, hello = 78, aloha = 128 };
+				
+				x = static.SetComplexTypes(strlist, intlist, dictry, strlist, intlist);
+
+				return x;";
+
+				Script S = new Script();
+
+				SomeClass obj = new SomeClass();
+
+				UserData.UnregisterType<SomeClass>();
+				UserData.RegisterType<SomeClass>(opt);
+
+				Script.GlobalOptions.CustomConverters.Clear();
+
+				Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Table, typeof(List<string>),
+					v => null);
+
+				Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Table, typeof(IList<int>),
+					v => new List<int>() { 42, 77, 125, 13 });
+
+				Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Table, typeof(int[]),
+					v => new int[] { 43, 78, 126, 14 });
+
+				Script.GlobalOptions.CustomConverters.SetClrToScriptCustomConversion<StringBuilder>(
+					v => DynValue.NewString(v.ToString().ToUpper()));
+
+
+				S.Globals.Set("static", UserData.CreateStatic<SomeClass>());
+				S.Globals.Set("myobj", UserData.Create(obj));
+
+				DynValue res = S.DoString(script);
+
+				Assert.AreEqual(DataType.String, res.Type);
+				Assert.AreEqual("CIAO,HELLO,ALOHA|42,77,125,13|ALOHA,CIAO,HELLO|39,78,128|CIAO,HELLO,ALOHA|43,78,126,14", res.String);
+			}
+			finally
+			{
+				Script.GlobalOptions.CustomConverters.Clear();
+			}
+		}
+
+
+		public void Test_ConcatMethodStaticComplex(InteropAccessMode opt)
+		{
+			UserData.UnregisterType<SomeClass>();
+
+			string script = @"    
+				strlist = { 'ciao', 'hello', 'aloha' };
+				intlist = { 42, 77, 125, 13 };
+				dictry = { ciao = 39, hello = 78, aloha = 128 };
+				
+				x = static.SetComplexTypes(strlist, intlist, dictry, strlist, intlist);
+
+				return x;";
+
+			Script S = new Script();
+
+			SomeClass obj = new SomeClass();
+
+			UserData.UnregisterType<SomeClass>();
+			UserData.RegisterType<SomeClass>(opt);
+
+			S.Globals.Set("static", UserData.CreateStatic<SomeClass>());
+			S.Globals.Set("myobj", UserData.Create(obj));
+
+			DynValue res = S.DoString(script);
+
+			Assert.AreEqual(DataType.String, res.Type);
+			Assert.AreEqual("ciao,hello,aloha|42,77,125,13|aloha,ciao,hello|39,78,128|ciao,hello,aloha|42,77,125,13", res.String);
+		}
+
+		public void Test_ConcatMethodStaticComplexRec(InteropAccessMode opt)
+		{
+			UserData.UnregisterType<SomeClass>();
+
+			string script = @"    
+				array = { { 1, 2, 3 }, { 11, 35, 77 }, { 16, 42, 64 }, {99, 76, 17 } };				
+			
+				x = static.SetComplexRecursive(array);
+
+				return x;";
+
+			Script S = new Script();
+
+			SomeClass obj = new SomeClass();
+
+			UserData.UnregisterType<SomeClass>();
+			UserData.RegisterType<SomeClass>(opt);
+
+			S.Globals.Set("static", UserData.CreateStatic<SomeClass>());
+			S.Globals.Set("myobj", UserData.Create(obj));
+
+			DynValue res = S.DoString(script);
+
+			Assert.AreEqual(DataType.String, res.Type);
+			Assert.AreEqual("1,2,3|11,35,77|16,42,64|99,76,17|", res.String);
+		}
+
+
+
 
 
 		public void Test_ConcatMethodStatic(InteropAccessMode opt)
@@ -399,7 +552,59 @@ namespace MoonSharp.Interpreter.Tests.EndToEnd
 			Test_ConstructorAndConcatMethodSemicolon(InteropAccessMode.Preoptimized);
 		}
 
+		[Test]
+		public void Interop_ConcatMethodStaticCplxCustomConv_None()
+		{
+			Test_ConcatMethodStaticComplexCustomConv(InteropAccessMode.Reflection);
+		}
+
+		[Test]
+		public void Interop_ConcatMethodStaticCplxCustomConv_Lazy()
+		{
+			Test_ConcatMethodStaticComplexCustomConv(InteropAccessMode.LazyOptimized);
+		}
+
+		[Test]
+		public void Interop_ConcatMethodStaticCplxCustomConv_Precomputed()
+		{
+			Test_ConcatMethodStaticComplexCustomConv(InteropAccessMode.Preoptimized);
+		}
+
+		[Test]
+		public void Interop_ConcatMethodStaticCplx_None()
+		{
+			Test_ConcatMethodStaticComplex(InteropAccessMode.Reflection);
+		}
+
+		[Test]
+		public void Interop_ConcatMethodStaticCplx_Lazy()
+		{
+			Test_ConcatMethodStaticComplex(InteropAccessMode.LazyOptimized);
+		}
+
+		[Test]
+		public void Interop_ConcatMethodStaticCplx_Precomputed()
+		{
+			Test_ConcatMethodStaticComplex(InteropAccessMode.Preoptimized);
+		}
+
+		[Test]
+		public void Interop_ConcatMethodStaticCplxRec_None()
+		{
+			Test_ConcatMethodStaticComplexRec(InteropAccessMode.Reflection);
+		}
+
+		[Test]
+		public void Interop_ConcatMethodStaticCplxRec_Lazy()
+		{
+			Test_ConcatMethodStaticComplexRec(InteropAccessMode.LazyOptimized);
+		}
 
+		[Test]
+		public void Interop_ConcatMethodStaticCplxRec_Precomputed()
+		{
+			Test_ConcatMethodStaticComplexRec(InteropAccessMode.Preoptimized);
+		}
 
 		[Test]
 		public void Interop_ConcatMethodStatic_None()

+ 1 - 1
src/MoonSharp.Interpreter.Tests/TestMoreTests.cs

@@ -256,7 +256,7 @@ namespace MoonSharp.Interpreter.Tests
 
 		private bool AreCoreModulesFullySupported(CoreModules modules)
 		{
-			CoreModules supp = Script.Platform.FilterSupportedCoreModules(modules);
+			CoreModules supp = Script.GlobalOptions.Platform.FilterSupportedCoreModules(modules);
 			return supp == modules;
 		}
 

+ 1 - 1
src/MoonSharp.Interpreter.Tests/TestRunner.cs

@@ -37,7 +37,7 @@ namespace MoonSharp.Interpreter.Tests
 
 			this.loggerAction = loggerAction;
 
-			Console_WriteLine("MoonSharp Test Suite Runner - {0} [{1}]", Script.VERSION, Script.Platform.GetPlatformName());
+			Console_WriteLine("MoonSharp Test Suite Runner - {0} [{1}]", Script.VERSION, Script.GlobalOptions.Platform.GetPlatformName());
 			Console_WriteLine("http://www.moonsharp.org");
 			Console_WriteLine("");
 		}

+ 1 - 1
src/MoonSharp.Interpreter/CoreLib/IO/FileUserData.cs

@@ -13,7 +13,7 @@ namespace MoonSharp.Interpreter.CoreLib.IO
 	{
 		public FileUserData(Script script, string filename, Encoding encoding, string mode)
 		{
-			Stream stream = Script.Platform.IO_OpenFile(script, filename, encoding, mode);
+			Stream stream = Script.GlobalOptions.Platform.IO_OpenFile(script, filename, encoding, mode);
 
 			StreamReader reader = (stream.CanRead) ? new StreamReader(stream, encoding) : null;
 			StreamWriter writer = (stream.CanWrite) ? new StreamWriter(stream, encoding) : null;

+ 3 - 3
src/MoonSharp.Interpreter/CoreLib/IoModule.cs

@@ -55,7 +55,7 @@ namespace MoonSharp.Interpreter.CoreLib
 		{
 			Table R = S.Registry;
 
-			optionsStream = optionsStream ?? Script.Platform.IO_GetStandardStream(file);
+			optionsStream = optionsStream ?? Script.GlobalOptions.Platform.IO_GetStandardStream(file);
 
 			FileUserDataBase udb = null;
 
@@ -170,7 +170,7 @@ namespace MoonSharp.Interpreter.CoreLib
 			{
 				List<DynValue> readLines = new List<DynValue>();
 
-				using (var stream = Script.Platform.IO_OpenFile(executionContext.GetScript(), filename, null, "r"))
+				using (var stream = Script.GlobalOptions.Platform.IO_OpenFile(executionContext.GetScript(), filename, null, "r"))
 				{
 					using (var reader = new System.IO.StreamReader(stream))
 					{
@@ -290,7 +290,7 @@ namespace MoonSharp.Interpreter.CoreLib
 		[MoonSharpMethod]
 		public static DynValue tmpfile(ScriptExecutionContext executionContext, CallbackArguments args)
 		{
-			string tmpfilename = Script.Platform.IO_OS_GetTempFilename();
+			string tmpfilename = Script.GlobalOptions.Platform.IO_OS_GetTempFilename();
 			FileUserDataBase file = Open(executionContext, tmpfilename, GetUTF8Encoding(), "w");
 			return UserData.Create(file);
 		}

+ 8 - 8
src/MoonSharp.Interpreter/CoreLib/OsSystemModule.cs

@@ -28,7 +28,7 @@ namespace MoonSharp.Interpreter.CoreLib
 			{
 				try
 				{
-					int exitCode = Script.Platform.OS_Execute(v.String);
+					int exitCode = Script.GlobalOptions.Platform.OS_Execute(v.String);
 
 					return DynValue.NewTuple(
 						DynValue.Nil,
@@ -52,7 +52,7 @@ namespace MoonSharp.Interpreter.CoreLib
 			if (v_exitCode.IsNotNil())
 				exitCode = (int)v_exitCode.Number;
 
-			Script.Platform.OS_ExitFast(exitCode);
+			Script.GlobalOptions.Platform.OS_ExitFast(exitCode);
 
 			throw new InvalidOperationException("Unreachable code.. reached.");
 		}
@@ -62,7 +62,7 @@ namespace MoonSharp.Interpreter.CoreLib
 		{
 			DynValue varName = args.AsType(0, "getenv", DataType.String, false);
 
-			string val = Script.Platform.GetEnvironmentVariable(varName.String);
+			string val = Script.GlobalOptions.Platform.GetEnvironmentVariable(varName.String);
 
 			if (val == null)
 				return DynValue.Nil;
@@ -77,9 +77,9 @@ namespace MoonSharp.Interpreter.CoreLib
 
 			try
 			{
-				if (Script.Platform.OS_FileExists(fileName))
+				if (Script.GlobalOptions.Platform.OS_FileExists(fileName))
 				{
-					Script.Platform.OS_FileDelete(fileName);
+					Script.GlobalOptions.Platform.OS_FileDelete(fileName);
 					return DynValue.True;
 				}
 				else
@@ -104,14 +104,14 @@ namespace MoonSharp.Interpreter.CoreLib
 
 			try
 			{
-				if (!Script.Platform.OS_FileExists(fileNameOld))
+				if (!Script.GlobalOptions.Platform.OS_FileExists(fileNameOld))
 				{
 					return DynValue.NewTuple(DynValue.Nil,
 						DynValue.NewString("{0}: No such file or directory.", fileNameOld),
 						DynValue.NewNumber(-1));
 				}
 
-				Script.Platform.OS_FileMove(fileNameOld, fileNameNew);
+				Script.GlobalOptions.Platform.OS_FileMove(fileNameOld, fileNameNew);
 				return DynValue.True;
 			}
 			catch (Exception ex)
@@ -129,7 +129,7 @@ namespace MoonSharp.Interpreter.CoreLib
 		[MoonSharpMethod]
 		public static DynValue tmpname(ScriptExecutionContext executionContext, CallbackArguments args)
 		{
-			return DynValue.NewString(Script.Platform.IO_OS_GetTempFilename());
+			return DynValue.NewString(Script.GlobalOptions.Platform.IO_OS_GetTempFilename());
 		}
 	}
 }

+ 10 - 3
src/MoonSharp.Interpreter/DataTypes/CallbackFunction.cs

@@ -12,7 +12,6 @@ namespace MoonSharp.Interpreter
 	/// </summary>
 	public sealed class CallbackFunction : RefIdObject
 	{
-		Func<ScriptExecutionContext, CallbackArguments, DynValue> m_CallBack;
 		private static InteropAccessMode m_DefaultAccessMode = InteropAccessMode.LazyOptimized;
 
 		/// <summary>
@@ -20,6 +19,14 @@ namespace MoonSharp.Interpreter
 		/// </summary>
 		public string Name { get; private set; }
 
+		/// <summary>
+		/// Gets the call back.
+		/// </summary>
+		/// <value>
+		/// The call back.
+		/// </value>
+		public Func<ScriptExecutionContext, CallbackArguments, DynValue> ClrCallback { get; private set; }
+
 		/// <summary>
 		/// Initializes a new instance of the <see cref="CallbackFunction" /> class.
 		/// </summary>
@@ -27,7 +34,7 @@ namespace MoonSharp.Interpreter
 		/// <param name="name">The callback name, used in stacktraces, debugger, etc..</param>
 		public CallbackFunction(Func<ScriptExecutionContext, CallbackArguments, DynValue> callBack, string name = null)
 		{
-			m_CallBack = callBack;
+			ClrCallback = callBack;
 			Name = name;
 		}
 
@@ -40,7 +47,7 @@ namespace MoonSharp.Interpreter
 		/// <returns></returns>
 		public DynValue Invoke(ScriptExecutionContext executionContext, IList<DynValue> args, bool isMethodCall = false)
 		{
-			return m_CallBack(executionContext, new CallbackArguments(args, isMethodCall));
+			return ClrCallback(executionContext, new CallbackArguments(args, isMethodCall));
 		}
 
 		/// <summary>

+ 4 - 0
src/MoonSharp.Interpreter/DataTypes/DataType.cs

@@ -11,6 +11,8 @@ namespace MoonSharp.Interpreter
 	/// </summary>
 	public enum DataType
 	{
+		// DO NOT MODIFY ORDER - IT'S SIGNIFICANT
+
 		/// <summary>
 		/// A nil value, as in Lua
 		/// </summary>
@@ -57,6 +59,7 @@ namespace MoonSharp.Interpreter
 		/// A callback function
 		/// </summary>
 		ClrFunction,
+
 		/// <summary>
 		/// A request to execute a tail call
 		/// </summary>
@@ -73,6 +76,7 @@ namespace MoonSharp.Interpreter
 	public static class LuaTypeExtensions
 	{
 		public const DataType MaxMetaTypes = DataType.Table;
+		public const DataType MaxConvertibleTypes = DataType.ClrFunction;
 
 		/// <summary>
 		/// Determines whether this data type can have type metatables.

+ 3 - 0
src/MoonSharp.Interpreter/Execution/VM/CallStackItemFlags.cs

@@ -13,5 +13,8 @@ namespace MoonSharp.Interpreter.Execution.VM
 		EntryPoint = 1,
 		ResumeEntryPoint = 3,
 		CallEntryPoint = 5,
+
+		TailCall = 0x10,
+		MethodCall = 0x20,
 	}
 }

+ 11 - 4
src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_BinaryDump.cs

@@ -69,13 +69,20 @@ namespace MoonSharp.Interpreter.Execution.VM
 						AddSymbolToMap(symbolMap, sr.i_Env);
 				}
 
+				SymbolRef[] allSymbols = new SymbolRef[symbolMap.Count];
+
+				foreach (KeyValuePair<SymbolRef, int> pair in symbolMap)
+				{
+					allSymbols[pair.Value] = pair.Key;
+				}
+
 				bw.Write(symbolMap.Count);
 
-				foreach (KeyValuePair<SymbolRef, int> pair in symbolMap.OrderBy(kvp => kvp.Value))
-					pair.Key.WriteBinary(bw);
+				foreach (SymbolRef sym in allSymbols)
+					sym.WriteBinary(bw);
 
-				foreach (KeyValuePair<SymbolRef, int> pair in symbolMap.OrderBy(kvp => kvp.Value))
-					pair.Key.WriteBinaryEnv(bw, symbolMap);
+				foreach (SymbolRef sym in allSymbols)
+					sym.WriteBinaryEnv(bw, symbolMap);
 
 				for (int i = 0; i <= meta.NumVal; i++)
 					m_RootChunk.Code[baseAddress + i].WriteBinary(bw, baseAddress, symbolMap);

+ 61 - 15
src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs

@@ -548,17 +548,12 @@ namespace MoonSharp.Interpreter.Execution.VM
 
 		private void ExecBeginFn(Instruction i)
 		{
-			CallStackItem c = m_ExecutionStack.Peek();
-			c.Debug_Symbols = i.SymbolList;
-			c.LocalScope = new DynValue[i.NumVal];
+			CallStackItem cur = m_ExecutionStack.Peek();
 
-			if (i.NumVal2 >= 0 && i.NumVal > 0)
-			{
-				for (int idx = 0; idx < i.NumVal2; idx++)
-				{
-					c.LocalScope[idx] = DynValue.NewNil();
-				}
-			}
+			cur.Debug_Symbols = i.SymbolList;
+			cur.LocalScope = new DynValue[i.NumVal];
+
+			ClearBlockData(i);
 		}
 
 		private CallStackItem PopToBasePointer()
@@ -610,10 +605,38 @@ namespace MoonSharp.Interpreter.Execution.VM
 
 
 
-		private int Internal_ExecCall(int argsCount, int instructionPtr, CallbackFunction handler = null, CallbackFunction continuation = null, 
-			bool thisCall = false, string debugText = null, DynValue unwindHandler = null)
+		private int Internal_ExecCall(int argsCount, int instructionPtr, CallbackFunction handler = null, 
+			CallbackFunction continuation = null, bool thisCall = false, string debugText = null, DynValue unwindHandler = null)
 		{
 			DynValue fn = m_ValueStack.Peek(argsCount);
+			CallStackItemFlags flags = (thisCall ? CallStackItemFlags.MethodCall : CallStackItemFlags.None);
+
+			// if TCO threshold reached
+			if ((m_ExecutionStack.Count > this.m_Script.Options.TailCallOptimizationThreshold && m_ExecutionStack.Count > 1)
+				|| (m_ValueStack.Count > this.m_Script.Options.TailCallOptimizationThreshold && m_ValueStack.Count > 1))
+			{
+				// and the "will-be" return address is valid (we don't want to crash here)
+				if (instructionPtr >= 0 && instructionPtr < this.m_RootChunk.Code.Count)
+				{
+					Instruction I = this.m_RootChunk.Code[instructionPtr];
+										
+					// and we are followed by a blatant RET 1
+					if (I.OpCode == OpCode.Ret && I.NumVal == 1)
+					{
+						CallStackItem csi = m_ExecutionStack.Peek();
+
+						// if the current stack item has no "odd" things pending and neither has the new coming one..
+						if (csi.ClrFunction == null && csi.Continuation == null && csi.ErrorHandler == null 
+							&& csi.ErrorHandlerBeforeUnwind == null && continuation == null && unwindHandler == null && handler == null)
+						{
+							instructionPtr = PerformTCO(instructionPtr, argsCount);
+							flags |= CallStackItemFlags.TailCall;
+						}
+					}
+				}
+			}
+				
+
 
 			if (fn.Type == DataType.ClrFunction)
 			{
@@ -632,9 +655,10 @@ namespace MoonSharp.Interpreter.Execution.VM
 					ErrorHandler = handler,
 					Continuation = continuation,
 					ErrorHandlerBeforeUnwind = unwindHandler,
+					Flags = flags,
 				});
 
-				var ret = fn.Callback.Invoke(new ScriptExecutionContext(this, fn.Callback, sref), args, isMethodCall:thisCall);
+				var ret = fn.Callback.Invoke(new ScriptExecutionContext(this, fn.Callback, sref), args, isMethodCall: thisCall);
 				m_ValueStack.RemoveLast(argsCount + 1);
 				m_ValueStack.Push(ret);
 
@@ -650,11 +674,12 @@ namespace MoonSharp.Interpreter.Execution.VM
 					BasePointer = m_ValueStack.Count,
 					ReturnAddress = instructionPtr,
 					Debug_EntryPoint = fn.Function.EntryPointByteCodeLocation,
-					CallingSourceRef =  GetCurrentSourceRef(instructionPtr),
+					CallingSourceRef = GetCurrentSourceRef(instructionPtr),
 					ClosureScope = fn.Function.ClosureContext,
 					ErrorHandler = handler,
 					Continuation = continuation,
 					ErrorHandlerBeforeUnwind = unwindHandler,
+					Flags = flags,
 				});
 				return fn.Function.EntryPointByteCodeLocation;
 			}
@@ -685,6 +710,27 @@ namespace MoonSharp.Interpreter.Execution.VM
 			}
 		}
 
+		private int PerformTCO(int instructionPtr, int argsCount)
+		{
+			DynValue[] args = new DynValue[argsCount + 1];
+
+			// Remove all cur args and func ptr
+			for (int i = 0; i <= argsCount; i++)
+				args[i] = m_ValueStack.Pop();
+
+			// perform a fake RET
+			CallStackItem csi = PopToBasePointer();
+			int retpoint = csi.ReturnAddress;
+			var argscnt = (int)(m_ValueStack.Pop().Number);
+			m_ValueStack.RemoveLast(argscnt + 1);
+
+			// Re-push all cur args and func ptr
+			for (int i = argsCount; i >= 0; i--)
+				m_ValueStack.Push(args[i]);
+
+			return retpoint;
+		}
+
 
 
 
@@ -913,7 +959,7 @@ namespace MoonSharp.Interpreter.Execution.VM
 
 		private int ExecNeg(Instruction i, int instructionPtr)
 		{
-			DynValue r = m_ValueStack.Pop().ToScalar(); 
+			DynValue r = m_ValueStack.Pop().ToScalar();
 			double? rn = r.CastToNumber();
 
 			if (rn.HasValue)

+ 106 - 0
src/MoonSharp.Interpreter/Interop/Converters/ConversionHelper.cs

@@ -41,6 +41,14 @@ namespace MoonSharp.Interpreter.Interop
 			if (obj is DynValue)
 				return (DynValue)obj;
 
+			var converter = Script.GlobalOptions.CustomConverters.GetClrToScriptCustomConversion(obj.GetType());
+			if (converter != null)
+			{
+				var v = converter(obj);
+				if (v != null)
+					return v;
+			}
+
 			Type t = obj.GetType();
 
 			if (obj is bool)
@@ -156,6 +164,14 @@ namespace MoonSharp.Interpreter.Interop
 
 		internal static object MoonSharpValueToClrObject(DynValue value)
 		{
+			var converter = Script.GlobalOptions.CustomConverters.GetScriptToClrCustomConversion(value.Type, typeof(System.Object));
+			if (converter != null)
+			{
+				var v = converter(value);
+				if (v != null)
+					return v;
+			}
+
 			switch (value.Type)
 			{
 				case DataType.Void:
@@ -189,6 +205,14 @@ namespace MoonSharp.Interpreter.Interop
 
 		internal static object MoonSharpValueToObjectOfType(DynValue value, Type desiredType, object defaultValue)
 		{
+			var converter = Script.GlobalOptions.CustomConverters.GetScriptToClrCustomConversion(value.Type, desiredType);
+			if (converter != null)
+			{
+				var v = converter(value);
+				if (v != null)
+					return v;
+			}
+
 			if (desiredType == typeof(DynValue))
 				return value;
 
@@ -266,9 +290,11 @@ namespace MoonSharp.Interpreter.Interop
 					break;
 				case DataType.Function:
 					if (desiredType == typeof(Closure)) return value.Function;
+					else if (desiredType == typeof(ScriptFunctionDelegate)) return value.Function.GetDelegate();
 					break;
 				case DataType.ClrFunction:
 					if (desiredType == typeof(CallbackFunction)) return value.Callback;
+					else if (desiredType == typeof(Func<ScriptExecutionContext, CallbackArguments, DynValue>)) return value.Callback.ClrCallback;
 					break;
 				case DataType.UserData:
 					if (value.UserData.Object != null)
@@ -321,9 +347,89 @@ namespace MoonSharp.Interpreter.Interop
 			else if (t.IsAssignableFrom(typeof(DynValue[])))
 				return TableToList<DynValue>(table, v => v).ToArray();
 
+			if (t.IsGenericType)
+			{
+				if ((t.GetGenericTypeDefinition() == typeof(List<>))
+					|| (t.GetGenericTypeDefinition() == typeof(IList<>))
+					 || (t.GetGenericTypeDefinition() == typeof(ICollection<>))
+					 || (t.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
+				{
+					return ConvertTableToListOfGenericType(t, t.GetGenericArguments()[0], table);
+				}
+				else if ((t.GetGenericTypeDefinition() == typeof(Dictionary<,>))
+					|| (t.GetGenericTypeDefinition() == typeof(IDictionary<,>)))
+				{
+					return ConvertTableToDictionaryOfGenericType(t, t.GetGenericArguments()[0], t.GetGenericArguments()[1], table);
+				}
+			}
+
+			if (t.IsArray && t.GetArrayRank() == 1)
+				return ConvertTableToArrayOfGenericType(t, t.GetElementType(), table);
+
 			return null;
 		}
 
+		private static object ConvertTableToDictionaryOfGenericType(Type dictionaryType, Type keyType, Type valueType, Table table)
+		{
+			if (dictionaryType.GetGenericTypeDefinition() != typeof(Dictionary<,>))
+			{
+				dictionaryType = typeof(Dictionary<,>);
+				dictionaryType = dictionaryType.MakeGenericType(keyType, valueType);
+			}
+
+			System.Collections.IDictionary dic = (System.Collections.IDictionary)Activator.CreateInstance(dictionaryType);
+
+			foreach (var kvp in table.Pairs)
+			{
+				object key = MoonSharp.Interpreter.Interop.ConversionHelper.MoonSharpValueToObjectOfType(kvp.Key, keyType, null);
+				object val = MoonSharp.Interpreter.Interop.ConversionHelper.MoonSharpValueToObjectOfType(kvp.Value, valueType, null);
+
+				dic.Add(key, val);
+			}
+
+			return dic;
+		}
+
+		private static object ConvertTableToArrayOfGenericType(Type arrayType, Type itemType, Table table)
+		{
+			List<object> lst = new List<object>(); 
+
+			for (int i = 1, l = table.Length; i <= l; i++)
+			{
+				DynValue v = table.Get(i);
+				object o = MoonSharp.Interpreter.Interop.ConversionHelper.MoonSharpValueToObjectOfType(v, itemType, null);
+				lst.Add(o);
+			}
+
+			System.Collections.IList array = (System.Collections.IList)Activator.CreateInstance(arrayType,new object[] { lst.Count });
+
+			for (int i = 0; i < lst.Count; i++)
+				array[i] = lst[i];
+
+			return array;
+		}
+
+
+		private static object ConvertTableToListOfGenericType(Type listType, Type itemType, Table table)
+		{
+			if (listType.GetGenericTypeDefinition() != typeof(List<>))
+			{
+				listType = typeof(List<>);
+				listType = listType.MakeGenericType(itemType);
+			}
+
+			System.Collections.IList lst = (System.Collections.IList)Activator.CreateInstance(listType);
+
+			for (int i = 1, l = table.Length; i <= l; i++)
+			{
+				DynValue v = table.Get(i);
+				object o = MoonSharp.Interpreter.Interop.ConversionHelper.MoonSharpValueToObjectOfType(v, itemType, null);
+				lst.Add(o);
+			}
+
+			return lst;
+		}
+
 		private static List<T> TableToList<T>(Table table, Func<DynValue, T> converter)
 		{
 			List<T> lst = new List<T>();

+ 115 - 0
src/MoonSharp.Interpreter/Interop/CustomConvertersCollection.cs

@@ -0,0 +1,115 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MoonSharp.Interpreter.Interop
+{
+	/// <summary>
+	/// A collection of custom converters between MoonSharp types and CLR types.
+	/// If a converter function is not specified or returns null, the standard conversion path applies.
+	/// </summary>
+	public class CustomConvertersCollection 
+	{
+		private Dictionary<Type, Func<DynValue, object>>[] m_Script2Clr = new Dictionary<Type, Func<DynValue, object>>[(int)LuaTypeExtensions.MaxConvertibleTypes + 1];
+		private Dictionary<Type, Func<object, DynValue>> m_Clr2Script = new Dictionary<Type, Func<object, DynValue>>();
+
+		internal CustomConvertersCollection()
+		{
+			for (int i = 0; i < m_Script2Clr.Length; i++)
+				m_Script2Clr[i] = new Dictionary<Type, Func<DynValue, object>>();
+		}
+
+
+		/// <summary>
+		/// Sets a custom converter from a script data type to a CLR data type. Set null to remove a previous custom converter.
+		/// </summary>
+		/// <param name="scriptDataType">The script data type</param>
+		/// <param name="clrDataType">The CLR data type.</param>
+		/// <param name="converter">The converter, or null.</param>
+		public void SetScriptToClrCustomConversion(DataType scriptDataType, Type clrDataType, Func<DynValue, object> converter = null)
+		{
+			if ((int)scriptDataType > m_Script2Clr.Length)
+				throw new ArgumentException("scriptDataType");
+
+			Dictionary<Type, Func<DynValue, object>> map = m_Script2Clr[(int)scriptDataType];
+
+			if (converter == null)
+			{
+				if (map.ContainsKey(clrDataType))
+					map.Remove(clrDataType);
+			}
+			else
+			{
+				map[clrDataType] = converter;
+			}
+		}
+
+		/// <summary>
+		/// Gets a custom converter from a script data type to a CLR data type, or null
+		/// </summary>
+		/// <param name="scriptDataType">The script data type</param>
+		/// <param name="clrDataType">The CLR data type.</param>
+		/// <returns>The converter function, or null if not found</returns>
+		public Func<DynValue, object> GetScriptToClrCustomConversion(DataType scriptDataType, Type clrDataType)
+		{
+			if ((int)scriptDataType > m_Script2Clr.Length)
+				return null;
+
+			Dictionary<Type, Func<DynValue, object>> map = m_Script2Clr[(int)scriptDataType];
+			return map.GetOrDefault(clrDataType);
+		}
+
+		/// <summary>
+		/// Sets a custom converter from a CLR data type. Set null to remove a previous custom converter.
+		/// </summary>
+		/// <param name="clrDataType">The CLR data type.</param>
+		/// <param name="converter">The converter, or null.</param>
+		public void SetClrToScriptCustomConversion(Type clrDataType, Func<object, DynValue> converter = null)
+		{
+			if (converter == null)
+			{
+				if (m_Clr2Script.ContainsKey(clrDataType))
+					m_Clr2Script.Remove(clrDataType);
+			}
+			else
+			{
+				m_Clr2Script[clrDataType] = converter;
+			}
+		}
+
+		/// <summary>
+		/// Sets a custom converter from a CLR data type. Set null to remove a previous custom converter.
+		/// </summary>
+		/// <typeparam name="T">The CLR data type.</typeparam>
+		/// <param name="converter">The converter, or null.</param>
+		public void SetClrToScriptCustomConversion<T>(Func<T, DynValue> converter = null)
+		{
+			SetClrToScriptCustomConversion(typeof(T), o => converter((T)o));
+		}
+
+
+		/// <summary>
+		/// Gets a custom converter from a CLR data type, or null
+		/// </summary>
+		/// <param name="clrDataType">Type of the color data.</param>
+		/// <returns>The converter function, or null if not found</returns>
+		public Func<object, DynValue> GetClrToScriptCustomConversion(Type clrDataType)
+		{
+			return m_Clr2Script.GetOrDefault(clrDataType);
+		}
+
+
+		/// <summary>
+		/// Removes all converters
+		/// </summary>
+		public void Clear()
+		{
+			m_Clr2Script.Clear();
+
+			for (int i = 0; i < m_Script2Clr.Length; i++)
+				m_Script2Clr[i].Clear();
+		}
+
+	}
+}

+ 1 - 1
src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardUserDataDescriptor.cs

@@ -20,7 +20,7 @@ namespace MoonSharp.Interpreter.Interop
 
 		protected internal StandardUserDataDescriptor(Type type, InteropAccessMode accessMode, string friendlyName)
 		{
-			if (Script.Platform.IsRunningOnAOT())
+			if (Script.GlobalOptions.Platform.IsRunningOnAOT())
 				accessMode = InteropAccessMode.Reflection;
 
 			if (accessMode == InteropAccessMode.Default)

+ 1 - 1
src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardUserDataMethodDescriptor.cs

@@ -26,7 +26,7 @@ namespace MoonSharp.Interpreter.Interop
 
 		public StandardUserDataMethodDescriptor(MethodBase mi, InteropAccessMode accessMode = InteropAccessMode.Default)
 		{
-			if (Script.Platform.IsRunningOnAOT())
+			if (Script.GlobalOptions.Platform.IsRunningOnAOT())
 				accessMode = InteropAccessMode.Reflection;
 
 			if (accessMode == InteropAccessMode.Default)

+ 1 - 1
src/MoonSharp.Interpreter/Interop/StandardDescriptors/StandardUserDataPropertyDescriptor.cs

@@ -22,7 +22,7 @@ namespace MoonSharp.Interpreter.Interop
 
 		internal StandardUserDataPropertyDescriptor(PropertyInfo pi, InteropAccessMode accessMode)
 		{
-			if (Script.Platform.IsRunningOnAOT())
+			if (Script.GlobalOptions.Platform.IsRunningOnAOT())
 				accessMode = InteropAccessMode.Reflection;
 
 			this.PropertyInfo = pi;

+ 2 - 2
src/MoonSharp.Interpreter/Loaders/ScriptLoaderBase.cs

@@ -95,12 +95,12 @@ namespace MoonSharp.Interpreter.Loaders
 
 			if (modulePaths == null)
 			{
-				string env = Script.Platform.GetEnvironmentVariable("MOONSHARP_PATH");
+				string env = Script.GlobalOptions.Platform.GetEnvironmentVariable("MOONSHARP_PATH");
 				if (!string.IsNullOrEmpty(env)) modulePaths = UnpackStringPaths(env);
 
 				if (modulePaths == null)
 				{
-					env = Script.Platform.GetEnvironmentVariable("LUA_PATH");
+					env = Script.GlobalOptions.Platform.GetEnvironmentVariable("LUA_PATH");
 					if (!string.IsNullOrEmpty(env)) modulePaths = UnpackStringPaths(env);
 				}
 

+ 17 - 2
src/MoonSharp.Interpreter/Modules/ModuleRegister.cs

@@ -6,14 +6,24 @@ using System.Text;
 using MoonSharp.Interpreter.CoreLib;
 using MoonSharp.Interpreter.Execution;
 using MoonSharp.Interpreter.Interop;
+using MoonSharp.Interpreter.Platforms;
 
 namespace MoonSharp.Interpreter
 {
+	/// <summary>
+	/// Class managing modules (mostly as extension methods)
+	/// </summary>
 	public static class ModuleRegister
 	{
+		/// <summary>
+		/// Register the core modules to a table
+		/// </summary>
+		/// <param name="table">The table.</param>
+		/// <param name="modules">The modules.</param>
+		/// <returns></returns>
 		public static Table RegisterCoreModules(this Table table, CoreModules modules)
 		{
-			modules = Script.Platform.FilterSupportedCoreModules(modules);
+			modules = Script.GlobalOptions.Platform.FilterSupportedCoreModules(modules);
 
 			if (modules.Has(CoreModules.GlobalConsts)) RegisterConstants(table);
 			if (modules.Has(CoreModules.TableIterators)) RegisterModuleType<TableIteratorsModule>(table);
@@ -49,7 +59,12 @@ namespace MoonSharp.Interpreter
 
 			m.Set("version", DynValue.NewString(Script.VERSION));
 			m.Set("luacompat", DynValue.NewString(Script.LUA_VERSION));
-			// m.Set("platform", DynValue.NewString(Platform.Current.Name)); +++
+			m.Set("platform", DynValue.NewString(Script.GlobalOptions.Platform.GetPlatformName()));
+			m.Set("is_aot", DynValue.NewBoolean(Script.GlobalOptions.Platform.IsRunningOnAOT()));
+			m.Set("is_unity", DynValue.NewBoolean(PlatformAutoDetector.IsRunningOnUnity));
+			m.Set("is_mono", DynValue.NewBoolean(PlatformAutoDetector.IsRunningOnMono));
+			m.Set("is_clr4", DynValue.NewBoolean(PlatformAutoDetector.IsRunningOnClr4));
+			m.Set("is_pcl", DynValue.NewBoolean(PlatformAutoDetector.IsPortableFramework));
 
 			return table;
 		}

+ 2 - 0
src/MoonSharp.Interpreter/MoonSharp.Interpreter.net35-client.csproj

@@ -93,6 +93,7 @@
     <Compile Include="CoreLib\OsTimeModule.cs" />
     <Compile Include="CoreLib\StringLib\KopiLua_StrLib.cs" />
     <Compile Include="DataStructs\MultiDictionary.cs" />
+    <Compile Include="Interop\CustomConvertersCollection.cs" />
     <Compile Include="IO\BinDumpBinaryReader.cs" />
     <Compile Include="IO\BinDumpBinaryWriter.cs">
       <SubType>Code</SubType>
@@ -237,6 +238,7 @@
     <Compile Include="Modules\MoonSharpModuleAttribute.cs">
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="ScriptGlobalOptions.cs" />
     <Compile Include="ScriptOptions.cs" />
     <Compile Include="Tree\Expressions\AdjustmentExpression.cs" />
     <Compile Include="Tree\Expressions\BinaryOperatorExpression.cs" />

+ 17 - 0
src/MoonSharp.Interpreter/Properties/AssemblyInfo.cs

@@ -34,3 +34,20 @@ using System.Runtime.InteropServices;
 // [assembly: AssemblyVersion("1.0.*")]
 [assembly: AssemblyVersion(MoonSharp.Interpreter.Script.VERSION)]
 [assembly: AssemblyFileVersion(MoonSharp.Interpreter.Script.VERSION)]
+
+// Give 
+[assembly: InternalsVisibleTo("MoonSharp.Interpreter.Tests")]
+[assembly: InternalsVisibleTo("MoonSharp.Interpreter.Tests.net40-client")]
+[assembly: InternalsVisibleTo("MoonSharp.Interpreter.Tests.portable40")]
+[assembly: InternalsVisibleTo("MoonSharp.Interpreter.Tests.net35-client")]
+
+
+
+
+
+
+
+
+
+
+

+ 9 - 13
src/MoonSharp.Interpreter/Script.cs

@@ -47,26 +47,18 @@ namespace MoonSharp.Interpreter
 		/// </summary>
 		static Script()
 		{
-			Platform = PlatformAutoDetector.GetDefaultPlatform();
+			GlobalOptions = new ScriptGlobalOptions();
 
 			DefaultOptions = new ScriptOptions()
 			{
-				DebugPrint = s => { Script.Platform.DefaultPrint(s); },
-				DebugInput = () => { return Script.Platform.DefaultInput(); },
+				DebugPrint = s => { Script.GlobalOptions.Platform.DefaultPrint(s); },
+				DebugInput = () => { return Script.GlobalOptions.Platform.DefaultInput(); },
 				CheckThreadAccess = true,
-				ScriptLoader = PlatformAutoDetector.GetDefaultScriptLoader()
+				ScriptLoader = PlatformAutoDetector.GetDefaultScriptLoader(),
+				TailCallOptimizationThreshold = 65536
 			};
 		}
 
-		/// <summary>
-		/// Gets or sets the platform abstraction to use.
-		/// </summary>
-		/// <value>
-		/// The current platform abstraction.
-		/// </value>
-		public static IPlatformAccessor Platform { get; set; }
-
-
 		/// <summary>
 		/// Initializes a new instance of the <see cref="Script"/> class.
 		/// </summary>
@@ -102,6 +94,10 @@ namespace MoonSharp.Interpreter
 		/// </summary>
 		public ScriptOptions Options { get; private set; }
 
+		/// <summary>
+		/// Gets the global options, that is options which cannot be customized per-script.
+		/// </summary>
+		public static ScriptGlobalOptions GlobalOptions { get; private set; }
 
 		/// <summary>
 		/// Gets access to performance statistics.

+ 33 - 0
src/MoonSharp.Interpreter/ScriptGlobalOptions.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using MoonSharp.Interpreter.Interop;
+using MoonSharp.Interpreter.Platforms;
+
+namespace MoonSharp.Interpreter
+{
+	public class ScriptGlobalOptions
+	{
+		internal ScriptGlobalOptions()
+		{
+			Platform = PlatformAutoDetector.GetDefaultPlatform();
+			CustomConverters = new CustomConvertersCollection();
+		}
+
+		/// <summary>
+		/// Gets or sets the custom converters.
+		/// </summary>
+		public CustomConvertersCollection CustomConverters { get; set; }
+
+		/// <summary>
+		/// Gets or sets the platform abstraction to use.
+		/// </summary>
+		/// <value>
+		/// The current platform abstraction.
+		/// </value>
+		public IPlatformAccessor Platform { get; set; }
+
+
+	}
+}

+ 17 - 1
src/MoonSharp.Interpreter/ScriptOptions.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Text;
+using MoonSharp.Interpreter.Interop;
 using MoonSharp.Interpreter.Loaders;
 using MoonSharp.Interpreter.Platforms;
 
@@ -15,7 +16,6 @@ namespace MoonSharp.Interpreter
 	{
 		internal ScriptOptions()
 		{
-
 		}
 
 		internal ScriptOptions(ScriptOptions defaults)
@@ -27,6 +27,7 @@ namespace MoonSharp.Interpreter
 			this.Stdin = defaults.Stdin;
 			this.Stdout = defaults.Stdout;
 			this.Stderr = defaults.Stderr;
+			this.TailCallOptimizationThreshold = defaults.TailCallOptimizationThreshold;
 
 			this.ScriptLoader = defaults.ScriptLoader;
 
@@ -69,6 +70,20 @@ namespace MoonSharp.Interpreter
 		/// </summary>
 		public Stream Stderr { get; set; }
 
+		/// <summary>
+		/// Gets or sets the stack depth threshold at which MoonSharp starts doing
+		/// tail call optimizations.
+		/// TCOs can provide the little benefit of avoiding stack overflows in corner case
+		/// scenarios, at the expense of losing debug information and error stack traces 
+		/// in all other, more common scenarios. MoonSharp choice is to start performing
+		/// TCOs only after a certain threshold of stack usage is reached - by default
+		/// half the current stack depth (128K entries), thus 64K entries, on either
+		/// the internal stacks.
+		/// Set this to int.MaxValue to disable TCOs entirely, or to 0 to always have
+		/// TCOs enabled.
+		/// </summary>
+		public int TailCallOptimizationThreshold { get; set; }
+
 		/// <summary>
 		/// Gets or sets a value indicating whether the thread check is enabled.
 		/// A "lazy" thread check is performed everytime execution is entered to ensure that no two threads
@@ -81,5 +96,6 @@ namespace MoonSharp.Interpreter
 		/// </summary>
 		public bool CheckThreadAccess { get; set; }
 
+
 	}
 }

+ 6 - 0
src/MoonSharp.Interpreter/_Projects/MoonSharp.Interpreter.net40-client/MoonSharp.Interpreter.net40-client.csproj

@@ -97,6 +97,9 @@
     <Compile Include="..\..\DataStructs\MultiDictionary.cs">
       <Link>MultiDictionary.cs</Link>
     </Compile>
+    <Compile Include="..\..\Interop\CustomConvertersCollection.cs">
+      <Link>CustomConvertersCollection.cs</Link>
+    </Compile>
     <Compile Include="..\..\IO\BinDumpBinaryReader.cs">
       <Link>BinDumpBinaryReader.cs</Link>
     </Compile>
@@ -474,6 +477,9 @@
       <SubType>Code</SubType>
       <Link>MoonSharpModuleAttribute.cs</Link>
     </Compile>
+    <Compile Include="..\..\ScriptGlobalOptions.cs">
+      <Link>ScriptGlobalOptions.cs</Link>
+    </Compile>
     <Compile Include="..\..\ScriptOptions.cs">
       <Link>ScriptOptions.cs</Link>
     </Compile>

+ 6 - 0
src/MoonSharp.Interpreter/_Projects/MoonSharp.Interpreter.portable40/MoonSharp.Interpreter.portable40.csproj

@@ -94,6 +94,9 @@
     <Compile Include="..\..\DataStructs\MultiDictionary.cs">
       <Link>MultiDictionary.cs</Link>
     </Compile>
+    <Compile Include="..\..\Interop\CustomConvertersCollection.cs">
+      <Link>CustomConvertersCollection.cs</Link>
+    </Compile>
     <Compile Include="..\..\IO\BinDumpBinaryReader.cs">
       <Link>BinDumpBinaryReader.cs</Link>
     </Compile>
@@ -471,6 +474,9 @@
       <SubType>Code</SubType>
       <Link>MoonSharpModuleAttribute.cs</Link>
     </Compile>
+    <Compile Include="..\..\ScriptGlobalOptions.cs">
+      <Link>ScriptGlobalOptions.cs</Link>
+    </Compile>
     <Compile Include="..\..\ScriptOptions.cs">
       <Link>ScriptOptions.cs</Link>
     </Compile>

+ 1 - 1
src/MoonSharp/Program.cs

@@ -22,7 +22,7 @@ namespace MoonSharp
 		{
 			Script.DefaultOptions.ScriptLoader = new ReplInterpreterScriptLoader();
 
-			Console.WriteLine("MoonSharp REPL {0} [{1}]", Script.VERSION, Script.Platform.GetPlatformName());
+			Console.WriteLine("MoonSharp REPL {0} [{1}]", Script.VERSION, Script.GlobalOptions.Platform.GetPlatformName());
 			Console.WriteLine("Copyright (C) 2014 Marco Mastropaolo");
 			Console.WriteLine("http://www.moonsharp.org");
 			Console.WriteLine("Based on Lua 5.1 - 5.3, Copyright (C) 1994-2014 Lua.org");

+ 1 - 1
src/TestRunners/ConsoleTestRunner/Program.cs

@@ -30,7 +30,7 @@ namespace MoonSharpTests
 				if (LOG_ON_FILE != null)
 					File.WriteAllText(LOG_ON_FILE, "");
 
-				Console_WriteLine("Running on AOT : {0}", Script.Platform.IsRunningOnAOT());
+				Console_WriteLine("Running on AOT : {0}", Script.GlobalOptions.Platform.IsRunningOnAOT());
 
 				T.Test(RESTRICT_TEST);