|
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 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /* |
|
8 * JavaScript Debugging support - Value and Property support |
|
9 */ |
|
10 |
|
11 #include "jsd.h" |
|
12 #include "jsapi.h" |
|
13 #include "jsfriendapi.h" |
|
14 #include "jswrapper.h" |
|
15 #include "nsCxPusher.h" |
|
16 |
|
17 using mozilla::AutoSafeJSContext; |
|
18 |
|
19 #ifdef DEBUG |
|
20 void JSD_ASSERT_VALID_VALUE(JSDValue* jsdval) |
|
21 { |
|
22 MOZ_ASSERT(jsdval); |
|
23 MOZ_ASSERT(jsdval->nref > 0); |
|
24 if(!JS_CLIST_IS_EMPTY(&jsdval->props)) |
|
25 { |
|
26 MOZ_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS)); |
|
27 MOZ_ASSERT(!JSVAL_IS_PRIMITIVE(jsdval->val)); |
|
28 } |
|
29 |
|
30 if(jsdval->proto) |
|
31 { |
|
32 MOZ_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PROTO)); |
|
33 MOZ_ASSERT(jsdval->proto->nref > 0); |
|
34 } |
|
35 if(jsdval->parent) |
|
36 { |
|
37 MOZ_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PARENT)); |
|
38 MOZ_ASSERT(jsdval->parent->nref > 0); |
|
39 } |
|
40 if(jsdval->ctor) |
|
41 { |
|
42 MOZ_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_CTOR)); |
|
43 MOZ_ASSERT(jsdval->ctor->nref > 0); |
|
44 } |
|
45 } |
|
46 |
|
47 void JSD_ASSERT_VALID_PROPERTY(JSDProperty* jsdprop) |
|
48 { |
|
49 MOZ_ASSERT(jsdprop); |
|
50 MOZ_ASSERT(jsdprop->name); |
|
51 MOZ_ASSERT(jsdprop->name->nref > 0); |
|
52 MOZ_ASSERT(jsdprop->val); |
|
53 MOZ_ASSERT(jsdprop->val->nref > 0); |
|
54 if(jsdprop->alias) |
|
55 MOZ_ASSERT(jsdprop->alias->nref > 0); |
|
56 } |
|
57 #endif |
|
58 |
|
59 |
|
60 bool |
|
61 jsd_IsValueObject(JSDContext* jsdc, JSDValue* jsdval) |
|
62 { |
|
63 return !JSVAL_IS_PRIMITIVE(jsdval->val) || JSVAL_IS_NULL(jsdval->val); |
|
64 } |
|
65 |
|
66 bool |
|
67 jsd_IsValueNumber(JSDContext* jsdc, JSDValue* jsdval) |
|
68 { |
|
69 return JSVAL_IS_NUMBER(jsdval->val); |
|
70 } |
|
71 |
|
72 bool |
|
73 jsd_IsValueInt(JSDContext* jsdc, JSDValue* jsdval) |
|
74 { |
|
75 return JSVAL_IS_INT(jsdval->val); |
|
76 } |
|
77 |
|
78 bool |
|
79 jsd_IsValueDouble(JSDContext* jsdc, JSDValue* jsdval) |
|
80 { |
|
81 return JSVAL_IS_DOUBLE(jsdval->val); |
|
82 } |
|
83 |
|
84 bool |
|
85 jsd_IsValueString(JSDContext* jsdc, JSDValue* jsdval) |
|
86 { |
|
87 return JSVAL_IS_STRING(jsdval->val); |
|
88 } |
|
89 |
|
90 bool |
|
91 jsd_IsValueBoolean(JSDContext* jsdc, JSDValue* jsdval) |
|
92 { |
|
93 return JSVAL_IS_BOOLEAN(jsdval->val); |
|
94 } |
|
95 |
|
96 bool |
|
97 jsd_IsValueNull(JSDContext* jsdc, JSDValue* jsdval) |
|
98 { |
|
99 return JSVAL_IS_NULL(jsdval->val); |
|
100 } |
|
101 |
|
102 bool |
|
103 jsd_IsValueVoid(JSDContext* jsdc, JSDValue* jsdval) |
|
104 { |
|
105 return JSVAL_IS_VOID(jsdval->val); |
|
106 } |
|
107 |
|
108 bool |
|
109 jsd_IsValuePrimitive(JSDContext* jsdc, JSDValue* jsdval) |
|
110 { |
|
111 return JSVAL_IS_PRIMITIVE(jsdval->val); |
|
112 } |
|
113 |
|
114 bool |
|
115 jsd_IsValueFunction(JSDContext* jsdc, JSDValue* jsdval) |
|
116 { |
|
117 AutoSafeJSContext cx; // NB: Actually unused. |
|
118 return !JSVAL_IS_PRIMITIVE(jsdval->val) && |
|
119 JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(jsdval->val)); |
|
120 } |
|
121 |
|
122 bool |
|
123 jsd_IsValueNative(JSDContext* jsdc, JSDValue* jsdval) |
|
124 { |
|
125 AutoSafeJSContext cx; |
|
126 JS::RootedFunction fun(cx); |
|
127 |
|
128 if(jsd_IsValueFunction(jsdc, jsdval)) |
|
129 { |
|
130 JSAutoCompartment ac(cx, JSVAL_TO_OBJECT(jsdval->val)); |
|
131 AutoSaveExceptionState as(cx); |
|
132 bool ok = false; |
|
133 fun = JSD_GetValueFunction(jsdc, jsdval); |
|
134 if(fun) |
|
135 ok = JS_GetFunctionScript(cx, fun) ? false : true; |
|
136 MOZ_ASSERT(fun); |
|
137 return ok; |
|
138 } |
|
139 return !JSVAL_IS_PRIMITIVE(jsdval->val); |
|
140 } |
|
141 |
|
142 /***************************************************************************/ |
|
143 |
|
144 bool |
|
145 jsd_GetValueBoolean(JSDContext* jsdc, JSDValue* jsdval) |
|
146 { |
|
147 jsval val = jsdval->val; |
|
148 if(!JSVAL_IS_BOOLEAN(val)) |
|
149 return false; |
|
150 return JSVAL_TO_BOOLEAN(val); |
|
151 } |
|
152 |
|
153 int32_t |
|
154 jsd_GetValueInt(JSDContext* jsdc, JSDValue* jsdval) |
|
155 { |
|
156 jsval val = jsdval->val; |
|
157 if(!JSVAL_IS_INT(val)) |
|
158 return 0; |
|
159 return JSVAL_TO_INT(val); |
|
160 } |
|
161 |
|
162 double |
|
163 jsd_GetValueDouble(JSDContext* jsdc, JSDValue* jsdval) |
|
164 { |
|
165 if(!JSVAL_IS_DOUBLE(jsdval->val)) |
|
166 return 0; |
|
167 return JSVAL_TO_DOUBLE(jsdval->val); |
|
168 } |
|
169 |
|
170 JSString* |
|
171 jsd_GetValueString(JSDContext* jsdc, JSDValue* jsdval) |
|
172 { |
|
173 AutoSafeJSContext cx; |
|
174 JS::RootedValue stringval(cx); |
|
175 JS::RootedString string(cx); |
|
176 JS::RootedObject scopeObj(cx); |
|
177 |
|
178 if(jsdval->string) |
|
179 return jsdval->string; |
|
180 |
|
181 /* Reuse the string without copying or re-rooting it */ |
|
182 if(JSVAL_IS_STRING(jsdval->val)) { |
|
183 jsdval->string = JSVAL_TO_STRING(jsdval->val); |
|
184 return jsdval->string; |
|
185 } |
|
186 |
|
187 /* Objects call JS_ValueToString in their own compartment. */ |
|
188 scopeObj = !JSVAL_IS_PRIMITIVE(jsdval->val) ? JSVAL_TO_OBJECT(jsdval->val) : jsdc->glob; |
|
189 { |
|
190 JSAutoCompartment ac(cx, scopeObj); |
|
191 AutoSaveExceptionState as(cx); |
|
192 JS::RootedValue v(cx, jsdval->val); |
|
193 string = JS::ToString(cx, v); |
|
194 } |
|
195 |
|
196 JSAutoCompartment ac2(cx, jsdc->glob); |
|
197 if(string) { |
|
198 stringval = STRING_TO_JSVAL(string); |
|
199 } |
|
200 if(!string || !JS_WrapValue(cx, &stringval)) { |
|
201 return nullptr; |
|
202 } |
|
203 |
|
204 jsdval->string = JSVAL_TO_STRING(stringval); |
|
205 if(!JS::AddNamedStringRoot(cx, &jsdval->string, "ValueString")) |
|
206 jsdval->string = nullptr; |
|
207 |
|
208 return jsdval->string; |
|
209 } |
|
210 |
|
211 JSString* |
|
212 jsd_GetValueFunctionId(JSDContext* jsdc, JSDValue* jsdval) |
|
213 { |
|
214 AutoSafeJSContext cx; |
|
215 JS::RootedFunction fun(cx); |
|
216 |
|
217 if(!jsdval->funName && jsd_IsValueFunction(jsdc, jsdval)) |
|
218 { |
|
219 JSAutoCompartment ac(cx, JSVAL_TO_OBJECT(jsdval->val)); |
|
220 AutoSaveExceptionState as(cx); |
|
221 fun = JSD_GetValueFunction(jsdc, jsdval); |
|
222 if(!fun) |
|
223 return nullptr; |
|
224 jsdval->funName = JS_GetFunctionId(fun); |
|
225 |
|
226 /* For compatibility we return "anonymous", not an empty string here. */ |
|
227 if (!jsdval->funName) |
|
228 jsdval->funName = JS_GetAnonymousString(jsdc->jsrt); |
|
229 } |
|
230 return jsdval->funName; |
|
231 } |
|
232 |
|
233 /***************************************************************************/ |
|
234 |
|
235 /* |
|
236 * Create a new JSD value referring to a jsval. Copy string values into the |
|
237 * JSD compartment. Leave all other GCTHINGs in their native compartments |
|
238 * and access them through cross-compartment calls. |
|
239 */ |
|
240 JSDValue* |
|
241 jsd_NewValue(JSDContext* jsdc, jsval value) |
|
242 { |
|
243 JS::RootedValue val(jsdc->jsrt, value); |
|
244 AutoSafeJSContext cx; |
|
245 JSDValue* jsdval; |
|
246 |
|
247 if(!(jsdval = (JSDValue*) calloc(1, sizeof(JSDValue)))) |
|
248 return nullptr; |
|
249 |
|
250 if(JSVAL_IS_GCTHING(val)) |
|
251 { |
|
252 bool ok; |
|
253 JSAutoCompartment ac(cx, jsdc->glob); |
|
254 |
|
255 ok = JS::AddNamedValueRoot(cx, &jsdval->val, "JSDValue"); |
|
256 if(ok && JSVAL_IS_STRING(val)) { |
|
257 if(!JS_WrapValue(cx, &val)) { |
|
258 ok = false; |
|
259 } |
|
260 } |
|
261 |
|
262 if(!ok) |
|
263 { |
|
264 free(jsdval); |
|
265 return nullptr; |
|
266 } |
|
267 } |
|
268 jsdval->val = val; |
|
269 jsdval->nref = 1; |
|
270 JS_INIT_CLIST(&jsdval->props); |
|
271 |
|
272 return jsdval; |
|
273 } |
|
274 |
|
275 void |
|
276 jsd_DropValue(JSDContext* jsdc, JSDValue* jsdval) |
|
277 { |
|
278 MOZ_ASSERT(jsdval->nref > 0); |
|
279 if(0 == --jsdval->nref) |
|
280 { |
|
281 jsd_RefreshValue(jsdc, jsdval); |
|
282 if(JSVAL_IS_GCTHING(jsdval->val)) |
|
283 { |
|
284 AutoSafeJSContext cx; |
|
285 JSAutoCompartment ac(cx, jsdc->glob); |
|
286 JS::RemoveValueRoot(cx, &jsdval->val); |
|
287 } |
|
288 free(jsdval); |
|
289 } |
|
290 } |
|
291 |
|
292 jsval |
|
293 jsd_GetValueWrappedJSVal(JSDContext* jsdc, JSDValue* jsdval) |
|
294 { |
|
295 AutoSafeJSContext cx; |
|
296 JS::RootedValue val(cx, jsdval->val); |
|
297 if (!val.isPrimitive()) { |
|
298 JS::RootedObject obj(cx, &val.toObject()); |
|
299 JSAutoCompartment ac(cx, obj); |
|
300 obj = JS_ObjectToOuterObject(cx, obj); |
|
301 if (!obj) |
|
302 { |
|
303 JS_ClearPendingException(cx); |
|
304 val = JSVAL_NULL; |
|
305 } |
|
306 else |
|
307 val = JS::ObjectValue(*obj); |
|
308 } |
|
309 |
|
310 return val; |
|
311 } |
|
312 |
|
313 static JSDProperty* _newProperty(JSDContext* jsdc, JS::HandleValue propId, |
|
314 JS::HandleValue propValue, JS::HandleValue propAlias, |
|
315 uint8_t propFlags, unsigned additionalFlags) |
|
316 { |
|
317 JSDProperty* jsdprop; |
|
318 |
|
319 if(!(jsdprop = (JSDProperty*) calloc(1, sizeof(JSDProperty)))) |
|
320 return nullptr; |
|
321 |
|
322 JS_INIT_CLIST(&jsdprop->links); |
|
323 jsdprop->nref = 1; |
|
324 jsdprop->flags = propFlags | additionalFlags; |
|
325 |
|
326 if(!(jsdprop->name = jsd_NewValue(jsdc, propId))) |
|
327 goto new_prop_fail; |
|
328 |
|
329 if(!(jsdprop->val = jsd_NewValue(jsdc, propValue))) |
|
330 goto new_prop_fail; |
|
331 |
|
332 if((jsdprop->flags & JSDPD_ALIAS) && |
|
333 !(jsdprop->alias = jsd_NewValue(jsdc, propAlias))) |
|
334 goto new_prop_fail; |
|
335 |
|
336 return jsdprop; |
|
337 new_prop_fail: |
|
338 jsd_DropProperty(jsdc, jsdprop); |
|
339 return nullptr; |
|
340 } |
|
341 |
|
342 static void _freeProps(JSDContext* jsdc, JSDValue* jsdval) |
|
343 { |
|
344 JSDProperty* jsdprop; |
|
345 |
|
346 while(jsdprop = (JSDProperty*)jsdval->props.next, |
|
347 jsdprop != (JSDProperty*)&jsdval->props) |
|
348 { |
|
349 JS_REMOVE_AND_INIT_LINK(&jsdprop->links); |
|
350 jsd_DropProperty(jsdc, jsdprop); |
|
351 } |
|
352 MOZ_ASSERT(JS_CLIST_IS_EMPTY(&jsdval->props)); |
|
353 CLEAR_BIT_FLAG(jsdval->flags, GOT_PROPS); |
|
354 } |
|
355 |
|
356 static bool _buildProps(JSDContext* jsdc, JSDValue* jsdval) |
|
357 { |
|
358 AutoSafeJSContext cx; |
|
359 JS::RootedObject obj(cx); |
|
360 JSPropertyDescArray pda; |
|
361 unsigned i; |
|
362 |
|
363 MOZ_ASSERT(JS_CLIST_IS_EMPTY(&jsdval->props)); |
|
364 MOZ_ASSERT(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS))); |
|
365 MOZ_ASSERT(!JSVAL_IS_PRIMITIVE(jsdval->val)); |
|
366 |
|
367 if(JSVAL_IS_PRIMITIVE(jsdval->val)) |
|
368 return false; |
|
369 |
|
370 obj = JSVAL_TO_OBJECT(jsdval->val); |
|
371 |
|
372 JSAutoCompartment ac(cx, obj); |
|
373 |
|
374 if(!JS_GetPropertyDescArray(cx, obj, &pda)) |
|
375 { |
|
376 return false; |
|
377 } |
|
378 |
|
379 JS::RootedValue propId(cx); |
|
380 JS::RootedValue propValue(cx); |
|
381 JS::RootedValue propAlias(cx); |
|
382 uint8_t propFlags; |
|
383 for(i = 0; i < pda.length; i++) |
|
384 { |
|
385 propId = pda.array[i].id; |
|
386 propValue = pda.array[i].value; |
|
387 propAlias = pda.array[i].alias; |
|
388 propFlags = pda.array[i].flags; |
|
389 JSDProperty* prop = _newProperty(jsdc, propId, propValue, propAlias, propFlags, 0); |
|
390 if(!prop) |
|
391 { |
|
392 _freeProps(jsdc, jsdval); |
|
393 break; |
|
394 } |
|
395 JS_APPEND_LINK(&prop->links, &jsdval->props); |
|
396 } |
|
397 JS_PutPropertyDescArray(cx, &pda); |
|
398 SET_BIT_FLAG(jsdval->flags, GOT_PROPS); |
|
399 return !JS_CLIST_IS_EMPTY(&jsdval->props); |
|
400 } |
|
401 |
|
402 #undef DROP_CLEAR_VALUE |
|
403 #define DROP_CLEAR_VALUE(jsdc, x) if(x){jsd_DropValue(jsdc,x); x = nullptr;} |
|
404 |
|
405 void |
|
406 jsd_RefreshValue(JSDContext* jsdc, JSDValue* jsdval) |
|
407 { |
|
408 AutoSafeJSContext cx; |
|
409 if(jsdval->string) |
|
410 { |
|
411 /* if the jsval is a string, then we didn't need to root the string */ |
|
412 if(!JSVAL_IS_STRING(jsdval->val)) |
|
413 { |
|
414 JSAutoCompartment ac(cx, jsdc->glob); |
|
415 JS::RemoveStringRoot(cx, &jsdval->string); |
|
416 } |
|
417 jsdval->string = nullptr; |
|
418 } |
|
419 |
|
420 jsdval->funName = nullptr; |
|
421 jsdval->className = nullptr; |
|
422 DROP_CLEAR_VALUE(jsdc, jsdval->proto); |
|
423 DROP_CLEAR_VALUE(jsdc, jsdval->parent); |
|
424 DROP_CLEAR_VALUE(jsdc, jsdval->ctor); |
|
425 _freeProps(jsdc, jsdval); |
|
426 jsdval->flags = 0; |
|
427 } |
|
428 |
|
429 /***************************************************************************/ |
|
430 |
|
431 unsigned |
|
432 jsd_GetCountOfProperties(JSDContext* jsdc, JSDValue* jsdval) |
|
433 { |
|
434 JSDProperty* jsdprop; |
|
435 unsigned count = 0; |
|
436 |
|
437 if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS))) |
|
438 if(!_buildProps(jsdc, jsdval)) |
|
439 return 0; |
|
440 |
|
441 for(jsdprop = (JSDProperty*)jsdval->props.next; |
|
442 jsdprop != (JSDProperty*)&jsdval->props; |
|
443 jsdprop = (JSDProperty*)jsdprop->links.next) |
|
444 { |
|
445 count++; |
|
446 } |
|
447 return count; |
|
448 } |
|
449 |
|
450 JSDProperty* |
|
451 jsd_IterateProperties(JSDContext* jsdc, JSDValue* jsdval, JSDProperty **iterp) |
|
452 { |
|
453 JSDProperty* jsdprop = *iterp; |
|
454 if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS))) |
|
455 { |
|
456 MOZ_ASSERT(!jsdprop); |
|
457 if(!_buildProps(jsdc, jsdval)) |
|
458 return nullptr; |
|
459 } |
|
460 |
|
461 if(!jsdprop) |
|
462 jsdprop = (JSDProperty*)jsdval->props.next; |
|
463 if(jsdprop == (JSDProperty*)&jsdval->props) |
|
464 return nullptr; |
|
465 *iterp = (JSDProperty*)jsdprop->links.next; |
|
466 |
|
467 MOZ_ASSERT(jsdprop); |
|
468 jsdprop->nref++; |
|
469 return jsdprop; |
|
470 } |
|
471 |
|
472 JSDProperty* |
|
473 jsd_GetValueProperty(JSDContext* jsdc, JSDValue* jsdval, JSString* nameStr) |
|
474 { |
|
475 JS::RootedString name(jsdc->jsrt, nameStr); |
|
476 AutoSafeJSContext cx; |
|
477 JSAutoCompartment acBase(cx, jsdc->glob); |
|
478 JSDProperty* jsdprop; |
|
479 JSDProperty* iter = nullptr; |
|
480 JS::RootedObject obj(cx); |
|
481 JS::RootedValue val(cx), nameval(cx); |
|
482 JS::RootedId nameid(cx); |
|
483 JS::RootedValue propId(cx); |
|
484 JS::RootedValue propValue(cx); |
|
485 JS::RootedValue propAlias(cx); |
|
486 uint8_t propFlags; |
|
487 |
|
488 if(!jsd_IsValueObject(jsdc, jsdval)) |
|
489 return nullptr; |
|
490 |
|
491 /* If we already have the prop, then return it */ |
|
492 while(nullptr != (jsdprop = jsd_IterateProperties(jsdc, jsdval, &iter))) |
|
493 { |
|
494 JSString* propName = jsd_GetValueString(jsdc, jsdprop->name); |
|
495 if(propName) { |
|
496 int result; |
|
497 if (JS_CompareStrings(cx, propName, name, &result) && !result) |
|
498 return jsdprop; |
|
499 } |
|
500 JSD_DropProperty(jsdc, jsdprop); |
|
501 } |
|
502 /* Not found in property list, look it up explicitly */ |
|
503 |
|
504 nameval = STRING_TO_JSVAL(name); |
|
505 if(!JS_ValueToId(cx, nameval, &nameid)) |
|
506 return nullptr; |
|
507 |
|
508 if(!(obj = JSVAL_TO_OBJECT(jsdval->val))) |
|
509 return nullptr; |
|
510 |
|
511 JS::Rooted<JSPropertyDescriptor> desc(cx); |
|
512 { |
|
513 JSAutoCompartment ac(cx, obj); |
|
514 JS::RootedId id(cx, nameid); |
|
515 |
|
516 if(!JS_WrapId(cx, &id)) |
|
517 return nullptr; |
|
518 if(!JS_GetOwnPropertyDescriptorById(cx, obj, id, &desc)) |
|
519 return nullptr; |
|
520 if(!desc.object()) |
|
521 return nullptr; |
|
522 |
|
523 JS_ClearPendingException(cx); |
|
524 |
|
525 if(!JS_GetPropertyById(cx, obj, id, &val)) |
|
526 { |
|
527 if (JS_IsExceptionPending(cx)) |
|
528 { |
|
529 if (!JS_GetPendingException(cx, &propValue)) |
|
530 { |
|
531 return nullptr; |
|
532 } |
|
533 propFlags = JSPD_EXCEPTION; |
|
534 } |
|
535 else |
|
536 { |
|
537 propFlags = JSPD_ERROR; |
|
538 propValue = JSVAL_VOID; |
|
539 } |
|
540 } |
|
541 else |
|
542 { |
|
543 propValue = val; |
|
544 } |
|
545 } |
|
546 |
|
547 if (!JS_IdToValue(cx, nameid, &propId)) |
|
548 return nullptr; |
|
549 |
|
550 propAlias = JSVAL_NULL; |
|
551 propFlags |= desc.isEnumerable() ? JSPD_ENUMERATE : 0 |
|
552 | desc.isReadonly() ? JSPD_READONLY : 0 |
|
553 | desc.isPermanent() ? JSPD_PERMANENT : 0; |
|
554 |
|
555 return _newProperty(jsdc, propId, propValue, propAlias, propFlags, JSDPD_HINTED); |
|
556 } |
|
557 |
|
558 /* |
|
559 * Retrieve a JSFunction* from a JSDValue*. This differs from |
|
560 * JS_ValueToFunction by fully unwrapping the object first. |
|
561 */ |
|
562 JSFunction* |
|
563 jsd_GetValueFunction(JSDContext* jsdc, JSDValue* jsdval) |
|
564 { |
|
565 AutoSafeJSContext cx; |
|
566 |
|
567 JS::RootedObject obj(cx); |
|
568 JS::RootedFunction fun(cx); |
|
569 |
|
570 if (JSVAL_IS_PRIMITIVE(jsdval->val)) |
|
571 return nullptr; |
|
572 |
|
573 obj = js::UncheckedUnwrap(JSVAL_TO_OBJECT(jsdval->val)); |
|
574 JSAutoCompartment ac(cx, obj); |
|
575 JS::RootedValue funval(cx, JS::ObjectValue(*obj)); |
|
576 fun = JS_ValueToFunction(cx, funval); |
|
577 |
|
578 return fun; |
|
579 } |
|
580 |
|
581 JSDValue* |
|
582 jsd_GetValuePrototype(JSDContext* jsdc, JSDValue* jsdval) |
|
583 { |
|
584 AutoSafeJSContext cx; |
|
585 if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROTO))) |
|
586 { |
|
587 JS::RootedObject obj(cx); |
|
588 JS::RootedObject proto(cx); |
|
589 MOZ_ASSERT(!jsdval->proto); |
|
590 SET_BIT_FLAG(jsdval->flags, GOT_PROTO); |
|
591 if(JSVAL_IS_PRIMITIVE(jsdval->val)) |
|
592 return nullptr; |
|
593 obj = JSVAL_TO_OBJECT(jsdval->val); |
|
594 if(!JS_GetPrototype(cx, obj, &proto)) |
|
595 return nullptr; |
|
596 if(!proto) |
|
597 return nullptr; |
|
598 jsdval->proto = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(proto)); |
|
599 } |
|
600 if(jsdval->proto) |
|
601 jsdval->proto->nref++; |
|
602 return jsdval->proto; |
|
603 } |
|
604 |
|
605 JSDValue* |
|
606 jsd_GetValueParent(JSDContext* jsdc, JSDValue* jsdval) |
|
607 { |
|
608 if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PARENT))) |
|
609 { |
|
610 AutoSafeJSContext cx; |
|
611 JS::RootedObject obj(cx); |
|
612 JS::RootedObject parent(cx); |
|
613 MOZ_ASSERT(!jsdval->parent); |
|
614 SET_BIT_FLAG(jsdval->flags, GOT_PARENT); |
|
615 if(JSVAL_IS_PRIMITIVE(jsdval->val)) |
|
616 return nullptr; |
|
617 obj = JSVAL_TO_OBJECT(jsdval->val); |
|
618 { |
|
619 JSAutoCompartment ac(cx, obj); |
|
620 parent = JS_GetParentOrScopeChain(cx, obj); |
|
621 } |
|
622 if(!parent) |
|
623 return nullptr; |
|
624 jsdval->parent = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(parent)); |
|
625 } |
|
626 if(jsdval->parent) |
|
627 jsdval->parent->nref++; |
|
628 return jsdval->parent; |
|
629 } |
|
630 |
|
631 JSDValue* |
|
632 jsd_GetValueConstructor(JSDContext* jsdc, JSDValue* jsdval) |
|
633 { |
|
634 if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_CTOR))) |
|
635 { |
|
636 AutoSafeJSContext cx; |
|
637 JS::RootedObject obj(cx); |
|
638 JS::RootedObject proto(cx); |
|
639 JS::RootedObject ctor(cx); |
|
640 MOZ_ASSERT(!jsdval->ctor); |
|
641 SET_BIT_FLAG(jsdval->flags, GOT_CTOR); |
|
642 if(JSVAL_IS_PRIMITIVE(jsdval->val)) |
|
643 return nullptr; |
|
644 obj = JSVAL_TO_OBJECT(jsdval->val); |
|
645 if(!JS_GetPrototype(cx, obj, &proto)) |
|
646 return nullptr; |
|
647 if(!proto) |
|
648 return nullptr; |
|
649 { |
|
650 JSAutoCompartment ac(cx, obj); |
|
651 ctor = JS_GetConstructor(cx, proto); |
|
652 } |
|
653 if(!ctor) |
|
654 return nullptr; |
|
655 jsdval->ctor = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(ctor)); |
|
656 } |
|
657 if(jsdval->ctor) |
|
658 jsdval->ctor->nref++; |
|
659 return jsdval->ctor; |
|
660 } |
|
661 |
|
662 const char* |
|
663 jsd_GetValueClassName(JSDContext* jsdc, JSDValue* jsdval) |
|
664 { |
|
665 jsval val = jsdval->val; |
|
666 if(!jsdval->className && !JSVAL_IS_PRIMITIVE(val)) |
|
667 { |
|
668 JS::RootedObject obj(jsdc->jsrt, JSVAL_TO_OBJECT(val)); |
|
669 AutoSafeJSContext cx; |
|
670 JSAutoCompartment ac(cx, obj); |
|
671 jsdval->className = JS_GetDebugClassName(obj); |
|
672 } |
|
673 return jsdval->className; |
|
674 } |
|
675 |
|
676 JSDScript* |
|
677 jsd_GetScriptForValue(JSDContext* jsdc, JSDValue* jsdval) |
|
678 { |
|
679 AutoSafeJSContext cx; |
|
680 JS::RootedValue val(cx, jsdval->val); |
|
681 JS::RootedScript script(cx); |
|
682 JSDScript* jsdscript; |
|
683 |
|
684 if (!jsd_IsValueFunction(jsdc, jsdval)) |
|
685 return nullptr; |
|
686 |
|
687 { |
|
688 JSAutoCompartment ac(cx, JSVAL_TO_OBJECT(val)); |
|
689 AutoSaveExceptionState as(cx); |
|
690 JS::RootedFunction fun(cx, JSD_GetValueFunction(jsdc, jsdval)); |
|
691 if (fun) |
|
692 script = JS_GetFunctionScript(cx, fun); |
|
693 } |
|
694 |
|
695 if (!script) |
|
696 return nullptr; |
|
697 |
|
698 JSD_LOCK_SCRIPTS(jsdc); |
|
699 jsdscript = jsd_FindJSDScript(jsdc, script); |
|
700 JSD_UNLOCK_SCRIPTS(jsdc); |
|
701 return jsdscript; |
|
702 } |
|
703 |
|
704 |
|
705 /***************************************************************************/ |
|
706 /***************************************************************************/ |
|
707 |
|
708 JSDValue* |
|
709 jsd_GetPropertyName(JSDContext* jsdc, JSDProperty* jsdprop) |
|
710 { |
|
711 jsdprop->name->nref++; |
|
712 return jsdprop->name; |
|
713 } |
|
714 |
|
715 JSDValue* |
|
716 jsd_GetPropertyValue(JSDContext* jsdc, JSDProperty* jsdprop) |
|
717 { |
|
718 jsdprop->val->nref++; |
|
719 return jsdprop->val; |
|
720 } |
|
721 |
|
722 JSDValue* |
|
723 jsd_GetPropertyAlias(JSDContext* jsdc, JSDProperty* jsdprop) |
|
724 { |
|
725 if(jsdprop->alias) |
|
726 jsdprop->alias->nref++; |
|
727 return jsdprop->alias; |
|
728 } |
|
729 |
|
730 unsigned |
|
731 jsd_GetPropertyFlags(JSDContext* jsdc, JSDProperty* jsdprop) |
|
732 { |
|
733 return jsdprop->flags; |
|
734 } |
|
735 |
|
736 void |
|
737 jsd_DropProperty(JSDContext* jsdc, JSDProperty* jsdprop) |
|
738 { |
|
739 MOZ_ASSERT(jsdprop->nref > 0); |
|
740 if(0 == --jsdprop->nref) |
|
741 { |
|
742 MOZ_ASSERT(JS_CLIST_IS_EMPTY(&jsdprop->links)); |
|
743 DROP_CLEAR_VALUE(jsdc, jsdprop->val); |
|
744 DROP_CLEAR_VALUE(jsdc, jsdprop->name); |
|
745 DROP_CLEAR_VALUE(jsdc, jsdprop->alias); |
|
746 free(jsdprop); |
|
747 } |
|
748 } |