kernel-language.js 16 KB

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