js/src/jsapi-tests/testDebugger.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  */
     4 /* This Source Code Form is subject to the terms of the Mozilla Public
     5  * License, v. 2.0. If a copy of the MPL was not distributed with this
     6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     8 #include "jscntxt.h"
    10 #include "js/OldDebugAPI.h"
    11 #include "jsapi-tests/tests.h"
    13 using namespace js;
    15 static int callCounts[2] = {0, 0};
    17 static void *
    18 callCountHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, bool before,
    19               bool *ok, void *closure)
    20 {
    21     callCounts[before]++;
    23     JS::RootedValue thisv(cx);
    24     frame.getThisValue(cx, &thisv); // assert if fp is incomplete
    26     return cx;  // any non-null value causes the hook to be called again after
    27 }
    29 BEGIN_TEST(testDebugger_bug519719)
    30 {
    31     CHECK(JS_SetDebugMode(cx, true));
    32     JS_SetCallHook(rt, callCountHook, nullptr);
    33     EXEC("function call(fn) { fn(0); }\n"
    34          "function f(g) { for (var i = 0; i < 9; i++) call(g); }\n"
    35          "f(Math.sin);\n"    // record loop, starting in f
    36          "f(Math.cos);\n");  // side exit in f -> call
    37     CHECK_EQUAL(callCounts[0], 20);
    38     CHECK_EQUAL(callCounts[1], 20);
    39     return true;
    40 }
    41 END_TEST(testDebugger_bug519719)
    43 static void *
    44 nonStrictThisHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, bool before,
    45                   bool *ok, void *closure)
    46 {
    47     if (before) {
    48         bool *allWrapped = (bool *) closure;
    49         JS::RootedValue thisv(cx);
    50         frame.getThisValue(cx, &thisv);
    51         *allWrapped = *allWrapped && !JSVAL_IS_PRIMITIVE(thisv);
    52     }
    53     return nullptr;
    54 }
    56 BEGIN_TEST(testDebugger_getThisNonStrict)
    57 {
    58     bool allWrapped = true;
    59     CHECK(JS_SetDebugMode(cx, true));
    60     JS_SetCallHook(rt, nonStrictThisHook, (void *) &allWrapped);
    61     EXEC("function nonstrict() { }\n"
    62          "Boolean.prototype.nonstrict = nonstrict;\n"
    63          "String.prototype.nonstrict = nonstrict;\n"
    64          "Number.prototype.nonstrict = nonstrict;\n"
    65          "Object.prototype.nonstrict = nonstrict;\n"
    66          "nonstrict.call(true);\n"
    67          "true.nonstrict();\n"
    68          "nonstrict.call('');\n"
    69          "''.nonstrict();\n"
    70          "nonstrict.call(42);\n"
    71          "(42).nonstrict();\n"
    72          // The below don't really get 'wrapped', but it's okay.
    73          "nonstrict.call(undefined);\n"
    74          "nonstrict.call(null);\n"
    75          "nonstrict.call({});\n"
    76          "({}).nonstrict();\n");
    77     CHECK(allWrapped);
    78     return true;
    79 }
    80 END_TEST(testDebugger_getThisNonStrict)
    82 static void *
    83 strictThisHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, bool before,
    84                bool *ok, void *closure)
    85 {
    86     if (before) {
    87         bool *anyWrapped = (bool *) closure;
    88         JS::RootedValue thisv(cx);
    89         frame.getThisValue(cx, &thisv);
    90         *anyWrapped = *anyWrapped || !JSVAL_IS_PRIMITIVE(thisv);
    91     }
    92     return nullptr;
    93 }
    95 BEGIN_TEST(testDebugger_getThisStrict)
    96 {
    97     bool anyWrapped = false;
    98     CHECK(JS_SetDebugMode(cx, true));
    99     JS_SetCallHook(rt, strictThisHook, (void *) &anyWrapped);
   100     EXEC("function strict() { 'use strict'; }\n"
   101          "Boolean.prototype.strict = strict;\n"
   102          "String.prototype.strict = strict;\n"
   103          "Number.prototype.strict = strict;\n"
   104          "strict.call(true);\n"
   105          "true.strict();\n"
   106          "strict.call('');\n"
   107          "''.strict();\n"
   108          "strict.call(42);\n"
   109          "(42).strict();\n"
   110          "strict.call(undefined);\n"
   111          "strict.call(null);\n");
   112     CHECK(!anyWrapped);
   113     return true;
   114 }
   115 END_TEST(testDebugger_getThisStrict)
   117 static bool calledThrowHook = false;
   119 static JSTrapStatus
   120 ThrowHook(JSContext *cx, JSScript *, jsbytecode *, jsval *rval, void *closure)
   121 {
   122     JS_ASSERT(!closure);
   123     calledThrowHook = true;
   125     JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
   127     char text[] = "new Error()";
   128     JS::RootedValue _(cx);
   129     JS_EvaluateScript(cx, global, text, strlen(text), "", 0, &_);
   131     return JSTRAP_CONTINUE;
   132 }
   134 BEGIN_TEST(testDebugger_throwHook)
   135 {
   136     CHECK(JS_SetDebugMode(cx, true));
   137     CHECK(JS_SetThrowHook(rt, ThrowHook, nullptr));
   138     EXEC("function foo() { throw 3 };\n"
   139          "for (var i = 0; i < 10; ++i) { \n"
   140          "  var x = {}\n"
   141          "  try {\n"
   142          "    foo(); \n"
   143          "  } catch(e) {}\n"
   144          "}\n");
   145     CHECK(calledThrowHook);
   146     CHECK(JS_SetThrowHook(rt, nullptr, nullptr));
   147     return true;
   148 }
   149 END_TEST(testDebugger_throwHook)
   151 BEGIN_TEST(testDebugger_debuggerObjectVsDebugMode)
   152 {
   153     CHECK(JS_DefineDebuggerObject(cx, global));
   154     JS::RootedObject debuggee(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook));
   155     CHECK(debuggee);
   157     {
   158         JSAutoCompartment ae(cx, debuggee);
   159         CHECK(JS_SetDebugMode(cx, true));
   160         CHECK(JS_InitStandardClasses(cx, debuggee));
   161     }
   163     JS::RootedObject debuggeeWrapper(cx, debuggee);
   164     CHECK(JS_WrapObject(cx, &debuggeeWrapper));
   165     JS::RootedValue v(cx, JS::ObjectValue(*debuggeeWrapper));
   166     CHECK(JS_SetProperty(cx, global, "debuggee", v));
   168     EVAL("var dbg = new Debugger(debuggee);\n"
   169          "var hits = 0;\n"
   170          "dbg.onDebuggerStatement = function () { hits++; };\n"
   171          "debuggee.eval('debugger;');\n"
   172          "hits;\n",
   173          &v);
   174     CHECK_SAME(v, JSVAL_ONE);
   176     {
   177         JSAutoCompartment ae(cx, debuggee);
   178         CHECK(JS_SetDebugMode(cx, false));
   179     }
   181     EVAL("debuggee.eval('debugger; debugger; debugger;');\n"
   182          "hits;\n",
   183          &v);
   184     CHECK_SAME(v, INT_TO_JSVAL(4));
   186     return true;
   187 }
   188 END_TEST(testDebugger_debuggerObjectVsDebugMode)
   190 BEGIN_TEST(testDebugger_newScriptHook)
   191 {
   192     // Test that top-level indirect eval fires the newScript hook.
   193     CHECK(JS_DefineDebuggerObject(cx, global));
   194     JS::RootedObject g(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook));
   195     CHECK(g);
   196     {
   197         JSAutoCompartment ae(cx, g);
   198         CHECK(JS_InitStandardClasses(cx, g));
   199     }
   201     JS::RootedObject gWrapper(cx, g);
   202     CHECK(JS_WrapObject(cx, &gWrapper));
   203     JS::RootedValue v(cx, JS::ObjectValue(*gWrapper));
   204     CHECK(JS_SetProperty(cx, global, "g", v));
   206     EXEC("var dbg = Debugger(g);\n"
   207          "var hits = 0;\n"
   208          "dbg.onNewScript = function (s) {\n"
   209          "    hits += Number(s instanceof Debugger.Script);\n"
   210          "};\n");
   212     // Since g is a debuggee, g.eval should trigger newScript, regardless of
   213     // what scope object we use to enter the compartment.
   214     //
   215     // Scripts are associated with the global where they're compiled, so we
   216     // deliver them only to debuggers that are watching that particular global.
   217     //
   218     return testIndirectEval(g, "Math.abs(0)");
   219 }
   221 bool testIndirectEval(JS::HandleObject scope, const char *code)
   222 {
   223     EXEC("hits = 0;");
   225     {
   226         JSAutoCompartment ae(cx, scope);
   227         JSString *codestr = JS_NewStringCopyZ(cx, code);
   228         CHECK(codestr);
   229         JS::RootedValue arg(cx, JS::StringValue(codestr));
   230         JS::RootedValue v(cx);
   231         CHECK(JS_CallFunctionName(cx, scope, "eval", arg, &v));
   232     }
   234     JS::RootedValue hitsv(cx);
   235     EVAL("hits", &hitsv);
   236     CHECK_SAME(hitsv, INT_TO_JSVAL(1));
   237     return true;
   238 }
   239 END_TEST(testDebugger_newScriptHook)
   241 BEGIN_TEST(testDebugger_singleStepThrow)
   242     {
   243         CHECK(JS_SetDebugModeForCompartment(cx, cx->compartment(), true));
   244         CHECK(JS_SetInterrupt(rt, onStep, nullptr));
   246         CHECK(JS_DefineFunction(cx, global, "setStepMode", setStepMode, 0, 0));
   247         EXEC("var e;\n"
   248              "setStepMode();\n"
   249              "function f() { throw 0; }\n"
   250              "try { f(); }\n"
   251              "catch (x) { e = x; }\n");
   252         return true;
   253     }
   255     static bool
   256     setStepMode(JSContext *cx, unsigned argc, jsval *vp)
   257     {
   258         CallArgs args = CallArgsFromVp(argc, vp);
   260         NonBuiltinScriptFrameIter iter(cx);
   261         JS::RootedScript script(cx, iter.script());
   262         if (!JS_SetSingleStepMode(cx, script, true))
   263             return false;
   265         args.rval().set(UndefinedValue());
   266         return true;
   267     }
   269     static JSTrapStatus
   270     onStep(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure)
   271     {
   272         return JSTRAP_CONTINUE;
   273     }
   274 END_TEST(testDebugger_singleStepThrow)

mercurial