boot.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092
  1. /* ====================================================================
  2. |
  3. | Amber Smalltalk
  4. | http://amber-lang.net
  5. |
  6. ======================================================================
  7. ======================================================================
  8. |
  9. | Copyright (c) 2010-2013
  10. | Nicolas Petton <petton.nicolas@gmail.com>
  11. |
  12. | Copyright (c) 2012-2013
  13. | The Amber team https://github.com/amber-smalltalk?tab=members
  14. | Amber contributors https://github.com/amber-smalltalk/amber/graphs/contributors
  15. |
  16. | Amber is released under the MIT license
  17. |
  18. | Permission is hereby granted, free of charge, to any person obtaining
  19. | a copy of this software and associated documentation files (the
  20. | 'Software'), to deal in the Software without restriction, including
  21. | without limitation the rights to use, copy, modify, merge, publish,
  22. | distribute, sublicense, and/or sell copies of the Software, and to
  23. | permit persons to whom the Software is furnished to do so, subject to
  24. | the following conditions:
  25. |
  26. | The above copyright notice and this permission notice shall be
  27. | included in all copies or substantial portions of the Software.
  28. |
  29. | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
  30. | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  31. | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  32. | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  33. | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  34. | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  35. | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  36. |
  37. ==================================================================== */
  38. define("amber_vm/boot", [ './browser-compatibility' ], function () {
  39. /* Reconfigurable micro composition system, https://github.com/amber-smalltalk/brikz */
  40. function Brikz(api, apiKey, initKey) {
  41. var brikz = this, backup = {};
  42. apiKey = apiKey || 'exports';
  43. initKey = initKey || '__init__';
  44. function mixin(src, target, what) {
  45. for (var keys = Object.keys(what||src), l=keys.length, i=0; i<l; ++i) {
  46. var value = src[keys[i]];
  47. if (typeof value !== "undefined") { target[keys[i]] = value; }
  48. }
  49. return target;
  50. }
  51. var d={value: null, enumerable: false, configurable: true, writable: true};
  52. Object.defineProperties(this, { ensure: d, rebuild: d });
  53. var exclude = mixin(this, {});
  54. this.rebuild = function () {
  55. Object.keys(backup).forEach(function (key) {
  56. mixin({}, api, (backup[key]||0)[apiKey]);
  57. });
  58. var oapi = mixin(api, {}), order = [], chk = {};
  59. brikz.ensure = function(key) {
  60. if (key in exclude) { return null; }
  61. var b = brikz[key], bak = backup[key];
  62. mixin({}, api, api);
  63. while (typeof b === "function") { b = new b(brikz, api, bak); }
  64. if (b && !chk[key]) { chk[key]=true; order.push(b); }
  65. if (b && !b[apiKey]) { b[apiKey] = mixin(api, {}); }
  66. return brikz[key] = b;
  67. };
  68. Object.keys(brikz).forEach(function (key) { brikz.ensure(key); });
  69. brikz.ensure = null;
  70. mixin(oapi, mixin({}, api, api));
  71. order.forEach(function(brik) { mixin(brik[apiKey] || {}, api); });
  72. order.forEach(function(brik) { brik[initKey] && brik[initKey](); });
  73. backup = mixin(brikz, {});
  74. };
  75. }
  76. /* Brikz end */
  77. function inherits(child, parent) {
  78. child.prototype = Object.create(parent.prototype, {
  79. constructor: { value: child,
  80. enumerable: false, configurable: true, writable: true }
  81. });
  82. return child;
  83. }
  84. /* Smalltalk foundational objects */
  85. function SmalltalkObject() {}
  86. function Smalltalk() {}
  87. inherits(Smalltalk, SmalltalkObject);
  88. var api = new Smalltalk();
  89. var brikz = new Brikz(api);
  90. function RootBrik(brikz, st) {
  91. function SmalltalkNil() {}
  92. inherits(SmalltalkNil, SmalltalkObject);
  93. this.nil = new SmalltalkNil();
  94. this.__init__ = function () {
  95. st.addPackage("Kernel-Objects");
  96. st.addPackage("Kernel-Infrastructure");
  97. st.wrapClassName("Object", "Kernel-Objects", SmalltalkObject, undefined, false);
  98. st.wrapClassName("Smalltalk", "Kernel-Infrastructure", Smalltalk, st.Object, false);
  99. st.wrapClassName("UndefinedObject", "Kernel-Objects", SmalltalkNil, st.Object, false);
  100. };
  101. }
  102. function OrganizeBrik(brikz, st) {
  103. brikz.ensure("augments");
  104. brikz.ensure("root");
  105. function SmalltalkOrganizer () {}
  106. function SmalltalkPackageOrganizer () {
  107. this.elements = [];
  108. }
  109. function SmalltalkClassOrganizer () {
  110. this.elements = [];
  111. }
  112. inherits(SmalltalkOrganizer, SmalltalkObject);
  113. inherits(SmalltalkPackageOrganizer, SmalltalkOrganizer);
  114. inherits(SmalltalkClassOrganizer, SmalltalkOrganizer);
  115. this.__init__ = function () {
  116. st.wrapClassName("Organizer", "Kernel-Infrastructure", SmalltalkOrganizer, st.Object, false);
  117. st.wrapClassName("PackageOrganizer", "Kernel-Infrastructure", SmalltalkPackageOrganizer, st.Organizer, false);
  118. st.wrapClassName("ClassOrganizer", "Kernel-Infrastructure", SmalltalkClassOrganizer, st.Organizer, false);
  119. };
  120. this.setupClassOrganization = function (klass) {
  121. klass.organization = new SmalltalkClassOrganizer();
  122. klass.organization.theClass = klass;
  123. };
  124. this.setupPackageOrganization = function (pkg) {
  125. pkg.organization = new SmalltalkPackageOrganizer();
  126. };
  127. this.addOrganizationElement = function (owner, element) {
  128. owner.organization.elements.addElement(element);
  129. };
  130. this.removeOrganizationElement = function (owner, element) {
  131. owner.organization.elements.removeElement(element);
  132. };
  133. }
  134. function DNUBrik(brikz, st) {
  135. brikz.ensure("selectorConversion");
  136. brikz.ensure("messageSend");
  137. var manip = brikz.ensure("manipulation");
  138. /* Method not implemented handlers */
  139. var methods = [], checker = Object.create(null);
  140. this.selectors = [];
  141. this.get = function (string) {
  142. var index = this.selectors.indexOf(string);
  143. if(index !== -1) {
  144. return methods[index];
  145. }
  146. this.selectors.push(string);
  147. var selector = st.selector(string);
  148. checker[selector] = true;
  149. var method = {jsSelector: selector, fn: createHandler(selector)};
  150. methods.push(method);
  151. return method;
  152. };
  153. this.isSelector = function (selector) {
  154. return checker[selector];
  155. };
  156. /* Dnu handler method */
  157. function createHandler(selector) {
  158. return function() {
  159. var args = Array.prototype.slice.call(arguments);
  160. return brikz.messageSend.messageNotUnderstood(this, selector, args);
  161. };
  162. }
  163. this.installHandlers = function (klass) {
  164. for(var i=0; i<methods.length; i++) {
  165. manip.installMethodIfAbsent(methods[i], klass);
  166. }
  167. };
  168. }
  169. function ClassInitBrik(brikz, st) {
  170. var dnu = brikz.ensure("dnu");
  171. var manip = brikz.ensure("manipulation");
  172. var nil = brikz.ensure("root").nil;
  173. /* Initialize a class in its class hierarchy. Handle both classes and
  174. metaclasses. */
  175. st.init = function(klass) {
  176. st.initClass(klass);
  177. if(klass.klass && !klass.meta) {
  178. st.initClass(klass.klass);
  179. }
  180. };
  181. st.initClass = function(klass) {
  182. if(klass.wrapped) {
  183. copySuperclass(klass);
  184. }
  185. if(klass === st.Object || klass.wrapped) {
  186. dnu.installHandlers(klass);
  187. }
  188. };
  189. function copySuperclass(klass, superclass) {
  190. var inheritedMethods = Object.create(null);
  191. deinstallAllMethods(klass);
  192. for (superclass = superclass || klass.superclass;
  193. superclass && superclass !== nil;
  194. superclass = superclass.superclass) {
  195. for (var keys = Object.keys(superclass.methods), i = 0; i < keys.length; i++) {
  196. inheritMethodIfAbsent(superclass.methods[keys[i]]);
  197. }
  198. }
  199. manip.reinstallMethods(klass);
  200. function inheritMethodIfAbsent(method) {
  201. var selector = method.selector;
  202. //TODO: prepare klass methods into inheritedMethods to only test once
  203. if(klass.methods[selector] || inheritedMethods[selector]) {
  204. return;
  205. }
  206. manip.installMethod(method, klass);
  207. inheritedMethods[method.selector] = true;
  208. }
  209. }
  210. function deinstallAllMethods(klass) {
  211. var proto = klass.fn.prototype;
  212. for(var keys = Object.getOwnPropertyNames(proto), i=0; i<keys.length; i++) {
  213. var key = keys[i];
  214. if (dnu.isSelector(key)) {
  215. proto[key] = null;
  216. }
  217. }
  218. }
  219. }
  220. function ManipulationBrik(brikz, st) {
  221. this.installMethodIfAbsent = function (handler, klass) {
  222. if(!klass.fn.prototype[handler.jsSelector]) {
  223. installMethod(handler, klass);
  224. }
  225. };
  226. function installMethod (method, klass) {
  227. Object.defineProperty(klass.fn.prototype, method.jsSelector, {
  228. value: method.fn,
  229. enumerable: false, configurable: true, writable: true
  230. });
  231. }
  232. this.installMethod = installMethod;
  233. this.reinstallMethods = function (klass) {
  234. var methods = klass.methods;
  235. for(var keys = Object.keys(methods), i=0; i<keys.length; i++) {
  236. installMethod(methods[keys[i]], klass);
  237. }
  238. };
  239. }
  240. function ClassesBrik(brikz, st) {
  241. var org = brikz.ensure("organize");
  242. var nil = brikz.ensure("root").nil;
  243. function SmalltalkPackage() {}
  244. function SmalltalkBehavior() {}
  245. function SmalltalkClass() {}
  246. function SmalltalkMetaclass() {}
  247. inherits(SmalltalkPackage, SmalltalkObject);
  248. inherits(SmalltalkBehavior, SmalltalkObject);
  249. inherits(SmalltalkClass, SmalltalkBehavior);
  250. inherits(SmalltalkMetaclass, SmalltalkBehavior);
  251. SmalltalkMetaclass.prototype.meta = true;
  252. this.__init__ = function () {
  253. st.addPackage("Kernel-Classes");
  254. st.wrapClassName("Behavior", "Kernel-Classes", SmalltalkBehavior, st.Object, false);
  255. st.wrapClassName("Metaclass", "Kernel-Classes", SmalltalkMetaclass, st.Behavior, false);
  256. st.wrapClassName("Class", "Kernel-Classes", SmalltalkClass, st.Behavior, false);
  257. st.Object.klass.superclass = st.Class;
  258. addSubclass(st.Object.klass);
  259. st.wrapClassName("Package", "Kernel-Infrastructure", SmalltalkPackage, st.Object, false);
  260. };
  261. /* Smalltalk classes */
  262. var classes = [];
  263. var wrappedClasses = [];
  264. /* We hold all Packages in a separate Object */
  265. st.packages = {};
  266. /* Smalltalk package creation. To add a Package, use smalltalk.addPackage() */
  267. function pkg(spec) {
  268. var that = new SmalltalkPackage();
  269. that.pkgName = spec.pkgName;
  270. org.setupPackageOrganization(that);
  271. that.properties = spec.properties || {};
  272. return that;
  273. }
  274. /* Smalltalk class creation. A class is an instance of an automatically
  275. created metaclass object. Newly created classes (not their metaclass)
  276. should be added to the smalltalk object, see smalltalk.addClass().
  277. Superclass linking is *not* handled here, see smalltalk.init() */
  278. function klass(spec) {
  279. spec = spec || {};
  280. var meta = metaclass(spec);
  281. var that = meta.instanceClass;
  282. that.fn = spec.fn || inherits(function () {}, spec.superclass.fn);
  283. that.subclasses = [];
  284. setupClass(that, spec);
  285. that.className = spec.className;
  286. that.wrapped = spec.wrapped || false;
  287. meta.className = spec.className + ' class';
  288. if(spec.superclass) {
  289. that.superclass = spec.superclass;
  290. meta.superclass = spec.superclass.klass;
  291. }
  292. return that;
  293. }
  294. function metaclass(spec) {
  295. spec = spec || {};
  296. var that = new SmalltalkMetaclass();
  297. that.fn = inherits(function () {}, spec.superclass ? spec.superclass.klass.fn : SmalltalkClass);
  298. that.instanceClass = new that.fn();
  299. setupClass(that);
  300. return that;
  301. }
  302. SmalltalkBehavior.prototype.toString = function () {
  303. return 'Smalltalk ' + this.className;
  304. };
  305. function setupClass(klass, spec) {
  306. spec = spec || {};
  307. klass.iVarNames = spec.iVarNames || [];
  308. klass.pkg = spec.pkg;
  309. org.setupClassOrganization(klass);
  310. Object.defineProperty(klass, "methods", {
  311. value: Object.create(null),
  312. enumerable: false, configurable: true, writable: true
  313. });
  314. Object.defineProperty(klass.fn.prototype, "klass", {
  315. value: klass,
  316. enumerable: false, configurable: true, writable: true
  317. });
  318. }
  319. /* Add a package to the smalltalk.packages object, creating a new one if needed.
  320. If pkgName is null or empty we return nil, which is an allowed package for a class.
  321. If package already exists we still update the properties of it. */
  322. st.addPackage = function(pkgName, properties) {
  323. if(!pkgName) {return nil;}
  324. if(!(st.packages[pkgName])) {
  325. st.packages[pkgName] = pkg({
  326. pkgName: pkgName,
  327. properties: properties
  328. });
  329. } else {
  330. if(properties) {
  331. st.packages[pkgName].properties = properties;
  332. }
  333. }
  334. return st.packages[pkgName];
  335. };
  336. SmalltalkPackage.prototype.withDefaultTransport = function () {
  337. var defaultTransportType = st.getDefaultTransportType();
  338. if (this.transport) {
  339. throw new Error("Cannot set default transport; transport already set");
  340. }
  341. if (defaultTransportType) {
  342. this.transport = { type: defaultTransportType };
  343. }
  344. return this;
  345. };
  346. /* Add a class to the smalltalk object, creating a new one if needed.
  347. A Package is lazily created if it does not exist with given name. */
  348. st.addClass = function(className, superclass, iVarNames, pkgName) {
  349. if (superclass == nil) { superclass = null; }
  350. rawAddClass(pkgName, className, superclass, iVarNames, false, null);
  351. };
  352. function rawAddClass(pkgName, className, superclass, iVarNames, wrapped, fn) {
  353. var pkg = st.packages[pkgName];
  354. if (!pkg) {
  355. throw new Error("Missing package "+pkgName);
  356. }
  357. if(st[className] && st[className].superclass == superclass) {
  358. // st[className].superclass = superclass;
  359. st[className].iVarNames = iVarNames || [];
  360. if (pkg) st[className].pkg = pkg;
  361. if (fn) {
  362. fn.prototype = st[className].fn.prototype;
  363. st[className].fn = fn;
  364. fn.prototype.constructor = fn;
  365. }
  366. } else {
  367. if(st[className]) {
  368. st.removeClass(st[className]);
  369. }
  370. st[className] = klass({
  371. className: className,
  372. superclass: superclass,
  373. pkg: pkg,
  374. iVarNames: iVarNames,
  375. fn: fn,
  376. wrapped: wrapped
  377. });
  378. addSubclass(st[className]);
  379. }
  380. classes.addElement(st[className]);
  381. org.addOrganizationElement(pkg, st[className]);
  382. }
  383. st.removeClass = function(klass) {
  384. org.removeOrganizationElement(klass.pkg, klass);
  385. classes.removeElement(klass);
  386. removeSubclass(klass);
  387. delete st[klass.className];
  388. };
  389. function addSubclass(klass) {
  390. if(klass.superclass) {
  391. klass.superclass.subclasses.addElement(klass);
  392. }
  393. }
  394. function removeSubclass(klass) {
  395. if(klass.superclass) {
  396. klass.superclass.subclasses.removeElement(klass);
  397. }
  398. }
  399. /* Create a new class wrapping a JavaScript constructor, and add it to the
  400. global smalltalk object. Package is lazily created if it does not exist with given name. */
  401. st.wrapClassName = function(className, pkgName, fn, superclass, wrapped) {
  402. wrapped = wrapped !== false;
  403. rawAddClass(pkgName, className, superclass, st[className] && st[className].iVarNames, wrapped, fn);
  404. if(wrapped) {
  405. wrappedClasses.addElement(st[className]);
  406. }
  407. };
  408. /* Manually set the constructor of an existing Smalltalk klass, making it a wrapped class. */
  409. st.setClassConstructor = function(klass, constructor) {
  410. wrappedClasses.addElement(klass);
  411. klass.wrapped = true;
  412. klass.fn = constructor;
  413. // The fn property changed. We need to add back the klass property to the prototype
  414. Object.defineProperty(klass.fn.prototype, "klass", {
  415. value: klass,
  416. enumerable: false, configurable: true, writable: true
  417. });
  418. st.initClass(klass);
  419. };
  420. /* Create an alias for an existing class */
  421. st.alias = function(klass, alias) {
  422. st[alias] = klass;
  423. };
  424. /* Answer all registered Smalltalk classes */
  425. //TODO: remove the function and make smalltalk.classes an array
  426. st.classes = function() {
  427. return classes;
  428. };
  429. st.wrappedClasses = function() {
  430. return wrappedClasses;
  431. };
  432. // Still used, but could go away now that subclasses are stored
  433. // into classes directly.
  434. st.allSubclasses = function(klass) {
  435. return klass._allSubclasses();
  436. };
  437. }
  438. function MethodsBrik(brikz, st) {
  439. var manip = brikz.ensure("manipulation");
  440. var org = brikz.ensure("organize");
  441. var stInit = brikz.ensure("stInit");
  442. var dnu = brikz.ensure("dnu");
  443. brikz.ensure("selectorConversion");
  444. brikz.ensure("classes");
  445. brikz.ensure("classInit");
  446. function SmalltalkMethod() {}
  447. inherits(SmalltalkMethod, SmalltalkObject);
  448. this.__init__ = function () {
  449. st.addPackage("Kernel-Methods");
  450. st.wrapClassName("CompiledMethod", "Kernel-Methods", SmalltalkMethod, st.Object, false);
  451. };
  452. /* Smalltalk method object. To add a method to a class,
  453. use smalltalk.addMethod() */
  454. st.method = function(spec) {
  455. var that = new SmalltalkMethod();
  456. that.selector = spec.selector;
  457. that.jsSelector = spec.jsSelector;
  458. that.args = spec.args || {};
  459. that.category = spec.category;
  460. that.source = spec.source;
  461. that.messageSends = spec.messageSends || [];
  462. that.referencedClasses = spec.referencedClasses || [];
  463. that.fn = spec.fn;
  464. return that;
  465. };
  466. function installNewDnuHandler(newHandler) {
  467. manip.installMethodIfAbsent(newHandler, st.Object);
  468. var wrappedClasses = st.wrappedClasses();
  469. for(var i = 0; i < wrappedClasses.length; i++) {
  470. manip.installMethodIfAbsent(newHandler, wrappedClasses[i]);
  471. }
  472. }
  473. /* Add/remove a method to/from a class */
  474. st.addMethod = function (method, klass) {
  475. if (!(method.jsSelector)) {
  476. method.jsSelector = st.selector(method.selector);
  477. }
  478. manip.installMethod(method, klass);
  479. klass.methods[method.selector] = method;
  480. method.methodClass = klass;
  481. // During the bootstrap, #addCompiledMethod is not used.
  482. // Therefore we populate the organizer here too
  483. org.addOrganizationElement(klass, method.category);
  484. propagateMethodChange(klass);
  485. for(var i=0; i<method.messageSends.length; i++) {
  486. var dnuHandler = dnu.get(method.messageSends[i]);
  487. if(stInit.initialized()) {
  488. installNewDnuHandler(dnuHandler);
  489. }
  490. }
  491. };
  492. function propagateMethodChange(klass) {
  493. // If already initialized (else it will be done later anyway),
  494. // re-initialize all subclasses to ensure the method change
  495. // propagation (for wrapped classes, not using the prototype
  496. // chain).
  497. //TODO: optimize, only one method need to be updated, not all of them
  498. if (stInit.initialized()) {
  499. st.allSubclasses(klass).forEach(function (subclass) {
  500. st.initClass(subclass);
  501. });
  502. }
  503. }
  504. st.removeMethod = function(method, klass) {
  505. if (klass !== method.methodClass) {
  506. throw new Error(
  507. "Refusing to remove method "
  508. + method.methodClass.className+">>"+method.selector
  509. + " from different class "
  510. + klass.className);
  511. }
  512. delete klass.fn.prototype[st.selector(method.selector)];
  513. delete klass.methods[method.selector];
  514. st.initClass(klass);
  515. propagateMethodChange(klass);
  516. // Do *not* delete protocols from here.
  517. // This is handled by #removeCompiledMethod
  518. };
  519. /* Answer all method selectors based on dnu handlers */
  520. st.allSelectors = function() {
  521. return dnu.selectors;
  522. };
  523. }
  524. function AugmentsBrik(brikz, st) {
  525. /* Make sure that console is defined */
  526. if(typeof console === "undefined") {
  527. this.console = {
  528. log: function() {},
  529. warn: function() {},
  530. info: function() {},
  531. debug: function() {},
  532. error: function() {}
  533. };
  534. }
  535. /* Array extensions */
  536. Array.prototype.addElement = function(el) {
  537. if(typeof el === 'undefined') { return; }
  538. if(this.indexOf(el) == -1) {
  539. this.push(el);
  540. }
  541. };
  542. Array.prototype.removeElement = function(el) {
  543. var i = this.indexOf(el);
  544. if (i !== -1) { this.splice(i, 1); }
  545. };
  546. }
  547. function SmalltalkInitBrik(brikz, st) {
  548. brikz.ensure("classInit");
  549. brikz.ensure("classes");
  550. var nil = brikz.ensure("root").nil;
  551. var initialized = false;
  552. /* Smalltalk initialization. Called on page load */
  553. st.initialize = function() {
  554. if(initialized) { return; }
  555. st.classes().forEach(function(klass) {
  556. st.init(klass);
  557. });
  558. runnable();
  559. st.classes().forEach(function(klass) {
  560. klass._initialize();
  561. });
  562. initialized = true;
  563. };
  564. this.initialized = function () {
  565. return initialized;
  566. };
  567. this.__init__ = function () {
  568. st.addPackage("Kernel-Methods");
  569. st.wrapClassName("Number", "Kernel-Objects", Number, st.Object);
  570. st.wrapClassName("BlockClosure", "Kernel-Methods", Function, st.Object);
  571. st.wrapClassName("Boolean", "Kernel-Objects", Boolean, st.Object);
  572. st.wrapClassName("Date", "Kernel-Objects", Date, st.Object);
  573. st.addPackage("Kernel-Collections");
  574. st.addClass("Collection", st.Object, null, "Kernel-Collections");
  575. st.addClass("IndexableCollection", st.Collection, null, "Kernel-Collections");
  576. st.addClass("SequenceableCollection", st.IndexableCollection, null, "Kernel-Collections");
  577. st.addClass("CharacterArray", st.SequenceableCollection, null, "Kernel-Collections");
  578. st.wrapClassName("String", "Kernel-Collections", String, st.CharacterArray);
  579. st.wrapClassName("Array", "Kernel-Collections", Array, st.SequenceableCollection);
  580. st.wrapClassName("RegularExpression", "Kernel-Collections", RegExp, st.Object);
  581. st.addPackage("Kernel-Exceptions");
  582. st.wrapClassName("Error", "Kernel-Exceptions", Error, st.Object);
  583. /* Alias definitions */
  584. st.alias(st.Array, "OrderedCollection");
  585. st.alias(st.Date, "Time");
  586. };
  587. }
  588. function PrimitivesBrik(brikz, st) {
  589. /* Unique ID number generator */
  590. var oid = 0;
  591. st.nextId = function() {
  592. oid += 1;
  593. return oid;
  594. };
  595. /* Converts a JavaScript object to valid Smalltalk Object */
  596. st.readJSObject = function(js) {
  597. var object = js;
  598. var readObject = (js.constructor === Object);
  599. var readArray = (js.constructor === Array);
  600. if(readObject) {
  601. object = st.Dictionary._new();
  602. }
  603. for(var i in js) {
  604. if(readObject) {
  605. object._at_put_(i, st.readJSObject(js[i]));
  606. }
  607. if(readArray) {
  608. object[i] = st.readJSObject(js[i]);
  609. }
  610. }
  611. return object;
  612. };
  613. /* Boolean assertion */
  614. st.assert = function(shouldBeBoolean) {
  615. if (undefined !== shouldBeBoolean && shouldBeBoolean.klass === st.Boolean) {
  616. return shouldBeBoolean == true;
  617. } else {
  618. st.NonBooleanReceiver._new()._object_(shouldBeBoolean)._signal();
  619. }
  620. };
  621. /* List of all reserved words in JavaScript. They may not be used as variables
  622. in Smalltalk. */
  623. // list of reserved JavaScript keywords as of
  624. // http://es5.github.com/#x7.6.1.1
  625. // and
  626. // http://people.mozilla.org/~jorendorff/es6-draft.html#sec-7.6.1
  627. st.reservedWords = ['break', 'case', 'catch', 'continue', 'debugger',
  628. 'default', 'delete', 'do', 'else', 'finally', 'for', 'function',
  629. 'if', 'in', 'instanceof', 'new', 'return', 'switch', 'this', 'throw',
  630. 'try', 'typeof', 'var', 'void', 'while', 'with',
  631. // ES5: future use: http://es5.github.com/#x7.6.1.2
  632. 'class', 'const', 'enum', 'export', 'extends', 'import', 'super',
  633. // ES5: future use in strict mode
  634. 'implements', 'interface', 'let', 'package', 'private', 'protected',
  635. 'public', 'static', 'yield'];
  636. st.globalJsVariables = ['jQuery', 'window', 'document', 'process', 'global'];
  637. }
  638. function RuntimeBrik(brikz, st) {
  639. brikz.ensure("selectorConversion");
  640. var nil = brikz.ensure("root").nil;
  641. function SmalltalkMethodContext(home, setup) {
  642. this.sendIdx = {};
  643. this.homeContext = home;
  644. this.setup = setup || function() {};
  645. }
  646. inherits(SmalltalkMethodContext, SmalltalkObject);
  647. this.__init__ = function () {
  648. st.addPackage("Kernel-Methods");
  649. st.wrapClassName("MethodContext", "Kernel-Methods", SmalltalkMethodContext, st.Object, false);
  650. // Fallbacks
  651. SmalltalkMethodContext.prototype.locals = {};
  652. SmalltalkMethodContext.prototype.receiver = null;
  653. SmalltalkMethodContext.prototype.selector = null;
  654. SmalltalkMethodContext.prototype.lookupClass = null;
  655. SmalltalkMethodContext.prototype.fill = function(receiver, selector, locals, lookupClass) {
  656. this.receiver = receiver;
  657. this.selector = selector;
  658. this.locals = locals || {};
  659. this.lookupClass = lookupClass;
  660. if(this.homeContext) {
  661. this.homeContext.evaluatedSelector = selector;
  662. }
  663. };
  664. SmalltalkMethodContext.prototype.fillBlock = function(locals, ctx, index) {
  665. this.locals = locals || {};
  666. this.outerContext = ctx;
  667. this.index = index || 0;
  668. };
  669. SmalltalkMethodContext.prototype.init = function() {
  670. var home = this.homeContext;
  671. if(home) {
  672. home.init();
  673. }
  674. this.setup(this);
  675. };
  676. SmalltalkMethodContext.prototype.method = function() {
  677. var method;
  678. var lookup = this.lookupClass || this.receiver.klass;
  679. while(!method && lookup) {
  680. method = lookup.methods[st.convertSelector(this.selector)];
  681. lookup = lookup.superclass;
  682. }
  683. return method;
  684. };
  685. };
  686. /* This is the current call context object. While it is publicly available,
  687. Use smalltalk.getThisContext() instead which will answer a safe copy of
  688. the current context */
  689. st.thisContext = undefined;
  690. st.withContext = function(worker, setup) {
  691. if(st.thisContext) {
  692. return inContext(worker, setup);
  693. } else {
  694. try {
  695. return inContext(worker, setup);
  696. } catch(error) {
  697. handleError(error);
  698. st.thisContext = null;
  699. }
  700. }
  701. };
  702. function inContext(worker, setup) {
  703. var context = pushContext(setup);
  704. var result = worker(context);
  705. popContext(context);
  706. return result;
  707. }
  708. function wrappedError(error) {
  709. var errorWrapper = st.JavaScriptException._on_(error);
  710. try { errorWrapper._signal(); } catch (ex) {}
  711. errorWrapper._context_(st.getThisContext());
  712. return errorWrapper;
  713. }
  714. /* Handles Smalltalk errors. Triggers the registered ErrorHandler
  715. (See the Smalltalk class ErrorHandler and its subclasses */
  716. function handleError(error) {
  717. if (!error.smalltalkError) {
  718. error = wrappedError(error);
  719. }
  720. st.ErrorHandler._current()._handleError_(error);
  721. // Throw the exception anyway, as we want to stop
  722. // the execution to avoid infinite loops
  723. // Update: do not throw the exception. It's really annoying.
  724. // throw error;
  725. }
  726. /* Handle thisContext pseudo variable */
  727. st.getThisContext = function() {
  728. if(st.thisContext) {
  729. st.thisContext.init();
  730. return st.thisContext;
  731. } else {
  732. return nil;
  733. }
  734. };
  735. function pushContext(setup) {
  736. return st.thisContext = new SmalltalkMethodContext(st.thisContext, setup);
  737. }
  738. function popContext(context) {
  739. st.thisContext = context.homeContext;
  740. }
  741. }
  742. function MessageSendBrik(brikz, st) {
  743. brikz.ensure("selectorConversion");
  744. var nil = brikz.ensure("root").nil;
  745. /* Handles unhandled errors during message sends */
  746. // simply send the message and handle #dnu:
  747. st.send = function(receiver, selector, args, klass) {
  748. var method;
  749. if(receiver === null) {
  750. receiver = nil;
  751. }
  752. method = klass ? klass.fn.prototype[selector] : receiver.klass && receiver[selector];
  753. if(method) {
  754. return method.apply(receiver, args);
  755. } else {
  756. return messageNotUnderstood(receiver, selector, args);
  757. }
  758. };
  759. /* Handles #dnu: *and* JavaScript method calls.
  760. if the receiver has no klass, we consider it a JS object (outside of the
  761. Amber system). Else assume that the receiver understands #doesNotUnderstand: */
  762. function messageNotUnderstood(receiver, selector, args) {
  763. /* Handles JS method calls. */
  764. if(receiver.klass === undefined || receiver.allowJavaScriptCalls) {
  765. return callJavaScriptMethod(receiver, selector, args);
  766. }
  767. /* Handles not understood messages. Also see the Amber counter-part
  768. Object>>doesNotUnderstand: */
  769. return receiver._doesNotUnderstand_(
  770. st.Message._new()
  771. ._selector_(st.convertSelector(selector))
  772. ._arguments_(args)
  773. );
  774. }
  775. /* Call a method of a JS object, or answer a property if it exists.
  776. Else try wrapping a JSObjectProxy around the receiver.
  777. If the object property is a function, then call it, except if it starts with
  778. an uppercase character (we probably want to answer the function itself in this
  779. case and send it #new from Amber).
  780. Converts keyword-based selectors by using the first
  781. keyword only, but keeping all message arguments.
  782. Example:
  783. "self do: aBlock with: anObject" -> "self.do(aBlock, anObject)" */
  784. function callJavaScriptMethod(receiver, selector, args) {
  785. var jsSelector = selector._asJavaScriptSelector();
  786. var jsProperty = receiver[jsSelector];
  787. if(typeof jsProperty === "function" && !/^[A-Z]/.test(jsSelector)) {
  788. return jsProperty.apply(receiver, args);
  789. } else if(jsProperty !== undefined) {
  790. if(args[0]) {
  791. receiver[jsSelector] = args[0];
  792. return nil;
  793. } else {
  794. return jsProperty;
  795. }
  796. }
  797. return st.send(st.JSObjectProxy._on_(receiver), selector, args);
  798. }
  799. if(typeof jQuery !== "undefined") {
  800. jQuery.allowJavaScriptCalls = true;
  801. }
  802. this.messageNotUnderstood = messageNotUnderstood;
  803. }
  804. function SelectorConversionBrik(brikz, st) {
  805. /* Convert a Smalltalk selector into a JS selector */
  806. st.selector = function(string) {
  807. var selector = '_' + string;
  808. selector = selector.replace(/:/g, '_');
  809. selector = selector.replace(/[\&]/g, '_and');
  810. selector = selector.replace(/[\|]/g, '_or');
  811. selector = selector.replace(/[+]/g, '_plus');
  812. selector = selector.replace(/-/g, '_minus');
  813. selector = selector.replace(/[*]/g ,'_star');
  814. selector = selector.replace(/[\/]/g ,'_slash');
  815. selector = selector.replace(/[\\]/g ,'_backslash');
  816. selector = selector.replace(/[\~]/g ,'_tild');
  817. selector = selector.replace(/>/g ,'_gt');
  818. selector = selector.replace(/</g ,'_lt');
  819. selector = selector.replace(/=/g ,'_eq');
  820. selector = selector.replace(/,/g ,'_comma');
  821. selector = selector.replace(/[@]/g ,'_at');
  822. return selector;
  823. };
  824. /* Convert a string to a valid smalltalk selector.
  825. if you modify the following functions, also change String>>asSelector
  826. accordingly */
  827. st.convertSelector = function(selector) {
  828. if(selector.match(/__/)) {
  829. return convertBinarySelector(selector);
  830. } else {
  831. return convertKeywordSelector(selector);
  832. }
  833. };
  834. function convertKeywordSelector(selector) {
  835. return selector.replace(/^_/, '').replace(/_/g, ':');
  836. }
  837. function convertBinarySelector(selector) {
  838. return selector
  839. .replace(/^_/, '')
  840. .replace(/_and/g, '&')
  841. .replace(/_or/g, '|')
  842. .replace(/_plus/g, '+')
  843. .replace(/_minus/g, '-')
  844. .replace(/_star/g, '*')
  845. .replace(/_slash/g, '/')
  846. .replace(/_backslash/g, '\\')
  847. .replace(/_tild/g, '~')
  848. .replace(/_gt/g, '>')
  849. .replace(/_lt/g, '<')
  850. .replace(/_eq/g, '=')
  851. .replace(/_comma/g, ',')
  852. .replace(/_at/g, '@');
  853. }
  854. }
  855. /* Adds AMD and requirejs related methods to the smalltalk object */
  856. function AMDBrik(brikz, st) {
  857. this.__init__ = function () {
  858. st.amdRequire = st.amdRequire || null;
  859. st.defaultTransportType = st.defaultTransportType || "amd";
  860. st.defaultAmdNamespace = st.defaultAmdNamespace || "amber_core";
  861. };
  862. }
  863. /* Making smalltalk that can load */
  864. brikz.root = RootBrik;
  865. brikz.dnu = DNUBrik;
  866. brikz.organize = OrganizeBrik;
  867. brikz.selectorConversion = SelectorConversionBrik;
  868. brikz.classInit = ClassInitBrik;
  869. brikz.manipulation = ManipulationBrik;
  870. brikz.classes = ClassesBrik;
  871. brikz.methods = MethodsBrik;
  872. brikz.stInit = SmalltalkInitBrik;
  873. brikz.augments = AugmentsBrik;
  874. brikz.amdBrik = AMDBrik;
  875. brikz.rebuild();
  876. /* Making smalltalk that can run */
  877. function runnable () {
  878. brikz.messageSend = MessageSendBrik;
  879. brikz.runtime = RuntimeBrik;
  880. brikz.primitives = PrimitivesBrik;
  881. brikz.rebuild();
  882. };
  883. return { smalltalk: api, nil: brikz.root.nil };
  884. });