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)
|