diff --git a/src/compile.js b/src/compile.js index 6f1206b152..be0233f1ed 100644 --- a/src/compile.js +++ b/src/compile.js @@ -236,9 +236,12 @@ Compiler.prototype.outputInterruptTest = function () { // Added by RNL return output; }; -Compiler.prototype._jumpfalse = function (test, block) { - var cond = this._gr("jfalse", "(", test, "===false||!Sk.misceval.isTrue(", test, "))"); - out("if(", cond, "){/*test failed */$blk=", block, ";continue;}"); +Compiler.prototype._jumpfalse = function (test, block, canSuspend) { + out("$ret=Sk.misceval.isTrue(", test, (canSuspend ? ",true" : "")+ ");"); + if (canSuspend) { + this._checkSuspension(null, "$ret !== true && $ret !== false"); + } + out("if(!$ret){/*test failed */$blk=", block, ";continue;}"); }; Compiler.prototype._jumpundef = function (test, block) { @@ -264,8 +267,9 @@ Compiler.prototype._jump = function (block) { /** * @param {Object=} e Object with keys 'lineno' and 'col_offset' */ -Compiler.prototype._checkSuspension = function(e) { +Compiler.prototype._checkSuspension = function(e, suspCheck) { var retblk; + suspCheck = suspCheck ? suspCheck + " && " : ""; if (this.u.canSuspend) { retblk = this.newBlock("function return or resume suspension"); @@ -274,13 +278,13 @@ Compiler.prototype._checkSuspension = function(e) { e = e || {lineno: "$currLineNo", col_offset: "$currColNo"}; - out ("if ($ret && $ret.$isSuspension) { return $saveSuspension($ret,'"+this.filename+"',"+e.lineno+","+e.col_offset+"); }"); + out ("if (" + suspCheck + "$ret !== undefined && $ret !== null && $ret.$isSuspension) { return $saveSuspension($ret,'"+this.filename+"',"+e.lineno+","+e.col_offset+"); }"); this.u.doesSuspend = true; this.u.tempsToSave = this.u.tempsToSave.concat(this.u.localtemps); } else { - out ("if ($ret && $ret.$isSuspension) { $ret = Sk.misceval.retryOptionalSuspensionOrThrow($ret); }"); + out ("if (" + suspCheck + "$ret !== undefined && $ret !== null && $ret.$isSuspension) { $ret = Sk.misceval.retryOptionalSuspensionOrThrow($ret); }"); } }; Compiler.prototype.cunpackstarstoarray = function(elts, permitEndOnly) { @@ -510,7 +514,7 @@ Compiler.prototype.ccompgen = function (type, tmpname, generators, genIndex, val n = l.ifs ? l.ifs.length : 0; for (i = 0; i < n; ++i) { ifres = this.vexpr(l.ifs[i]); - this._jumpfalse(ifres, start); + this._jumpfalse(ifres, start, true); } if (++genIndex < generators.length) { @@ -570,10 +574,12 @@ Compiler.prototype.ccompare = function (e) { for (i = 0; i < n; ++i) { rhs = this.vexpr(e.comparators[i]); - out("$ret = Sk.builtin.bool(Sk.misceval.richCompareBool(", cur, ",", rhs, ",'", e.ops[i].prototype._astname, "', true));"); + out("$ret = Sk.misceval.richCompareBool(", cur, ",", rhs, ",'", e.ops[i].prototype._astname, "', true);"); this._checkSuspension(e); - out(fres, "=$ret;"); - this._jumpfalse("$ret", done); + out(fres, "=Sk.builtin.bool($ret);"); + if (i < n - 1) { + this._jumpfalse("$ret", done, false); + } cur = rhs; } this._jump(done); @@ -1268,14 +1274,14 @@ Compiler.prototype.cif = function (s) { test = this.vexpr(s.test); if (s.orelse && s.orelse.length > 0) { - this._jumpfalse(test, next); + this._jumpfalse(test, next, true); this.vseqstmt(s.body); this._jump(end); this.setBlock(next); this.vseqstmt(s.orelse); } else { - this._jumpfalse(test, end); + this._jumpfalse(test, end, true); this.vseqstmt(s.body); } this._jump(end); @@ -1304,7 +1310,7 @@ Compiler.prototype.cwhile = function (s) { body = this.newBlock("while body"); this.annotateSource(s); - this._jumpfalse(this.vexpr(s.test), orelse ? orelse : next); + this._jumpfalse(this.vexpr(s.test), orelse ? orelse : next, true); this._jump(body); this.pushBreakBlock(next); @@ -2199,7 +2205,7 @@ Compiler.prototype.cifexp = function (e) { var ret = this._gr("res", "null"); var test = this.vexpr(e.test); - this._jumpfalse(test, next); + this._jumpfalse(test, next, true); out(ret, "=", this.vexpr(e.body), ";"); this._jump(end); diff --git a/src/misceval.js b/src/misceval.js index cb6bc65b32..d817751e15 100644 --- a/src/misceval.js +++ b/src/misceval.js @@ -615,7 +615,7 @@ Sk.exportSymbol("Sk.misceval.opAllowsEquality", Sk.misceval.opAllowsEquality); * @returns {boolean} * @param {*} x */ -Sk.misceval.isTrue = function (x) { +Sk.misceval.isTrue = function (x, canSuspend) { if (x === true || x === Sk.builtin.bool.true$) { return true; } @@ -626,11 +626,11 @@ Sk.misceval.isTrue = function (x) { return false; } if (x.nb$bool) { - return x.nb$bool(); // the slot wrapper takes care of converting to js Boolean + return x.nb$bool(canSuspend); // the slot wrapper takes care of converting to js Boolean } if (x.sq$length) { // the slot wrapper takes care of the error message and converting to js int - return x.sq$length() !== 0; + return Sk.misceval.chain(x.sq$length(canSuspend), (r) => r !== 0); } return Boolean(x); }; diff --git a/src/slotdefs.js b/src/slotdefs.js index 4a163fe8e9..9961f9f412 100644 --- a/src/slotdefs.js +++ b/src/slotdefs.js @@ -135,25 +135,28 @@ function slotFuncNoArgs(dunderFunc) { */ function slotFuncNoArgsWithCheck(dunderName, checkFunc, checkMsg, f) { return function (dunderFunc) { - return function () { + return function slotfunc(canSuspend) { const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) : dunderFunc; - let res = Sk.misceval.callsimArray(func, []); - if (!checkFunc(res)) { - throw new Sk.builtin.TypeError(dunderName + " should return " + checkMsg + " (returned " + Sk.abstr.typeName(res) + ")"); - } - // f is might be a function that changes the result to a js object like for nb$bool which returns a Boolean - if (f !== undefined) { - return f(res); - } - return res; + const ret = Sk.misceval.chain(Sk.misceval.callsimOrSuspendArray(func, []), (res) => { + if (checkFunc && !checkFunc(res)) { + throw new Sk.builtin.TypeError(dunderName + " should return " + checkMsg + " (returned " + Sk.abstr.typeName(res) + ")"); + } + // f might be a function that changes the result to a js object like for nb$bool which returns a Boolean + if (f !== undefined) { + return f(res); + } + return res; + }); + return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret); }; }; } function slotFuncOneArg(dunderFunc) { - return function (value) { + return function (value, canSuspend) { const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) : dunderFunc; - return Sk.misceval.callsimArray(func, [value]); + const ret = Sk.misceval.callsimOrSuspendArray(func, [value]); + return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret); }; } @@ -846,21 +849,7 @@ slots.__next__ = { slots.__len__ = { $name: "__len__", $slot_name: "sq$length", - $slot_func: function (dunderFunc) { - return function sq$length(canSuspend) { - let res; - const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) : dunderFunc; - if (canSuspend) { - res = Sk.misceval.callsimOrSuspendArray(func, []); - return Sk.misceval.chain(res, (r) => { - return Sk.misceval.asIndexOrThrow(r); - }); - } else { - res = Sk.misceval.callsimArray(func, []); - return Sk.misceval.asIndexOrThrow(res); - } - }; - }, + $slot_func: slotFuncNoArgsWithCheck(null, null, null, (r) => Sk.misceval.asIndexOrThrow(r)), // asIndexOrThrow does the checks and conversion $wrapper: wrapperCallBack(wrapperCallNoArgs, (res) => new Sk.builtin.int_(res)), $flags: { NoArgs: true }, $textsig: "($self, /)", @@ -886,7 +875,7 @@ slots.__contains__ = { const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) : dunderFunc; let res = Sk.misceval.callsimOrSuspendArray(func, [key]); res = Sk.misceval.chain(res, (r) => Sk.misceval.isTrue(r)); - if (res.$isSuspension) { + if (res !== true && res !== false && res.$isSuspension) { return canSuspend ? res : Sk.misceval.retryOptionalSuspensionOrThrow(res); } return res; @@ -912,13 +901,7 @@ slots.__contains__ = { slots.__getitem__ = { $name: "__getitem__", $slot_name: "mp$subscript", - $slot_func: function (dunderFunc) { - return function mp$subscript(key, canSuspend) { - const func = dunderFunc.tp$descr_get ? dunderFunc.tp$descr_get(this) : dunderFunc; - const ret = Sk.misceval.callsimOrSuspendArray(func, [key]); - return canSuspend ? ret : Sk.misceval.retryOptionalSuspensionOrThrow(ret); - }; - }, + $slot_func: slotFuncOneArg, $wrapper: wrapperCallOneArg, $textsig: "($self, key, /)", $flags: { OneArg: true },