summaryrefslogtreecommitdiffstats
path: root/js/src/vm
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm')
-rw-r--r--js/src/vm/CommonPropertyNames.h2
-rw-r--r--js/src/vm/Interpreter.cpp111
-rw-r--r--js/src/vm/Interpreter.h9
-rw-r--r--js/src/vm/Opcodes.h44
4 files changed, 154 insertions, 12 deletions
diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h
index bd0705446..e971dc844 100644
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -13,7 +13,7 @@
#define FOR_EACH_COMMON_PROPERTYNAME(macro) \
macro(add, add, "add") \
- macro(allowContentSpread, allowContentSpread, "allowContentSpread") \
+ macro(allowContentIter, allowContentIter, "allowContentIter") \
macro(anonymous, anonymous, "anonymous") \
macro(Any, Any, "Any") \
macro(apply, apply, "apply") \
diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
index 9cba1f4dc..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;
}
@@ -1156,6 +1159,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 +1168,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;
@@ -1189,7 +1221,31 @@ ProcessTryNotes(JSContext* cx, EnvironmentIter& ei, InterpreterRegs& regs)
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);
+ 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);
+ return ErrorReturnContinuation;
+ }
+ }
+ break;
+ }
+
+ case JSTRY_FOR_OF_ITERCLOSE:
+ inForOfIterClose = true;
+ break;
+
case JSTRY_FOR_OF:
+ inForOfIterClose = false;
+ break;
+
case JSTRY_LOOP:
break;
@@ -1860,14 +1916,11 @@ CASE(EnableInterruptsPseudoOpcode)
/* Various 1-byte no-ops. */
CASE(JSOP_NOP)
CASE(JSOP_NOP_DESTRUCTURING)
-CASE(JSOP_UNUSED183)
-CASE(JSOP_UNUSED187)
CASE(JSOP_UNUSED192)
CASE(JSOP_UNUSED209)
CASE(JSOP_UNUSED210)
CASE(JSOP_UNUSED211)
-CASE(JSOP_UNUSED219)
-CASE(JSOP_UNUSED220)
+CASE(JSOP_TRY_DESTRUCTURING_ITERCLOSE)
CASE(JSOP_UNUSED221)
CASE(JSOP_UNUSED222)
CASE(JSOP_UNUSED223)
@@ -2156,6 +2209,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);
@@ -2193,6 +2253,16 @@ CASE(JSOP_PICK)
}
END_CASE(JSOP_PICK)
+CASE(JSOP_UNPICK)
+{
+ int i = GET_UINT8(REGS.pc);
+ MOZ_ASSERT(REGS.stackDepth() >= unsigned(i) + 1);
+ Value lval = REGS.sp[-1];
+ memmove(REGS.sp - i, REGS.sp - (i + 1), sizeof(Value) * i);
+ REGS.sp[-(i + 1)] = lval;
+}
+END_CASE(JSOP_UNPICK)
+
CASE(JSOP_BINDGNAME)
CASE(JSOP_BINDNAME)
{
@@ -2594,6 +2664,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)) {
@@ -5031,7 +5110,16 @@ 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::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);
@@ -5043,6 +5131,19 @@ js::ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind)
}
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)
{
RootedFunction fun(cx);
diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h
index 1ffe1fdca..330dbef5f 100644
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -562,12 +562,21 @@ ReportRuntimeRedeclaration(JSContext* cx, HandlePropertyName name, const char* r
enum class CheckIsObjectKind : uint8_t {
IteratorNext,
+ IteratorReturn,
+ IteratorThrow,
GetIterator
};
bool
ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind);
+enum class CheckIsCallableKind : uint8_t {
+ IteratorReturn
+};
+
+bool
+ThrowCheckIsCallable(JSContext* cx, CheckIsCallableKind kind);
+
bool
ThrowUninitializedThis(JSContext* cx, AbstractFramePtr frame);
diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h
index f6636004d..4b044c8d8 100644
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1880,8 +1880,14 @@
* Stack: fun, name => fun
*/ \
macro(JSOP_SETFUNNAME, 182,"setfunname", NULL, 2, 2, 1, JOF_UINT8) \
- macro(JSOP_UNUSED183, 183,"unused183", NULL, 1, 0, 0, JOF_BYTE) \
- \
+ /*
+ * Moves the top of the stack value under the nth element of the stack.
+ * Category: Operators
+ * Type: Stack Operations
+ * Operands: uint8_t n
+ * Stack: v[n], v[n-1], ..., v[1], v[0] => v[0], v[n], v[n-1], ..., v[1]
+ */ \
+ macro(JSOP_UNPICK, 183,"unpick", NULL, 2, 0, 0, JOF_UINT8) \
/*
* Pops the top of stack value, pushes property of it onto the stack.
*
@@ -1910,8 +1916,16 @@
* Stack: => this
*/ \
macro(JSOP_GLOBALTHIS, 186,"globalthis", NULL, 1, 0, 1, JOF_BYTE) \
- macro(JSOP_UNUSED187, 187,"unused187", NULL, 1, 0, 0, JOF_BYTE) \
- \
+ /*
+ * Pushes a boolean indicating whether the top of the stack is
+ * MagicValue(JS_GENERATOR_CLOSING).
+ *
+ * Category: Statements
+ * Type: For-In Statement
+ * Operands:
+ * Stack: val => val, res
+ */ \
+ macro(JSOP_ISGENCLOSING, 187, "isgenclosing", NULL, 1, 1, 2, JOF_BYTE) \
/*
* Pushes unsigned 24-bit int immediate integer operand onto the stack.
* Category: Literals
@@ -2187,8 +2201,26 @@
*/ \
macro(JSOP_HOLE, 218, "hole", NULL, 1, 0, 1, JOF_BYTE) \
\
- macro(JSOP_UNUSED219, 219,"unused219", NULL, 1, 0, 0, JOF_BYTE) \
- macro(JSOP_UNUSED220, 220,"unused220", NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * Checks that the top value on the stack is callable, and throws a
+ * TypeError if not. The operand 'kind' is used only to generate an
+ * appropriate error message.
+ * Category: Statements
+ * Type: Function
+ * Operands: uint8_t kind
+ * Stack: result => result, callable
+ */ \
+ macro(JSOP_CHECKISCALLABLE, 219, "checkiscallable", NULL, 2, 1, 1, JOF_UINT8) \
+ \
+ /*
+ * No-op used by the exception unwinder to determine the correct
+ * environment to unwind to when performing IteratorClose due to
+ * destructuring.
+ * Category: Other
+ * Operands:
+ * Stack: =>
+ */ \
+ macro(JSOP_TRY_DESTRUCTURING_ITERCLOSE, 220, "try-destructuring-iterclose", NULL, 1, 0, 0, JOF_BYTE) \
macro(JSOP_UNUSED221, 221,"unused221", NULL, 1, 0, 0, JOF_BYTE) \
macro(JSOP_UNUSED222, 222,"unused222", NULL, 1, 0, 0, JOF_BYTE) \
macro(JSOP_UNUSED223, 223,"unused223", NULL, 1, 0, 0, JOF_BYTE) \