Bug 1322019 - Part 2: Decompile more opcodes. r=nbp
authorTooru Fujisawa <arai_a@mac.com>
Mon, 27 Feb 2017 20:02:55 +0900
changeset 374080 20cd9a2ede17439c9a8c87fc7c6542ff93945a24
parent 374079 bcb3bb41c374eaa64fd64d651148b59b70acf377
child 374081 c905752969fb007959730ae170449dc1e97cd62d
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1322019
milestone54.0a1
Bug 1322019 - Part 2: Decompile more opcodes. r=nbp
js/src/jit-test/tests/basic/expression-autopsy.js
js/src/jsopcode.cpp
--- a/js/src/jit-test/tests/basic/expression-autopsy.js
+++ b/js/src/jit-test/tests/basic/expression-autopsy.js
@@ -101,16 +101,86 @@ check("o[(- (o + 1))]");
 check_one("6", (function () { 6() }), " is not a function");
 check_one("4", (function() { (4||eval)(); }), " is not a function");
 check_one("0", (function () { Array.prototype.reverse.call('123'); }), " is read-only");
 check_one("(intermediate value)[Symbol.iterator](...).next(...).value",
           function () { ieval("{ let x; var [a, b, [c0, c1]] = [x, x, x]; }") }, " is undefined");
 check_one("(void 1)", function() { (void 1)(); }, " is not a function");
 check_one("(void o[1])", function() { var o = []; (void o[1])() }, " is not a function");
 
