123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- /* ====================================================================
- |
- | Amber Smalltalk
- | http://amber-lang.net
- |
- ======================================================================
- ======================================================================
- |
- | Copyright (c) 2010-2014
- | Nicolas Petton <petton.nicolas@gmail.com>
- |
- | Copyright (c) 2012-2017
- | The Amber team https://lolg.it/org/amber/members
- | Amber contributors (see /CONTRIBUTORS)
- |
- | Amber is released under the MIT license
- |
- | Permission is hereby granted, free of charge, to any person obtaining
- | a copy of this software and associated documentation files (the
- | 'Software'), to deal in the Software without restriction, including
- | without limitation the rights to use, copy, modify, merge, publish,
- | distribute, sublicense, and/or sell copies of the Software, and to
- | permit persons to whom the Software is furnished to do so, subject to
- | the following conditions:
- |
- | The above copyright notice and this permission notice shall be
- | included in all copies or substantial portions of the Software.
- |
- | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
- | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- |
- ==================================================================== */
- //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.className;
- });
- 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 (className) {
- return {
- className: className,
- make: function () {
- var that = new SmalltalkTrait();
- that.className = className;
- 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) {
- if (method.selector === selector) return method;
- var result = st.method({
- selector: selector,
- args: method.args,
- protocol: method.protocol,
- source: '"Aliased as ' + selector + '"\n' + method.source,
- messageSends: method.messageSends,
- referencesClasses: method.referencedClasses,
- fn: method.fn
- });
- result.methodClass = method.methodClass;
- 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] = 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,
- a$cls: {fn: SmalltalkClass},
- klass: {fn: SmalltalkClass}
- };
- SmalltalkMetaclass.prototype.meta = true;
- defineMethod(SmalltalkClass, "toString", function () {
- return 'Smalltalk ' + this.className;
- });
- defineMethod(SmalltalkMetaclass, "toString", function () {
- return 'Smalltalk Metaclass ' + this.instanceClass.className;
- });
- 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);
- });
- this.bootstrapHierarchy = function () {
- var nilSubclasses = [globals.ProtoObject];
- nilAsClass.a$cls = nilAsClass.klass = globals.Class;
- nilSubclasses.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, iVarNames, 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.iVarNames = iVarNames || [];
- that.className = 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.iVarNames = [];
- that.instanceClass = new that.fn();
- wireKlass(that);
- setupMethods(that);
- return that;
- }
- return {
- className: className,
- make: klass,
- updateExisting: function (klass) {
- if (klass.superclass == logicalSuperclass && (!fn || fn === klass.fn)) {
- if (iVarNames) klass.iVarNames = iVarNames;
- } else throw new Error("Incompatible change of class: " + klass.className);
- }
- };
- }
- function wireKlass (klass) {
- Object.defineProperty(klass.fn.prototype, "a$cls", {
- value: klass,
- enumerable: false, configurable: true, writable: true
- });
- Object.defineProperty(klass.fn.prototype, "klass", {
- 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, iVarNames, category) {
- // 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, iVarNames, coreFns[className]));
- };
- st.removeClass = removeTraitOrClass;
- function addSubclass (klass) {
- if (klass.superclass) {
- addElement(klass.superclass.subclasses, klass);
- }
- }
- function removeSubclass (klass) {
- if (klass.superclass) {
- removeElement(klass.superclass.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.rebuild();
- }
- return configureWithHierarchy;
- });
|