compile.js 81 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509
  1. /** @param {...*} x */
  2. var out;
  3. Sk.gensymcount = 0;
  4. /**
  5. * @constructor
  6. * @param {string} filename
  7. * @param {SymbolTable} st
  8. * @param {number} flags
  9. * @param {boolean=} canSuspend whether compiled code can suspend
  10. * @param {string=} sourceCodeForAnnotation used to add original source to listing if desired
  11. */
  12. function Compiler (filename, st, flags, canSuspend, sourceCodeForAnnotation) {
  13. this.filename = filename;
  14. this.st = st;
  15. this.flags = flags;
  16. this.canSuspend = canSuspend;
  17. this.interactive = false;
  18. this.nestlevel = 0;
  19. this.u = null;
  20. this.stack = [];
  21. this.result = [];
  22. // this.gensymcount = 0;
  23. this.allUnits = [];
  24. this.source = sourceCodeForAnnotation ? sourceCodeForAnnotation.split("\n") : false;
  25. }
  26. /**
  27. * @constructor
  28. *
  29. * Stuff that changes on entry/exit of code blocks. must be saved and restored
  30. * when returning to a block.
  31. *
  32. * Corresponds to the body of a module, class, or function.
  33. */
  34. function CompilerUnit () {
  35. this.ste = null;
  36. this.name = null;
  37. this.canSuspend = false;
  38. this.doesSuspend = false;
  39. this.private_ = null;
  40. this.firstlineno = 0;
  41. this.lineno = 0;
  42. this.linenoSet = false;
  43. this.localnames = [];
  44. this.localtemps = [];
  45. this.tempsToSave = [];
  46. this.blocknum = 0;
  47. this.blocks = [];
  48. this.curblock = 0;
  49. this.scopename = null;
  50. this.prefixCode = "";
  51. this.varDeclsCode = "";
  52. this.switchCode = "";
  53. this.suffixCode = "";
  54. // stack of where to go on a break
  55. this.breakBlocks = [];
  56. // stack of where to go on a continue
  57. this.continueBlocks = [];
  58. this.exceptBlocks = [];
  59. // state of where to go on a return
  60. this.finallyBlocks = [];
  61. }
  62. CompilerUnit.prototype.activateScope = function () {
  63. var self = this;
  64. out = function () {
  65. var i;
  66. var b = self.blocks[self.curblock];
  67. if (b._next === null) {
  68. for (i = 0; i < arguments.length; ++i) {
  69. b.push(arguments[i]);
  70. }
  71. }
  72. // TODO: Warn about unreachable code after an unconditional jump?
  73. };
  74. };
  75. Compiler.prototype.getSourceLine = function (lineno) {
  76. goog.asserts.assert(this.source);
  77. return this.source[lineno - 1];
  78. };
  79. Compiler.prototype.annotateSource = function (ast, shouldStep) {
  80. var i;
  81. var col_offset;
  82. var lineno;
  83. if (this.source) {
  84. lineno = ast.lineno;
  85. col_offset = ast.col_offset;
  86. out("\n//\n// line ", lineno, ":\n// ", this.getSourceLine(lineno), "\n// ");
  87. for (i = 0; i < col_offset; ++i) {
  88. out(" ");
  89. }
  90. out("^\n//\n");
  91. out("\nSk.currLineNo = ",lineno, ";\nSk.currColNo = ",col_offset,";\n\n"); // Added by RNL
  92. out("\nSk.currFilename = '",this.filename,"';\n\n"); // Added by RNL
  93. if (shouldStep) {
  94. out("\nif (typeof Sk.afterSingleExecution == 'function') {\n\tSk.afterSingleExecution($gbl, Sk.currLineNo, Sk.currColNo, Sk.currFilename);\n}\n");
  95. }
  96. out("$currLineNo = ", lineno, ";\n$currColNo = ", col_offset, ";\n\n");
  97. }
  98. };
  99. Compiler.prototype.gensym = function (hint) {
  100. hint = hint || "";
  101. hint = "$" + hint;
  102. hint += Sk.gensymcount++;
  103. return hint;
  104. };
  105. Compiler.prototype.niceName = function (roughName) {
  106. return this.gensym(roughName.replace("<", "").replace(">", "").replace(" ", "_"));
  107. };
  108. var reservedWords_ = {
  109. "abstract": true,
  110. "as": true,
  111. "boolean": true,
  112. "break": true,
  113. "byte": true,
  114. "case": true,
  115. "catch": true,
  116. "char": true,
  117. "class": true,
  118. "continue": true,
  119. "const": true,
  120. "debugger": true,
  121. "default": true,
  122. "delete": true,
  123. "do": true,
  124. "double": true,
  125. "else": true,
  126. "enum": true,
  127. "export": true,
  128. "extends": true,
  129. "false": true,
  130. "final": true,
  131. "finally": true,
  132. "float": true,
  133. "for": true,
  134. "function": true,
  135. "goto": true,
  136. "if": true,
  137. "implements": true,
  138. "import": true,
  139. "in": true,
  140. "instanceof": true,
  141. "int": true,
  142. "interface": true,
  143. "is": true,
  144. "long": true,
  145. "namespace": true,
  146. "native": true,
  147. "new": true,
  148. "null": true,
  149. "package": true,
  150. "private": true,
  151. "protected": true,
  152. "public": true,
  153. "return": true,
  154. "short": true,
  155. "static": true,
  156. "super": false,
  157. "switch": true,
  158. "synchronized": true,
  159. "this": true,
  160. "throw": true,
  161. "throws": true,
  162. "transient": true,
  163. "true": true,
  164. "try": true,
  165. "typeof": true,
  166. "use": true,
  167. "var": true,
  168. "void": true,
  169. "volatile": true,
  170. "while": true,
  171. "with": true
  172. };
  173. function fixReservedWords (name) {
  174. if (reservedWords_[name] !== true) {
  175. return name;
  176. }
  177. return name + "_$rw$";
  178. }
  179. var reservedNames_ = {
  180. "__defineGetter__": true,
  181. "__defineSetter__": true,
  182. "apply": true,
  183. "call": true,
  184. "eval": true,
  185. "hasOwnProperty": true,
  186. "isPrototypeOf": true,
  187. "__lookupGetter__": true,
  188. "__lookupSetter__": true,
  189. "__noSuchMethod__": true,
  190. "propertyIsEnumerable": true,
  191. "toSource": true,
  192. "toLocaleString": true,
  193. "toString": true,
  194. "unwatch": true,
  195. "valueOf": true,
  196. "watch": true,
  197. "length": true
  198. };
  199. function fixReservedNames (name) {
  200. if (reservedNames_[name]) {
  201. return name + "_$rn$";
  202. }
  203. return name;
  204. }
  205. function unfixReserved(name) {
  206. return name.replace(/_\$r[wn]\$$/, "");
  207. }
  208. function mangleName (priv, ident) {
  209. var name = ident.v;
  210. var strpriv = null;
  211. if (priv === null || name === null || name.charAt(0) !== "_" || name.charAt(1) !== "_") {
  212. return ident;
  213. }
  214. // don't mangle __id__
  215. if (name.charAt(name.length - 1) === "_" && name.charAt(name.length - 2) === "_") {
  216. return ident;
  217. }
  218. // don't mangle classes that are all _ (obscure much?)
  219. strpriv = priv.v;
  220. strpriv.replace(/_/g, "");
  221. if (strpriv === "") {
  222. return ident;
  223. }
  224. strpriv = priv.v;
  225. strpriv.replace(/^_*/, "");
  226. strpriv = new Sk.builtin.str("_" + strpriv + name);
  227. return strpriv;
  228. }
  229. /**
  230. * @param {string} hint basename for gensym
  231. * @param {...*} rest
  232. */
  233. Compiler.prototype._gr = function (hint, rest) {
  234. var i;
  235. var v = this.gensym(hint);
  236. this.u.localtemps.push(v);
  237. out("var ", v, "=");
  238. for (i = 1; i < arguments.length; ++i) {
  239. out(arguments[i]);
  240. }
  241. out(";");
  242. return v;
  243. };
  244. /**
  245. * Function to test if an interrupt should occur if the program has been running for too long.
  246. * This function is executed at every test/branch operation.
  247. */
  248. Compiler.prototype.outputInterruptTest = function () { // Added by RNL
  249. var output = "";
  250. if (Sk.execLimit !== null || Sk.yieldLimit !== null && this.u.canSuspend) {
  251. output += "var $dateNow = Date.now();";
  252. if (Sk.execLimit !== null) {
  253. output += "if ($dateNow - Sk.execStart > Sk.execLimit && Sk.execLimit !== null) {throw new Sk.builtin.TimeLimitError(Sk.timeoutMsg())}";
  254. }
  255. if (Sk.yieldLimit !== null && this.u.canSuspend) {
  256. output += "if ($dateNow - Sk.lastYield > Sk.yieldLimit) {";
  257. output += "var $susp = $saveSuspension({data: {type: 'Sk.yield'}, resume: function() {}}, '"+this.filename+"',$currLineNo,$currColNo);";
  258. output += "$susp.$blk = $blk;";
  259. output += "$susp.optional = true;";
  260. output += "return $susp;";
  261. output += "}";
  262. this.u.doesSuspend = true;
  263. }
  264. }
  265. return output;
  266. };
  267. Compiler.prototype._jumpfalse = function (test, block) {
  268. var cond = this._gr("jfalse", "(", test, "===false||!Sk.misceval.isTrue(", test, "))");
  269. out("if(", cond, "){/*test failed */$blk=", block, ";continue;}");
  270. };
  271. Compiler.prototype._jumpundef = function (test, block) {
  272. out("if(", test, "===undefined){$blk=", block, ";continue;}");
  273. };
  274. Compiler.prototype._jumpnotundef = function (test, block) {
  275. out("if(", test, "!==undefined){$blk=", block, ";continue;}");
  276. };
  277. Compiler.prototype._jumptrue = function (test, block) {
  278. var cond = this._gr("jtrue", "(", test, "===true||Sk.misceval.isTrue(", test, "))");
  279. out("if(", cond, "){/*test passed */$blk=", block, ";continue;}");
  280. };
  281. Compiler.prototype._jump = function (block) {
  282. if (this.u.blocks[this.u.curblock]._next === null) {
  283. out("$blk=", block, ";");
  284. this.u.blocks[this.u.curblock]._next = block;
  285. }
  286. };
  287. /**
  288. * @param {Object=} e Object with keys 'lineno' and 'col_offset'
  289. */
  290. Compiler.prototype._checkSuspension = function(e) {
  291. var retblk;
  292. if (this.u.canSuspend) {
  293. retblk = this.newBlock("function return or resume suspension");
  294. this._jump(retblk);
  295. this.setBlock(retblk);
  296. e = e || {lineno: "$currLineNo", col_offset: "$currColNo"};
  297. out ("if ($ret && $ret.$isSuspension) { return $saveSuspension($ret,'"+this.filename+"',"+e.lineno+","+e.col_offset+"); }");
  298. this.u.doesSuspend = true;
  299. this.u.tempsToSave = this.u.tempsToSave.concat(this.u.localtemps);
  300. } else {
  301. out ("if ($ret && $ret.$isSuspension) { $ret = Sk.misceval.retryOptionalSuspensionOrThrow($ret); }");
  302. }
  303. };
  304. Compiler.prototype.ctuplelistorset = function(e, data, tuporlist) {
  305. var i;
  306. var items;
  307. goog.asserts.assert(tuporlist === "tuple" || tuporlist === "list" || tuporlist === "set");
  308. if (e.ctx === Store) {
  309. items = this._gr("items", "Sk.abstr.sequenceUnpack(" + data + "," + e.elts.length + ")");
  310. for (i = 0; i < e.elts.length; ++i) {
  311. this.vexpr(e.elts[i], items + "[" + i + "]");
  312. }
  313. }
  314. else if (e.ctx === Load || tuporlist === "set") { //because set's can't be assigned to.
  315. items = [];
  316. for (i = 0; i < e.elts.length; ++i) {
  317. items.push(this._gr("elem", this.vexpr(e.elts[i])));
  318. }
  319. return this._gr("load" + tuporlist, "new Sk.builtins['", tuporlist, "']([", items, "])");
  320. }
  321. };
  322. Compiler.prototype.cdict = function (e) {
  323. var v;
  324. var i;
  325. var items;
  326. goog.asserts.assert(e.values.length === e.keys.length);
  327. items = [];
  328. for (i = 0; i < e.values.length; ++i) {
  329. v = this.vexpr(e.values[i]); // "backwards" to match order in cpy
  330. items.push(this.vexpr(e.keys[i]));
  331. items.push(v);
  332. }
  333. return this._gr("loaddict", "new Sk.builtins['dict']([", items, "])");
  334. };
  335. Compiler.prototype.clistcomp = function(e) {
  336. goog.asserts.assert(e instanceof ListComp);
  337. var tmp = this._gr("_compr", "new Sk.builtins['list']([])"); // note: _ is impt. for hack in name mangling (same as cpy)
  338. return this.ccompgen("list", tmp, e.generators, 0, e.elt, null, e);
  339. };
  340. Compiler.prototype.cdictcomp = function(e) {
  341. goog.asserts.assert(e instanceof DictComp);
  342. var tmp = this._gr("_dcompr", "new Sk.builtins.dict([])");
  343. return this.ccompgen("dict", tmp, e.generators, 0, e.value, e.key, e);
  344. };
  345. Compiler.prototype.csetcomp = function(e) {
  346. goog.asserts.assert(e instanceof SetComp);
  347. var tmp = this._gr("_setcompr", "new Sk.builtins.set([])");
  348. return this.ccompgen("set", tmp, e.generators, 0, e.elt, null, e);
  349. };
  350. Compiler.prototype.ccompgen = function (type, tmpname, generators, genIndex, value, key, e) {
  351. var start = this.newBlock(type + " comp start");
  352. var skip = this.newBlock(type + " comp skip");
  353. var anchor = this.newBlock(type + " comp anchor");
  354. var l = generators[genIndex];
  355. var toiter = this.vexpr(l.iter);
  356. var iter = this._gr("iter", "Sk.abstr.iter(", toiter, ")");
  357. var lvalue;
  358. var lkey;
  359. var ifres;
  360. var i;
  361. var target;
  362. var nexti;
  363. var n;
  364. this._jump(start);
  365. this.setBlock(start);
  366. // load targets
  367. out("$ret = Sk.abstr.iternext(", iter, ", true);");
  368. this._checkSuspension(e);
  369. nexti = this._gr("next", "$ret");
  370. this._jumpundef(nexti, anchor); // todo; this should be handled by StopIteration
  371. target = this.vexpr(l.target, nexti);
  372. n = l.ifs.length;
  373. for (i = 0; i < n; ++i) {
  374. ifres = this.vexpr(l.ifs[i]);
  375. this._jumpfalse(ifres, start);
  376. }
  377. if (++genIndex < generators.length) {
  378. this.ccompgen(type, tmpname, generators, genIndex, value, key, e);
  379. }
  380. if (genIndex >= generators.length) {
  381. lvalue = this.vexpr(value);
  382. if (type === "dict") {
  383. lkey = this.vexpr(key);
  384. out(tmpname, ".mp$ass_subscript(", lkey, ",", lvalue, ");");
  385. }
  386. else if (type === "list") {
  387. out(tmpname, ".v.push(", lvalue, ");"); // todo;
  388. }
  389. else if (type === "set") {
  390. out(tmpname, ".v.mp$ass_subscript(", lvalue, ", true);");
  391. }
  392. this._jump(skip);
  393. this.setBlock(skip);
  394. }
  395. this._jump(start);
  396. this.setBlock(anchor);
  397. return tmpname;
  398. };
  399. Compiler.prototype.cyield = function(e)
  400. {
  401. if (this.u.ste.blockType !== FunctionBlock) {
  402. throw new SyntaxError("'yield' outside function");
  403. }
  404. var val = "null",
  405. nextBlock;
  406. if (e.value) {
  407. val = this.vexpr(e.value);
  408. }
  409. nextBlock = this.newBlock("after yield");
  410. // return a pair: resume target block and yielded value
  411. out("return [/*resume*/", nextBlock, ",/*ret*/", val, "];");
  412. this.setBlock(nextBlock);
  413. return "$gen.gi$sentvalue"; // will either be null if none sent, or the value from gen.send(value)
  414. };
  415. Compiler.prototype.ccompare = function (e) {
  416. var res;
  417. var rhs;
  418. var i;
  419. var fres;
  420. var done;
  421. var n;
  422. var cur;
  423. goog.asserts.assert(e.ops.length === e.comparators.length);
  424. cur = this.vexpr(e.left);
  425. n = e.ops.length;
  426. done = this.newBlock("done");
  427. fres = this._gr("compareres", "null");
  428. for (i = 0; i < n; ++i) {
  429. rhs = this.vexpr(e.comparators[i]);
  430. out("$ret = Sk.builtin.bool(Sk.misceval.richCompareBool(", cur, ",", rhs, ",'", e.ops[i].prototype._astname, "', true));");
  431. this._checkSuspension(e);
  432. out(fres, "=$ret;");
  433. this._jumpfalse("$ret", done);
  434. cur = rhs;
  435. }
  436. this._jump(done);
  437. this.setBlock(done);
  438. return fres;
  439. };
  440. Compiler.prototype.ccall = function (e) {
  441. var kwargs;
  442. var starargs;
  443. var keywords;
  444. var i;
  445. var kwarray;
  446. var func = this.vexpr(e.func);
  447. var args = this.vseqexpr(e.args);
  448. //print(JSON.stringify(e, null, 2));
  449. if (e.keywords.length > 0 || e.starargs || e.kwargs) {
  450. kwarray = [];
  451. for (i = 0; i < e.keywords.length; ++i) {
  452. kwarray.push("'" + e.keywords[i].arg.v + "'");
  453. kwarray.push(this.vexpr(e.keywords[i].value));
  454. }
  455. keywords = "[" + kwarray.join(",") + "]";
  456. starargs = "undefined";
  457. kwargs = "undefined";
  458. if (e.starargs) {
  459. starargs = this.vexpr(e.starargs);
  460. }
  461. if (e.kwargs) {
  462. kwargs = this.vexpr(e.kwargs);
  463. }
  464. out ("$ret;"); // This forces a failure if $ret isn't defined
  465. out ("$ret = Sk.misceval.callOrSuspend(", func, ",", kwargs, ",", starargs, ",", keywords, args.length > 0 ? "," : "", args, ");");
  466. }
  467. else {
  468. out ("$ret;"); // This forces a failure if $ret isn't defined
  469. out ("$ret = Sk.misceval.callsimOrSuspend(", func, args.length > 0 ? "," : "", args, ");");
  470. }
  471. this._checkSuspension(e);
  472. return this._gr("call", "$ret");
  473. };
  474. Compiler.prototype.cslice = function (s) {
  475. var step;
  476. var high;
  477. var low;
  478. goog.asserts.assert(s instanceof Slice);
  479. low = s.lower ? this.vexpr(s.lower) : s.step ? "Sk.builtin.none.none$" : "new Sk.builtin.int_(0)"; // todo;ideally, these numbers would be constants
  480. high = s.upper ? this.vexpr(s.upper) : s.step ? "Sk.builtin.none.none$" : "new Sk.builtin.int_(2147483647)";
  481. step = s.step ? this.vexpr(s.step) : "Sk.builtin.none.none$";
  482. return this._gr("slice", "new Sk.builtins['slice'](", low, ",", high, ",", step, ")");
  483. };
  484. Compiler.prototype.eslice = function (dims) {
  485. var i;
  486. var dimSubs, subs;
  487. goog.asserts.assert(dims instanceof Array);
  488. dimSubs = [];
  489. for (i = 0; i < dims.length; i++) {
  490. dimSubs.push(this.vslicesub(dims[i]));
  491. }
  492. return this._gr("extslice", "new Sk.builtins['tuple']([", dimSubs, "])");
  493. };
  494. Compiler.prototype.vslicesub = function (s) {
  495. var subs;
  496. switch (s.constructor) {
  497. case Index:
  498. subs = this.vexpr(s.value);
  499. break;
  500. case Slice:
  501. subs = this.cslice(s);
  502. break;
  503. case Ellipsis:
  504. goog.asserts.fail("todo compile.js Ellipsis;");
  505. break;
  506. case ExtSlice:
  507. subs = this.eslice(s.dims);
  508. break;
  509. default:
  510. goog.asserts.fail("invalid subscript kind");
  511. }
  512. return subs;
  513. };
  514. Compiler.prototype.vslice = function (s, ctx, obj, dataToStore) {
  515. var subs = this.vslicesub(s);
  516. return this.chandlesubscr(ctx, obj, subs, dataToStore);
  517. };
  518. Compiler.prototype.chandlesubscr = function (ctx, obj, subs, data) {
  519. if (ctx === Load || ctx === AugLoad) {
  520. out("$ret = Sk.abstr.objectGetItem(", obj, ",", subs, ", true);");
  521. this._checkSuspension();
  522. return this._gr("lsubscr", "$ret");
  523. }
  524. else if (ctx === Store || ctx === AugStore) {
  525. out("$ret = Sk.abstr.objectSetItem(", obj, ",", subs, ",", data, ", true);");
  526. this._checkSuspension();
  527. }
  528. else if (ctx === Del) {
  529. out("Sk.abstr.objectDelItem(", obj, ",", subs, ");");
  530. }
  531. else {
  532. goog.asserts.fail("handlesubscr fail");
  533. }
  534. };
  535. Compiler.prototype.cboolop = function (e) {
  536. var expres;
  537. var i;
  538. var retval;
  539. var n;
  540. var s;
  541. var end;
  542. var ifFailed;
  543. var jtype;
  544. goog.asserts.assert(e instanceof BoolOp);
  545. if (e.op === And) {
  546. jtype = this._jumpfalse;
  547. }
  548. else {
  549. jtype = this._jumptrue;
  550. }
  551. end = this.newBlock("end of boolop");
  552. s = e.values;
  553. n = s.length;
  554. for (i = 0; i < n; ++i) {
  555. expres = this.vexpr(s[i]);
  556. if (i === 0) {
  557. retval = this._gr("boolopsucc", expres);
  558. }
  559. out(retval, "=", expres, ";");
  560. jtype.call(this, expres, end);
  561. }
  562. this._jump(end);
  563. this.setBlock(end);
  564. return retval;
  565. };
  566. /**
  567. *
  568. * compiles an expression. to 'return' something, it'll gensym a var and store
  569. * into that var so that the calling code doesn't have avoid just pasting the
  570. * returned name.
  571. *
  572. * @param {Object} e
  573. * @param {string=} data data to store in a store operation
  574. * @param {Object=} augvar var to load/store to for augmented assignments like '+='.
  575. * (already vexpr'ed, so we can evaluate it once and reuse for both load and store ops)
  576. * @param {Object=} augsubs precomputed subscript for augmented assignments like '+='.
  577. * (already vexpr'ed, so we can evaluate it once and reuse for both load and store ops)
  578. */
  579. Compiler.prototype.vexpr = function (e, data, augvar, augsubs) {
  580. var mangled;
  581. var val;
  582. var result;
  583. var nStr; // used for preserving signs for floats (zeros)
  584. if (e.lineno > this.u.lineno) {
  585. this.u.lineno = e.lineno;
  586. this.u.linenoSet = false;
  587. }
  588. //this.annotateSource(e);
  589. switch (e.constructor) {
  590. case BoolOp:
  591. return this.cboolop(e);
  592. case BinOp:
  593. return this._gr("binop", "Sk.abstr.numberBinOp(", this.vexpr(e.left), ",", this.vexpr(e.right), ",'", e.op.prototype._astname, "')");
  594. case UnaryOp:
  595. return this._gr("unaryop", "Sk.abstr.numberUnaryOp(", this.vexpr(e.operand), ",'", e.op.prototype._astname, "')");
  596. case Lambda:
  597. return this.clambda(e);
  598. case IfExp:
  599. return this.cifexp(e);
  600. case Dict:
  601. return this.cdict(e);
  602. case ListComp:
  603. return this.clistcomp(e);
  604. case DictComp:
  605. return this.cdictcomp(e);
  606. case SetComp:
  607. return this.csetcomp(e);
  608. case GeneratorExp:
  609. return this.cgenexp(e);
  610. case Yield:
  611. return this.cyield(e);
  612. case Compare:
  613. return this.ccompare(e);
  614. case Call:
  615. result = this.ccall(e);
  616. // After the function call, we've returned to this line
  617. this.annotateSource(e, false);
  618. return result;
  619. case Num:
  620. if (typeof e.n === "number") {
  621. return e.n;
  622. }
  623. else if (e.n instanceof Sk.builtin.int_) {
  624. return "new Sk.builtin.int_(" + e.n.v + ")";
  625. } else if (e.n instanceof Sk.builtin.float_) {
  626. // Preserve sign of zero for floats
  627. nStr = e.n.v === 0 && 1/e.n.v === -Infinity ? "-0" : e.n.v;
  628. return "new Sk.builtin.float_(" + nStr + ")";
  629. }
  630. else if (e.n instanceof Sk.builtin.lng) {
  631. // long uses the tp$str() method which delegates to nmber.str$ which preserves the sign
  632. return "Sk.longFromStr('" + e.n.tp$str().v + "')";
  633. }
  634. else if (e.n instanceof Sk.builtin.complex) {
  635. // preserve sign of zero here too
  636. var real_val = e.n.real.v === 0 && 1/e.n.real.v === -Infinity ? "-0" : e.n.real.v;
  637. var imag_val = e.n.imag.v === 0 && 1/e.n.imag.v === -Infinity ? "-0" : e.n.imag.v;
  638. return "new Sk.builtin.complex(new Sk.builtin.float_(" + real_val + "), new Sk.builtin.float_(" + imag_val + "))";
  639. }
  640. goog.asserts.fail("unhandled Num type");
  641. case Str:
  642. return this._gr("str", "new Sk.builtins['str'](", e.s["$r"]().v, ")");
  643. case Attribute:
  644. if (e.ctx !== AugLoad && e.ctx !== AugStore) {
  645. val = this.vexpr(e.value);
  646. }
  647. mangled = e.attr["$r"]().v;
  648. mangled = mangled.substring(1, mangled.length - 1);
  649. mangled = mangleName(this.u.private_, new Sk.builtin.str(mangled)).v;
  650. mangled = fixReservedWords(mangled);
  651. mangled = fixReservedNames(mangled);
  652. switch (e.ctx) {
  653. case AugLoad:
  654. out("$ret = Sk.abstr.gattr(", augvar, ",'", mangled, "', true);");
  655. this._checkSuspension(e);
  656. return this._gr("lattr", "$ret");
  657. case Load:
  658. out("$ret = Sk.abstr.gattr(", val, ",'", mangled, "', true);");
  659. this._checkSuspension(e);
  660. return this._gr("lattr", "$ret");
  661. case AugStore:
  662. // To be more correct, we shouldn't sattr() again if the in-place update worked.
  663. // At the time of writing (26/Feb/2015), Sk.abstr.numberInplaceBinOp never returns undefined,
  664. // so this will never *not* execute. But it could, if Sk.abstr.numberInplaceBinOp were fixed.
  665. out("$ret = undefined;");
  666. out("if(", data, "!==undefined){");
  667. out("$ret = Sk.abstr.sattr(", augvar, ",'", mangled, "',", data, ", true);");
  668. out("}");
  669. this._checkSuspension(e);
  670. break;
  671. case Store:
  672. out("$ret = Sk.abstr.sattr(", val, ",'", mangled, "',", data, ", true);");
  673. this._checkSuspension(e);
  674. break;
  675. case Del:
  676. goog.asserts.fail("todo Del;");
  677. break;
  678. case Param:
  679. default:
  680. goog.asserts.fail("invalid attribute expression");
  681. }
  682. break;
  683. case Subscript:
  684. switch (e.ctx) {
  685. case AugLoad:
  686. out("$ret = Sk.abstr.objectGetItem(",augvar,",",augsubs,", true);");
  687. this._checkSuspension(e)
  688. return this._gr("gitem", "$ret");
  689. case Load:
  690. case Store:
  691. case Del:
  692. return this.vslice(e.slice, e.ctx, this.vexpr(e.value), data);
  693. case AugStore:
  694. // To be more correct, we shouldn't sattr() again if the in-place update worked.
  695. // At the time of writing (26/Feb/2015), Sk.abstr.numberInplaceBinOp never returns undefined,
  696. // so this will never *not* execute. But it could, if Sk.abstr.numberInplaceBinOp were fixed.
  697. out("$ret=undefined;");
  698. out("if(", data, "!==undefined){");
  699. out("$ret=Sk.abstr.objectSetItem(",augvar,",",augsubs,",",data,", true)");
  700. out("}");
  701. this._checkSuspension(e);
  702. break;
  703. case Param:
  704. default:
  705. goog.asserts.fail("invalid subscript expression");
  706. }
  707. break;
  708. case Name:
  709. return this.nameop(e.id, e.ctx, data);
  710. case List:
  711. return this.ctuplelistorset(e, data, 'list');
  712. case Tuple:
  713. return this.ctuplelistorset(e, data, 'tuple');
  714. case Set:
  715. return this.ctuplelistorset(e, data, 'set');
  716. default:
  717. goog.asserts.fail("unhandled case in vexpr");
  718. }
  719. };
  720. /**
  721. * @param {Array.<Object>} exprs
  722. * @param {Array.<string>=} data
  723. */
  724. Compiler.prototype.vseqexpr = function (exprs, data) {
  725. var i;
  726. var ret;
  727. goog.asserts.assert(data === undefined || exprs.length === data.length);
  728. ret = [];
  729. for (i = 0; i < exprs.length; ++i) {
  730. ret.push(this.vexpr(exprs[i], data === undefined ? undefined : data[i]));
  731. }
  732. return ret;
  733. };
  734. Compiler.prototype.caugassign = function (s) {
  735. var to;
  736. var augsub;
  737. var res;
  738. var val;
  739. var aug;
  740. var auge;
  741. var e;
  742. goog.asserts.assert(s instanceof AugAssign);
  743. e = s.target;
  744. switch (e.constructor) {
  745. case Attribute:
  746. to = this.vexpr(e.value);
  747. auge = new Attribute(e.value, e.attr, AugLoad, e.lineno, e.col_offset, e.endlineno, e.col_endoffset);
  748. aug = this.vexpr(auge, undefined, to);
  749. val = this.vexpr(s.value);
  750. res = this._gr("inplbinopattr", "Sk.abstr.numberInplaceBinOp(", aug, ",", val, ",'", s.op.prototype._astname, "')");
  751. auge.ctx = AugStore;
  752. return this.vexpr(auge, res, to);
  753. case Subscript:
  754. // Only compile the subscript value once
  755. to = this.vexpr(e.value);
  756. augsub = this.vslicesub(e.slice);
  757. auge = new Subscript(e.value, augsub, AugLoad, e.lineno, e.col_offset, e.endlineno, e.col_endoffset);
  758. aug = this.vexpr(auge, undefined, to, augsub);
  759. val = this.vexpr(s.value);
  760. res = this._gr("inplbinopsubscr", "Sk.abstr.numberInplaceBinOp(", aug, ",", val, ",'", s.op.prototype._astname, "')");
  761. auge.ctx = AugStore;
  762. return this.vexpr(auge, res, to, augsub);
  763. case Name:
  764. to = this.nameop(e.id, Load);
  765. val = this.vexpr(s.value);
  766. res = this._gr("inplbinop", "Sk.abstr.numberInplaceBinOp(", to, ",", val, ",'", s.op.prototype._astname, "')");
  767. return this.nameop(e.id, Store, res);
  768. default:
  769. goog.asserts.fail("unhandled case in augassign");
  770. }
  771. };
  772. /**
  773. * optimize some constant exprs. returns 0 if always false, 1 if always true or -1 otherwise.
  774. */
  775. Compiler.prototype.exprConstant = function (e) {
  776. switch (e.constructor) {
  777. case Num:
  778. return Sk.misceval.isTrue(e.n) ? 1 : 0;
  779. case Str:
  780. return Sk.misceval.isTrue(e.s) ? 1 : 0;
  781. case Name:
  782. // todo; do __debug__ test here if opt
  783. default:
  784. return -1;
  785. }
  786. };
  787. Compiler.prototype.newBlock = function (name) {
  788. var ret = this.u.blocknum++;
  789. this.u.blocks[ret] = [];
  790. this.u.blocks[ret]._name = name || "<unnamed>";
  791. this.u.blocks[ret]._next = null;
  792. return ret;
  793. };
  794. Compiler.prototype.setBlock = function (n) {
  795. goog.asserts.assert(n >= 0 && n < this.u.blocknum);
  796. this.u.curblock = n;
  797. };
  798. Compiler.prototype.pushBreakBlock = function (n) {
  799. goog.asserts.assert(n >= 0 && n < this.u.blocknum);
  800. this.u.breakBlocks.push(n);
  801. };
  802. Compiler.prototype.popBreakBlock = function () {
  803. this.u.breakBlocks.pop();
  804. };
  805. Compiler.prototype.pushContinueBlock = function (n) {
  806. goog.asserts.assert(n >= 0 && n < this.u.blocknum);
  807. this.u.continueBlocks.push(n);
  808. };
  809. Compiler.prototype.popContinueBlock = function () {
  810. this.u.continueBlocks.pop();
  811. };
  812. Compiler.prototype.pushExceptBlock = function (n) {
  813. goog.asserts.assert(n >= 0 && n < this.u.blocknum);
  814. this.u.exceptBlocks.push(n);
  815. };
  816. Compiler.prototype.popExceptBlock = function () {
  817. this.u.exceptBlocks.pop();
  818. };
  819. Compiler.prototype.pushFinallyBlock = function (n) {
  820. goog.asserts.assert(n >= 0 && n < this.u.blocknum);
  821. goog.asserts.assert(this.u.breakBlocks.length === this.u.continueBlocks.length);
  822. this.u.finallyBlocks.push({blk: n, breakDepth: this.u.breakBlocks.length});
  823. };
  824. Compiler.prototype.popFinallyBlock = function () {
  825. this.u.finallyBlocks.pop();
  826. };
  827. Compiler.prototype.peekFinallyBlock = function() {
  828. return (this.u.finallyBlocks.length > 0) ? this.u.finallyBlocks[this.u.finallyBlocks.length-1] : undefined;
  829. };
  830. Compiler.prototype.setupExcept = function (eb) {
  831. out("$exc.push(", eb, ");");
  832. //this.pushExceptBlock(eb);
  833. };
  834. Compiler.prototype.endExcept = function () {
  835. out("$exc.pop();");
  836. };
  837. Compiler.prototype.outputLocals = function (unit) {
  838. var name;
  839. var output;
  840. var i;
  841. var have = {};
  842. //print("args", unit.name.v, JSON.stringify(unit.argnames));
  843. for (i = 0; unit.argnames && i < unit.argnames.length; ++i) {
  844. have[unit.argnames[i]] = true;
  845. }
  846. unit.localnames.sort();
  847. output = [];
  848. for (i = 0; i < unit.localnames.length; ++i) {
  849. name = unit.localnames[i];
  850. if (have[name] === undefined) {
  851. output.push(name);
  852. have[name] = true;
  853. }
  854. }
  855. if (output.length > 0) {
  856. return "var " + output.join(",") + "; /* locals */";
  857. }
  858. return "";
  859. };
  860. Compiler.prototype.outputSuspensionHelpers = function (unit) {
  861. var i, t;
  862. var localSaveCode = [];
  863. var localsToSave = unit.localnames.concat(unit.tempsToSave);
  864. var seenTemps = {};
  865. var hasCell = unit.ste.blockType === FunctionBlock && unit.ste.childHasFree;
  866. var output = (localsToSave.length > 0 ? ("var " + localsToSave.join(",") + ";") : "") +
  867. "var $wakeFromSuspension = function() {" +
  868. "var susp = "+unit.scopename+".$wakingSuspension; delete "+unit.scopename+".$wakingSuspension;" +
  869. "$blk=susp.$blk; $loc=susp.$loc; $gbl=susp.$gbl; $exc=susp.$exc; $err=susp.$err; $postfinally=susp.$postfinally;" +
  870. "$currLineNo=susp.$lineno; $currColNo=susp.$colno; Sk.lastYield=Date.now();" +
  871. (hasCell?"$cell=susp.$cell;":"");
  872. for (i = 0; i < localsToSave.length; i++) {
  873. t = localsToSave[i];
  874. if (seenTemps[t]===undefined) {
  875. output += t + "=susp.$tmps." + t + ";";
  876. seenTemps[t] = true;
  877. }
  878. }
  879. output += "try { $ret=susp.child.resume(); } catch(err) { if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now()} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if($exc.length>0) { $err=err; $blk=$exc.pop(); } else { throw err; } }" +
  880. "};";
  881. output += "var $saveSuspension = function($child, $filename, $lineno, $colno) {" +
  882. "var susp = new Sk.misceval.Suspension(); susp.child=$child;" +
  883. "susp.resume=function(){"+unit.scopename+".$wakingSuspension=susp; return "+unit.scopename+"("+(unit.ste.generator?"$gen":"")+"); };" +
  884. "susp.data=susp.child.data;susp.$blk=$blk;susp.$loc=$loc;susp.$gbl=$gbl;susp.$exc=$exc;susp.$err=$err;susp.$postfinally=$postfinally;" +
  885. "susp.$filename=$filename;susp.$lineno=$lineno;susp.$colno=$colno;" +
  886. "susp.optional=susp.child.optional;" +
  887. (hasCell ? "susp.$cell=$cell;" : "");
  888. seenTemps = {};
  889. for (i = 0; i < localsToSave.length; i++) {
  890. t = localsToSave[i];
  891. if (seenTemps[t]===undefined) {
  892. localSaveCode.push("\"" + t + "\":" + t);
  893. seenTemps[t]=true;
  894. }
  895. }
  896. output += "susp.$tmps={" + localSaveCode.join(",") + "};" +
  897. "return susp;" +
  898. "};";
  899. return output;
  900. }
  901. Compiler.prototype.outputAllUnits = function () {
  902. var i;
  903. var blocks;
  904. var unit;
  905. var j;
  906. var ret = "";
  907. var block;
  908. var generatedBlocks;
  909. for (j = 0; j < this.allUnits.length; ++j) {
  910. unit = this.allUnits[j];
  911. ret += unit.prefixCode;
  912. ret += this.outputLocals(unit);
  913. if (unit.doesSuspend) {
  914. ret += this.outputSuspensionHelpers(unit);
  915. }
  916. ret += unit.varDeclsCode;
  917. ret += unit.switchCode;
  918. blocks = unit.blocks;
  919. generatedBlocks = Object.create(null);
  920. for (i = 0; i < blocks.length; ++i) {
  921. block = i;
  922. if (block in generatedBlocks)
  923. continue;
  924. while (true) {
  925. generatedBlocks[block] = true;
  926. ret += "case " + block + ": /* --- " + blocks[block]._name + " --- */";
  927. ret += blocks[block].join("");
  928. if (blocks[block]._next !== null) {
  929. if (!(blocks[block]._next in generatedBlocks)) {
  930. ret += "/* allowing case fallthrough */";
  931. block = blocks[block]._next;
  932. }
  933. else {
  934. ret += "/* jump */ continue;";
  935. break;
  936. }
  937. }
  938. else {
  939. ret += "throw new Sk.builtin.SystemError('internal error: unterminated block');";
  940. break;
  941. }
  942. }
  943. }
  944. ret += unit.suffixCode;
  945. }
  946. return ret;
  947. };
  948. Compiler.prototype.cif = function (s) {
  949. var test;
  950. var next;
  951. var end;
  952. var constant;
  953. goog.asserts.assert(s instanceof If_);
  954. constant = this.exprConstant(s.test);
  955. if (constant === 0) {
  956. if (s.orelse && s.orelse.length > 0) {
  957. this.vseqstmt(s.orelse);
  958. }
  959. }
  960. else if (constant === 1) {
  961. this.vseqstmt(s.body);
  962. }
  963. else {
  964. end = this.newBlock("end of if");
  965. if (s.orelse && s.orelse.length > 0) {
  966. next = this.newBlock("next branch of if");
  967. }
  968. test = this.vexpr(s.test);
  969. if (s.orelse && s.orelse.length > 0) {
  970. this._jumpfalse(test, next);
  971. this.vseqstmt(s.body);
  972. this._jump(end);
  973. this.setBlock(next);
  974. this.vseqstmt(s.orelse);
  975. }
  976. else {
  977. this._jumpfalse(test, end);
  978. this.vseqstmt(s.body);
  979. }
  980. this._jump(end);
  981. this.setBlock(end);
  982. }
  983. };
  984. Compiler.prototype.cwhile = function (s) {
  985. var body;
  986. var orelse;
  987. var next;
  988. var top;
  989. var constant = this.exprConstant(s.test);
  990. if (constant === 0) {
  991. if (s.orelse) {
  992. this.vseqstmt(s.orelse);
  993. }
  994. }
  995. else {
  996. top = this.newBlock("while test");
  997. this._jump(top);
  998. this.setBlock(top);
  999. next = this.newBlock("after while");
  1000. orelse = s.orelse.length > 0 ? this.newBlock("while orelse") : null;
  1001. body = this.newBlock("while body");
  1002. this.annotateSource(s, true);
  1003. this._jumpfalse(this.vexpr(s.test), orelse ? orelse : next);
  1004. this._jump(body);
  1005. this.pushBreakBlock(next);
  1006. this.pushContinueBlock(top);
  1007. this.setBlock(body);
  1008. this.vseqstmt(s.body);
  1009. this._jump(top);
  1010. this.popContinueBlock();
  1011. this.popBreakBlock();
  1012. if (s.orelse.length > 0) {
  1013. this.setBlock(orelse);
  1014. this.vseqstmt(s.orelse);
  1015. this._jump(next);
  1016. }
  1017. this.setBlock(next);
  1018. }
  1019. };
  1020. Compiler.prototype.cfor = function (s) {
  1021. var target;
  1022. var nexti;
  1023. var iter;
  1024. var toiter;
  1025. var start = this.newBlock("for start");
  1026. var cleanup = this.newBlock("for cleanup");
  1027. var end = this.newBlock("for end");
  1028. this.pushBreakBlock(end);
  1029. this.pushContinueBlock(start);
  1030. // get the iterator
  1031. toiter = this.vexpr(s.iter);
  1032. if (this.u.ste.generator) {
  1033. // if we're in a generator, we have to store the iterator to a local
  1034. // so it's preserved (as we cross blocks here and assume it survives)
  1035. iter = "$loc." + this.gensym("iter");
  1036. out(iter, "=Sk.abstr.iter(", toiter, ", "+s.constructor.name+");");
  1037. }
  1038. else {
  1039. //print(JSON.stringify(s.iter))
  1040. iter = this._gr("iter", "Sk.abstr.iter(", toiter, ", "+JSON.stringify(s.iter)+")");
  1041. this.u.tempsToSave.push(iter); // Save it across suspensions
  1042. }
  1043. this._jump(start);
  1044. this.setBlock(start);
  1045. // load targets
  1046. out ("$ret = Sk.abstr.iternext(", iter,(this.u.canSuspend?", true":", false"),");");
  1047. this._checkSuspension(s);
  1048. nexti = this._gr("next", "$ret");
  1049. this._jumpundef(nexti, cleanup); // todo; this should be handled by StopIteration
  1050. target = this.vexpr(s.target, nexti);
  1051. // execute body
  1052. this.vseqstmt(s.body);
  1053. // jump to top of loop
  1054. this._jump(start);
  1055. this.setBlock(cleanup);
  1056. this.popContinueBlock();
  1057. this.popBreakBlock();
  1058. this.vseqstmt(s.orelse);
  1059. this._jump(end);
  1060. this.setBlock(end);
  1061. };
  1062. Compiler.prototype.craise = function (s) {
  1063. var inst = "", exc;
  1064. if (s.inst) {
  1065. // handles: raise Error, arguments
  1066. inst = this.vexpr(s.inst);
  1067. out("throw ", this.vexpr(s.type), "(", inst, ");");
  1068. }
  1069. else if (s.type) {
  1070. if (s.type.func) {
  1071. // handles: raise Error(arguments)
  1072. out("throw ", this.vexpr(s.type), ";");
  1073. }
  1074. else {
  1075. // handles: raise Error OR raise someinstance
  1076. exc = this._gr("err", this.vexpr(s.type));
  1077. out("if(",exc," instanceof Sk.builtin.type) {",
  1078. "throw Sk.misceval.callsim(", exc, ");",
  1079. "} else if(typeof(",exc,") === 'function') {",
  1080. "throw ",exc,"();",
  1081. "} else {",
  1082. "throw ", exc, ";",
  1083. "}");
  1084. }
  1085. }
  1086. else {
  1087. // re-raise
  1088. out("throw $err;");
  1089. }
  1090. };
  1091. Compiler.prototype.ctryexcept = function (s) {
  1092. var check;
  1093. var next;
  1094. var handlertype;
  1095. var handler;
  1096. var end;
  1097. var orelse;
  1098. var unhandled;
  1099. var i;
  1100. var n = s.handlers.length;
  1101. // Create a block for each except clause
  1102. var handlers = [];
  1103. for (i = 0; i < n; ++i) {
  1104. handlers.push(this.newBlock("except_" + i + "_"));
  1105. }
  1106. unhandled = this.newBlock("unhandled");
  1107. orelse = this.newBlock("orelse");
  1108. end = this.newBlock("end");
  1109. this.setupExcept(handlers[0]);
  1110. this.vseqstmt(s.body);
  1111. this.endExcept();
  1112. this._jump(orelse);
  1113. for (i = 0; i < n; ++i) {
  1114. this.setBlock(handlers[i]);
  1115. handler = s.handlers[i];
  1116. if (!handler.type && i < n - 1) {
  1117. throw new SyntaxError("default 'except:' must be last");
  1118. }
  1119. if (handler.type) {
  1120. // should jump to next handler if err not isinstance of handler.type
  1121. handlertype = this.vexpr(handler.type);
  1122. next = (i == n - 1) ? unhandled : handlers[i + 1];
  1123. // var isinstance = this.nameop(new Sk.builtin.str("isinstance"), Load));
  1124. // var check = this._gr('call', "Sk.misceval.callsim(", isinstance, ", $err, ", handlertype, ")");
  1125. check = this._gr("instance", "Sk.misceval.isTrue(Sk.builtin.isinstance($err, ", handlertype, "))");
  1126. this._jumpfalse(check, next);
  1127. }
  1128. if (handler.name) {
  1129. this.vexpr(handler.name, "$err");
  1130. }
  1131. this.vseqstmt(handler.body);
  1132. this._jump(end);
  1133. }
  1134. // If no except clause catches exception, throw it again
  1135. this.setBlock(unhandled);
  1136. // Should execute finally first
  1137. out("throw $err;");
  1138. this.setBlock(orelse);
  1139. this.vseqstmt(s.orelse);
  1140. // Should jump to finally, but finally is not implemented yet
  1141. this._jump(end);
  1142. this.setBlock(end);
  1143. };
  1144. Compiler.prototype.outputFinallyCascade = function (thisFinally) {
  1145. var nextFinally;
  1146. // What do we do when we're done executing a 'finally' block?
  1147. // Normally you just fall off the end. If we're 'return'ing,
  1148. // 'continue'ing or 'break'ing, $postfinally tells us what to do.
  1149. //
  1150. // But we might be in a nested pair of 'finally' blocks. If so, we need
  1151. // to work out whether to jump to the outer finally block.
  1152. //
  1153. // (NB we do NOT deal with re-raising exceptions here. That's handled
  1154. // elsewhere, because 'with' does special things with exceptions.)
  1155. if (this.u.finallyBlocks.length == 0) {
  1156. // No nested 'finally' block. Easy.
  1157. out("if($postfinally!==undefined) { if ($postfinally.returning) { return $postfinally.returning; } else { $blk=$postfinally.gotoBlock; $postfinally=undefined; continue; } }");
  1158. } else {
  1159. // OK, we're nested. Do we jump straight to the outer 'finally' block?
  1160. // Depends on how we got here here.
  1161. // Normal execution ($postfinally===undefined)? No, we're done here.
  1162. // Returning ($postfinally.returning)? Yes, we want to execute all the
  1163. // 'finally' blocks on the way out.
  1164. // Breaking ($postfinally.isBreak)? It depends. Is the outer 'finally'
  1165. // block inside or outside the loop we're breaking out of? We compare
  1166. // its breakDepth to ours to find out. If we're at the same breakDepth,
  1167. // we're both inside the innermost loop, so we both need to execute.
  1168. // ('continue' is the same thing as 'break' for us)
  1169. nextFinally = this.peekFinallyBlock();
  1170. out("if($postfinally!==undefined) {",
  1171. "if ($postfinally.returning",
  1172. (nextFinally.breakDepth == thisFinally.breakDepth) ? "|| $postfinally.isBreak" : "", ") {",
  1173. "$blk=",nextFinally.blk,";continue;",
  1174. "} else {",
  1175. "$blk=$postfinally.gotoBlock;$postfinally=undefined;continue;",
  1176. "}",
  1177. "}");
  1178. }
  1179. };
  1180. Compiler.prototype.ctryfinally = function (s) {
  1181. var finalBody = this.newBlock("finalbody");
  1182. var exceptionHandler = this.newBlock("finalexh");
  1183. var exceptionToReRaise = this._gr("finally_reraise", "undefined");
  1184. var thisFinally;
  1185. this.u.tempsToSave.push(exceptionToReRaise);
  1186. this.pushFinallyBlock(finalBody);
  1187. thisFinally = this.peekFinallyBlock();
  1188. this.setupExcept(exceptionHandler);
  1189. this.vseqstmt(s.body);
  1190. this.endExcept();
  1191. // Normal execution falls through to finally body
  1192. this._jump(finalBody);
  1193. this.setBlock(exceptionHandler);
  1194. // Exception handling also goes to the finally body,
  1195. // stashing the original exception to re-raise
  1196. out(exceptionToReRaise,"=$err;");
  1197. this._jump(finalBody);
  1198. this.setBlock(finalBody);
  1199. this.popFinallyBlock();
  1200. this.vseqstmt(s.finalbody);
  1201. // If finalbody executes normally, AND we have an exception
  1202. // to re-raise, we raise it.
  1203. out("if(",exceptionToReRaise,"!==undefined) { throw ",exceptionToReRaise,";}");
  1204. this.outputFinallyCascade(thisFinally);
  1205. // Else, we continue from here.
  1206. };
  1207. Compiler.prototype.cwith = function (s) {
  1208. var mgr, exit, value, exception;
  1209. var exceptionHandler = this.newBlock("withexh"), tidyUp = this.newBlock("withtidyup");
  1210. var carryOn = this.newBlock("withcarryon");
  1211. var thisFinallyBlock;
  1212. // NB this does not *quite* match the semantics in PEP 343, which
  1213. // specifies "exit = type(mgr).__exit__" rather than getattr()ing,
  1214. // presumably for performance reasons.
  1215. mgr = this._gr("mgr", this.vexpr(s.context_expr));
  1216. // exit = mgr.__exit__
  1217. out("$ret = Sk.abstr.gattr(",mgr,",'__exit__', true);");
  1218. this._checkSuspension(s);
  1219. exit = this._gr("exit", "$ret");
  1220. this.u.tempsToSave.push(exit);
  1221. // value = mgr.__enter__()
  1222. out("$ret = Sk.abstr.gattr(",mgr,",'__enter__', true);");
  1223. this._checkSuspension(s);
  1224. out("$ret = Sk.misceval.callsimOrSuspend($ret);");
  1225. this._checkSuspension(s);
  1226. value = this._gr("value", "$ret");
  1227. // try:
  1228. this.pushFinallyBlock(tidyUp);
  1229. thisFinallyBlock = this.u.finallyBlocks[this.u.finallyBlocks.length-1];
  1230. this.setupExcept(exceptionHandler);
  1231. // VAR = value
  1232. if (s.optional_vars) {
  1233. this.nameop(s.optional_vars.id, Store, value);
  1234. }
  1235. // (try body)
  1236. this.vseqstmt(s.body);
  1237. this.endExcept();
  1238. this._jump(tidyUp);
  1239. // except:
  1240. this.setBlock(exceptionHandler);
  1241. // if not exit(*sys.exc_info()):
  1242. // raise
  1243. out("$ret = Sk.misceval.applyOrSuspend(",exit,",undefined,Sk.builtin.getExcInfo($err),undefined,[]);");
  1244. this._checkSuspension(s);
  1245. this._jumptrue("$ret", carryOn);
  1246. out("throw $err;");
  1247. // finally: (kinda. NB that this is a "finally" that doesn't run in the
  1248. // exception case!)
  1249. this.setBlock(tidyUp);
  1250. this.popFinallyBlock();
  1251. // exit(None, None, None)
  1252. out("$ret = Sk.misceval.callsimOrSuspend(",exit,",Sk.builtin.none.none$,Sk.builtin.none.none$,Sk.builtin.none.none$);");
  1253. this._checkSuspension(s);
  1254. // Ignore $ret.
  1255. this.outputFinallyCascade(thisFinallyBlock);
  1256. this._jump(carryOn);
  1257. this.setBlock(carryOn);
  1258. };
  1259. Compiler.prototype.cassert = function (s) {
  1260. /* todo; warnings method
  1261. if (s.test instanceof Tuple && s.test.elts.length > 0)
  1262. Sk.warn("assertion is always true, perhaps remove parentheses?");
  1263. */
  1264. var test = this.vexpr(s.test);
  1265. var end = this.newBlock("end");
  1266. this._jumptrue(test, end);
  1267. // todo; exception handling
  1268. // maybe replace with goog.asserts.fail?? or just an alert?
  1269. out("throw new Sk.builtin.AssertionError(", s.msg ? this.vexpr(s.msg) : "", ");");
  1270. this.setBlock(end);
  1271. };
  1272. Compiler.prototype.cimportas = function (name, asname, mod) {
  1273. var attr;
  1274. var src = name.v;
  1275. var dotLoc = src.indexOf(".");
  1276. //print("src", src);
  1277. //print("dotLoc", dotLoc);
  1278. var cur = mod;
  1279. if (dotLoc !== -1) {
  1280. // if there's dots in the module name, __import__ will have returned
  1281. // the top-level module. so, we need to extract the actual module by
  1282. // getattr'ing up through the names, and then storing the leaf under
  1283. // the name it was to be imported as.
  1284. src = src.substr(dotLoc + 1);
  1285. //print("src now", src);
  1286. while (dotLoc !== -1) {
  1287. dotLoc = src.indexOf(".");
  1288. attr = dotLoc !== -1 ? src.substr(0, dotLoc) : src;
  1289. cur = this._gr("lattr", "Sk.abstr.gattr(", cur, ",'", attr, "')");
  1290. src = src.substr(dotLoc + 1);
  1291. }
  1292. }
  1293. return this.nameop(asname, Store, cur);
  1294. };
  1295. Compiler.prototype.cimport = function (s) {
  1296. var lastDot;
  1297. var tmp;
  1298. var mod;
  1299. var alias;
  1300. var i;
  1301. var n = s.names.length;
  1302. for (i = 0; i < n; ++i) {
  1303. alias = s.names[i];
  1304. out("$ret = Sk.builtin.__import__(", alias.name["$r"]().v, ",$gbl,$loc,[]);");
  1305. this._checkSuspension(s);
  1306. mod = this._gr("module", "$ret");
  1307. if (alias.asname) {
  1308. this.cimportas(alias.name, alias.asname, mod);
  1309. }
  1310. else {
  1311. tmp = alias.name;
  1312. lastDot = tmp.v.indexOf(".");
  1313. if (lastDot !== -1) {
  1314. tmp = new Sk.builtin.str(tmp.v.substr(0, lastDot));
  1315. }
  1316. this.nameop(tmp, Store, mod);
  1317. }
  1318. }
  1319. };
  1320. Compiler.prototype.cfromimport = function (s) {
  1321. var storeName;
  1322. var got;
  1323. var alias;
  1324. var mod;
  1325. var i;
  1326. var n = s.names.length;
  1327. var names = [];
  1328. for (i = 0; i < n; ++i) {
  1329. names[i] = s.names[i].name["$r"]().v;
  1330. }
  1331. out("$ret = Sk.builtin.__import__(", s.module["$r"]().v, ",$gbl,$loc,[", names, "]);");
  1332. this._checkSuspension(s);
  1333. //out("print('__import__ returned ' + $ret);");
  1334. //out("for (var x in $ret) { print(x); }");
  1335. mod = this._gr("module", "$ret");
  1336. for (i = 0; i < n; ++i) {
  1337. alias = s.names[i];
  1338. if (i === 0 && alias.name.v === "*") {
  1339. goog.asserts.assert(n === 1);
  1340. out("Sk.importStar(", mod, ",$loc, $gbl);");
  1341. return;
  1342. }
  1343. //out("print(\"getting Sk.abstr.gattr(", mod, ",", alias.name["$r"]().v, ")\");");
  1344. got = this._gr("item", "Sk.abstr.gattr(", mod, ",", alias.name["$r"]().v, ")");
  1345. //out("print('got');");
  1346. storeName = alias.name;
  1347. if (alias.asname) {
  1348. storeName = alias.asname;
  1349. }
  1350. this.nameop(storeName, Store, got);
  1351. }
  1352. };
  1353. /**
  1354. * builds a code object (js function) for various constructs. used by def,
  1355. * lambda, generator expressions. it isn't used for class because it seemed
  1356. * different enough.
  1357. *
  1358. * handles:
  1359. * - setting up a new scope
  1360. * - decorators (if any)
  1361. * - defaults setup
  1362. * - setup for cell and free vars
  1363. * - setup and modification for generators
  1364. *
  1365. * @param {Object} n ast node to build for
  1366. * @param {Sk.builtin.str} coname name of code object to build
  1367. * @param {Array} decorator_list ast of decorators if any
  1368. * @param {arguments_} args arguments to function, if any
  1369. * @param {Function} callback called after setup to do actual work of function
  1370. *
  1371. * @returns the name of the newly created function or generator object.
  1372. *
  1373. */
  1374. Compiler.prototype.buildcodeobj = function (n, coname, decorator_list, args, callback) {
  1375. var containingHasFree;
  1376. var frees;
  1377. var argnamesarr;
  1378. var argnames;
  1379. var start;
  1380. var kw;
  1381. var maxargs;
  1382. var minargs;
  1383. var id;
  1384. var argname;
  1385. var offset;
  1386. var cells;
  1387. var locals;
  1388. var i;
  1389. var funcArgs;
  1390. var entryBlock;
  1391. var hasCell;
  1392. var hasFree;
  1393. var isGenerator;
  1394. var scopename;
  1395. var decos = [];
  1396. var defaults = [];
  1397. var vararg = null;
  1398. var kwarg = null;
  1399. // decorators and defaults have to be evaluated out here before we enter
  1400. // the new scope. we output the defaults and attach them to this code
  1401. // object, but only once we know the name of it (so we do it after we've
  1402. // exited the scope near the end of this function).
  1403. if (decorator_list) {
  1404. decos = this.vseqexpr(decorator_list);
  1405. }
  1406. if (args && args.defaults) {
  1407. defaults = this.vseqexpr(args.defaults);
  1408. }
  1409. if (args && args.vararg) {
  1410. vararg = args.vararg;
  1411. }
  1412. if (args && args.kwarg) {
  1413. kwarg = args.kwarg;
  1414. }
  1415. //
  1416. // enter the new scope, and create the first block
  1417. //
  1418. scopename = this.enterScope(coname, n, n.lineno, this.canSuspend);
  1419. isGenerator = this.u.ste.generator;
  1420. hasFree = this.u.ste.hasFree;
  1421. hasCell = this.u.ste.childHasFree;
  1422. entryBlock = this.newBlock("codeobj entry");
  1423. //
  1424. // the header of the function, and arguments
  1425. //
  1426. this.u.prefixCode = "var " + scopename + "=(function " + this.niceName(coname.v) + "$(";
  1427. funcArgs = [];
  1428. if (isGenerator) {
  1429. if (kwarg) {
  1430. throw new SyntaxError(coname.v + "(): keyword arguments in generators not supported");
  1431. }
  1432. if (vararg) {
  1433. throw new SyntaxError(coname.v + "(): variable number of arguments in generators not supported");
  1434. }
  1435. funcArgs.push("$gen");
  1436. }
  1437. else {
  1438. if (kwarg) {
  1439. funcArgs.push("$kwa");
  1440. this.u.tempsToSave.push("$kwa");
  1441. }
  1442. for (i = 0; args && i < args.args.length; ++i) {
  1443. funcArgs.push(this.nameop(args.args[i].id, Param));
  1444. }
  1445. }
  1446. if (hasFree) {
  1447. if (vararg) {
  1448. this.u.varDeclsCode += "$free = arguments[arguments.length-1];"
  1449. } else {
  1450. funcArgs.push("$free");
  1451. this.u.tempsToSave.push("$free");
  1452. }
  1453. }
  1454. this.u.prefixCode += funcArgs.join(",");
  1455. this.u.prefixCode += "){";
  1456. if (isGenerator) {
  1457. this.u.prefixCode += "\n// generator\n";
  1458. }
  1459. if (hasFree) {
  1460. this.u.prefixCode += "\n// has free\n";
  1461. }
  1462. if (hasCell) {
  1463. this.u.prefixCode += "\n// has cell\n";
  1464. }
  1465. //
  1466. // set up standard dicts/variables
  1467. //
  1468. locals = "{}";
  1469. if (isGenerator) {
  1470. entryBlock = "$gen.gi$resumeat";
  1471. locals = "$gen.gi$locals";
  1472. }
  1473. cells = "";
  1474. if (hasCell) {
  1475. if (isGenerator) {
  1476. cells = ",$cell=$gen.gi$cells";
  1477. }
  1478. else {
  1479. cells = ",$cell={}";
  1480. }
  1481. }
  1482. // note special usage of 'this' to avoid having to slice globals into
  1483. // all function invocations in call
  1484. this.u.varDeclsCode += "var $blk=" + entryBlock + ",$exc=[],$loc=" + locals + cells + ",$gbl=this,$err=undefined,$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;";
  1485. if (Sk.execLimit !== null) {
  1486. this.u.varDeclsCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now()}";
  1487. }
  1488. if (Sk.yieldLimit !== null && this.u.canSuspend) {
  1489. this.u.varDeclsCode += "if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}";
  1490. }
  1491. //
  1492. // If there is a suspension, resume from it. Otherwise, initialise
  1493. // parameters appropriately.
  1494. //
  1495. this.u.varDeclsCode += "if ("+scopename+".$wakingSuspension!==undefined) { $wakeFromSuspension(); } else {";
  1496. //
  1497. // initialize default arguments. we store the values of the defaults to
  1498. // this code object as .$defaults just below after we exit this scope.
  1499. //
  1500. if (defaults.length > 0) {
  1501. // defaults have to be "right justified" so if there's less defaults
  1502. // than args we offset to make them match up (we don't need another
  1503. // correlation in the ast)
  1504. offset = args.args.length - defaults.length;
  1505. for (i = 0; i < defaults.length; ++i) {
  1506. argname = this.nameop(args.args[i + offset].id, Param);
  1507. this.u.varDeclsCode += "if(" + argname + "===undefined)" + argname + "=" + scopename + ".$defaults[" + i + "];";
  1508. }
  1509. }
  1510. //
  1511. // copy all parameters that are also cells into the cells dict. this is so
  1512. // they can be accessed correctly by nested scopes.
  1513. //
  1514. for (i = 0; args && i < args.args.length; ++i) {
  1515. id = args.args[i].id;
  1516. if (this.isCell(id)) {
  1517. this.u.varDeclsCode += "$cell." + id.v + "=" + id.v + ";";
  1518. }
  1519. }
  1520. //
  1521. // make sure correct number of arguments were passed (generators handled below)
  1522. //
  1523. if (!isGenerator) {
  1524. minargs = args ? args.args.length - defaults.length : 0;
  1525. maxargs = vararg ? Infinity : (args ? args.args.length : 0);
  1526. kw = kwarg ? true : false;
  1527. this.u.varDeclsCode += "Sk.builtin.pyCheckArgs(\"" + coname.v +
  1528. "\", arguments, " + minargs + ", " + maxargs + ", " + kw +
  1529. ", " + hasFree + ");";
  1530. }
  1531. //
  1532. // initialize vararg, if any
  1533. //
  1534. if (vararg) {
  1535. start = funcArgs.length;
  1536. this.u.localnames.push(vararg.v);
  1537. this.u.varDeclsCode += vararg.v + "=new Sk.builtins['tuple'](Array.prototype.slice.call(arguments," + start + (hasFree ? ",-1)" : ")") + "); /*vararg*/";
  1538. }
  1539. //
  1540. // initialize kwarg, if any
  1541. //
  1542. if (kwarg) {
  1543. this.u.localnames.push(kwarg.v);
  1544. this.u.varDeclsCode += kwarg.v + "=new Sk.builtins['dict']($kwa);";
  1545. }
  1546. //
  1547. // close the else{} block from the wakingSuspension check
  1548. //
  1549. this.u.varDeclsCode += "}";
  1550. //
  1551. // finally, set up the block switch that the jump code expects
  1552. //
  1553. // Old switch code
  1554. // this.u.switchCode += "while(true){switch($blk){";
  1555. // this.u.suffixCode = "}break;}});";
  1556. // New switch code to catch exceptions
  1557. this.u.switchCode = "while(true){try{"
  1558. this.u.switchCode += this.outputInterruptTest();
  1559. this.u.switchCode += "switch($blk){";
  1560. this.u.suffixCode = "} }catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now()} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} }});";
  1561. //
  1562. // jump back to the handler so it can do the main actual work of the
  1563. // function
  1564. //
  1565. callback.call(this, scopename);
  1566. //
  1567. // get a list of all the argument names (used to attach to the code
  1568. // object, and also to allow us to declare only locals that aren't also
  1569. // parameters).
  1570. if (args && args.args.length > 0) {
  1571. argnamesarr = [];
  1572. for (i = 0; i < args.args.length; ++i) {
  1573. argnamesarr.push(args.args[i].id.v);
  1574. }
  1575. argnames = argnamesarr.join("', '");
  1576. // store to unit so we know what local variables not to declare
  1577. this.u.argnames = argnamesarr;
  1578. }
  1579. //
  1580. // and exit the code object scope
  1581. //
  1582. this.exitScope();
  1583. //
  1584. // attach the default values we evaluated at the beginning to the code
  1585. // object so that it can get at them to set any arguments that are left
  1586. // unset.
  1587. //
  1588. if (defaults.length > 0) {
  1589. out(scopename, ".$defaults=[", defaults.join(","), "];");
  1590. }
  1591. if (decos.length > 0) {
  1592. out(scopename, ".$decorators=[", decos.join(","), "];");
  1593. }
  1594. //
  1595. // attach co_varnames (only the argument names) for keyword argument
  1596. // binding.
  1597. //
  1598. if (argnames) {
  1599. out(scopename, ".co_varnames=['", argnames, "'];");
  1600. }
  1601. //
  1602. // attach flags
  1603. //
  1604. if (kwarg) {
  1605. out(scopename, ".co_kwargs=1;");
  1606. }
  1607. //
  1608. // build either a 'function' or 'generator'. the function is just a simple
  1609. // constructor call. the generator is more complicated. it needs to make a
  1610. // new generator every time it's called, so the thing that's returned is
  1611. // actually a function that makes the generator (and passes arguments to
  1612. // the function onwards to the generator). this should probably actually
  1613. // be a function object, rather than a js function like it is now. we also
  1614. // have to build the argument names to pass to the generator because it
  1615. // needs to store all locals into itself so that they're maintained across
  1616. // yields.
  1617. //
  1618. // todo; possibly this should be outside?
  1619. //
  1620. frees = "";
  1621. if (hasFree) {
  1622. frees = ",$cell";
  1623. // if the scope we're in where we're defining this one has free
  1624. // vars, they may also be cell vars, so we pass those to the
  1625. // closure too.
  1626. containingHasFree = this.u.ste.hasFree;
  1627. if (containingHasFree) {
  1628. frees += ",$free";
  1629. }
  1630. }
  1631. if (isGenerator)
  1632. // Keyword and variable arguments are not currently supported in generators.
  1633. // The call to pyCheckArgs assumes they can't be true.
  1634. {
  1635. if (args && args.args.length > 0) {
  1636. return this._gr("gener", "new Sk.builtins['function']((function(){var $origargs=Array.prototype.slice.call(arguments);Sk.builtin.pyCheckArgs(\"",
  1637. coname.v, "\",arguments,", args.args.length - defaults.length, ",", args.args.length,
  1638. ");return new Sk.builtins['generator'](", scopename, ",$gbl,$origargs", frees, ");}))");
  1639. }
  1640. else {
  1641. return this._gr("gener", "new Sk.builtins['function']((function(){Sk.builtin.pyCheckArgs(\"", coname.v,
  1642. "\",arguments,0,0);return new Sk.builtins['generator'](", scopename, ",$gbl,[]", frees, ");}))");
  1643. }
  1644. }
  1645. else {
  1646. var res;
  1647. if (decos.length > 0) {
  1648. out("$ret = Sk.misceval.callsimOrSuspend(", scopename, ".$decorators[0], new Sk.builtins['function'](", scopename, ",$gbl", frees, "));");
  1649. this._checkSuspension();
  1650. return this._gr("funcobj", "$ret");
  1651. }
  1652. return this._gr("funcobj", "new Sk.builtins['function'](", scopename, ",$gbl", frees, ")");
  1653. }
  1654. };
  1655. Compiler.prototype.cfunction = function (s) {
  1656. var funcorgen;
  1657. goog.asserts.assert(s instanceof FunctionDef);
  1658. funcorgen = this.buildcodeobj(s, s.name, s.decorator_list, s.args, function (scopename) {
  1659. this.vseqstmt(s.body);
  1660. out("return Sk.builtin.none.none$;"); // if we fall off the bottom, we want the ret to be None
  1661. });
  1662. this.nameop(s.name, Store, funcorgen);
  1663. };
  1664. Compiler.prototype.clambda = function (e) {
  1665. var func;
  1666. goog.asserts.assert(e instanceof Lambda);
  1667. func = this.buildcodeobj(e, new Sk.builtin.str("<lambda>"), null, e.args, function (scopename) {
  1668. var val = this.vexpr(e.body);
  1669. out("return ", val, ";");
  1670. });
  1671. return func;
  1672. };
  1673. Compiler.prototype.cifexp = function (e) {
  1674. var next = this.newBlock("next of ifexp");
  1675. var end = this.newBlock("end of ifexp");
  1676. var ret = this._gr("res", "null");
  1677. var test = this.vexpr(e.test);
  1678. this._jumpfalse(test, next);
  1679. out(ret, "=", this.vexpr(e.body), ";");
  1680. this._jump(end);
  1681. this.setBlock(next);
  1682. out(ret, "=", this.vexpr(e.orelse), ";");
  1683. this._jump(end);
  1684. this.setBlock(end);
  1685. return ret;
  1686. };
  1687. Compiler.prototype.cgenexpgen = function (generators, genIndex, elt) {
  1688. var velt;
  1689. var ifres;
  1690. var i;
  1691. var n;
  1692. var target;
  1693. var nexti;
  1694. var toiter;
  1695. var start = this.newBlock("start for " + genIndex);
  1696. var skip = this.newBlock("skip for " + genIndex);
  1697. var ifCleanup = this.newBlock("if cleanup for " + genIndex);
  1698. var end = this.newBlock("end for " + genIndex);
  1699. var ge = generators[genIndex];
  1700. var iter;
  1701. if (genIndex === 0) {
  1702. // the outer most iterator is evaluated in the scope outside so we
  1703. // have to evaluate it outside and store it into the generator as a
  1704. // local, which we retrieve here.
  1705. iter = "$loc.$iter0";
  1706. }
  1707. else {
  1708. toiter = this.vexpr(ge.iter);
  1709. iter = "$loc." + this.gensym("iter");
  1710. out(iter, "=", "Sk.abstr.iter(", toiter, ");");
  1711. }
  1712. this._jump(start);
  1713. this.setBlock(start);
  1714. this.annotateSource(elt, false);
  1715. // load targets
  1716. out ("$ret = Sk.abstr.iternext(", iter,(this.u.canSuspend?", true":", false"),");");
  1717. this._checkSuspension(elt);
  1718. nexti = this._gr("next", "$ret");
  1719. this._jumpundef(nexti, end); // todo; this should be handled by StopIteration
  1720. target = this.vexpr(ge.target, nexti);
  1721. n = ge.ifs.length;
  1722. for (i = 0; i < n; ++i) {
  1723. this.annotateSource(ge.ifs[i], false);
  1724. ifres = this.vexpr(ge.ifs[i]);
  1725. this._jumpfalse(ifres, start);
  1726. }
  1727. if (++genIndex < generators.length) {
  1728. this.cgenexpgen(generators, genIndex, elt);
  1729. }
  1730. if (genIndex >= generators.length) {
  1731. this.annotateSource(elt, false);
  1732. velt = this.vexpr(elt);
  1733. out("return [", skip, "/*resume*/,", velt, "/*ret*/];");
  1734. this.setBlock(skip);
  1735. }
  1736. this._jump(start);
  1737. this.setBlock(end);
  1738. if (genIndex === 1) {
  1739. out("return Sk.builtin.none.none$;");
  1740. }
  1741. };
  1742. Compiler.prototype.cgenexp = function (e) {
  1743. var gen = this.buildcodeobj(e, new Sk.builtin.str("<genexpr>"), null, null, function (scopename) {
  1744. this.cgenexpgen(e.generators, 0, e.elt);
  1745. });
  1746. // call the generator maker to get the generator. this is kind of dumb,
  1747. // but the code builder builds a wrapper that makes generators for normal
  1748. // function generators, so we just do it outside (even just new'ing it
  1749. // inline would be fine).
  1750. var gener = this._gr("gener", "Sk.misceval.callsim(", gen, ");");
  1751. // stuff the outermost iterator into the generator after evaluating it
  1752. // outside of the function. it's retrieved by the fixed name above.
  1753. out(gener, ".gi$locals.$iter0=Sk.abstr.iter(", this.vexpr(e.generators[0].iter), ");");
  1754. return gener;
  1755. };
  1756. Compiler.prototype.cclass = function (s) {
  1757. var wrapped;
  1758. var entryBlock;
  1759. var scopename;
  1760. var bases;
  1761. var decos;
  1762. goog.asserts.assert(s instanceof ClassDef);
  1763. decos = s.decorator_list;
  1764. // decorators and bases need to be eval'd out here
  1765. //this.vseqexpr(decos);
  1766. bases = this.vseqexpr(s.bases);
  1767. scopename = this.enterScope(s.name, s, s.lineno);
  1768. entryBlock = this.newBlock("class entry");
  1769. this.u.prefixCode = "var " + scopename + "=(function $" + s.name.v + "$class_outer($globals,$locals,$rest){var $gbl=$globals,$loc=$locals;";
  1770. this.u.switchCode += "(function $" + s.name.v + "$_closure(){";
  1771. this.u.switchCode += "var $blk=" + entryBlock + ",$exc=[],$ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;"
  1772. if (Sk.execLimit !== null) {
  1773. this.u.switchCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now()}";
  1774. }
  1775. if (Sk.yieldLimit !== null && this.u.canSuspend) {
  1776. this.u.switchCode += "if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}";
  1777. }
  1778. this.u.switchCode += "while(true){try{";
  1779. this.u.switchCode += this.outputInterruptTest();
  1780. this.u.switchCode += "switch($blk){";
  1781. this.u.suffixCode = "}}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now()} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }}}"
  1782. this.u.suffixCode += "}).apply(null,$rest);});";
  1783. this.u.private_ = s.name;
  1784. this.cbody(s.body);
  1785. out("return;");
  1786. // build class
  1787. // apply decorators
  1788. this.exitScope();
  1789. // todo; metaclass
  1790. wrapped = this._gr("built", "Sk.misceval.buildClass($gbl,", scopename, ",", s.name["$r"]().v, ",[", bases, "])");
  1791. // store our new class under the right name
  1792. this.nameop(s.name, Store, wrapped);
  1793. };
  1794. Compiler.prototype.ccontinue = function (s) {
  1795. var nextFinally = this.peekFinallyBlock(), gotoBlock;
  1796. if (this.u.continueBlocks.length == 0) {
  1797. throw new SyntaxError("'continue' outside loop");
  1798. }
  1799. // todo; continue out of exception blocks
  1800. gotoBlock = this.u.continueBlocks[this.u.continueBlocks.length - 1];
  1801. goog.asserts.assert(this.u.breakBlocks.length === this.u.continueBlocks.length);
  1802. if (nextFinally && nextFinally.breakDepth == this.u.continueBlocks.length) {
  1803. out("$postfinally={isBreak:true,gotoBlock:",gotoBlock,"};");
  1804. } else {
  1805. this._jump(gotoBlock);
  1806. }
  1807. };
  1808. Compiler.prototype.cbreak = function (s) {
  1809. var nextFinally = this.peekFinallyBlock(), gotoBlock;
  1810. if (this.u.breakBlocks.length === 0) {
  1811. throw new SyntaxError("'break' outside loop");
  1812. }
  1813. gotoBlock = this.u.breakBlocks[this.u.breakBlocks.length - 1];
  1814. if (nextFinally && nextFinally.breakDepth == this.u.breakBlocks.length) {
  1815. out("$postfinally={isBreak:true,gotoBlock:",gotoBlock,"};");
  1816. } else {
  1817. this._jump(gotoBlock);
  1818. }
  1819. };
  1820. /**
  1821. * compiles a statement
  1822. */
  1823. Compiler.prototype.vstmt = function (s) {
  1824. var i;
  1825. var val;
  1826. var n;
  1827. var debugBlock;
  1828. this.u.lineno = s.lineno;
  1829. this.u.linenoSet = false;
  1830. this.u.localtemps = [];
  1831. if (Sk.debugging && this.u.canSuspend) {
  1832. debugBlock = this.newBlock("debug breakpoint for line "+s.lineno);
  1833. out("if (Sk.breakpoints('"+this.filename+"',"+s.lineno+","+s.col_offset+")) {",
  1834. "var $susp = $saveSuspension({data: {type: 'Sk.debug'}, resume: function() {}}, '"+this.filename+"',"+s.lineno+","+s.col_offset+");",
  1835. "$susp.$blk = "+debugBlock+";",
  1836. "$susp.optional = true;",
  1837. "return $susp;",
  1838. "}");
  1839. this._jump(debugBlock);
  1840. this.setBlock(debugBlock);
  1841. this.u.doesSuspend = true;
  1842. }
  1843. this.annotateSource(s, true);
  1844. switch (s.constructor) {
  1845. case FunctionDef:
  1846. this.cfunction(s);
  1847. break;
  1848. case ClassDef:
  1849. this.cclass(s);
  1850. break;
  1851. case Return_:
  1852. if (this.u.ste.blockType !== FunctionBlock) {
  1853. throw new SyntaxError("'return' outside function");
  1854. }
  1855. val = s.value ? this.vexpr(s.value) : "Sk.builtin.none.none$";
  1856. if (this.u.finallyBlocks.length == 0) {
  1857. out("return ", val, ";");
  1858. } else {
  1859. out("$postfinally={returning:",val,"};");
  1860. this._jump(this.peekFinallyBlock().blk);
  1861. }
  1862. break;
  1863. case Delete_:
  1864. this.vseqexpr(s.targets);
  1865. break;
  1866. case Assign:
  1867. n = s.targets.length;
  1868. val = this.vexpr(s.value);
  1869. for (i = 0; i < n; ++i) {
  1870. this.vexpr(s.targets[i], val);
  1871. }
  1872. break;
  1873. case AugAssign:
  1874. return this.caugassign(s);
  1875. case Print:
  1876. this.cprint(s);
  1877. break;
  1878. case For_:
  1879. return this.cfor(s);
  1880. case While_:
  1881. return this.cwhile(s);
  1882. case If_:
  1883. return this.cif(s);
  1884. case Raise:
  1885. return this.craise(s);
  1886. case TryExcept:
  1887. return this.ctryexcept(s);
  1888. case TryFinally:
  1889. return this.ctryfinally(s);
  1890. case With_:
  1891. return this.cwith(s);
  1892. case Assert:
  1893. return this.cassert(s);
  1894. case Import_:
  1895. return this.cimport(s);
  1896. case ImportFrom:
  1897. return this.cfromimport(s);
  1898. case Global:
  1899. break;
  1900. case Expr:
  1901. this.vexpr(s.value);
  1902. break;
  1903. case Pass:
  1904. break;
  1905. case Break_:
  1906. this.cbreak(s);
  1907. break;
  1908. case Continue_:
  1909. this.ccontinue(s);
  1910. break;
  1911. case Debugger_:
  1912. out("debugger;");
  1913. break;
  1914. default:
  1915. goog.asserts.fail("unhandled case in vstmt: " + s.constructor.name);
  1916. }
  1917. };
  1918. Compiler.prototype.vseqstmt = function (stmts) {
  1919. var i;
  1920. for (i = 0; i < stmts.length; ++i) {
  1921. this.vstmt(stmts[i]);
  1922. }
  1923. };
  1924. var OP_FAST = 0;
  1925. var OP_GLOBAL = 1;
  1926. var OP_DEREF = 2;
  1927. var OP_NAME = 3;
  1928. var D_NAMES = 0;
  1929. var D_FREEVARS = 1;
  1930. var D_CELLVARS = 2;
  1931. Compiler.prototype.isCell = function (name) {
  1932. var mangled = mangleName(this.u.private_, name).v;
  1933. var scope = this.u.ste.getScope(mangled);
  1934. var dict = null;
  1935. return scope === CELL;
  1936. };
  1937. /**
  1938. * @param {Sk.builtin.str} name
  1939. * @param {Object} ctx
  1940. * @param {string=} dataToStore
  1941. */
  1942. Compiler.prototype.nameop = function (name, ctx, dataToStore) {
  1943. var v;
  1944. var mangledNoPre;
  1945. var dict;
  1946. var scope;
  1947. var optype;
  1948. var op;
  1949. var mangled;
  1950. if ((ctx === Store || ctx === AugStore || ctx === Del) && name.v === "__debug__") {
  1951. throw new Sk.builtin.SyntaxError("can not assign to __debug__");
  1952. }
  1953. if ((ctx === Store || ctx === AugStore || ctx === Del) && name.v === "None") {
  1954. throw new Sk.builtin.SyntaxError("can not assign to None");
  1955. }
  1956. if (name.v === "None") {
  1957. return "Sk.builtin.none.none$";
  1958. }
  1959. if (name.v === "True") {
  1960. return "Sk.builtin.bool.true$";
  1961. }
  1962. if (name.v === "False") {
  1963. return "Sk.builtin.bool.false$";
  1964. }
  1965. if (name.v === "NotImplemented") {
  1966. return "Sk.builtin.NotImplemented.NotImplemented$";
  1967. }
  1968. mangled = mangleName(this.u.private_, name).v;
  1969. // Have to do this before looking it up in the scope
  1970. mangled = fixReservedNames(mangled);
  1971. op = 0;
  1972. optype = OP_NAME;
  1973. scope = this.u.ste.getScope(mangled);
  1974. dict = null;
  1975. switch (scope) {
  1976. case FREE:
  1977. dict = "$free";
  1978. optype = OP_DEREF;
  1979. break;
  1980. case CELL:
  1981. dict = "$cell";
  1982. optype = OP_DEREF;
  1983. break;
  1984. case LOCAL:
  1985. // can't do FAST in generators or at module/class scope
  1986. if (this.u.ste.blockType === FunctionBlock && !this.u.ste.generator) {
  1987. optype = OP_FAST;
  1988. }
  1989. break;
  1990. case GLOBAL_IMPLICIT:
  1991. if (this.u.ste.blockType === FunctionBlock) {
  1992. optype = OP_GLOBAL;
  1993. }
  1994. break;
  1995. case GLOBAL_EXPLICIT:
  1996. optype = OP_GLOBAL;
  1997. default:
  1998. break;
  1999. }
  2000. // have to do this after looking it up in the scope
  2001. mangled = fixReservedWords(mangled);
  2002. //print("mangled", mangled);
  2003. // TODO TODO TODO todo; import * at global scope failing here
  2004. goog.asserts.assert(scope || name.v.charAt(1) === "_");
  2005. // in generator or at module scope, we need to store to $loc, rather that
  2006. // to actual JS stack variables.
  2007. mangledNoPre = mangled;
  2008. if (this.u.ste.generator || this.u.ste.blockType !== FunctionBlock) {
  2009. mangled = "$loc." + mangled;
  2010. }
  2011. else if (optype === OP_FAST || optype === OP_NAME) {
  2012. this.u.localnames.push(mangled);
  2013. }
  2014. switch (optype) {
  2015. case OP_FAST:
  2016. switch (ctx) {
  2017. case Load:
  2018. case Param:
  2019. // Need to check that it is bound!
  2020. out("if (", mangled, " === undefined) { throw new Sk.builtin.UnboundLocalError('local variable \\\'", mangled, "\\\' referenced before assignment'); }\n");
  2021. return mangled;
  2022. case Store:
  2023. out(mangled, "=", dataToStore, ";");
  2024. break;
  2025. case Del:
  2026. out("delete ", mangled, ";");
  2027. break;
  2028. default:
  2029. goog.asserts.fail("unhandled");
  2030. }
  2031. break;
  2032. case OP_NAME:
  2033. switch (ctx) {
  2034. case Load:
  2035. // can't be || for loc.x = 0 or null
  2036. return this._gr("loadname", mangled, "!==undefined?", mangled, ":Sk.misceval.loadname('", mangledNoPre, "',$gbl);");
  2037. case Store:
  2038. out(mangled, "=", dataToStore, ";");
  2039. break;
  2040. case Del:
  2041. out("delete ", mangled, ";");
  2042. break;
  2043. case Param:
  2044. return mangled;
  2045. default:
  2046. goog.asserts.fail("unhandled");
  2047. }
  2048. break;
  2049. case OP_GLOBAL:
  2050. switch (ctx) {
  2051. case Load:
  2052. return this._gr("loadgbl", "Sk.misceval.loadname('", mangledNoPre, "',$gbl)");
  2053. case Store:
  2054. out("$gbl.", mangledNoPre, "=", dataToStore, ";");
  2055. break;
  2056. case Del:
  2057. out("delete $gbl.", mangledNoPre);
  2058. break;
  2059. default:
  2060. goog.asserts.fail("unhandled case in name op_global");
  2061. }
  2062. break;
  2063. case OP_DEREF:
  2064. switch (ctx) {
  2065. case Load:
  2066. return dict + "." + mangledNoPre;
  2067. case Store:
  2068. out(dict, ".", mangledNoPre, "=", dataToStore, ";");
  2069. break;
  2070. case Param:
  2071. return mangledNoPre;
  2072. default:
  2073. goog.asserts.fail("unhandled case in name op_deref");
  2074. }
  2075. break;
  2076. default:
  2077. goog.asserts.fail("unhandled case");
  2078. }
  2079. };
  2080. /**
  2081. * @param {Sk.builtin.str} name
  2082. * @param {Object} key
  2083. * @param {number} lineno
  2084. * @param {boolean=} canSuspend
  2085. */
  2086. Compiler.prototype.enterScope = function (name, key, lineno, canSuspend) {
  2087. var scopeName;
  2088. var u = new CompilerUnit();
  2089. u.ste = this.st.getStsForAst(key);
  2090. u.name = name;
  2091. u.firstlineno = lineno;
  2092. u.canSuspend = canSuspend || false;
  2093. if (this.u && this.u.private_) {
  2094. u.private_ = this.u.private_;
  2095. }
  2096. this.stack.push(this.u);
  2097. this.allUnits.push(u);
  2098. scopeName = this.gensym("scope");
  2099. u.scopename = scopeName;
  2100. this.u = u;
  2101. this.u.activateScope();
  2102. this.nestlevel++;
  2103. return scopeName;
  2104. };
  2105. Compiler.prototype.exitScope = function () {
  2106. var mangled;
  2107. var prev = this.u;
  2108. this.nestlevel--;
  2109. if (this.stack.length - 1 >= 0) {
  2110. this.u = this.stack.pop();
  2111. }
  2112. else {
  2113. this.u = null;
  2114. }
  2115. if (this.u) {
  2116. this.u.activateScope();
  2117. }
  2118. if (prev.name.v !== "<module>") {// todo; hacky
  2119. mangled = prev.name["$r"]().v;
  2120. mangled = mangled.substring(1, mangled.length - 1);
  2121. mangled = fixReservedWords(mangled);
  2122. mangled = fixReservedNames(mangled);
  2123. out(prev.scopename, ".co_name=new Sk.builtins['str']('", mangled, "');");
  2124. }
  2125. };
  2126. Compiler.prototype.cbody = function (stmts) {
  2127. var i;
  2128. for (i = 0; i < stmts.length; ++i) {
  2129. this.vstmt(stmts[i]);
  2130. }
  2131. };
  2132. Compiler.prototype.cprint = function (s) {
  2133. var i;
  2134. var n;
  2135. var dest;
  2136. goog.asserts.assert(s instanceof Print);
  2137. dest = "null";
  2138. if (s.dest) {
  2139. dest = this.vexpr(s.dest);
  2140. }
  2141. n = s.values.length;
  2142. // todo; dest disabled
  2143. for (i = 0; i < n; ++i) {
  2144. out("Sk.misceval.print_(", /*dest, ',',*/ "new Sk.builtins['str'](", this.vexpr(s.values[i]), ").v);");
  2145. }
  2146. if (s.nl) {
  2147. out("Sk.misceval.print_(", /*dest, ',*/ "\"\\n\");");
  2148. }
  2149. };
  2150. Compiler.prototype.cmod = function (mod) {
  2151. //print("-----");
  2152. //print(Sk.astDump(mod));
  2153. var modf = this.enterScope(new Sk.builtin.str("<module>"), mod, 0, this.canSuspend);
  2154. var entryBlock = this.newBlock("module entry");
  2155. this.u.prefixCode = "var " + modf + "=(function($modname){";
  2156. this.u.varDeclsCode =
  2157. "var $gbl = {}, $blk=" + entryBlock +
  2158. ",$exc=[],$loc=$gbl,$err=undefined;$gbl.__name__=$modname;$loc.__file__=new Sk.builtins.str('" + this.filename +
  2159. "');var $ret=undefined,$postfinally=undefined,$currLineNo=undefined,$currColNo=undefined;";
  2160. if (Sk.execLimit !== null) {
  2161. this.u.varDeclsCode += "if (typeof Sk.execStart === 'undefined') {Sk.execStart = Date.now()}";
  2162. }
  2163. if (Sk.yieldLimit !== null && this.u.canSuspend) {
  2164. this.u.varDeclsCode += "if (typeof Sk.lastYield === 'undefined') {Sk.lastYield = Date.now()}";
  2165. }
  2166. this.u.varDeclsCode += "if ("+modf+".$wakingSuspension!==undefined) { $wakeFromSuspension(); }" +
  2167. "if (Sk.retainGlobals) {" +
  2168. " if (Sk.globals) { $gbl = Sk.globals; Sk.globals = $gbl; $loc = $gbl; }" +
  2169. " else { Sk.globals = $gbl; }" +
  2170. "} else { Sk.globals = $gbl; }";
  2171. // Add the try block that pops the try/except stack if one exists
  2172. // Github Issue #38
  2173. // Google Code Issue: 109 / 114
  2174. // Old code:
  2175. //this.u.switchCode = "while(true){switch($blk){";
  2176. //this.u.suffixCode = "}}});";
  2177. // New Code:
  2178. this.u.switchCode = "while(true){try{";
  2179. this.u.switchCode += this.outputInterruptTest();
  2180. this.u.switchCode += "switch($blk){";
  2181. this.u.suffixCode = "}"
  2182. this.u.suffixCode += "}catch(err){ if (err instanceof Sk.builtin.TimeLimitError) { Sk.execStart = Date.now()} if (!(err instanceof Sk.builtin.BaseException)) { err = new Sk.builtin.ExternalError(err); } err.traceback.push({lineno: $currLineNo, colno: $currColNo, filename: '"+this.filename+"'}); if ($exc.length>0) { $err = err; $blk=$exc.pop(); continue; } else { throw err; }} } });";
  2183. // Note - this change may need to be adjusted for all the other instances of
  2184. // switchCode and suffixCode in this file. Not knowing how to test those
  2185. // other cases I left them alone. At least the changes to
  2186. // setupExcept and endExcept will insure that the generated JavaScript
  2187. // will be syntactically correct. The worst that will happen is that when
  2188. // code in a try block blows up, we will not know to run the except block.
  2189. // The other problem is that we might catch something that is really an internal
  2190. // error - it might be nice to add code in the above catch block that looked at
  2191. // the kind of exception and only popped the stack for exceptions that are
  2192. // from the original code rather than artifacts of some code generation or
  2193. // exeution environment error. We at least err on the side of exceptions
  2194. // being revealed to the user. drchuck - Wed Jan 23 19:20:18 EST 2013
  2195. switch (mod.constructor) {
  2196. case Module:
  2197. this.cbody(mod.body);
  2198. out("return $loc;");
  2199. break;
  2200. default:
  2201. goog.asserts.fail("todo; unhandled case in compilerMod");
  2202. }
  2203. this.exitScope();
  2204. this.result.push(this.outputAllUnits());
  2205. return modf;
  2206. };
  2207. /**
  2208. * @param {string} source the code
  2209. * @param {string} filename where it came from
  2210. * @param {string} mode one of 'exec', 'eval', or 'single'
  2211. * @param {boolean=} canSuspend if the generated code supports suspension
  2212. */
  2213. Sk.compile = function (source, filename, mode, canSuspend) {
  2214. //print("FILE:", filename);
  2215. var parse = Sk.parse(filename, source);
  2216. var ast = Sk.astFromParse(parse.cst, filename, parse.flags);
  2217. //print(JSON.stringify(ast, null, 2));
  2218. // compilers flags, later we can add other ones too
  2219. var flags = {};
  2220. flags.cf_flags = parse.flags;
  2221. var st = Sk.symboltable(ast, filename);
  2222. var c = new Compiler(filename, st, flags.cf_flags, canSuspend, source); // todo; CO_xxx
  2223. var funcname = c.cmod(ast);
  2224. var ret = "$compiledmod = function() {" + c.result.join("") + "\nreturn " + funcname + ";}();";
  2225. return {
  2226. funcname: "$compiledmod",
  2227. code : ret
  2228. };
  2229. };
  2230. goog.exportSymbol("Sk.compile", Sk.compile);
  2231. Sk.resetCompiler = function () {
  2232. Sk.gensymcount = 0;
  2233. };
  2234. goog.exportSymbol("Sk.resetCompiler", Sk.resetCompiler);
  2235. Sk.fixReservedWords = fixReservedWords;
  2236. goog.exportSymbol("Sk.fixReservedWords", Sk.fixReservedWords);
  2237. Sk.fixReservedNames = fixReservedNames;
  2238. goog.exportSymbol("Sk.fixReservedNames", Sk.fixReservedNames);
  2239. Sk.unfixReserved = unfixReserved;
  2240. goog.exportSymbol("Sk.unfixReserved", Sk.unfixReserved);