LuaValue.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  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 FromObject(object obj)
  364. {
  365. return obj switch
  366. {
  367. null => Nil,
  368. LuaValue luaValue => luaValue,
  369. bool boolValue => boolValue,
  370. double doubleValue => doubleValue,
  371. string stringValue => stringValue,
  372. LuaFunction luaFunction => luaFunction,
  373. LuaTable luaTable => luaTable,
  374. LuaThread luaThread => luaThread,
  375. ILuaUserData userData => FromUserData(userData),
  376. int intValue => intValue,
  377. long longValue => longValue,
  378. float floatValue => floatValue,
  379. _ => new LuaValue(obj)
  380. };
  381. }
  382. public static LuaValue FromUserData(ILuaUserData userData)
  383. {
  384. return new(userData);
  385. }
  386. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  387. LuaValue(object obj)
  388. {
  389. Type = LuaValueType.LightUserData;
  390. referenceValue = obj;
  391. }
  392. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  393. public LuaValue(bool value)
  394. {
  395. Type = LuaValueType.Boolean;
  396. this.value = value ? 1 : 0;
  397. }
  398. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  399. public LuaValue(double value)
  400. {
  401. Type = LuaValueType.Number;
  402. this.value = value;
  403. }
  404. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  405. public LuaValue(string value)
  406. {
  407. Type = LuaValueType.String;
  408. referenceValue = value;
  409. }
  410. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  411. public LuaValue(LuaFunction value)
  412. {
  413. Type = LuaValueType.Function;
  414. referenceValue = value;
  415. }
  416. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  417. public LuaValue(LuaTable value)
  418. {
  419. Type = LuaValueType.Table;
  420. referenceValue = value;
  421. }
  422. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  423. public LuaValue(LuaThread value)
  424. {
  425. Type = LuaValueType.Thread;
  426. referenceValue = value;
  427. }
  428. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  429. public LuaValue(ILuaUserData value)
  430. {
  431. Type = LuaValueType.UserData;
  432. referenceValue = value;
  433. }
  434. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  435. public static implicit operator LuaValue(bool value)
  436. {
  437. return new(value);
  438. }
  439. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  440. public static implicit operator LuaValue(double value)
  441. {
  442. return new(value);
  443. }
  444. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  445. public static implicit operator LuaValue(string value)
  446. {
  447. return new(value);
  448. }
  449. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  450. public static implicit operator LuaValue(LuaTable value)
  451. {
  452. return new(value);
  453. }
  454. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  455. public static implicit operator LuaValue(LuaFunction value)
  456. {
  457. return new(value);
  458. }
  459. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  460. public static implicit operator LuaValue(LuaThread value)
  461. {
  462. return new(value);
  463. }
  464. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  465. public override int GetHashCode()
  466. {
  467. return Type switch
  468. {
  469. LuaValueType.Nil => 0,
  470. LuaValueType.Boolean or LuaValueType.Number => value.GetHashCode(),
  471. LuaValueType.String => Unsafe.As<string>(referenceValue)!.GetHashCode(),
  472. _ => referenceValue!.GetHashCode()
  473. };
  474. }
  475. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  476. public bool Equals(LuaValue other)
  477. {
  478. if (other.Type != Type) return false;
  479. return Type switch
  480. {
  481. LuaValueType.Nil => true,
  482. LuaValueType.Boolean or LuaValueType.Number => other.value == value,
  483. LuaValueType.String => Unsafe.As<string>(other.referenceValue) == Unsafe.As<string>(referenceValue),
  484. _ => other.referenceValue!.Equals(referenceValue)
  485. };
  486. }
  487. public override bool Equals(object? obj)
  488. {
  489. return obj is LuaValue value1 && Equals(value1);
  490. }
  491. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  492. public static bool operator ==(LuaValue a, LuaValue b)
  493. {
  494. return a.Equals(b);
  495. }
  496. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  497. public static bool operator !=(LuaValue a, LuaValue b)
  498. {
  499. return !a.Equals(b);
  500. }
  501. public override string ToString()
  502. {
  503. return Type switch
  504. {
  505. LuaValueType.Nil => "nil",
  506. LuaValueType.Boolean => Read<bool>() ? "true" : "false",
  507. LuaValueType.String => Read<string>(),
  508. LuaValueType.Number => Read<double>().ToString(CultureInfo.InvariantCulture),
  509. LuaValueType.Function => $"function: {referenceValue!.GetHashCode()}",
  510. LuaValueType.Thread => $"thread: {referenceValue!.GetHashCode()}",
  511. LuaValueType.Table => $"table: {referenceValue!.GetHashCode()}",
  512. LuaValueType.LightUserData => $"userdata: {referenceValue!.GetHashCode()}",
  513. LuaValueType.UserData => $"userdata: {referenceValue!.GetHashCode()}",
  514. _ => "",
  515. };
  516. }
  517. public string TypeToString()
  518. {
  519. return Type switch
  520. {
  521. LuaValueType.Nil => "nil",
  522. LuaValueType.Boolean => "boolean",
  523. LuaValueType.String => "string",
  524. LuaValueType.Number => "number",
  525. LuaValueType.Function => "function",
  526. LuaValueType.Thread => "thread",
  527. LuaValueType.Table => "table",
  528. LuaValueType.LightUserData => "userdata",
  529. LuaValueType.UserData => "userdata",
  530. _ => "",
  531. };
  532. }
  533. public static string ToString(LuaValueType type)
  534. {
  535. return type switch
  536. {
  537. LuaValueType.Nil => "nil",
  538. LuaValueType.Boolean => "boolean",
  539. LuaValueType.String => "string",
  540. LuaValueType.Number => "number",
  541. LuaValueType.Function => "function",
  542. LuaValueType.Thread => "thread",
  543. LuaValueType.Table => "table",
  544. LuaValueType.LightUserData => "userdata",
  545. LuaValueType.UserData => "userdata",
  546. _ => "",
  547. };
  548. }
  549. public static bool TryGetLuaValueType(Type type, out LuaValueType result)
  550. {
  551. if (type == typeof(double) || type == typeof(float) || type == typeof(int) || type == typeof(long))
  552. {
  553. result = LuaValueType.Number;
  554. return true;
  555. }
  556. else if (type == typeof(bool))
  557. {
  558. result = LuaValueType.Boolean;
  559. return true;
  560. }
  561. else if (type == typeof(string))
  562. {
  563. result = LuaValueType.String;
  564. return true;
  565. }
  566. else if (type == typeof(LuaFunction) || type.IsSubclassOf(typeof(LuaFunction)))
  567. {
  568. result = LuaValueType.Function;
  569. return true;
  570. }
  571. else if (type == typeof(LuaTable))
  572. {
  573. result = LuaValueType.Table;
  574. return true;
  575. }
  576. else if (type == typeof(LuaThread))
  577. {
  578. result = LuaValueType.Thread;
  579. return true;
  580. }
  581. else if (type == typeof(ILuaUserData) || type.IsAssignableFrom(typeof(ILuaUserData)))
  582. {
  583. result = LuaValueType.UserData;
  584. return true;
  585. }
  586. result = default;
  587. return false;
  588. }
  589. internal ValueTask<int> CallToStringAsync(LuaFunctionExecutionContext context, CancellationToken cancellationToken)
  590. {
  591. if (this.TryGetMetamethod(context.State, Metamethods.ToString, out var metamethod))
  592. {
  593. var stack = context.Thread.Stack;
  594. stack.Push(metamethod);
  595. stack.Push(this);
  596. return LuaVirtualMachine.Call(context.Thread, stack.Count - 2, stack.Count - 2, cancellationToken);
  597. }
  598. else
  599. {
  600. context.Thread.Stack.Push(ToString());
  601. return default;
  602. }
  603. }
  604. }