root/trunk/lua/error.d

Revision 324, 18.1 kB (checked in by Trass3r, 3 years ago)

+ D2 compatibility!
+ precompiled lua 5.1.4
+ Exceptions support file and line arguments
* changed file encoding to UTF-8
* several bugfixes

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