LuaState.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. using System.Diagnostics.CodeAnalysis;
  2. using Lua.Internal;
  3. using Lua.Loaders;
  4. using Lua.Runtime;
  5. namespace Lua;
  6. public sealed class LuaState
  7. {
  8. public const string DefaultChunkName = "chunk";
  9. // states
  10. readonly LuaMainThread mainThread = new();
  11. FastListCore<UpValue> openUpValues;
  12. FastStackCore<LuaThread> threadStack;
  13. readonly LuaTable environment;
  14. readonly UpValue envUpValue;
  15. bool isRunning;
  16. internal UpValue EnvUpValue => envUpValue;
  17. internal ref FastStackCore<LuaThread> ThreadStack => ref threadStack;
  18. internal ref FastListCore<UpValue> OpenUpValues => ref openUpValues;
  19. public LuaTable Environment => environment;
  20. public LuaMainThread MainThread => mainThread;
  21. public LuaThread CurrentThread
  22. {
  23. get
  24. {
  25. if (threadStack.TryPeek(out var thread)) return thread;
  26. return mainThread;
  27. }
  28. }
  29. public ILuaModuleLoader ModuleLoader { get; set; } = FileModuleLoader.Instance;
  30. // metatables
  31. LuaTable? nilMetatable;
  32. LuaTable? numberMetatable;
  33. LuaTable? stringMetatable;
  34. LuaTable? booleanMetatable;
  35. LuaTable? functionMetatable;
  36. LuaTable? threadMetatable;
  37. public static LuaState Create()
  38. {
  39. return new();
  40. }
  41. LuaState()
  42. {
  43. environment = new();
  44. envUpValue = UpValue.Closed(mainThread, environment);
  45. }
  46. public async ValueTask<int> RunAsync(Chunk chunk, Memory<LuaValue> buffer, CancellationToken cancellationToken = default)
  47. {
  48. ThrowIfRunning();
  49. Volatile.Write(ref isRunning, true);
  50. try
  51. {
  52. var closure = new Closure(this, chunk);
  53. return await closure.InvokeAsync(new()
  54. {
  55. State = this,
  56. ArgumentCount = 0,
  57. StackPosition = 0,
  58. SourcePosition = null,
  59. RootChunkName = chunk.Name ?? DefaultChunkName,
  60. ChunkName = chunk.Name ?? DefaultChunkName,
  61. }, buffer, cancellationToken);
  62. }
  63. finally
  64. {
  65. Volatile.Write(ref isRunning, false);
  66. }
  67. }
  68. public void Push(LuaValue value)
  69. {
  70. CurrentThread.Stack.Push(value);
  71. }
  72. public LuaThread CreateThread(LuaFunction function, bool isProtectedMode = true)
  73. {
  74. return new LuaCoroutine(this, function, isProtectedMode);
  75. }
  76. public Traceback GetTraceback()
  77. {
  78. // TODO: optimize
  79. return new()
  80. {
  81. StackFrames = threadStack.AsSpan().ToArray()
  82. .Append(MainThread)
  83. .SelectMany(x => x.GetStackFrames())
  84. .ToArray()
  85. };
  86. }
  87. internal bool TryGetMetatable(LuaValue value, [NotNullWhen(true)] out LuaTable? result)
  88. {
  89. result = value.Type switch
  90. {
  91. LuaValueType.Nil => nilMetatable,
  92. LuaValueType.Boolean => booleanMetatable,
  93. LuaValueType.String => stringMetatable,
  94. LuaValueType.Number => numberMetatable,
  95. LuaValueType.Function => functionMetatable,
  96. LuaValueType.Thread => threadMetatable,
  97. LuaValueType.UserData => value.Read<LuaUserData>().Metatable,
  98. LuaValueType.Table => value.Read<LuaTable>().Metatable,
  99. _ => null
  100. };
  101. return result != null;
  102. }
  103. internal void SetMetatable(LuaValue value, LuaTable metatable)
  104. {
  105. switch (value.Type)
  106. {
  107. case LuaValueType.Nil:
  108. nilMetatable = metatable;
  109. break;
  110. case LuaValueType.Boolean:
  111. booleanMetatable = metatable;
  112. break;
  113. case LuaValueType.String:
  114. stringMetatable = metatable;
  115. break;
  116. case LuaValueType.Number:
  117. numberMetatable = metatable;
  118. break;
  119. case LuaValueType.Function:
  120. functionMetatable = metatable;
  121. break;
  122. case LuaValueType.Thread:
  123. threadMetatable = metatable;
  124. break;
  125. case LuaValueType.UserData:
  126. value.Read<LuaUserData>().Metatable = metatable;
  127. break;
  128. case LuaValueType.Table:
  129. value.Read<LuaTable>().Metatable = metatable;
  130. break;
  131. }
  132. }
  133. internal UpValue GetOrAddUpValue(LuaThread thread, int registerIndex)
  134. {
  135. foreach (var upValue in openUpValues.AsSpan())
  136. {
  137. if (upValue.RegisterIndex == registerIndex && upValue.Thread == thread)
  138. {
  139. return upValue;
  140. }
  141. }
  142. var newUpValue = UpValue.Open(thread, registerIndex);
  143. openUpValues.Add(newUpValue);
  144. return newUpValue;
  145. }
  146. internal void CloseUpValues(LuaThread thread, int frameBase)
  147. {
  148. for (int i = 0; i < openUpValues.Length; i++)
  149. {
  150. var upValue = openUpValues[i];
  151. if (upValue.Thread != thread) continue;
  152. if (upValue.RegisterIndex >= frameBase)
  153. {
  154. upValue.Close();
  155. openUpValues.RemoveAtSwapback(i);
  156. i--;
  157. }
  158. }
  159. }
  160. void ThrowIfRunning()
  161. {
  162. if (Volatile.Read(ref isRunning))
  163. {
  164. throw new InvalidOperationException("the lua state is currently running");
  165. }
  166. }
  167. }