Browse Source

__newindex and type(v)

Xanathar 11 years ago
parent
commit
f0ff0fcf49

+ 72 - 0
src/MoonSharp.Interpreter.Tests/EndToEnd/MetatableTests.cs

@@ -133,5 +133,77 @@ namespace MoonSharp.Interpreter.Tests.EndToEnd
 
 		}
 
+		[Test]
+		public void MetatableIndexAndSetIndexFuncs()
+		{
+			string script = @"    
+					T = { a = 'a', b = 'b', c = 'c' };
+
+					t = { };
+
+					m = { };
+
+					s = '';
+
+
+					function m.__index(obj, idx)
+						return T[idx];
+					end
+
+					function m.__newindex(obj, idx, val)
+						T[idx] = val;
+					end
+
+					setmetatable(t, m);
+
+					s = s .. t.a .. t.b .. t.c;
+
+					t.a = '!';
+
+					s = s .. t.a .. t.b .. t.c;
+
+					return(s);
+				";
+
+			DynValue res = (new Script()).DoString(script);
+
+
+			Assert.AreEqual(DataType.String, res.Type);
+			Assert.AreEqual("abc!bc", res.String);
+		}
+
+		[Test]
+		public void MetatableIndexAndSetIndexBounce()
+		{
+			string script = @"    
+					T = { a = 'a', b = 'b', c = 'c' };
+
+					t = { };
+
+					m = { __index = T, __newindex = T };
+
+					s = '';
+
+					setmetatable(t, m);
+
+					s = s .. t.a .. t.b .. t.c;
+
+					t.a = '!';
+
+					s = s .. t.a .. t.b .. t.c;
+
+					return(s);
+				";
+
+			DynValue res = (new Script()).DoString(script);
+
+
+			Assert.AreEqual(DataType.String, res.Type);
+			Assert.AreEqual("abc!bc", res.String);
+		}
+
+
+
+
 	}
 }

+ 13 - 0
src/MoonSharp.Interpreter/CoreLib/BasicMethods.cs

@@ -9,6 +9,19 @@ namespace MoonSharp.Interpreter.CoreLib
 	[MoonSharpModule]
 	public static class BasicMethods
 	{
+		//type (v)
+		//----------------------------------------------------------------------------------------------------------------
+		//Returns the type of its only argument, coded as a string. The possible results of this function are "nil" 
+		//(a string, not the value nil), "number", "string", "boolean", "table", "function", "thread", and "userdata". 
+		[MoonSharpMethod]
+		public static DynValue type(ScriptExecutionContext executionContext, CallbackArguments args)
+		{
+			DynValue v = args[0];
+			return DynValue.NewString(v.Type.ToLuaTypeString());
+		}
+
+
+
 		//assert (v [, message])
 		//----------------------------------------------------------------------------------------------------------------
 		//Issues an error when the value of its argument v is false (i.e., nil or false); 

+ 3 - 3
src/MoonSharp.Interpreter/Execution/VM/ByteCode.cs

@@ -114,17 +114,17 @@ namespace MoonSharp.Interpreter.Execution.VM
 
 		public Instruction Emit_Enter(RuntimeScopeBlock runtimeScopeBlock)
 		{
-			return AppendInstruction(new Instruction() { OpCode = OpCode.Enter, Block = runtimeScopeBlock });
+			return AppendInstruction(new Instruction() { OpCode = OpCode.Enter, NumVal = runtimeScopeBlock.From, NumVal2 = runtimeScopeBlock.ToInclusive });
 		}
 
 		public Instruction Emit_Leave(RuntimeScopeBlock runtimeScopeBlock)
 		{
-			return AppendInstruction(new Instruction() { OpCode = OpCode.Leave, Block = runtimeScopeBlock });
+			return AppendInstruction(new Instruction() { OpCode = OpCode.Leave, NumVal = runtimeScopeBlock.From, NumVal2 = runtimeScopeBlock.To });
 		}
 
 		public Instruction Emit_Exit(RuntimeScopeBlock runtimeScopeBlock)
 		{
-			return AppendInstruction(new Instruction() { OpCode = OpCode.Exit, Block = runtimeScopeBlock });
+			return AppendInstruction(new Instruction() { OpCode = OpCode.Exit, NumVal = runtimeScopeBlock.From, NumVal2 = runtimeScopeBlock.ToInclusive });
 		}
 
 		public Instruction Emit_Closure(SymbolRef[] symbols, int jmpnum)

+ 3 - 14
src/MoonSharp.Interpreter/Execution/VM/Instruction.cs

@@ -15,7 +15,6 @@ namespace MoonSharp.Interpreter.Execution.VM
 		public DynValue Value;
 		public int NumVal;
 		public int NumVal2;
-		public RuntimeScopeBlock Block;
 		public bool Breakpoint;
 		public SourceRef SourceCodeRef;
 
@@ -31,11 +30,6 @@ namespace MoonSharp.Interpreter.Execution.VM
 				case OpCode.Args:
 					append = string.Format("{0}({1})", GenSpaces(), string.Join(",", SymbolList.Select(s => s.ToString()).ToArray()));
 					break;
-				case OpCode.Enter:
-				case OpCode.Leave:
-				case OpCode.Exit:
-					append = string.Format("{0}{1}", GenSpaces(), FrameToString(Block));
-					break;
 				case OpCode.Debug:
 					return string.Format("[[ {0} ]]", Name);
 				case OpCode.Literal:
@@ -57,6 +51,9 @@ namespace MoonSharp.Interpreter.Execution.VM
 				case OpCode.Copy:
 					append = string.Format("{0}{1}", GenSpaces(), NumVal);
 					break;
+				case OpCode.Enter:
+				case OpCode.Leave:
+				case OpCode.Exit:
 				case OpCode.Swap:
 					append = string.Format("{0}{1},{2}", GenSpaces(), NumVal, NumVal2);
 					break;
@@ -89,14 +86,6 @@ namespace MoonSharp.Interpreter.Execution.VM
 			return this.OpCode.ToString().ToUpperInvariant() + append;
 		}
 
