Exceptions.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. using Lua.CodeAnalysis;
  2. using Lua.CodeAnalysis.Syntax;
  3. using Lua.Internal;
  4. using Lua.Runtime;
  5. using System.Runtime.CompilerServices;
  6. namespace Lua;
  7. public class LuaException : Exception
  8. {
  9. protected LuaException(Exception innerException) : base(innerException.Message, innerException)
  10. {
  11. }
  12. protected LuaException(string message, Exception innerException) : base(message, innerException)
  13. {
  14. }
  15. public LuaException(string message) : base(message)
  16. {
  17. }
  18. protected LuaException()
  19. {
  20. }
  21. }
  22. public class LuaParseException(string? chunkName, SourcePosition position, string message) : LuaException(message)
  23. {
  24. public string? ChunkName { get; } = chunkName;
  25. public SourcePosition Position { get; } = position;
  26. public static void UnexpectedToken(string? chunkName, SourcePosition position, SyntaxToken token)
  27. {
  28. throw new LuaParseException(chunkName, position, $"unexpected symbol <{token.Type}> near '{token.Text}'");
  29. }
  30. public static void ExpectedToken(string? chunkName, SourcePosition position, SyntaxTokenType token)
  31. {
  32. throw new LuaParseException(chunkName, position, $"'{token}' expected");
  33. }
  34. public static void UnfinishedLongComment(string? chunkName, SourcePosition position)
  35. {
  36. throw new LuaParseException(chunkName, position, $"unfinished long comment (starting at line {position.Line})");
  37. }
  38. public static void SyntaxError(string? chunkName, SourcePosition position, SyntaxToken? token)
  39. {
  40. throw new LuaParseException(chunkName, position, $"syntax error {(token == null ? "" : $"near '{token.Value.Text}'")}");
  41. }
  42. public static void NoVisibleLabel(string label, string? chunkName, SourcePosition position)
  43. {
  44. throw new LuaParseException(chunkName, position, $"no visible label '{label}' for <goto>");
  45. }
  46. public static void BreakNotInsideALoop(string? chunkName, SourcePosition position)
  47. {
  48. throw new LuaParseException(chunkName, position, "<break> not inside a loop");
  49. }
  50. public override string Message => $"{ChunkName}:{Position.Line}: {base.Message}";
  51. }
  52. public class LuaCompileException(string chunkName, SourcePosition position, int offset, string message, string? nearToken) : LuaException(GetMessageWithNearToken(message, nearToken))
  53. {
  54. public string ChunkName { get; } = chunkName;
  55. public int OffSet { get; } = offset;
  56. public SourcePosition Position => position;
  57. public string MainMessage => message;
  58. public string? NearToken => nearToken;
  59. public string MessageWithNearToken => base.Message;
  60. public override string Message => $"{ChunkName}:{Position.Line}: {base.Message}";
  61. static string GetMessageWithNearToken(string message, string? nearToken)
  62. {
  63. if (string.IsNullOrEmpty(nearToken))
  64. {
  65. return message;
  66. }
  67. return $"{message} near {nearToken}";
  68. }
  69. }
  70. public class LuaUnDumpException(string message) : LuaException(message);
  71. public class LuaRuntimeException : LuaException
  72. {
  73. public LuaRuntimeException(LuaThread? thread, Exception innerException) : base(innerException)
  74. {
  75. Thread = thread;
  76. }
  77. public LuaRuntimeException(LuaThread? thread, LuaValue errorObject)
  78. {
  79. if (thread != null)
  80. {
  81. thread.CurrentException?.Build();
  82. thread.ExceptionTrace.Clear();
  83. thread.CurrentException = this;
  84. }
  85. Thread = thread;
  86. ErrorObject = errorObject;
  87. }
  88. Traceback? luaTraceback;
  89. public Traceback? LuaTraceback
  90. {
  91. get
  92. {
  93. if (luaTraceback == null)
  94. {
  95. Build();
  96. }
  97. return luaTraceback;
  98. }
  99. }
  100. internal LuaThread? Thread { get; private set; } = default!;
  101. public LuaValue ErrorObject { get; }
  102. public static void AttemptInvalidOperation(LuaThread? thread, string op, LuaValue a, LuaValue b)
  103. {
  104. throw new LuaRuntimeException(thread, $"attempt to {op} a '{a.Type}' with a '{b.Type}'");
  105. }
  106. public static void AttemptInvalidOperation(LuaThread? thread, string op, LuaValue a)
  107. {
  108. throw new LuaRuntimeException(thread, $"attempt to {op} a '{a.Type}' value");
  109. }
  110. public static void BadArgument(LuaThread? thread, int argumentId, string functionName)
  111. {
  112. throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{functionName}' (value expected)");
  113. }
  114. public static void BadArgument(LuaThread? thread, int argumentId, string functionName, LuaValueType[] expected)
  115. {
  116. throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{functionName}' ({string.Join(" or ", expected)} expected)");
  117. }
  118. public static void BadArgument(LuaThread? thread, int argumentId, string functionName, string expected, string actual)
  119. {
  120. throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{functionName}' ({expected} expected, got {actual})");
  121. }
  122. public static void BadArgument(LuaThread? thread, int argumentId, string functionName, string message)
  123. {
  124. throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{functionName}' ({message})");
  125. }
  126. public static void BadArgumentNumberIsNotInteger(LuaThread? thread, int argumentId, string functionName)
  127. {
  128. throw new LuaRuntimeException(thread, $"bad argument #{argumentId} to '{functionName}' (number has no integer representation)");
  129. }
  130. public static void ThrowBadArgumentIfNumberIsNotInteger(LuaThread? thread, string functionName, int argumentId, double value)
  131. {
  132. if (!MathEx.IsInteger(value))
  133. {
  134. BadArgumentNumberIsNotInteger(thread, argumentId, functionName);
  135. }
  136. }
  137. static string CreateMessage(Traceback traceback, LuaValue errorObject)
  138. {
  139. var pooledList = new PooledList<char>(64);
  140. pooledList.Clear();
  141. try
  142. {
  143. pooledList.AddRange("Lua-CSharp: ");
  144. traceback.WriteLastLuaTrace(ref pooledList);
  145. pooledList.AddRange(": ");
  146. pooledList.AddRange($"{errorObject}");
  147. return pooledList.AsSpan().ToString();
  148. }
  149. finally
  150. {
  151. pooledList.Dispose();
  152. }
  153. }
  154. [MethodImpl(MethodImplOptions.NoInlining)]
  155. internal Traceback? Build()
  156. {
  157. if (luaTraceback != null) return luaTraceback;
  158. if (Thread != null)
  159. {
  160. var callStack = Thread.ExceptionTrace.AsSpan();
  161. if (callStack.IsEmpty) return null;
  162. luaTraceback = new Traceback(Thread.State,callStack);
  163. Thread.ExceptionTrace.Clear();
  164. Thread = null;
  165. }
  166. return luaTraceback;
  167. }
  168. internal void Forget()
  169. {
  170. Thread?.ExceptionTrace.Clear();
  171. Thread = null;
  172. }
  173. public override string Message
  174. {
  175. get
  176. {
  177. if (InnerException != null) return InnerException.Message;
  178. if (LuaTraceback == null)
  179. {
  180. return ErrorObject.ToString();
  181. }
  182. return CreateMessage(LuaTraceback, ErrorObject);
  183. }
  184. }
  185. public override string ToString()
  186. {
  187. if (LuaTraceback == null)
  188. {
  189. return base.ToString();
  190. }
  191. var pooledList = new PooledList<char>(64);
  192. pooledList.Clear();
  193. try
  194. {
  195. pooledList.AddRange(Message);
  196. pooledList.Add('\n');
  197. pooledList.AddRange(LuaTraceback.ToString());
  198. pooledList.Add('\n');
  199. pooledList.AddRange(StackTrace);
  200. return pooledList.AsSpan().ToString();
  201. }
  202. finally
  203. {
  204. pooledList.Dispose();
  205. }
  206. }
  207. }
  208. public class LuaAssertionException(LuaThread? traceback, string message) : LuaRuntimeException(traceback, message);
  209. public class LuaModuleNotFoundException(string moduleName) : LuaException($"module '{moduleName}' not found");