kernel-language.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. //jshint eqnull:true
  2. define(function () {
  3. "use strict";
  4. function inherits (child, parent) {
  5. child.prototype = Object.create(parent.prototype, {
  6. constructor: {
  7. value: child,
  8. enumerable: false, configurable: true, writable: true
  9. }
  10. });
  11. return child;
  12. }
  13. function defineMethod (klass, name, method) {
  14. Object.defineProperty(klass.prototype, name, {
  15. value: method,
  16. enumerable: false, configurable: true, writable: true
  17. });
  18. }
  19. TraitsBrik.deps = ["event", "behaviors", "methods", "composition", "root"];
  20. function TraitsBrik (brikz, st) {
  21. var coreFns = brikz.root.coreFns;
  22. var SmalltalkObject = brikz.root.Object;
  23. var setupMethods = brikz.methods.setupMethods;
  24. var traitMethodChanged = brikz.composition.traitMethodChanged;
  25. var buildTraitOrClass = brikz.behaviors.buildTraitOrClass;
  26. var emit = brikz.event.emit;
  27. var declareEvent = brikz.event.declareEvent;
  28. function SmalltalkTrait () {
  29. }
  30. coreFns.Trait = inherits(SmalltalkTrait, SmalltalkObject);
  31. SmalltalkTrait.prototype.trait = true;
  32. defineMethod(SmalltalkTrait, "toString", function () {
  33. return 'Smalltalk Trait ' + this.name;
  34. });
  35. declareEvent("traitAdded");
  36. defineMethod(SmalltalkTrait, "added", function () {
  37. emit.traitAdded(this);
  38. });
  39. declareEvent("traitRemoved");
  40. defineMethod(SmalltalkTrait, "removed", function () {
  41. emit.traitRemoved(this);
  42. });
  43. declareEvent("traitMethodAdded");
  44. defineMethod(SmalltalkTrait, "methodAdded", function (method) {
  45. var self = this;
  46. this.traitUsers.forEach(function (each) {
  47. traitMethodChanged(method.selector, method, self, each);
  48. });
  49. emit.traitMethodAdded(method, this);
  50. });
  51. declareEvent("traitMethodRemoved");
  52. defineMethod(SmalltalkTrait, "methodRemoved", function (method) {
  53. var self = this;
  54. this.traitUsers.forEach(function (each) {
  55. traitMethodChanged(method.selector, null, self, each);
  56. });
  57. emit.traitMethodRemoved(method, this);
  58. });
  59. function traitBuilder (traitName) {
  60. return {
  61. name: traitName,
  62. make: function () {
  63. var that = new SmalltalkTrait();
  64. that.name = traitName;
  65. that.traitUsers = [];
  66. setupMethods(that);
  67. return that;
  68. },
  69. updateExisting: function (trait) {
  70. }
  71. };
  72. }
  73. st.addTrait = function (className, category) {
  74. return buildTraitOrClass(category, traitBuilder(className));
  75. };
  76. }
  77. MethodCompositionBrik.deps = ["methods", "arraySet"];
  78. function MethodCompositionBrik (brikz, st) {
  79. var updateMethod = brikz.methods.updateMethod;
  80. var addElement = brikz.arraySet.addElement;
  81. var removeElement = brikz.arraySet.removeElement;
  82. function aliased (selector, method) {
  83. if (method.selector === selector) return method;
  84. var result = st.method({
  85. selector: selector,
  86. args: method.args,
  87. protocol: method.protocol,
  88. source: '"Aliased as ' + selector + '"\n' + method.source,
  89. messageSends: method.messageSends,
  90. referencesClasses: method.referencedClasses,
  91. fn: method.fn
  92. });
  93. result.owner = method.owner;
  94. return result;
  95. }
  96. function deleteKeysFrom (keys, obj) {
  97. keys.forEach(function (each) {
  98. delete obj[each];
  99. });
  100. }
  101. function fillTraitTransformation (traitTransformation, obj) {
  102. // assert(Object.getOwnProperties(obj).length === 0)
  103. var traitMethods = traitTransformation.trait.methods;
  104. Object.keys(traitMethods).forEach(function (selector) {
  105. obj[selector] = traitMethods[selector];
  106. });
  107. var traitAliases = traitTransformation.aliases;
  108. if (traitAliases) {
  109. Object.keys(traitAliases).forEach(function (aliasSelector) {
  110. var aliasedMethod = traitMethods[traitAliases[aliasSelector]];
  111. if (aliasedMethod) obj[aliasSelector] = aliased(aliasSelector, aliasedMethod);
  112. // else delete obj[aliasSelector]; // semantically correct; optimized away
  113. });
  114. }
  115. var traitExclusions = traitTransformation.exclusions;
  116. if (traitExclusions) {
  117. deleteKeysFrom(traitExclusions, obj);
  118. }
  119. return obj;
  120. }
  121. function buildCompositionChain (traitComposition) {
  122. return traitComposition.reduce(function (soFar, each) {
  123. return fillTraitTransformation(each, Object.create(soFar));
  124. }, null);
  125. }
  126. st.setTraitComposition = function (traitComposition, traitOrBehavior) {
  127. var oldLocalMethods = traitOrBehavior.localMethods,
  128. newLocalMethods = Object.create(buildCompositionChain(traitComposition));
  129. Object.keys(oldLocalMethods).forEach(function (selector) {
  130. newLocalMethods[selector] = oldLocalMethods[selector];
  131. });
  132. var selector;
  133. traitOrBehavior.localMethods = newLocalMethods;
  134. for (selector in newLocalMethods) {
  135. updateMethod(selector, traitOrBehavior);
  136. }
  137. for (selector in oldLocalMethods) {
  138. updateMethod(selector, traitOrBehavior);
  139. }
  140. (traitOrBehavior.traitComposition || []).forEach(function (each) {
  141. removeElement(each.trait.traitUsers, traitOrBehavior);
  142. });
  143. traitOrBehavior.traitComposition = traitComposition && traitComposition.length ? traitComposition : null;
  144. (traitOrBehavior.traitComposition || []).forEach(function (each) {
  145. addElement(each.trait.traitUsers, traitOrBehavior);
  146. });
  147. };
  148. function aliasesOfSelector (selector, traitAliases) {
  149. if (!traitAliases) return [selector];
  150. var result = Object.keys(traitAliases).filter(function (aliasSelector) {
  151. return traitAliases[aliasSelector] === selector
  152. });
  153. if (!traitAliases[selector]) result.push(selector);
  154. return result;
  155. }
  156. function applyTraitMethodAddition (selector, method, traitTransformation, obj) {
  157. var changes = aliasesOfSelector(selector, traitTransformation.aliases);
  158. changes.forEach(function (aliasSelector) {
  159. obj[aliasSelector] = aliased(aliasSelector, method);
  160. });
  161. var traitExclusions = traitTransformation.exclusions;
  162. if (traitExclusions) {
  163. deleteKeysFrom(traitExclusions, obj);
  164. }
  165. return changes;
  166. }
  167. function applyTraitMethodDeletion (selector, traitTransformation, obj) {
  168. var changes = aliasesOfSelector(selector, traitTransformation.aliases);
  169. deleteKeysFrom(changes, obj);
  170. return changes;
  171. }
  172. function traitMethodChanged (selector, method, trait, traitOrBehavior) {
  173. var traitComposition = traitOrBehavior.traitComposition,
  174. chain = traitOrBehavior.localMethods,
  175. changes = [];
  176. for (var i = traitComposition.length - 1; i >= 0; --i) {
  177. chain = Object.getPrototypeOf(chain);
  178. var traitTransformation = traitComposition[i];
  179. if (traitTransformation.trait !== trait) continue;
  180. changes.push.apply(changes, method ?
  181. applyTraitMethodAddition(selector, method, traitTransformation, chain) :
  182. applyTraitMethodDeletion(selector, traitTransformation, chain));
  183. }
  184. // assert(chain === null);
  185. changes.forEach(function (each) {
  186. updateMethod(each, traitOrBehavior);
  187. });
  188. }
  189. this.traitMethodChanged = traitMethodChanged;
  190. }
  191. ClassesBrik.deps = ["root", "event", "behaviors", "methods", "arraySet", "smalltalkGlobals"];
  192. function ClassesBrik (brikz, st) {
  193. var SmalltalkRoot = brikz.root.Root;
  194. var coreFns = brikz.root.coreFns;
  195. var globals = brikz.smalltalkGlobals.globals;
  196. var SmalltalkObject = brikz.root.Object;
  197. var buildTraitOrClass = brikz.behaviors.buildTraitOrClass;
  198. var setupMethods = brikz.methods.setupMethods;
  199. var removeTraitOrClass = brikz.behaviors.removeTraitOrClass;
  200. var addElement = brikz.arraySet.addElement;
  201. var removeElement = brikz.arraySet.removeElement;
  202. var emit = brikz.event.emit;
  203. var declareEvent = brikz.event.declareEvent;
  204. function SmalltalkBehavior () {
  205. }
  206. function SmalltalkClass () {
  207. }
  208. function SmalltalkMetaclass () {
  209. }
  210. coreFns.Behavior = inherits(SmalltalkBehavior, SmalltalkObject);
  211. coreFns.Class = inherits(SmalltalkClass, SmalltalkBehavior);
  212. coreFns.Metaclass = inherits(SmalltalkMetaclass, SmalltalkBehavior);
  213. // Fake root class of the system.
  214. // Effective superclass of all classes created with `nil subclass: ...`.
  215. var nilAsClass = this.nilAsClass = {
  216. fn: SmalltalkRoot,
  217. a$cls: {fn: SmalltalkClass}
  218. };
  219. SmalltalkMetaclass.prototype.meta = true;
  220. defineMethod(SmalltalkClass, "toString", function () {
  221. return 'Smalltalk ' + this.name;
  222. });
  223. defineMethod(SmalltalkMetaclass, "toString", function () {
  224. return 'Smalltalk Metaclass ' + this.instanceClass.name;
  225. });
  226. declareEvent("classAdded");
  227. defineMethod(SmalltalkClass, "added", function () {
  228. addSubclass(this);
  229. emit.classAdded(this);
  230. });
  231. declareEvent("classRemoved");
  232. defineMethod(SmalltalkClass, "removed", function () {
  233. emit.classRemoved(this);
  234. removeSubclass(this);
  235. });
  236. declareEvent("behaviorMethodAdded");
  237. defineMethod(SmalltalkBehavior, "methodAdded", function (method) {
  238. emit.behaviorMethodAdded(method, this);
  239. });
  240. declareEvent("behaviorMethodRemove");
  241. defineMethod(SmalltalkBehavior, "methodRemoved", function (method) {
  242. emit.behaviorMethodRemoved(method, this);
  243. });
  244. // TODO remove, ["@foo"] backward compatibility
  245. function installIvarCompat (klass) {
  246. var ivars = klass.slots;
  247. ivars.forEach(function (ivar) {
  248. Object.defineProperty(klass.fn.prototype, "@" + ivar, {
  249. get: function () {
  250. return this[ivar];
  251. },
  252. set: function (value) {
  253. return this[ivar] = value;
  254. },
  255. enumerable: false,
  256. configurable: true
  257. });
  258. });
  259. }
  260. this.installIvarCompat = installIvarCompat;
  261. function setSlots (klass, slots) {
  262. slots.forEach(function (name) {
  263. if (!name.match(/^[a-zA-Z][a-zA-Z0-9]*$/))
  264. throw new Error("Wrong identifier name: " + name);
  265. });
  266. klass.slots = slots;
  267. installIvarCompat(klass);
  268. }
  269. st.setSlots = setSlots;
  270. // TODO remove, .iVarNames backward compatibility
  271. Object.defineProperty(SmalltalkBehavior.prototype, "iVarNames", {
  272. enumerable: true,
  273. configurable: true,
  274. get: function () {
  275. return this.slots;
  276. },
  277. set: function (instanceVariableNames) {
  278. setSlots(this, instanceVariableNames);
  279. }
  280. });
  281. this.bootstrapHierarchy = function () {
  282. var nilSubclasses = [globals.ProtoObject];
  283. nilAsClass.a$cls = globals.Class;
  284. nilSubclasses.forEach(function (each) {
  285. each.a$cls.superclass = globals.Class;
  286. addSubclass(each.a$cls);
  287. });
  288. };
  289. /* Smalltalk class creation. A class is an instance of an automatically
  290. created metaclass object. Newly created classes (not their metaclass)
  291. should be added to the system, see smalltalk.addClass().
  292. Superclass linking is *not* handled here, see api.initialize() */
  293. function classBuilder (className, superclass, fn) {
  294. var logicalSuperclass = superclass;
  295. if (superclass == null || superclass.a$nil) {
  296. superclass = nilAsClass;
  297. logicalSuperclass = null;
  298. }
  299. function klass () {
  300. var that = metaclass().instanceClass;
  301. that.superclass = logicalSuperclass;
  302. that.fn = fn || inherits(function () {
  303. }, superclass.fn);
  304. that.slots = [];
  305. that.name = className;
  306. that.subclasses = [];
  307. setupMethods(that);
  308. return that;
  309. }
  310. function metaclass () {
  311. var that = new SmalltalkMetaclass();
  312. that.superclass = superclass.a$cls;
  313. that.fn = inherits(function () {
  314. }, that.superclass.fn);
  315. that.slots = [];
  316. that.instanceClass = new that.fn();
  317. wireKlass(that);
  318. setupMethods(that);
  319. return that;
  320. }
  321. return {
  322. name: className,
  323. make: klass,
  324. updateExisting: function (klass) {
  325. if (klass.superclass != logicalSuperclass || fn && fn !== klass.fn)
  326. throw new Error("Incompatible change of class: " + klass.name);
  327. }
  328. };
  329. }
  330. function wireKlass (klass) {
  331. Object.defineProperty(klass.fn.prototype, "a$cls", {
  332. value: klass,
  333. enumerable: false, configurable: true, writable: true
  334. });
  335. }
  336. this.wireKlass = wireKlass;
  337. /* Add a class to the system, creating a new one if needed.
  338. A Package is lazily created if one with given name does not exist. */
  339. st.addClass = function (className, superclass, category) {
  340. // TODO remove, backward compatibility
  341. if (arguments[3]) {
  342. var added = st.addClass(className, superclass, arguments[3]);
  343. setSlots(added, category);
  344. return added;
  345. }
  346. // While subclassing nil is allowed, it might be an error, so
  347. // warn about it.
  348. if (typeof superclass === 'undefined' || superclass && superclass.a$nil) {
  349. console.warn('Compiling ' + className + ' as a subclass of `nil`. A dependency might be missing.');
  350. }
  351. return buildTraitOrClass(category, classBuilder(className, superclass, coreFns[className]));
  352. };
  353. st.removeClass = removeTraitOrClass;
  354. function addSubclass (klass) {
  355. if (klass.superclass) {
  356. addElement(klass.superclass.subclasses, klass);
  357. }
  358. }
  359. function removeSubclass (klass) {
  360. if (klass.superclass) {
  361. removeElement(klass.superclass.subclasses, klass);
  362. }
  363. }
  364. function metaSubclasses (metaclass) {
  365. return metaclass.instanceClass.subclasses
  366. .filter(function (each) {
  367. return !each.meta;
  368. })
  369. .map(function (each) {
  370. return each.a$cls;
  371. });
  372. }
  373. st.metaSubclasses = metaSubclasses;
  374. st.traverseClassTree = function (klass, fn) {
  375. var queue = [klass], sentinel = {};
  376. for (var i = 0; i < queue.length; ++i) {
  377. var item = queue[i];
  378. if (fn(item, sentinel) === sentinel) continue;
  379. var subclasses = item.meta ? metaSubclasses(item) : item.subclasses;
  380. queue.push.apply(queue, subclasses);
  381. }
  382. };
  383. }
  384. /* Making smalltalk that can load */
  385. function configureWithHierarchy (brikz) {
  386. brikz.traits = TraitsBrik;
  387. brikz.composition = MethodCompositionBrik;
  388. brikz.classes = ClassesBrik;
  389. brikz.rebuild();
  390. }
  391. return configureWithHierarchy;
  392. });