123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- //jshint eqnull:true
- define(function () {
- "use strict";
- function inherits (child, parent) {
- child.prototype = Object.create(parent.prototype, {
- constructor: {
- value: child,
- enumerable: false, configurable: true, writable: true
- }
- });
- return child;
- }
- function defineMethod (klass, name, method) {
- Object.defineProperty(klass.prototype, name, {
- value: method,
- enumerable: false, configurable: true, writable: true
- });
- }
- TraitsBrik.deps = ["event", "behaviors", "methods", "composition", "root"];
- function TraitsBrik (brikz, st) {
- var coreFns = brikz.root.coreFns;
- var SmalltalkObject = brikz.root.Object;
- var setupMethods = brikz.methods.setupMethods;
- var traitMethodChanged = brikz.composition.traitMethodChanged;
- var buildTraitOrClass = brikz.behaviors.buildTraitOrClass;
- var emit = brikz.event.emit;
- var declareEvent = brikz.event.declareEvent;
- function SmalltalkTrait () {
- }
- coreFns.Trait = inherits(SmalltalkTrait, SmalltalkObject);
- SmalltalkTrait.prototype.trait = true;
- defineMethod(SmalltalkTrait, "toString", function () {
- return 'Smalltalk Trait ' + this.name;
- });
- declareEvent("traitAdded");
- defineMethod(SmalltalkTrait, "added", function () {
- emit.traitAdded(this);
- });
- declareEvent("traitRemoved");
- defineMethod(SmalltalkTrait, "removed", function () {
- emit.traitRemoved(this);
- });
- declareEvent("traitMethodAdded");
- defineMethod(SmalltalkTrait, "methodAdded", function (method) {
- var self = this;
- this.traitUsers.forEach(function (each) {
- traitMethodChanged(method.selector, method, self, each);
- });
- emit.traitMethodAdded(method, this);
- });
- declareEvent("traitMethodRemoved");
- defineMethod(SmalltalkTrait, "methodRemoved", function (method) {
- var self = this;
- this.traitUsers.forEach(function (each) {
- traitMethodChanged(method.selector, null, self, each);
- });
- emit.traitMethodRemoved(method, this);
- });
- function traitBuilder (traitName) {
- return {
- name: traitName,
- make: function () {
- var that = new SmalltalkTrait();
- that.name = traitName;
- that.traitUsers = [];
- setupMethods(that);
- return that;
- },
- updateExisting: function (trait) {
- }
- };
- }
- st.addTrait = function (className, category) {
- return buildTraitOrClass(category, traitBuilder(className));
- };
- }
- MethodCompositionBrik.deps = ["methods", "arraySet"];
- function MethodCompositionBrik (brikz, st) {
- var updateMethod = brikz.methods.updateMethod;
- var addElement = brikz.arraySet.addElement;
- var removeElement = brikz.arraySet.removeElement;
- function aliased (selector, method) {
- var result = st.method(method);
- if (method.selector !== selector) {
- result.selector = selector;
- result.source = '"Aliased as ' + selector + '"\n' + method.source;
- }
- result.owner = method.owner;
- return result;
- }
- function deleteKeysFrom (keys, obj) {
- keys.forEach(function (each) {
- delete obj[each];
- });
- }
- function fillTraitTransformation (traitTransformation, obj) {
- // assert(Object.getOwnProperties(obj).length === 0)
- var traitMethods = traitTransformation.trait.methods;
- Object.keys(traitMethods).forEach(function (selector) {
- obj[selector] = aliased(selector, traitMethods[selector]);
- });
- var traitAliases = traitTransformation.aliases;
- if (traitAliases) {
- Object.keys(traitAliases).forEach(function (aliasSelector) {
- var aliasedMethod = traitMethods[traitAliases[aliasSelector]];
- if (aliasedMethod) obj[aliasSelector] = aliased(aliasSelector, aliasedMethod);
- // else delete obj[aliasSelector]; // semantically correct; optimized away
- });
- }
- var traitExclusions = traitTransformation.exclusions;
- if (traitExclusions) {
- deleteKeysFrom(traitExclusions, obj);
- }
- return obj;
- }
- function buildCompositionChain (traitComposition) {
- return traitComposition.reduce(function (soFar, each) {
- return fillTraitTransformation(each, Object.create(soFar));
- }, null);
- }
- st.setTraitComposition = function (traitComposition, traitOrBehavior) {
- var oldLocalMethods = traitOrBehavior.localMethods,
- newLocalMethods = Object.create(buildCompositionChain(traitComposition));
- Object.keys(oldLocalMethods).forEach(function (selector) {
- newLocalMethods[selector] = oldLocalMethods[selector];
- });
- var selector;
- traitOrBehavior.localMethods = newLocalMethods;
- for (selector in newLocalMethods) {
- updateMethod(selector, traitOrBehavior);
- }
- for (selector in oldLocalMethods) {
- updateMethod(selector, traitOrBehavior);
- }
- (traitOrBehavior.traitComposition || []).forEach(function (each) {
- removeElement(each.trait.traitUsers, traitOrBehavior);
- });
- traitOrBehavior.traitComposition = traitComposition && traitComposition.length ? traitComposition : null;
- (traitOrBehavior.traitComposition || []).forEach(function (each) {
- addElement(each.trait.traitUsers, traitOrBehavior);
- });
- };
- function aliasesOfSelector (selector, traitAliases) {
- if (!traitAliases) return [selector];
- var result = Object.keys(traitAliases).filter(function (aliasSelector) {
- return traitAliases[aliasSelector] === selector
- });
- if (!traitAliases[selector]) result.push(selector);
- return result;
- }
- function applyTraitMethodAddition (selector, method, traitTransformation, obj) {
- var changes = aliasesOfSelector(selector, traitTransformation.aliases);
- changes.forEach(function (aliasSelector) {
- obj[aliasSelector] = aliased(aliasSelector, method);
- });
- var traitExclusions = traitTransformation.exclusions;
- if (traitExclusions) {
- deleteKeysFrom(traitExclusions, obj);
- }
- return changes;
- }
- function applyTraitMethodDeletion (selector, traitTransformation, obj) {
- var changes = aliasesOfSelector(selector, traitTransformation.aliases);
- deleteKeysFrom(changes, obj);
- return changes;
- }
- function traitMethodChanged (selector, method, trait, traitOrBehavior) {
- var traitComposition = traitOrBehavior.traitComposition,
- chain = traitOrBehavior.localMethods,
- changes = [];
- for (var i = traitComposition.length - 1; i >= 0; --i) {
- chain = Object.getPrototypeOf(chain);
- var traitTransformation = traitComposition[i];
- if (traitTransformation.trait !== trait) continue;
- changes.push.apply(changes, method ?
- applyTraitMethodAddition(selector, method, traitTransformation, chain) :
- applyTraitMethodDeletion(selector, traitTransformation, chain));
- }
- // assert(chain === null);
- changes.forEach(function (each) {
- updateMethod(each, traitOrBehavior);
- });
- }
- this.traitMethodChanged = traitMethodChanged;
- }
- ClassesBrik.deps = ["root", "event", "behaviors", "methods", "arraySet", "smalltalkGlobals"];
- function ClassesBrik (brikz, st) {
- var SmalltalkRoot = brikz.root.Root;
- var coreFns = brikz.root.coreFns;
- var globals = brikz.smalltalkGlobals.globals;
- var SmalltalkObject = brikz.root.Object;
- var buildTraitOrClass = brikz.behaviors.buildTraitOrClass;
- var setupMethods = brikz.methods.setupMethods;
- var removeTraitOrClass = brikz.behaviors.removeTraitOrClass;
- var addElement = brikz.arraySet.addElement;
- var removeElement = brikz.arraySet.removeElement;
- var emit = brikz.event.emit;
- var declareEvent = brikz.event.declareEvent;
- function SmalltalkBehavior () {
- }
- function SmalltalkClass () {
- }
- function SmalltalkMetaclass () {
- }
- coreFns.Behavior = inherits(SmalltalkBehavior, SmalltalkObject);
- coreFns.Class = inherits(SmalltalkClass, SmalltalkBehavior);
- coreFns.Metaclass = inherits(SmalltalkMetaclass, SmalltalkBehavior);
- // Fake root class of the system.
- // Effective superclass of all classes created with `nil subclass: ...`.
- var nilAsClass = this.nilAsClass = {
- fn: SmalltalkRoot,
- subclasses: [],
- a$cls: {fn: SmalltalkClass}
- };
- SmalltalkMetaclass.prototype.meta = true;
- defineMethod(SmalltalkClass, "toString", function () {
- return 'Smalltalk ' + this.name;
- });
- defineMethod(SmalltalkMetaclass, "toString", function () {
- return 'Smalltalk Metaclass ' + this.instanceClass.name;
- });
- declareEvent("classAdded");
- defineMethod(SmalltalkClass, "added", function () {
- addSubclass(this);
- emit.classAdded(this);
- });
- declareEvent("classRemoved");
- defineMethod(SmalltalkClass, "removed", function () {
- emit.classRemoved(this);
- removeSubclass(this);
- });
- declareEvent("behaviorMethodAdded");
- defineMethod(SmalltalkBehavior, "methodAdded", function (method) {
- emit.behaviorMethodAdded(method, this);
- });
- declareEvent("behaviorMethodRemove");
- defineMethod(SmalltalkBehavior, "methodRemoved", function (method) {
- emit.behaviorMethodRemoved(method, this);
- });
- // TODO remove, ["@foo"] backward compatibility
- function installIvarCompat (klass) {
- var ivars = klass.slots;
- ivars.forEach(function (ivar) {
- Object.defineProperty(klass.fn.prototype, "@" + ivar, {
- get: function () {
- return this[ivar];
- },
- set: function (value) {
- return this[ivar] = value;
- },
- enumerable: false,
- configurable: true
- });
- });
- }
- this.installIvarCompat = installIvarCompat;
- function setSlots (klass, slots) {
- slots.forEach(function (name) {
- if (!name.match(/^[a-zA-Z][a-zA-Z0-9]*$/))
- throw new Error("Wrong identifier name: " + name);
- });
- klass.slots = slots;
- installIvarCompat(klass);
- }
- st.setSlots = setSlots;
- // TODO remove, .iVarNames backward compatibility
- Object.defineProperty(SmalltalkBehavior.prototype, "iVarNames", {
- enumerable: true,
- configurable: true,
- get: function () {
- return this.slots;
- },
- set: function (instanceVariableNames) {
- setSlots(this, instanceVariableNames);
- }
- });
- this.bootstrapHierarchy = function () {
- nilAsClass.a$cls = globals.Class;
- nilAsClass.subclasses.forEach(function (each) {
- each.a$cls.superclass = globals.Class;
- addSubclass(each.a$cls);
- });
- };
- /* Smalltalk class creation. A class is an instance of an automatically
- created metaclass object. Newly created classes (not their metaclass)
- should be added to the system, see smalltalk.addClass().
- Superclass linking is *not* handled here, see api.initialize() */
- function classBuilder (className, superclass, fn) {
- var logicalSuperclass = superclass;
- if (superclass == null || superclass.a$nil) {
- superclass = nilAsClass;
- logicalSuperclass = null;
- }
- function klass () {
- var that = metaclass().instanceClass;
- that.superclass = logicalSuperclass;
- that.fn = fn || inherits(function () {
- }, superclass.fn);
- that.slots = [];
- that.name = className;
- that.subclasses = [];
- setupMethods(that);
- return that;
- }
- function metaclass () {
- var that = new SmalltalkMetaclass();
- that.superclass = superclass.a$cls;
- that.fn = inherits(function () {
- }, that.superclass.fn);
- that.slots = [];
- that.instanceClass = new that.fn();
- wireKlass(that);
- setupMethods(that);
- return that;
- }
- return {
- name: className,
- make: klass,
- updateExisting: function (klass) {
- if (klass.superclass != logicalSuperclass || fn && fn !== klass.fn)
- throw new Error("Incompatible change of class: " + klass.name);
- }
- };
- }
- function wireKlass (klass) {
- Object.defineProperty(klass.fn.prototype, "a$cls", {
- value: klass,
- enumerable: false, configurable: true, writable: true
- });
- }
- this.wireKlass = wireKlass;
- /* Add a class to the system, creating a new one if needed.
- A Package is lazily created if one with given name does not exist. */
- st.addClass = function (className, superclass, category) {
- // TODO remove, backward compatibility
- if (arguments[3]) {
- var added = st.addClass(className, superclass, arguments[3]);
- setSlots(added, category);
- return added;
- }
- // While subclassing nil is allowed, it might be an error, so
- // warn about it.
- if (typeof superclass === 'undefined' || superclass && superclass.a$nil) {
- console.warn('Compiling ' + className + ' as a subclass of `nil`. A dependency might be missing.');
- }
- return buildTraitOrClass(category, classBuilder(className, superclass, coreFns[className]));
- };
- st.removeClass = removeTraitOrClass;
- function addSubclass (klass) {
- addElement((klass.superclass || nilAsClass).subclasses, klass);
- }
- function removeSubclass (klass) {
- removeElement((klass.superclass || nilAsClass).subclasses, klass);
- }
- function metaSubclasses (metaclass) {
- return metaclass.instanceClass.subclasses
- .filter(function (each) {
- return !each.meta;
- })
- .map(function (each) {
- return each.a$cls;
- });
- }
- st.metaSubclasses = metaSubclasses;
- st.traverseClassTree = function (klass, fn) {
- var queue = [klass], sentinel = {};
- for (var i = 0; i < queue.length; ++i) {
- var item = queue[i];
- if (fn(item, sentinel) === sentinel) continue;
- var subclasses = item.meta ? metaSubclasses(item) : item.subclasses;
- queue.push.apply(queue, subclasses);
- }
- };
- }
- /* Making smalltalk that can load */
- function configureWithHierarchy (brikz) {
- brikz.traits = TraitsBrik;
- brikz.composition = MethodCompositionBrik;
- brikz.classes = ClassesBrik;
- brikz();
- }
- return configureWithHierarchy;
- });
|