https://github.com/mozilla/gecko-dev
Tip revision: b9c3ece3cdced24a92f21a6a571d53f7c9fd0f9d authored by B2G Bumper Bot on 11 October 2014, 17:31:46 UTC
Bumping manifests a=b2g-bump
Bumping manifests a=b2g-bump
Tip revision: b9c3ece
testDebugger.cpp
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jscntxt.h"
#include "js/OldDebugAPI.h"
#include "jsapi-tests/tests.h"
using namespace js;
static int callCounts[2] = {0, 0};
static void *
callCountHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, bool before,
bool *ok, void *closure)
{
callCounts[before]++;
JS::RootedValue thisv(cx);
frame.getThisValue(cx, &thisv); // assert if fp is incomplete
return cx; // any non-null value causes the hook to be called again after
}
BEGIN_TEST(testDebugger_bug519719)
{
CHECK(JS_SetDebugMode(cx, true));
JS_SetCallHook(rt, callCountHook, nullptr);
EXEC("function call(fn) { fn(0); }\n"
"function f(g) { for (var i = 0; i < 9; i++) call(g); }\n"
"f(Math.sin);\n" // record loop, starting in f
"f(Math.cos);\n"); // side exit in f -> call
CHECK_EQUAL(callCounts[0], 20);
CHECK_EQUAL(callCounts[1], 20);
return true;
}
END_TEST(testDebugger_bug519719)
static void *
nonStrictThisHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, bool before,
bool *ok, void *closure)
{
if (before) {
bool *allWrapped = (bool *) closure;
JS::RootedValue thisv(cx);
frame.getThisValue(cx, &thisv);
*allWrapped = *allWrapped && !JSVAL_IS_PRIMITIVE(thisv);
}
return nullptr;
}
BEGIN_TEST(testDebugger_getThisNonStrict)
{
bool allWrapped = true;
CHECK(JS_SetDebugMode(cx, true));
JS_SetCallHook(rt, nonStrictThisHook, (void *) &allWrapped);
EXEC("function nonstrict() { }\n"
"Boolean.prototype.nonstrict = nonstrict;\n"
"String.prototype.nonstrict = nonstrict;\n"
"Number.prototype.nonstrict = nonstrict;\n"
"Object.prototype.nonstrict = nonstrict;\n"
"nonstrict.call(true);\n"
"true.nonstrict();\n"
"nonstrict.call('');\n"
"''.nonstrict();\n"
"nonstrict.call(42);\n"
"(42).nonstrict();\n"
// The below don't really get 'wrapped', but it's okay.
"nonstrict.call(undefined);\n"
"nonstrict.call(null);\n"
"nonstrict.call({});\n"
"({}).nonstrict();\n");
CHECK(allWrapped);
return true;
}
END_TEST(testDebugger_getThisNonStrict)
static void *
strictThisHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, bool before,
bool *ok, void *closure)
{
if (before) {
bool *anyWrapped = (bool *) closure;
JS::RootedValue thisv(cx);
frame.getThisValue(cx, &thisv);
*anyWrapped = *anyWrapped || !JSVAL_IS_PRIMITIVE(thisv);
}
return nullptr;
}
BEGIN_TEST(testDebugger_getThisStrict)
{
bool anyWrapped = false;
CHECK(JS_SetDebugMode(cx, true));
JS_SetCallHook(rt, strictThisHook, (void *) &anyWrapped);
EXEC("function strict() { 'use strict'; }\n"
"Boolean.prototype.strict = strict;\n"
"String.prototype.strict = strict;\n"
"Number.prototype.strict = strict;\n"
"strict.call(true);\n"
"true.strict();\n"
"strict.call('');\n"
"''.strict();\n"
"strict.call(42);\n"
"(42).strict();\n"
"strict.call(undefined);\n"
"strict.call(null);\n");
CHECK(!anyWrapped);
return true;
}
END_TEST(testDebugger_getThisStrict)
static bool calledThrowHook = false;
static JSTrapStatus
ThrowHook(JSContext *cx, JSScript *, jsbytecode *, jsval *rval, void *closure)
{
JS_ASSERT(!closure);
calledThrowHook = true;
JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
char text[] = "new Error()";
JS::RootedValue _(cx);
JS_EvaluateScript(cx, global, text, strlen(text), "", 0, _.address());
return JSTRAP_CONTINUE;
}
BEGIN_TEST(testDebugger_throwHook)
{
CHECK(JS_SetDebugMode(cx, true));
CHECK(JS_SetThrowHook(rt, ThrowHook, nullptr));
EXEC("function foo() { throw 3 };\n"
"for (var i = 0; i < 10; ++i) { \n"
" var x = {}\n"
" try {\n"
" foo(); \n"
" } catch(e) {}\n"
"}\n");
CHECK(calledThrowHook);
CHECK(JS_SetThrowHook(rt, nullptr, nullptr));
return true;
}
END_TEST(testDebugger_throwHook)
BEGIN_TEST(testDebugger_debuggerObjectVsDebugMode)
{
CHECK(JS_DefineDebuggerObject(cx, global));
JS::RootedObject debuggee(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook));
CHECK(debuggee);
{
JSAutoCompartment ae(cx, debuggee);
CHECK(JS_SetDebugMode(cx, true));
CHECK(JS_InitStandardClasses(cx, debuggee));
}
JS::RootedObject debuggeeWrapper(cx, debuggee);
CHECK(JS_WrapObject(cx, &debuggeeWrapper));
JS::RootedValue v(cx, JS::ObjectValue(*debuggeeWrapper));
CHECK(JS_SetProperty(cx, global, "debuggee", v));
EVAL("var dbg = new Debugger(debuggee);\n"
"var hits = 0;\n"
"dbg.onDebuggerStatement = function () { hits++; };\n"
"debuggee.eval('debugger;');\n"
"hits;\n",
v.address());
CHECK_SAME(v, JSVAL_ONE);
{
JSAutoCompartment ae(cx, debuggee);
CHECK(JS_SetDebugMode(cx, false));
}
EVAL("debuggee.eval('debugger; debugger; debugger;');\n"
"hits;\n",
v.address());
CHECK_SAME(v, INT_TO_JSVAL(4));
return true;
}
END_TEST(testDebugger_debuggerObjectVsDebugMode)
BEGIN_TEST(testDebugger_newScriptHook)
{
// Test that top-level indirect eval fires the newScript hook.
CHECK(JS_DefineDebuggerObject(cx, global));
JS::RootedObject g(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook));
CHECK(g);
{
JSAutoCompartment ae(cx, g);
CHECK(JS_InitStandardClasses(cx, g));
}
JS::RootedObject gWrapper(cx, g);
CHECK(JS_WrapObject(cx, &gWrapper));
JS::RootedValue v(cx, JS::ObjectValue(*gWrapper));
CHECK(JS_SetProperty(cx, global, "g", v));
EXEC("var dbg = Debugger(g);\n"
"var hits = 0;\n"
"dbg.onNewScript = function (s) {\n"
" hits += Number(s instanceof Debugger.Script);\n"
"};\n");
// Since g is a debuggee, g.eval should trigger newScript, regardless of
// what scope object we use to enter the compartment.
//
// Scripts are associated with the global where they're compiled, so we
// deliver them only to debuggers that are watching that particular global.
//
return testIndirectEval(g, "Math.abs(0)");
}
bool testIndirectEval(JS::HandleObject scope, const char *code)
{
EXEC("hits = 0;");
{
JSAutoCompartment ae(cx, scope);
JSString *codestr = JS_NewStringCopyZ(cx, code);
CHECK(codestr);
JS::RootedValue arg(cx, JS::StringValue(codestr));
JS::RootedValue v(cx);
CHECK(JS_CallFunctionName(cx, scope, "eval", arg, &v));
}
JS::RootedValue hitsv(cx);
EVAL("hits", hitsv.address());
CHECK_SAME(hitsv, INT_TO_JSVAL(1));
return true;
}
END_TEST(testDebugger_newScriptHook)
BEGIN_TEST(testDebugger_singleStepThrow)
{
CHECK(JS_SetDebugModeForCompartment(cx, cx->compartment(), true));
CHECK(JS_SetInterrupt(rt, onStep, nullptr));
CHECK(JS_DefineFunction(cx, global, "setStepMode", setStepMode, 0, 0));
EXEC("var e;\n"
"setStepMode();\n"
"function f() { throw 0; }\n"
"try { f(); }\n"
"catch (x) { e = x; }\n");
return true;
}
static bool
setStepMode(JSContext *cx, unsigned argc, jsval *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
NonBuiltinScriptFrameIter iter(cx);
if (!JS_SetSingleStepMode(cx, iter.script(), true))
return false;
args.rval().set(UndefinedValue());
return true;
}
static JSTrapStatus
onStep(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure)
{
return JSTRAP_CONTINUE;
}
END_TEST(testDebugger_singleStepThrow)