js/src/jsapi-tests/testDebugger.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:8c58ce292ba1
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/. */
7
8 #include "jscntxt.h"
9
10 #include "js/OldDebugAPI.h"
11 #include "jsapi-tests/tests.h"
12
13 using namespace js;
14
15 static int callCounts[2] = {0, 0};
16
17 static void *
18 callCountHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, bool before,
19 bool *ok, void *closure)
20 {
21 callCounts[before]++;
22
23 JS::RootedValue thisv(cx);
24 frame.getThisValue(cx, &thisv); // assert if fp is incomplete
25
26 return cx; // any non-null value causes the hook to be called again after
27 }
28
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)
42
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 }
55
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)
81
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 }
94
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)
116
117 static bool calledThrowHook = false;
118
119 static JSTrapStatus
120 ThrowHook(JSContext *cx, JSScript *, jsbytecode *, jsval *rval, void *closure)
121 {
122 JS_ASSERT(!closure);
123 calledThrowHook = true;
124
125 JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
126
127 char text[] = "new Error()";
128 JS::RootedValue _(cx);
129 JS_EvaluateScript(cx, global, text, strlen(text), "", 0, &_);
130
131 return JSTRAP_CONTINUE;
132 }
133
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)
150
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);
156
157 {
158 JSAutoCompartment ae(cx, debuggee);
159 CHECK(JS_SetDebugMode(cx, true));
160 CHECK(JS_InitStandardClasses(cx, debuggee));
161 }
162
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));
167
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);
175
176 {
177 JSAutoCompartment ae(cx, debuggee);
178 CHECK(JS_SetDebugMode(cx, false));
179 }
180
181 EVAL("debuggee.eval('debugger; debugger; debugger;');\n"
182 "hits;\n",
183 &v);
184 CHECK_SAME(v, INT_TO_JSVAL(4));
185
186 return true;
187 }
188 END_TEST(testDebugger_debuggerObjectVsDebugMode)
189
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 }
200
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));
205
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");
211
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 }
220
221 bool testIndirectEval(JS::HandleObject scope, const char *code)
222 {
223 EXEC("hits = 0;");
224
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 }
233
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)
240
241 BEGIN_TEST(testDebugger_singleStepThrow)
242 {
243 CHECK(JS_SetDebugModeForCompartment(cx, cx->compartment(), true));
244 CHECK(JS_SetInterrupt(rt, onStep, nullptr));
245
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 }
254
255 static bool
256 setStepMode(JSContext *cx, unsigned argc, jsval *vp)
257 {
258 CallArgs args = CallArgsFromVp(argc, vp);
259
260 NonBuiltinScriptFrameIter iter(cx);
261 JS::RootedScript script(cx, iter.script());
262 if (!JS_SetSingleStepMode(cx, script, true))
263 return false;
264
265 args.rval().set(UndefinedValue());
266 return true;
267 }
268
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