-		private string FrameToString(RuntimeScopeBlock frame)
-		{
-			if (frame == null)
-				return "<null>";
-			else
-				return frame.ToString();
-		}
-
 		private string PurifyFromNewLines(DynValue Value)
 		{
 			if (Value == null)

+ 49 - 11
src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs

@@ -106,11 +106,11 @@ namespace MoonSharp.Interpreter.Execution.VM
 						ExecMkTuple(i);
 						break;
 					case OpCode.Enter:
-						NilifyBlockData(i.Block);
+						NilifyBlockData(i);
 						break;
 					case OpCode.Leave:
 					case OpCode.Exit:
-						ClearBlockData(i.Block, i.OpCode == OpCode.Exit);
+						ClearBlockData(i);
 						break;
 					case OpCode.Closure:
 						ExecClosure(i);
@@ -877,20 +877,58 @@ namespace MoonSharp.Interpreter.Execution.VM
 
 		private int ExecIndexSet(Instruction i, int instructionPtr)
 		{
+			int nestedMetaOps = 100; // sanity check, to avoid potential infinite loop here
+			
 			// stack: vals.. - base - index
 			DynValue idx = i.Value ?? m_ValueStack.Pop();
 			DynValue obj = m_ValueStack.Pop();
-			var v = GetStoreValue(i);
+			var value = GetStoreValue(i);
+			DynValue h = null;
 
-			if (obj.Type == DataType.Table)
-			{
-				obj.Table[idx] = v;
-				return instructionPtr;
-			}
-			else
+			while (nestedMetaOps > 0)
 			{
-				throw new NotImplementedException();
+				--nestedMetaOps;
+
+				if (obj.Type == DataType.Table)
+				{
+					if (!obj.Table[idx].IsNil())
+					{
+						obj.Table[idx] = value;
+						return instructionPtr;
+					}
+
+					if (obj.MetaTable != null)
+						h = obj.MetaTable.RawGet("__newindex");
+
+					if (h == null || h.IsNil())
+					{
+						obj.Table[idx] = value;
+						return instructionPtr;
+					}
+				}
+				else
+				{
+					h = obj.MetaTable.RawGet("__newindex");
+
+					if (h == null || h.IsNil())
+						throw new ScriptRuntimeException("Can't index non table: {0}", obj);
+				}
+
+				if (h.Type == DataType.Function || h.Type == DataType.ClrFunction)
+				{
+					m_ValueStack.Push(h);
+					m_ValueStack.Push(obj);
+					m_ValueStack.Push(idx);
+					m_ValueStack.Push(value);
+					return Internal_ExecCall(3, instructionPtr);
+				}
+				else
+				{
+					obj = h;
+					h = null;
+				}
 			}
+			throw new ScriptRuntimeException("__newindex returning too many nested tables");
 		}
 
 		private int ExecIndex(Instruction i, int instructionPtr)
@@ -935,7 +973,7 @@ namespace MoonSharp.Interpreter.Execution.VM
 						throw new ScriptRuntimeException("Can't index non table: {0}", obj);
 				}
 
-				if (h.Type == DataType.Function)
+				if (h.Type == DataType.Function || h.Type == DataType.ClrFunction)
 				{
 					m_ValueStack.Push(h);
 					m_ValueStack.Push(obj);

+ 6 - 6
src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Scope.cs

@@ -8,10 +8,10 @@ namespace MoonSharp.Interpreter.Execution.VM
 {
 	sealed partial class Processor
 	{
-		private void NilifyBlockData(RuntimeScopeBlock runtimeScopeBlock)
+		private void NilifyBlockData(Instruction I)
 		{
-			int from = runtimeScopeBlock.From;
-			int to = runtimeScopeBlock.To;
+			int from = I.NumVal;
+			int to = I.NumVal2;
 
 			var array = this.m_ExecutionStack.Peek().LocalScope;
 
@@ -22,10 +22,10 @@ namespace MoonSharp.Interpreter.Execution.VM
 			}
 		}
 
-		private void ClearBlockData(RuntimeScopeBlock runtimeScopeBlock, bool clearToInclusive)
+		private void ClearBlockData(Instruction I)
 		{
-			int from = runtimeScopeBlock.From;
-			int to = clearToInclusive ? runtimeScopeBlock.ToInclusive : runtimeScopeBlock.To;
+			int from = I.NumVal;
+			int to = I.NumVal2;
 
 			var array = this.m_ExecutionStack.Peek().LocalScope;