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.
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.
24 // superclass is stored as a symbol to allow forward reference during compilation
28 isMetaClass { ^this.class === Class }
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 }, {
44 classesInited.add(aClass);
45 if(aClass.subclasses.notNil,{
46 aClass.subclasses.do({ arg class; this.initClassTree(class); });
51 *allClasses { _AllClasses }
53 findMethod { arg methodName;
54 if ( methods.notNil, {
55 ^methods.detect({ arg method; method.name == methodName });
58 findRespondingMethodFor { arg methodName;
59 this.superclassesDo { arg class;
60 var method = class.findMethod(methodName);
61 method !? { ^method };
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 }
73 superclassesDo { arg function;
75 while { class.notNil } {
76 function.value(class);
77 class = class.superclass;
81 dumpByteCodes { arg methodName;
83 meth = this.findMethod(methodName);
84 if (meth.notNil, { meth.dumpByteCodes },{ Post << methodName << " not found.\n"; });
87 dumpClassSubtree { _DumpClassSubtree }
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;
93 numargs = meth.argNames.size - 1;
97 meth.argNames.do({ arg name, i;
98 if (i > 0, { // skip 'this'
112 printOn { arg stream;
113 stream << "class " << name;
115 storeOn { arg stream;
118 archiveAsCompileString { ^true }
121 //should cache this in Library or classvar
122 //can't add instance variables to Class
123 ^this.name.asString.findHelpFile.notNil
126 ^this.name.asString.findHelpFile
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
139 shallowCopy { ^this }
140 //listInstances { _ListInstancesOf }
141 //traceAnyPathToAllInstancesOf { _TraceAnyPathToAllInstancesOf }
144 this.filenameSymbol.asString.openTextFile(this.charPos, -1);
148 start = this.classVarIndex;
149 end = start + this.classVarNames.size;
150 ^thisProcess.instVarAt(0).copyRange(start, end)
152 inspectorClass { ^ClassInspector }
153 findReferences { arg aSymbol, references;
154 methods.do({ arg meth;
155 references = meth.findReferences(aSymbol, references)
159 *findAllReferences { arg aSymbol;
160 // this will not find method calls that are compiled with special byte codes such as 'value'.
162 Class.allClasses.do({ arg class;
163 references = class.findReferences(aSymbol, references);
169 list = subclasses.copy;
170 subclasses.do({ arg class; list = list ++ class.allSubclasses; });
175 this.superclass.superclassesDo { arg class; list = list.add(class) }
182 // A Process is a runtime environment.
183 var classVars, <interpreter;
184 var curThread, <mainThread;
186 var <>nowExecutingPath;
188 // SCVersion.sc overrides these for Main
189 *scVersionMajor { ^123 }
190 *scVersionMinor { ^0 }
191 *scVersionPostfix { ^"unknown" }
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;
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
213 // This method is called when 'Run Main' is chosen from the menu.
214 // Override in class 'Main' to do whatever you want.
217 // This method is called when 'Stop Main' is chosen from the menu.
218 // Override in class 'Main' to do whatever you want.
221 // This method is called before recompiling or quitting.
222 // Override in class 'Main' to do whatever you want.
224 NetAddr.disconnectAll;
228 tick { // called repeatedly by SCVirtualMachine::doPeriodicTask
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}) {
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;
252 method = class.findMethod(words.at(1).asSymbol);
254 method.filenameSymbol.asString.openTextFile(method.charPos, -1);
258 class = string.asSymbol.asClass;
260 class = class.classRedirect;
261 class.filenameSymbol.asString.openTextFile(class.charPos, -1);
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;
274 method = class.findMethod(words.at(1).asSymbol);
276 method.filenameSymbol.asString.openWinTextFile(method.charPos, -1);
280 class = string.asSymbol.asClass;
282 class = class.classRedirect;
283 class.filenameSymbol.asString.openWinTextFile(class.charPos, -1);
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);
302 Post << "\nNo references to '" << name << "'.\n";
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;
313 Post << "\nNo implementations of ''.\n";
316 if (text[0].toLower != text[0]) {
317 // user pressed the wrong key. DWIM.
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, {
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)"; });
332 out << method.argNames.at(0);
333 if (name.asString.at(0).isAlpha, {
334 out << "." << name << "(";
335 method.argNames.do({ arg argName, i;
337 if (i != 1, { out << ", " });
343 out << " " << name << " ";
344 out << method.argNames.at(1);
354 Post << "\nNo implementations of '" << name << "'.\n";
358 interpreter.cmdLine = namestring;
362 out.collection.newTextWindow(name.asString);
367 // interpret some text from the command line
368 interpreter.interpretCmdLine;
371 interpretPrintCmdLine {
372 // interpret some text from the command line and print result
373 interpreter.interpretPrintCmdLine;
376 interpretPrintSelectedText {
377 interpreter.cmdLine = this.getCurrentSelection;
378 interpreter.interpretPrintCmdLine;
382 this.getCurrentSelection.help
387 shallowCopy { ^this }
389 *elapsedTime { _ElapsedTime }
391 storeOn { arg stream;
392 stream << "thisProcess";
394 archiveAsCompileString { ^true }
396 prSchedulerQueue { ^schedulerQueue }
401 var raw1, raw2, <code, <selectors, <constants, <prototypeFrame, <context, <argNames, <varNames;
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 }
417 // this is only legal for closed functions.
418 _FunctionDefAsFunction
419 ^this.primitiveFailed
423 _FunctionDefDumpContexts
425 inspectorClass { ^FunctionDefInspector }
427 findReferences { arg aSymbol, references;
429 lits = selectors.asArray;
430 if (lits.includes(aSymbol), {
431 references = references.add(this);
434 if (item.isKindOf(FunctionDef), {
435 references = item.findReferences(aSymbol, references)
440 storeOn { arg stream;
443 checkCanArchive { "cannot archive FunctionDefs".warn }
444 archiveAsCompileString { ^true }
446 argumentString { arg withDefaultValues=true;
448 if(argNames.isNil) { ^nil };
450 last = argNames.size-1;
451 argNames.do { |name, i|
454 if(withDefaultValues and: { value = prototypeFrame[i]; value.notNil }) {
455 res = res ++ " = " ++ value.asCompileString
457 if(i != last) { res = res ++ ", " };
463 var argNames, argVals;
464 argNames = this.argNames;
465 argVals = this.prototypeFrame.keep(argNames.size);
466 ^().putPairs([argNames, argVals].flop.flatten)
471 Method : FunctionDef {
472 var <ownerClass, <name, <primitiveName;
473 var <filenameSymbol, <charPos;
476 this.filenameSymbol.asString.openTextFile(this.charPos, -1);
479 //should cache this in Library or classvar
480 //can't add instance variables to Class
481 ^this.name.asString.findHelpFile.notNil
484 HelpBrowser.openHelpForMethod(this);
486 inspectorClass { ^MethodInspector }
487 storeOn { arg stream;
488 stream << ownerClass.name << ".findMethod(" << name.asCompileString << ")"
490 archiveAsObject { ^true }
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
500 if (item.isKindOf(FunctionDef), {
501 functionRefs = item.findReferences(aSymbol, functionRefs);
504 functionRefs.notNil.if({references = references.add(this)});
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 }
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 }
534 // class used to hold raw pointers from the
539 // The interpreter defines a context in which interactive commands
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) }
557 ^this.compile(cmdLine).value;
560 interpretPrintCmdLine {
561 var res, func, code = cmdLine, doc = Document.current, ideClass = \ScIDE.asClass;
563 preProcessor !? { cmdLine = preProcessor.value(cmdLine, this) };
564 func = this.compile(cmdLine);
565 if (ideClass.notNil) {
566 thisProcess.nowExecutingPath = ideClass.currentPath
568 if(doc.tryPerform(\dataptr).notNil) {
569 thisProcess.nowExecutingPath = doc.tryPerform(\path);
573 thisProcess.nowExecutingPath = nil;
574 codeDump.value(code, res, func, this);
578 interpret { arg string ... args;
581 ^this.compile(string).valueArray(args);
583 interpretPrint { arg string ... args;
584 // compile, evaluate, print
586 ^this.compile(string).valueArray(args).postln;
588 compile { arg string;
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.
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;
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);
608 thisProcess.nowExecutingPath = pathName;
610 result = this.compileFile(pathName).valueArray(args)
612 exception !? { exception.path = pathName };
613 thisProcess.nowExecutingPath = saveExecutingPath
618 compileFile { arg pathName;
620 file = File.new(pathName, "r");
622 error("file open failed\n");
625 text = file.readAllString;
627 if (text.beginsWith("#!"), {
628 // comment out shebang to preserve line count
629 text.overWrite("//");
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
643 shallowCopy { ^this }