Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / SCClassLibrary / Common / Core / Error.sc
blob33dbf34c3bbd88de8a8d35daa15237bb8a03517f
1 Exception {
2         classvar <>handling = false;
3         classvar <>debug = false;
4         classvar <>inProtectedFunction = false;
6         var <>what, <>protectedBacktrace, <>path;
8         *new { arg what;
9                 var protectedBacktrace, instance;
10                 if (debug || inProtectedFunction, {
11                         protectedBacktrace = this.getBackTrace.caller;
12                         inProtectedFunction = false;
13                 });
14                 ^super.newCopyArgs(what ? this.name, protectedBacktrace, thisProcess.nowExecutingPath);
15         }
16         errorString {
17                 ^"EXCEPTION: " ++ what
18         }
19         reportError {
20                 this.errorString.postln;
21                 if(protectedBacktrace.notNil, { this.postProtectedBacktrace });
22                 this.dumpBackTrace;
23                 // this.adviceLink.postln;
24                 "^^ The preceding error dump is for %\n".postf(this.errorString);
25         }
26         adviceLink {
27                 ^("For advice: [http://supercollider.sf.net/wiki/index.php/%]"
28                         .format(this.adviceLinkPage));
29         }
30         adviceLinkPage {
31                 ^this.errorString.tr($ , $_).tr($\n, $_);
32         }
33         postProtectedBacktrace {
34                 var out, currentFrame, def, ownerClass, methodName, pos, tempStr;
35                 out = CollStream.new;
36                 "\nPROTECTED CALL STACK:".postln;
37                 currentFrame = protectedBacktrace;
38                 while({currentFrame.notNil}, {
39                         def = currentFrame.functionDef;
40                         if(def.isKindOf(Method), {
41                                 ownerClass = def.ownerClass;
42                                 methodName = def.name;
43                                 if(ownerClass == Function && { #['protect', 'try'].includes(methodName) }, {
44                                         pos = out.pos;
45                                 });
46                                 out << "\t%:%\t%\n".format(ownerClass, methodName, currentFrame.address);
47                         }, {
48                                 out << "\ta FunctionDef\t%\n".format(currentFrame.address);
49                                 // sourceCode may be ridiculously huge,
50                                 // so steal the technique from Object:asString to reduce the printed size
51                                 tempStr = String.streamContentsLimit({ |stream|
52                                         stream << "\t\tsourceCode = " <<< (def.sourceCode ? "<an open Function>");
53                                 }, 512);
54                                 out << tempStr;
55                                 if(tempStr.size >= 512) { out << "...etc..." << $" };
56                                 out << Char.nl;
57                         });
58                         def.argNames.do({|name, i|
59                                 out << "\t\targ % = %\n".format(name, currentFrame.args[i]);
60                         });
61                         def.varNames.do({|name, i|
62                                 out << "\t\tvar % = %\n".format(name, currentFrame.vars[i]);
63                         });
64                         currentFrame = currentFrame.caller;
65                 });
66                 // lose everything after the last Function:protect
67                 // it just duplicates the normal stack with less info
68                 // but, an Error in a routine in a Scheduler
69                 // may not have a try/protect in the protectedBacktrace
70                 // then, pos is nil and we should print everything
71                 postln(
72                         if(pos.notNil) {
73                                 out.collection.copyFromStart(pos)
74                         } {
75                                 out.collection
76                         }
77                 );
78         }
80         isException { ^true }
83 Error : Exception {
84         errorString {
85                 ^"ERROR: " ++ what
86         }
87         errorPathString {
88                 ^if(path.isNil) { "" } { "PATH:" + path ++ "\n" }
89         }
92 MethodError : Error {
93         var <>receiver;
95         *new { arg what, receiver;
96                 ^super.new(what).receiver_(receiver)
97         }
98         reportError {
99                 this.errorString.postln;
100                 "RECEIVER:\n".post;
101                 receiver.dump;
102                 this.errorPathString.post;
103                 if(protectedBacktrace.notNil, { this.postProtectedBacktrace });
104                 this.dumpBackTrace;
105                 // this.adviceLink.postln;
106                 "^^ The preceding error dump is for %\nRECEIVER: %\n".postf(this.errorString, receiver);
107         }
108         adviceLinkPage {
109                 ^this.class.name
110         }
114 PrimitiveFailedError : MethodError {
115         var <>failedPrimitiveName;
117         *new { arg receiver;
118                 ^super.new(Thread.primitiveErrorString, receiver)
119                         .failedPrimitiveName_(thisThread.failedPrimitiveName)
120         }
121         errorString {
122                 ^"ERROR: Primitive '%' failed.\n%".format(failedPrimitiveName, what ? "")
123         }
126 SubclassResponsibilityError : MethodError {
127         var <>method, <>class;
128         *new { arg receiver, method, class;
129                 ^super.new(nil, receiver).method_(method).class_(class)
130         }
131         errorString {
132                 ^"ERROR: '" ++ method.name ++ "' should have been implemented by "
133                         ++ class.name ++ "."
134         }
137 ShouldNotImplementError : MethodError {
138         var <>method, <>class;
139         *new { arg receiver, method, class;
140                 ^super.new(nil, receiver).method_(method).class_(class)
141         }
142         errorString {
143                 ^"ERROR: '" ++ method.ownerClass.name ++ "-" ++ method.name
144                         ++ "' Message not valid for this subclass: "
145                         ++ class.name ++ "."
146         }
149 DoesNotUnderstandError : MethodError {
150         var <>selector, <>args;
151         *new { arg receiver, selector, args;
152                 ^super.new(nil, receiver).selector_(selector).args_(args)
153         }
154         errorString {
155                 ^"ERROR: Message '" ++ selector ++ "' not understood."
156         }
157         reportError {
158                 this.errorString.postln;
159                 "RECEIVER:\n".post;
160                 receiver.dump;
161                 "ARGS:\n".post;
162                 args.dumpAll;
163                 this.errorPathString.post;
164                 if(protectedBacktrace.notNil, { this.postProtectedBacktrace });
165                 this.dumpBackTrace;
166                 // this.adviceLink.postln;
167                 "^^ The preceding error dump is for %\nRECEIVER: %\n".postf(this.errorString, receiver);
168         }
169         adviceLinkPage {
170                 ^"%#%".format(this.class.name, selector)
171         }
175 MustBeBooleanError : MethodError {
176         errorString {
177                 ^"ERROR: Non Boolean in test."
178         }
181 NotYetImplementedError : MethodError {
184 OutOfContextReturnError : MethodError {
185         var <>method, <>result;
186         *new { arg receiver, method, result;
187                 ^super.new(nil, receiver).method_(method).result_(result)
188         }
189         errorString {
190                 ^"ERROR: '" ++ method.ownerClass.name ++ "-" ++ method.name
191                         ++ "' Out of context return of value: " ++ result
192         }
195 ImmutableError : MethodError {
196         var <>value;
197         *new { arg receiver, value;
198                 ^super.new(nil, receiver).value_(value)
199         }
200         errorString {
201                 ^"ERROR: Object is immutable: " ++ receiver
202         }
205 BinaryOpFailureError : DoesNotUnderstandError {
206         errorString {
207                 ^"ERROR: binary operator '" ++ selector ++ "' failed."
208         }
211 DeprecatedError : MethodError {
212         var <>method, <>class, <>alternateMethod;
214         *new { arg receiver, method, alternateMethod, class;
215                 ^super.new(nil).receiver_(receiver).method_(method).class_(class).alternateMethod_(alternateMethod)
216         }
217         errorString {
218                 var methodSignature = { arg m;
219                         m.ownerClass.name.asString  ++ ":" ++ m.name;
220                 };
221                 var searchForCaller = { arg backtrace, m;
222                         while {
223                                 backtrace.notNil and: {
224                                         backtrace.functionDef !== m
225                                 }
226                         } {
227                                 backtrace = backtrace.caller;
228                         };
229                         // backtrace.caller may now be a FunctionDef,
230                         // useless for troubleshooting
231                         // so roll back to the last real method
232                         while {
233                                 backtrace.notNil and: {
234                                         backtrace = backtrace.caller;
235                                         backtrace.functionDef.isKindOf(Method).not
236                                 }
237                         };
238                         if(backtrace.notNil) { backtrace.tryPerform(\functionDef) };
239                 };
240                 var caller, string;
241                 if(protectedBacktrace.notNil) {
242                         caller = searchForCaller.value(protectedBacktrace, method);
243                 };
244                 if(caller.isNil) {
245                         caller = searchForCaller.value(this.getBackTrace, method);
246                 };
247                 if(caller.isNil) {
248                         caller = "{unknown}"
249                 } {
250                         if(caller.isKindOf(Method)) {
251                                 caller = methodSignature.value(caller);
252                         } {
253                                 caller = caller.asString;
254                         };
255                 };
256                 string = "WARNING: Called from %, method % is deprecated and will be removed.".format(
257                         caller,
258                         methodSignature.value(method)
259                 );
260                 if(alternateMethod.notNil, {
261                         string = string + "Use" + methodSignature.value(alternateMethod) + "instead.";
262                 });
263                 ^string;
264         }
266         reportError {
267                 this.errorString.postln;
268                 this.errorPathString.post;
269                 // this.adviceLink.postln;
270         }
272         throw {
273                 Error.handling = true;
274                 this.reportError;
275                 if (Error.debug) {
276                         if(protectedBacktrace.notNil, { this.postProtectedBacktrace });
277                         this.dumpBackTrace;
278                         Error.handling = false;
279                         this.halt;
280                 } {
281                         Error.handling = false;
282                 };
284         }
285         adviceLinkPage {
286                 ^"DeprecatedError"
287         }