Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / jsd / jsd_val.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is mozilla.org code.
16  *
17  * The Initial Developer of the Original Code is
18  * Netscape Communications Corporation.
19  * Portions created by the Initial Developer are Copyright (C) 1998
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *
24  * Alternatively, the contents of this file may be used under the terms of
25  * either the GNU General Public License Version 2 or later (the "GPL"), or
26  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27  * in which case the provisions of the GPL or the LGPL are applicable instead
28  * of those above. If you wish to allow use of your version of this file only
29  * under the terms of either the GPL or the LGPL, and not to allow others to
30  * use your version of this file under the terms of the MPL, indicate your
31  * decision by deleting the provisions above and replace them with the notice
32  * and other provisions required by the GPL or the LGPL. If you do not delete
33  * the provisions above, a recipient may use your version of this file under
34  * the terms of any one of the MPL, the GPL or the LGPL.
35  *
36  * ***** END LICENSE BLOCK ***** */
37
38 /*
39  * JavaScript Debugging support - Value and Property support
40  */
41
42 #include "jsd.h"
43 #include "jsapi.h"
44 #include "jsfriendapi.h"
45
46 #ifdef DEBUG
47 void JSD_ASSERT_VALID_VALUE(JSDValue* jsdval)
48 {
49     JS_ASSERT(jsdval);
50     JS_ASSERT(jsdval->nref > 0);
51     if(!JS_CLIST_IS_EMPTY(&jsdval->props))
52     {
53         JS_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS));
54         JS_ASSERT(JSVAL_IS_OBJECT(jsdval->val));
55     }
56
57     if(jsdval->proto)
58     {
59         JS_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PROTO));
60         JS_ASSERT(jsdval->proto->nref > 0);
61     }
62     if(jsdval->parent)
63     {
64         JS_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_PARENT));
65         JS_ASSERT(jsdval->parent->nref > 0);
66     }
67     if(jsdval->ctor)
68     {
69         JS_ASSERT(CHECK_BIT_FLAG(jsdval->flags, GOT_CTOR));
70         JS_ASSERT(jsdval->ctor->nref > 0);
71     }
72 }
73
74 void JSD_ASSERT_VALID_PROPERTY(JSDProperty* jsdprop)
75 {
76     JS_ASSERT(jsdprop);
77     JS_ASSERT(jsdprop->name);
78     JS_ASSERT(jsdprop->name->nref > 0);
79     JS_ASSERT(jsdprop->val);
80     JS_ASSERT(jsdprop->val->nref > 0);
81     if(jsdprop->alias)
82         JS_ASSERT(jsdprop->alias->nref > 0);
83 }
84 #endif
85
86
87 JSBool
88 jsd_IsValueObject(JSDContext* jsdc, JSDValue* jsdval)
89 {
90     return JSVAL_IS_OBJECT(jsdval->val);
91 }
92
93 JSBool
94 jsd_IsValueNumber(JSDContext* jsdc, JSDValue* jsdval)
95 {
96     return JSVAL_IS_NUMBER(jsdval->val);
97 }
98
99 JSBool
100 jsd_IsValueInt(JSDContext* jsdc, JSDValue* jsdval)
101 {
102     return JSVAL_IS_INT(jsdval->val);
103 }
104
105 JSBool
106 jsd_IsValueDouble(JSDContext* jsdc, JSDValue* jsdval)
107 {
108     return JSVAL_IS_DOUBLE(jsdval->val);
109 }
110
111 JSBool
112 jsd_IsValueString(JSDContext* jsdc, JSDValue* jsdval)
113 {
114     return JSVAL_IS_STRING(jsdval->val);
115 }
116
117 JSBool
118 jsd_IsValueBoolean(JSDContext* jsdc, JSDValue* jsdval)
119 {
120     return JSVAL_IS_BOOLEAN(jsdval->val);
121 }
122
123 JSBool
124 jsd_IsValueNull(JSDContext* jsdc, JSDValue* jsdval)
125 {
126     return JSVAL_IS_NULL(jsdval->val);
127 }
128
129 JSBool
130 jsd_IsValueVoid(JSDContext* jsdc, JSDValue* jsdval)
131 {
132     return JSVAL_IS_VOID(jsdval->val);
133 }
134
135 JSBool
136 jsd_IsValuePrimitive(JSDContext* jsdc, JSDValue* jsdval)
137 {
138     return JSVAL_IS_PRIMITIVE(jsdval->val);
139 }
140
141 JSBool
142 jsd_IsValueFunction(JSDContext* jsdc, JSDValue* jsdval)
143 {
144     return !JSVAL_IS_PRIMITIVE(jsdval->val) &&
145            JS_ObjectIsCallable(jsdc->dumbContext, JSVAL_TO_OBJECT(jsdval->val));
146 }
147
148 JSBool
149 jsd_IsValueNative(JSDContext* jsdc, JSDValue* jsdval)
150 {
151     JSContext* cx = jsdc->dumbContext;
152     JSFunction* fun;
153     JSExceptionState* exceptionState;
154     JSCrossCompartmentCall *call = NULL;
155
156     if(jsd_IsValueFunction(jsdc, jsdval))
157     {
158         JSBool ok = JS_FALSE;
159         JS_BeginRequest(cx);
160         call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, JSVAL_TO_OBJECT(jsdval->val));
161         if(!call) {
162             JS_EndRequest(cx);
163
164             return JS_FALSE;
165         }
166
167         exceptionState = JS_SaveExceptionState(cx);
168         fun = JSD_GetValueFunction(jsdc, jsdval);
169         JS_RestoreExceptionState(cx, exceptionState);
170         if(fun)
171             ok = JS_GetFunctionScript(cx, fun) ? JS_FALSE : JS_TRUE;
172         JS_LeaveCrossCompartmentCall(call);
173         JS_EndRequest(cx);
174         JS_ASSERT(fun);
175         return ok;
176     }
177     return !JSVAL_IS_PRIMITIVE(jsdval->val);
178 }
179
180 /***************************************************************************/
181
182 JSBool
183 jsd_GetValueBoolean(JSDContext* jsdc, JSDValue* jsdval)
184 {
185     jsval val = jsdval->val;
186     if(!JSVAL_IS_BOOLEAN(val))
187         return JS_FALSE;
188     return JSVAL_TO_BOOLEAN(val);
189 }
190
191 int32
192 jsd_GetValueInt(JSDContext* jsdc, JSDValue* jsdval)
193 {
194     jsval val = jsdval->val;
195     if(!JSVAL_IS_INT(val))
196         return 0;
197     return JSVAL_TO_INT(val);
198 }
199
200 jsdouble
201 jsd_GetValueDouble(JSDContext* jsdc, JSDValue* jsdval)
202 {
203     if(!JSVAL_IS_DOUBLE(jsdval->val))
204         return 0;
205     return JSVAL_TO_DOUBLE(jsdval->val);
206 }
207
208 JSString*
209 jsd_GetValueString(JSDContext* jsdc, JSDValue* jsdval)
210 {
211     JSContext* cx = jsdc->dumbContext;
212     JSExceptionState* exceptionState;
213     JSCrossCompartmentCall *call = NULL;
214     jsval stringval;
215     JSString *string;
216     JSBool needWrap;
217     JSObject *scopeObj;
218
219     if(jsdval->string)
220         return jsdval->string;
221
222     /* Reuse the string without copying or re-rooting it */
223     if(JSVAL_IS_STRING(jsdval->val)) {
224         jsdval->string = JSVAL_TO_STRING(jsdval->val);
225         return jsdval->string;
226     }
227
228     JS_BeginRequest(cx);
229
230     /* Objects call JS_ValueToString in their own compartment. */
231     scopeObj = JSVAL_IS_OBJECT(jsdval->val) ? JSVAL_TO_OBJECT(jsdval->val) : jsdc->glob;
232     call = JS_EnterCrossCompartmentCall(cx, scopeObj);
233     if(!call) {
234         JS_EndRequest(cx);
235         return NULL;
236     }
237     exceptionState = JS_SaveExceptionState(cx);
238
239     string = JS_ValueToString(cx, jsdval->val);
240
241     JS_RestoreExceptionState(cx, exceptionState);
242     JS_LeaveCrossCompartmentCall(call);
243
244     if(string) {
245         stringval = STRING_TO_JSVAL(string);
246         call = JS_EnterCrossCompartmentCall(cx, jsdc->glob);
247     }
248     if(!string || !call || !JS_WrapValue(cx, &stringval)) {
249         if(call)
250             JS_LeaveCrossCompartmentCall(call);
251         JS_EndRequest(cx);
252         return NULL;
253     }
254
255     jsdval->string = JSVAL_TO_STRING(stringval);
256     if(!JS_AddNamedStringRoot(cx, &jsdval->string, "ValueString"))
257         jsdval->string = NULL;
258
259     JS_LeaveCrossCompartmentCall(call);
260     JS_EndRequest(cx);
261
262     return jsdval->string;
263 }
264
265 JSString*
266 jsd_GetValueFunctionId(JSDContext* jsdc, JSDValue* jsdval)
267 {
268     JSContext* cx = jsdc->dumbContext;
269     JSFunction* fun;
270     JSExceptionState* exceptionState;
271     JSCrossCompartmentCall *call = NULL;
272
273     if(!jsdval->funName && jsd_IsValueFunction(jsdc, jsdval))
274     {
275         JS_BeginRequest(cx);
276
277         call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, JSVAL_TO_OBJECT(jsdval->val));
278         if(!call) {
279             JS_EndRequest(cx);
280
281             return NULL;
282         }
283
284         exceptionState = JS_SaveExceptionState(cx);
285         fun = JSD_GetValueFunction(jsdc, jsdval);
286         JS_RestoreExceptionState(cx, exceptionState);
287         JS_LeaveCrossCompartmentCall(call);
288         JS_EndRequest(cx);
289         if(!fun)
290             return NULL;
291         jsdval->funName = JS_GetFunctionId(fun);
292
293         /* For compatibility we return "anonymous", not an empty string here. */
294         if (!jsdval->funName)
295             jsdval->funName = JS_GetAnonymousString(jsdc->jsrt);
296     }
297     return jsdval->funName;
298 }
299
300 /***************************************************************************/
301
302 /*
303  * Create a new JSD value referring to a jsval. Copy string values into the
304  * JSD compartment. Leave all other GCTHINGs in their native compartments
305  * and access them through cross-compartment calls.
306  */
307 JSDValue*
308 jsd_NewValue(JSDContext* jsdc, jsval val)
309 {
310     JSDValue* jsdval;
311     JSCrossCompartmentCall *call = NULL;
312
313     if(!(jsdval = (JSDValue*) calloc(1, sizeof(JSDValue))))
314         return NULL;
315
316     if(JSVAL_IS_GCTHING(val))
317     {
318         JSBool ok;
319         JS_BeginRequest(jsdc->dumbContext);
320
321         call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, jsdc->glob);
322         if(!call) {
323             JS_EndRequest(jsdc->dumbContext);
324             free(jsdval);
325             return NULL;
326         }
327
328         ok = JS_AddNamedValueRoot(jsdc->dumbContext, &jsdval->val, "JSDValue");
329         if(ok && JSVAL_IS_STRING(val)) {
330             if(!JS_WrapValue(jsdc->dumbContext, &val)) {
331                 ok = JS_FALSE;
332             }
333         }
334
335         JS_LeaveCrossCompartmentCall(call);
336         JS_EndRequest(jsdc->dumbContext);
337         if(!ok)
338         {
339             free(jsdval);
340             return NULL;
341         }
342     }
343     jsdval->val  = val;
344     jsdval->nref = 1;
345     JS_INIT_CLIST(&jsdval->props);
346
347     return jsdval;
348 }
349
350 void
351 jsd_DropValue(JSDContext* jsdc, JSDValue* jsdval)
352 {
353     JSCrossCompartmentCall *call = NULL;
354
355     JS_ASSERT(jsdval->nref > 0);
356     if(0 == --jsdval->nref)
357     {
358         jsd_RefreshValue(jsdc, jsdval);
359         if(JSVAL_IS_GCTHING(jsdval->val))
360         {
361             JS_BeginRequest(jsdc->dumbContext);
362             call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, jsdc->glob);
363             if(!call) {
364                 JS_EndRequest(jsdc->dumbContext);
365
366                 return;
367             }
368
369             JS_RemoveValueRoot(jsdc->dumbContext, &jsdval->val);
370             JS_LeaveCrossCompartmentCall(call);
371             JS_EndRequest(jsdc->dumbContext);
372         }
373         free(jsdval);
374     }
375 }
376
377 jsval
378 jsd_GetValueWrappedJSVal(JSDContext* jsdc, JSDValue* jsdval)
379 {
380     JSObject* obj;
381     JSContext* cx;
382     jsval val = jsdval->val;
383     if (!JSVAL_IS_PRIMITIVE(val)) {
384         cx = JSD_GetDefaultJSContext(jsdc);
385         obj = js_ObjectToOuterObject(cx, JSVAL_TO_OBJECT(val));
386         if (!obj)
387         {
388             JS_ClearPendingException(cx);
389             val = JSVAL_NULL;
390         }
391         else
392             val = OBJECT_TO_JSVAL(obj);
393     }
394     
395     return val;
396 }
397
398 static JSDProperty* _newProperty(JSDContext* jsdc, JSPropertyDesc* pd,
399                                  uintN additionalFlags)
400 {
401     JSDProperty* jsdprop;
402
403     if(!(jsdprop = (JSDProperty*) calloc(1, sizeof(JSDProperty))))
404         return NULL;
405
406     JS_INIT_CLIST(&jsdprop->links);
407     jsdprop->nref = 1;
408     jsdprop->flags = pd->flags | additionalFlags;
409     jsdprop->slot = pd->slot;
410
411     if(!(jsdprop->name = jsd_NewValue(jsdc, pd->id)))
412         goto new_prop_fail;
413
414     if(!(jsdprop->val = jsd_NewValue(jsdc, pd->value)))
415         goto new_prop_fail;
416
417     if((jsdprop->flags & JSDPD_ALIAS) &&
418        !(jsdprop->alias = jsd_NewValue(jsdc, pd->alias)))
419         goto new_prop_fail;
420
421     return jsdprop;
422 new_prop_fail:
423     jsd_DropProperty(jsdc, jsdprop);
424     return NULL;
425 }
426
427 static void _freeProps(JSDContext* jsdc, JSDValue* jsdval)
428 {
429     JSDProperty* jsdprop;
430
431     while(jsdprop = (JSDProperty*)jsdval->props.next,
432           jsdprop != (JSDProperty*)&jsdval->props)
433     {
434         JS_REMOVE_AND_INIT_LINK(&jsdprop->links);
435         jsd_DropProperty(jsdc, jsdprop);
436     }
437     JS_ASSERT(JS_CLIST_IS_EMPTY(&jsdval->props));
438     CLEAR_BIT_FLAG(jsdval->flags, GOT_PROPS);
439 }
440
441 static JSBool _buildProps(JSDContext* jsdc, JSDValue* jsdval)
442 {
443     JSContext* cx = jsdc->dumbContext;
444     JSObject *obj;
445     JSPropertyDescArray pda;
446     uintN i;
447     JSCrossCompartmentCall *call = NULL;
448
449     JS_ASSERT(JS_CLIST_IS_EMPTY(&jsdval->props));
450     JS_ASSERT(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS)));
451     JS_ASSERT(JSVAL_IS_OBJECT(jsdval->val));
452
453     if(JSVAL_IS_PRIMITIVE(jsdval->val))
454         return JS_FALSE;
455
456     obj = JSVAL_TO_OBJECT(jsdval->val);
457
458     JS_BeginRequest(cx);
459     call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, obj);
460     if(!call)
461     {
462         JS_EndRequest(jsdc->dumbContext);
463         return JS_FALSE;
464     }
465
466     if(!JS_GetPropertyDescArray(cx, obj, &pda))
467     {
468         JS_EndRequest(cx);
469         JS_LeaveCrossCompartmentCall(call);
470         return JS_FALSE;
471     }
472
473     for(i = 0; i < pda.length; i++)
474     {
475         JSDProperty* prop = _newProperty(jsdc, &pda.array[i], 0);
476         if(!prop)
477         {
478             _freeProps(jsdc, jsdval);
479             break;
480         }
481         JS_APPEND_LINK(&prop->links, &jsdval->props);
482     }
483     JS_PutPropertyDescArray(cx, &pda);
484     JS_LeaveCrossCompartmentCall(call);
485     JS_EndRequest(cx);
486     SET_BIT_FLAG(jsdval->flags, GOT_PROPS);
487     return !JS_CLIST_IS_EMPTY(&jsdval->props);
488 }
489
490 #undef  DROP_CLEAR_VALUE
491 #define DROP_CLEAR_VALUE(jsdc, x) if(x){jsd_DropValue(jsdc,x); x = NULL;}
492
493 void
494 jsd_RefreshValue(JSDContext* jsdc, JSDValue* jsdval)
495 {
496     JSContext* cx = jsdc->dumbContext;
497     JSCrossCompartmentCall *call = NULL;
498
499     if(jsdval->string)
500     {
501         /* if the jsval is a string, then we didn't need to root the string */
502         if(!JSVAL_IS_STRING(jsdval->val))
503         {
504             JS_BeginRequest(cx);
505             call = JS_EnterCrossCompartmentCall(cx, jsdc->glob);
506             if(!call) {
507                 JS_EndRequest(cx);
508
509                 return;
510             }
511
512             JS_RemoveStringRoot(cx, &jsdval->string);
513             JS_LeaveCrossCompartmentCall(call);
514             JS_EndRequest(cx);
515         }
516         jsdval->string = NULL;
517     }
518
519     jsdval->funName = NULL;
520     jsdval->className = NULL;
521     DROP_CLEAR_VALUE(jsdc, jsdval->proto);
522     DROP_CLEAR_VALUE(jsdc, jsdval->parent);
523     DROP_CLEAR_VALUE(jsdc, jsdval->ctor);
524     _freeProps(jsdc, jsdval);
525     jsdval->flags = 0;
526 }
527
528 /***************************************************************************/
529
530 uintN
531 jsd_GetCountOfProperties(JSDContext* jsdc, JSDValue* jsdval)
532 {
533     JSDProperty* jsdprop;
534     uintN count = 0;
535
536     if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS)))
537         if(!_buildProps(jsdc, jsdval))
538             return 0;
539
540     for(jsdprop = (JSDProperty*)jsdval->props.next;
541         jsdprop != (JSDProperty*)&jsdval->props;
542         jsdprop = (JSDProperty*)jsdprop->links.next)
543     {
544         count++;
545     }
546     return count;
547 }
548
549 JSDProperty*
550 jsd_IterateProperties(JSDContext* jsdc, JSDValue* jsdval, JSDProperty **iterp)
551 {
552     JSDProperty* jsdprop = *iterp;
553     if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROPS)))
554     {
555         JS_ASSERT(!jsdprop);
556         if(!_buildProps(jsdc, jsdval))
557             return NULL;
558     }
559
560     if(!jsdprop)
561         jsdprop = (JSDProperty*)jsdval->props.next;
562     if(jsdprop == (JSDProperty*)&jsdval->props)
563         return NULL;
564     *iterp = (JSDProperty*)jsdprop->links.next;
565
566     JS_ASSERT(jsdprop);
567     jsdprop->nref++;
568     return jsdprop;
569 }
570
571 JSDProperty*
572 jsd_GetValueProperty(JSDContext* jsdc, JSDValue* jsdval, JSString* name)
573 {
574     JSContext* cx = jsdc->dumbContext;
575     JSDProperty* jsdprop;
576     JSDProperty* iter = NULL;
577     JSObject* obj;
578     uintN  attrs = 0;
579     JSBool found;
580     JSPropertyDesc pd;
581     const jschar * nameChars;
582     size_t nameLen;
583     jsval val, nameval;
584     jsid nameid;
585     JSCrossCompartmentCall *call = NULL;
586
587     if(!jsd_IsValueObject(jsdc, jsdval))
588         return NULL;
589
590     /* If we already have the prop, then return it */
591     while(NULL != (jsdprop = jsd_IterateProperties(jsdc, jsdval, &iter)))
592     {
593         JSString* propName = jsd_GetValueString(jsdc, jsdprop->name);
594         if(propName) {
595             intN result;
596             if (JS_CompareStrings(cx, propName, name, &result) && !result)
597                 return jsdprop;
598         }
599         JSD_DropProperty(jsdc, jsdprop);
600     }
601     /* Not found in property list, look it up explicitly */
602
603     if(!(obj = JSVAL_TO_OBJECT(jsdval->val)))
604         return NULL;
605
606     if (!(nameChars = JS_GetStringCharsZAndLength(cx, name, &nameLen)))
607         return NULL;
608
609     JS_BeginRequest(cx);
610     call = JS_EnterCrossCompartmentCall(cx, obj);
611     if(!call) {
612         JS_EndRequest(cx);
613
614         return NULL;
615     }
616
617     JS_GetUCPropertyAttributes(cx, obj, nameChars, nameLen, &attrs, &found);
618     if (!found)
619     {
620         JS_LeaveCrossCompartmentCall(call);
621         JS_EndRequest(cx);
622         return NULL;
623     }
624
625     JS_ClearPendingException(cx);
626
627     if(!JS_GetUCProperty(cx, obj, nameChars, nameLen, &val))
628     {
629         if (JS_IsExceptionPending(cx))
630         {
631             if (!JS_GetPendingException(cx, &pd.value))
632             {
633                 JS_LeaveCrossCompartmentCall(call);
634                 JS_EndRequest(cx);
635                 return NULL;
636             }
637             pd.flags = JSPD_EXCEPTION;
638         }
639         else
640         {
641             pd.flags = JSPD_ERROR;
642             pd.value = JSVAL_VOID;
643         }
644     }
645     else
646     {
647         pd.value = val;
648     }
649
650     JS_LeaveCrossCompartmentCall(call);
651     JS_EndRequest(cx);
652
653     nameval = STRING_TO_JSVAL(name);
654     if (!JS_ValueToId(cx, nameval, &nameid) ||
655         !JS_IdToValue(cx, nameid, &pd.id)) {
656         return NULL;
657     }
658
659     pd.slot = pd.spare = 0;
660     pd.alias = JSVAL_NULL;
661     pd.flags |= (attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0
662         | (attrs & JSPROP_READONLY)  ? JSPD_READONLY  : 0
663         | (attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0;
664
665     return _newProperty(jsdc, &pd, JSDPD_HINTED);
666 }
667
668 /*
669  * Retrieve a JSFunction* from a JSDValue*. This differs from
670  * JS_ValueToFunction by fully unwrapping the object first.
671  */
672 JSFunction*
673 jsd_GetValueFunction(JSDContext* jsdc, JSDValue* jsdval)
674 {
675     JSObject *obj;
676     JSFunction *fun;
677     JSCrossCompartmentCall *call = NULL;
678     if (!JSVAL_IS_OBJECT(jsdval->val))
679         return NULL;
680     if(!(obj = JSVAL_TO_OBJECT(jsdval->val)))
681         return NULL;
682     obj = JS_UnwrapObject(jsdc->dumbContext, obj);
683
684     call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, obj);
685     if (!call)
686         return NULL;
687     fun = JS_ValueToFunction(jsdc->dumbContext, OBJECT_TO_JSVAL(obj));
688     JS_LeaveCrossCompartmentCall(call);
689
690     return fun;
691 }
692
693 JSDValue*
694 jsd_GetValuePrototype(JSDContext* jsdc, JSDValue* jsdval)
695 {
696     JSCrossCompartmentCall *call = NULL;
697
698     if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PROTO)))
699     {
700         JSObject* obj;
701         JSObject* proto;
702         JS_ASSERT(!jsdval->proto);
703         SET_BIT_FLAG(jsdval->flags, GOT_PROTO);
704         if(!JSVAL_IS_OBJECT(jsdval->val))
705             return NULL;
706         if(!(obj = JSVAL_TO_OBJECT(jsdval->val)))
707             return NULL;
708         JS_BeginRequest(jsdc->dumbContext);
709         call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, obj);
710         if(!call) {
711             JS_EndRequest(jsdc->dumbContext);
712
713             return NULL;
714         }
715         proto = JS_GetPrototype(jsdc->dumbContext, obj);
716         JS_LeaveCrossCompartmentCall(call);
717         JS_EndRequest(jsdc->dumbContext);
718         if(!proto)
719             return NULL;
720         jsdval->proto = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(proto));
721     }
722     if(jsdval->proto)
723         jsdval->proto->nref++;
724     return jsdval->proto;
725 }
726
727 JSDValue*
728 jsd_GetValueParent(JSDContext* jsdc, JSDValue* jsdval)
729 {
730     JSCrossCompartmentCall *call = NULL;
731
732     if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_PARENT)))
733     {
734         JSObject* obj;
735         JSObject* parent;
736         JS_ASSERT(!jsdval->parent);
737         SET_BIT_FLAG(jsdval->flags, GOT_PARENT);
738         if(!JSVAL_IS_OBJECT(jsdval->val))
739             return NULL;
740         if(!(obj = JSVAL_TO_OBJECT(jsdval->val)))
741             return NULL;
742         JS_BeginRequest(jsdc->dumbContext);
743         call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, obj);
744         if(!call) {
745             JS_EndRequest(jsdc->dumbContext);
746
747             return NULL;
748         }
749         parent = JS_GetParent(jsdc->dumbContext,obj);
750         JS_LeaveCrossCompartmentCall(call);
751         JS_EndRequest(jsdc->dumbContext);
752         if(!parent)
753             return NULL;
754         jsdval->parent = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(parent));
755     }
756     if(jsdval->parent)
757         jsdval->parent->nref++;
758     return jsdval->parent;
759 }
760
761 JSDValue*
762 jsd_GetValueConstructor(JSDContext* jsdc, JSDValue* jsdval)
763 {
764     JSCrossCompartmentCall *call = NULL;
765
766     if(!(CHECK_BIT_FLAG(jsdval->flags, GOT_CTOR)))
767     {
768         JSObject* obj;
769         JSObject* proto;
770         JSObject* ctor;
771         JS_ASSERT(!jsdval->ctor);
772         SET_BIT_FLAG(jsdval->flags, GOT_CTOR);
773         if(!JSVAL_IS_OBJECT(jsdval->val))
774             return NULL;
775         if(!(obj = JSVAL_TO_OBJECT(jsdval->val)))
776             return NULL;
777         JS_BeginRequest(jsdc->dumbContext);
778         call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, obj);
779         if(!call) {
780             JS_EndRequest(jsdc->dumbContext);
781
782             return NULL;
783         }
784         proto = JS_GetPrototype(jsdc->dumbContext,obj);
785         if(!proto)
786         {
787             JS_LeaveCrossCompartmentCall(call);
788             JS_EndRequest(jsdc->dumbContext);
789             return NULL;
790         }
791         ctor = JS_GetConstructor(jsdc->dumbContext,proto);
792         JS_LeaveCrossCompartmentCall(call);
793         JS_EndRequest(jsdc->dumbContext);
794         if(!ctor)
795             return NULL;
796         jsdval->ctor = jsd_NewValue(jsdc, OBJECT_TO_JSVAL(ctor));
797     }
798     if(jsdval->ctor)
799         jsdval->ctor->nref++;
800     return jsdval->ctor;
801 }
802
803 const char*
804 jsd_GetValueClassName(JSDContext* jsdc, JSDValue* jsdval)
805 {
806     jsval val = jsdval->val;
807     JSCrossCompartmentCall *call = NULL;
808
809     if(!jsdval->className && JSVAL_IS_OBJECT(val))
810     {
811         JSObject* obj;
812         if(!(obj = JSVAL_TO_OBJECT(val)))
813             return NULL;
814         JS_BeginRequest(jsdc->dumbContext);
815         call = JS_EnterCrossCompartmentCall(jsdc->dumbContext, obj);
816         if(!call) {
817             JS_EndRequest(jsdc->dumbContext);
818
819             return NULL;
820         }
821         if(JS_GET_CLASS(jsdc->dumbContext, obj))
822             jsdval->className = JS_GET_CLASS(jsdc->dumbContext, obj)->name;
823         JS_LeaveCrossCompartmentCall(call);
824         JS_EndRequest(jsdc->dumbContext);
825     }
826     return jsdval->className;
827 }
828
829 JSDScript*
830 jsd_GetScriptForValue(JSDContext* jsdc, JSDValue* jsdval)
831 {
832     JSContext* cx = jsdc->dumbContext;
833     jsval val = jsdval->val;
834     JSFunction* fun = NULL;
835     JSExceptionState* exceptionState;
836     JSScript* script = NULL;
837     JSDScript* jsdscript;
838     JSCrossCompartmentCall *call = NULL;
839
840     if (!jsd_IsValueFunction(jsdc, jsdval))
841         return NULL;
842
843     JS_BeginRequest(cx);
844     call = JS_EnterCrossCompartmentCall(cx, JSVAL_TO_OBJECT(val));
845     if (!call) {
846         JS_EndRequest(cx);
847
848         return NULL;
849     }
850
851     exceptionState = JS_SaveExceptionState(cx);
852     fun = JSD_GetValueFunction(jsdc, jsdval);
853     JS_RestoreExceptionState(cx, exceptionState);
854     if (fun)
855         script = JS_GetFunctionScript(cx, fun);
856     JS_LeaveCrossCompartmentCall(call);
857     JS_EndRequest(cx);
858
859     if (!script)
860         return NULL;
861
862     JSD_LOCK_SCRIPTS(jsdc);
863     jsdscript = jsd_FindJSDScript(jsdc, script);
864     JSD_UNLOCK_SCRIPTS(jsdc);
865     return jsdscript;
866 }
867
868
869 /***************************************************************************/
870 /***************************************************************************/
871
872 JSDValue*
873 jsd_GetPropertyName(JSDContext* jsdc, JSDProperty* jsdprop)
874 {
875     jsdprop->name->nref++;
876     return jsdprop->name;
877 }
878
879 JSDValue*
880 jsd_GetPropertyValue(JSDContext* jsdc, JSDProperty* jsdprop)
881 {
882     jsdprop->val->nref++;
883     return jsdprop->val;
884 }
885
886 JSDValue*
887 jsd_GetPropertyAlias(JSDContext* jsdc, JSDProperty* jsdprop)
888 {
889     if(jsdprop->alias)
890         jsdprop->alias->nref++;
891     return jsdprop->alias;
892 }
893
894 uintN
895 jsd_GetPropertyFlags(JSDContext* jsdc, JSDProperty* jsdprop)
896 {
897     return jsdprop->flags;
898 }
899
900 uintN
901 jsd_GetPropertyVarArgSlot(JSDContext* jsdc, JSDProperty* jsdprop)
902 {
903     return jsdprop->slot;
904 }
905
906 void
907 jsd_DropProperty(JSDContext* jsdc, JSDProperty* jsdprop)
908 {
909     JS_ASSERT(jsdprop->nref > 0);
910     if(0 == --jsdprop->nref)
911     {
912         JS_ASSERT(JS_CLIST_IS_EMPTY(&jsdprop->links));
913         DROP_CLEAR_VALUE(jsdc, jsdprop->val);
914         DROP_CLEAR_VALUE(jsdc, jsdprop->name);
915         DROP_CLEAR_VALUE(jsdc, jsdprop->alias);
916         free(jsdprop);
917     }
918 }