Bug 1135708 - Implement es7 exponentiation operator. (r=efaust, f=jandem)
☠☠ backed out by f02358b1e7e0 ☠ ☠
authorMariusz Kierski <mkierski@mozilla.com>
Mon, 27 Jul 2015 13:21:11 -0700
changeset 286509 e01d0f7a3c6fd95da3429a339a109a046c6e3451
parent 286508 e2e7ff226eca8a9945d26720835ee5e48a156db6
child 286510 a674c7019cb567bd4f8696d274b6fbf146363a65
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust
bugs1135708
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1135708 - Implement es7 exponentiation operator. (r=efaust, f=jandem)
js/src/builtin/ReflectParse.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/FoldConstants.cpp
js/src/frontend/NameFunctions.cpp
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/TokenKind.h
js/src/frontend/TokenStream.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
js/src/jit/BaselineIC.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/MCallOptimize.cpp
js/src/tests/ecma_7/Math/Pow.js
js/src/vm/Interpreter.cpp
js/src/vm/Opcodes.h
js/src/vm/Xdr.h
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -42,17 +42,17 @@ enum ASTType {
 };
 
 enum AssignmentOperator {
     AOP_ERR = -1,
 
     /* assign */
     AOP_ASSIGN = 0,
     /* operator-assign */
-    AOP_PLUS, AOP_MINUS, AOP_STAR, AOP_DIV, AOP_MOD,
+    AOP_PLUS, AOP_MINUS, AOP_STAR, AOP_DIV, AOP_MOD, AOP_POW,
     /* shift-assign */
     AOP_LSH, AOP_RSH, AOP_URSH,
     /* binary */
     AOP_BITOR, AOP_BITXOR, AOP_BITAND,
 
     AOP_LIMIT
 };
 
@@ -61,17 +61,17 @@ enum BinaryOperator {
 
     /* eq */
     BINOP_EQ = 0, BINOP_NE, BINOP_STRICTEQ, BINOP_STRICTNE,
     /* rel */
     BINOP_LT, BINOP_LE, BINOP_GT, BINOP_GE,
     /* shift */
     BINOP_LSH, BINOP_RSH, BINOP_URSH,
     /* arithmetic */
-    BINOP_ADD, BINOP_SUB, BINOP_STAR, BINOP_DIV, BINOP_MOD,
+    BINOP_ADD, BINOP_SUB, BINOP_STAR, BINOP_DIV, BINOP_MOD, BINOP_POW,
     /* binary */
     BINOP_BITOR, BINOP_BITXOR, BINOP_BITAND,
     /* misc */
     BINOP_IN, BINOP_INSTANCEOF,
 
     BINOP_LIMIT
 };
 
@@ -108,16 +108,17 @@ enum PropKind {
 
 static const char* const aopNames[] = {
     "=",    /* AOP_ASSIGN */
     "+=",   /* AOP_PLUS */
     "-=",   /* AOP_MINUS */
     "*=",   /* AOP_STAR */
     "/=",   /* AOP_DIV */
     "%=",   /* AOP_MOD */
+    "**=",  /* AOP_POW */
     "<<=",  /* AOP_LSH */
     ">>=",  /* AOP_RSH */
     ">>>=", /* AOP_URSH */
     "|=",   /* AOP_BITOR */
     "^=",   /* AOP_BITXOR */
     "&="    /* AOP_BITAND */
 };
 
@@ -133,16 +134,17 @@ static const char* const binopNames[] = 
     "<<",         /* BINOP_LSH */
     ">>",         /* BINOP_RSH */
     ">>>",        /* BINOP_URSH */
     "+",          /* BINOP_PLUS */
     "-",          /* BINOP_MINUS */
     "*",          /* BINOP_STAR */
     "/",          /* BINOP_DIV */
     "%",          /* BINOP_MOD */
+    "**",         /* BINOP_POW */
     "|",          /* BINOP_BITOR */
     "^",          /* BINOP_BITXOR */
     "&",          /* BINOP_BITAND */
     "in",         /* BINOP_IN */
     "instanceof", /* BINOP_INSTANCEOF */
 };
 
 static const char* const unopNames[] = {
@@ -1853,16 +1855,17 @@ class ASTSerializer
 
     BinaryOperator binop(ParseNodeKind kind, JSOp op);
     UnaryOperator unop(ParseNodeKind kind, JSOp op);
     AssignmentOperator aop(JSOp op);
 
     bool statements(ParseNode* pn, NodeVector& elts);
     bool expressions(ParseNode* pn, NodeVector& elts);
     bool leftAssociate(ParseNode* pn, MutableHandleValue dst);
+    bool rightAssociate(ParseNode* pn, MutableHandleValue dst);
     bool functionArgs(ParseNode* pn, ParseNode* pnargs, ParseNode* pnbody,
                       NodeVector& args, NodeVector& defaults, MutableHandleValue rest);
 
     bool sourceElement(ParseNode* pn, MutableHandleValue dst);
 
     bool declaration(ParseNode* pn, MutableHandleValue dst);
     bool variableDeclaration(ParseNode* pn, bool lexical, MutableHandleValue dst);
     bool variableDeclarator(ParseNode* pn, MutableHandleValue dst);
@@ -1969,16 +1972,18 @@ ASTSerializer::aop(JSOp op)
       case JSOP_SUB:
         return AOP_MINUS;
       case JSOP_MUL:
         return AOP_STAR;
       case JSOP_DIV:
         return AOP_DIV;
       case JSOP_MOD:
         return AOP_MOD;
+      case JSOP_POW:
+        return AOP_POW;
       case JSOP_LSH:
         return AOP_LSH;
       case JSOP_RSH:
         return AOP_RSH;
       case JSOP_URSH:
         return AOP_URSH;
       case JSOP_BITOR:
         return AOP_BITOR;
@@ -2047,16 +2052,18 @@ ASTSerializer::binop(ParseNodeKind kind,
       case PNK_SUB:
         return BINOP_SUB;
       case PNK_STAR:
         return BINOP_STAR;
       case PNK_DIV:
         return BINOP_DIV;
       case PNK_MOD:
         return BINOP_MOD;
+      case PNK_POW:
+        return BINOP_POW;
       case PNK_BITOR:
         return BINOP_BITOR;
       case PNK_BITXOR:
         return BINOP_BITXOR;
       case PNK_BITAND:
         return BINOP_BITAND;
       case PNK_IN:
         return BINOP_IN;
@@ -2782,16 +2789,59 @@ ASTSerializer::leftAssociate(ParseNode* 
         }
     }
 
     dst.set(left);
     return true;
 }
 
 bool
