LuaValue.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. using System.Runtime.CompilerServices;
  2. using System.Runtime.InteropServices;
  3. using Lua.Runtime;
  4. namespace Lua;
  5. public enum LuaValueType : byte
  6. {
  7. Nil,
  8. Boolean,
  9. String,
  10. Number,
  11. Function,
  12. UserData,
  13. Table,
  14. }
  15. [StructLayout(LayoutKind.Auto)]
  16. public readonly struct LuaValue : IEquatable<LuaValue>
  17. {
  18. public static readonly LuaValue Nil = default;
  19. readonly LuaValueType type;
  20. readonly double value;
  21. readonly object? referenceValue;
  22. public LuaValueType Type => type;
  23. public bool TryRead<T>(out T result)
  24. {
  25. var t = typeof(T);
  26. switch (type)
  27. {
  28. case LuaValueType.Number:
  29. if (t == typeof(int))
  30. {
  31. var v = (int)value;
  32. result = Unsafe.As<int, T>(ref v);
  33. return true;
  34. }
  35. else if (t == typeof(long))
  36. {
  37. var v = (long)value;
  38. result = Unsafe.As<long, T>(ref v);
  39. return true;
  40. }
  41. else if (t == typeof(float))
  42. {
  43. var v = (float)value;
  44. result = Unsafe.As<float, T>(ref v);
  45. return true;
  46. }
  47. else if (t == typeof(double))
  48. {
  49. var v = value;
  50. result = Unsafe.As<double, T>(ref v);
  51. return true;
  52. }
  53. else if (t == typeof(object))
  54. {
  55. result = (T)(object)value;
  56. return true;
  57. }
  58. else
  59. {
  60. break;
  61. }
  62. case LuaValueType.Boolean:
  63. if (t == typeof(bool))
  64. {
  65. var v = value == 1;
  66. result = Unsafe.As<bool, T>(ref v);
  67. return true;
  68. }
  69. else if (t == typeof(object))
  70. {
  71. result = (T)(object)value;
  72. return true;
  73. }
  74. else
  75. {
  76. break;
  77. }
  78. case LuaValueType.String:
  79. if (t == typeof(string))
  80. {
  81. var v = (string)referenceValue!;
  82. result = Unsafe.As<string, T>(ref v);
  83. return true;
  84. }
  85. else if (t == typeof(object))
  86. {
  87. result = (T)referenceValue!;
  88. return true;
  89. }
  90. else
  91. {
  92. break;
  93. }
  94. case LuaValueType.Function:
  95. if (t == typeof(LuaFunction) || t.IsSubclassOf(typeof(LuaFunction)))
  96. {
  97. var v = (LuaFunction)referenceValue!;
  98. result = Unsafe.As<LuaFunction, T>(ref v);
  99. return true;
  100. }
  101. else if (t == typeof(object))
  102. {
  103. result = (T)referenceValue!;
  104. return true;
  105. }
  106. else
  107. {
  108. break;
  109. }
  110. case LuaValueType.UserData:
  111. if (referenceValue is T userData)
  112. {
  113. result = userData;
  114. return true;
  115. }
  116. break;
  117. case LuaValueType.Table:
  118. if (t == typeof(LuaTable))
  119. {
  120. var v = (LuaTable)referenceValue!;
  121. result = Unsafe.As<LuaTable, T>(ref v);
  122. return true;
  123. }
  124. else if (t == typeof(object))
  125. {
  126. result = (T)referenceValue!;
  127. return true;
  128. }
  129. else
  130. {
  131. break;
  132. }
  133. }
  134. result = default!;
  135. return false;
  136. }
  137. public T Read<T>()
  138. {
  139. if (!TryRead<T>(out var result)) throw new InvalidOperationException($"Cannot convert LuaValueType.{Type} to {typeof(T).FullName}.");
  140. return result;
  141. }
  142. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  143. public bool ToBoolean()
  144. {
  145. if (Type is LuaValueType.Nil) return false;
  146. if (TryRead<bool>(out var result)) return result;
  147. return true;
  148. }
  149. public LuaValue(bool value)
  150. {
  151. type = LuaValueType.Boolean;
  152. this.value = value ? 1 : 0;
  153. }
  154. public LuaValue(double value)
  155. {
  156. type = LuaValueType.Number;
  157. this.value = value;
  158. }
  159. public LuaValue(string value)
  160. {
  161. type = LuaValueType.String;
  162. referenceValue = value;
  163. }
  164. public LuaValue(LuaFunction value)
  165. {
  166. type = LuaValueType.Function;
  167. referenceValue = value;
  168. }
  169. public LuaValue(LuaTable value)
  170. {
  171. type = LuaValueType.Table;
  172. referenceValue = value;
  173. }
  174. public LuaValue(object? value)
  175. {
  176. type = LuaValueType.UserData;
  177. referenceValue = value;
  178. }
  179. public static implicit operator LuaValue(bool value)
  180. {
  181. return new(value);
  182. }
  183. public static implicit operator LuaValue(double value)
  184. {
  185. return new(value);
  186. }
  187. public static implicit operator LuaValue(string value)
  188. {
  189. return new(value);
  190. }
  191. public static implicit operator LuaValue(LuaTable value)
  192. {
  193. return new(value);
  194. }
  195. public static implicit operator LuaValue(LuaFunction value)
  196. {
  197. return new(value);
  198. }
  199. public override int GetHashCode()
  200. {
  201. var valueHash = type switch
  202. {
  203. LuaValueType.Nil => 0,
  204. LuaValueType.Boolean => Read<bool>().GetHashCode(),
  205. LuaValueType.String => Read<string>().GetHashCode(),
  206. LuaValueType.Number => Read<double>().GetHashCode(),
  207. LuaValueType.Function => Read<LuaFunction>().GetHashCode(),
  208. LuaValueType.Table => Read<LuaTable>().GetHashCode(),
  209. LuaValueType.UserData => referenceValue == null ? 0 : referenceValue.GetHashCode(),
  210. _ => 0,
  211. };
  212. return HashCode.Combine(type, valueHash);
  213. }
  214. public bool Equals(LuaValue other)
  215. {
  216. if (other.Type != Type) return false;
  217. return type switch
  218. {
  219. LuaValueType.Nil => true,
  220. LuaValueType.Boolean => Read<bool>().Equals(other.Read<bool>()),
  221. LuaValueType.String => Read<string>().Equals(other.Read<string>()),
  222. LuaValueType.Number => Read<double>().Equals(other.Read<double>()),
  223. LuaValueType.Function => Read<LuaFunction>().Equals(other.Read<LuaFunction>()),
  224. LuaValueType.Table => Read<LuaTable>().Equals(other.Read<LuaTable>()),
  225. LuaValueType.UserData => referenceValue == other.referenceValue,
  226. _ => false,
  227. };
  228. }
  229. public override bool Equals(object? obj)
  230. {
  231. return obj is LuaValue value1 && Equals(value1);
  232. }
  233. public static bool operator ==(LuaValue a, LuaValue b)
  234. {
  235. return a.Equals(b);
  236. }
  237. public static bool operator !=(LuaValue a, LuaValue b)
  238. {
  239. return !a.Equals(b);
  240. }
  241. public override string? ToString()
  242. {
  243. return type switch
  244. {
  245. LuaValueType.Nil => "Nil",
  246. LuaValueType.Boolean => Read<bool>().ToString(),
  247. LuaValueType.String => Read<string>().ToString(),
  248. LuaValueType.Number => Read<double>().ToString(),
  249. LuaValueType.Function => Read<LuaFunction>().ToString(),
  250. LuaValueType.Table => Read<LuaTable>().ToString(),
  251. LuaValueType.UserData => referenceValue?.ToString(),
  252. _ => "",
  253. };
  254. }
  255. public static bool TryGetLuaValueType(Type type, out LuaValueType result)
  256. {
  257. if (type == typeof(double) || type == typeof(float) || type == typeof(int) || type == typeof(long))
  258. {
  259. result = LuaValueType.Number;
  260. return true;
  261. }
  262. else if (type == typeof(bool))
  263. {
  264. result = LuaValueType.Boolean;
  265. return true;
  266. }
  267. else if (type == typeof(string))
  268. {
  269. result = LuaValueType.String;
  270. return true;
  271. }
  272. else if (type == typeof(LuaFunction) || type.IsSubclassOf(typeof(LuaFunction)))
  273. {
  274. result = LuaValueType.Function;
  275. return true;
  276. }
  277. else if (type == typeof(LuaTable))
  278. {
  279. result = LuaValueType.Table;
  280. return true;
  281. }
  282. result = default;
  283. return false;
  284. }
  285. internal async ValueTask<int> CallToStringAsync(LuaFunctionExecutionContext context, Memory<LuaValue> buffer, CancellationToken cancellationToken)
  286. {
  287. if (this.TryGetMetamethod(Metamethods.ToString, out var metamethod))
  288. {
  289. if (!metamethod.TryRead<LuaFunction>(out var func))
  290. {
  291. LuaRuntimeException.AttemptInvalidOperation(context.State.GetTracebacks(), "call", metamethod);
  292. }
  293. context.State.Push(this);
  294. return await func.InvokeAsync(context with
  295. {
  296. ArgumentCount = 1,
  297. }, buffer, cancellationToken);
  298. }
  299. else
  300. {
  301. buffer.Span[0] = ToString()!;
  302. return 1;
  303. }
  304. }
  305. }