kernel-language.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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 specialConstructors = brikz.commonSpecialConstructors;
  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.commonEmit;
  27. var declareEvent = brikz.event.declareEvent;
  28. function SmalltalkTrait () {
  29. }
  30. specialConstructors.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", "nil"];
  187. function ClassesBrik (brikz, st) {
  188. var SmalltalkRoot = brikz.root.Root;
  189. var specialConstructors = brikz.commonSpecialConstructors;
  190. var globals = brikz.commonGlobals;
  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.commonEmit;
  198. var declareEvent = brikz.event.declareEvent;
  199. var nilAsReceiver = brikz.nil.nilAsReceiver;
  200. function SmalltalkBehavior () {
  201. }
  202. function SmalltalkClass () {
  203. }
  204. function SmalltalkMetaclass () {
  205. }
  206. specialConstructors.Behavior = inherits(SmalltalkBehavior, SmalltalkObject);
  207. specialConstructors.Class = inherits(SmalltalkClass, SmalltalkBehavior);
  208. specialConstructors.Metaclass = inherits(SmalltalkMetaclass, SmalltalkBehavior);
  209. // Fake root class of the system.
  210. // Effective superclass of all classes created with `nil subclass: ...`.
  211. var nilAsClass = this.nilAsClass = {
  212. fn: SmalltalkRoot,
  213. subclasses: [],
  214. a$cls: {fn: SmalltalkClass}
  215. };
  216. SmalltalkMetaclass.prototype.meta = true;
  217. defineMethod(SmalltalkClass, "toString", function () {
  218. return 'Smalltalk ' + this.name;
  219. });
  220. defineMethod(SmalltalkMetaclass, "toString", function () {
  221. return 'Smalltalk Metaclass ' + this.instanceClass.name;
  222. });
  223. declareEvent("classAdded");
  224. defineMethod(SmalltalkClass, "added", function () {
  225. addSubclass(this);
  226. emit.classAdded(this);
  227. });
  228. declareEvent("classRemoved");
  229. defineMethod(SmalltalkClass, "removed", function () {
  230. emit.classRemoved(this);
  231. removeSubclass(this);
  232. });
  233. declareEvent("behaviorMethodAdded");
  234. defineMethod(SmalltalkBehavior, "methodAdded", function (method) {
  235. emit.behaviorMethodAdded(method, this);
  236. });
  237. declareEvent("behaviorMethodRemove");
  238. defineMethod(SmalltalkBehavior, "methodRemoved", function (method) {
  239. emit.behaviorMethodRemoved(method, this);
  240. });
  241. // TODO remove, ["@foo"] backward compatibility
  242. function installIvarCompat (klass) {
  243. var ivars = klass.slots;
  244. ivars.forEach(function (ivar) {
  245. Object.defineProperty(klass.fn.prototype, "@" + ivar, {
  246. get: function () {
  247. return this[ivar];
  248. },
  249. set: function (value) {
  250. return this[ivar] = value;
  251. },
  252. enumerable: false,
  253. configurable: true
  254. });
  255. });
  256. }
  257. this.installIvarCompat = installIvarCompat;
  258. function setSlots (klass, slots) {
  259. slots.forEach(function (name) {
  260. if (!name.match(/^[a-zA-Z][a-zA-Z0-9]*$/))
  261. throw new Error("Wrong identifier name: " + name);
  262. });
  263. klass.slots = slots;
  264. installIvarCompat(klass);
  265. }
  266. st.setSlots = setSlots;
  267. // TODO remove, .iVarNames backward compatibility
  268. Object.defineProperty(SmalltalkBehavior.prototype, "iVarNames", {
  269. enumerable: true,
  270. configurable: true,
  271. get: function () {
  272. return this.slots;
  273. },
  274. set: function (instanceVariableNames) {
  275. setSlots(this, instanceVariableNames);
  276. }
  277. });
  278. this.bootstrapHierarchy = function () {
  279. nilAsClass.a$cls = globals.Class;
  280. nilAsClass.subclasses.forEach(function (each) {
  281. each.a$cls.superclass = globals.Class;
  282. addSubclass(each.a$cls);
  283. });
  284. };
  285. /* Smalltalk class creation. A class is an instance of an automatically
  286. created metaclass object. Newly created classes (not their metaclass)
  287. should be added to the system, see smalltalk.addClass().
  288. Superclass linking is *not* handled here, see api.initialize() */
  289. function classBuilder (className, superclass, fn) {
  290. var logicalSuperclass = superclass;
  291. if (superclass == null || superclass.a$nil) {
  292. superclass = nilAsClass;
  293. logicalSuperclass = null;
  294. }
  295. function klass () {
  296. var that = metaclass().instanceClass;
  297. that.superclass = logicalSuperclass;
  298. that.fn = fn || inherits(function () {
  299. }, superclass.fn);
  300. that.slots = [];
  301. that.name = className;
  302. that.subclasses = [];
  303. setupMethods(that);
  304. return that;
  305. }
  306. function metaclass () {
  307. var that = new SmalltalkMetaclass();
  308. that.superclass = superclass.a$cls;
  309. that.fn = inherits(function () {
  310. }, that.superclass.fn);
  311. that.slots = [];
  312. that.instanceClass = new that.fn();
  313. wireKlass(that);
  314. setupMethods(that);
  315. return that;
  316. }
  317. return {
  318. name: className,
  319. make: klass,
  320. updateExisting: function (klass) {
  321. if (klass.superclass != logicalSuperclass || fn && fn !== klass.fn)
  322. throw new Error("Incompatible change of class: " + klass.name);
  323. }
  324. };
  325. }
  326. function wireKlass (klass) {
  327. Object.defineProperty(klass.fn.prototype, "a$cls", {
  328. value: klass,
  329. enumerable: false, configurable: true, writable: true
  330. });
  331. }
  332. this.wireKlass = wireKlass;
  333. /**
  334. * This function is used all over the compiled amber code.
  335. * It takes any value (JavaScript or Smalltalk)
  336. * and returns a proper Amber Smalltalk receiver.
  337. *
  338. * null or undefined -> nilAsReceiver,
  339. * object having Smalltalk signature -> unchanged,
  340. * otherwise wrapped foreign (JS) object
  341. */
  342. this.asReceiver = function (o) {
  343. if (o == null) return nilAsReceiver;
  344. else if (o.a$cls != null) return o;
  345. else return st.wrapJavaScript(o);
  346. };
  347. /* Add a class to the system, creating a new one if needed.
  348. A Package is lazily created if one with given name does not exist. */
  349. st.addClass = function (className, superclass, category) {
  350. // TODO remove, backward compatibility
  351. if (arguments[3]) {
  352. var added = st.addClass(className, superclass, arguments[3]);
  353. setSlots(added, category);
  354. return added;
  355. }
  356. // While subclassing nil is allowed, it might be an error, so
  357. // warn about it.
  358. if (typeof superclass === 'undefined' || superclass && superclass.a$nil) {
  359. console.warn('Compiling ' + className + ' as a subclass of `nil`. A dependency might be missing.');
  360. }
  361. return buildTraitOrClass(category, classBuilder(className, superclass, specialConstructors[className]));
  362. };
  363. st.removeClass = removeTraitOrClass;
  364. function addSubclass (klass) {
  365. addElement((klass.superclass || nilAsClass).subclasses, klass);
  366. }
  367. function removeSubclass (klass) {
  368. removeElement((klass.superclass || nilAsClass).subclasses, klass);
  369. }
  370. function metaSubclasses (metaclass) {
  371. return metaclass.instanceClass.subclasses
  372. .filter(function (each) {
  373. return !each.meta;
  374. })
  375. .map(function (each) {
  376. return each.a$cls;
  377. });
  378. }
  379. st.metaSubclasses = metaSubclasses;
  380. st.traverseClassTree = function (klass, fn) {
  381. var queue = [klass], sentinel = {};
  382. for (var i = 0; i < queue.length; ++i) {
  383. var item = queue[i];
  384. if (fn(item, sentinel) === sentinel) continue;
  385. var subclasses = item.meta ? metaSubclasses(item) : item.subclasses;
  386. queue.push.apply(queue, subclasses);
  387. }
  388. };
  389. }
  390. /* Making smalltalk that can load */
  391. function configureWithHierarchy (brikz) {
  392. brikz.traits = TraitsBrik;
  393. brikz.composition = MethodCompositionBrik;
  394. brikz.classes = ClassesBrik;
  395. brikz();
  396. }
  397. return configureWithHierarchy;
  398. });