+ASTSerializer::rightAssociate(ParseNode* pn, MutableHandleValue dst)
+{
+    MOZ_ASSERT(pn->isArity(PN_LIST));
+    MOZ_ASSERT(pn->pn_count >= 1);
+
+    // First, we need to reverse the list, so that we can traverse it in the right order.
+    // It's OK to destructively reverse the list, because there are no other consumers.
+
+    ParseNode* head = pn->pn_head;
+    ParseNode* prev = nullptr;
+    ParseNode* current = head;
+    ParseNode* next;
+    while (current != nullptr) {
+        next = current->pn_next;
+        current->pn_next = prev;
+        prev = current;
+        current = next;
+    }
+
+    head = prev;
+
+    RootedValue right(cx);
+    if (!expression(head, &right))
+        return false;
+    for (ParseNode* next = head->pn_next; next; next = next->pn_next) {
+        RootedValue left(cx);
+        if (!expression(next, &left))
+            return false;
+
+        TokenPos subpos(pn->pn_pos.begin, next->pn_pos.end);
+
+        BinaryOperator op = binop(pn->getKind(), pn->getOp());
+        LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT);
+
+        if (!builder.binaryExpression(op, left, right, &subpos, &right))
+            return false;
+    }
+
+    dst.set(right);
+    return true;
+}
+
+bool
 ASTSerializer::comprehensionBlock(ParseNode* pn, MutableHandleValue dst)
 {
     LOCAL_ASSERT(pn->isArity(PN_BINARY));
 
     ParseNode* in = pn->pn_left;
 
     LOCAL_ASSERT(in && (in->isKind(PNK_FORIN) || in->isKind(PNK_FOROF)));
 
@@ -2969,16 +3019,17 @@ ASTSerializer::expression(ParseNode* pn,
       case PNK_BITXORASSIGN:
       case PNK_BITANDASSIGN:
       case PNK_LSHASSIGN:
       case PNK_RSHASSIGN:
       case PNK_URSHASSIGN:
       case PNK_MULASSIGN:
       case PNK_DIVASSIGN:
       case PNK_MODASSIGN:
+      case PNK_POWASSIGN:
       {
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
 
         AssignmentOperator op = aop(pn->getOp());
         LOCAL_ASSERT(op > AOP_ERR && op < AOP_LIMIT);
 
         RootedValue lhs(cx), rhs(cx);
@@ -3005,16 +3056,19 @@ ASTSerializer::expression(ParseNode* pn,
       case PNK_MOD:
       case PNK_BITOR:
       case PNK_BITXOR:
       case PNK_BITAND:
       case PNK_IN:
       case PNK_INSTANCEOF:
         return leftAssociate(pn, dst);
 
+      case PNK_POW:
+	return rightAssociate(pn, dst);
+
       case PNK_DELETENAME:
       case PNK_DELETEPROP:
       case PNK_DELETESUPERPROP:
       case PNK_DELETEELEM:
       case PNK_DELETESUPERELEM:
       case PNK_DELETEEXPR:
       case PNK_TYPEOFNAME:
       case PNK_TYPEOFEXPR:
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2034,16 +2034,17 @@ BytecodeEmitter::checkSideEffects(ParseN
       case PNK_BITXORASSIGN:
       case PNK_BITANDASSIGN:
       case PNK_LSHASSIGN:
       case PNK_RSHASSIGN:
       case PNK_URSHASSIGN:
       case PNK_MULASSIGN:
       case PNK_DIVASSIGN:
       case PNK_MODASSIGN:
+      case PNK_POWASSIGN:
         MOZ_ASSERT(pn->isArity(PN_BINARY));
         *answer = true;
         return true;
 
       case PNK_STATEMENTLIST:
       case PNK_CATCHLIST:
       // Strict equality operations and logical operators are well-behaved and
       // perform no conversions.
@@ -2086,16 +2087,17 @@ BytecodeEmitter::checkSideEffects(ParseN
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_ADD:
       case PNK_SUB:
       case PNK_STAR:
       case PNK_DIV:
       case PNK_MOD:
+      case PNK_POW:
         MOZ_ASSERT(pn->isArity(PN_LIST));
         MOZ_ASSERT(pn->pn_count >= 2);
         *answer = true;
         return true;
 
       case PNK_DEFAULT:
       case PNK_COLON:
       case PNK_CASE:
@@ -7725,16 +7727,17 @@ BytecodeEmitter::emitTree(ParseNode* pn)
       case PNK_BITXORASSIGN:
       case PNK_BITANDASSIGN:
       case PNK_LSHASSIGN:
       case PNK_RSHASSIGN:
       case PNK_URSHASSIGN:
       case PNK_MULASSIGN:
       case PNK_DIVASSIGN:
       case PNK_MODASSIGN:
+      case PNK_POWASSIGN:
         if (!emitAssignment(pn->pn_left, pn->getOp(), pn->pn_right))
             return false;
         break;
 
       case PNK_CONDITIONAL:
         ok = emitConditionalExpression(pn->as<ConditionalExpression>());
         break;
 
@@ -7774,16 +7777,30 @@ BytecodeEmitter::emitTree(ParseNode* pn)
             if (!emitTree(subexpr))
                 return false;
             if (!emit1(op))
                 return false;
         }
         break;
       }
 
+      case PNK_POW: {
+        MOZ_ASSERT(pn->isArity(PN_LIST));
+        /* Right-associative operator chain. */
+        for (ParseNode* subexpr = pn->pn_head; subexpr; subexpr = subexpr->pn_next) {
+            if (!emitTree(subexpr))
+                return false;
+        }
+        for (int i = 0; i < pn->pn_count - 1; i++) {
+            if (!emit1(JSOP_POW))
+                return false;
+        }
+        break;
+      }
+          
       case PNK_TYPEOFNAME:
         ok = emitTypeof(pn, JSOP_TYPEOF);
         break;
 
       case PNK_TYPEOFEXPR:
         ok = emitTypeof(pn, JSOP_TYPEOFEXPR);
         break;
 
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -358,28 +358,30 @@ ContainsHoistedDeclaration(ExclusiveCont
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_ADD:
       case PNK_SUB:
       case PNK_STAR:
       case PNK_DIV:
       case PNK_MOD:
+      case PNK_POW:
       case PNK_ASSIGN:
       case PNK_ADDASSIGN:
       case PNK_SUBASSIGN:
       case PNK_BITORASSIGN:
       case PNK_BITXORASSIGN:
       case PNK_BITANDASSIGN:
       case PNK_LSHASSIGN:
       case PNK_RSHASSIGN:
       case PNK_URSHASSIGN:
       case PNK_MULASSIGN:
       case PNK_DIVASSIGN:
       case PNK_MODASSIGN:
+      case PNK_POWASSIGN:
       case PNK_COMMA:
       case PNK_ARRAY:
       case PNK_OBJECT:
       case PNK_DOT:
       case PNK_ELEM:
       case PNK_CALL:
       case PNK_NAME:
       case PNK_TEMPLATE_STRING:
@@ -525,16 +527,20 @@ FoldBinaryNumeric(ExclusiveContext* cx, 
       case JSOP_MOD:
         if (d2 == 0) {
             d = GenericNaN();
         } else {
             d = js_fmod(d, d2);
         }
         break;
 
+      case JSOP_POW:
+        d = ecmaPow(d, d2);
+        break;
+
       default:;
     }
 
     /* Take care to allow pn1 or pn2 to alias pn. */
     pn->setKind(PNK_NUMBER);
     pn->setOp(JSOP_DOUBLE);
     pn->setArity(PN_NULLARY);
     pn->pn_dval = d;
@@ -1393,27 +1399,34 @@ Fold(ExclusiveContext* cx, ParseNode** p
 
       case PNK_SUB:
       case PNK_STAR:
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_DIV:
       case PNK_MOD:
+      case PNK_POW:
         MOZ_ASSERT(pn->getArity() == PN_LIST);
         MOZ_ASSERT(pn->pn_count >= 2);
         for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
             if (!FoldType(cx, pn2, PNK_NUMBER))
                 return false;
         }
         for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
             /* XXX fold only if all operands convert to number */
             if (!pn2->isKind(PNK_NUMBER))
                 break;
         }
+
+        // No constant-folding for (2**3**5), because (**) is right-
+        // associative. We would have to reverse the list. It's not worth it.
+        if (pn->getKind() == PNK_POW && pn->pn_count > 2)
+            break;
+
         if (!pn2) {
             JSOp op = pn->getOp();
 
             pn2 = pn1->pn_next;
             pn3 = pn2->pn_next;
             if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn))
                 return false;
             while ((pn2 = pn3) != nullptr) {
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -434,16 +434,17 @@ class NameResolver
           case PNK_BITXORASSIGN:
           case PNK_BITANDASSIGN:
           case PNK_LSHASSIGN:
           case PNK_RSHASSIGN:
           case PNK_URSHASSIGN:
           case PNK_MULASSIGN:
           case PNK_DIVASSIGN:
           case PNK_MODASSIGN:
+          case PNK_POWASSIGN:
           case PNK_ELEM:
           case PNK_COLON:
           case PNK_CASE:
           case PNK_SHORTHAND:
           case PNK_DOWHILE:
           case PNK_WHILE:
           case PNK_SWITCH:
           case PNK_LETBLOCK:
@@ -654,16 +655,17 @@ class NameResolver
           case PNK_LSH:
           case PNK_RSH:
           case PNK_URSH:
           case PNK_ADD:
           case PNK_SUB:
           case PNK_STAR:
           case PNK_DIV:
           case PNK_MOD:
+          case PNK_POW:
           case PNK_COMMA:
           case PNK_NEW:
           case PNK_CALL:
           case PNK_GENEXP:
           case PNK_ARRAY:
           case PNK_STATEMENTLIST:
           case PNK_ARGSBODY:
           // Initializers for individual variables, and computed property names
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -267,16 +267,17 @@ PushNodeChildren(ParseNode* pn, NodeStac
       case PNK_BITXORASSIGN:
       case PNK_BITANDASSIGN:
       case PNK_LSHASSIGN:
       case PNK_RSHASSIGN:
       case PNK_URSHASSIGN:
       case PNK_MULASSIGN:
       case PNK_DIVASSIGN:
       case PNK_MODASSIGN:
+      case PNK_POWASSIGN:
       // ...and a few others.
       case PNK_ELEM:
       case PNK_IMPORT_SPEC:
       case PNK_EXPORT_SPEC:
       case PNK_COLON:
       case PNK_CASE:
       case PNK_SHORTHAND:
       case PNK_DOWHILE:
@@ -473,16 +474,17 @@ PushNodeChildren(ParseNode* pn, NodeStac
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_ADD:
       case PNK_SUB:
       case PNK_STAR:
       case PNK_DIV:
       case PNK_MOD:
+      case PNK_POW:
       case PNK_COMMA:
       case PNK_NEW:
       case PNK_CALL:
       case PNK_GENEXP:
       case PNK_ARRAY:
       case PNK_OBJECT:
       case PNK_TEMPLATE_STRING_LIST:
       case PNK_TAGGED_TEMPLATE:
@@ -606,17 +608,28 @@ ParseNode::appendOrCreateList(ParseNodeK
     // binary tree of lists exactly as ECMAScript would by skipping the
     // following optimization.
     if (!pc->useAsmOrInsideUseAsm()) {
         // Left-associative trees of a given operator (e.g. |a + b + c|) are
         // binary trees in the spec: (+ (+ a b) c) in Lisp terms.  Recursively
         // processing such a tree, exactly implemented that way, would blow the
         // the stack.  We use a list node that uses O(1) stack to represent
         // such operations: (+ a b c).
-        if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
+        //
+        // (**) is right-associative; per spec |a ** b ** c| parses as
+        // (** a (** b c)). But we treat this the same way, creating a list
+        // node: (** a b c). All consumers must understand that this must be
+        // processed with a right fold, whereas the list (+ a b c) must be
+        // processed with a left fold because (+) is left-associative.
+        //
+        if (left->isKind(kind) &&
+            left->isOp(op) &&
+            (js_CodeSpec[op].format & JOF_LEFTASSOC ||
+             (kind == PNK_POW && !left->pn_parens)))
+        {
             ListNode* list = &left->as<ListNode>();
 
             list->append(right);
             list->pn_pos.end = right->pn_pos.end;
 
             return list;
         }
     }
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -192,31 +192,33 @@ class UpvarCookie
     F(LSH) \
     F(RSH) \
     F(URSH) \
     F(ADD) \
     F(SUB) \
     F(STAR) \
     F(DIV) \
     F(MOD) \
+    F(POW) \
     \
     /* Assignment operators (= += -= etc.). */ \
     /* ParseNode::isAssignment assumes all these are consecutive. */ \
     F(ASSIGN) \
     F(ADDASSIGN) \
     F(SUBASSIGN) \
     F(BITORASSIGN) \
     F(BITXORASSIGN) \
     F(BITANDASSIGN) \
     F(LSHASSIGN) \
     F(RSHASSIGN) \
     F(URSHASSIGN) \
     F(MULASSIGN) \
     F(DIVASSIGN) \
-    F(MODASSIGN)
+    F(MODASSIGN) \
+    F(POWASSIGN)
 
 /*
  * Parsing builds a tree of nodes that directs code generation.  This tree is
  * not a concrete syntax tree in all respects (for example, || and && are left
  * associative, but (A && B && C) translates into the right-associated tree
  * <A && <B && C>> so that code generation can emit a left-associative branch
  * around <B && C> when A is false).  Nodes are labeled by kind, with a
  * secondary JSOp label when needed.
@@ -225,19 +227,19 @@ class UpvarCookie
  */
 enum ParseNodeKind
 {
 #define EMIT_ENUM(name) PNK_##name,
     FOR_EACH_PARSE_NODE_KIND(EMIT_ENUM)
 #undef EMIT_ENUM
     PNK_LIMIT, /* domain size */
     PNK_BINOP_FIRST = PNK_OR,
-    PNK_BINOP_LAST = PNK_MOD,
+    PNK_BINOP_LAST = PNK_POW,
     PNK_ASSIGNMENT_START = PNK_ASSIGN,
-    PNK_ASSIGNMENT_LAST = PNK_MODASSIGN
+    PNK_ASSIGNMENT_LAST = PNK_POWASSIGN
 };
 
 inline bool
 IsDeleteKind(ParseNodeKind kind)
 {
     return PNK_DELETENAME <= kind && kind <= PNK_DELETEEXPR;
 }
 
@@ -364,41 +366,44 @@ IsDeleteKind(ParseNodeKind kind)
  * PNK_BITORASSIGN,
  * PNK_BITXORASSIGN,
  * PNK_BITANDASSIGN,
  * PNK_LSHASSIGN,
  * PNK_RSHASSIGN,
  * PNK_URSHASSIGN,
  * PNK_MULASSIGN,
  * PNK_DIVASSIGN,
- * PNK_MODASSIGN
+ * PNK_MODASSIGN,
+ * PNK_POWASSIGN
  * PNK_CONDITIONAL ternary  (cond ? trueExpr : falseExpr)
  *                          pn_kid1: cond, pn_kid2: then, pn_kid3: else
- * PNK_OR       binary      pn_left: first in || chain, pn_right: rest of chain
- * PNK_AND      binary      pn_left: first in && chain, pn_right: rest of chain
- * PNK_BITOR    binary      pn_left: left-assoc | expr, pn_right: ^ expr
- * PNK_BITXOR   binary      pn_left: left-assoc ^ expr, pn_right: & expr
- * PNK_BITAND   binary      pn_left: left-assoc & expr, pn_right: EQ expr
- *
- * PNK_EQ,      binary      pn_left: left-assoc EQ expr, pn_right: REL expr
+ * PNK_OR,      list        pn_head; list of pn_count subexpressions
+ * PNK_AND,                 All of these operators are left-associative except (**).
+ * PNK_BITOR,
+ * PNK_BITXOR,
+ * PNK_BITAND,
+ * PNK_EQ,
  * PNK_NE,
  * PNK_STRICTEQ,
- * PNK_STRICTNE
- * PNK_LT,      binary      pn_left: left-assoc REL expr, pn_right: SH expr
+ * PNK_STRICTNE,
+ * PNK_LT,
  * PNK_LE,
  * PNK_GT,
- * PNK_GE
- * PNK_LSH,     binary      pn_left: left-assoc SH expr, pn_right: ADD expr
+ * PNK_GE,
+ * PNK_LSH,
  * PNK_RSH,
- * PNK_URSH
- * PNK_ADD,     binary      pn_left: left-assoc ADD expr, pn_right: MUL expr
- * PNK_SUB
- * PNK_STAR,    binary      pn_left: left-assoc MUL expr, pn_right: UNARY expr
- * PNK_DIV,                 pn_op: JSOP_MUL, JSOP_DIV, JSOP_MOD
- * PNK_MOD
+ * PNK_URSH,
+ * PNK_ADD,
+ * PNK_SUB,
+ * PNK_STAR,
+ * PNK_DIV,
+ * PNK_MOD,
+ * PNK_POW                  (**) is right-associative, but forms a list
+ *                          nonetheless. Special hacks everywhere.
+ *
  * PNK_POS,     unary       pn_kid: UNARY expr
  * PNK_NEG
  * PNK_VOID,    unary       pn_kid: UNARY expr
  * PNK_NOT,
  * PNK_BITNOT
  * PNK_TYPEOFNAME, unary    pn_kid: UNARY expr
  * PNK_TYPEOFEXPR
  * PNK_PREINCREMENT, unary  pn_kid: MEMBER expr
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -6346,17 +6346,18 @@ static const JSOp ParseNodeKindToJSOp[] 
     JSOP_IN,
     JSOP_LSH,
     JSOP_RSH,
     JSOP_URSH,
     JSOP_ADD,
     JSOP_SUB,
     JSOP_MUL,
     JSOP_DIV,
-    JSOP_MOD
+    JSOP_MOD,
+    JSOP_POW
 };
 
 static inline JSOp
 BinaryOpParseNodeKindToJSOp(ParseNodeKind pnk)
 {
     MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
     MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
     return ParseNodeKindToJSOp[pnk - PNK_BINOP_FIRST];
@@ -6387,20 +6388,21 @@ static const int PrecedenceTable[] = {
     7, /* PNK_IN */
     8, /* PNK_LSH */
     8, /* PNK_RSH */
     8, /* PNK_URSH */
     9, /* PNK_ADD */
     9, /* PNK_SUB */
     10, /* PNK_STAR */
     10, /* PNK_DIV */
-    10  /* PNK_MOD */
+    10, /* PNK_MOD */
+    11  /* PNK_POW */
 };
 
-static const int PRECEDENCE_CLASSES = 10;
+static const int PRECEDENCE_CLASSES = 11;
 
 static int
 Precedence(ParseNodeKind pnk) {
     // Everything binds tighter than PNK_LIMIT, because we want to reduce all
     // nodes to a single node when we reach a token that is not another binary
     // operator.
     if (pnk == PNK_LIMIT)
         return 0;
@@ -6410,18 +6412,18 @@ Precedence(ParseNodeKind pnk) {
     return PrecedenceTable[pnk - PNK_BINOP_FIRST];
 }
 
 template <typename ParseHandler>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
 Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling,
                               InvokedPrediction invoked)
 {
-    // Shift-reduce parser for the left-associative binary operator part of
-    // the JS syntax.
+    // Shift-reduce parser for the binary operator part of the JS expression
+    // syntax.
 
     // Conceptually there's just one stack, a stack of pairs (lhs, op).
     // It's implemented using two separate arrays, though.
     Node nodeStack[PRECEDENCE_CLASSES];
     ParseNodeKind kindStack[PRECEDENCE_CLASSES];
     int depth = 0;
 
     Node pn;
@@ -6443,20 +6445,19 @@ Parser<ParseHandler>::orExpr1(InHandling
             tok = TOK_EOF;
             pnk = PNK_LIMIT;
         }
 
         // If pnk has precedence less than or equal to another operator on the
         // stack, reduce. This combines nodes on the stack until we form the
         // actual lhs of pnk.
         //
-        // The >= in this condition works because all the operators in question
-        // are left-associative; if any were not, the case where two operators
-        // have equal precedence would need to be handled specially, and the
-        // stack would need to be a Vector.
+        // The >= in this condition works because it is appendOrCreateList's
+        // job to decide if the operator in question is left- or
+        // right-associative, and build the corresponding tree.
         while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) {
             depth--;
             ParseNodeKind combiningPnk = kindStack[depth];
             JSOp combiningOp = BinaryOpParseNodeKindToJSOp(combiningPnk);
             pn = handler.appendOrCreateList(combiningPnk, nodeStack[depth], pn, pc, combiningOp);
             if (!pn)
                 return pn;
         }
@@ -6608,16 +6609,17 @@ Parser<ParseHandler>::assignExpr(InHandl
       case TOK_BITXORASSIGN: kind = PNK_BITXORASSIGN; op = JSOP_BITXOR; break;
       case TOK_BITANDASSIGN: kind = PNK_BITANDASSIGN; op = JSOP_BITAND; break;
       case TOK_LSHASSIGN:    kind = PNK_LSHASSIGN;    op = JSOP_LSH;    break;
       case TOK_RSHASSIGN:    kind = PNK_RSHASSIGN;    op = JSOP_RSH;    break;
       case TOK_URSHASSIGN:   kind = PNK_URSHASSIGN;   op = JSOP_URSH;   break;
       case TOK_MULASSIGN:    kind = PNK_MULASSIGN;    op = JSOP_MUL;    break;
       case TOK_DIVASSIGN:    kind = PNK_DIVASSIGN;    op = JSOP_DIV;    break;
       case TOK_MODASSIGN:    kind = PNK_MODASSIGN;    op = JSOP_MOD;    break;
+      case TOK_POWASSIGN:    kind = PNK_POWASSIGN;    op = JSOP_POW;    break;
 
       case TOK_ARROW: {
         // A line terminator between ArrowParameters and the => should trigger a SyntaxError.
         tokenStream.ungetToken();
         TokenKind next;
         if (!tokenStream.peekTokenSameLine(&next) || next != TOK_ARROW) {
             report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                    "expression", TokenKindToDesc(TOK_ARROW));
--- a/js/src/frontend/TokenKind.h
+++ b/js/src/frontend/TokenKind.h
@@ -117,17 +117,17 @@
     /* reserved keywords in strict mode */ \
     macro(STRICT_RESERVED, "reserved keyword") \
     \
     /* \
      * The following token types occupy contiguous ranges to enable easy \
      * range-testing. \
      */ \
     /* \
-     * Binary operators tokens, TOK_OR thru TOK_MOD. These must be in the same \
+     * Binary operators tokens, TOK_OR thru TOK_POW. These must be in the same \
      * order as F(OR) and friends in FOR_EACH_PARSE_NODE_KIND in ParseNode.h. \
      */ \
     macro(OR,           "'||'")   /* logical or */ \
     range(BINOP_FIRST, OR) \
     macro(AND,          "'&&'")   /* logical and */ \
     macro(BITOR,        "'|'")    /* bitwise-or */ \
     macro(BITXOR,       "'^'")    /* bitwise-xor */ \
     macro(BITAND,       "'&'")    /* bitwise-and */ \
@@ -158,17 +158,18 @@
     macro(URSH,         "'>>>'") \
     range(SHIFTOP_LAST, URSH) \
     \
     macro(ADD,          "'+'") \
     macro(SUB,          "'-'") \
     macro(MUL,          "'*'") \
     macro(DIV,          "'/'") \
     macro(MOD,          "'%'") \
-    range(BINOP_LAST, MOD) \
+    macro(POW,          "'**'") \
+    range(BINOP_LAST, POW) \
     \
     /* Unary operation tokens. */ \
     macro(TYPEOF,       "keyword 'typeof'") \
     macro(VOID,         "keyword 'void'") \
     macro(NOT,          "'!'") \
     macro(BITNOT,       "'~'") \
     \
     macro(ARROW,        "'=>'")   /* function arrow */ \
@@ -182,17 +183,18 @@
     macro(BITXORASSIGN, "'^='") \
     macro(BITANDASSIGN, "'&='") \
     macro(LSHASSIGN,    "'<<='") \
     macro(RSHASSIGN,    "'>>='") \
     macro(URSHASSIGN,   "'>>>='") \
     macro(MULASSIGN,    "'*='") \
     macro(DIVASSIGN,    "'/='") \
     macro(MODASSIGN,    "'%='") \
-    range(ASSIGNMENT_LAST, MODASSIGN)
+    macro(POWASSIGN,    "'**='") \
+    range(ASSIGNMENT_LAST, POWASSIGN)
 
 #define TOKEN_KIND_RANGE_EMIT_NONE(name, value)
 #define FOR_EACH_TOKEN_KIND(macro) \
     FOR_EACH_TOKEN_KIND_WITH_RANGE(macro, TOKEN_KIND_RANGE_EMIT_NONE)
 
 namespace js {
 namespace frontend {
 
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -1504,17 +1504,20 @@ TokenStream::getTokenInternal(TokenKind*
             else
                 tp->type = matchChar('=') ? TOK_RSHASSIGN : TOK_RSH;
         } else {
             tp->type = matchChar('=') ? TOK_GE : TOK_GT;
         }
         goto out;
 
       case '*':
-        tp->type = matchChar('=') ? TOK_MULASSIGN : TOK_MUL;
+        if (matchChar('*'))
+            tp->type = matchChar('=') ? TOK_POWASSIGN : TOK_POW;
+        else
+            tp->type = matchChar('=') ? TOK_MULASSIGN : TOK_MUL;
         goto out;
 
       case '/':
         // Look for a single-line comment.
         if (matchChar('/')) {
             c = peekChar();
             if (c == '@' || c == '#') {
                 bool shouldWarn = getChar() == '@';
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1594,16 +1594,22 @@ BaselineCompiler::emit_JSOP_DIV()
 
 bool
 BaselineCompiler::emit_JSOP_MOD()
 {
     return emitBinaryArith();
 }
 
 bool
+BaselineCompiler::emit_JSOP_POW()
+{
+    return emitBinaryArith();
+}
+
+bool
 BaselineCompiler::emitBinaryArith()
 {
     // Keep top JSStack value in R0 and R2
     frame.popRegsAndSync(2);
 
     // Call IC
     ICBinaryArith_Fallback::Compiler stubCompiler(cx);
     if (!emitOpIC(stubCompiler.getStub(&stubSpace_)))
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -75,16 +75,17 @@ namespace jit {
     _(JSOP_LSH)                \
     _(JSOP_RSH)                \
     _(JSOP_URSH)               \
     _(JSOP_ADD)                \
     _(JSOP_SUB)                \
     _(JSOP_MUL)                \
     _(JSOP_DIV)                \
     _(JSOP_MOD)                \
+    _(JSOP_POW)                \
     _(JSOP_LT)                 \
     _(JSOP_LE)                 \
     _(JSOP_GT)                 \
     _(JSOP_GE)                 \
     _(JSOP_EQ)                 \
     _(JSOP_NE)                 \
     _(JSOP_STRICTEQ)           \
     _(JSOP_STRICTNE)           \
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1900,16 +1900,20 @@ DoBinaryArithFallback(JSContext* cx, Bas
       case JSOP_DIV:
         if (!DivValues(cx, &lhsCopy, &rhsCopy, ret))
             return false;
         break;
       case JSOP_MOD:
         if (!ModValues(cx, &lhsCopy, &rhsCopy, ret))
             return false;
         break;
+      case JSOP_POW:
+        if (!math_pow_handle(cx, lhsCopy, rhsCopy, ret))
+            return false;
+        break;
       case JSOP_BITOR: {
         int32_t result;
         if (!BitOr(cx, lhs, rhs, &result))
             return false;
         ret.setInt32(result);
         break;
       }
       case JSOP_BITXOR: {
@@ -2033,17 +2037,17 @@ DoBinaryArithFallback(JSContext* cx, Bas
             stub->addNewStub(doubleStub);
             return true;
           }
           default:
             break;
         }
     }
 
-    if (lhs.isInt32() && rhs.isInt32()) {
+    if (lhs.isInt32() && rhs.isInt32() && op != JSOP_POW) {
         bool allowDouble = ret.isDouble();
         if (allowDouble)
             stub->unlinkStubsWithKind(cx, ICStub::BinaryArith_Int32);
         JitSpew(JitSpew_BaselineIC, "  Generating %s(Int32, Int32%s) stub", js_CodeName[op],
                 allowDouble ? " => Double" : "");
         ICBinaryArith_Int32::Compiler compilerInt32(cx, op, allowDouble);
         ICStub* int32Stub = compilerInt32.getStub(compilerInt32.getStubSpace(script));
         if (!int32Stub)
@@ -10019,17 +10023,17 @@ ICCallStubCompiler::pushSpreadCallArgume
                                             Register argcReg, bool isJitCall,
                                             bool isConstructing)
 {
     // Pull the array off the stack before aligning.
     Register startReg = regs.takeAny();
     masm.unboxObject(Address(masm.getStackPointer(),
                              (isConstructing * sizeof(Value)) + STUB_FRAME_SIZE), startReg);
     masm.loadPtr(Address(startReg, NativeObject::offsetOfElements()), startReg);
-    
+
     // Align the stack such that the JitFrameLayout is aligned on the
     // JitStackAlignment.
     if (isJitCall) {
         Register alignReg = argcReg;
         if (isConstructing) {
             alignReg = regs.takeAny();
             masm.movePtr(argcReg, alignReg);
             masm.addPtr(Imm32(1), alignReg);
@@ -10269,28 +10273,28 @@ ICCall_Fallback::Compiler::generateStubC
 
     if (MOZ_UNLIKELY(isSpread_)) {
         // Push a stub frame so that we can perform a non-tail call.
         enterStubFrame(masm, R1.scratchReg());
 
         // Use BaselineFrameReg instead of BaselineStackReg, because
         // BaselineFrameReg and BaselineStackReg hold the same value just after
         // calling enterStubFrame.
-        
+
         // newTarget
         if (isConstructing_)
             masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE));
-        
+
         // array
         uint32_t valueOffset = isConstructing_;
         masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
-        
+
         // this
         masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
-        
+
         // callee
         masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE));
 
         masm.push(masm.getStackPointer());
         masm.push(ICStubReg);
 
         pushFramePtr(masm, R0.scratchReg());
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1652,16 +1652,19 @@ IonBuilder::inspectOpcode(JSOp op)
 
       case JSOP_ADD:
       case JSOP_SUB:
       case JSOP_MUL:
       case JSOP_DIV:
       case JSOP_MOD:
         return jsop_binary(op);
 
+      case JSOP_POW:
+        return jsop_pow();
+
       case JSOP_POS:
         return jsop_pos();
 
       case JSOP_NEG:
         return jsop_neg();
 
       case JSOP_AND:
       case JSOP_OR:
@@ -4593,16 +4596,35 @@ IonBuilder::jsop_binary(JSOp op, MDefini
     current->push(ins);
 
     if (ins->isEffectful())
         return resumeAfter(ins);
     return maybeInsertResume();
 }
 
 bool
+IonBuilder::jsop_pow()
+{
+    MDefinition* exponent = current->pop();
+    MDefinition* base = current->pop();
+
+    if (inlineMathPowHelper(base, exponent, MIRType_Double) == InliningStatus_Inlined) {
+        base->setImplicitlyUsedUnchecked();
+        exponent->setImplicitlyUsedUnchecked();
+        return true;
+    }
+
+    // For now, use MIRType_Double, as a safe cover-all. See bug 1188079.
+    MPow* pow = MPow::New(alloc(), base, exponent, MIRType_Double);
+    current->add(pow);
+    current->push(pow);
+    return true;
+}
+
+bool
 IonBuilder::jsop_binary(JSOp op)
 {
     MDefinition* right = current->pop();
     MDefinition* left = current->pop();
 
     return jsop_binary(op, left, right);
 }
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -619,16 +619,17 @@ class IonBuilder
     bool tryFoldInstanceOf(MDefinition* lhs, JSObject* protoObject);
     bool hasOnProtoChain(TypeSet::ObjectKey* key, JSObject* protoObject, bool* hasOnProto);
 
     bool jsop_add(MDefinition* left, MDefinition* right);
     bool jsop_bitnot();
     bool jsop_bitop(JSOp op);
     bool jsop_binary(JSOp op);
     bool jsop_binary(JSOp op, MDefinition* left, MDefinition* right);
+    bool jsop_pow();
     bool jsop_pos();
     bool jsop_neg();
     bool jsop_setarg(uint32_t arg);
     bool jsop_defvar(uint32_t index);
     bool jsop_deffun(uint32_t index);
     bool jsop_notearg();
     bool jsop_checklexical();
     bool jsop_checkaliasedlet(ScopeCoordinate sc);
@@ -751,16 +752,17 @@ class IonBuilder
     InliningStatus inlineMathCeil(CallInfo& callInfo);
     InliningStatus inlineMathClz32(CallInfo& callInfo);
     InliningStatus inlineMathRound(CallInfo& callInfo);
     InliningStatus inlineMathSqrt(CallInfo& callInfo);
     InliningStatus inlineMathAtan2(CallInfo& callInfo);
     InliningStatus inlineMathHypot(CallInfo& callInfo);
     InliningStatus inlineMathMinMax(CallInfo& callInfo, bool max);
     InliningStatus inlineMathPow(CallInfo& callInfo);
+    InliningStatus inlineMathPowHelper(MDefinition* lhs, MDefinition* rhs, MIRType outputType);
     InliningStatus inlineMathRandom(CallInfo& callInfo);
     InliningStatus inlineMathImul(CallInfo& callInfo);
     InliningStatus inlineMathFRound(CallInfo& callInfo);
     InliningStatus inlineMathFunction(CallInfo& callInfo, MMathFunction::Function function);
 
     // String natives.
     InliningStatus inlineStringObject(CallInfo& callInfo);
     InliningStatus inlineConstantStringSplit(CallInfo& callInfo);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1342,46 +1342,36 @@ IonBuilder::inlineMathHypot(CallInfo& ca
         return InliningStatus_NotInlined;
 
     current->add(hypot);
     current->push(hypot);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineMathPow(CallInfo& callInfo)
+IonBuilder::inlineMathPowHelper(MDefinition* lhs, MDefinition* rhs, MIRType outputType)
 {
-    if (callInfo.argc() != 2 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
-
     // Typechecking.
-    MIRType baseType = callInfo.getArg(0)->type();
-    MIRType powerType = callInfo.getArg(1)->type();
-    MIRType outputType = getInlineReturnType();
+    MIRType baseType = lhs->type();
+    MIRType powerType = rhs->type();
 
     if (outputType != MIRType_Int32 && outputType != MIRType_Double)
         return InliningStatus_NotInlined;
     if (!IsNumberType(baseType))
         return InliningStatus_NotInlined;
     if (!IsNumberType(powerType))
         return InliningStatus_NotInlined;
 
-    callInfo.setImplicitlyUsedUnchecked();
-
-    MDefinition* base = callInfo.getArg(0);
-    MDefinition* power = callInfo.getArg(1);
+    MDefinition* base = lhs;
+    MDefinition* power = rhs;
     MDefinition* output = nullptr;
 
     // Optimize some constant powers.
-    if (callInfo.getArg(1)->isConstantValue() &&
-        callInfo.getArg(1)->constantValue().isNumber())
-    {
-        double pow = callInfo.getArg(1)->constantValue().toNumber();
+    if (rhs->isConstantValue() && rhs->constantValue().isNumber()) {
+        double pow = rhs->constantValue().toNumber();
 
         // Math.pow(x, 0.5) is a sqrt with edge-case detection.
         if (pow == 0.5) {
             MPowHalf* half = MPowHalf::New(alloc(), base);
             current->add(half);
             output = half;
         }
 
@@ -1447,16 +1437,33 @@ IonBuilder::inlineMathPow(CallInfo& call
         output = toDouble;
     }
 
     current->push(output);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
+IonBuilder::inlineMathPow(CallInfo& callInfo)
+{
+    if (callInfo.argc() != 2 || callInfo.constructing()) {
+        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
+        return InliningStatus_NotInlined;
+    }
+
+    IonBuilder::InliningStatus status =
+        inlineMathPowHelper(callInfo.getArg(0), callInfo.getArg(1), getInlineReturnType());
+
+    if (status == IonBuilder::InliningStatus_Inlined)
+        callInfo.setImplicitlyUsedUnchecked();
+
+    return status;
+}
+
+IonBuilder::InliningStatus
 IonBuilder::inlineMathRandom(CallInfo& callInfo)
 {
     if (callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     if (getInlineReturnType() != MIRType_Double)
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/Math/Pow.js
@@ -0,0 +1,103 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var BUGNUMBER = 1135708;
+var summary = "Implement the exponentiation operator";
+
+print(BUGNUMBER + ": " + summary);
+
+// Constant folding
+assertEq(2 ** 2 ** 3, 256);
+assertEq(1 ** 1 ** 4, 1);
+
+// No folding
+var two = 2;
+var three = 3;
+var four = 4;
+assertEq(two ** two ** three, 256);
+assertEq(1 ** 1 ** four, 1);
+
+// Operator precedence
+assertEq(2 ** 3 / 2 ** 3, 1);
+assertEq(2 ** 3 * 2 ** 3, 64);
+assertEq(2 ** 3 + 2 ** 3, 16);
+
+// With parentheses
+assertEq((2 ** 3) ** 2, 64);
+assertEq(2 ** (3 ** 2), 512);
+
+// Assignment operator
+var x = 2;
+assertEq(x **= 2 ** 3, 256);
+assertEq(x, 256);
+
+// Loop to test baseline and ION
+for (var i=0; i<10000; i++) {
+    assertEq((2 ** 3) ** 2, 64);
+    assertEq(2 ** (3 ** 2), 512);
+    var x = 2;
+    assertEq(x **= 2 ** 3, 256);
+    assertEq(x, 256);
+}
+
+// Comments should not be confused with exp operator
+var a, c, e;
+a = c = e = 2;
+assertEq(a**/**b**/c/**/**/**d**/e, 16);
+
+// Two stars separated should not parse as exp operator
+assertThrows(function() { return Reflect.parse("2 * * 3"); }, SyntaxError);
+
+// Check if error propagation works
+var thrower = {
+    get value() {
+        throw new Error();
+    }
+};
+
+assertThrowsInstanceOf(function() { return thrower.value ** 2; }, Error);
+assertThrowsInstanceOf(function() { return 2 ** thrower.value; }, Error);
+assertThrowsInstanceOf(function() { return 2 ** thrower.value ** 2; }, Error);
+
+var convertibleToPrimitive = {
+    valueOf: function() {
+        throw new Error("oops");
+    }
+};
+
+assertThrowsInstanceOf(function() { return convertibleToPrimitive ** 3; }, Error);
+assertThrowsInstanceOf(function() { return 3 ** convertibleToPrimitive; }, Error);
+
+assertEq(NaN ** 2, NaN);
+assertEq(2 ** NaN, NaN);
+assertEq(2 ** "3", 8);
+assertEq("2" ** 3, 8);
+
+// Reflect.parse generates a correct parse tree for simplest case
+var parseTree = Reflect.parse("a ** b");
+assertEq(parseTree.body[0].type, "ExpressionStatement");
+assertEq(parseTree.body[0].expression.operator, "**");
+assertEq(parseTree.body[0].expression.left.name, "a");
+assertEq(parseTree.body[0].expression.right.name, "b");
+
+// Reflect.parse generates a tree following the right-associativity rule
+var parseTree = Reflect.parse("a ** b ** c");
+assertEq(parseTree.body[0].type, "ExpressionStatement");
+assertEq(parseTree.body[0].expression.left.name, "a");
+assertEq(parseTree.body[0].expression.right.operator, "**");
+assertEq(parseTree.body[0].expression.right.left.name, "b");
+assertEq(parseTree.body[0].expression.right.right.name, "c");
+
+
+function assertTrue(v) {
+    assertEq(v, true);
+}
+
+function assertFalse(v) {
+    assertEq(v, false);
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -2047,17 +2047,16 @@ CASE(EnableInterruptsPseudoOpcode)
     SANITY_CHECKS();
     DISPATCH_TO(op);
 }
 
 /* Various 1-byte no-ops. */
 CASE(JSOP_NOP)
 CASE(JSOP_UNUSED2)
 CASE(JSOP_BACKPATCH)
-CASE(JSOP_UNUSED150)
 CASE(JSOP_UNUSED161)
 CASE(JSOP_UNUSED162)
 CASE(JSOP_UNUSED163)
 CASE(JSOP_UNUSED164)
 CASE(JSOP_UNUSED165)
 CASE(JSOP_UNUSED166)
 CASE(JSOP_UNUSED167)
 CASE(JSOP_UNUSED168)
@@ -2650,16 +2649,27 @@ CASE(JSOP_MOD)
     ReservedRooted<Value> rval(&rootValue1, REGS.sp[-1]);
     MutableHandleValue res = REGS.stackHandleAt(-2);
     if (!ModOperation(cx, lval, rval, res))
         goto error;
     REGS.sp--;
 }
 END_CASE(JSOP_MOD)
 
+CASE(JSOP_POW)
+{
+    ReservedRooted<Value> lval(&rootValue0, REGS.sp[-2]);
+    ReservedRooted<Value> rval(&rootValue1, REGS.sp[-1]);
+    MutableHandleValue res = REGS.stackHandleAt(-2);
+    if (!math_pow_handle(cx, lval, rval, res))
+        goto error;
+    REGS.sp--;
+}
+END_CASE(JSOP_POW)
+
 CASE(JSOP_NOT)
 {
     bool cond = ToBoolean(REGS.stackHandleAt(-1));
     REGS.sp--;
     PUSH_BOOLEAN(!cond);
 }
 END_CASE(JSOP_NOT)
 
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1528,17 +1528,25 @@ 1234567890123456789012345678901234567890
      * Placeholder opcode used during bytecode generation. This never
      * appears in a finished script. FIXME: bug 473671.
      *   Category: Statements
      *   Type: Jumps
      *   Operands: int32_t offset
      *   Stack: =>
      */ \
     macro(JSOP_BACKPATCH,     149,"backpatch", NULL,      5,  0,  0,  JOF_JUMP) \
-    macro(JSOP_UNUSED150,     150,"unused150", NULL,      1,  0,  0,  JOF_BYTE) \
+    /*
+     * Pops the top two values 'lval' and 'rval' from the stack, then pushes
+     * the result of 'Math.pow(lval, rval)'.
+     *   Category: Operators
+     *   Type: Arithmetic Operators
+     *   Operands:
+     *   Stack: lval, rval => (lval ** rval)
+     */ \
+    macro(JSOP_POW,           150, "pow",     "**",       1,  2,  1, JOF_BYTE|JOF_ARITH) \
     \
     /*
      * Pops the top of stack value as 'v', sets pending exception as 'v',
      * to trigger rethrow.
      *
      * This opcode is used in conditional catch clauses.
      *   Category: Statements
      *   Type: Exception Handling
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,17 +24,17 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 298;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 299;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
 static_assert(JSErr_Limit == 407,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");