2 classvar <dependantsDictionary, currentEnvironment, topEnvironment, <uniqueMethods;
6 *new { arg maxSize = 0;
9 // creates a new instance that can hold up to maxSize
10 // indexable slots. the indexed size will be zero.
11 // to actually put things in the object you need to
14 *newCopyArgs { arg ... args;
15 _BasicNewCopyArgsToInstVars
17 // creates a new instance that can hold up to maxSize
18 // indexable slots. the indexed size will be zero.
19 // to actually put things in the object you need to
23 // debugging and diagnostics
25 post { this.asString.post }
26 postln { this.asString.postln; }
27 postc { this.asString.postc }
28 postcln { this.asString.postcln; }
29 postcs { this.asCompileString.postln }
30 totalFree { _TotalFree }
31 largestFreeBlock { _LargestFreeBlock }
32 gcDumpGrey { _GCDumpGrey }
33 gcDumpSet { arg set; _GCDumpSet }
35 gcSanity { _GCSanity }
36 canCallOS { _CanCallOS }
43 do { arg function; function.value(this, 0) }
44 generate { arg function, state; this.do(function); ^state }
45 //reverseDo { arg function; function.value(this, 0) }
48 class { _ObjectClass; ^this.primitiveFailed }
49 isKindOf { arg aClass; _ObjectIsKindOf; ^this.primitiveFailed }
50 isMemberOf { arg aClass; _ObjectIsMemberOf; ^this.primitiveFailed }
51 respondsTo { arg aSymbol; _ObjectRespondsTo; ^this.primitiveFailed }
58 perform { arg selector ... args;
62 performList { arg selector, arglist;
67 // perform only if Function. see Function-functionPerformList
71 // super.perform(selector,arg) doesn't do what you might think.
72 // \perform would be looked up in the superclass, not the selector you are interested in.
73 // Hence these methods, which look up the selector in the superclass.
74 // These methods must be called with this as the receiver.
75 superPerform { arg selector ... args;
79 superPerformList { arg selector, arglist;
84 tryPerform { arg selector ... args;
85 ^if(this.respondsTo(selector),{
86 this.performList(selector,args)
89 multiChannelPerform { arg selector ... args;
90 ^flop([this, selector] ++ args).collect { |item|
91 performList(item[0], item[1], item[2..])
95 performWithEnvir { |selector, envir|
97 var method = this.class.findRespondingMethodFor(selector);
98 if(method.isNil) { ^this.doesNotUnderstand(selector) };
100 envir = method.makeEnvirFromArgs.putAll(envir);
102 argNames = method.argNames.drop(1);
103 args = envir.atAll(argNames);
104 ^this.performList(selector, args)
107 performKeyValuePairs { |selector, pairs|
108 ^this.performWithEnvir(selector, ().putPairs(pairs))
112 copy { ^this.shallowCopy }
113 contentsCopy { ^this.shallowCopy }
114 shallowCopy { _ObjectShallowCopy; ^this.primitiveFailed }
116 // if object is immutable then return a shallow copy, else return receiver.
117 _ObjectCopyImmutable;
118 ^this.primitiveFailed
123 ^this.primitiveFailed
126 ^Array.fill(n, { this.copy });
137 valueArrayEnvir { ^this }
139 // equality, identity
140 == { arg obj; ^this === obj }
141 != { arg obj; ^not(this == obj) }
142 === { arg obj; _Identical; ^this.primitiveFailed }
143 !== { arg obj;_NotIdentical; ^this.primitiveFailed }
144 equals { arg that, properties;
145 ^that.respondsTo(properties) and: {
146 properties.every { |selector| this.perform(selector) == that.perform(selector) }
149 compareObject { arg that, instVarNames;
150 if(this === that,{ ^true });
151 // possibly ok if one of us isKindOf the other
152 if(this.class !== that.class,{ ^false });
153 if(instVarNames.notNil,{
154 instVarNames.do({ |varname|
155 if(this.instVarAt(varname) != that.instVarAt(varname),{
160 this.instVarSize.do({ arg i;
161 if(this.instVarAt(i) != that.instVarAt(i),{ ^false });
166 instVarHash { arg instVarNames;
167 var res = this.class.hash;
168 var indices = if(instVarNames.notNil) {
169 instVarNames.collect(this.slotIndex(_))
171 (0..this.instVarSize-1)
174 var obj = this.instVarAt(i);
175 res = res bitXor: obj.hash;
180 basicHash { _ObjectHash; ^this.primitiveFailed }
181 hash { _ObjectHash; ^this.primitiveFailed }
182 identityHash { _ObjectHash; ^this.primitiveFailed }
184 // create an association
185 -> { arg obj; ^Association.new(this, obj) }
190 first { arg inval; this.reset; ^this.next(inval) }
191 iter { ^OneShotStream(this) }
195 removedFromScheduler { ^this }
197 embedInStream { ^this.yield; }
201 inval = this.embedInStream(inval);
210 item = this.next(inval);
211 if (item.isNil) { nil.alwaysYield };
217 repeat { arg repeats = inf; ^Pn(this, repeats).asStream }
218 loop { ^this.repeat(inf) }
223 composeEvents { arg event; ^event.copy }
230 ?? { arg obj; ^this }
231 !? { arg obj; ^obj.value }
238 isSequenceableCollection { ^false }
241 containsSeqColl { ^false }
242 isValidUGenInput { ^false }
243 isException { ^false }
244 isFunction { ^false }
246 matchItem {|item| ^this === item }
249 ^this.trueAt(key).not
252 pointsTo { arg obj; _ObjectPointsTo; ^this.primitiveFailed }
253 mutable { _ObjectIsMutable; ^this.primitiveFailed }
254 frozen { _ObjectIsPermanent; ^this.primitiveFailed }
258 thisProcess.nowExecutingPath = nil;
264 PrimitiveFailedError(this).throw;
267 error(this.asString);
271 subclassResponsibility { arg method;
272 SubclassResponsibilityError(this, method, this.class).throw;
274 doesNotUnderstand { arg selector ... args;
275 DoesNotUnderstandError(this, selector, args).throw;
277 shouldNotImplement { arg method;
278 ShouldNotImplementError(this, method, this.class).throw;
280 outOfContextReturn { arg method, result;
281 OutOfContextReturnError(this, method, result).throw;
283 immutableError { arg value;
284 ImmutableError(this, value).throw;
287 deprecated { arg method, alternateMethod;
288 DeprecatedError(this, method, alternateMethod, this.class).throw;
291 mustBeBoolean { MustBeBooleanError(nil, this).throw; }
292 notYetImplemented { NotYetImplementedError(nil, this).throw; }
294 dumpBackTrace { _DumpBackTrace }
295 getBackTrace { _GetBackTrace }
297 if (Error.handling) {
298 error("throw during error handling!\n");
302 thisThread.handleError(this);
307 species { ^this.class }
308 asCollection { ^[this] }
309 asSymbol { ^this.asString.asSymbol }
310 asString { arg limit = 512;
313 string = String.streamContentsLimit({ arg stream; this.printOn(stream); }, limit);
314 if (string.size >= limit, { ^(string ++ "...etc..."); });
319 ^String.streamContents({ arg stream; this.storeOn(stream); });
322 cs { ^this.asCompileString }
324 printClassNameOn { arg stream;
326 title = this.class.name.asString;
327 stream << if((title @ 0).isVowel, { "an " }, { "a " }) << title;
329 printOn { arg stream;
330 this.printClassNameOn(stream);
332 storeOn { arg stream;
333 stream << this.class.name;
334 this.storeParamsOn(stream);
335 this.storeModifiersOn(stream);
337 storeParamsOn { arg stream;
338 var args = this.storeArgs;
340 stream << "(" <<<* this.simplifyStoreArgs(args) << ")";
345 simplifyStoreArgs { arg args;
346 var res = Array.new, newMethod, methodArgs;
347 newMethod = this.class.class.findRespondingMethodFor(\new);
348 methodArgs = newMethod.prototypeFrame.drop(1);
349 args.size.reverseDo { |i|
350 if(methodArgs[i] != args[i]) {
357 storeModifiersOn { arg stream;}
360 as { arg aSimilarClass; ^aSimilarClass.newFrom(this) }
361 dereference { ^this } // see Ref::dereference
362 reference { ^Ref.new(this) }
363 asRef { ^Ref.new(this) }
364 // asArray { ^Array.with(this) }
365 asArray { ^this.asCollection.asArray }
366 asSequenceableCollection { ^this.asArray }
370 deepCollect { arg depth, function; ^function.value(this, 0) }
374 bubble { arg depth=0, levels=1;
375 if (levels <= 1) { ^[this] };
376 ^[this.bubble(depth,levels-1)]
379 // compatibility with sequenceable collection
381 obtain { arg index, default; ^if(index == 0) { this } { default } }
383 instill { arg index, item, default;
384 ^if(index == 0) { item } {
385 this.asArray.instill(index, item, default)
389 // FunctionList support
390 addFunc { arg ... functions;
391 ^FunctionList([this] ++ functions)
393 removeFunc { arg function; if(this === function) { ^nil } }
394 replaceFunc { arg find, replace; if(this === find) { ^replace } }
395 addFuncTo { arg variableName ... functions;
396 this.perform(variableName.asSetter, this.perform(variableName).addFunc(*functions))
398 removeFuncFrom { arg variableName, function;
399 this.perform(variableName).removeFunc(function)
404 // compiler magic: the compiler inlines the following loop
405 // thus an uninlinable while can be implemented using while itself
406 while({ this.value }, {
410 switch { arg ... cases;
411 cases.pairsDo { | test, trueFunc |
412 if (this == test.value) { ^trueFunc.value };
414 if (cases.size.odd) { ^cases.last.value };
421 ^this.primitiveFailed
425 ^this.primitiveFailed
427 yieldAndReset { arg reset = true;
428 _RoutineYieldAndReset
429 ^this.primitiveFailed
432 var time = thisThread.beats;
433 while { thisThread.beats - time < val } { this.value.yield }
436 // dependancy support
437 *initClass { dependantsDictionary = IdentityDictionary.new(4); }
439 ^dependantsDictionary.at(this) ?? { IdentitySet.new };
441 changed { arg what ... moreArgs;
442 dependantsDictionary.at(this).copy.do({ arg item;
443 item.update(this, what, *moreArgs);
446 addDependant { arg dependant;
448 theDependants = dependantsDictionary.at(this);
449 if(theDependants.isNil,{
450 theDependants = IdentitySet.new.add(dependant);
451 dependantsDictionary.put(this, theDependants);
453 theDependants.add(dependant);
456 removeDependant { arg dependant;
458 theDependants = dependantsDictionary.at(this);
459 if (theDependants.notNil, {
460 theDependants.remove(dependant);
461 if (theDependants.size == 0, {
462 dependantsDictionary.removeAt(this);
467 this.releaseDependants;
470 dependantsDictionary.removeAt(this);
472 update { arg theChanged, theChanger; // respond to a change in a model
476 // instance specific method support
477 addUniqueMethod { arg selector, function;
479 if(function.isKindOf(Function).not) {
480 Error("A method must be defined using a function").throw
482 if(uniqueMethods.isNil, { uniqueMethods = IdentityDictionary.new });
483 methodDict = uniqueMethods.at(this);
484 if (methodDict.isNil, {
485 methodDict = IdentityDictionary.new;
486 uniqueMethods.put(this, methodDict);
488 methodDict.put(selector, function);
490 removeUniqueMethods {
491 if (uniqueMethods.notNil, {
492 uniqueMethods.removeAt(this);
495 removeUniqueMethod { arg selector;
497 if (uniqueMethods.notNil, {
498 methodDict = uniqueMethods.at(this);
499 if (methodDict.notNil, {
500 methodDict.removeAt(selector);
501 if (methodDict.size < 1, {
502 uniqueMethods.removeAt(this);
508 inspect { ^this.inspectorClass.new(this) }
509 inspectorClass { ^ObjectInspector }
511 // finds the inspector for this object, if any.
512 ^Inspector.inspectorFor(this)
516 // virtual machine debugging...
517 crash { _HostDebugger } // for serious problems..
518 stackDepth { _StackDepth }
519 dumpStack { _DumpStack }
520 dumpDetailedBackTrace { _DumpDetailedBackTrace }
525 ^this.primitiveFailed
528 // Math protocol support
529 // translate these operators to names the code generator can safely generate in C++
530 & { arg that; ^bitAnd(this, that) }
531 | { arg that; ^bitOr(this, that) }
532 % { arg that; ^mod(this, that) }
533 ** { arg that; ^pow(this, that) }
534 << { arg that; ^leftShift(this, that) }
535 >> { arg that; ^rightShift(this, that) }
536 +>> { arg that; ^unsignedRightShift(this, that) }
537 <! { arg that; ^firstArg(this, that) }
539 asInt { ^this.asInteger }
541 blend { arg that, blendFrac = 0.5;
542 // blendFrac should be from zero to one
543 ^this + (blendFrac * (that - this));
546 blendAt { arg index, method='clipAt';
547 var iMin = index.roundUp.asInteger - 1;
548 ^blend(this.perform(method, iMin), this.perform(method, iMin+1), absdif(index, iMin));
551 blendPut { arg index, val, method='wrapPut';
552 var iMin = index.floor.asInteger;
553 var ratio = absdif(index, iMin);
554 this.perform(method, iMin, val * (1-ratio));
555 this.perform(method, iMin + 1, val * ratio);
559 fuzzyEqual { arg that, precision=1.0; ^max(0.0, 1.0 - (abs(this - that)/precision)) }
564 pair { arg that; ^[this, that] }
570 list = list.add(a.asArray ++ b)
578 awake { arg beats, seconds, clock;
580 time = seconds; // prevent optimization
583 beats_ { } // for PauseStream
584 clock_ { } // for Clock
586 // catch binary operators failure
587 performBinaryOpOnSomething { arg aSelector, thing, adverb;
588 if (aSelector === '==', {
591 if (aSelector === '!=', {
594 BinaryOpFailureError(this, aSelector, [thing, adverb]).throw;
597 performBinaryOpOnSimpleNumber { arg aSelector, thing, adverb;
598 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
600 performBinaryOpOnSignal { arg aSelector, thing, adverb;
601 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
603 performBinaryOpOnComplex { arg aSelector, thing, adverb;
604 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
606 performBinaryOpOnSeqColl { arg aSelector, thing, adverb;
607 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
609 performBinaryOpOnUGen { arg aSelector, thing, adverb;
610 ^this.performBinaryOpOnSomething(aSelector, thing, adverb)
613 writeDefFile { arg name, dir, overwrite = (true);
615 StartUp.defer { // make sure the synth defs are written to the right path
617 dir = dir ? SynthDef.synthDefDir;
618 if (name.isNil) { error("missing SynthDef file name") } {
619 name = dir +/+ name ++ ".scsyndef";
620 if(overwrite or: { pathMatch(name).isEmpty })
622 file = File(name, "w");
624 AbstractMDPlugin.clearMetadata(name);
625 this.asArray.writeDef(file);
635 isInputUGen { ^false }
636 isOutputUGen { ^false }
637 isControlUGen { ^false }
639 asUGenInput { ^this }
640 asControlInput { ^this }
641 asAudioRateInput { ^if(this.rate != \audio) { K2A.ar(this) } { this } }
644 // these are the same as new and newCopyArgs, but should not be overridden by any class.
645 *prNew { arg maxSize = 0;
647 ^this.primitiveFailed
648 // creates a new instance that can hold up to maxSize
649 // indexable slots. the indexed size will be zero.
650 // to actually put things in the object you need to
653 *prNewCopyArgs { arg ... args;
654 _BasicNewCopyArgsToInstVars
655 ^this.primitiveFailed
656 // creates a new instance which holds the args as slots
660 // these are dangerous operations as they break encapsulation and
661 // can allow access to slots that should not be accessed because they are private to the
662 // virtual machine, such as Frame objects.
664 // see counterparts to these in ArrayedCollection
669 // index can be an integer or symbol.
670 ^this.instVarAt(index);
672 slotPut { arg index, value;
673 // index can be an integer or symbol.
674 ^this.instVarPut(index, value);
677 // index must be an integer.
678 ^this.class.instVarNames.at(index)
681 // key must be a symbol.
682 ^this.class.instVarNames.indexOf(key)
684 slotsDo { arg function;
685 this.slotSize.do {|i|
686 function.value(this.slotKey(i), this.slotAt(i), i);
689 slotValuesDo { arg function;
690 this.slotSize.do {|i|
691 function.value(this.slotAt(i), i);
695 // getSlots and setSlots will be used for a new implementation of asCompileString.
696 // getSlots stores the keys and values so that if the instance
697 // variable order changes, setSlots they will still set the right one.
700 array = Array.new(this.slotSize * 2);
701 this.slotSize.do {|i|
702 array.add(this.slotKey(i));
703 array.add(this.slotAt(i));
707 setSlots { arg array;
708 array.pairsDo {|key, value|
709 this.slotPut(key, value);
713 instVarSize { _InstVarSize; ^this.primitiveFailed }
714 instVarAt { arg index;
715 // index can be an integer or symbol.
717 ^this.primitiveFailed;
719 instVarPut { arg index, item;
720 // index can be an integer or symbol.
722 ^this.primitiveFailed;
725 //////////// ARCHIVING ////////////
727 writeArchive { arg pathname;
728 ^this.writeTextArchive(pathname)
730 *readArchive { arg pathname;
731 ^this.readTextArchive(pathname)
739 archiveAsCompileString { ^false }
740 archiveAsObject { ^this.archiveAsCompileString.not }
744 writeTextArchive { arg pathname;
745 var text = this.asTextArchive;
746 var file = File(pathname, "w");
752 MethodError("Could not open file % for writing".format(pathname.asCompileString), this).throw;
755 *readTextArchive { arg pathname;
759 var objects, list, stream, firsttime = true;
761 if (this.archiveAsCompileString) {
762 this.checkCanArchive;
763 ^this.asCompileString ++ "\n"
766 objects = IdentityDictionary.new;
768 this.getContainedObjects(objects);
770 stream = CollStream.new;
771 stream << "var o, p;\n";
773 list = List.newClear(objects.size);
774 objects.keysValuesDo {|obj, index| list[index] = obj };
779 if (i != 0) { stream << ", "; };
780 if ((i & 3) == 0) { stream << "\n\t" };
782 if (obj.archiveAsCompileString) {
783 stream << obj.asCompileString;
785 size = obj.indexedSize;
786 stream << obj.class.name << ".prNew";
788 stream << "(" << size << ")"
792 stream << "\n];\np = [";
797 if (obj.archiveAsCompileString.not) {
798 slots = obj.getSlots;
799 if (slots.size > 0) {
800 if (firsttime.not) { stream << ", "; };
802 stream << "\n\t// " << obj.class.name;
804 stream << i << ", [ ";
805 if (obj.isKindOf(ArrayedCollection)) {
808 if (j != 0) { stream << ", "; };
809 if ((j != 0) && ((j & 3) == 0)) { stream << "\n\t\t" };
810 index = objects[slot];
812 stream << slot.asCompileString;
814 stream << "o[" << index << "]";
818 slots.pairsDo {|key, slot, j|
820 if (j != 0) { stream << ", "; };
821 if ((j != 0) && ((j & 3) == 0)) { stream << "\n\t\t" };
822 stream << key << ": ";
823 index = objects[slot];
825 stream << slot.asCompileString;
827 stream << "o[" << index << "]";
837 stream << "prUnarchive(o,p);\n";
840 getContainedObjects { arg objects;
841 if (objects[this].notNil) {^this};
842 objects[this] = objects.size;
844 if (this.archiveAsCompileString.not) {
845 this.slotsDo {|key, slot|
846 if (slot.archiveAsObject) {
847 slot.getContainedObjects(objects);
853 // old binary archiving
854 // this will break if the instance vars change !
856 writeBinaryArchive { arg pathname;
858 ^this.primitiveFailed;
860 *readBinaryArchive { arg pathname;
862 ^this.primitiveFailed;
866 ^this.primitiveFailed;
872 // support for ViewRedirect
873 *classRedirect { ^this }