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
129 this.name.asString.openHelpFile
132 shallowCopy { ^this }
133 //listInstances { _ListInstancesOf }
134 //traceAnyPathToAllInstancesOf { _TraceAnyPathToAllInstancesOf }
137 this.filenameSymbol.asString.openTextFile(this.charPos, -1);
141 start = this.classVarIndex;
142 end = start + this.classVarNames.size;
143 ^thisProcess.instVarAt(0).copyRange(start, end)
145 inspectorClass { ^ClassInspector }
146 findReferences { arg aSymbol, references;
147 methods.do({ arg meth;
148 references = meth.findReferences(aSymbol, references)
152 *findAllReferences { arg aSymbol;
153 // this will not find method calls that are compiled with special byte codes such as 'value'.
155 Class.allClasses.do({ arg class;
156 references = class.findReferences(aSymbol, references);
162 list = subclasses.copy;
163 subclasses.do({ arg class; list = list ++ class.allSubclasses; });
168 this.superclass.superclassesDo { arg class; list = list.add(class) }
175 // A Process is a runtime environment.
176 var classVars, <interpreter;
177 var curThread, mainThread;
179 var <>nowExecutingPath;
181 // SCVersion.sc overrides these for Main
182 *scVersionMajor { ^123 }
183 *scVersionMinor { ^0 }
184 *scVersionPostfix { ^"unknown" }
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;
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
206 // This method is called when 'Run Main' is chosen from the menu.
207 // Override in class 'Main' to do whatever you want.
210 // This method is called when 'Stop Main' is chosen from the menu.
211 // Override in class 'Main' to do whatever you want.
214 // This method is called before recompiling or quitting.
215 // Override in class 'Main' to do whatever you want.
217 NetAddr.disconnectAll;
221 tick { // called repeatedly by SCVirtualMachine::doPeriodicTask
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}) {
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;
245 method = class.findMethod(words.at(1).asSymbol);
247 method.filenameSymbol.asString.openTextFile(method.charPos, -1);
251 class = string.asSymbol.asClass;
253 class = class.classRedirect;
254 class.filenameSymbol.asString.openTextFile(class.charPos, -1);
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;
267 method = class.findMethod(words.at(1).asSymbol);
269 method.filenameSymbol.asString.openWinTextFile(method.charPos, -1);
273 class = string.asSymbol.asClass;
275 class = class.classRedirect;
276 class.filenameSymbol.asString.openWinTextFile(class.charPos, -1);
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);
295 Post << "\nNo references to '" << name << "'.\n";
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;
306 Post << "\nNo implementations of ''.\n";
309 if (text[0].toLower != text[0]) {
310 // user pressed the wrong key. DWIM.
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, {
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)"; });
325 out << method.argNames.at(0);
326 if (name.asString.at(0).isAlpha, {
327 out << "." << name << "(";
328 method.argNames.do({ arg argName, i;
330 if (i != 1, { out << ", " });
336 out << " " << name << " ";
337 out << method.argNames.at(1);
347 Post << "\nNo implementations of '" << name << "'.\n";
351 interpreter.cmdLine = namestring;
355 out.collection.newTextWindow(name.asString);
360 // interpret some text from the command line
361 interpreter.interpretCmdLine;
364 interpretPrintCmdLine {
365 // interpret some text from the command line and print result
366 interpreter.interpretPrintCmdLine;
369 interpretPrintSelectedText {
370 interpreter.cmdLine = this.getCurrentSelection;
371 interpreter.interpretPrintCmdLine;
375 this.getCurrentSelection.openHelpFile
380 shallowCopy { ^this }
382 *elapsedTime { _ElapsedTime }
384 storeOn { arg stream;
385 stream << "thisProcess";
387 archiveAsCompileString { ^true }
389 prSchedulerQueue { ^schedulerQueue }
394 var raw1, raw2, <code, <selectors, <constants, <prototypeFrame, <context, <argNames, <varNames;
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 }
410 // this is only legal for closed functions.
411 _FunctionDefAsFunction
412 ^this.primitiveFailed
416 _FunctionDefDumpContexts
418 inspectorClass { ^FunctionDefInspector }
420 findReferences { arg aSymbol, references;
422 lits = selectors.asArray;
423 if (lits.includes(aSymbol), {
424 references = references.add(this);
427 if (item.isKindOf(FunctionDef), {
428 references = item.findReferences(aSymbol, references)
433 storeOn { arg stream;
436 checkCanArchive { "cannot archive FunctionDefs".warn }
437 archiveAsCompileString { ^true }
439 argumentString { arg withDefaultValues=true;
441 if(argNames.isNil) { ^nil };
443 last = argNames.size-1;
444 argNames.do { |name, i|
447 if(withDefaultValues and: { value = prototypeFrame[i]; value.notNil }) {
448 res = res ++ " = " ++ value.asCompileString
450 if(i != last) { res = res ++ ", " };
456 var argNames, argVals;
457 argNames = this.argNames;
458 argVals = this.prototypeFrame;
459 ^().putPairs([argNames, argVals].flop.flat)
464 Method : FunctionDef {
465 var <ownerClass, <name, <primitiveName;
466 var <filenameSymbol, <charPos;
469 this.filenameSymbol.asString.openTextFile(this.charPos, -1);
472 //should cache this in Library or classvar
473 //can't add instance variables to Class
474 ^this.name.asString.findHelpFile.notNil
477 HelpBrowser.openHelpForMethod(this);
479 inspectorClass { ^MethodInspector }
480 storeOn { arg stream;
481 stream << ownerClass.name << ".findMethod(" << name.asCompileString << ")"
483 archiveAsObject { ^true }
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
493 if (item.isKindOf(FunctionDef), {
494 functionRefs = item.findReferences(aSymbol, functionRefs);
497 functionRefs.notNil.if({references = references.add(this)});
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 }
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 }
527 // class used to hold raw pointers from the
532 // The interpreter defines a context in which interactive commands
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) }
550 ^this.compile(cmdLine).value;
553 interpretPrintCmdLine {
554 var res, func, code = cmdLine, doc = Document.current;
556 preProcessor !? { cmdLine = preProcessor.value(cmdLine, this) };
557 func = this.compile(cmdLine);
558 if(doc.tryPerform(\dataptr).notNil) {
559 thisProcess.nowExecutingPath = doc.tryPerform(\path);
562 thisProcess.nowExecutingPath = nil;
563 codeDump.value(code, res, func, this);
567 interpret { arg string ... args;
570 ^this.compile(string).valueArray(args);
572 interpretPrint { arg string ... args;
573 // compile, evaluate, print
575 ^this.compile(string).valueArray(args).postln;
577 compile { arg string;
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.
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;
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);
597 thisProcess.nowExecutingPath = pathName;
599 result = this.compileFile(pathName).valueArray(args)
601 exception !? { exception.path = pathName };
602 thisProcess.nowExecutingPath = saveExecutingPath
607 compileFile { arg pathName;
609 file = File.new(pathName, "r");
611 error("file open failed\n");
614 text = file.readAllString;
616 if (text.beginsWith("#!"), {
617 // comment out shebang to preserve line count
618 text.overWrite("//");
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
632 shallowCopy { ^this }