LuaValue.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. using System.Globalization;
  2. using System.Runtime.CompilerServices;
  3. using System.Runtime.InteropServices;
  4. using Lua.Internal;
  5. using Lua.Runtime;
  6. namespace Lua;
  7. public enum LuaValueType : byte
  8. {
  9. Nil,
  10. Boolean,
  11. String,
  12. Number,
  13. Function,
  14. Thread,
  15. LightUserData,
  16. UserData,
  17. Table,
  18. }
  19. [StructLayout(LayoutKind.Auto)]
  20. public readonly struct LuaValue : IEquatable<LuaValue>
  21. {
  22. public static readonly LuaValue Nil = default;
  23. public readonly LuaValueType Type;
  24. readonly double value;
  25. readonly object? referenceValue;
  26. public bool TryRead<T>(out T result)
  27. {
  28. var t = typeof(T);
  29. switch (Type)
  30. {
  31. case LuaValueType.Number:
  32. if (t == typeof(float))
  33. {
  34. var v = (float)value;
  35. result = Unsafe.As<float, T>(ref v);
  36. return true;
  37. }
  38. else if (t == typeof(double))
  39. {
  40. var v = value;
  41. result = Unsafe.As<double, T>(ref v);
  42. return true;
  43. }
  44. else if (t == typeof(int))
  45. {
  46. if (!MathEx.IsInteger(value)) break;
  47. var v = (int)value;
  48. result = Unsafe.As<int, T>(ref v);
  49. return true;
  50. }
  51. else if (t == typeof(long))
  52. {
  53. if (!MathEx.IsInteger(value)) break;
  54. var v = (long)value;
  55. result = Unsafe.As<long, T>(ref v);
  56. return true;
  57. }
  58. else if (t == typeof(object))
  59. {
  60. result = (T)(object)value;
  61. return true;
  62. }
  63. else
  64. {
  65. break;
  66. }
  67. case LuaValueType.Boolean:
  68. if (t == typeof(bool))
  69. {
  70. var v = value != 0;
  71. result = Unsafe.As<bool, T>(ref v);
  72. return true;
  73. }
  74. else if (t == typeof(object))
  75. {
  76. result = (T)(object)value;
  77. return true;
  78. }
  79. else
  80. {
  81. break;
  82. }
  83. case LuaValueType.String:
  84. if (t == typeof(string))
  85. {
  86. var v = referenceValue!;
  87. result = Unsafe.As<object, T>(ref v);
  88. return true;
  89. }
  90. else if (t == typeof(double))
  91. {
  92. result = default!;
  93. return TryParseToDouble(out Unsafe.As<T, double>(ref result));
  94. }
  95. else if (t == typeof(object))
  96. {
  97. result = (T)referenceValue!;
  98. return true;
  99. }
  100. else
  101. {
  102. break;
  103. }
  104. case LuaValueType.Function:
  105. if (t == typeof(LuaFunction) || t.IsSubclassOf(typeof(LuaFunction)))
  106. {
  107. var v = referenceValue!;
  108. result = Unsafe.As<object, T>(ref v);
  109. return true;
  110. }
  111. else if (t == typeof(object))
  112. {
  113. result = (T)referenceValue!;
  114. return true;
  115. }
  116. else
  117. {
  118. break;
  119. }
  120. case LuaValueType.Thread:
  121. if (t == typeof(LuaThread))
  122. {
  123. var v = referenceValue!;
  124. result = Unsafe.As<object, T>(ref v);
  125. return true;
  126. }
  127. else if (t == typeof(object))
  128. {
  129. result = (T)referenceValue!;
  130. return true;
  131. }
  132. else
  133. {
  134. break;
  135. }
  136. case LuaValueType.LightUserData:
  137. {
  138. if (referenceValue is T tValue)
  139. {
  140. result = tValue;
  141. return true;
  142. }
  143. break;
  144. }
  145. case LuaValueType.UserData:
  146. if (t == typeof(ILuaUserData) || typeof(ILuaUserData).IsAssignableFrom(t))
  147. {
  148. if (referenceValue is T tValue)
  149. {
  150. result = tValue;
  151. return true;
  152. }
  153. break;
  154. }
  155. else if (t == typeof(object))
  156. {
  157. result = (T)referenceValue!;
  158. return true;
  159. }
  160. else
  161. {
  162. break;
  163. }
  164. case LuaValueType.Table:
  165. if (t == typeof(LuaTable))
  166. {
  167. var v = referenceValue!;
  168. result = Unsafe.As<object, T>(ref v);
  169. return true;
  170. }
  171. else if (t == typeof(object))
  172. {
  173. result = (T)referenceValue!;
  174. return true;
  175. }
  176. else
  177. {
  178. break;
  179. }
  180. }
  181. result = default!;
  182. return false;
  183. }
  184. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  185. internal bool TryReadBool(out bool result)
  186. {
  187. if (Type == LuaValueType.Boolean)
  188. {
  189. result = value != 0;
  190. return true;
  191. }
  192. result = default!;
  193. return false;
  194. }
  195. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  196. internal bool TryReadNumber(out double result)
  197. {
  198. if (Type == LuaValueType.Number)
  199. {
  200. result = value;
  201. return true;
  202. }
  203. result = default!;
  204. return false;
  205. }
  206. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  207. internal bool TryReadTable(out LuaTable result)
  208. {
  209. if (Type == LuaValueType.Table)
  210. {
  211. var v = referenceValue!;
  212. result = Unsafe.As<object, LuaTable>(ref v);
  213. return true;
  214. }
  215. result = default!;
  216. return false;
  217. }
  218. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  219. internal bool TryReadFunction(out LuaFunction result)
  220. {
  221. if (Type == LuaValueType.Function)
  222. {
  223. var v = referenceValue!;
  224. result = Unsafe.As<object, LuaFunction>(ref v);
  225. return true;
  226. }
  227. result = default!;
  228. return false;
  229. }
  230. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  231. internal bool TryReadString(out string result)
  232. {
  233. if (Type == LuaValueType.String)
  234. {
  235. var v = referenceValue!;
  236. result = Unsafe.As<object, string>(ref v);
  237. return true;
  238. }
  239. result = default!;
  240. return false;
  241. }
  242. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  243. internal bool TryReadDouble(out double result)
  244. {
  245. if (Type == LuaValueType.Number)
  246. {
  247. result = value;
  248. return true;
  249. }
  250. return TryParseToDouble(out result);
  251. }
  252. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  253. internal static bool TryReadOrSetDouble(ref LuaValue luaValue, out double result)
  254. {
  255. if (luaValue.Type == LuaValueType.Number)
  256. {
  257. result = luaValue.value;
  258. return true;
  259. }
  260. if (luaValue.TryParseToDouble(out result))
  261. {
  262. luaValue = result;
  263. return true;
  264. }
  265. return false;
  266. }
  267. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  268. internal double UnsafeReadDouble()
  269. {
  270. return value;
  271. }
  272. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  273. internal string UnsafeReadString()
  274. {
  275. return Unsafe.As<string>(referenceValue!);
  276. }
  277. bool TryParseToDouble(out double result)
  278. {
  279. if (Type != LuaValueType.String)
  280. {
  281. result = default!;
  282. return false;
  283. }
  284. var str = Unsafe.As<string>(referenceValue!);
  285. var span = str.AsSpan().Trim();
  286. if (span.Length == 0)
  287. {
  288. result = default!;
  289. return false;
  290. }
  291. var sign = 1;
  292. var first = span[0];
  293. if (first is '+')
  294. {
  295. sign = 1;
  296. span = span[1..];
  297. }
  298. else if (first is '-')
  299. {
  300. sign = -1;
  301. span = span[1..];
  302. }
  303. if (span.Length > 2 && span[0] is '0' && span[1] is 'x' or 'X')
  304. {
  305. // TODO: optimize
  306. try
  307. {
  308. var d = HexConverter.ToDouble(span) * sign;
  309. result = d;
  310. return true;
  311. }
  312. catch (FormatException)
  313. {
  314. result = default!;
  315. return false;
  316. }
  317. }
  318. else
  319. {
  320. return double.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out result);
  321. }
  322. }
  323. public T Read<T>()
  324. {
  325. if (!TryRead<T>(out var result)) throw new InvalidOperationException($"Cannot convert LuaValueType.{Type} to {typeof(T).FullName}.");
  326. return result;
  327. }
  328. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  329. internal T UnsafeRead<T>()
  330. {
  331. switch (Type)
  332. {
  333. case LuaValueType.Boolean:
  334. {
  335. var v = value != 0;
  336. return Unsafe.As<bool, T>(ref v);
  337. }
  338. case LuaValueType.Number:
  339. {
  340. var v = value;
  341. return Unsafe.As<double, T>(ref v);
  342. }
  343. case LuaValueType.String:
  344. case LuaValueType.Thread:
  345. case LuaValueType.Function:
  346. case LuaValueType.Table:
  347. case LuaValueType.LightUserData:
  348. case LuaValueType.UserData:
  349. {
  350. var v = referenceValue!;
  351. return Unsafe.As<object, T>(ref v);
  352. }
  353. }
  354. return default!;
  355. }
  356. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  357. public bool ToBoolean()
  358. {
  359. if (Type == LuaValueType.Boolean) return value != 0;
  360. if (Type is LuaValueType.Nil) return false;
  361. return true;
  362. }
  363. public static LuaValue FromUserData(ILuaUserData userData)
  364. {
  365. return new (userData);
  366. }
  367. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  368. internal LuaValue(object obj)
  369. {
  370. Type = LuaValueType.LightUserData;
  371. referenceValue = obj;
  372. }
  373. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  374. public LuaValue(bool value)
  375. {
  376. Type = LuaValueType.Boolean;
  377. this.value = value ? 1 : 0;
  378. }
  379. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  380. public LuaValue(double value)
  381. {
  382. Type = LuaValueType.Number;
  383. this.value = value;
  384. }
  385. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  386. public LuaValue(string value)
  387. {
  388. Type = LuaValueType.String;
  389. referenceValue = value;
  390. }
  391. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  392. public LuaValue(LuaFunction value)
  393. {
  394. Type = LuaValueType.Function;
  395. referenceValue = value;
  396. }
  397. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  398. public LuaValue(LuaTable value)
  399. {
  400. Type = LuaValueType.Table;
  401. referenceValue = value;
  402. }
  403. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  404. public LuaValue(LuaThread value)
  405. {
  406. Type = LuaValueType.Thread;
  407. referenceValue = value;
  408. }
  409. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  410. public LuaValue(ILuaUserData value)
  411. {
  412. Type = LuaValueType.UserData;
  413. referenceValue = value;
  414. }
  415. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  416. public static implicit operator LuaValue(bool value)
  417. {
  418. return new(value);
  419. }
  420. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  421. public static implicit operator LuaValue(double value)
  422. {
  423. return new(value);
  424. }
  425. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  426. public static implicit operator LuaValue(string value)
  427. {
  428. return new(value);
  429. }
  430. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  431. public static implicit operator LuaValue(LuaTable value)
  432. {
  433. return new(value);
  434. }
  435. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  436. public static implicit operator LuaValue(LuaFunction value)
  437. {
  438. return new(value);
  439. }
  440. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  441. public static implicit operator LuaValue(LuaThread value)
  442. {
  443. return new(value);
  444. }
  445. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  446. public override int GetHashCode()
  447. {
  448. return Type switch
  449. {
  450. LuaValueType.Nil => 0,
  451. LuaValueType.Boolean or LuaValueType.Number => value.GetHashCode(),
  452. LuaValueType.String => Unsafe.As<string>(referenceValue)!.GetHashCode(),
  453. _ => referenceValue!.GetHashCode()
  454. };
  455. }
  456. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  457. public bool Equals(LuaValue other)
  458. {
  459. if (other.Type != Type) return false;
  460. return Type switch
  461. {
  462. LuaValueType.Nil => true,
  463. LuaValueType.Boolean or LuaValueType.Number => other.value == value,
  464. LuaValueType.String => Unsafe.As<string>(other.referenceValue) == Unsafe.As<string>(referenceValue),
  465. _ => other.referenceValue!.Equals(referenceValue)
  466. };
  467. }
  468. public override bool Equals(object? obj)
  469. {
  470. return obj is LuaValue value1 && Equals(value1);
  471. }
  472. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  473. public static bool operator ==(LuaValue a, LuaValue b)
  474. {
  475. return a.Equals(b);
  476. }
  477. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  478. public static bool operator !=(LuaValue a, LuaValue b)
  479. {
  480. return !a.Equals(b);
  481. }
  482. public override string ToString()
  483. {
  484. return Type switch
  485. {
  486. LuaValueType.Nil => "nil",
  487. LuaValueType.Boolean => Read<bool>() ? "true" : "false",
  488. LuaValueType.String => Read<string>(),
  489. LuaValueType.Number => Read<double>().ToString(CultureInfo.InvariantCulture),
  490. LuaValueType.Function => $"function: {referenceValue!.GetHashCode()}",
  491. LuaValueType.Thread => $"thread: {referenceValue!.GetHashCode()}",
  492. LuaValueType.Table => $"table: {referenceValue!.GetHashCode()}",
  493. LuaValueType.LightUserData => $"userdata: {referenceValue!.GetHashCode()}",
  494. LuaValueType.UserData => $"userdata: {referenceValue!.GetHashCode()}",
  495. _ => "",
  496. };
  497. }
  498. public string TypeToString()
  499. {
  500. return Type switch
  501. {
  502. LuaValueType.Nil => "nil",
  503. LuaValueType.Boolean => "boolean",
  504. LuaValueType.String => "string",
  505. LuaValueType.Number => "number",
  506. LuaValueType.Function => "function",
  507. LuaValueType.Thread => "thread",
  508. LuaValueType.Table => "table",
  509. LuaValueType.LightUserData => "userdata",
  510. LuaValueType.UserData => "userdata",
  511. _ => "",
  512. };
  513. }
  514. public static string ToString(LuaValueType type)
  515. {
  516. return type switch
  517. {
  518. LuaValueType.Nil => "nil",
  519. LuaValueType.Boolean => "boolean",
  520. LuaValueType.String => "string",
  521. LuaValueType.Number => "number",
  522. LuaValueType.Function => "function",
  523. LuaValueType.Thread => "thread",
  524. LuaValueType.Table => "table",
  525. LuaValueType.LightUserData => "userdata",
  526. LuaValueType.UserData => "userdata",
  527. _ => "",
  528. };
  529. }
  530. public static bool TryGetLuaValueType(Type type, out LuaValueType result)
  531. {
  532. if (type == typeof(double) || type == typeof(float) || type == typeof(int) || type == typeof(long))
  533. {
  534. result = LuaValueType.Number;
  535. return true;
  536. }
  537. else if (type == typeof(bool))
  538. {
  539. result = LuaValueType.Boolean;
  540. return true;
  541. }
  542. else if (type == typeof(string))
  543. {
  544. result = LuaValueType.String;
  545. return true;
  546. }
  547. else if (type == typeof(LuaFunction) || type.IsSubclassOf(typeof(LuaFunction)))
  548. {
  549. result = LuaValueType.Function;
  550. return true;
  551. }
  552. else if (type == typeof(LuaTable))
  553. {
  554. result = LuaValueType.Table;
  555. return true;
  556. }
  557. else if (type == typeof(LuaThread))
  558. {
  559. result = LuaValueType.Thread;
  560. return true;
  561. }
  562. else if (type == typeof(ILuaUserData) || type.IsAssignableFrom(typeof(ILuaUserData)))
  563. {
  564. result = LuaValueType.UserData;
  565. return true;
  566. }
  567. result = default;
  568. return false;
  569. }
  570. internal ValueTask<int> CallToStringAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
  571. {
  572. if (this.TryGetMetamethod(context.State, Metamethods.ToString, out var metamethod))
  573. {
  574. var stack = context.Thread.Stack;
  575. stack.Push(metamethod);
  576. stack.Push(this);
  577. return LuaVirtualMachine.Call(context.Thread,stack.Count-2, stack.Count - 2, cancellationToken);
  578. }
  579. else
  580. {
  581. context.Thread.Stack.Push(ToString());
  582. return default;
  583. }
  584. }
  585. }