# HG changeset patch # Parent db8ea632731159dd69fc17e6181c5e7ed0ba732e # User Tom Schuster diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -7226,16 +7226,18 @@ frontend::EmitTree(JSContext *cx, Byteco case PNK_ADD: case PNK_SUB: case PNK_BITOR: case PNK_BITXOR: case PNK_BITAND: case PNK_STRICTEQ: case PNK_EQ: case PNK_STRICTNE: + case PNK_IS: + case PNK_ISNT: case PNK_NE: case PNK_LT: case PNK_LE: case PNK_GT: case PNK_GE: case PNK_IN: case PNK_INSTANCEOF: case PNK_LSH: diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -161,16 +161,20 @@ enum ParseNodeKind { */ /* Equality operators. */ PNK_STRICTEQ, PNK_EQ, PNK_STRICTNE, PNK_NE, + /* Harmony is/isnt */ + PNK_IS, + PNK_ISNT, + /* Unary operators. */ PNK_TYPEOF, PNK_VOID, PNK_NOT, PNK_BITNOT, /* Relational operators (< <= > >=). */ PNK_LT, diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4568,26 +4568,64 @@ EqualityTokenToParseNodeKind(const Token case TOK_STRICTNE: return PNK_STRICTNE; default: JS_ASSERT(token.type == TOK_NE); return PNK_NE; } } +bool +TokenIsHarmonyEquality(const Token &token, JSContext *cx) +{ + if (token.type != TOK_NAME) + return false; + if (token.name() == cx->runtime->atomState.isAtom) + return true; + if (token.name() == cx->runtime->atomState.isntAtom) + return true; + return false; +} + BEGIN_EXPR_PARSER(eqExpr1) { + uintN line = tokenStream.getLineno(); ParseNode *left = relExpr1i(); - while (left && tokenStream.isCurrentTokenEquality()) { - ParseNodeKind kind = EqualityTokenToParseNodeKind(tokenStream.currentToken()); - JSOp op = tokenStream.currentToken().t_op; - ParseNode *right = relExpr1n(); - if (!right) - return NULL; - left = tc->parser->new_(kind, op, left, right); + while (left) { + if (TokenIsHarmonyEquality(tokenStream.currentToken(), context)) { + /* Is defined as [no LineTerminator here] */ + if (tokenStream.currentToken().pos.end.lineno != line) { + reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_NEW_LINE_BEFORE_IN); + return NULL; + } + + JSOp op; + ParseNodeKind kind; + if (tokenStream.currentToken().name() == context->runtime->atomState.isAtom) { + op = JSOP_IS; + kind = PNK_IS; + } else { + op = JSOP_ISNT; + kind = PNK_ISNT; + } + + ParseNode *right = relExpr1n(); + if (!right) + return NULL; + left = tc->parser->new_(kind, op, left, right); + } + if (tokenStream.isCurrentTokenEquality()) { + ParseNodeKind kind = EqualityTokenToParseNodeKind(tokenStream.currentToken()); + JSOp op = tokenStream.currentToken().t_op; + ParseNode *right = relExpr1n(); + if (!right) + return NULL; + left = tc->parser->new_(kind, op, left, right); + } + break; } return left; } END_EXPR_PARSER(eqExpr1) BEGIN_EXPR_PARSER(bitAndExpr1) { ParseNode *pn = eqExpr1i(); diff --git a/js/src/js.msg b/js/src/js.msg --- a/js/src/js.msg +++ b/js/src/js.msg @@ -369,8 +369,9 @@ MSG_DEF(JSMSG_DEBUG_BAD_OFFSET, 28 MSG_DEF(JSMSG_DEBUG_BAD_LINE, 283, 0, JSEXN_TYPEERR, "invalid line number") MSG_DEF(JSMSG_DEBUG_NOT_DEBUGGING, 284, 0, JSEXN_ERR, "can't set breakpoint: script global is not a debuggee") MSG_DEF(JSMSG_DEBUG_COMPARTMENT_MISMATCH, 285, 2, JSEXN_TYPEERR, "{0}: descriptor .{1} property is an object in a different compartment than the target object") MSG_DEF(JSMSG_DEBUG_NOT_SCRIPT_FRAME, 286, 0, JSEXN_ERR, "stack frame is not running JavaScript code") MSG_DEF(JSMSG_CANT_WATCH_PROP, 287, 0, JSEXN_TYPEERR, "properties whose names are objects can't be watched") MSG_DEF(JSMSG_CSP_BLOCKED_EVAL, 288, 0, JSEXN_ERR, "call to eval() blocked by CSP") MSG_DEF(JSMSG_DEBUG_NO_SCOPE_OBJECT, 289, 0, JSEXN_TYPEERR, "declarative Environments don't have binding objects") MSG_DEF(JSMSG_EMPTY_CONSEQUENT, 290, 0, JSEXN_SYNTAXERR, "mistyped ; after conditional?") +MSG_DEF(JSMSG_NEW_LINE_BEFORE_IN, 291, 0, JSEXN_SYNTAXERR, "new line before 'is' or 'isnt'") diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -209,17 +209,20 @@ const char *const js_common_atom_names[] "keys", /* keysAtom */ "iterate", /* iterateAtom */ "WeakMap", /* WeakMapAtom */ "byteLength", /* byteLengthAtom */ "return", /* returnAtom */ - "throw" /* throwAtom */ + "throw", /* throwAtom */ + + "is", /* isAtom */ + "isnt" /* isntAtom */ }; void JSAtomState::checkStaticInvariants() { /* * Start and limit offsets for atom pointers in JSAtomState must be aligned * on the word boundary. diff --git a/js/src/jsatom.h b/js/src/jsatom.h --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -358,16 +358,19 @@ struct JSAtomState js::PropertyName *WeakMapAtom; js::PropertyName *byteLengthAtom; js::PropertyName *returnAtom; js::PropertyName *throwAtom; + js::PropertyName *isAtom; + js::PropertyName *isntAtom; + /* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */ struct { js::PropertyName *XMLListAtom; js::PropertyName *decodeURIAtom; js::PropertyName *decodeURIComponentAtom; js::PropertyName *defineGetterAtom; js::PropertyName *defineSetterAtom; js::PropertyName *encodeURIAtom; diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2385,16 +2385,18 @@ END_CASE(JSOP_BITAND) BEGIN_CASE(JSOP_EQ) EQUALITY_OP(==); END_CASE(JSOP_EQ) BEGIN_CASE(JSOP_NE) EQUALITY_OP(!=); END_CASE(JSOP_NE) + + #undef EQUALITY_OP #define STRICT_EQUALITY_OP(OP, COND) \ JS_BEGIN_MACRO \ const Value &rref = regs.sp[-1]; \ const Value &lref = regs.sp[-2]; \ JSBool equal; \ if (!StrictlyEqual(cx, lref, rref, &equal)) \ @@ -2414,16 +2416,43 @@ END_CASE(JSOP_STRICTEQ) BEGIN_CASE(JSOP_STRICTNE) { bool cond; STRICT_EQUALITY_OP(!=, cond); regs.sp[-1].setBoolean(cond); } END_CASE(JSOP_STRICTNE) +#define SAME_VALUE_OP(OP, COND) \ + JS_BEGIN_MACRO \ + const Value &rref = regs.sp[-1]; \ + const Value &lref = regs.sp[-2]; \ + JSBool equal; \ + if (!SameValue(cx, lref, rref, &equal)) \ + goto error; \ + COND = equal OP JS_TRUE; \ + regs.sp--; \ + JS_END_MACRO + +BEGIN_CASE(JSOP_IS) +{ + bool cond; + SAME_VALUE_OP(==, cond); + regs.sp[-1].setBoolean(cond); +} +END_CASE(JSOP_IS) + +BEGIN_CASE(JSOP_ISNT) +{ + bool cond; + SAME_VALUE_OP(!=, cond); + regs.sp[-1].setBoolean(cond); +} +END_CASE(JSOP_ISNT) + BEGIN_CASE(JSOP_CASE) { bool cond; STRICT_EQUALITY_OP(==, cond); if (cond) { regs.sp--; len = GET_JUMP_OFFSET(regs.pc); BRANCH(len); diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -565,8 +565,11 @@ OPDEF(JSOP_LAMBDA_FC, 221,"lambda_fc */ OPDEF(JSOP_SETMETHOD, 222,"setmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) OPDEF(JSOP_INITMETHOD, 223,"initmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) OPDEF(JSOP_SHARPINIT, 224,"sharpinit", NULL, 3, 0, 0, 0, JOF_UINT16|JOF_SHARPSLOT) /* Pop the stack, convert to a jsid (int or string), and push back. */ OPDEF(JSOP_TOID, 225, "toid", NULL, 1, 1, 1, 0, JOF_BYTE) + +OPDEF(JSOP_IS, 226, "is", "is", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH|JOF_DETECTING) +OPDEF(JSOP_ISNT, 227, "isnt", "isnt", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_ARITH|JOF_DETECTING)