Dump.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. using Lua.Internal;
  2. using Lua.Runtime;
  3. using System.Buffers;
  4. using System.Buffers.Binary;
  5. using System.Diagnostics;
  6. using System.Diagnostics.CodeAnalysis;
  7. using System.Runtime.CompilerServices;
  8. using System.Runtime.InteropServices;
  9. using System.Text;
  10. namespace Lua.CodeAnalysis.Compilation;
  11. [SuppressMessage("ReSharper", "MemberCanBePrivate.Local")]
  12. [StructLayout(LayoutKind.Sequential, Pack = 1)]
  13. internal unsafe struct Header
  14. {
  15. public static ReadOnlySpan<byte> LuaSignature => "\eLua"u8;
  16. public static ReadOnlySpan<byte> LuaTail => [0x19, 0x93, 0x0d, 0x0a, 0x1a, 0x0a];
  17. public fixed byte Signature[4];
  18. public byte Version, Format, Endianness, IntSize;
  19. public byte PointerSize, InstructionSize;
  20. public byte NumberSize, IntegralNumber;
  21. public fixed byte Tail[6];
  22. public const int Size = 18;
  23. public Header(bool isLittleEndian)
  24. {
  25. fixed (byte* signature = Signature)
  26. {
  27. LuaSignature.CopyTo(new(signature, 4));
  28. }
  29. Version = Constants.VersionMajor << 4 | Constants.VersionMinor;
  30. Format = 0;
  31. Endianness = (byte)(isLittleEndian ? 1 : 0);
  32. IntSize = 4;
  33. PointerSize = (byte)sizeof(IntPtr);
  34. InstructionSize = 4;
  35. NumberSize = 8;
  36. IntegralNumber = 0;
  37. fixed (byte* tail = Tail)
  38. {
  39. LuaTail.CopyTo(new(tail, 6));
  40. }
  41. }
  42. public void Validate(ReadOnlySpan<char> name)
  43. {
  44. fixed (byte* signature = Signature)
  45. {
  46. if (!LuaSignature.SequenceEqual(new(signature, 4)))
  47. {
  48. throw new LuaUnDumpException($"{name.ToString()}: is not a precompiled chunk");
  49. }
  50. }
  51. var major = Version >> 4;
  52. var minor = Version & 0xF;
  53. if (major != Constants.VersionMajor || minor != Constants.VersionMinor)
  54. {
  55. throw new LuaUnDumpException($"{name.ToString()}: version mismatch in precompiled chunk {major}.{minor} != {Constants.VersionMajor}.{Constants.VersionMinor}");
  56. }
  57. if (IntSize != 4 || Format != 0 || IntegralNumber != 0 || PointerSize is not (4 or 8) || InstructionSize != 4 || NumberSize != 8)
  58. {
  59. goto ErrIncompatible;
  60. }
  61. fixed (byte* tail = Tail)
  62. {
  63. if (!LuaTail.SequenceEqual(new(tail, 6)))
  64. {
  65. goto ErrIncompatible;
  66. }
  67. }
  68. return;
  69. ErrIncompatible:
  70. throw new LuaUnDumpException($"{name.ToString()}: incompatible precompiled chunk");
  71. }
  72. }
  73. internal unsafe ref struct DumpState(IBufferWriter<byte> writer, bool reversedEndian)
  74. {
  75. public readonly IBufferWriter<byte> Writer = writer;
  76. Span<byte> unWritten;
  77. void Write(ReadOnlySpan<byte> span)
  78. {
  79. var toWrite = span;
  80. var remaining = unWritten.Length;
  81. if (span.Length > remaining)
  82. {
  83. span[..remaining].CopyTo(unWritten);
  84. Writer.Advance(remaining);
  85. toWrite = span[remaining..];
  86. unWritten = Writer.GetSpan(toWrite.Length);
  87. }
  88. toWrite.CopyTo(unWritten);
  89. Writer.Advance(toWrite.Length);
  90. unWritten = unWritten[toWrite.Length..];
  91. }
  92. public bool IsReversedEndian => reversedEndian;
  93. void DumpHeader()
  94. {
  95. var header = new Header(BitConverter.IsLittleEndian ^ IsReversedEndian);
  96. Write(new(&header, Header.Size));
  97. }
  98. public void Dump(Prototype prototype)
  99. {
  100. if (unWritten.Length == 0)
  101. {
  102. unWritten = Writer.GetSpan(Header.Size + 32);
  103. }
  104. DumpHeader();
  105. DumpFunction(prototype);
  106. }
  107. void DumpFunction(Prototype prototype)
  108. {
  109. WriteInt(prototype.LineDefined); //4
  110. WriteInt(prototype.LastLineDefined); //4
  111. WriteByte((byte)prototype.ParameterCount); //1
  112. WriteByte((byte)prototype.MaxStackSize); //1
  113. WriteByte((byte)(prototype.HasVariableArguments ? 1 : 0)); //1
  114. WriteIntSpanWithLength(MemoryMarshal.Cast<Instruction, int>(prototype.Code)); //4
  115. WriteConstants(prototype.Constants); //4
  116. WritePrototypes(prototype.ChildPrototypes); //4
  117. WriteUpValues(prototype.UpValues); //4
  118. //Debug
  119. WriteString(prototype.ChunkName);
  120. WriteIntSpanWithLength(prototype.LineInfo);
  121. WriteLocalVariables(prototype.LocalVariables);
  122. WriteInt(prototype.UpValues.Length);
  123. foreach (var desc in prototype.UpValues)
  124. {
  125. WriteString(desc.Name);
  126. }
  127. }
  128. void WriteInt(int v)
  129. {
  130. if (reversedEndian)
  131. {
  132. v = BinaryPrimitives.ReverseEndianness(v);
  133. }
  134. Write(new(&v, sizeof(int)));
  135. }
  136. void WriteLong(long v)
  137. {
  138. if (reversedEndian)
  139. {
  140. v = BinaryPrimitives.ReverseEndianness(v);
  141. }
  142. Write(new(&v, sizeof(long)));
  143. }
  144. void WriteByte(byte v)
  145. {
  146. Write(new(&v, sizeof(byte)));
  147. }
  148. void WriteDouble(double v)
  149. {
  150. long l = BitConverter.DoubleToInt64Bits(v);
  151. WriteLong(l);
  152. }
  153. void WriteIntSpanWithLength(ReadOnlySpan<int> v)
  154. {
  155. WriteInt(v.Length);
  156. if (IsReversedEndian)
  157. {
  158. foreach (var i in v)
  159. {
  160. var reversed = BinaryPrimitives.ReverseEndianness(i);
  161. Write(new(&reversed, 4));
  162. }
  163. }
  164. else
  165. {
  166. Write(MemoryMarshal.Cast<int, byte>(v));
  167. }
  168. }
  169. void WriteBool(bool v)
  170. {
  171. WriteByte(v ? (byte)1 : (byte)0);
  172. }
  173. void WriteString(string v)
  174. {
  175. var bytes = Encoding.UTF8.GetBytes(v);
  176. var len = bytes.Length;
  177. if (bytes.Length != 0) len++;
  178. if (sizeof(IntPtr) == 8) WriteLong(len);
  179. else WriteInt(len);
  180. if (len != 0)
  181. {
  182. Write(bytes);
  183. WriteByte(0);
  184. }
  185. }
  186. void WriteConstants(ReadOnlySpan<LuaValue> constants)
  187. {
  188. WriteInt(constants.Length);
  189. foreach (var c in constants)
  190. {
  191. WriteByte((byte)c.Type);
  192. switch (c.Type)
  193. {
  194. case LuaValueType.Nil: break;
  195. case LuaValueType.Boolean:
  196. WriteBool(c.UnsafeReadDouble() != 0);
  197. break;
  198. case LuaValueType.Number:
  199. WriteDouble(c.UnsafeReadDouble());
  200. break;
  201. case LuaValueType.String:
  202. WriteString(c.UnsafeRead<string>());
  203. break;
  204. }
  205. }
  206. }
  207. void WritePrototypes(ReadOnlySpan<Prototype> prototypes)
  208. {
  209. WriteInt(prototypes.Length);
  210. foreach (var p in prototypes)
  211. {
  212. DumpFunction(p);
  213. }
  214. }
  215. void WriteLocalVariables(ReadOnlySpan<LocalVariable> localVariables)
  216. {
  217. WriteInt(localVariables.Length);
  218. foreach (var v in localVariables)
  219. {
  220. WriteString(v.Name);
  221. WriteInt(v.StartPc);
  222. WriteInt(v.EndPc);
  223. }
  224. }
  225. void WriteUpValues(ReadOnlySpan<UpValueDesc> upValues)
  226. {
  227. WriteInt(upValues.Length);
  228. foreach (var u in upValues)
  229. {
  230. WriteBool(u.IsLocal);
  231. WriteByte((byte)u.Index);
  232. }
  233. }
  234. }
  235. internal unsafe ref struct UnDumpState(ReadOnlySpan<byte> span, ReadOnlySpan<char> name)
  236. {
  237. public ReadOnlySpan<byte> Unread = span;
  238. bool otherEndian;
  239. int pointerSize;
  240. readonly ReadOnlySpan<char> name = name;
  241. void Throw(string why) => throw new LuaUnDumpException($"{name.ToString()}: {why} precompiled chunk");
  242. void ThrowTooShort() => Throw("truncate");
  243. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  244. internal void Read(Span<byte> dst)
  245. {
  246. if (Unread.Length < dst.Length) ThrowTooShort();
  247. Unread[..dst.Length].CopyTo(dst);
  248. Unread = Unread[dst.Length..];
  249. }
  250. byte ReadByte()
  251. {
  252. if (0 < Unread.Length)
  253. {
  254. var b = Unread[0];
  255. Unread = Unread[1..];
  256. return b;
  257. }
  258. ThrowTooShort();
  259. return 0;
  260. }
  261. bool ReadBool()
  262. {
  263. if (0 < Unread.Length)
  264. {
  265. var b = Unread[0];
  266. Unread = Unread[1..];
  267. return b != 0;
  268. }
  269. ThrowTooShort();
  270. return false;
  271. }
  272. int ReadInt()
  273. {
  274. var i = 0;
  275. var span = new Span<byte>(&i, sizeof(int));
  276. Read(span);
  277. if (otherEndian) i = BinaryPrimitives.ReverseEndianness(i);
  278. return i;
  279. }
  280. long ReadLong()
  281. {
  282. long i = 0;
  283. var span = new Span<byte>(&i, sizeof(long));
  284. Read(span);
  285. if (otherEndian) i = BinaryPrimitives.ReverseEndianness(i);
  286. return i;
  287. }
  288. double ReadDouble()
  289. {
  290. long i = ReadLong();
  291. return *(double*)(&i);
  292. }
  293. public Prototype UnDump()
  294. {
  295. Header h = default;
  296. var span = new Span<byte>(&h, sizeof(Header));
  297. Read(span);
  298. h.Validate(name);
  299. otherEndian = BitConverter.IsLittleEndian ^ (h.Endianness == 1);
  300. pointerSize = h.PointerSize;
  301. return UnDumpFunction();
  302. }
  303. Prototype UnDumpFunction()
  304. {
  305. var lineDefined = ReadInt(); //4
  306. var lastLineDefined = ReadInt(); //4
  307. var parameterCount = ReadByte(); //1
  308. var maxStackSize = ReadByte(); //1
  309. var isVarArg = ReadByte() == 1; //1
  310. var codeLength = ReadInt();
  311. var code = new Instruction[codeLength];
  312. ReadInToIntSpan(MemoryMarshal.Cast<Instruction, int>(code));
  313. var constants = ReadConstants();
  314. var prototypes = ReadPrototypes();
  315. var upValues = ReadUpValues();
  316. //Debug
  317. var source = ReadString();
  318. var lineInfoLength = ReadInt();
  319. var lineInfo = new int[lineInfoLength];
  320. ReadInToIntSpan(lineInfo.AsSpan());
  321. var localVariables = ReadLocalVariables();
  322. foreach (ref var desc in upValues.AsSpan())
  323. {
  324. var name = ReadString();
  325. desc.Name = name;
  326. }
  327. return new(source, lineDefined, lastLineDefined, parameterCount, maxStackSize, isVarArg, constants, code, prototypes, lineInfo, localVariables, upValues);
  328. }
  329. void ReadInToIntSpan(Span<int> toWrite)
  330. {
  331. for (int i = 0; i < toWrite.Length; i++)
  332. {
  333. toWrite[i] = ReadInt();
  334. }
  335. }
  336. string ReadString()
  337. {
  338. var len = pointerSize == 4 ? ReadInt() : (int)ReadLong();
  339. if (len == 0) return "";
  340. len--;
  341. var arrayPooled = ArrayPool<byte>.Shared.Rent(len);
  342. try
  343. {
  344. var span = arrayPooled.AsSpan(0, len);
  345. Read(span);
  346. var l = ReadByte();
  347. Debug.Assert(l == 0);
  348. var str = Encoding.UTF8.GetString(span);
  349. return str;
  350. }
  351. finally
  352. {
  353. ArrayPool<byte>.Shared.Return(arrayPooled);
  354. }
  355. }
  356. LuaValue[] ReadConstants()
  357. {
  358. var count = ReadInt();
  359. var constants = new LuaValue[count];
  360. for (int i = 0; i < count; i++)
  361. {
  362. var type = (LuaValueType)ReadByte();
  363. switch (type)
  364. {
  365. case LuaValueType.Nil: break;
  366. case LuaValueType.Boolean:
  367. constants[i] = (ReadByte() == 1);
  368. break;
  369. case LuaValueType.Number:
  370. constants[i] = (ReadDouble());
  371. break;
  372. case LuaValueType.String:
  373. constants[i] = (ReadString());
  374. break;
  375. }
  376. }
  377. return constants;
  378. }
  379. Prototype[] ReadPrototypes()
  380. {
  381. var count = ReadInt();
  382. var prototypes = count != 0 ? new Prototype[count] : [];
  383. for (int i = 0; i < count; i++)
  384. {
  385. prototypes[i] = UnDumpFunction();
  386. }
  387. return prototypes;
  388. }
  389. LocalVariable[] ReadLocalVariables()
  390. {
  391. var count = ReadInt();
  392. var localVariables = new LocalVariable[count];
  393. for (int i = 0; i < count; i++)
  394. {
  395. var name = ReadString();
  396. var startPc = ReadInt();
  397. var endPc = ReadInt();
  398. localVariables[i] = new() { Name = name, StartPc = startPc, EndPc = endPc };
  399. }
  400. return localVariables;
  401. }
  402. UpValueDesc[] ReadUpValues()
  403. {
  404. var count = ReadInt();
  405. Debug.Assert(count < 100, $" too many upvalues :{count}");
  406. var upValues = new UpValueDesc[count];
  407. for (int i = 0; i < count; i++)
  408. {
  409. var isLocal = ReadBool();
  410. var index = ReadByte();
  411. upValues[i] = new() { IsLocal = isLocal, Index = index };
  412. }
  413. return upValues;
  414. }
  415. }