root/trunk/lua/error.d

Revision 314, 15.2 kB (checked in by xammy, 3 weeks ago)

Bugfixes for LuaLib (package library, thread destruction)

Line 
1 /*******************************************************************************
2
3     copyright:      Copyright (c) 2008 Matthias Walter. All rights reserved
4
5     authors:        Matthias Walter, Andreas Hollandt
6
7 *******************************************************************************/
8
9 module lua.error;
10
11 private import lua.lua;
12 private import lua.lauxlib;
13 private import lua.data;
14 private import lua.state;
15 private import lua.utils : strlenz, int2string;
16
17 /*******************************************************************************
18
19     LuaActivationRecords contain information about running functions. This
20     is useful for debugging purposes.
21
22 *******************************************************************************/
23
24 class LuaActivationRecord
25 {
26     /// Code type of activation records
27
28     public enum CodeType : int
29     {
30         LUA,
31         C,
32         MAIN,
33         TAIL
34     }
35
36     /// Name type of activation records
37
38     enum NameType : int
39     {
40         GLOBAL,
41         LOCAL,
42         METHOD,
43         FIELD,
44         UPVALUE,
45         UNKNOWN
46     }
47
48     /***************************************************************************
49
50         Converts the given string into a Code type.
51
52         Params:
53         str = String representation of the Code type
54
55     ***************************************************************************/
56
57     private static CodeType parseCodeType (char[] str)
58     {
59         switch (str)
60         {
61             case "Lua": return CodeType.LUA;
62             case "C": return CodeType.C;
63             case "main": return CodeType.MAIN;
64             case "tail": return CodeType.TAIL;
65             default: throw new LuaFatalException (str ~ " is an invalid LuaCodeType.");
66         }
67     }
68
69     /***************************************************************************
70
71         Converts the given Code type into its string representation.
72
73         Params:
74         code_type = Code type
75
76     ***************************************************************************/
77
78     public static char[] toString (CodeType code_type)
79     {
80         switch (code_type)
81         {
82             case CodeType.LUA: return "Lua";
83             case CodeType.C: return "C";
84             case CodeType.MAIN: return "main";
85             case CodeType.TAIL: return "tail call";
86         }
87     }
88
89     /***************************************************************************
90
91         Converts the given string into a Name type.
92
93         Params:
94         str = String representation of the Name type
95
96     ***************************************************************************/
97
98     private static NameType parseNameType (char[] str)
99     {
100         switch (str)
101         {
102             case "global": return NameType.GLOBAL;
103             case "local": return NameType.LOCAL;
104             case "method": return NameType.METHOD;
105             case "field": return NameType.FIELD;
106             case "upvalue": return NameType.UPVALUE;
107             default: return NameType.UNKNOWN;
108         }
109     }
110
111     /***************************************************************************
112
113         Converts the given Name type into its string representation.
114
115         Params:
116         name_type = Name type
117
118     ***************************************************************************/
119
120     public static char[] toString (NameType name_type)
121     {
122         switch (name_type)
123         {
124             case NameType.GLOBAL: return "global";
125             case NameType.LOCAL: return "local";
126             case NameType.METHOD: return "method";
127             case NameType.FIELD: return "field";
128             case NameType.UPVALUE: return "upvalue";
129             default: return "";
130         }
131     }
132
133
134     /// Code type
135     private CodeType code_type_;
136     /// Name type
137     private NameType name_type_;
138     /// Name
139     private char[] name_;
140     /// Whether this function was loaded from a file.
141     private bool is_from_file_;
142     /// Source string, containing the source or filename.
143     private char[] source_;
144     /// Start of the definition of this function in the source.
145     private uint definition_start_;
146     /// End of the definition of this function in the source.
147     private uint definition_end_;
148     /// Current line in the source.
149     private int current_line_;
150     /// Number of upvalues of this function.
151     private uint upvalues_;
152
153     /***************************************************************************
154
155         Constructs an activation record which describes one state of a
156         running function.
157
158         Params:
159         L = Lua state (C)
160         record = Lua debug record (C)
161
162     ***************************************************************************/
163
164     package this (lua_State* L, lua_Debug* record)
165     {
166         // 'n'
167         if (lua_getinfo (L, "n", record) == 0)
168             throw new LuaFatalException ("Unable to get 'name' and 'namewhat' information from activation record.");
169         this.name_ = record.name[0 .. strlenz (record.name)].dup;
170         this.name_type_ = parseNameType (record.namewhat[0 .. strlenz (record.namewhat)]);
171
172         // 'S'
173         if (lua_getinfo (L, "S", record) == 0)
174             throw new LuaFatalException ("Unable to get 'source', 'short_src', 'linedefined', 'lastlinedefined' and 'whatname' information from activation record.");
175
176         if (record.source[0] == '@')
177         {
178             this.is_from_file_ = true;
179             this.source_ = record.source[1 .. strlenz (record.source)].dup;
180         }
181         else
182         {
183             this.is_from_file_ = false;
184             this.source_ = record.source[0 .. strlenz (record.source)].dup;
185         }
186
187         this.definition_start_ = record.linedefined;
188         this.definition_end_ = record.lastlinedefined;
189         this.code_type_ = parseCodeType (record.what[0 .. strlenz (record.what)]);
190
191         if (this.code_type_ == CodeType.C)
192             this.source_ = null;
193
194         // 'l'
195         if (lua_getinfo (L, "l", record) == 0)
196             throw new LuaFatalException ("Unable to get 'currentline' information from activation record.");
197
198         this.current_line_ = record.currentline;
199
200         // 'u'
201         if (lua_getinfo (L, "u", record) == 0)
202             throw new LuaFatalException ("Unable to get 'nups' information from activation record.");
203
204         this.upvalues_ = record.nups;
205     }
206
207     /// Returns the Code type of the function.
208
209     public CodeType codeType ()
210     {
211         return this.code_type_;
212     }
213
214     /// Returns the Name type of the function.
215
216     public NameType nameType ()
217     {
218         return this.name_type_;
219     }
220
221     /// Returns the name of the function.
222
223     public char[] name ()
224     {
225         return this.name_;
226     }
227
228     /// Returns true, if and only if the function was defined in a file.
229
230     public bool isFromFile ()
231     {
232         return this.is_from_file_;
233     }
234
235     /// Returns the source for the function or the filename, where it was defined.
236
237     public char[] source ()
238     {
239         return this.source_;
240     }
241
242     /// Returns the start of the defintion of the function.
243
244     public uint definitionStart ()
245     {
246         return this.definition_start_;
247     }
248
249     /// Returns the end of the defintion of the function.
250
251     public uint definitionEnd ()
252     {
253         return this.definition_end_;
254     }
255
256     /// Returns the the current line in the function.
257
258     public int currentLine ()
259     {
260         return this.current_line_;
261     }
262
263     /// Returns the number of upvalues of the function.
264
265     public uint upvalues ()
266     {
267         return this.upvalues_;
268     }
269
270     /// Returns of compact string representation of the information about the function.
271
272     public char[] toString ()
273     {
274         char[] result = toString (codeType) ~ " function";
275
276         if (this.name_ !is null)
277             result ~= " " ~ name ~ ((this.nameType == NameType.UNKNOWN) ? "" : (" (" ~ toString (nameType) ~ ")"));
278
279         if (codeType != CodeType.C)
280         {
281             if (isFromFile)
282                 result ~= " from file " ~ source;
283             else
284                 result ~= " from string \"" ~ source ~ "\"";
285             result ~= ":" ~ int2string (currentLine);
286         }
287
288         return result;
289     }
290 }
291
292 /*******************************************************************************
293
294     LuaCallTrace is a collection of Activation Records which together
295     describe the current call stack. This is useful for debugging purposes
296     to keep track of the function calls.
297
298 *******************************************************************************/
299
300 class LuaCallTrace
301 {
302     /// List of Activation Records
303     private LuaActivationRecord[] records_;
304
305     /// Constructs a Call Trace from a given state, gathering all information.
306
307     public this (LuaState state)
308     {
309         LuaActivationRecord record;
310         uint level = 0;
311
312         while ((record = state.getActivationRecord (level)) !is null)
313         {
314             this.records_ ~= record;
315             level++;
316         }
317     }
318
319     /// Returns the specified Activation Record from the Call Trace.
320
321     public LuaActivationRecord opIndex (size_t index)
322     {
323         return this.records_[index];
324     }
325
326     /// Prints the Call Trace in a user-friendly way.
327
328     public char[] toString ()
329     {
330
331         char[] result = "* Call Trace (last function call)\n";
332         foreach (level, record; this.records_)
333         {
334             result ~= "*  " ~ int2string (records_.length - level) ~ ": " ~ record.toString () ~ "\n";
335         }
336         result ~= "* End of Call Trace (first function call)";
337         return result;
338     }
339 }
340
341 /*******************************************************************************
342
343     LuaStackTrace is a collection of all objects which are currently on the
344     stack. This is useful for debugging purposes.
345
346 *******************************************************************************/
347
348 class LuaStackTrace
349 {
350     /// List of objects
351     private LuaObject[] objects_;
352
353     /// Constructs a Stack Trace from a given state, gathering all information.
354
355     public this (LuaState state)
356     {
357         this.objects_ = new LuaObject [state.top];
358         foreach (i, ref obj; this.objects_)
359         {
360             obj = state.getObject (i+1);
361         }
362     }
363
364     /// Returns the specified object from the Stack Trace.
365
366     public LuaObject opIndex (size_t index)
367     {
368         return this.objects_[index];
369     }
370
371     /// Prints the Stack Trace in a user-friendly way.
372
373     public char[] toString ()
374     {
375         char[] result = "# Stack Trace (top of stack)\n";
376         foreach_reverse (i, obj; this.objects_)
377         {
378             result ~= "#  " ~ int2string (i+1) ~ ": " ~ obj.toString () ~ "\n";
379         }
380         result ~= "# End of Stack Trace (bottom of stack)";
381         return result;
382     }
383 }
384
385 /*******************************************************************************
386
387     Exception for fatal errors like failed memory allocation or missing
388     functionality.
389
390 *******************************************************************************/
391
392 class LuaFatalException : Exception
393 {
394     /***************************************************************************
395
396         Constructs a fatal exception.
397
398         Params:
399         message = Error message
400
401     ***************************************************************************/
402
403     this (char[] message)
404     {
405         super (message);
406     }
407
408     /***************************************************************************
409
410         Constructs a fatal exception from a Lua error code.
411
412         Params:
413         error = Lua error code.
414
415     ***************************************************************************/
416
417     this (int error)
418     {
419         if (error == LUA_ERRRUN)
420             super ("Runtime error");
421         else if (error == LUA_ERRMEM)
422             super ("Memory allocation error");
423         else if (error == LUA_ERRERR)
424             super ("Error while running the error handler function");
425         else if (error == LUA_ERRFILE)
426             super ("Error opening the file");
427         else
428             super ("Unknown Lua error");
429     }
430 }
431
432
433 /*******************************************************************************
434
435     Exception for I/O errors while loading Lua code from a file.
436
437 *******************************************************************************/
438
439 class LuaIOException : Exception
440 {
441     /***************************************************************************
442
443     Constructs an I/O exception.
444
445     ***************************************************************************/
446
447     public this (char[] message)
448     {
449         super (message);
450     }
451 }
452
453 /*******************************************************************************
454
455     Exception for errors in Lua code.
456
457 *******************************************************************************/
458
459 class LuaCodeException : Exception
460 {
461     /***************************************************************************
462
463     Constructs a code exception.
464
465     ***************************************************************************/
466
467     public this (char[] message)
468     {
469         super (message);
470     }
471 }
472
473
474
475
476
477 version (Tango)
478 {
479     alias Exception ExtendedException;
480 }
481 else
482 {
483     class ExtendedException : Exception
484     {
485     private Exception next_;
486    
487     this (char[] message, Exception next)
488     {
489         this.next_ = next;
490         super (message);
491     }
492    
493     this (char[] message, char[] file, int line, Exception next)
494     {
495         this.next_ = next;
496         super (message);
497     }
498     }
499 }
500
501
502 /*******************************************************************************
503
504     Exception for forwarding other Exceptions through Lua code. It contains
505     information about the forwarding process as well as a reference to the
506     Exception, which is forwarded.
507
508     This is necessary, as the Lua library cannot handle exceptions. Therefore,
509     the lua routines which call D code catch exceptions, save them in
510     LuaForwardException.exceptions and call lua_error. If the lua code was
511     called in protected mode, the error is parsed by LuaState.call which
512     extracts the exception from LuaForwardException.exceptions, adds some
513     forwarder information and throws it again.
514
515     For better debugging, a Lua Call Trace and Stack Trace are added at the
516     point, where the original exception was caught.
517
518     Remember that there might be a stack of protected library calls, in which
519     case each of them adds a LuaForwardException to the chain.
520
521 *******************************************************************************/
522
523 class LuaForwardException : ExtendedException
524 {
525     /// Call Trace
526     private LuaCallTrace call_trace_;
527
528     /// Stack Trace
529     private LuaStackTrace stack_trace_;
530
531     /***************************************************************************
532
533         Constructs a forward exception, as well as the current Call Trace and
534         Stack Trace.
535
536         Params:
537         exception = exception to be forwarded
538         catcher_name = name of the place, where the exception was forwarded.
539         state = Lua state.
540
541     ***************************************************************************/
542
543     public this (LuaState state, Exception exception, char[] catcher_name)
544     {
545         super (catcher_name ~ " caught:\n\n" ~ this.call_trace_.toString ~ "\n\n" ~ this.stack_trace_.toString, exception);
546
547         this.call_trace_ = new LuaCallTrace (state);
548         this.stack_trace_ = new LuaStackTrace (state);
549     }
550
551     /***************************************************************************
552
553         Constructs a forward exception, as well as the current Call Trace and
554         Stack Trace. Also attaches file and line information.
555
556         Params:
557         exception = exception to be forwarded
558         catcher_name = name of the place, where the exception was forwarded.
559         file = name of the file.
560         line = line in the file.
561         state = Lua state.
562
563     ***************************************************************************/
564
565     public this (LuaState state, Exception exception, char[] catcher_name, char[] file, long line)
566     {
567         this.call_trace_ = new LuaCallTrace (state);
568         this.stack_trace_ = new LuaStackTrace (state);
569
570         super (catcher_name ~ " caught:\n\n" ~ this.call_trace_.toString ~ "\n\n" ~ this.stack_trace_.toString, file, line, exception);
571     }
572
573     /***************************************************************************
574
575         Adds forward information to the exception.
576
577         Params:
578         forwarder_name = name of the forwarder.
579
580     ***************************************************************************/
581
582     public void forward (char[] forwarder_name)
583     {
584         this.msg = forwarder_name ~ " forwarded, " ~ this.msg;
585     }
586
587     /// Returns the attached Call Trace.
588
589     public LuaCallTrace getCallTrace ()
590     {
591         return this.call_trace_;
592     }
593
594     /// Returns the attached Stack Trace.
595
596     public LuaStackTrace getStackTrace ()
597     {
598         return this.stack_trace_;
599     }
600
601     /// Associative array which maps pointers to Exceptions, to avoid collection of them during error processing.
602
603     private static LuaForwardException[void *] exceptions;
604 }
Note: See TracBrowser for help on using the browser.