1
0

boot.js 29 KB

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