misceval.js 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277
  1. /**
  2. * @namespace Sk.misceval
  3. *
  4. */
  5. Sk.misceval = {};
  6. /*
  7. Suspension object format:
  8. {resume: function() {...}, // the continuation - returns either another suspension or the return value
  9. data: <copied down from innermost level>,
  10. optional: <if true, can be resumed immediately (eg debug stops)>,
  11. child: <Suspension, or null if we are the innermost level>,
  12. $blk: <>, $loc: <>, $gbl: <>, $exc: <>, $err: <>, [$cell: <>],
  13. }
  14. */
  15. /**
  16. *
  17. * Hi kids lets make a suspension...
  18. * @constructor
  19. * @param{function(?)=} resume A function to be called on resume. child is resumed first and its return value is passed to this function.
  20. * @param{Object=} child A child suspension. 'optional' will be copied from here if supplied.
  21. * @param{Object=} data Data attached to this suspension. Will be copied from child if not supplied.
  22. */
  23. Sk.misceval.Suspension = function Suspension(resume, child, data) {
  24. this.$isSuspension = true;
  25. if (resume !== undefined && child !== undefined) {
  26. this.resume = function() { return resume(child.resume()); };
  27. }
  28. this.child = child;
  29. this.optional = child !== undefined && child.optional;
  30. if (data === undefined && child !== undefined) {
  31. this.data = child.data;
  32. } else {
  33. this.data = data;
  34. }
  35. };
  36. goog.exportSymbol("Sk.misceval.Suspension", Sk.misceval.Suspension);
  37. /**
  38. *
  39. * Well this seems pretty obvious by the name what it should do..
  40. *
  41. * @param{Sk.misceval.Suspension} susp
  42. * @param{string=} message
  43. */
  44. Sk.misceval.retryOptionalSuspensionOrThrow = function (susp, message) {
  45. while (susp instanceof Sk.misceval.Suspension) {
  46. if (!susp.optional) {
  47. throw new Sk.builtin.SuspensionError(message || "Cannot call a function that blocks or suspends here");
  48. }
  49. susp = susp.resume();
  50. }
  51. return susp;
  52. };
  53. goog.exportSymbol("Sk.misceval.retryOptionalSuspensionOrThrow", Sk.misceval.retryOptionalSuspensionOrThrow);
  54. /**
  55. * Check if the given object is valid to use as an index. Only ints, or if the object has an `__index__` method.
  56. * @param o
  57. * @returns {boolean}
  58. */
  59. Sk.misceval.isIndex = function (o) {
  60. if (Sk.builtin.checkInt(o)) {
  61. return true;
  62. }
  63. if (Sk.abstr.lookupSpecial(o, "__index__")) {
  64. return true;
  65. }
  66. return false;
  67. };
  68. goog.exportSymbol("Sk.misceval.isIndex", Sk.misceval.isIndex);
  69. Sk.misceval.asIndex = function (o) {
  70. var idxfn, ret;
  71. if (!Sk.misceval.isIndex(o)) {
  72. return undefined;
  73. }
  74. if (o === null) {
  75. return undefined;
  76. }
  77. if (o === true) {
  78. return 1;
  79. }
  80. if (o === false) {
  81. return 0;
  82. }
  83. if (typeof o === "number") {
  84. return o;
  85. }
  86. if (o.constructor === Sk.builtin.int_) {
  87. return o.v;
  88. }
  89. if (o.constructor === Sk.builtin.lng) {
  90. return o.tp$index();
  91. }
  92. if (o.constructor === Sk.builtin.bool) {
  93. return Sk.builtin.asnum$(o);
  94. }
  95. idxfn = Sk.abstr.lookupSpecial(o, "__index__");
  96. if (idxfn) {
  97. ret = Sk.misceval.callsim(idxfn, o);
  98. if (!Sk.builtin.checkInt(ret)) {
  99. throw new Sk.builtin.TypeError("__index__ returned non-(int,long) (type " +
  100. Sk.abstr.typeName(ret) + ")");
  101. }
  102. return Sk.builtin.asnum$(ret);
  103. }
  104. goog.asserts.fail("todo asIndex;");
  105. };
  106. /**
  107. * return u[v:w]
  108. */
  109. Sk.misceval.applySlice = function (u, v, w, canSuspend) {
  110. var ihigh;
  111. var ilow;
  112. if (u.sq$slice && Sk.misceval.isIndex(v) && Sk.misceval.isIndex(w)) {
  113. ilow = Sk.misceval.asIndex(v);
  114. if (ilow === undefined) {
  115. ilow = 0;
  116. }
  117. ihigh = Sk.misceval.asIndex(w);
  118. if (ihigh === undefined) {
  119. ihigh = 1e100;
  120. }
  121. return Sk.abstr.sequenceGetSlice(u, ilow, ihigh);
  122. }
  123. return Sk.abstr.objectGetItem(u, new Sk.builtin.slice(v, w, null), canSuspend);
  124. };
  125. goog.exportSymbol("Sk.misceval.applySlice", Sk.misceval.applySlice);
  126. /**
  127. * u[v:w] = x
  128. */
  129. Sk.misceval.assignSlice = function (u, v, w, x, canSuspend) {
  130. var slice;
  131. var ihigh;
  132. var ilow;
  133. if (u.sq$ass_slice && Sk.misceval.isIndex(v) && Sk.misceval.isIndex(w)) {
  134. ilow = Sk.misceval.asIndex(v) || 0;
  135. ihigh = Sk.misceval.asIndex(w) || 1e100;
  136. if (x === null) {
  137. Sk.abstr.sequenceDelSlice(u, ilow, ihigh);
  138. } else {
  139. Sk.abstr.sequenceSetSlice(u, ilow, ihigh, x);
  140. }
  141. } else {
  142. slice = new Sk.builtin.slice(v, w);
  143. if (x === null) {
  144. return Sk.abstr.objectDelItem(u, slice);
  145. } else {
  146. return Sk.abstr.objectSetItem(u, slice, x, canSuspend);
  147. }
  148. }
  149. };
  150. goog.exportSymbol("Sk.misceval.assignSlice", Sk.misceval.assignSlice);
  151. /**
  152. * Used by min() and max() to get an array from arbitrary input.
  153. * Note that this does no validation, just coercion.
  154. */
  155. Sk.misceval.arrayFromArguments = function (args) {
  156. // If args is not a single thing return as is
  157. var it, i;
  158. var res;
  159. var arg;
  160. if (args.length != 1) {
  161. return args;
  162. }
  163. arg = args[0];
  164. if (arg instanceof Sk.builtin.set) {
  165. // this is a Sk.builtin.set
  166. arg = arg.tp$iter().$obj;
  167. } else if (arg instanceof Sk.builtin.dict) {
  168. // this is a Sk.builtin.list
  169. arg = Sk.builtin.dict.prototype["keys"].func_code(arg);
  170. }
  171. // shouldn't else if here as the above may output lists to arg.
  172. if (arg instanceof Sk.builtin.list || arg instanceof Sk.builtin.tuple) {
  173. return arg.v;
  174. } else if (Sk.builtin.checkIterable(arg)) {
  175. // handle arbitrary iterable (strings, generators, etc.)
  176. res = [];
  177. for (it = Sk.abstr.iter(arg), i = it.tp$iternext();
  178. i !== undefined; i = it.tp$iternext()) {
  179. res.push(i);
  180. }
  181. return res;
  182. }
  183. throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(arg) + "' object is not iterable");
  184. };
  185. goog.exportSymbol("Sk.misceval.arrayFromArguments", Sk.misceval.arrayFromArguments);
  186. /**
  187. * for reversed comparison: Gt -> Lt, etc.
  188. */
  189. Sk.misceval.swappedOp_ = {
  190. "Eq" : "Eq",
  191. "NotEq": "NotEq",
  192. "Lt" : "GtE",
  193. "LtE" : "Gt",
  194. "Gt" : "LtE",
  195. "GtE" : "Lt",
  196. "Is" : "IsNot",
  197. "IsNot": "Is",
  198. "In_" : "NotIn",
  199. "NotIn": "In_"
  200. };
  201. /**
  202. * @param{*} v
  203. * @param{*} w
  204. * @param{string} op
  205. * @param{boolean=} canSuspend
  206. */
  207. Sk.misceval.richCompareBool = function (v, w, op, canSuspend) {
  208. // v and w must be Python objects. will return Javascript true or false for internal use only
  209. // if you want to return a value from richCompareBool to Python you must wrap as Sk.builtin.bool first
  210. var wname,
  211. vname,
  212. ret,
  213. swapped_method,
  214. method,
  215. swapped_shortcut,
  216. shortcut,
  217. v_has_shortcut,
  218. w_has_shortcut,
  219. op2method,
  220. op2shortcut,
  221. vcmp,
  222. wcmp,
  223. w_seq_type,
  224. w_num_type,
  225. v_seq_type,
  226. v_num_type,
  227. sequence_types,
  228. numeric_types,
  229. w_type,
  230. v_type;
  231. goog.asserts.assert((v !== null) && (v !== undefined), "passed null or undefined parameter to Sk.misceval.richCompareBool");
  232. goog.asserts.assert((w !== null) && (w !== undefined), "passed null or undefined parameter to Sk.misceval.richCompareBool");
  233. v_type = new Sk.builtin.type(v);
  234. w_type = new Sk.builtin.type(w);
  235. // Python has specific rules when comparing two different builtin types
  236. // currently, this code will execute even if the objects are not builtin types
  237. // but will fall through and not return anything in this section
  238. if ((v_type !== w_type) &&
  239. (op === "GtE" || op === "Gt" || op === "LtE" || op === "Lt")) {
  240. // note: sets are omitted here because they can only be compared to other sets
  241. numeric_types = [Sk.builtin.float_.prototype.ob$type,
  242. Sk.builtin.int_.prototype.ob$type,
  243. Sk.builtin.lng.prototype.ob$type,
  244. Sk.builtin.bool.prototype.ob$type];
  245. sequence_types = [Sk.builtin.dict.prototype.ob$type,
  246. Sk.builtin.enumerate.prototype.ob$type,
  247. Sk.builtin.list.prototype.ob$type,
  248. Sk.builtin.str.prototype.ob$type,
  249. Sk.builtin.tuple.prototype.ob$type];
  250. v_num_type = numeric_types.indexOf(v_type);
  251. v_seq_type = sequence_types.indexOf(v_type);
  252. w_num_type = numeric_types.indexOf(w_type);
  253. w_seq_type = sequence_types.indexOf(w_type);
  254. // NoneTypes are considered less than any other type in Python
  255. // note: this only handles comparing NoneType with any non-NoneType.
  256. // Comparing NoneType with NoneType is handled further down.
  257. if (v_type === Sk.builtin.none.prototype.ob$type) {
  258. switch (op) {
  259. case "Lt":
  260. return true;
  261. case "LtE":
  262. return true;
  263. case "Gt":
  264. return false;
  265. case "GtE":
  266. return false;
  267. }
  268. }
  269. if (w_type === Sk.builtin.none.prototype.ob$type) {
  270. switch (op) {
  271. case "Lt":
  272. return false;
  273. case "LtE":
  274. return false;
  275. case "Gt":
  276. return true;
  277. case "GtE":
  278. return true;
  279. }
  280. }
  281. // numeric types are always considered smaller than sequence types in Python
  282. if (v_num_type !== -1 && w_seq_type !== -1) {
  283. switch (op) {
  284. case "Lt":
  285. return true;
  286. case "LtE":
  287. return true;
  288. case "Gt":
  289. return false;
  290. case "GtE":
  291. return false;
  292. }
  293. }
  294. if (v_seq_type !== -1 && w_num_type !== -1) {
  295. switch (op) {
  296. case "Lt":
  297. return false;
  298. case "LtE":
  299. return false;
  300. case "Gt":
  301. return true;
  302. case "GtE":
  303. return true;
  304. }
  305. }
  306. // in Python, different sequence types are ordered alphabetically
  307. // by name so that dict < list < str < tuple
  308. if (v_seq_type !== -1 && w_seq_type !== -1) {
  309. switch (op) {
  310. case "Lt":
  311. return v_seq_type < w_seq_type;
  312. case "LtE":
  313. return v_seq_type <= w_seq_type;
  314. case "Gt":
  315. return v_seq_type > w_seq_type;
  316. case "GtE":
  317. return v_seq_type >= w_seq_type;
  318. }
  319. }
  320. }
  321. // handle identity and membership comparisons
  322. if (op === "Is") {
  323. if (v instanceof Sk.builtin.int_ && w instanceof Sk.builtin.int_) {
  324. return v.numberCompare(w) === 0;
  325. } else if (v instanceof Sk.builtin.float_ && w instanceof Sk.builtin.float_) {
  326. return v.numberCompare(w) === 0;
  327. } else if (v instanceof Sk.builtin.lng && w instanceof Sk.builtin.lng) {
  328. return v.longCompare(w) === 0;
  329. }
  330. return v === w;
  331. }
  332. if (op === "IsNot") {
  333. if (v instanceof Sk.builtin.int_ && w instanceof Sk.builtin.int_) {
  334. return v.numberCompare(w) !== 0;
  335. } else if (v instanceof Sk.builtin.float_ && w instanceof Sk.builtin.float_) {
  336. return v.numberCompare(w) !== 0;
  337. }else if (v instanceof Sk.builtin.lng && w instanceof Sk.builtin.lng) {
  338. return v.longCompare(w) !== 0;
  339. }
  340. return v !== w;
  341. }
  342. if (op === "In") {
  343. return Sk.misceval.chain(Sk.abstr.sequenceContains(w, v, canSuspend), Sk.misceval.isTrue);
  344. }
  345. if (op === "NotIn") {
  346. return Sk.misceval.chain(Sk.abstr.sequenceContains(w, v, canSuspend),
  347. function(x) { return !Sk.misceval.isTrue(x); });
  348. }
  349. // Call Javascript shortcut method if exists for either object
  350. op2shortcut = {
  351. "Eq" : "ob$eq",
  352. "NotEq": "ob$ne",
  353. "Gt" : "ob$gt",
  354. "GtE" : "ob$ge",
  355. "Lt" : "ob$lt",
  356. "LtE" : "ob$le"
  357. };
  358. shortcut = op2shortcut[op];
  359. v_has_shortcut = v.constructor.prototype.hasOwnProperty(shortcut);
  360. if (v_has_shortcut) {
  361. if ((ret = v[shortcut](w)) !== Sk.builtin.NotImplemented.NotImplemented$) {
  362. return Sk.misceval.isTrue(ret);
  363. }
  364. }
  365. swapped_shortcut = op2shortcut[Sk.misceval.swappedOp_[op]];
  366. w_has_shortcut = w.constructor.prototype.hasOwnProperty(swapped_shortcut);
  367. if (w_has_shortcut) {
  368. if ((ret = w[swapped_shortcut](v)) !== Sk.builtin.NotImplemented.NotImplemented$) {
  369. return Sk.misceval.isTrue(ret);
  370. }
  371. }
  372. // use comparison methods if they are given for either object
  373. if (v.tp$richcompare && (ret = v.tp$richcompare(w, op)) !== undefined) {
  374. if (ret != Sk.builtin.NotImplemented.NotImplemented$) {
  375. return Sk.misceval.isTrue(ret);
  376. }
  377. }
  378. if (w.tp$richcompare && (ret = w.tp$richcompare(v, Sk.misceval.swappedOp_[op])) !== undefined) {
  379. if (ret != Sk.builtin.NotImplemented.NotImplemented$) {
  380. return Sk.misceval.isTrue(ret);
  381. }
  382. }
  383. // depending on the op, try left:op:right, and if not, then
  384. // right:reversed-top:left
  385. op2method = {
  386. "Eq" : "__eq__",
  387. "NotEq": "__ne__",
  388. "Gt" : "__gt__",
  389. "GtE" : "__ge__",
  390. "Lt" : "__lt__",
  391. "LtE" : "__le__"
  392. };
  393. method = Sk.abstr.lookupSpecial(v, op2method[op]);
  394. if (method && !v_has_shortcut) {
  395. ret = Sk.misceval.callsim(method, v, w);
  396. if (ret != Sk.builtin.NotImplemented.NotImplemented$) {
  397. return Sk.misceval.isTrue(ret);
  398. }
  399. }
  400. swapped_method = Sk.abstr.lookupSpecial(w, op2method[Sk.misceval.swappedOp_[op]]);
  401. if (swapped_method && !w_has_shortcut) {
  402. ret = Sk.misceval.callsim(swapped_method, w, v);
  403. if (ret != Sk.builtin.NotImplemented.NotImplemented$) {
  404. return Sk.misceval.isTrue(ret);
  405. }
  406. }
  407. vcmp = Sk.abstr.lookupSpecial(v, "__cmp__");
  408. if (vcmp) {
  409. try {
  410. ret = Sk.misceval.callsim(vcmp, v, w);
  411. if (Sk.builtin.checkNumber(ret)) {
  412. ret = Sk.builtin.asnum$(ret);
  413. if (op === "Eq") {
  414. return ret === 0;
  415. } else if (op === "NotEq") {
  416. return ret !== 0;
  417. } else if (op === "Lt") {
  418. return ret < 0;
  419. } else if (op === "Gt") {
  420. return ret > 0;
  421. } else if (op === "LtE") {
  422. return ret <= 0;
  423. } else if (op === "GtE") {
  424. return ret >= 0;
  425. }
  426. }
  427. if (ret !== Sk.builtin.NotImplemented.NotImplemented$) {
  428. throw new Sk.builtin.TypeError("comparison did not return an int");
  429. }
  430. } catch (e) {
  431. throw new Sk.builtin.TypeError("comparison did not return an int");
  432. }
  433. }
  434. wcmp = Sk.abstr.lookupSpecial(w, "__cmp__");
  435. if (wcmp) {
  436. // note, flipped on return value and call
  437. try {
  438. ret = Sk.misceval.callsim(wcmp, w, v);
  439. if (Sk.builtin.checkNumber(ret)) {
  440. ret = Sk.builtin.asnum$(ret);
  441. if (op === "Eq") {
  442. return ret === 0;
  443. } else if (op === "NotEq") {
  444. return ret !== 0;
  445. } else if (op === "Lt") {
  446. return ret > 0;
  447. } else if (op === "Gt") {
  448. return ret < 0;
  449. } else if (op === "LtE") {
  450. return ret >= 0;
  451. } else if (op === "GtE") {
  452. return ret <= 0;
  453. }
  454. }
  455. if (ret !== Sk.builtin.NotImplemented.NotImplemented$) {
  456. throw new Sk.builtin.TypeError("comparison did not return an int");
  457. }
  458. } catch (e) {
  459. throw new Sk.builtin.TypeError("comparison did not return an int");
  460. }
  461. }
  462. // handle special cases for comparing None with None or Bool with Bool
  463. if (((v instanceof Sk.builtin.none) && (w instanceof Sk.builtin.none)) ||
  464. ((v instanceof Sk.builtin.bool) && (w instanceof Sk.builtin.bool))) {
  465. // Javascript happens to return the same values when comparing null
  466. // with null or true/false with true/false as Python does when
  467. // comparing None with None or True/False with True/False
  468. if (op === "Eq") {
  469. return v.v === w.v;
  470. }
  471. if (op === "NotEq") {
  472. return v.v !== w.v;
  473. }
  474. if (op === "Gt") {
  475. return v.v > w.v;
  476. }
  477. if (op === "GtE") {
  478. return v.v >= w.v;
  479. }
  480. if (op === "Lt") {
  481. return v.v < w.v;
  482. }
  483. if (op === "LtE") {
  484. return v.v <= w.v;
  485. }
  486. }
  487. // handle equality comparisons for any remaining objects
  488. if (op === "Eq") {
  489. if ((v instanceof Sk.builtin.str) && (w instanceof Sk.builtin.str)) {
  490. return v.v === w.v;
  491. }
  492. return v === w;
  493. }
  494. if (op === "NotEq") {
  495. if ((v instanceof Sk.builtin.str) && (w instanceof Sk.builtin.str)) {
  496. return v.v !== w.v;
  497. }
  498. return v !== w;
  499. }
  500. vname = Sk.abstr.typeName(v);
  501. wname = Sk.abstr.typeName(w);
  502. throw new Sk.builtin.ValueError("don't know how to compare '" + vname + "' and '" + wname + "'");
  503. };
  504. goog.exportSymbol("Sk.misceval.richCompareBool", Sk.misceval.richCompareBool);
  505. Sk.misceval.objectRepr = function (v) {
  506. goog.asserts.assert(v !== undefined, "trying to repr undefined");
  507. if ((v === null) || (v instanceof Sk.builtin.none)) {
  508. return new Sk.builtin.str("None");
  509. } else if (v === true) {
  510. // todo; these should be consts
  511. return new Sk.builtin.str("True");
  512. } else if (v === false) {
  513. return new Sk.builtin.str("False");
  514. } else if (typeof v === "number") {
  515. return new Sk.builtin.str("" + v);
  516. } else if (!v["$r"]) {
  517. if (v.tp$name) {
  518. return new Sk.builtin.str("<" + v.tp$name + " object>");
  519. } else {
  520. return new Sk.builtin.str("<unknown>");
  521. }
  522. } else if (v.constructor === Sk.builtin.float_) {
  523. if (v.v === Infinity) {
  524. return new Sk.builtin.str("inf");
  525. } else if (v.v === -Infinity) {
  526. return new Sk.builtin.str("-inf");
  527. } else {
  528. return v["$r"]();
  529. }
  530. } else if (v.constructor === Sk.builtin.int_) {
  531. return v["$r"]();
  532. } else {
  533. return v["$r"]();
  534. }
  535. };
  536. goog.exportSymbol("Sk.misceval.objectRepr", Sk.misceval.objectRepr);
  537. Sk.misceval.opAllowsEquality = function (op) {
  538. switch (op) {
  539. case "LtE":
  540. case "Eq":
  541. case "GtE":
  542. return true;
  543. }
  544. return false;
  545. };
  546. goog.exportSymbol("Sk.misceval.opAllowsEquality", Sk.misceval.opAllowsEquality);
  547. Sk.misceval.isTrue = function (x) {
  548. var ret;
  549. if (x === true) {
  550. return true;
  551. }
  552. if (x === false) {
  553. return false;
  554. }
  555. if (x === null) {
  556. return false;
  557. }
  558. if (x.constructor === Sk.builtin.none) {
  559. return false;
  560. }
  561. if (x.constructor === Sk.builtin.NotImplemented) {
  562. return false;
  563. }
  564. if (x.constructor === Sk.builtin.bool) {
  565. return x.v;
  566. }
  567. if (typeof x === "number") {
  568. return x !== 0;
  569. }
  570. if (x instanceof Sk.builtin.lng) {
  571. return x.nb$nonzero();
  572. }
  573. if (x.constructor === Sk.builtin.int_) {
  574. return x.v !== 0;
  575. }
  576. if (x.constructor === Sk.builtin.float_) {
  577. return x.v !== 0;
  578. }
  579. if (x["__nonzero__"]) {
  580. ret = Sk.misceval.callsim(x["__nonzero__"], x);
  581. if (!Sk.builtin.checkInt(ret)) {
  582. throw new Sk.builtin.TypeError("__nonzero__ should return an int");
  583. }
  584. return Sk.builtin.asnum$(ret) !== 0;
  585. }
  586. if (x["__len__"]) {
  587. ret = Sk.misceval.callsim(x["__len__"], x);
  588. if (!Sk.builtin.checkInt(ret)) {
  589. throw new Sk.builtin.TypeError("__len__ should return an int");
  590. }
  591. return Sk.builtin.asnum$(ret) !== 0;
  592. }
  593. if (x.mp$length) {
  594. return Sk.builtin.asnum$(x.mp$length()) !== 0;
  595. }
  596. if (x.sq$length) {
  597. return Sk.builtin.asnum$(x.sq$length()) !== 0;
  598. }
  599. return true;
  600. };
  601. goog.exportSymbol("Sk.misceval.isTrue", Sk.misceval.isTrue);
  602. Sk.misceval.softspace_ = false;
  603. Sk.misceval.print_ = function (x) {
  604. // this was function print(x) not sure why...
  605. var isspace;
  606. var s;
  607. if (Sk.misceval.softspace_) {
  608. if (x !== "\n") {
  609. Sk.output(" ");
  610. }
  611. Sk.misceval.softspace_ = false;
  612. }
  613. s = new Sk.builtin.str(x);
  614. var sys = Sk.importModule("sys");
  615. Sk.misceval.apply(sys["$d"]["stdout"]["write"], undefined, undefined, undefined, [sys["$d"]["stdout"], s]);
  616. isspace = function (c) {
  617. return c === "\n" || c === "\t" || c === "\r";
  618. };
  619. if (s.v.length === 0 || !isspace(s.v[s.v.length - 1]) || s.v[s.v.length - 1] === " ") {
  620. Sk.misceval.softspace_ = true;
  621. }
  622. };
  623. goog.exportSymbol("Sk.misceval.print_", Sk.misceval.print_);
  624. /**
  625. * @param {string} name
  626. * @param {Object=} other generally globals
  627. */
  628. Sk.misceval.loadname = function (name, other) {
  629. var bi;
  630. var v = other[name];
  631. if (v !== undefined) {
  632. return v;
  633. }
  634. bi = Sk.builtins[name];
  635. if (bi !== undefined) {
  636. return bi;
  637. }
  638. throw new Sk.builtin.NameError("name '" + Sk.unfixReserved(name) + "' is not defined");
  639. };
  640. goog.exportSymbol("Sk.misceval.loadname", Sk.misceval.loadname);
  641. /**
  642. *
  643. * Notes on necessity for 'call()':
  644. *
  645. * Classes are callable in python to create an instance of the class. If
  646. * we're calling "C()" we cannot tell at the call site whether we're
  647. * calling a standard function, or instantiating a class.
  648. *
  649. * JS does not support user-level callables. So, we can't use the normal
  650. * prototype hierarchy to make the class inherit from a 'class' type
  651. * where the various tp$getattr, etc. methods would live.
  652. *
  653. * Instead, we must copy all the methods from the prototype of our class
  654. * type onto every instance of the class constructor function object.
  655. * That way, both "C()" and "C.tp$getattr(...)" can still work. This is
  656. * of course quite expensive.
  657. *
  658. * The alternative would be to indirect all calls (whether classes or
  659. * regular functions) through something like C.$call(...). In the case
  660. * of class construction, $call could then call the constructor after
  661. * munging arguments to pass them on. This would impose a penalty on
  662. * regular function calls unfortunately, as they would have to do the
  663. * same thing.
  664. *
  665. * Note that the same problem exists for function objects too (a "def"
  666. * creates a function object that also has properties). It just happens
  667. * that attributes on classes in python are much more useful and common
  668. * that the attributes on functions.
  669. *
  670. * Also note, that for full python compatibility we have to do the $call
  671. * method because any python object could have a __call__ method which
  672. * makes the python object callable too. So, unless we were to make
  673. * *all* objects simply (function(){...}) and use the dict to create
  674. * hierarchy, there would be no way to call that python user function. I
  675. * think I'm prepared to sacrifice __call__ support, or only support it
  676. * post-ECMA5 or something.
  677. *
  678. * Is using (function(){...}) as the only object type too crazy?
  679. * Probably. Better or worse than having two levels of function
  680. * invocation for every function call?
  681. *
  682. * For a class `C' with instance `inst' we have the following cases:
  683. *
  684. * 1. C.attr
  685. *
  686. * 2. C.staticmeth()
  687. *
  688. * 3. x = C.staticmeth; x()
  689. *
  690. * 4. inst = C()
  691. *
  692. * 5. inst.attr
  693. *
  694. * 6. inst.meth()
  695. *
  696. * 7. x = inst.meth; x()
  697. *
  698. * 8. inst(), where C defines a __call__
  699. *
  700. * Because in general these are accomplished by a helper function
  701. * (tp$getattr/setattr/slice/ass_slice/etc.) it seems appropriate to add
  702. * a call that generally just calls through, but sometimes handles the
  703. * unusual cases. Once ECMA-5 is more broadly supported we can revisit
  704. * and hopefully optimize.
  705. *
  706. * @param {Object} func the thing to call
  707. * @param {Object=} kwdict **kwargs
  708. * @param {Object=} varargseq **args
  709. * @param {Object=} kws keyword args or undef
  710. * @param {...*} args stuff to pass it
  711. *
  712. *
  713. * TODO I think all the above is out of date.
  714. */
  715. Sk.misceval.call = function (func, kwdict, varargseq, kws, args) {
  716. args = Array.prototype.slice.call(arguments, 4);
  717. // todo; possibly inline apply to avoid extra stack frame creation
  718. return Sk.misceval.apply(func, kwdict, varargseq, kws, args);
  719. };
  720. goog.exportSymbol("Sk.misceval.call", Sk.misceval.call);
  721. /**
  722. * @param {?Object} suspensionHandlers
  723. * @param {Object} func the thing to call
  724. * @param {Object=} kwdict **kwargs
  725. * @param {Object=} varargseq **args
  726. * @param {Object=} kws keyword args or undef
  727. * @param {...*} args stuff to pass it
  728. *
  729. *
  730. * TODO I think all the above is out of date.
  731. */
  732. Sk.misceval.callAsync = function (suspensionHandlers, func, kwdict, varargseq, kws, args) {
  733. args = Array.prototype.slice.call(arguments, 5);
  734. // todo; possibly inline apply to avoid extra stack frame creation
  735. return Sk.misceval.applyAsync(suspensionHandlers, func, kwdict, varargseq, kws, args);
  736. };
  737. goog.exportSymbol("Sk.misceval.callAsync", Sk.misceval.callAsync);
  738. Sk.misceval.callOrSuspend = function (func, kwdict, varargseq, kws, args) {
  739. args = Array.prototype.slice.call(arguments, 4);
  740. // todo; possibly inline apply to avoid extra stack frame creation
  741. return Sk.misceval.applyOrSuspend(func, kwdict, varargseq, kws, args);
  742. };
  743. goog.exportSymbol("Sk.misceval.callOrSuspend", Sk.misceval.callOrSuspend);
  744. /**
  745. * @param {Object} func the thing to call
  746. * @param {...*} args stuff to pass it
  747. */
  748. Sk.misceval.callsim = function (func, args) {
  749. args = Array.prototype.slice.call(arguments, 1);
  750. return Sk.misceval.apply(func, undefined, undefined, undefined, args);
  751. };
  752. goog.exportSymbol("Sk.misceval.callsim", Sk.misceval.callsim);
  753. /**
  754. * @param {?Object} suspensionHandlers any custom suspension handlers
  755. * @param {Object} func the thing to call
  756. * @param {...*} args stuff to pass it
  757. */
  758. Sk.misceval.callsimAsync = function (suspensionHandlers, func, args) {
  759. args = Array.prototype.slice.call(arguments, 2);
  760. return Sk.misceval.applyAsync(suspensionHandlers, func, undefined, undefined, undefined, args);
  761. };
  762. goog.exportSymbol("Sk.misceval.callsimAsync", Sk.misceval.callsimAsync);
  763. /**
  764. * @param {Object} func the thing to call
  765. * @param {...*} args stuff to pass it
  766. */
  767. Sk.misceval.callsimOrSuspend = function (func, args) {
  768. args = Array.prototype.slice.call(arguments, 1);
  769. return Sk.misceval.applyOrSuspend(func, undefined, undefined, undefined, args);
  770. };
  771. goog.exportSymbol("Sk.misceval.callsimOrSuspend", Sk.misceval.callsimOrSuspend);
  772. /**
  773. * Wrap Sk.misceval.applyOrSuspend, but throw an error if we suspend
  774. */
  775. Sk.misceval.apply = function (func, kwdict, varargseq, kws, args) {
  776. var r = Sk.misceval.applyOrSuspend(func, kwdict, varargseq, kws, args);
  777. if (r instanceof Sk.misceval.Suspension) {
  778. return Sk.misceval.retryOptionalSuspensionOrThrow(r);
  779. } else {
  780. return r;
  781. }
  782. };
  783. goog.exportSymbol("Sk.misceval.apply", Sk.misceval.apply);
  784. /**
  785. * Wraps anything that can return an Sk.misceval.Suspension, and returns a
  786. * JS Promise with the result. Also takes an object map of suspension handlers:
  787. * pass in {"suspType": function (susp) {} }, and your function will be called
  788. * with the Suspension object if susp.type=="suspType". The type "*" will match
  789. * all otherwise unhandled suspensions.
  790. *
  791. * A suspension handler should return a Promise yielding the return value of
  792. * r.resume() - ie, either the final return value of this call or another
  793. * Suspension. That is, the null suspension handler is:
  794. *
  795. * function handler(susp) {
  796. * return new Promise(function(resolve, reject) {
  797. * try {
  798. * resolve(susp.resume());
  799. * } catch(e) {
  800. * reject(e);
  801. * }
  802. * });
  803. * }
  804. *
  805. * Alternatively, a handler can return null to perform the default action for
  806. * that suspension type.
  807. *
  808. * (Note: do *not* call asyncToPromise() in a suspension handler; this will
  809. * create a new Promise object for each such suspension that occurs)
  810. *
  811. * asyncToPromise() returns a Promise that will be resolved with the final
  812. * return value, or rejected with an exception if one is thrown.
  813. *
  814. * @param{function()} suspendablefn returns either a result or a Suspension
  815. * @param{Object=} suspHandlers an object map of suspension handlers
  816. */
  817. Sk.misceval.asyncToPromise = function(suspendablefn, suspHandlers) {
  818. return new Promise(function(resolve, reject) {
  819. try {
  820. var r = suspendablefn();
  821. (function handleResponse (r) {
  822. try {
  823. // jsh*nt insists these be defined outside the loop
  824. var resume = function() {
  825. handleResponse(r.resume());
  826. };
  827. var resumeWithData = function resolved(x) {
  828. try {
  829. r.data["result"] = x;
  830. resume();
  831. } catch(e) {
  832. reject(e);
  833. }
  834. };
  835. var resumeWithError = function rejected(e) {
  836. try {
  837. r.data["error"] = e;
  838. resume();
  839. } catch(ex) {
  840. reject(ex);
  841. }
  842. };
  843. while (r instanceof Sk.misceval.Suspension) {
  844. var handler = suspHandlers && (suspHandlers[r.data["type"]] || suspHandlers["*"]);
  845. if (handler) {
  846. var handlerPromise = handler(r);
  847. if (handlerPromise) {
  848. handlerPromise.then(handleResponse, reject);
  849. return;
  850. }
  851. }
  852. if (r.data["type"] == "Sk.promise") {
  853. r.data["promise"].then(resumeWithData, resumeWithError);
  854. return;
  855. } else if (r.data["type"] == "Sk.yield") {
  856. // Assumes all yields are optional, as Sk.setTimeout might
  857. // not be able to yield.
  858. Sk.setTimeout(resume, 0);
  859. return;
  860. } else if (r.optional) {
  861. // Unhandled optional suspensions just get
  862. // resumed immediately, and we go around the loop again.
  863. r = r.resume();
  864. } else {
  865. // Unhandled, non-optional suspension.
  866. throw new Sk.builtin.SuspensionError("Unhandled non-optional suspension of type '"+r.data["type"]+"'");
  867. }
  868. }
  869. resolve(r);
  870. } catch(e) {
  871. reject(e);
  872. }
  873. })(r);
  874. } catch (e) {
  875. reject(e);
  876. }
  877. });
  878. };
  879. goog.exportSymbol("Sk.misceval.asyncToPromise", Sk.misceval.asyncToPromise);
  880. Sk.misceval.applyAsync = function (suspHandlers, func, kwdict, varargseq, kws, args) {
  881. return Sk.misceval.asyncToPromise(function() {
  882. return Sk.misceval.applyOrSuspend(func, kwdict, varargseq, kws, args);
  883. }, suspHandlers);
  884. };
  885. goog.exportSymbol("Sk.misceval.applyAsync", Sk.misceval.applyAsync);
  886. /**
  887. * Chain together a set of functions, each of which might return a value or
  888. * an Sk.misceval.Suspension. Each function is called with the return value of
  889. * the preceding function, but does not see any suspensions. If a function suspends,
  890. * Sk.misceval.chain() returns a suspension that will resume the chain once an actual
  891. * return value is available.
  892. *
  893. * The idea is to allow a Promise-like chaining of possibly-suspending steps without
  894. * repeating boilerplate suspend-and-resume code.
  895. *
  896. * For example, imagine we call Sk.misceval.chain(x, f).
  897. * - If x is a value, we return f(x).
  898. * - If x is a suspension, we suspend. We will suspend and resume until we get a
  899. * return value, and then we will return f(<resumed-value).
  900. * This can be expanded to an arbitrary number of functions
  901. * (eg Sk.misceval.chain(x, f, g), which is equivalent to chain(chain(x, f), g).)
  902. *
  903. * @param {*} initialValue
  904. * @param {...function(*)} chainedFns
  905. */
  906. Sk.misceval.chain = function (initialValue, chainedFns) {
  907. // We try to minimse overhead when nothing suspends (the common case)
  908. var i = 1, value = initialValue, j, fs;
  909. while (true) {
  910. if (i == arguments.length) {
  911. return value;
  912. }
  913. if (value && value.$isSuspension) { break; } // oops, slow case
  914. value = arguments[i](value);
  915. i++;
  916. }
  917. // Okay, we've suspended at least once, so we're taking the slow(er) path.
  918. // Copy our remaining arguments into an array (inline, because passing
  919. // "arguments" out of a function kills the V8 optimiser).
  920. // (discussion: https://github.com/skulpt/skulpt/pull/552)
  921. fs = new Array(arguments.length - i);
  922. for (j = 0; j < arguments.length - i; j++) {
  923. fs[j] = arguments[i+j];
  924. }
  925. j = 0;
  926. return (function nextStep(r) {
  927. while (j < fs.length) {
  928. if (r instanceof Sk.misceval.Suspension) {
  929. return new Sk.misceval.Suspension(nextStep, r);
  930. }
  931. r = fs[j](r);
  932. j++;
  933. }
  934. return r;
  935. })(value);
  936. };
  937. goog.exportSymbol("Sk.misceval.chain", Sk.misceval.chain);
  938. /**
  939. * Catch any exceptions thrown by a function, or by resuming any suspension it
  940. * returns.
  941. *
  942. * var result = Sk.misceval.tryCatch(asyncFunc, function(err) {
  943. * console.log(err);
  944. * });
  945. *
  946. * Because exceptions are returned asynchronously aswell you can't catch them
  947. * with a try/catch. That's what this function is for.
  948. */
  949. Sk.misceval.tryCatch = function (tryFn, catchFn) {
  950. var r;
  951. try {
  952. r = tryFn();
  953. } catch(e) {
  954. return catchFn(e);
  955. }
  956. if (r instanceof Sk.misceval.Suspension) {
  957. var susp = new Sk.misceval.Suspension(undefined, r);
  958. susp.resume = function() { return Sk.misceval.tryCatch(r.resume, catchFn); };
  959. return susp;
  960. } else {
  961. return r;
  962. }
  963. };
  964. goog.exportSymbol("Sk.misceval.tryCatch", Sk.misceval.tryCatch);
  965. /**
  966. * Perform a suspension-aware for-each on an iterator, without
  967. * blowing up the stack.
  968. * forFn() is called for each element in the iterator, with two
  969. * arguments: the current element and the previous return value
  970. * of forFn() (or initialValue on the first call). In this way,
  971. * iterFor() can be used as a simple for loop, or alternatively
  972. * as a 'reduce' operation. The return value of the final call to
  973. * forFn() will be the return value of iterFor() (after all
  974. * suspensions are resumed, that is; if the iterator is empty then
  975. * initialValue will be returned.)
  976. *
  977. * The iteration can be terminated early, by returning
  978. * an instance of Sk.misceval.Break. If an argument is given to
  979. * the Sk.misceval.Break() constructor, that value will be
  980. * returned from iterFor(). It is therefore possible to use
  981. * iterFor() on infinite iterators.
  982. *
  983. * @param {*} iter
  984. * @param {function(*,*=)} forFn
  985. * @param {*=} initialValue
  986. */
  987. Sk.misceval.iterFor = function (iter, forFn, initialValue) {
  988. var prevValue = initialValue;
  989. var breakOrIterNext = function(r) {
  990. prevValue = r;
  991. return (r instanceof Sk.misceval.Break) ? r : iter.tp$iternext(true);
  992. };
  993. return (function nextStep(i) {
  994. while (i !== undefined) {
  995. if (i instanceof Sk.misceval.Suspension) {
  996. return new Sk.misceval.Suspension(nextStep, i);
  997. }
  998. if (i === Sk.misceval.Break || i instanceof Sk.misceval.Break) {
  999. return i.brValue;
  1000. }
  1001. i = Sk.misceval.chain(
  1002. forFn(i, prevValue),
  1003. breakOrIterNext
  1004. );
  1005. }
  1006. return prevValue;
  1007. })(iter.tp$iternext(true));
  1008. };
  1009. goog.exportSymbol("Sk.misceval.iterFor", Sk.misceval.iterFor);
  1010. /**
  1011. * A special value to return from an iterFor() function,
  1012. * to abort the iteration. Optionally supply a value for iterFor() to return
  1013. * (defaults to 'undefined')
  1014. *
  1015. * @constructor
  1016. * @param {*=} brValue
  1017. */
  1018. Sk.misceval.Break = function(brValue) {
  1019. if (!(this instanceof Sk.misceval.Break)) {
  1020. return new Sk.misceval.Break(brValue);
  1021. }
  1022. this.brValue = brValue;
  1023. };
  1024. goog.exportSymbol("Sk.misceval.Break", Sk.misceval.Break);
  1025. /**
  1026. * same as Sk.misceval.call except args is an actual array, rather than
  1027. * varargs.
  1028. */
  1029. Sk.misceval.applyOrSuspend = function (func, kwdict, varargseq, kws, args) {
  1030. var fcall;
  1031. var kwix;
  1032. var numPosParams;
  1033. var numNonOptParams;
  1034. var it, i;
  1035. if (func === null || func instanceof Sk.builtin.none) {
  1036. throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(func) + "' object is not callable");
  1037. } else if (typeof func === "function" && func.tp$call === undefined) {
  1038. // This happens in the wrapper functions around generators
  1039. // (that creates the iterator), and all the builtin functions
  1040. // (in builtin.js, for example) as they are javascript functions,
  1041. // not Sk.builtin.func objects.
  1042. if (varargseq) {
  1043. for (it = varargseq.tp$iter(), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) {
  1044. args.push(i);
  1045. }
  1046. }
  1047. if (kwdict) {
  1048. for (it = Sk.abstr.iter(kwdict), i = it.tp$iternext(); i!== undefined; i = it.tp$iternext()) {
  1049. if (!Sk.builtin.checkString(i)) {
  1050. throw new Sk.builtin.TypeError("Function keywords must be strings");
  1051. }
  1052. kws.push(i.v);
  1053. kws.push(Sk.abstr.objectGetItem(kwdict, i, false));
  1054. }
  1055. }
  1056. //goog.asserts.assert(((kws === undefined) || (kws.length === 0)));
  1057. //print('kw args location: '+ kws + ' args ' + args.length)
  1058. if (kws !== undefined && kws.length > 0) {
  1059. if (!func.co_varnames) {
  1060. throw new Sk.builtin.ValueError("Keyword arguments are not supported by this function");
  1061. }
  1062. //number of positionally placed optional parameters
  1063. numNonOptParams = func.co_numargs - func.co_varnames.length;
  1064. numPosParams = args.length - numNonOptParams;
  1065. //add defaults
  1066. args = args.concat(func.$defaults.slice(numPosParams));
  1067. for (i = 0; i < kws.length; i = i + 2) {
  1068. kwix = func.co_varnames.indexOf(kws[i]);
  1069. if (kwix === -1) {
  1070. throw new Sk.builtin.TypeError("'" + kws[i] + "' is an invalid keyword argument for this function");
  1071. }
  1072. if (kwix < numPosParams) {
  1073. throw new Sk.builtin.TypeError("Argument given by name ('" + kws[i] + "') and position (" + (kwix + numNonOptParams + 1) + ")");
  1074. }
  1075. args[kwix + numNonOptParams] = kws[i + 1];
  1076. }
  1077. }
  1078. //append kw args to args, filling in the default value where none is provided.
  1079. return func.apply(null, args);
  1080. } else {
  1081. fcall = func.tp$call;
  1082. if (fcall !== undefined) {
  1083. if (varargseq) {
  1084. for (it = varargseq.tp$iter(), i = it.tp$iternext(); i !== undefined; i = it.tp$iternext()) {
  1085. args.push(i);
  1086. }
  1087. }
  1088. if (kwdict) {
  1089. for (it = Sk.abstr.iter(kwdict), i = it.tp$iternext(); i!== undefined; i = it.tp$iternext()) {
  1090. if (!Sk.builtin.checkString(i)) {
  1091. throw new Sk.builtin.TypeError("Function keywords must be strings");
  1092. }
  1093. kws.push(i.v);
  1094. kws.push(Sk.abstr.objectGetItem(kwdict, i, false));
  1095. }
  1096. }
  1097. return fcall.call(func, args, kws, kwdict);
  1098. }
  1099. // todo; can we push this into a tp$call somewhere so there's
  1100. // not redundant checks everywhere for all of these __x__ ones?
  1101. fcall = func.__call__;
  1102. if (fcall !== undefined) {
  1103. // func is actually the object here because we got __call__
  1104. // from it. todo; should probably use descr_get here
  1105. args.unshift(func);
  1106. return Sk.misceval.apply(fcall, kwdict, varargseq, kws, args);
  1107. }
  1108. throw new Sk.builtin.TypeError("'" + Sk.abstr.typeName(func) + "' object is not callable");
  1109. }
  1110. };
  1111. goog.exportSymbol("Sk.misceval.applyOrSuspend", Sk.misceval.applyOrSuspend);
  1112. /**
  1113. * Constructs a class object given a code object representing the body
  1114. * of the class, the name of the class, and the list of bases.
  1115. *
  1116. * There are no "old-style" classes in Skulpt, so use the user-specified
  1117. * metaclass (todo;) if there is one, the type of the 0th base class if
  1118. * there's bases, or otherwise the 'type' type.
  1119. *
  1120. * The func code object is passed a (js) dict for its locals which it
  1121. * stores everything into.
  1122. *
  1123. * The metaclass is then called as metaclass(name, bases, locals) and
  1124. * should return a newly constructed class object.
  1125. *
  1126. */
  1127. Sk.misceval.buildClass = function (globals, func, name, bases) {
  1128. // todo; metaclass
  1129. var klass;
  1130. var meta = Sk.builtin.type;
  1131. var locals = {};
  1132. // init the dict for the class
  1133. func(globals, locals, []);
  1134. // ToDo: check if func contains the __meta__ attribute
  1135. // or if the bases contain __meta__
  1136. // new Syntax would be different
  1137. // file's __name__ is class's __module__
  1138. locals.__module__ = globals["__name__"];
  1139. var _name = new Sk.builtin.str(name);
  1140. var _bases = new Sk.builtin.tuple(bases);
  1141. var _locals = [];
  1142. var key;
  1143. // build array for python dict
  1144. for (key in locals) {
  1145. if (!locals.hasOwnProperty(key)) {
  1146. //The current property key not a direct property of p
  1147. continue;
  1148. }
  1149. _locals.push(new Sk.builtin.str(key)); // push key
  1150. _locals.push(locals[key]); // push associated value
  1151. }
  1152. _locals = new Sk.builtin.dict(_locals);
  1153. klass = Sk.misceval.callsim(meta, _name, _bases, _locals);
  1154. return klass;
  1155. };
  1156. goog.exportSymbol("Sk.misceval.buildClass", Sk.misceval.buildClass);