From 7d753c1a8f22f85f6279a3c016034ce8f8e740f7 Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Sat, 24 Mar 2018 12:09:30 +0100 Subject: Bug 1147371: Implement IteratorClose for for-of Issue #74 --- js/src/vm/Interpreter.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'js/src/vm/Interpreter.cpp') diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 8ae9c43b0..489d0a155 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1171,13 +1171,19 @@ ProcessTryNotes(JSContext* cx, EnvironmentIter& ei, InterpreterRegs& regs) SettleOnTryNote(cx, tn, ei, regs); return FinallyContinuation; - case JSTRY_FOR_IN: { + case JSTRY_FOR_IN: + case JSTRY_ITERCLOSE: { /* This is similar to JSOP_ENDITER in the interpreter loop. */ DebugOnly pc = regs.fp()->script()->main() + tn->start + tn->length; - MOZ_ASSERT(JSOp(*pc) == JSOP_ENDITER); + MOZ_ASSERT_IF(tn->kind == JSTRY_FOR_IN, JSOp(*pc) == JSOP_ENDITER); Value* sp = regs.spForStackDepth(tn->stackDepth); RootedObject obj(cx, &sp[-1].toObject()); - if (!UnwindIteratorForException(cx, obj)) { + bool ok; + if (tn->kind == JSTRY_FOR_IN) + ok = UnwindIteratorForException(cx, obj); + else + ok = IteratorCloseForException(cx, obj); + if (!ok) { // We should only settle on the note only if // UnwindIteratorForException itself threw, as // onExceptionUnwind should be called anew with the new @@ -5040,7 +5046,12 @@ js::ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind) { switch (kind) { case CheckIsObjectKind::IteratorNext: - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NEXT_RETURNED_PRIMITIVE); + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, "next"); + break; + case CheckIsObjectKind::IteratorReturn: + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, "return"); break; case CheckIsObjectKind::GetIterator: JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_GET_ITER_RETURNED_PRIMITIVE); -- cgit v1.2.3 From 2bb0252ab48a97a72c33cef9cbe54e86563f15c9 Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Sat, 24 Mar 2018 12:23:14 +0100 Subject: Bug 1147371: Implement IteratorClose for array destructuring Issue #74 --- js/src/vm/Interpreter.cpp | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) (limited to 'js/src/vm/Interpreter.cpp') diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 489d0a155..923c824ce 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1171,19 +1171,13 @@ ProcessTryNotes(JSContext* cx, EnvironmentIter& ei, InterpreterRegs& regs) SettleOnTryNote(cx, tn, ei, regs); return FinallyContinuation; - case JSTRY_FOR_IN: - case JSTRY_ITERCLOSE: { + case JSTRY_FOR_IN: { /* This is similar to JSOP_ENDITER in the interpreter loop. */ DebugOnly pc = regs.fp()->script()->main() + tn->start + tn->length; - MOZ_ASSERT_IF(tn->kind == JSTRY_FOR_IN, JSOp(*pc) == JSOP_ENDITER); + MOZ_ASSERT(JSOp(*pc) == JSOP_ENDITER); Value* sp = regs.spForStackDepth(tn->stackDepth); RootedObject obj(cx, &sp[-1].toObject()); - bool ok; - if (tn->kind == JSTRY_FOR_IN) - ok = UnwindIteratorForException(cx, obj); - else - ok = IteratorCloseForException(cx, obj); - if (!ok) { + if (!UnwindIteratorForException(cx, obj)) { // We should only settle on the note only if // UnwindIteratorForException itself threw, as // onExceptionUnwind should be called anew with the new @@ -1195,6 +1189,33 @@ ProcessTryNotes(JSContext* cx, EnvironmentIter& ei, InterpreterRegs& regs) break; } + case JSTRY_ITERCLOSE: { + // The iterator object is at the top of the stack. + Value* sp = regs.spForStackDepth(tn->stackDepth); + RootedObject iterObject(cx, &sp[-1].toObject()); + if (!IteratorCloseForException(cx, iterObject)) { + SettleOnTryNote(cx, tn, ei, regs); + return ErrorReturnContinuation; + } + break; + } + + case JSTRY_DESTRUCTURING_ITERCLOSE: { + // Whether the destructuring iterator is done is at the top of the + // stack. The iterator object is second from the top. + MOZ_ASSERT(tn->stackDepth > 1); + Value* sp = regs.spForStackDepth(tn->stackDepth); + MOZ_ASSERT(sp[-1].isBoolean()); + if (sp[-1].isFalse()) { + RootedObject iterObject(cx, &sp[-2].toObject()); + if (!IteratorCloseForException(cx, iterObject)) { + SettleOnTryNote(cx, tn, ei, regs); + return ErrorReturnContinuation; + } + } + break; + } + case JSTRY_FOR_OF: case JSTRY_LOOP: break; -- cgit v1.2.3 From 1ea1ed151571a523d1c8016dcd314e12238cd785 Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Sat, 24 Mar 2018 12:27:00 +0100 Subject: Bug 1147371: Implement calling IteratorClose and "return" on iterators in yield* Issue #74 --- js/src/vm/Interpreter.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'js/src/vm/Interpreter.cpp') diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 923c824ce..9a8c6777f 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1887,7 +1887,6 @@ CASE(EnableInterruptsPseudoOpcode) /* Various 1-byte no-ops. */ CASE(JSOP_NOP) CASE(JSOP_NOP_DESTRUCTURING) -CASE(JSOP_UNUSED187) CASE(JSOP_UNUSED192) CASE(JSOP_UNUSED209) CASE(JSOP_UNUSED210) @@ -2182,6 +2181,13 @@ CASE(JSOP_ENDITER) } END_CASE(JSOP_ENDITER) +CASE(JSOP_ISGENCLOSING) +{ + bool b = REGS.sp[-1].isMagic(JS_GENERATOR_CLOSING); + PUSH_BOOLEAN(b); +} +END_CASE(JSOP_ISGENCLOSING) + CASE(JSOP_DUP) { MOZ_ASSERT(REGS.stackDepth() >= 1); @@ -5074,6 +5080,10 @@ js::ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind) JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, "return"); break; + case CheckIsObjectKind::IteratorThrow: + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, "throw"); + break; case CheckIsObjectKind::GetIterator: JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_GET_ITER_RETURNED_PRIMITIVE); break; -- cgit v1.2.3 From e7a220aae2dd6f92c57b92fdd08ff94e5826c035 Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Sun, 25 Mar 2018 14:48:34 +0200 Subject: Bug 1331585 - Allow falsy "done" values for IteratorClose due to exception during array destructuring Issue #74 --- js/src/vm/Interpreter.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'js/src/vm/Interpreter.cpp') diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 9a8c6777f..7f8ff8445 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1205,8 +1205,9 @@ ProcessTryNotes(JSContext* cx, EnvironmentIter& ei, InterpreterRegs& regs) // stack. The iterator object is second from the top. MOZ_ASSERT(tn->stackDepth > 1); Value* sp = regs.spForStackDepth(tn->stackDepth); - MOZ_ASSERT(sp[-1].isBoolean()); - if (sp[-1].isFalse()) { + RootedValue doneValue(cx, sp[-1]); + bool done = ToBoolean(doneValue); + if (!done) { RootedObject iterObject(cx, &sp[-2].toObject()); if (!IteratorCloseForException(cx, iterObject)) { SettleOnTryNote(cx, tn, ei, regs); -- cgit v1.2.3 From b311e8fa718e2a32805c25847781cc8a6a0541bd Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Sun, 25 Mar 2018 16:22:38 +0200 Subject: Bug 1342553, Bug 1343072, Bug 1344753 (details in the description) Bug 1342553 - Part 0.1: Use try-catch for IteratorClose in for-of Bug 1343072 - Update HasLiveStackValueAtDepth to follow the change in JSTRY_FOR_OF Bug 1344753 - Update for-of stack depth in ControlFlowGenerator::processWhileOrForInLoop Issue #74 --- js/src/vm/Interpreter.cpp | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) (limited to 'js/src/vm/Interpreter.cpp') diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 7f8ff8445..eb3000e07 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1189,17 +1189,6 @@ ProcessTryNotes(JSContext* cx, EnvironmentIter& ei, InterpreterRegs& regs) break; } - case JSTRY_ITERCLOSE: { - // The iterator object is at the top of the stack. - Value* sp = regs.spForStackDepth(tn->stackDepth); - RootedObject iterObject(cx, &sp[-1].toObject()); - if (!IteratorCloseForException(cx, iterObject)) { - SettleOnTryNote(cx, tn, ei, regs); - return ErrorReturnContinuation; - } - break; - } - case JSTRY_DESTRUCTURING_ITERCLOSE: { // Whether the destructuring iterator is done is at the top of the // stack. The iterator object is second from the top. @@ -1892,7 +1881,6 @@ CASE(JSOP_UNUSED192) CASE(JSOP_UNUSED209) CASE(JSOP_UNUSED210) CASE(JSOP_UNUSED211) -CASE(JSOP_UNUSED219) CASE(JSOP_UNUSED220) CASE(JSOP_UNUSED221) CASE(JSOP_UNUSED222) @@ -2637,6 +2625,15 @@ CASE(JSOP_CHECKISOBJ) } END_CASE(JSOP_CHECKISOBJ) +CASE(JSOP_CHECKISCALLABLE) +{ + if (!IsCallable(REGS.sp[-1])) { + MOZ_ALWAYS_FALSE(ThrowCheckIsCallable(cx, CheckIsCallableKind(GET_UINT8(REGS.pc)))); + goto error; + } +} +END_CASE(JSOP_CHECKISCALLABLE) + CASE(JSOP_CHECKTHIS) { if (REGS.sp[-1].isMagic(JS_UNINITIALIZED_LEXICAL)) { @@ -5094,6 +5091,19 @@ js::ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind) return false; } +bool +js::ThrowCheckIsCallable(JSContext* cx, CheckIsCallableKind kind) +{ + switch (kind) { + case CheckIsCallableKind::IteratorReturn: + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_RETURN_NOT_CALLABLE); + break; + default: + MOZ_CRASH("Unknown kind"); + } + return false; +} + bool js::ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame) { -- cgit v1.2.3 From 05441d12b6bbc9dde268914fcfd374db61b83462 Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Sun, 25 Mar 2018 18:21:15 +0200 Subject: Bug 1346862 - Fix IteratorClose due to non-local jumps being catchable by try statements inside for-of Issue #74 --- js/src/vm/Interpreter.cpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'js/src/vm/Interpreter.cpp') diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index eb3000e07..d20e5284d 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1156,6 +1156,7 @@ enum HandleErrorContinuation static HandleErrorContinuation ProcessTryNotes(JSContext* cx, EnvironmentIter& ei, InterpreterRegs& regs) { + bool inForOfIterClose = false; for (TryNoteIterInterpreter tni(cx, regs); !tni.done(); ++tni) { JSTryNote* tn = *tni; @@ -1164,10 +1165,38 @@ ProcessTryNotes(JSContext* cx, EnvironmentIter& ei, InterpreterRegs& regs) /* Catch cannot intercept the closing of a generator. */ if (cx->isClosingGenerator()) break; + + // If IteratorClose due to abnormal completion threw inside a + // for-of loop, it is not catchable by try statements inside of + // the for-of loop. + // + // This is handled by this weirdness in the exception handler + // instead of in bytecode because it is hard to do so in bytecode: + // + // 1. IteratorClose emitted due to abnormal completion (break, + // throw, return) are emitted inline, at the source location of + // the break, throw, or return statement. For example: + // + // for (x of iter) { + // try { return; } catch (e) { } + // } + // + // From the try-note nesting's perspective, the IteratorClose + // resulting from |return| is covered by the inner try, when it + // should not be. + // + // 2. Try-catch notes cannot be disjoint. That is, we can't have + // multiple notes with disjoint pc ranges jumping to the same + // catch block. + if (inForOfIterClose) + break; SettleOnTryNote(cx, tn, ei, regs); return CatchContinuation; case JSTRY_FINALLY: + // See note above. + if (inForOfIterClose) + break; SettleOnTryNote(cx, tn, ei, regs); return FinallyContinuation; @@ -1206,7 +1235,14 @@ ProcessTryNotes(JSContext* cx, EnvironmentIter& ei, InterpreterRegs& regs) break; } + case JSTRY_FOR_OF_ITERCLOSE: + inForOfIterClose = true; + break; + case JSTRY_FOR_OF: + inForOfIterClose = false; + break; + case JSTRY_LOOP: break; -- cgit v1.2.3 From aafdd314442c903815f6fdf6072b001c25ae85c5 Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Sun, 25 Mar 2018 18:24:06 +0200 Subject: Bug 1357075 - Pad a nop to unwind to the scope just before a destructuring iterator close trynote Issue #74 --- js/src/vm/Interpreter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'js/src/vm/Interpreter.cpp') diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index d20e5284d..b747e4d7a 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1080,6 +1080,9 @@ js::UnwindEnvironmentToTryPc(JSScript* script, JSTryNote* tn) if (tn->kind == JSTRY_CATCH || tn->kind == JSTRY_FINALLY) { pc -= JSOP_TRY_LENGTH; MOZ_ASSERT(*pc == JSOP_TRY); + } else if (tn->kind == JSTRY_DESTRUCTURING_ITERCLOSE) { + pc -= JSOP_TRY_DESTRUCTURING_ITERCLOSE_LENGTH; + MOZ_ASSERT(*pc == JSOP_TRY_DESTRUCTURING_ITERCLOSE); } return pc; } @@ -1917,7 +1920,7 @@ CASE(JSOP_UNUSED192) CASE(JSOP_UNUSED209) CASE(JSOP_UNUSED210) CASE(JSOP_UNUSED211) -CASE(JSOP_UNUSED220) +CASE(JSOP_TRY_DESTRUCTURING_ITERCLOSE) CASE(JSOP_UNUSED221) CASE(JSOP_UNUSED222) CASE(JSOP_UNUSED223) -- cgit v1.2.3