clean up indentation and spacing
[supercollider.git] / SCClassLibrary / Common / Core / Kernel.sc
blobfe8239c829df8b287f05374949ebba147ebf8dec
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         openHelpFile {
129                 this.name.asString.openHelpFile
130         }
132         shallowCopy { ^this }
133         //listInstances { _ListInstancesOf }
134         //traceAnyPathToAllInstancesOf { _TraceAnyPathToAllInstancesOf }
136         openCodeFile {
137                 this.filenameSymbol.asString.openTextFile(this.charPos, -1);
138         }
139         classVars {
140                 var start, end;
141                 start = this.classVarIndex;
142                 end = start + this.classVarNames.size;
143                 ^thisProcess.instVarAt(0).copyRange(start, end)
144         }
145         inspectorClass { ^ClassInspector }
146         findReferences { arg aSymbol, references;
147                 methods.do({ arg meth;
148                         references = meth.findReferences(aSymbol, references)
149                 });
150                 ^references
151         }
152         *findAllReferences { arg aSymbol;
153                 // this will not find method calls that are compiled with special byte codes such as 'value'.
154                 var references;
155                 Class.allClasses.do({ arg class;
156                         references = class.findReferences(aSymbol, references);
157                 });
158                 ^references;
159         }
160         allSubclasses {
161                 var list;
162                 list = subclasses.copy;
163                 subclasses.do({ arg class; list = list ++ class.allSubclasses; });
164                 ^list
165         }
166         superclasses {
167                 var list;
168                 this.superclass.superclassesDo { arg class; list = list.add(class) }
169                 ^list
170         }
174 Process {
175         // A Process is a runtime environment.
176         var classVars, <interpreter;
177         var curThread, mainThread;
178         var schedulerQueue;
179         var <>nowExecutingPath;
181         // SCVersion.sc overrides these for Main
182         *scVersionMajor { ^123 }
183         *scVersionMinor { ^0 }
184         *scVersionPostfix { ^"unknown" }
186         startup {
187                 var time;
189                 Class.initClassTree(AppClock); // AppClock first in case of error
190                 time = this.class.elapsedTime;
191                 Class.initClassTree(Object);
192                 ("Class tree inited in" + (this.class.elapsedTime - time).round(0.01) + "seconds").inform;
193                 Class.classesInited = nil;
195                 topEnvironment = Environment.new;
196                 currentEnvironment = topEnvironment;
197                 Archive.read;
199                 // This method is called automatically right after compiling.
200                 // Override in class 'Main' to do initialization stuff,
201                 // but make sure to call this superclass method.
203                 // the AppClock is not started until after this method is complete
204         }
205         run {
206                 // This method is called when 'Run Main' is chosen from the menu.
207                 // Override in class 'Main' to do whatever you want.
208         }
209         stop {
210                 // This method is called when 'Stop Main' is chosen from the menu.
211                 // Override in class 'Main' to do whatever you want.
212         }
213         shutdown {
214                 // This method is called before recompiling or quitting.
215                 // Override in class 'Main' to do whatever you want.
216                 ShutDown.run;
217                 NetAddr.disconnectAll;
218                 File.closeAll;
219                 Archive.write;
220         }
221         tick { // called repeatedly by SCVirtualMachine::doPeriodicTask
222                 ^AppClock.tick;
223         }
225         *tailCallOptimize { _GetTailCallOptimize }
226         *tailCallOptimize_ { arg bool; _SetTailCallOptimize ^this.primitiveFailed }
228         getCurrentSelection {
229                 var qt = \QtGUI.asClass;
230                 ^if(qt.notNil and: {qt.focusView.notNil}) {
231                         qt.selectedText;
232                 } {
233                         interpreter.cmdLine;
234                 }
235         }
237         openCodeFile {
238                 var string, class, method, words;
239                 string = this.getCurrentSelection;
240                 if (string.includes($:), {
241                         string.removeAllSuchThat(_.isSpace);
242                         words = string.delimit({ arg c; c == $: });
243                         class = words.at(0).asSymbol.asClass;
244                         if (class.notNil, {
245                                 method = class.findMethod(words.at(1).asSymbol);
246                                 if (method.notNil, {
247                                         method.filenameSymbol.asString.openTextFile(method.charPos, -1);
248                                 });
249                         });
250                 },{
251                         class = string.asSymbol.asClass;
252                         if (class.notNil, {
253                                 class = class.classRedirect;
254                                 class.filenameSymbol.asString.openTextFile(class.charPos, -1);
255                         });
256                 });
257         }
259         openWinCodeFile {
260                 var string, class, method, words;
261                 string = this.getCurrentSelection;
262                 if (string.includes($:), {
263                         string.removeAllSuchThat(_.isSpace);
264                         words = string.delimit({ arg c; c == $: });
265                         class = words.at(0).asSymbol.asClass;
266                         if (class.notNil, {
267                                 method = class.findMethod(words.at(1).asSymbol);
268                                 if (method.notNil, {
269                                         method.filenameSymbol.asString.openWinTextFile(method.charPos, -1);
270                                 });
271                         });
272                 },{
273                         class = string.asSymbol.asClass;
274                         if (class.notNil, {
275                                 class = class.classRedirect;
276                                 class.filenameSymbol.asString.openWinTextFile(class.charPos, -1);
277                         });
278                 });
279         }
282         methodReferences {
283                 // this will not find method calls that are compiled with special byte codes such as 'value'.
284                 var name, out, references, nameString;
285                 out = CollStream.new;
286                 name = this.getCurrentSelection.asSymbol;
287                 references = Class.findAllReferences(name);
288                 if (references.notNil, {
289                         out << "References to '" << name << "' :\n";
290                         references.do({ arg ref;
291                                 nameString = ref.ownerClass.name ++ ":" ++ ref.name;
292                                 out << "   [" << nameString << "]\n"; });
293                         out.collection.newTextWindow(name.asString);
294                 },{
295                         Post << "\nNo references to '" << name << "'.\n";
296                 });
297         }
298         methodTemplates {
299                 // this constructs the method templates when cmd-Y is pressed in the Lang menu.
300                 var name, out, found = 0, namestring, text;
301                 out = CollStream.new;
303                 text = this.getCurrentSelection;
305                 if (text.isEmpty){
306                         Post << "\nNo implementations of ''.\n";
307                         ^this
308                 };
309                 if (text[0].toLower != text[0]) {
310                         // user pressed the wrong key. DWIM.
311                         ^this.openCodeFile;
312                 };
313                 name = text.asSymbol;
314                 out << "Implementations of '" << name << "' :\n";
315                 Class.allClasses.do({ arg class;
316                         class.methods.do({ arg method;
317                                 if (method.name == name, {
318                                         found = found + 1;
319                                         namestring = class.name ++ ":" ++ name;
320                                         out << "   [" << namestring << "] :     ";
321                                         if (method.argNames.isNil or: { method.argNames.size == 1 }, {
322                                                 out << "this." << name;
323                                                 if (name.isSetter, { out << "(val)"; });
324                                         },{
325                                                 out << method.argNames.at(0);
326                                                 if (name.asString.at(0).isAlpha, {
327                                                         out << "." << name << "(";
328                                                         method.argNames.do({ arg argName, i;
329                                                                 if (i > 0, {
330                                                                         if (i != 1, { out << ", " });
331                                                                         out << argName;
332                                                                 });
333                                                         });
334                                                         out << ")";
335                                                 },{
336                                                         out << " " << name << " ";
337                                                         out << method.argNames.at(1);
338                                                 });
339                                         });
340                                         out.nl;
341                                 });
342                         });
343                 });
344                 case
345                 { found == 0 }
346                 {
347                         Post << "\nNo implementations of '" << name << "'.\n";
348                 }
349                 { found == 1 }
350                 {
351                         interpreter.cmdLine = namestring;
352                         this.openCodeFile;
353                 }
354                 {
355                         out.collection.newTextWindow(name.asString);
356                 };
357         }
359         interpretCmdLine {
360                 // interpret some text from the command line
361                 interpreter.interpretCmdLine;
362         }
364         interpretPrintCmdLine {
365                 // interpret some text from the command line and print result
366                 interpreter.interpretPrintCmdLine;
367         }
369         interpretPrintSelectedText {
370                 interpreter.cmdLine = this.getCurrentSelection;
371                 interpreter.interpretPrintCmdLine;
372         }
374         showHelp {
375                 this.getCurrentSelection.openHelpFile
376         }
378         argv { ^[] }
380         shallowCopy { ^this }
382         *elapsedTime { _ElapsedTime }
384         storeOn { arg stream;
385                 stream << "thisProcess";
386         }
387         archiveAsCompileString { ^true }
389         prSchedulerQueue { ^schedulerQueue }
393 FunctionDef {
394         var raw1, raw2, <code, <selectors, <constants, <prototypeFrame, <context, <argNames, <varNames;
395         var <sourceCode;
397         // a FunctionDef is defined by a code within curly braces {}
398         // When you use a FunctionDef in your code it gets pushed on the stack
399         // as an instance of Function
401         dumpByteCodes { _DumpByteCodes }
403         numArgs { _FunDef_NumArgs }             // return number of arguments to the function
404         numVars { _FunDef_NumVars }             // return number of variables in the function
405         varArgs { _FunDef_VarArgs }             // return boolean whether function has ellipsis argument
407         shallowCopy { ^this }
409         asFunction {
410                 // this is only legal for closed functions.
411                 _FunctionDefAsFunction
412                 ^this.primitiveFailed
413         }
415         dumpContexts {
416                 _FunctionDefDumpContexts
417         }
418         inspectorClass { ^FunctionDefInspector }
420         findReferences { arg aSymbol, references;
421                 var lits;
422                 lits = selectors.asArray;
423                 if (lits.includes(aSymbol), {
424                         references = references.add(this);
425                 });
426                 lits.do({ arg item;
427                         if (item.isKindOf(FunctionDef), {
428                                 references = item.findReferences(aSymbol, references)
429                         })
430                 });
431                 ^references
432         }
433         storeOn { arg stream;
434                 stream << "nil"
435         }
436         checkCanArchive { "cannot archive FunctionDefs".warn }
437         archiveAsCompileString { ^true }
439         argumentString { arg withDefaultValues=true;
440                 var res, last;
441                 if(argNames.isNil) { ^nil };
442                 res = "";
443                 last = argNames.size-1;
444                 argNames.do { |name, i|
445                         var value;
446                         res = res ++ name;
447                         if(withDefaultValues and: { value = prototypeFrame[i]; value.notNil }) {
448                                 res = res ++ " = " ++ value.asCompileString
449                         };
450                         if(i != last) { res = res ++ ", " };
451                 }
452                 ^res
453         }
455         makeEnvirFromArgs {
456                 var argNames, argVals;
457                 argNames = this.argNames;
458                 argVals = this.prototypeFrame;
459                 ^().putPairs([argNames, argVals].flop.flat)
460         }
464 Method : FunctionDef {
465         var <ownerClass, <name, <primitiveName;
466         var <filenameSymbol, <charPos;
468         openCodeFile {
469                 this.filenameSymbol.asString.openTextFile(this.charPos, -1);
470         }
471         hasHelpFile {
472                 //should cache this in Library or classvar
473                 //can't add instance variables to Class
474                 ^this.name.asString.findHelpFile.notNil
475         }
476         openHelpFile {
477                 HelpBrowser.openHelpForMethod(this);
478         }
479         inspectorClass { ^MethodInspector }
480         storeOn { arg stream;
481                 stream << ownerClass.name << ".findMethod(" << name.asCompileString << ")"
482         }
483         archiveAsObject { ^true }
484         checkCanArchive {}
485         findReferences { arg aSymbol, references;
486                 var lits, functionRefs;
487                 lits = selectors.asArray;
488                 if (lits.includes(aSymbol), {
489                         references = references.add(this);
490                         ^references // we only need to be listed once
491                 });
492                 lits.do({ arg item;
493                         if (item.isKindOf(FunctionDef), {
494                                 functionRefs = item.findReferences(aSymbol, functionRefs);
495                         })
496                 });
497                 functionRefs.notNil.if({references = references.add(this)});
498                 ^references
499         }
502 Frame {
503         // frames contain the local variables, context and continuation of a function or method invocation.
504         // since some Frames are deleted instead of garbage collected, it is too
505         // dangerous to allow access to them. Dangling pointers could result.
506         shallowCopy { ^this }
507         inspectorClass { ^FrameInspector }
509         storeOn { arg stream; stream << "nil"; }
510         archiveAsCompileString { ^true }
511         checkCanArchive { "cannot archive Frames".warn }
514 DebugFrame {
515         var <functionDef, <args, <vars, <caller, <context, <address;
516         // Object.getBackTrace returns one of these.
517         // 'functionDef' is the FunctionDef for this function or method.
518         // 'args' the values of the arguments to the function call.
519         // 'vars' the values of the local variables.
520         // 'caller' points to another DebugFrame for the caller to this function.
521         // 'context' points to another DebugFrame for the frame lexically enclosing this one.
522         // 'address' memory address of the actual frame object.
523         asString { ^"DebugFrame of " ++ functionDef.asString }
526 RawPointer {
527         // class used to hold raw pointers from the
528         // host environment.
531 Interpreter {
532         // The interpreter defines a context in which interactive commands
533         // are compiled.
535         var <>cmdLine; // place holder for text executed from a worksheet
536         var context; // faked interpreter context frame. Don't mess with it.
538         // a-z are predefined variables for use by the interactive context.
539         // They are read+write so that programmatic methods can
540         // get and alter the values that the interpreter has access to.
541         var <>a, <>b, <>c, <>d, <>e, <>f, <>g, <>h, <>i, <>j;
542         var <>k, <>l, <>m, <>n, <>o, <>p, <>q, <>r, <>s, <>t;
543         var <>u, <>v, <>w, <>x, <>y, <>z;
545         var <>codeDump, <>preProcessor;
547         *new { ^this.shouldNotImplement(thisMethod) }
549         interpretCmdLine {
550                 ^this.compile(cmdLine).value;
551         }
553         interpretPrintCmdLine {
554                 var res, func, code = cmdLine, doc = Document.current;
555                 "\n".post;
556                 preProcessor !? { cmdLine = preProcessor.value(cmdLine, this) };
557                 func = this.compile(cmdLine);
558                 if(doc.tryPerform(\dataptr).notNil) {
559                         thisProcess.nowExecutingPath = doc.tryPerform(\path);
560                 };
561                 res = func.value;
562                 thisProcess.nowExecutingPath = nil;
563                 codeDump.value(code, res, func, this);
564                 res.postln;
565         }
567         interpret { arg string ... args;
568                 // compile, evaluate
569                 cmdLine = string;
570                 ^this.compile(string).valueArray(args);
571         }
572         interpretPrint { arg string ... args;
573                 // compile, evaluate, print
574                 cmdLine = string;
575                 ^this.compile(string).valueArray(args).postln;
576         }
577         compile { arg string;
578                 _CompileExpression
579                 // compiles string and returns a Function.
580                 // the string must be a valid expression.
581                 // You cannot compile a class definition this way.
582                 // This method is not implemented in SCPlay.
583                 ^nil
584         }
586         clearAll {
587                 a = b = c = d = e = f = g = h = i = j = k = l = m =
588                 n = o = p = q = r = s = t = u = v = w = x = y = z = nil;
589         }
591         executeFile { arg pathName ... args;
592                 var     result, saveExecutingPath = thisProcess.nowExecutingPath;
593                 if (File.exists(pathName).not) {
594                         "file \"%\" does not exist.\n".postf(pathName);
595                         ^nil
596                 };
597                 thisProcess.nowExecutingPath = pathName;
598                 protect {
599                         result = this.compileFile(pathName).valueArray(args)
600                 } { |exception|
601                                 exception !? { exception.path = pathName };
602                                 thisProcess.nowExecutingPath = saveExecutingPath
603                 };
604                 ^result
605         }
607         compileFile { arg pathName;
608                 var file, text;
609                 file = File.new(pathName, "r");
610                 if (file.isNil, {
611                         error("file open failed\n");
612                         ^nil
613                 });
614                 text = file.readAllString;
615                 file.close;
616                 if (text.beginsWith("#!"), {
617                         // comment out shebang to preserve line count
618                         text.overWrite("//");
619                 });
620                 ^this.compile(text)
621         }
623         // PRIVATE
624         functionCompileContext {
625                 // compiler uses this method as a fake context in which to compile
626                 // the user's function.
627                 // Do not edit this method!
629                 {}      // this forces the compiler to generate a heap allocated frame rather than
630                         // a frame on the stack
631         }
632         shallowCopy { ^this }