+check_one("(typeof 1)", function() { (typeof 1)(); }, " is not a function");
+check_one("(typeof o[1])", function() { var o = []; (typeof o[1])() }, " is not a function");
+
+check_one("(delete foo)",
+          function() { (delete foo)(); },
+          " is not a function");
+check_one("(delete obj.foo)",
+          function() { var obj = {}; (delete obj.foo)(); },
+          " is not a function");
+check_one("(delete obj.foo)",
+          function() { "use strict"; var obj = {}; (delete obj.foo)(); },
+          " is not a function");
+check_one("(delete obj[y])",
+          function() { var obj = {}, y = {}; (delete obj[y])(); },
+          " is not a function");
+check_one("(delete obj[y])",
+          function() { "use strict"; var obj = {}, y = {}; (delete obj[y])(); },
+          " is not a function");
+
+check_one("foo.apply(...)",
+          function() { function foo() {} foo.apply()(); },
+          " is not a function");
+
+check_one("super(...)",
+          function() {
+            class X extends Object {
+              constructor() {
+                super()();
+              }
+            }
+            new X();
+          },
+          " is not a function");
+check_one("super(...)",
+          function() {
+            class X extends Object {
+              constructor() {
+                super(...[])();
+              }
+            }
+            new X();
+          },
+          " is not a function");
+
+check_one("eval(...)",
+          function() { eval("")(); },
+          " is not a function");
+check_one("eval(...)",
+          function() { "use strict"; eval("")(); },
+          " is not a function");
+check_one("eval(...)",
+          function() { eval(...[""])(); },
+          " is not a function");
+check_one("eval(...)",
+          function() { "use strict"; eval(...[""])(); },
+          " is not a function");
+
+check_one("(new foo(...))",
+          function() { function foo() {}; new foo()(); },
+          " is not a function");
+check_one("(new foo(...))",
+          function() { function foo() {}; new foo(...[])(); },
+          " is not a function");
+check_one("(new foo.x(...))",
+          function() { var foo = { x: function() {} }; new foo.x()(); },
+          " is not a function");
+check_one("(new foo.x(...))",
+          function() { var foo = { x: function() {} }; new foo.x(...[])(); },
+          " is not a function");
+
 // Manual testing for this case: the only way to trigger an error is *not* on
 // an attempted property access during destructuring, and the error message
 // invoking ToObject(null) is different: "can't convert {0} to object".
 try
 {
   (function() {
     var [{x}] = [null, {}];
    })();
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1248,16 +1248,21 @@ ExpressionDecompiler::decompilePC(jsbyte
                    decompilePCForStackOperand(pc, -1) &&
                    write(")");
           default:
             break;
         }
     }
 
     switch (op) {
+      case JSOP_DELNAME:
+        return write("(delete ") &&
+               write(loadAtom(pc)) &&
+               write(")");
+
       case JSOP_GETGNAME:
       case JSOP_GETNAME:
       case JSOP_GETINTRINSIC:
         return write(loadAtom(pc));
       case JSOP_GETARG: {
         unsigned slot = GET_ARGNO(pc);
 
         // For self-hosted scripts that are called from non-self-hosted code,
@@ -1287,29 +1292,31 @@ ExpressionDecompiler::decompilePC(jsbyte
         MOZ_ASSERT(atom);
         return write(atom);
       }
       case JSOP_GETALIASEDVAR: {
         JSAtom* atom = EnvironmentCoordinateName(cx->caches().envCoordinateNameCache, script, pc);
         MOZ_ASSERT(atom);
         return write(atom);
       }
+
+      case JSOP_DELPROP:
+      case JSOP_STRICTDELPROP:
       case JSOP_LENGTH:
       case JSOP_GETPROP:
+      case JSOP_GETBOUNDNAME:
       case JSOP_CALLPROP: {
+        bool hasDelete = op == JSOP_DELPROP || op == JSOP_STRICTDELPROP;
         RootedAtom prop(cx, (op == JSOP_LENGTH) ? cx->names().length : loadAtom(pc));
-        if (!decompilePCForStackOperand(pc, -1))
-            return false;
-        if (IsIdentifier(prop)) {
-            return write(".") &&
-                   quote(prop, '\0');
-        }
-        return write("[") &&
-               quote(prop, '\'') &&
-               write("]");
+        return (hasDelete ? write("(delete ") : true) &&
+               decompilePCForStackOperand(pc, -1) &&
+               (IsIdentifier(prop)
+                ? write(".") && quote(prop, '\0')
+                : write("[") && quote(prop, '\'') && write("]")) &&
+               (hasDelete ? write(")") : true);
       }
       case JSOP_GETPROP_SUPER:
       {
         RootedAtom prop(cx, loadAtom(pc));
         return write("super.") &&
                quote(prop, '\0');
       }
       case JSOP_SETELEM:
@@ -1317,22 +1324,30 @@ ExpressionDecompiler::decompilePC(jsbyte
         // NOTE: We don't show the right hand side of the operation because
         // it's used in error messages like: "a[0] is not readable".
         //
         // We could though.
         return decompilePCForStackOperand(pc, -3) &&
                write("[") &&
                decompilePCForStackOperand(pc, -2) &&
                write("]");
+
+      case JSOP_DELELEM:
+      case JSOP_STRICTDELELEM:
       case JSOP_GETELEM:
-      case JSOP_CALLELEM:
-        return decompilePCForStackOperand(pc, -2) &&
+      case JSOP_CALLELEM: {
+        bool hasDelete = (op == JSOP_DELELEM || op == JSOP_STRICTDELELEM);
+        return (hasDelete ? write("(delete ") : true) &&
+               decompilePCForStackOperand(pc, -2) &&
                write("[") &&
                decompilePCForStackOperand(pc, -1) &&
-               write("]");
+               write("]") &&
+               (hasDelete ? write(")") : true);
+      }
+
       case JSOP_GETELEM_SUPER:
         return write("super[") &&
                decompilePCForStackOperand(pc, -3) &&
                write("]");
       case JSOP_NULL:
         return write(js_null_str);
       case JSOP_TRUE:
         return write(js_true_str);
@@ -1360,16 +1375,17 @@ ExpressionDecompiler::decompilePC(jsbyte
         // |this| could convert to a very long object initialiser, so cite it by
         // its keyword name.
         return write(js_this_str);
       case JSOP_NEWTARGET:
         return write("new.target");
       case JSOP_CALL:
       case JSOP_CALLITER:
       case JSOP_FUNCALL:
+      case JSOP_FUNAPPLY:
         return decompilePCForStackOperand(pc, -int32_t(GET_ARGC(pc) + 2)) &&
                write("(...)");
       case JSOP_SPREADCALL:
         return decompilePCForStackOperand(pc, -3) &&
                write("(...)");
       case JSOP_NEWARRAY:
         return write("[]");
       case JSOP_REGEXP:
@@ -1383,16 +1399,44 @@ ExpressionDecompiler::decompilePC(jsbyte
         return write(str);
       }
       case JSOP_CHECKISOBJ:
         return decompilePCForStackOperand(pc, -1);
       case JSOP_VOID:
         return write("(void ") &&
                decompilePCForStackOperand(pc, -1) &&
                write(")");
+
+      case JSOP_SUPERCALL:
+      case JSOP_SPREADSUPERCALL:
+        return write("super(...)");
+      case JSOP_SUPERFUN:
+        return write("super");
+
+      case JSOP_EVAL:
+      case JSOP_SPREADEVAL:
+      case JSOP_STRICTEVAL:
+      case JSOP_STRICTSPREADEVAL:
+        return write("eval(...)");
+
+      case JSOP_NEW:
+        return write("(new ") &&
+               decompilePCForStackOperand(pc, -int32_t(GET_ARGC(pc) + 3)) &&
+               write("(...))");
+
+      case JSOP_SPREADNEW:
+        return write("(new ") &&
+               decompilePCForStackOperand(pc, -4) &&
+               write("(...))");
+
+      case JSOP_TYPEOF:
+      case JSOP_TYPEOFEXPR:
+        return write("(typeof ") &&
+               decompilePCForStackOperand(pc, -1) &&
+               write(")");
       default:
         break;
     }
     return write("(intermediate value)");
 }
 
 bool
 ExpressionDecompiler::init()