Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / SCClassLibrary / Common / Core / Kernel.sc
blob4d93b4d850c5b26780217731cbd45f4277efe284
1 // you must not make any change at all to the order or number of
2 // instance variables in these classes!
3 // You should also not muck with the contents of the instance
4 // variables unless you are sure you know what you are doing.
5 // You may add methods.
7 Class {
8         var <name, <nextclass, superclass, <subclasses;
9         var <methods, <instVarNames, <classVarNames;
10         var <iprototype, <cprototype;
11         var <constNames, <constValues;
12         var instanceFormat, instanceFlags;
13         var <classIndex, classFlags, <maxSubclassIndex;
14         var <filenameSymbol, <charPos, <classVarIndex;
16         classvar <>classesInited;
18         // Every class has a metaclass which has 'Meta_' prepended to the name.
19         // Though there is a class Meta_Class which is the class of Class, the
20         // class of Meta_Class is Class. It is a loop, but a different one
21         // than the structure in Smalltalk.
23         superclass {
24                 // superclass is stored as a symbol to allow forward reference during compilation
25                 ^superclass.asClass
26         }
27         asClass { ^this }
28         isMetaClass { ^this.class === Class }
30         initClass {   }
33         // call Class.initClassTree(SomeClass) to force a class to init if you depend on its resources
34         *initClassTree { arg aClass;
35                 var implementsInitClass;
36                 // sometimes you need a class to be inited before another class
37                 // start the process: Class.initClassTree(Object)
38                 if(classesInited.isNil, { classesInited = IdentitySet.new });
39                 if(classesInited.includes(aClass).not, {
40                         if(aClass.isMetaClass.not and: { aClass.class.findMethod(\initClass).notNil }, {
41                                         aClass.initClass;
42                         });
44                         classesInited.add(aClass);
45                         if(aClass.subclasses.notNil,{
46                                 aClass.subclasses.do({ arg class; this.initClassTree(class); });
47                         });
48                 });
49         }
51         *allClasses { _AllClasses }
53         findMethod { arg methodName;
54                 if ( methods.notNil, {
55                         ^methods.detect({ arg method; method.name == methodName });
56                 },{ ^nil });
57         }
58         findRespondingMethodFor { arg methodName;
59                 this.superclassesDo { arg class;
60                         var method = class.findMethod(methodName);
61                         method !? { ^method };
62                 };
63                 ^nil
64         }
65         findOverriddenMethod { arg methodName;
66                 if(this.findMethod(methodName).isNil) { ^nil };
67                 this.superclass.superclassesDo { arg class;
68                         var method = class.findMethod(methodName);
69                         if(method.notNil) { ^method }
70                 };
71                 ^nil
72         }
73         superclassesDo { arg function;
74                 var class = this;
75                 while { class.notNil } {
76                         function.value(class);
77                         class = class.superclass;
78                 }
79         }
81         dumpByteCodes { arg methodName;
82                 var meth;
83                 meth = this.findMethod(methodName);
84                 if (meth.notNil, { meth.dumpByteCodes },{ Post << methodName << " not found.\n"; });
85         }
87         dumpClassSubtree { _DumpClassSubtree }
88         dumpInterface {
89                 // show all methods and their arguments defined for this class
90                 // does not include methods defined in superclasses
91                 this.methods.do({ arg meth;
92                         var numargs;
93                         numargs = meth.argNames.size - 1;
94                         "   ".post;
95                         meth.name.post;
96                         " ( ".post;
97                         meth.argNames.do({ arg name, i;
98                                 if (i > 0, { // skip 'this'
99                                         name.post;
100                                         if (i < numargs, {
101                                                 ", ".post;
102                                         });
103                                 });
104                         });
105                         " )\n".post;
106                 });
107         }
109         asString {
110                 ^name.asString
111         }
112         printOn { arg stream;
113                 stream << "class " << name;
114         }
115         storeOn { arg stream;
116                 stream << name;
117         }
118         archiveAsCompileString { ^true }
120         hasHelpFile {
121                 //should cache this in Library or classvar
122                 //can't add instance variables to Class
123                 ^this.name.asString.findHelpFile.notNil
124         }
125         helpFilePath {
126                 ^this.name.asString.findHelpFile
127         }
128         help {
129                 this.openHelpFile
130         }
131         openHelpFile {
132                 // NOTE: because wslib provided the shortcut "Object:*help --> Object:*openHelpFile", we do the same
133                 // rather than moving the implementation to the future-compatible :help method.
134                 // This prevents infinite recursions for people with wslib installed.
135                 // In future (3.7) this method content should be moved to :help, but no sooner.
136                 this.name.asString.help
137         }
139         shallowCopy { ^this }
140         //listInstances { _ListInstancesOf }
141         //traceAnyPathToAllInstancesOf { _TraceAnyPathToAllInstancesOf }
143         openCodeFile {
144                 this.filenameSymbol.asString.openTextFile(this.charPos, -1);
145         }
146         classVars {
147                 var start, end;
148                 start = this.classVarIndex;
149                 end = start + this.classVarNames.size;
150                 ^thisProcess.instVarAt(0).copyRange(start, end)
151         }
152         inspectorClass { ^ClassInspector }
153         findReferences { arg aSymbol, references;
154                 methods.do({ arg meth;
155                         references = meth.findReferences(aSymbol, references)
156                 });
157                 ^references
158         }
159         *findAllReferences { arg aSymbol;
160                 // this will not find method calls that are compiled with special byte codes such as 'value'.
161                 var references;
162                 Class.allClasses.do({ arg class;
163                         references = class.findReferences(aSymbol, references);
164                 });
165                 ^references;
166         }
167         allSubclasses {
168                 var list;
169                 list = subclasses.copy;
170                 subclasses.do({ arg class; list = list ++ class.allSubclasses; });
171                 ^list
172         }
173         superclasses {
174                 var list;
175                 this.superclass.superclassesDo { arg class; list = list.add(class) }
176                 ^list
177         }
181 Process {
182         // A Process is a runtime environment.
183         var classVars, <interpreter;
184         var curThread, <mainThread;
185         var schedulerQueue;
186         var <>nowExecutingPath;
188         // SCVersion.sc overrides these for Main
189         *scVersionMajor { ^123 }
190         *scVersionMinor { ^0 }
191         *scVersionPostfix { ^"unknown" }
193         startup {
194                 var time;
196                 Class.initClassTree(AppClock); // AppClock first in case of error
197                 time = this.class.elapsedTime;
198                 Class.initClassTree(Object);
199                 ("Class tree inited in" + (this.class.elapsedTime - time).round(0.01) + "seconds").inform;
200                 Class.classesInited = nil;
202                 topEnvironment = Environment.new;
203                 currentEnvironment = topEnvironment;
204                 Archive.read;
206                 // This method is called automatically right after compiling.
207                 // Override in class 'Main' to do initialization stuff,
208                 // but make sure to call this superclass method.
210                 // the AppClock is not started until after this method is complete
211         }
212         run {
213                 // This method is called when 'Run Main' is chosen from the menu.
214                 // Override in class 'Main' to do whatever you want.
215         }
216         stop {
217                 // This method is called when 'Stop Main' is chosen from the menu.
218                 // Override in class 'Main' to do whatever you want.
219         }
220         shutdown {
221                 // This method is called before recompiling or quitting.
222                 // Override in class 'Main' to do whatever you want.
223                 ShutDown.run;
224                 NetAddr.disconnectAll;
225                 File.closeAll;
226                 Archive.write;
227         }
228         tick { // called repeatedly by SCVirtualMachine::doPeriodicTask
229                 ^AppClock.tick;
230         }
232         *tailCallOptimize { _GetTailCallOptimize }
233         *tailCallOptimize_ { arg bool; _SetTailCallOptimize ^this.primitiveFailed }
235         getCurrentSelection {
236                 var qt = \QtGUI.asClass;
237                 ^if(qt.notNil and: {qt.focusView.notNil}) {
238                         qt.selectedText;
239                 } {
240                         interpreter.cmdLine;
241                 }
242         }
244         openCodeFile {
245                 var string, class, method, words;
246                 string = this.getCurrentSelection;
247                 if (string.includes($:), {
248                         string.removeAllSuchThat(_.isSpace);
249                         words = string.delimit({ arg c; c == $: });
250                         class = words.at(0).asSymbol.asClass;
251                         if (class.notNil, {
252                                 method = class.findMethod(words.at(1).asSymbol);
253                                 if (method.notNil, {
254                                         method.filenameSymbol.asString.openTextFile(method.charPos, -1);
255                                 });
256                         });
257                 },{
258                         class = string.asSymbol.asClass;
259                         if (class.notNil, {
260                                 class = class.classRedirect;
261                                 class.filenameSymbol.asString.openTextFile(class.charPos, -1);
262                         });
263                 });
264         }
266         openWinCodeFile {
267                 var string, class, method, words;
268                 string = this.getCurrentSelection;
269                 if (string.includes($:), {
270                         string.removeAllSuchThat(_.isSpace);
271                         words = string.delimit({ arg c; c == $: });
272                         class = words.at(0).asSymbol.asClass;
273                         if (class.notNil, {
274                                 method = class.findMethod(words.at(1).asSymbol);
275                                 if (method.notNil, {
276                                         method.filenameSymbol.asString.openWinTextFile(method.charPos, -1);
277                                 });
278                         });
279                 },{
280                         class = string.asSymbol.asClass;
281                         if (class.notNil, {
282                                 class = class.classRedirect;
283                                 class.filenameSymbol.asString.openWinTextFile(class.charPos, -1);
284                         });
285                 });
286         }
289         methodReferences {
290                 // this will not find method calls that are compiled with special byte codes such as 'value'.
291                 var name, out, references, nameString;
292                 out = CollStream.new;
293                 name = this.getCurrentSelection.asSymbol;
294                 references = Class.findAllReferences(name);
295                 if (references.notNil, {
296                         out << "References to '" << name << "' :\n";
297                         references.do({ arg ref;
298                                 nameString = ref.ownerClass.name ++ ":" ++ ref.name;
299                                 out << "   [" << nameString << "]\n"; });
300                         out.collection.newTextWindow(name.asString);
301                 },{
302                         Post << "\nNo references to '" << name << "'.\n";
303                 });
304         }
305         methodTemplates {
306                 // this constructs the method templates when cmd-Y is pressed in the Lang menu.
307                 var name, out, found = 0, namestring, text;
308                 out = CollStream.new;
310                 text = this.getCurrentSelection;
312                 if (text.isEmpty){
313                         Post << "\nNo implementations of ''.\n";
314                         ^this
315                 };
316                 if (text[0].toLower != text[0]) {
317                         // user pressed the wrong key. DWIM.
318                         ^this.openCodeFile;
319                 };
320                 name = text.asSymbol;
321                 out << "Implementations of '" << name << "' :\n";
322                 Class.allClasses.do({ arg class;
323                         class.methods.do({ arg method;
324                                 if (method.name == name, {
325                                         found = found + 1;
326                                         namestring = class.name ++ ":" ++ name;
327                                         out << "   [" << namestring << "] :     ";
328                                         if (method.argNames.isNil or: { method.argNames.size == 1 }, {
329                                                 out << "this." << name;
330                                                 if (name.isSetter, { out << "(val)"; });
331                                         },{
332                                                 out << method.argNames.at(0);
333                                                 if (name.asString.at(0).isAlpha, {
334                                                         out << "." << name << "(";
335                                                         method.argNames.do({ arg argName, i;
336                                                                 if (i > 0, {
337                                                                         if (i != 1, { out << ", " });
338                                                                         out << argName;
339                                                                 });
340                                                         });
341                                                         out << ")";
342                                                 },{
343                                                         out << " " << name << " ";
344                                                         out << method.argNames.at(1);
345                                                 });
346                                         });
347                                         out.nl;
348                                 });
349                         });
350                 });
351                 case
352                 { found == 0 }
353                 {
354                         Post << "\nNo implementations of '" << name << "'.\n";
355                 }
356                 { found == 1 }
357                 {
358                         interpreter.cmdLine = namestring;
359                         this.openCodeFile;
360                 }
361                 {
362                         out.collection.newTextWindow(name.asString);
363                 };
364         }
366         interpretCmdLine {
367                 // interpret some text from the command line
368                 interpreter.interpretCmdLine;
369         }
371         interpretPrintCmdLine {
372                 // interpret some text from the command line and print result
373                 interpreter.interpretPrintCmdLine;
374         }
376         interpretPrintSelectedText {
377                 interpreter.cmdLine = this.getCurrentSelection;
378                 interpreter.interpretPrintCmdLine;
379         }
381         showHelp {
382                 this.getCurrentSelection.help
383         }
385         argv { ^[] }
387         shallowCopy { ^this }
389         *elapsedTime { _ElapsedTime }
391         storeOn { arg stream;
392                 stream << "thisProcess";
393         }
394         archiveAsCompileString { ^true }
396         prSchedulerQueue { ^schedulerQueue }
400 FunctionDef {
401         var raw1, raw2, <code, <selectors, <constants, <prototypeFrame, <context, <argNames, <varNames;
402         var <sourceCode;
404         // a FunctionDef is defined by a code within curly braces {}
405         // When you use a FunctionDef in your code it gets pushed on the stack
406         // as an instance of Function
408         dumpByteCodes { _DumpByteCodes }
410         numArgs { _FunDef_NumArgs }             // return number of arguments to the function
411         numVars { _FunDef_NumVars }             // return number of variables in the function
412         varArgs { _FunDef_VarArgs }             // return boolean whether function has ellipsis argument
414         shallowCopy { ^this }
416         asFunction {
417                 // this is only legal for closed functions.
418                 _FunctionDefAsFunction
419                 ^this.primitiveFailed
420         }
422         dumpContexts {
423                 _FunctionDefDumpContexts
424         }
425         inspectorClass { ^FunctionDefInspector }
427         findReferences { arg aSymbol, references;
428                 var lits;
429                 lits = selectors.asArray;
430                 if (lits.includes(aSymbol), {
431                         references = references.add(this);
432                 });
433                 lits.do({ arg item;
434                         if (item.isKindOf(FunctionDef), {
435                                 references = item.findReferences(aSymbol, references)
436                         })
437                 });
438                 ^references
439         }
440         storeOn { arg stream;
441                 stream << "nil"
442         }
443         checkCanArchive { "cannot archive FunctionDefs".warn }
444         archiveAsCompileString { ^true }
446         argumentString { arg withDefaultValues=true;
447                 var res, last;
448                 if(argNames.isNil) { ^nil };
449                 res = "";
450                 last = argNames.size-1;
451                 argNames.do { |name, i|
452                         var value;
453                         res = res ++ name;
454                         if(withDefaultValues and: { value = prototypeFrame[i]; value.notNil }) {
455                                 res = res ++ " = " ++ value.asCompileString
456                         };
457                         if(i != last) { res = res ++ ", " };
458                 }
459                 ^res
460         }
462         makeEnvirFromArgs {
463                 var argNames, argVals;
464                 argNames = this.argNames;
465                 argVals = this.prototypeFrame.keep(argNames.size);
466                 ^().putPairs([argNames, argVals].flop.flatten)
467         }
471 Method : FunctionDef {
472         var <ownerClass, <name, <primitiveName;
473         var <filenameSymbol, <charPos;
475         openCodeFile {
476                 this.filenameSymbol.asString.openTextFile(this.charPos, -1);
477         }
478         hasHelpFile {
479                 //should cache this in Library or classvar
480                 //can't add instance variables to Class
481                 ^this.name.asString.findHelpFile.notNil
482         }
483         help {
484                 HelpBrowser.openHelpForMethod(this);
485         }
486         inspectorClass { ^MethodInspector }
487         storeOn { arg stream;
488                 stream << ownerClass.name << ".findMethod(" << name.asCompileString << ")"
489         }
490         archiveAsObject { ^true }
491         checkCanArchive {}
492         findReferences { arg aSymbol, references;
493                 var lits, functionRefs;
494                 lits = selectors.asArray;
495                 if (lits.includes(aSymbol), {
496                         references = references.add(this);
497                         ^references // we only need to be listed once
498                 });
499                 lits.do({ arg item;
500                         if (item.isKindOf(FunctionDef), {
501                                 functionRefs = item.findReferences(aSymbol, functionRefs);
502                         })
503                 });
504                 functionRefs.notNil.if({references = references.add(this)});
505                 ^references
506         }
509 Frame {
510         // frames contain the local variables, context and continuation of a function or method invocation.
511         // since some Frames are deleted instead of garbage collected, it is too
512         // dangerous to allow access to them. Dangling pointers could result.
513         shallowCopy { ^this }
514         inspectorClass { ^FrameInspector }
516         storeOn { arg stream; stream << "nil"; }
517         archiveAsCompileString { ^true }
518         checkCanArchive { "cannot archive Frames".warn }
521 DebugFrame {
522         var <functionDef, <args, <vars, <caller, <context, <address;
523         // Object.getBackTrace returns one of these.
524         // 'functionDef' is the FunctionDef for this function or method.
525         // 'args' the values of the arguments to the function call.
526         // 'vars' the values of the local variables.
527         // 'caller' points to another DebugFrame for the caller to this function.
528         // 'context' points to another DebugFrame for the frame lexically enclosing this one.
529         // 'address' memory address of the actual frame object.
530         asString { ^"DebugFrame of " ++ functionDef.asString }
533 RawPointer {
534         // class used to hold raw pointers from the
535         // host environment.
538 Interpreter {
539         // The interpreter defines a context in which interactive commands
540         // are compiled.
542         var <>cmdLine; // place holder for text executed from a worksheet
543         var context; // faked interpreter context frame. Don't mess with it.
545         // a-z are predefined variables for use by the interactive context.
546         // They are read+write so that programmatic methods can
547         // get and alter the values that the interpreter has access to.
548         var <>a, <>b, <>c, <>d, <>e, <>f, <>g, <>h, <>i, <>j;
549         var <>k, <>l, <>m, <>n, <>o, <>p, <>q, <>r, <>s, <>t;
550         var <>u, <>v, <>w, <>x, <>y, <>z;
552         var <>codeDump, <>preProcessor;
554         *new { ^this.shouldNotImplement(thisMethod) }
556         interpretCmdLine {
557                 ^this.compile(cmdLine).value;
558         }
560         interpretPrintCmdLine {
561                 var res, func, code = cmdLine, doc = Document.current, ideClass = \ScIDE.asClass;
562                 "\n".post;
563                 preProcessor !? { cmdLine = preProcessor.value(cmdLine, this) };
564                 func = this.compile(cmdLine);
565                 if (ideClass.notNil) {
566                         thisProcess.nowExecutingPath = ideClass.currentPath
567                 } {
568                         if(doc.tryPerform(\dataptr).notNil) {
569                                 thisProcess.nowExecutingPath = doc.tryPerform(\path);
570                         }
571                 };
572                 res = func.value;
573                 thisProcess.nowExecutingPath = nil;
574                 codeDump.value(code, res, func, this);
575                 res.postln;
576         }
578         interpret { arg string ... args;
579                 // compile, evaluate
580                 cmdLine = string;
581                 ^this.compile(string).valueArray(args);
582         }
583         interpretPrint { arg string ... args;
584                 // compile, evaluate, print
585                 cmdLine = string;
586                 ^this.compile(string).valueArray(args).postln;
587         }
588         compile { arg string;
589                 _CompileExpression
590                 // compiles string and returns a Function.
591                 // the string must be a valid expression.
592                 // You cannot compile a class definition this way.
593                 // This method is not implemented in SCPlay.
594                 ^nil
595         }
597         clearAll {
598                 a = b = c = d = e = f = g = h = i = j = k = l = m =
599                 n = o = p = q = r = s = t = u = v = w = x = y = z = nil;
600         }
602         executeFile { arg pathName ... args;
603                 var     result, saveExecutingPath = thisProcess.nowExecutingPath;
604                 if (File.exists(pathName).not) {
605                         "file \"%\" does not exist.\n".postf(pathName);
606                         ^nil
607                 };
608                 thisProcess.nowExecutingPath = pathName;
609                 protect {
610                         result = this.compileFile(pathName).valueArray(args)
611                 } { |exception|
612                                 exception !? { exception.path = pathName };
613                                 thisProcess.nowExecutingPath = saveExecutingPath
614                 };
615                 ^result
616         }
618         compileFile { arg pathName;
619                 var file, text;
620                 file = File.new(pathName, "r");
621                 if (file.isNil, {
622                         error("file open failed\n");
623                         ^nil
624                 });
625                 text = file.readAllString;
626                 file.close;
627                 if (text.beginsWith("#!"), {
628                         // comment out shebang to preserve line count
629                         text.overWrite("//");
630                 });
631                 ^this.compile(text)
632         }
634         // PRIVATE
635         functionCompileContext {
636                 // compiler uses this method as a fake context in which to compile
637                 // the user's function.
638                 // Do not edit this method!
640                 {}      // this forces the compiler to generate a heap allocated frame rather than
641                         // a frame on the stack
642         }
643         shallowCopy { ^this }