LCOV - code coverage report
Current view: directory - js/src/jsapi-tests - testDebugger.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 153 152 99.3 %
Date: 2012-06-02 Functions: 55 45 81.8 %

       1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: set ts=8 sw=4 et tw=99:
       3                 :  */
       4                 : 
       5                 : #include "tests.h"
       6                 : #include "jsdbgapi.h"
       7                 : #include "jscntxt.h"
       8                 : 
       9                 : static int callCount[2] = {0, 0};
      10                 : 
      11                 : static void *
      12              40 : callCountHook(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure)
      13                 : {
      14              40 :     callCount[before]++;
      15                 : 
      16                 :     jsval thisv;
      17              40 :     JS_GetFrameThis(cx, fp, &thisv);  // assert if fp is incomplete
      18                 : 
      19              40 :     return cx;  // any non-null value causes the hook to be called again after
      20                 : }
      21                 : 
      22               4 : BEGIN_TEST(testDebugger_bug519719)
      23                 : {
      24               1 :     CHECK(JS_SetDebugMode(cx, JS_TRUE));
      25               1 :     JS_SetCallHook(rt, callCountHook, NULL);
      26               1 :     EXEC("function call(fn) { fn(0); }\n"
      27                 :          "function f(g) { for (var i = 0; i < 9; i++) call(g); }\n"
      28                 :          "f(Math.sin);\n"    // record loop, starting in f
      29                 :          "f(Math.cos);\n");  // side exit in f -> call
      30               1 :     CHECK_EQUAL(callCount[0], 20);
      31               1 :     CHECK_EQUAL(callCount[1], 20);
      32               1 :     return true;
      33                 : }
      34               1 : END_TEST(testDebugger_bug519719)
      35                 : 
      36                 : static void *
      37              10 : nonStrictThisHook(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure)
      38                 : {
      39              10 :     if (before) {
      40              10 :         bool *allWrapped = (bool *) closure;
      41                 :         jsval thisv;
      42              10 :         JS_GetFrameThis(cx, fp, &thisv);
      43              10 :         *allWrapped = *allWrapped && !JSVAL_IS_PRIMITIVE(thisv);
      44                 :     }
      45              10 :     return NULL;
      46                 : }
      47                 : 
      48               4 : BEGIN_TEST(testDebugger_getThisNonStrict)
      49                 : {
      50               1 :     bool allWrapped = true;
      51               1 :     CHECK(JS_SetDebugMode(cx, JS_TRUE));
      52               1 :     JS_SetCallHook(rt, nonStrictThisHook, (void *) &allWrapped);
      53               1 :     EXEC("function nonstrict() { }\n"
      54                 :          "Boolean.prototype.nonstrict = nonstrict;\n"
      55                 :          "String.prototype.nonstrict = nonstrict;\n"
      56                 :          "Number.prototype.nonstrict = nonstrict;\n"
      57                 :          "Object.prototype.nonstrict = nonstrict;\n"
      58                 :          "nonstrict.call(true);\n"
      59                 :          "true.nonstrict();\n"
      60                 :          "nonstrict.call('');\n"
      61                 :          "''.nonstrict();\n"
      62                 :          "nonstrict.call(42);\n"
      63                 :          "(42).nonstrict();\n"
      64                 :          // The below don't really get 'wrapped', but it's okay.
      65                 :          "nonstrict.call(undefined);\n"
      66                 :          "nonstrict.call(null);\n"
      67                 :          "nonstrict.call({});\n"
      68                 :          "({}).nonstrict();\n");
      69               1 :     CHECK(allWrapped);
      70               1 :     return true;
      71                 : }
      72               1 : END_TEST(testDebugger_getThisNonStrict)
      73                 : 
      74                 : static void *
      75               8 : strictThisHook(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure)
      76                 : {
      77               8 :     if (before) {
      78               8 :         bool *anyWrapped = (bool *) closure;
      79                 :         jsval thisv;
      80               8 :         JS_GetFrameThis(cx, fp, &thisv);
      81               8 :         *anyWrapped = *anyWrapped || !JSVAL_IS_PRIMITIVE(thisv);
      82                 :     }
      83               8 :     return NULL;
      84                 : }
      85                 : 
      86               4 : BEGIN_TEST(testDebugger_getThisStrict)
      87                 : {
      88               1 :     bool anyWrapped = false;
      89               1 :     CHECK(JS_SetDebugMode(cx, JS_TRUE));
      90               1 :     JS_SetCallHook(rt, strictThisHook, (void *) &anyWrapped);
      91               1 :     EXEC("function strict() { 'use strict'; }\n"
      92                 :          "Boolean.prototype.strict = strict;\n"
      93                 :          "String.prototype.strict = strict;\n"
      94                 :          "Number.prototype.strict = strict;\n"
      95                 :          "strict.call(true);\n"
      96                 :          "true.strict();\n"
      97                 :          "strict.call('');\n"
      98                 :          "''.strict();\n"
      99                 :          "strict.call(42);\n"
     100                 :          "(42).strict();\n"
     101                 :          "strict.call(undefined);\n"
     102                 :          "strict.call(null);\n");
     103               1 :     CHECK(!anyWrapped);
     104               1 :     return true;
     105                 : }
     106               1 : END_TEST(testDebugger_getThisStrict)
     107                 : 
     108                 : bool called = false;
     109                 : 
     110                 : static JSTrapStatus
     111              20 : ThrowHook(JSContext *cx, JSScript *, jsbytecode *, jsval *rval, void *closure)
     112                 : {
     113              20 :     JS_ASSERT(!closure);
     114              20 :     called = true;
     115                 : 
     116              20 :     JSObject *global = JS_GetGlobalForScopeChain(cx);
     117                 : 
     118              20 :     char text[] = "new Error()";
     119                 :     jsval _;
     120              20 :     JS_EvaluateScript(cx, global, text, strlen(text), "", 0, &_);
     121                 : 
     122              20 :     return JSTRAP_CONTINUE;
     123                 : }
     124                 : 
     125               4 : BEGIN_TEST(testDebugger_throwHook)
     126                 : {
     127               1 :     uint32_t newopts = JS_GetOptions(cx) | JSOPTION_METHODJIT | JSOPTION_METHODJIT_ALWAYS;
     128               1 :     uint32_t oldopts = JS_SetOptions(cx, newopts);
     129                 : 
     130               1 :     CHECK(JS_SetThrowHook(rt, ThrowHook, NULL));
     131               1 :     EXEC("function foo() { throw 3 };\n"
     132                 :          "for (var i = 0; i < 10; ++i) { \n"
     133                 :          "  var x = <tag></tag>;\n"
     134                 :          "  try {\n"
     135                 :          "    foo(); \n"
     136                 :          "  } catch(e) {}\n"
     137                 :          "}\n");
     138               1 :     CHECK(called);
     139               1 :     CHECK(JS_SetThrowHook(rt, NULL, NULL));
     140               1 :     JS_SetOptions(cx, oldopts);
     141               1 :     return true;
     142                 : }
     143               1 : END_TEST(testDebugger_throwHook)
     144                 : 
     145               4 : BEGIN_TEST(testDebugger_debuggerObjectVsDebugMode)
     146                 : {
     147               1 :     CHECK(JS_DefineDebuggerObject(cx, global));
     148               1 :     JSObject *debuggee = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), NULL);
     149               1 :     CHECK(debuggee);
     150                 : 
     151                 :     {
     152               2 :         JSAutoEnterCompartment ae;
     153               1 :         CHECK(ae.enter(cx, debuggee));
     154               1 :         CHECK(JS_SetDebugMode(cx, true));
     155               1 :         CHECK(JS_InitStandardClasses(cx, debuggee));
     156                 :     }
     157                 : 
     158               1 :     JSObject *debuggeeWrapper = debuggee;
     159               1 :     CHECK(JS_WrapObject(cx, &debuggeeWrapper));
     160               1 :     jsval v = OBJECT_TO_JSVAL(debuggeeWrapper);
     161               1 :     CHECK(JS_SetProperty(cx, global, "debuggee", &v));
     162                 : 
     163               1 :     EVAL("var dbg = new Debugger(debuggee);\n"
     164                 :          "var hits = 0;\n"
     165                 :          "dbg.onDebuggerStatement = function () { hits++; };\n"
     166                 :          "debuggee.eval('debugger;');\n"
     167                 :          "hits;\n",
     168                 :          &v);
     169               1 :     CHECK_SAME(v, JSVAL_ONE);
     170                 : 
     171                 :     {
     172               2 :         JSAutoEnterCompartment ae;
     173               1 :         CHECK(ae.enter(cx, debuggee));
     174               1 :         CHECK(JS_SetDebugMode(cx, false));
     175                 :     }
     176                 : 
     177               1 :     EVAL("debuggee.eval('debugger; debugger; debugger;');\n"
     178                 :          "hits;\n",
     179                 :          &v);
     180               1 :     CHECK_SAME(v, INT_TO_JSVAL(4));
     181                 :     
     182               1 :     return true;
     183                 : }
     184               1 : END_TEST(testDebugger_debuggerObjectVsDebugMode)
     185                 : 
     186               4 : BEGIN_TEST(testDebugger_newScriptHook)
     187                 : {
     188                 :     // Test that top-level indirect eval fires the newScript hook.
     189               1 :     CHECK(JS_DefineDebuggerObject(cx, global));
     190                 :     JSObject *g1, *g2;
     191               1 :     g1 = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), NULL);
     192               1 :     CHECK(g1);
     193                 :     {
     194               2 :         JSAutoEnterCompartment ae;
     195               1 :         CHECK(ae.enter(cx, g1));
     196               1 :         CHECK(JS_InitStandardClasses(cx, g1));
     197               1 :         g2 = JS_NewGlobalObject(cx, getGlobalClass());
     198               1 :         CHECK(g2);
     199               1 :         CHECK(JS_InitStandardClasses(cx, g2));
     200                 :     }
     201                 : 
     202               1 :     JSObject *g1Wrapper = g1;
     203               1 :     CHECK(JS_WrapObject(cx, &g1Wrapper));
     204               1 :     jsval v = OBJECT_TO_JSVAL(g1Wrapper);
     205               1 :     CHECK(JS_SetProperty(cx, global, "g1", &v));
     206                 : 
     207               1 :     JSObject *g2Wrapper = g2;
     208               1 :     CHECK(JS_WrapObject(cx, &g2Wrapper));
     209               1 :     v = OBJECT_TO_JSVAL(g2Wrapper);
     210               1 :     CHECK(JS_SetProperty(cx, global, "g2", &v));
     211                 : 
     212               1 :     EXEC("var dbg = Debugger(g1);\n"
     213                 :          "var hits = 0;\n"
     214                 :          "dbg.onNewScript = function (s) {\n"
     215                 :          "    hits += Number(s instanceof Debugger.Script);\n"
     216                 :          "};\n");
     217                 : 
     218                 :     // Since g1 is a debuggee and g2 is not, g1.eval should trigger newScript
     219                 :     // and g2.eval should not, regardless of what scope object we use to enter
     220                 :     // the compartment.
     221                 :     //
     222                 :     // (Not all scripts are permanently associated with specific global
     223                 :     // objects, but eval scripts are, so we deliver them only to debuggers that
     224                 :     // are watching that particular global.)
     225                 :     //
     226               1 :     bool ok = true;
     227               1 :     ok = ok && testIndirectEval(g1, g1, "Math.abs(0)", 1);
     228               1 :     ok = ok && testIndirectEval(g2, g1, "Math.abs(1)", 1);
     229               1 :     ok = ok && testIndirectEval(g1, g2, "Math.abs(-1)", 0);
     230               1 :     ok = ok && testIndirectEval(g2, g2, "Math.abs(-2)", 0);
     231               1 :     return ok;
     232                 : }
     233                 : 
     234               4 : bool testIndirectEval(JSObject *scope, JSObject *g, const char *code, int expectedHits)
     235                 : {
     236               4 :     EXEC("hits = 0;");
     237                 : 
     238                 :     {
     239               8 :         JSAutoEnterCompartment ae;
     240               4 :         CHECK(ae.enter(cx, scope));
     241               4 :         JSString *codestr = JS_NewStringCopyZ(cx, code);
     242               4 :         CHECK(codestr);
     243               4 :         jsval argv[1] = { STRING_TO_JSVAL(codestr) };
     244                 :         jsval v;
     245               4 :         CHECK(JS_CallFunctionName(cx, g, "eval", 1, argv, &v));
     246                 :     }
     247                 : 
     248                 :     jsval hitsv;
     249               4 :     EVAL("hits", &hitsv);
     250               4 :     CHECK_SAME(hitsv, INT_TO_JSVAL(expectedHits));
     251               4 :     return true;
     252                 : }
     253               1 : END_TEST(testDebugger_newScriptHook)
     254                 : 
     255               4 : BEGIN_TEST(testDebugger_singleStepThrow)
     256                 :     {
     257               1 :         CHECK(JS_SetDebugModeForCompartment(cx, cx->compartment, true));
     258               1 :         CHECK(JS_SetInterrupt(rt, onStep, NULL));
     259                 : 
     260               1 :         uint32_t opts = JS_GetOptions(cx);
     261               1 :         opts |= JSOPTION_METHODJIT | JSOPTION_METHODJIT_ALWAYS;
     262               1 :         JS_SetOptions(cx, opts);
     263                 : 
     264               1 :         CHECK(JS_DefineFunction(cx, global, "setStepMode", setStepMode, 0, 0));
     265               1 :         EXEC("var e;\n"
     266                 :              "setStepMode();\n"
     267                 :              "function f() { throw 0; }\n"
     268                 :              "try { f(); }\n"
     269                 :              "catch (x) { e = x; }\n");
     270               1 :         return true;
     271                 :     }
     272                 : 
     273                 :     static JSBool
     274               1 :     setStepMode(JSContext *cx, unsigned argc, jsval *vp)
     275                 :     {
     276                 :         JSScript *script;
     277               1 :         JS_DescribeScriptedCaller(cx, &script, NULL);
     278               1 :         JS_ASSERT(script);
     279                 : 
     280               1 :         if (!JS_SetSingleStepMode(cx, script, true))
     281               0 :             return false;
     282               1 :         JS_SET_RVAL(cx, vp, JSVAL_VOID);
     283               1 :         return true;
     284                 :     }
     285                 : 
     286                 :     static JSTrapStatus
     287              16 :     onStep(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure)
     288                 :     {
     289              16 :         return JSTRAP_CONTINUE;
     290                 :     }
     291               1 : END_TEST(testDebugger_singleStepThrow)
     292                 : 
     293               4 : BEGIN_TEST(testDebugger_emptyObjectPropertyIterator)
     294                 : {
     295               1 :     JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
     296               1 :     JSScopeProperty *prop = NULL;
     297               1 :     CHECK(!JS_PropertyIterator(obj, &prop));
     298               1 :     CHECK(!prop);
     299                 : 
     300               1 :     return true;
     301                 : }
     302               1 : END_TEST(testDebugger_emptyObjectPropertyIterator)
     303                 : 
     304               4 : BEGIN_TEST(testDebugger_nonEmptyObjectPropertyIterator)
     305                 : {
     306                 :     jsval v;
     307               1 :     EVAL("({a: 15})", &v);
     308               1 :     JSObject *obj = JSVAL_TO_OBJECT(v);
     309               1 :     JSScopeProperty *prop = NULL;
     310               1 :     CHECK(JS_PropertyIterator(obj, &prop));
     311                 :     JSPropertyDesc desc;
     312               1 :     CHECK(JS_GetPropertyDesc(cx, obj, prop, &desc));
     313               1 :     CHECK_EQUAL(JSVAL_IS_INT(desc.value), true);
     314               1 :     CHECK_EQUAL(JSVAL_TO_INT(desc.value), 15);
     315               1 :     CHECK(!JS_PropertyIterator(obj, &prop));
     316               1 :     CHECK(!prop);
     317                 : 
     318               1 :     return true;
     319                 : }
     320               3 : END_TEST(testDebugger_nonEmptyObjectPropertyIterator)

Generated by: LCOV version 1.7