~centralelyon2010/inkscape/imagelinks2

« back to all changes in this revision

Viewing changes to src/dom/js/jsobj.c

  • Committer: mental
  • Date: 2006-01-16 02:36:01 UTC
  • Revision ID: mental@users.sourceforge.net-20060116023601-wkr0h7edl5veyudq
moving trunk for module inkscape

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 
2
 *
 
3
 * ***** BEGIN LICENSE BLOCK *****
 
4
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 
5
 *
 
6
 * The contents of this file are subject to the Mozilla Public License Version
 
7
 * 1.1 (the "License"); you may not use this file except in compliance with
 
8
 * the License. You may obtain a copy of the License at
 
9
 * http://www.mozilla.org/MPL/
 
10
 *
 
11
 * Software distributed under the License is distributed on an "AS IS" basis,
 
12
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
13
 * for the specific language governing rights and limitations under the
 
14
 * License.
 
15
 *
 
16
 * The Original Code is Mozilla Communicator client code, released
 
17
 * March 31, 1998.
 
18
 *
 
19
 * The Initial Developer of the Original Code is
 
20
 * Netscape Communications Corporation.
 
21
 * Portions created by the Initial Developer are Copyright (C) 1998
 
22
 * the Initial Developer. All Rights Reserved.
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * Alternatively, the contents of this file may be used under the terms of
 
27
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 
28
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
29
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
30
 * of those above. If you wish to allow use of your version of this file only
 
31
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
32
 * use your version of this file under the terms of the MPL, indicate your
 
33
 * decision by deleting the provisions above and replace them with the notice
 
34
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
35
 * the provisions above, a recipient may use your version of this file under
 
36
 * the terms of any one of the MPL, the GPL or the LGPL.
 
37
 *
 
38
 * ***** END LICENSE BLOCK ***** */
 
39
 
 
40
/*
 
41
 * JS object implementation.
 
42
 */
 
43
#include "jsstddef.h"
 
44
#include <stdlib.h>
 
45
#include <string.h>
 
46
#include "jstypes.h"
 
47
#include "jsarena.h" /* Added by JSIFY */
 
48
#include "jsutil.h" /* Added by JSIFY */
 
49
#include "jshash.h" /* Added by JSIFY */
 
50
#include "jsdhash.h"
 
51
#include "jsprf.h"
 
52
#include "jsapi.h"
 
53
#include "jsarray.h"
 
54
#include "jsatom.h"
 
55
#include "jsbool.h"
 
56
#include "jscntxt.h"
 
57
#include "jsconfig.h"
 
58
#include "jsfun.h"
 
59
#include "jsgc.h"
 
60
#include "jsinterp.h"
 
61
#include "jslock.h"
 
62
#include "jsnum.h"
 
63
#include "jsobj.h"
 
64
#include "jsscope.h"
 
65
#include "jsscript.h"
 
66
#include "jsstr.h"
 
67
#include "jsopcode.h"
 
68
 
 
69
#include "jsdbgapi.h"   /* whether or not JS_HAS_OBJ_WATCHPOINT */
 
70
 
 
71
#ifdef JS_THREADSAFE
 
72
#define NATIVE_DROP_PROPERTY js_DropProperty
 
73
 
 
74
extern void
 
75
js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
 
76
#else
 
77
#define NATIVE_DROP_PROPERTY NULL
 
78
#endif
 
79
 
 
80
#ifdef XP_MAC
 
81
#pragma export on
 
82
#endif
 
83
 
 
84
JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
 
85
    js_NewObjectMap,        js_DestroyObjectMap,
 
86
#if defined JS_THREADSAFE && defined DEBUG
 
87
    _js_LookupProperty,     js_DefineProperty,
 
88
#else
 
89
    js_LookupProperty,      js_DefineProperty,
 
90
#endif
 
91
    js_GetProperty,         js_SetProperty,
 
92
    js_GetAttributes,       js_SetAttributes,
 
93
    js_DeleteProperty,      js_DefaultValue,
 
94
    js_Enumerate,           js_CheckAccess,
 
95
    NULL,                   NATIVE_DROP_PROPERTY,
 
96
    js_Call,                js_Construct,
 
97
    NULL,                   js_HasInstance,
 
98
    js_SetProtoOrParent,    js_SetProtoOrParent,
 
99
    js_Mark,                js_Clear,
 
100
    js_GetRequiredSlot,     js_SetRequiredSlot
 
101
};
 
102
 
 
103
#ifdef XP_MAC
 
104
#pragma export off
 
105
#endif
 
106
 
 
107
JSClass js_ObjectClass = {
 
108
    js_Object_str,
 
109
    0,
 
110
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
 
111
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
 
112
    JSCLASS_NO_OPTIONAL_MEMBERS
 
113
};
 
114
 
 
115
#if JS_HAS_OBJ_PROTO_PROP
 
116
 
 
117
static JSBool
 
118
obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
 
119
 
 
120
static JSBool
 
121
obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
 
122
 
 
123
static JSBool
 
124
obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
 
125
 
 
126
static JSPropertySpec object_props[] = {
 
127
    /* These two must come first; see object_props[slot].name usage below. */
 
128
    {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED,
 
129
                                                  obj_getSlot,  obj_setSlot},
 
130
    {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
 
131
                                                  obj_getSlot,  obj_setSlot},
 
132
    {js_count_str, 0,            JSPROP_PERMANENT,obj_getCount, obj_getCount},
 
133
    {0,0,0,0,0}
 
134
};
 
135
 
 
136
/* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
 
137
#define JSSLOT_COUNT 2
 
138
 
 
139
static JSBool
 
140
ReportStrictSlot(JSContext *cx, uint32 slot)
 
141
{
 
142
    if (slot == JSSLOT_PROTO)
 
143
        return JS_TRUE;
 
144
    return JS_ReportErrorFlagsAndNumber(cx,
 
145
                                        JSREPORT_WARNING | JSREPORT_STRICT,
 
146
                                        js_GetErrorMessage, NULL,
 
147
                                        JSMSG_DEPRECATED_USAGE,
 
148
                                        object_props[slot].name);
 
149
}
 
150
 
 
151
static JSBool
 
152
obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 
153
{
 
154
    uint32 slot;
 
155
    JSAccessMode mode;
 
156
    uintN attrs;
 
157
 
 
158
    slot = (uint32) JSVAL_TO_INT(id);
 
159
    if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
 
160
        return JS_FALSE;
 
161
    if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
 
162
        id = (jsid)cx->runtime->atomState.protoAtom;
 
163
        mode = JSACC_PROTO;
 
164
    } else {
 
165
        id = (jsid)cx->runtime->atomState.parentAtom;
 
166
        mode = JSACC_PARENT;
 
167
    }
 
168
    if (!OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, &attrs))
 
169
        return JS_FALSE;
 
170
    *vp = OBJ_GET_SLOT(cx, obj, slot);
 
171
    return JS_TRUE;
 
172
}
 
173
 
 
174
static JSBool
 
175
obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 
176
{
 
177
    JSObject *pobj;
 
178
    uint32 slot;
 
179
    uintN attrs;
 
180
 
 
181
    if (!JSVAL_IS_OBJECT(*vp))
 
182
        return JS_TRUE;
 
183
    pobj = JSVAL_TO_OBJECT(*vp);
 
184
    slot = (uint32) JSVAL_TO_INT(id);
 
185
    if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
 
186
        return JS_FALSE;
 
187
 
 
188
    /* __parent__ is readonly and permanent, only __proto__ may be set. */
 
189
    if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_PROTO | JSACC_WRITE, vp, &attrs))
 
190
        return JS_FALSE;
 
191
 
 
192
    return js_SetProtoOrParent(cx, obj, slot, pobj);
 
193
}
 
194
 
 
195
static JSBool
 
196
obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 
197
{
 
198
    jsval iter_state;
 
199
    jsid num_properties;
 
200
    JSBool ok;
 
201
 
 
202
    if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
 
203
        return JS_FALSE;
 
204
 
 
205
    /* Get the number of properties to enumerate. */
 
206
    iter_state = JSVAL_NULL;
 
207
    ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
 
208
    if (!ok)
 
209
        goto out;
 
210
 
 
211
    if (!JSVAL_IS_INT(num_properties)) {
 
212
        JS_ASSERT(0);
 
213
        *vp = JSVAL_ZERO;
 
214
        goto out;
 
215
    }
 
216
    *vp = num_properties;
 
217
 
 
218
out:
 
219
    if (iter_state != JSVAL_NULL)
 
220
        ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
 
221
    return ok;
 
222
}
 
223
 
 
224
#else  /* !JS_HAS_OBJ_PROTO_PROP */
 
225
 
 
226
#define object_props NULL
 
227
 
 
228
#endif /* !JS_HAS_OBJ_PROTO_PROP */
 
229
 
 
230
JSBool
 
231
js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
 
232
{
 
233
    JSRuntime *rt;
 
234
    JSObject *obj2, *oldproto;
 
235
    JSScope *scope, *newscope;
 
236
 
 
237
    /*
 
238
     * Serialize all proto and parent setting in order to detect cycles.
 
239
     * We nest locks in this function, and only here, in the following orders:
 
240
     *
 
241
     * (1)  rt->setSlotLock < pobj's scope lock;
 
242
     *      rt->setSlotLock < pobj's proto-or-parent's scope lock;
 
243
     *      rt->setSlotLock < pobj's grand-proto-or-parent's scope lock;
 
244
     *      etc...
 
245
     * (2)  rt->setSlotLock < obj's scope lock < pobj's scope lock.
 
246
     *
 
247
     * We avoid AB-BA deadlock by restricting obj from being on pobj's parent
 
248
     * or proto chain (pobj may already be on obj's parent or proto chain; it
 
249
     * could be moving up or down).  We finally order obj with respect to pobj
 
250
     * at the bottom of this routine (just before releasing rt->setSlotLock),
 
251
     * by making pobj be obj's prototype or parent.
 
252
     *
 
253
     * After we have set the slot and released rt->setSlotLock, another call
 
254
     * to js_SetProtoOrParent could nest locks according to the first order
 
255
     * list above, but it cannot deadlock with any other thread.  For there
 
256
     * to be a deadlock, other parts of the engine would have to nest scope
 
257
     * locks in the opposite order.  XXXbe ensure they don't!
 
258
     */
 
259
    rt = cx->runtime;
 
260
#ifdef JS_THREADSAFE
 
261
 
 
262
    JS_ACQUIRE_LOCK(rt->setSlotLock);
 
263
    while (rt->setSlotBusy) {
 
264
        jsrefcount saveDepth;
 
265
 
 
266
        /* Take pains to avoid nesting rt->gcLock inside rt->setSlotLock! */
 
267
        JS_RELEASE_LOCK(rt->setSlotLock);
 
268
        saveDepth = JS_SuspendRequest(cx);
 
269
        JS_ACQUIRE_LOCK(rt->setSlotLock);
 
270
        if (rt->setSlotBusy)
 
271
            JS_WAIT_CONDVAR(rt->setSlotDone, JS_NO_TIMEOUT);
 
272
        JS_RELEASE_LOCK(rt->setSlotLock);
 
273
        JS_ResumeRequest(cx, saveDepth);
 
274
        JS_ACQUIRE_LOCK(rt->setSlotLock);
 
275
    }
 
276
    rt->setSlotBusy = JS_TRUE;
 
277
    JS_RELEASE_LOCK(rt->setSlotLock);
 
278
 
 
279
#define SET_SLOT_DONE(rt)                                                     \
 
280
    JS_BEGIN_MACRO                                                            \
 
281
        JS_ACQUIRE_LOCK((rt)->setSlotLock);                                   \
 
282
        (rt)->setSlotBusy = JS_FALSE;                                         \
 
283
        JS_NOTIFY_ALL_CONDVAR((rt)->setSlotDone);                             \
 
284
        JS_RELEASE_LOCK((rt)->setSlotLock);                                   \
 
285
    JS_END_MACRO
 
286
 
 
287
#else
 
288
 
 
289
#define SET_SLOT_DONE(rt)       /* nothing */
 
290
 
 
291
#endif
 
292
 
 
293
    obj2 = pobj;
 
294
    while (obj2) {
 
295
        if (obj2 == obj) {
 
296
            SET_SLOT_DONE(rt);
 
297
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
298
                                 JSMSG_CYCLIC_VALUE,
 
299
#if JS_HAS_OBJ_PROTO_PROP
 
300
                                 object_props[slot].name
 
301
#else
 
302
                                 (slot == JSSLOT_PROTO) ? js_proto_str
 
303
                                                        : js_parent_str
 
304
#endif
 
305
                                 );
 
306
            return JS_FALSE;
 
307
        }
 
308
        obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot));
 
309
    }
 
310
 
 
311
    if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) {
 
312
        /* Check to see whether obj shares its prototype's scope. */
 
313
        JS_LOCK_OBJ(cx, obj);
 
314
        scope = OBJ_SCOPE(obj);
 
315
        oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO));
 
316
        if (oldproto && OBJ_SCOPE(oldproto) == scope) {
 
317
            /* Either obj needs a new empty scope, or it should share pobj's. */
 
318
            if (!pobj ||
 
319
                !OBJ_IS_NATIVE(pobj) ||
 
320
                OBJ_GET_CLASS(cx, pobj) != LOCKED_OBJ_GET_CLASS(oldproto)) {
 
321
                /*
 
322
                 * With no proto and no scope of its own, obj is truly empty.
 
323
                 *
 
324
                 * If pobj is not native, obj needs its own empty scope -- it
 
325
                 * should not continue to share oldproto's scope once oldproto
 
326
                 * is not on obj's prototype chain.  That would put properties
 
327
                 * from oldproto's scope ahead of properties defined by pobj,
 
328
                 * in lookup order.
 
329
                 *
 
330
                 * If pobj's class differs from oldproto's, we may need a new
 
331
                 * scope to handle differences in private and reserved slots,
 
332
                 * so we suboptimally but safely make one.
 
333
                 */
 
334
                scope = js_GetMutableScope(cx, obj);
 
335
                if (!scope) {
 
336
                    JS_UNLOCK_OBJ(cx, obj);
 
337
                    SET_SLOT_DONE(rt);
 
338
                    return JS_FALSE;
 
339
                }
 
340
            } else if (OBJ_SCOPE(pobj) != scope) {
 
341
#ifdef JS_THREADSAFE
 
342
                /*
 
343
                 * We are about to nest scope locks.  Help jslock.c:ShareScope
 
344
                 * keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while
 
345
                 * avoiding deadlock, by recording scope in rt->setSlotScope.
 
346
                 */
 
347
                if (scope->ownercx) {
 
348
                    JS_ASSERT(scope->ownercx == cx);
 
349
                    rt->setSlotScope = scope;
 
350
                }
 
351
#endif
 
352
 
 
353
                /* We can't deadlock because we checked for cycles above (2). */
 
354
                JS_LOCK_OBJ(cx, pobj);
 
355
                newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map);
 
356
                obj->map = &newscope->map;
 
357
                js_DropObjectMap(cx, &scope->map, obj);
 
358
                JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
 
359
                scope = newscope;
 
360
#ifdef JS_THREADSAFE
 
361
                rt->setSlotScope = NULL;
 
362
#endif
 
363
            }
 
364
        }
 
365
        LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj));
 
366
        JS_UNLOCK_SCOPE(cx, scope);
 
367
    } else {
 
368
        OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj));
 
369
    }
 
370
 
 
371
    SET_SLOT_DONE(rt);
 
372
    return JS_TRUE;
 
373
 
 
374
#undef SET_SLOT_DONE
 
375
}
 
376
 
 
377
JS_STATIC_DLL_CALLBACK(JSHashNumber)
 
378
js_hash_object(const void *key)
 
379
{
 
380
    return (JSHashNumber)key >> JSVAL_TAGBITS;
 
381
}
 
382
 
 
383
static JSHashEntry *
 
384
MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
 
385
{
 
386
    JSSharpObjectMap *map;
 
387
    JSHashTable *table;
 
388
    JSHashNumber hash;
 
389
    JSHashEntry **hep, *he;
 
390
    jsatomid sharpid;
 
391
    JSIdArray *ida;
 
392
    JSBool ok;
 
393
    jsint i, length;
 
394
    jsid id;
 
395
#if JS_HAS_GETTER_SETTER
 
396
    JSObject *obj2;
 
397
    JSProperty *prop;
 
398
    uintN attrs;
 
399
#endif
 
400
    jsval val;
 
401
 
 
402
    map = &cx->sharpObjectMap;
 
403
    table = map->table;
 
404
    hash = js_hash_object(obj);
 
405
    hep = JS_HashTableRawLookup(table, hash, obj);
 
406
    he = *hep;
 
407
    if (!he) {
 
408
        sharpid = 0;
 
409
        he = JS_HashTableRawAdd(table, hep, hash, obj, (void *)sharpid);
 
410
        if (!he) {
 
411
            JS_ReportOutOfMemory(cx);
 
412
            return NULL;
 
413
        }
 
414
        ida = JS_Enumerate(cx, obj);
 
415
        if (!ida)
 
416
            return NULL;
 
417
        ok = JS_TRUE;
 
418
        for (i = 0, length = ida->length; i < length; i++) {
 
419
            id = ida->vector[i];
 
420
#if JS_HAS_GETTER_SETTER
 
421
            ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
 
422
            if (!ok)
 
423
                break;
 
424
            if (prop) {
 
425
                ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
 
426
                if (ok) {
 
427
                    if (OBJ_IS_NATIVE(obj2) &&
 
428
                        (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
 
429
                        val = JSVAL_NULL;
 
430
                        if (attrs & JSPROP_GETTER)
 
431
                            val = (jsval) ((JSScopeProperty*)prop)->getter;
 
432
                        if (attrs & JSPROP_SETTER) {
 
433
                            if (val != JSVAL_NULL) {
 
434
                                /* Mark the getter, then set val to setter. */
 
435
                                ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
 
436
                                                       NULL)
 
437
                                      != NULL);
 
438
                            }
 
439
                            val = (jsval) ((JSScopeProperty*)prop)->setter;
 
440
                        }
 
441
                    } else {
 
442
                        ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
 
443
                    }
 
444
                }
 
445
                OBJ_DROP_PROPERTY(cx, obj2, prop);
 
446
            }
 
447
#else
 
448
            ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
 
449
#endif
 
450
            if (!ok)
 
451
                break;
 
452
            if (!JSVAL_IS_PRIMITIVE(val) &&
 
453
                !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
 
454
                ok = JS_FALSE;
 
455
                break;
 
456
            }
 
457
        }
 
458
        if (!ok || !idap)
 
459
            JS_DestroyIdArray(cx, ida);
 
460
        if (!ok)
 
461
            return NULL;
 
462
    } else {
 
463
        sharpid = (jsatomid) he->value;
 
464
        if (sharpid == 0) {
 
465
            sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
 
466
            he->value = (void *) sharpid;
 
467
        }
 
468
        ida = NULL;
 
469
    }
 
470
    if (idap)
 
471
        *idap = ida;
 
472
    return he;
 
473
}
 
474
 
 
475
JSHashEntry *
 
476
js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
 
477
                    jschar **sp)
 
478
{
 
479
    JSSharpObjectMap *map;
 
480
    JSHashTable *table;
 
481
    JSIdArray *ida;
 
482
    JSHashNumber hash;
 
483
    JSHashEntry *he, **hep;
 
484
    jsatomid sharpid;
 
485
    char buf[20];
 
486
    size_t len;
 
487
 
 
488
    /* Set to null in case we return an early error. */
 
489
    *sp = NULL;
 
490
    map = &cx->sharpObjectMap;
 
491
    table = map->table;
 
492
    if (!table) {
 
493
        table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
 
494
                                JS_CompareValues, NULL, NULL);
 
495
        if (!table) {
 
496
            JS_ReportOutOfMemory(cx);
 
497
            return NULL;
 
498
        }
 
499
        map->table = table;
 
500
    }
 
501
 
 
502
    ida = NULL;
 
503
    if (map->depth == 0) {
 
504
        he = MarkSharpObjects(cx, obj, &ida);
 
505
        if (!he)
 
506
            goto bad;
 
507
        JS_ASSERT((((jsatomid) he->value) & SHARP_BIT) == 0);
 
508
        if (!idap) {
 
509
            JS_DestroyIdArray(cx, ida);
 
510
            ida = NULL;
 
511
        }
 
512
    } else {
 
513
        hash = js_hash_object(obj);
 
514
        hep = JS_HashTableRawLookup(table, hash, obj);
 
515
        he = *hep;
 
516
 
 
517
        /*
 
518
         * It's possible that the value of a property has changed from the
 
519
         * first time the object's properties are traversed (when the property
 
520
         * ids are entered into the hash table) to the second (when they are
 
521
         * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not
 
522
         * idempotent.
 
523
         */
 
524
        if (!he) {
 
525
            he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
 
526
            if (!he) {
 
527
                JS_ReportOutOfMemory(cx);
 
528
                goto bad;
 
529
            }
 
530
            *sp = NULL;
 
531
            sharpid = 0;
 
532
            goto out;
 
533
        }
 
534
    }
 
535
 
 
536
    sharpid = (jsatomid) he->value;
 
537
    if (sharpid == 0) {
 
538
        *sp = NULL;
 
539
    } else {
 
540
        len = JS_snprintf(buf, sizeof buf, "#%u%c",
 
541
                          sharpid >> SHARP_ID_SHIFT,
 
542
                          (sharpid & SHARP_BIT) ? '#' : '=');
 
543
        *sp = js_InflateString(cx, buf, len);
 
544
        if (!*sp) {
 
545
            if (ida)
 
546
                JS_DestroyIdArray(cx, ida);
 
547
            goto bad;
 
548
        }
 
549
    }
 
550
 
 
551
out:
 
552
    JS_ASSERT(he);
 
553
    if ((sharpid & SHARP_BIT) == 0) {
 
554
        if (idap && !ida) {
 
555
            ida = JS_Enumerate(cx, obj);
 
556
            if (!ida) {
 
557
                if (*sp) {
 
558
                    JS_free(cx, *sp);
 
559
                    *sp = NULL;
 
560
                }
 
561
                goto bad;
 
562
            }
 
563
        }
 
564
        map->depth++;
 
565
    }
 
566
 
 
567
    if (idap)
 
568
        *idap = ida;
 
569
    return he;
 
570
 
 
571
bad:
 
572
    /* Clean up the sharpObjectMap table on outermost error. */
 
573
    if (map->depth == 0) {
 
574
        map->sharpgen = 0;
 
575
        JS_HashTableDestroy(map->table);
 
576
        map->table = NULL;
 
577
    }
 
578
    return NULL;
 
579
}
 
580
 
 
581
void
 
582
js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
 
583
{
 
584
    JSSharpObjectMap *map;
 
585
    JSIdArray *ida;
 
586
 
 
587
    map = &cx->sharpObjectMap;
 
588
    JS_ASSERT(map->depth > 0);
 
589
    if (--map->depth == 0) {
 
590
        map->sharpgen = 0;
 
591
        JS_HashTableDestroy(map->table);
 
592
        map->table = NULL;
 
593
    }
 
594
    if (idap) {
 
595
        ida = *idap;
 
596
        if (ida) {
 
597
            JS_DestroyIdArray(cx, ida);
 
598
            *idap = NULL;
 
599
        }
 
600
    }
 
601
}
 
602
 
 
603
#define OBJ_TOSTRING_EXTRA      3       /* for 3 local GC roots */
 
604
 
 
605
#if JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE
 
606
JSBool
 
607
js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
608
                jsval *rval)
 
609
{
 
610
    JSBool ok, outermost;
 
611
    JSHashEntry *he;
 
612
    JSIdArray *ida;
 
613
    jschar *chars, *ochars, *vsharp;
 
614
    const jschar *idstrchars, *vchars;
 
615
    size_t nchars, idstrlength, gsoplength, vlength, vsharplength;
 
616
    char *comma;
 
617
    jsint i, j, length, valcnt;
 
618
    jsid id;
 
619
#if JS_HAS_GETTER_SETTER
 
620
    JSObject *obj2;
 
621
    JSProperty *prop;
 
622
    uintN attrs;
 
623
#endif
 
624
    jsval val[2];
 
625
    JSString *gsop[2];
 
626
    JSAtom *atom;
 
627
    JSString *idstr, *valstr, *str;
 
628
    int stackDummy;
 
629
 
 
630
    if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
 
631
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
 
632
        return JS_FALSE;
 
633
    }
 
634
 
 
635
    /*
 
636
     * obj_toString for 1.2 calls toSource, and doesn't want the extra parens
 
637
     * on the outside.
 
638
     */
 
639
    outermost = (cx->version != JSVERSION_1_2 && cx->sharpObjectMap.depth == 0);
 
640
    he = js_EnterSharpObject(cx, obj, &ida, &chars);
 
641
    if (!he)
 
642
        return JS_FALSE;
 
643
    if (IS_SHARP(he)) {
 
644
        /*
 
645
         * We didn't enter -- obj is already "sharp", meaning we've visited it
 
646
         * already in our depth first search, and therefore chars contains a
 
647
         * string of the form "#n#".
 
648
         */
 
649
        JS_ASSERT(!ida);
 
650
#if JS_HAS_SHARP_VARS
 
651
        nchars = js_strlen(chars);
 
652
#else
 
653
        chars[0] = '{';
 
654
        chars[1] = '}';
 
655
        chars[2] = 0;
 
656
        nchars = 2;
 
657
#endif
 
658
        goto make_string;
 
659
    }
 
660
    JS_ASSERT(ida);
 
661
    ok = JS_TRUE;
 
662
 
 
663
    if (!chars) {
 
664
        /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
 
665
        chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
 
666
        nchars = 0;
 
667
        if (!chars)
 
668
            goto error;
 
669
        if (outermost)
 
670
            chars[nchars++] = '(';
 
671
    } else {
 
672
        /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
 
673
        MAKE_SHARP(he);
 
674
        nchars = js_strlen(chars);
 
675
        chars = (jschar *)
 
676
            realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
 
677
        if (!chars) {
 
678
            free(ochars);
 
679
            goto error;
 
680
        }
 
681
        if (outermost) {
 
682
            /*
 
683
             * No need for parentheses around the whole shebang, because #n=
 
684
             * unambiguously begins an object initializer, and never a block
 
685
             * statement.
 
686
             */
 
687
            outermost = JS_FALSE;
 
688
        }
 
689
    }
 
690
 
 
691
    chars[nchars++] = '{';
 
692
 
 
693
    comma = NULL;
 
694
 
 
695
    for (i = 0, length = ida->length; i < length; i++) {
 
696
        /* Get strings for id and value and GC-root them via argv. */
 
697
        id = ida->vector[i];
 
698
 
 
699
#if JS_HAS_GETTER_SETTER
 
700
 
 
701
        ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
 
702
        if (!ok)
 
703
            goto error;
 
704
        valcnt = 0;
 
705
        if (prop) {
 
706
            ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
 
707
            if (!ok) {
 
708
                OBJ_DROP_PROPERTY(cx, obj2, prop);
 
709
                goto error;
 
710
            }
 
711
            if (OBJ_IS_NATIVE(obj2) &&
 
712
                (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
 
713
                if (attrs & JSPROP_GETTER) {
 
714
                    val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter;
 
715
#ifdef OLD_GETTER_SETTER
 
716
                    gsop[valcnt] =
 
717
                        ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
 
718
#else
 
719
                    gsop[valcnt] =
 
720
                        ATOM_TO_STRING(cx->runtime->atomState.getAtom);
 
721
#endif
 
722
                    valcnt++;
 
723
                }
 
724
                if (attrs & JSPROP_SETTER) {
 
725
                    val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
 
726
#ifdef OLD_GETTER_SETTER
 
727
                    gsop[valcnt] =
 
728
                        ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
 
729
#else
 
730
                    gsop[valcnt] =
 
731
                        ATOM_TO_STRING(cx->runtime->atomState.setAtom);
 
732
#endif
 
733
                    valcnt++;
 
734
                }
 
735
            } else {
 
736
                valcnt = 1;
 
737
                gsop[0] = NULL;
 
738
                ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
 
739
            }
 
740
            OBJ_DROP_PROPERTY(cx, obj2, prop);
 
741
        }
 
742
 
 
743
#else  /* !JS_HAS_GETTER_SETTER */
 
744
 
 
745
        valcnt = 1;
 
746
        gsop[0] = NULL;
 
747
        ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
 
748
 
 
749
#endif /* !JS_HAS_GETTER_SETTER */
 
750
 
 
751
        if (!ok)
 
752
            goto error;
 
753
 
 
754
        /* Convert id to a jsval and then to a string. */
 
755
        atom = JSVAL_IS_INT(id) ? NULL : (JSAtom *)id;
 
756
        id = ID_TO_VALUE(id);
 
757
        idstr = js_ValueToString(cx, id);
 
758
        if (!idstr) {
 
759
            ok = JS_FALSE;
 
760
            goto error;
 
761
        }
 
762
        argv[0] = STRING_TO_JSVAL(idstr);
 
763
 
 
764
        /*
 
765
         * If id is a string that's a reserved identifier, or else id is not
 
766
         * an identifier at all, then it needs to be quoted.
 
767
         */
 
768
        if (atom && (ATOM_KEYWORD(atom) || !js_IsIdentifier(idstr))) {
 
769
            idstr = js_QuoteString(cx, idstr, (jschar)'\'');
 
770
            if (!idstr) {
 
771
                ok = JS_FALSE;
 
772
                goto error;
 
773
            }
 
774
            argv[0] = STRING_TO_JSVAL(idstr);
 
775
        }
 
776
        idstrchars = JSSTRING_CHARS(idstr);
 
777
        idstrlength = JSSTRING_LENGTH(idstr);
 
778
 
 
779
        for (j = 0; j < valcnt; j++) {
 
780
            /* Convert val[j] to its canonical source form. */
 
781
            valstr = js_ValueToSource(cx, val[j]);
 
782
            if (!valstr) {
 
783
                ok = JS_FALSE;
 
784
                goto error;
 
785
            }
 
786
            argv[1+j] = STRING_TO_JSVAL(valstr);
 
787
            vchars = JSSTRING_CHARS(valstr);
 
788
            vlength = JSSTRING_LENGTH(valstr);
 
789
 
 
790
#ifndef OLD_GETTER_SETTER
 
791
            /* Remove 'function ' from beginning of valstr. */
 
792
            if (gsop[j]) {
 
793
                int n = strlen(js_function_str) + 1;
 
794
                vchars += n;
 
795
                vlength -= n;
 
796
            }
 
797
#endif
 
798
 
 
799
            /* If val[j] is a non-sharp object, consider sharpening it. */
 
800
            vsharp = NULL;
 
801
            vsharplength = 0;
 
802
#if JS_HAS_SHARP_VARS
 
803
            if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
 
804
                he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
 
805
                                         &vsharp);
 
806
                if (!he) {
 
807
                    ok = JS_FALSE;
 
808
                    goto error;
 
809
                }
 
810
                if (IS_SHARP(he)) {
 
811
                    vchars = vsharp;
 
812
                    vlength = js_strlen(vchars);
 
813
                } else {
 
814
                    if (vsharp) {
 
815
                        vsharplength = js_strlen(vsharp);
 
816
                        MAKE_SHARP(he);
 
817
                    }
 
818
                    js_LeaveSharpObject(cx, NULL);
 
819
                }
 
820
            }
 
821
#endif
 
822
 
 
823
            /* Allocate 1 + 1 at end for closing brace and terminating 0. */
 
824
            chars = (jschar *)
 
825
                realloc((ochars = chars),
 
826
                        (nchars + (comma ? 2 : 0) +
 
827
                         idstrlength + 1 +
 
828
                         (gsop[j] ? 1 + JSSTRING_LENGTH(gsop[j]) : 0) +
 
829
                         vsharplength + vlength +
 
830
                         (outermost ? 2 : 1) + 1) * sizeof(jschar));
 
831
            if (!chars) {
 
832
                /* Save code space on error: let JS_free ignore null vsharp. */
 
833
                JS_free(cx, vsharp);
 
834
                free(ochars);
 
835
                goto error;
 
836
            }
 
837
 
 
838
            if (comma) {
 
839
                chars[nchars++] = comma[0];
 
840
                chars[nchars++] = comma[1];
 
841
            }
 
842
            comma = ", ";
 
843
 
 
844
#ifdef OLD_GETTER_SETTER
 
845
            js_strncpy(&chars[nchars], idstrchars, idstrlength);
 
846
            nchars += idstrlength;
 
847
            if (gsop[j]) {
 
848
                chars[nchars++] = ' ';
 
849
                gsoplength = JSSTRING_LENGTH(gsop[j]);
 
850
                js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
 
851
                nchars += gsoplength;
 
852
            }
 
853
            chars[nchars++] = ':';
 
854
#else
 
855
            if (gsop[j]) {
 
856
                gsoplength = JSSTRING_LENGTH(gsop[j]);
 
857
                js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
 
858
                nchars += gsoplength;
 
859
                chars[nchars++] = ' ';
 
860
            }
 
861
            js_strncpy(&chars[nchars], idstrchars, idstrlength);
 
862
            nchars += idstrlength;
 
863
            if (!gsop[j])
 
864
                chars[nchars++] = ':';
 
865
#endif
 
866
            if (vsharplength) {
 
867
                js_strncpy(&chars[nchars], vsharp, vsharplength);
 
868
                nchars += vsharplength;
 
869
            }
 
870
            js_strncpy(&chars[nchars], vchars, vlength);
 
871
            nchars += vlength;
 
872
 
 
873
            if (vsharp)
 
874
                JS_free(cx, vsharp);
 
875
        }
 
876
    }
 
877
 
 
878
    chars[nchars++] = '}';
 
879
    if (outermost)
 
880
        chars[nchars++] = ')';
 
881
    chars[nchars] = 0;
 
882
 
 
883
  error:
 
884
    js_LeaveSharpObject(cx, &ida);
 
885
 
 
886
    if (!ok) {
 
887
        if (chars)
 
888
            free(chars);
 
889
        return ok;
 
890
    }
 
891
 
 
892
    if (!chars) {
 
893
        JS_ReportOutOfMemory(cx);
 
894
        return JS_FALSE;
 
895
    }
 
896
  make_string:
 
897
    str = js_NewString(cx, chars, nchars, 0);
 
898
    if (!str) {
 
899
        free(chars);
 
900
        return JS_FALSE;
 
901
    }
 
902
    *rval = STRING_TO_JSVAL(str);
 
903
    return JS_TRUE;
 
904
}
 
905
#endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */
 
906
 
 
907
JSBool
 
908
js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
909
                jsval *rval)
 
910
{
 
911
    jschar *chars;
 
912
    size_t nchars;
 
913
    const char *clazz, *prefix;
 
914
    JSString *str;
 
915
 
 
916
#if JS_HAS_INITIALIZERS
 
917
    if (cx->version == JSVERSION_1_2)
 
918
        return js_obj_toSource(cx, obj, argc, argv, rval);
 
919
#endif
 
920
 
 
921
    clazz = OBJ_GET_CLASS(cx, obj)->name;
 
922
    nchars = 9 + strlen(clazz);         /* 9 for "[object ]" */
 
923
    chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
 
924
    if (!chars)
 
925
        return JS_FALSE;
 
926
 
 
927
    prefix = "[object ";
 
928
    nchars = 0;
 
929
    while ((chars[nchars] = (jschar)*prefix) != 0)
 
930
        nchars++, prefix++;
 
931
    while ((chars[nchars] = (jschar)*clazz) != 0)
 
932
        nchars++, clazz++;
 
933
    chars[nchars++] = ']';
 
934
    chars[nchars] = 0;
 
935
 
 
936
    str = js_NewString(cx, chars, nchars, 0);
 
937
    if (!str) {
 
938
        JS_free(cx, chars);
 
939
        return JS_FALSE;
 
940
    }
 
941
    *rval = STRING_TO_JSVAL(str);
 
942
    return JS_TRUE;
 
943
}
 
944
 
 
945
static JSBool
 
946
obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
947
{
 
948
    *rval = OBJECT_TO_JSVAL(obj);
 
949
    return JS_TRUE;
 
950
}
 
951
 
 
952
static JSBool
 
953
obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
954
{
 
955
    JSStackFrame *fp, *caller;
 
956
    JSBool indirectCall;
 
957
    JSObject *scopeobj;
 
958
    JSString *str;
 
959
    const char *file;
 
960
    uintN line;
 
961
    JSPrincipals *principals;
 
962
    JSScript *script;
 
963
    JSBool ok;
 
964
#if JS_HAS_EVAL_THIS_SCOPE
 
965
    JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
 
966
    JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE;
 
967
#endif
 
968
 
 
969
    fp = cx->fp;
 
970
    caller = JS_GetScriptedCaller(cx, fp);
 
971
    indirectCall = (caller && caller->pc && *caller->pc != JSOP_EVAL);
 
972
 
 
973
    if (JSVERSION_IS_ECMA(cx->version) &&
 
974
        indirectCall &&
 
975
        !JS_ReportErrorFlagsAndNumber(cx,
 
976
                                      JSREPORT_WARNING | JSREPORT_STRICT,
 
977
                                      js_GetErrorMessage, NULL,
 
978
                                      JSMSG_BAD_INDIRECT_CALL,
 
979
                                      js_eval_str)) {
 
980
        return JS_FALSE;
 
981
    }
 
982
 
 
983
    if (!JSVAL_IS_STRING(argv[0])) {
 
984
        *rval = argv[0];
 
985
        return JS_TRUE;
 
986
    }
 
987
 
 
988
#if JS_HAS_SCRIPT_OBJECT
 
989
    /*
 
990
     * Script.prototype.compile/exec and Object.prototype.eval all take an
 
991
     * optional trailing argument that overrides the scope object.
 
992
     */
 
993
    scopeobj = NULL;
 
994
    if (argc >= 2) {
 
995
        if (!js_ValueToObject(cx, argv[1], &scopeobj))
 
996
            return JS_FALSE;
 
997
        argv[1] = OBJECT_TO_JSVAL(scopeobj);
 
998
    }
 
999
    if (!scopeobj)
 
1000
#endif
 
1001
    {
 
1002
#if JS_HAS_EVAL_THIS_SCOPE
 
1003
        /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
 
1004
        if (indirectCall) {
 
1005
            callerScopeChain = caller->scopeChain;
 
1006
            if (obj != callerScopeChain) {
 
1007
                scopeobj = js_NewObject(cx, &js_WithClass, obj,
 
1008
                                        callerScopeChain);
 
1009
                if (!scopeobj)
 
1010
                    return JS_FALSE;
 
1011
 
 
1012
                /* Set fp->scopeChain too, for the compiler. */
 
1013
                caller->scopeChain = fp->scopeChain = scopeobj;
 
1014
                setCallerScopeChain = JS_TRUE;
 
1015
            }
 
1016
 
 
1017
            callerVarObj = caller->varobj;
 
1018
            if (obj != callerVarObj) {
 
1019
                /* Set fp->varobj too, for the compiler. */
 
1020
                caller->varobj = fp->varobj = obj;
 
1021
                setCallerVarObj = JS_TRUE;
 
1022
            }
 
1023
        }
 
1024
        /* From here on, control must exit through label out with ok set. */
 
1025
#endif
 
1026
 
 
1027
#if JS_BUG_EVAL_THIS_SCOPE
 
1028
        /* An old version used the object in which eval was found for scope. */
 
1029
        scopeobj = obj;
 
1030
#else
 
1031
        /* Compile using caller's current scope object. */
 
1032
        if (caller)
 
1033
            scopeobj = caller->scopeChain;
 
1034
#endif
 
1035
    }
 
1036
 
 
1037
    str = JSVAL_TO_STRING(argv[0]);
 
1038
    if (caller) {
 
1039
        file = caller->script->filename;
 
1040
        line = js_PCToLineNumber(cx, caller->script, caller->pc);
 
1041
        principals = JS_EvalFramePrincipals(cx, fp, caller);
 
1042
    } else {
 
1043
        file = NULL;
 
1044
        line = 0;
 
1045
        principals = NULL;
 
1046
    }
 
1047
 
 
1048
    /* XXXbe set only for the compiler, which does not currently test it */
 
1049
    fp->flags |= JSFRAME_EVAL;
 
1050
    script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
 
1051
                                             JSSTRING_CHARS(str),
 
1052
                                             JSSTRING_LENGTH(str),
 
1053
                                             file, line);
 
1054
    if (!script) {
 
1055
        ok = JS_FALSE;
 
1056
        goto out;
 
1057
    }
 
1058
 
 
1059
#if !JS_BUG_EVAL_THIS_SCOPE
 
1060
#if JS_HAS_SCRIPT_OBJECT
 
1061
    if (argc < 2)
 
1062
#endif
 
1063
    {
 
1064
        /* Execute using caller's new scope object (might be a Call object). */
 
1065
        if (caller)
 
1066
            scopeobj = caller->scopeChain;
 
1067
    }
 
1068
#endif
 
1069
    ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
 
1070
    JS_DestroyScript(cx, script);
 
1071
 
 
1072
out:
 
1073
#if JS_HAS_EVAL_THIS_SCOPE
 
1074
    /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
 
1075
    if (setCallerScopeChain)
 
1076
        caller->scopeChain = callerScopeChain;
 
1077
    if (setCallerVarObj)
 
1078
        caller->varobj = callerVarObj;
 
1079
#endif
 
1080
    return ok;
 
1081
}
 
1082
 
 
1083
JS_STATIC_DLL_CALLBACK(const void *)
 
1084
resolving_GetKey(JSDHashTable *table, JSDHashEntryHdr *hdr)
 
1085
{
 
1086
    JSResolvingEntry *entry = (JSResolvingEntry *)hdr;
 
1087
 
 
1088
    return &entry->key;
 
1089
}
 
1090
 
 
1091
JS_STATIC_DLL_CALLBACK(JSDHashNumber)
 
1092
resolving_HashKey(JSDHashTable *table, const void *ptr)
 
1093
{
 
1094
    const JSResolvingKey *key = (const JSResolvingKey *)ptr;
 
1095
 
 
1096
    return ((JSDHashNumber)key->obj >> JSVAL_TAGBITS) ^ key->id;
 
1097
}
 
1098
 
 
1099
JS_PUBLIC_API(JSBool)
 
1100
resolving_MatchEntry(JSDHashTable *table,
 
1101
                     const JSDHashEntryHdr *hdr,
 
1102
                     const void *ptr)
 
1103
{
 
1104
    const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr;
 
1105
    const JSResolvingKey *key = (const JSResolvingKey *)ptr;
 
1106
 
 
1107
    return entry->key.obj == key->obj && entry->key.id == key->id;
 
1108
}
 
1109
 
 
1110
static const JSDHashTableOps resolving_dhash_ops = {
 
1111
    JS_DHashAllocTable,
 
1112
    JS_DHashFreeTable,
 
1113
    resolving_GetKey,
 
1114
    resolving_HashKey,
 
1115
    resolving_MatchEntry,
 
1116
    JS_DHashMoveEntryStub,
 
1117
    JS_DHashClearEntryStub,
 
1118
    JS_DHashFinalizeStub,
 
1119
    NULL
 
1120
};
 
1121
 
 
1122
static JSBool
 
1123
StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
 
1124
               JSResolvingEntry **entryp)
 
1125
{
 
1126
    JSDHashTable *table;
 
1127
    JSResolvingEntry *entry;
 
1128
 
 
1129
    table = cx->resolvingTable;
 
1130
    if (!table) {
 
1131
        table = JS_NewDHashTable(&resolving_dhash_ops, NULL,
 
1132
                                 sizeof(JSResolvingEntry),
 
1133
                                 JS_DHASH_MIN_SIZE);
 
1134
        if (!table)
 
1135
            goto outofmem;
 
1136
        cx->resolvingTable = table;
 
1137
    }
 
1138
 
 
1139
    entry = (JSResolvingEntry *)
 
1140
            JS_DHashTableOperate(table, key, JS_DHASH_ADD);
 
1141
    if (!entry)
 
1142
        goto outofmem;
 
1143
 
 
1144
    if (entry->flags & flag) {
 
1145
        /* An entry for (key, flag) exists already -- dampen recursion. */
 
1146
        entry = NULL;
 
1147
    } else {
 
1148
        /* Fill in key if we were the first to add entry, then set flag. */
 
1149
        if (!entry->key.obj)
 
1150
            entry->key = *key;
 
1151
        entry->flags |= flag;
 
1152
    }
 
1153
    *entryp = entry;
 
1154
    return JS_TRUE;
 
1155
 
 
1156
outofmem:
 
1157
    JS_ReportOutOfMemory(cx);
 
1158
    return JS_FALSE;
 
1159
}
 
1160
 
 
1161
static void
 
1162
StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag,
 
1163
              JSResolvingEntry *entry, uint32 generation)
 
1164
{
 
1165
    JSDHashTable *table;
 
1166
 
 
1167
    /*
 
1168
     * Clear flag from entry->flags and return early if other flags remain.
 
1169
     * We must take care to re-lookup entry if the table has changed since
 
1170
     * it was found by StartResolving.
 
1171
     */
 
1172
    table = cx->resolvingTable;
 
1173
    if (table->generation != generation) {
 
1174
        entry = (JSResolvingEntry *)
 
1175
                JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);
 
1176
    }
 
1177
    entry->flags &= ~flag;
 
1178
    if (entry->flags)
 
1179
        return;
 
1180
 
 
1181
    /*
 
1182
     * Do a raw remove only if fewer entries were removed than would cause
 
1183
     * alpha to be less than .5 (alpha is at most .75).  Otherwise, we just
 
1184
     * call JS_DHashTableOperate to re-lookup the key and remove its entry,
 
1185
     * compressing or shrinking the table as needed.
 
1186
     */
 
1187
    if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2)
 
1188
        JS_DHashTableRawRemove(table, &entry->hdr);
 
1189
    else
 
1190
        JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);
 
1191
}
 
1192
 
 
1193
#if JS_HAS_OBJ_WATCHPOINT
 
1194
 
 
1195
static JSBool
 
1196
obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
 
1197
                  void *closure)
 
1198
{
 
1199
    JSResolvingKey key;
 
1200
    JSResolvingEntry *entry;
 
1201
    uint32 generation;
 
1202
    JSObject *funobj;
 
1203
    jsval argv[3];
 
1204
    JSBool ok;
 
1205
 
 
1206
    /* Avoid recursion on (obj, id) already being watched on cx. */
 
1207
    key.obj = obj;
 
1208
    key.id = id;
 
1209
    if (!StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
 
1210
        return JS_FALSE;
 
1211
    if (!entry)
 
1212
        return JS_TRUE;
 
1213
    generation = cx->resolvingTable->generation;
 
1214
 
 
1215
    funobj = (JSObject *) closure;
 
1216
    argv[0] = id;
 
1217
    argv[1] = old;
 
1218
    argv[2] = *nvp;
 
1219
    ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(funobj), 3, argv, nvp);
 
1220
    StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
 
1221
    return ok;
 
1222
}
 
1223
 
 
1224
static JSBool
 
1225
obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
1226
{
 
1227
    JSFunction *fun;
 
1228
    jsval userid, value;
 
1229
    jsid propid;
 
1230
    uintN attrs;
 
1231
 
 
1232
    fun = js_ValueToFunction(cx, &argv[1], 0);
 
1233
    if (!fun)
 
1234
        return JS_FALSE;
 
1235
    argv[1] = OBJECT_TO_JSVAL(fun->object);
 
1236
 
 
1237
    /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
 
1238
    userid = argv[0];
 
1239
    if (!JS_ValueToId(cx, userid, &propid))
 
1240
        return JS_FALSE;
 
1241
 
 
1242
    if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
 
1243
        return JS_FALSE;
 
1244
    if (attrs & JSPROP_READONLY)
 
1245
        return JS_TRUE;
 
1246
    return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, fun->object);
 
1247
}
 
1248
 
 
1249
static JSBool
 
1250
obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
1251
{
 
1252
    return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);
 
1253
}
 
1254
 
 
1255
#endif /* JS_HAS_OBJ_WATCHPOINT */
 
1256
 
 
1257
#if JS_HAS_NEW_OBJ_METHODS
 
1258
/*
 
1259
 * Prototype and property query methods, to complement the 'in' and
 
1260
 * 'instanceof' operators.
 
1261
 */
 
1262
 
 
1263
/* Proposed ECMA 15.2.4.5. */
 
1264
static JSBool
 
1265
obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
1266
                   jsval *rval)
 
1267
{
 
1268
    jsid id;
 
1269
    JSObject *obj2;
 
1270
    JSProperty *prop;
 
1271
    JSScopeProperty *sprop;
 
1272
 
 
1273
    if (!JS_ValueToId(cx, argv[0], &id))
 
1274
        return JS_FALSE;
 
1275
    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
 
1276
        return JS_FALSE;
 
1277
    if (!prop) {
 
1278
        *rval = JSVAL_FALSE;
 
1279
    } else if (obj2 == obj) {
 
1280
        *rval = JSVAL_TRUE;
 
1281
    } else if (OBJ_IS_NATIVE(obj2)) {
 
1282
        sprop = (JSScopeProperty *)prop;
 
1283
        *rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
 
1284
    } else {
 
1285
        *rval = JSVAL_FALSE;
 
1286
    }
 
1287
    if (prop)
 
1288
        OBJ_DROP_PROPERTY(cx, obj2, prop);
 
1289
    return JS_TRUE;
 
1290
}
 
1291
 
 
1292
/* Proposed ECMA 15.2.4.6. */
 
1293
static JSBool
 
1294
obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
1295
                  jsval *rval)
 
1296
{
 
1297
    JSBool b;
 
1298
 
 
1299
    if (!js_IsDelegate(cx, obj, *argv, &b))
 
1300
        return JS_FALSE;
 
1301
    *rval = BOOLEAN_TO_JSVAL(b);
 
1302
    return JS_TRUE;
 
1303
}
 
1304
 
 
1305
/* Proposed ECMA 15.2.4.7. */
 
1306
static JSBool
 
1307
obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
1308
                         jsval *rval)
 
1309
{
 
1310
    jsid id;
 
1311
    uintN attrs;
 
1312
    JSObject *obj2;
 
1313
    JSProperty *prop;
 
1314
    JSBool ok;
 
1315
 
 
1316
    if (!JS_ValueToId(cx, argv[0], &id))
 
1317
        return JS_FALSE;
 
1318
 
 
1319
    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
 
1320
        return JS_FALSE;
 
1321
 
 
1322
    if (!prop) {
 
1323
        *rval = JSVAL_FALSE;
 
1324
        return JS_TRUE;
 
1325
    }
 
1326
 
 
1327
    /*
 
1328
     * XXX ECMA spec error compatible: return false unless hasOwnProperty.
 
1329
     * The ECMA spec really should be fixed so propertyIsEnumerable and the
 
1330
     * for..in loop agree on whether prototype properties are enumerable,
 
1331
     * obviously by fixing this method (not by breaking the for..in loop!).
 
1332
     *
 
1333
     * We check here for shared permanent prototype properties, which should
 
1334
     * be treated as if they are local to obj.  They are an implementation
 
1335
     * technique used to satisfy ECMA requirements; users should not be able
 
1336
     * to distinguish a shared permanent proto-property from a local one.
 
1337
     */
 
1338
    if (obj2 != obj &&
 
1339
        !(OBJ_IS_NATIVE(obj2) &&
 
1340
          SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
 
1341
        OBJ_DROP_PROPERTY(cx, obj2, prop);
 
1342
        *rval = JSVAL_FALSE;
 
1343
        return JS_TRUE;
 
1344
    }
 
1345
 
 
1346
    ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
 
1347
    OBJ_DROP_PROPERTY(cx, obj2, prop);
 
1348
    if (ok)
 
1349
        *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
 
1350
    return ok;
 
1351
}
 
1352
#endif /* JS_HAS_NEW_OBJ_METHODS */
 
1353
 
 
1354
#if JS_HAS_GETTER_SETTER
 
1355
static JSBool
 
1356
obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
1357
                 jsval *rval)
 
1358
{
 
1359
    jsval fval, junk;
 
1360
    jsid id;
 
1361
    JSBool found;
 
1362
    uintN attrs;
 
1363
 
 
1364
    fval = argv[1];
 
1365
    if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
 
1366
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
1367
                             JSMSG_BAD_GETTER_OR_SETTER,
 
1368
                             js_getter_str);
 
1369
        return JS_FALSE;
 
1370
    }
 
1371
 
 
1372
    if (!JS_ValueToId(cx, argv[0], &id))
 
1373
        return JS_FALSE;
 
1374
    if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, &found))
 
1375
        return JS_FALSE;
 
1376
    /*
 
1377
     * Getters and setters are just like watchpoints from an access
 
1378
     * control point of view.
 
1379
     */
 
1380
    if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
 
1381
        return JS_FALSE;
 
1382
    return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
 
1383
                               (JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL,
 
1384
                               JSPROP_GETTER | JSPROP_SHARED, NULL);
 
1385
}
 
1386
 
 
1387
static JSBool
 
1388
obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
1389
                 jsval *rval)
 
1390
{
 
1391
    jsval fval, junk;
 
1392
    jsid id;
 
1393
    JSBool found;
 
1394
    uintN attrs;
 
1395
 
 
1396
    fval = argv[1];
 
1397
    if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
 
1398
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
1399
                             JSMSG_BAD_GETTER_OR_SETTER,
 
1400
                             js_setter_str);
 
1401
        return JS_FALSE;
 
1402
    }
 
1403
 
 
1404
    if (!JS_ValueToId(cx, argv[0], &id))
 
1405
        return JS_FALSE;
 
1406
    if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, &found))
 
1407
        return JS_FALSE;
 
1408
    /*
 
1409
     * Getters and setters are just like watchpoints from an access
 
1410
     * control point of view.
 
1411
     */
 
1412
    if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
 
1413
        return JS_FALSE;
 
1414
    return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
 
1415
                               NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval),
 
1416
                               JSPROP_SETTER | JSPROP_SHARED, NULL);
 
1417
}
 
1418
 
 
1419
static JSBool
 
1420
obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
1421
                 jsval *rval)
 
1422
{
 
1423
    jsid id;
 
1424
    JSObject *pobj;
 
1425
    JSScopeProperty *sprop;
 
1426
 
 
1427
    if (!JS_ValueToId(cx, argv[0], &id))
 
1428
        return JS_FALSE;
 
1429
    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, (JSProperty **) &sprop))
 
1430
        return JS_FALSE;
 
1431
    if (sprop) {
 
1432
        if (sprop->attrs & JSPROP_GETTER)
 
1433
            *rval = OBJECT_TO_JSVAL(sprop->getter);
 
1434
        OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
 
1435
    }
 
1436
    return JS_TRUE;
 
1437
}
 
1438
 
 
1439
static JSBool
 
1440
obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
1441
                 jsval *rval)
 
1442
{
 
1443
    jsid id;
 
1444
    JSObject *pobj;
 
1445
    JSScopeProperty *sprop;
 
1446
 
 
1447
    if (!JS_ValueToId(cx, argv[0], &id))
 
1448
        return JS_FALSE;
 
1449
    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, (JSProperty **) &sprop))
 
1450
        return JS_FALSE;
 
1451
    if (sprop) {
 
1452
        if (sprop->attrs & JSPROP_SETTER)
 
1453
            *rval = OBJECT_TO_JSVAL(sprop->setter);
 
1454
        OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
 
1455
    }
 
1456
    return JS_TRUE;
 
1457
}
 
1458
#endif /* JS_HAS_GETTER_SETTER */
 
1459
 
 
1460
#if JS_HAS_OBJ_WATCHPOINT
 
1461
const char js_watch_str[] = "watch";
 
1462
const char js_unwatch_str[] = "unwatch";
 
1463
#endif
 
1464
#if JS_HAS_NEW_OBJ_METHODS
 
1465
const char js_hasOwnProperty_str[] = "hasOwnProperty";
 
1466
const char js_isPrototypeOf_str[] = "isPrototypeOf";
 
1467
const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
 
1468
#endif
 
1469
#if JS_HAS_GETTER_SETTER
 
1470
const char js_defineGetter_str[] = "__defineGetter__";
 
1471
const char js_defineSetter_str[] = "__defineSetter__";
 
1472
const char js_lookupGetter_str[] = "__lookupGetter__";
 
1473
const char js_lookupSetter_str[] = "__lookupSetter__";
 
1474
#endif
 
1475
 
 
1476
static JSFunctionSpec object_methods[] = {
 
1477
#if JS_HAS_TOSOURCE
 
1478
    {js_toSource_str,             js_obj_toSource,    0, 0, OBJ_TOSTRING_EXTRA},
 
1479
#endif
 
1480
    {js_toString_str,             js_obj_toString,    0, 0, OBJ_TOSTRING_EXTRA},
 
1481
    {js_toLocaleString_str,       js_obj_toString,    0, 0, OBJ_TOSTRING_EXTRA},
 
1482
    {js_valueOf_str,              obj_valueOf,        0,0,0},
 
1483
    {js_eval_str,                 obj_eval,           1,0,0},
 
1484
#if JS_HAS_OBJ_WATCHPOINT
 
1485
    {js_watch_str,                obj_watch,          2,0,0},
 
1486
    {js_unwatch_str,              obj_unwatch,        1,0,0},
 
1487
#endif
 
1488
#if JS_HAS_NEW_OBJ_METHODS
 
1489
    {js_hasOwnProperty_str,       obj_hasOwnProperty, 1,0,0},
 
1490
    {js_isPrototypeOf_str,        obj_isPrototypeOf,  1,0,0},
 
1491
    {js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0},
 
1492
#endif
 
1493
#if JS_HAS_GETTER_SETTER
 
1494
    {js_defineGetter_str,         obj_defineGetter,   2,0,0},
 
1495
    {js_defineSetter_str,         obj_defineSetter,   2,0,0},
 
1496
    {js_lookupGetter_str,         obj_lookupGetter,   1,0,0},
 
1497
    {js_lookupSetter_str,         obj_lookupSetter,   1,0,0},
 
1498
#endif
 
1499
    {0,0,0,0,0}
 
1500
};
 
1501
 
 
1502
static JSBool
 
1503
Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
1504
{
 
1505
    if (argc == 0) {
 
1506
        /* Trigger logic below to construct a blank object. */
 
1507
        obj = NULL;
 
1508
    } else {
 
1509
        /* If argv[0] is null or undefined, obj comes back null. */
 
1510
        if (!js_ValueToObject(cx, argv[0], &obj))
 
1511
            return JS_FALSE;
 
1512
    }
 
1513
    if (!obj) {
 
1514
        JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
 
1515
        if (cx->fp->flags & JSFRAME_CONSTRUCTING)
 
1516
            return JS_TRUE;
 
1517
        obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
 
1518
        if (!obj)
 
1519
            return JS_FALSE;
 
1520
    }
 
1521
    *rval = OBJECT_TO_JSVAL(obj);
 
1522
    return JS_TRUE;
 
1523
}
 
1524
 
 
1525
/*
 
1526
 * ObjectOps and Class for with-statement stack objects.
 
1527
 */
 
1528
static JSBool
 
1529
with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
 
1530
                    JSProperty **propp
 
1531
#if defined JS_THREADSAFE && defined DEBUG
 
1532
                    , const char *file, uintN line
 
1533
#endif
 
1534
                    )
 
1535
{
 
1536
    JSObject *proto;
 
1537
    JSScopeProperty *sprop;
 
1538
    JSStackFrame *fp;
 
1539
 
 
1540
    proto = OBJ_GET_PROTO(cx, obj);
 
1541
    if (!proto)
 
1542
        return js_LookupProperty(cx, obj, id, objp, propp);
 
1543
    if (!OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp))
 
1544
        return JS_FALSE;
 
1545
 
 
1546
    /*
 
1547
     * Check whether id names an argument or local variable in an active
 
1548
     * function.  If so, pretend we didn't find it, so that the real arg or
 
1549
     * var property can be found in the function's call object, later on in
 
1550
     * the scope chain.  But skip unshared arg and var properties -- those
 
1551
     * result when a script explicitly sets a function "static" property of
 
1552
     * the same name.  See jsinterp.c:SetFunctionSlot.
 
1553
     *
 
1554
     * XXX blame pre-ECMA reflection of function args and vars as properties
 
1555
     */
 
1556
    if ((sprop = (JSScopeProperty *) *propp) &&
 
1557
        (proto = *objp, OBJ_IS_NATIVE(proto)) &&
 
1558
        (sprop->getter == js_GetArgument ||
 
1559
         sprop->getter == js_GetLocalVariable) &&
 
1560
        (sprop->attrs & JSPROP_SHARED)) {
 
1561
        JS_ASSERT(OBJ_GET_CLASS(cx, proto) == &js_FunctionClass);
 
1562
        for (fp = cx->fp; fp && (!fp->fun || fp->fun->native); fp = fp->down)
 
1563
            continue;
 
1564
        if (fp && fp->fun == (JSFunction *) JS_GetPrivate(cx, proto)) {
 
1565
            OBJ_DROP_PROPERTY(cx, proto, *propp);
 
1566
            *objp = NULL;
 
1567
            *propp = NULL;
 
1568
        }
 
1569
    }
 
1570
    return JS_TRUE;
 
1571
}
 
1572
 
 
1573
static JSBool
 
1574
with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 
1575
{
 
1576
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
 
1577
    if (!proto)
 
1578
        return js_GetProperty(cx, obj, id, vp);
 
1579
    return OBJ_GET_PROPERTY(cx, proto, id, vp);
 
1580
}
 
1581
 
 
1582
static JSBool
 
1583
with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 
1584
{
 
1585
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
 
1586
    if (!proto)
 
1587
        return js_SetProperty(cx, obj, id, vp);
 
1588
    return OBJ_SET_PROPERTY(cx, proto, id, vp);
 
1589
}
 
1590
 
 
1591
static JSBool
 
1592
with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
 
1593
                   uintN *attrsp)
 
1594
{
 
1595
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
 
1596
    if (!proto)
 
1597
        return js_GetAttributes(cx, obj, id, prop, attrsp);
 
1598
    return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);
 
1599
}
 
1600
 
 
1601
static JSBool
 
1602
with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
 
1603
                   uintN *attrsp)
 
1604
{
 
1605
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
 
1606
    if (!proto)
 
1607
        return js_SetAttributes(cx, obj, id, prop, attrsp);
 
1608
    return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp);
 
1609
}
 
1610
 
 
1611
static JSBool
 
1612
with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
 
1613
{
 
1614
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
 
1615
    if (!proto)
 
1616
        return js_DeleteProperty(cx, obj, id, rval);
 
1617
    return OBJ_DELETE_PROPERTY(cx, proto, id, rval);
 
1618
}
 
1619
 
 
1620
static JSBool
 
1621
with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
 
1622
{
 
1623
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
 
1624
    if (!proto)
 
1625
        return js_DefaultValue(cx, obj, hint, vp);
 
1626
    return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);
 
1627
}
 
1628
 
 
1629
static JSBool
 
1630
with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
 
1631
               jsval *statep, jsid *idp)
 
1632
{
 
1633
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
 
1634
    if (!proto)
 
1635
        return js_Enumerate(cx, obj, enum_op, statep, idp);
 
1636
    return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);
 
1637
}
 
1638
 
 
1639
static JSBool
 
1640
with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
 
1641
                 jsval *vp, uintN *attrsp)
 
1642
{
 
1643
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
 
1644
    if (!proto)
 
1645
        return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
 
1646
    return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp);
 
1647
}
 
1648
 
 
1649
static JSObject *
 
1650
with_ThisObject(JSContext *cx, JSObject *obj)
 
1651
{
 
1652
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
 
1653
    if (!proto)
 
1654
        return obj;
 
1655
    return OBJ_THIS_OBJECT(cx, proto);
 
1656
}
 
1657
 
 
1658
JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
 
1659
    js_NewObjectMap,        js_DestroyObjectMap,
 
1660
    with_LookupProperty,    js_DefineProperty,
 
1661
    with_GetProperty,       with_SetProperty,
 
1662
    with_GetAttributes,     with_SetAttributes,
 
1663
    with_DeleteProperty,    with_DefaultValue,
 
1664
    with_Enumerate,         with_CheckAccess,
 
1665
    with_ThisObject,        NATIVE_DROP_PROPERTY,
 
1666
    NULL,                   NULL,
 
1667
    NULL,                   NULL,
 
1668
    js_SetProtoOrParent,    js_SetProtoOrParent,
 
1669
    js_Mark,                js_Clear,
 
1670
    NULL,                   NULL
 
1671
};
 
1672
 
 
1673
static JSObjectOps *
 
1674
with_getObjectOps(JSContext *cx, JSClass *clasp)
 
1675
{
 
1676
    return &js_WithObjectOps;
 
1677
}
 
1678
 
 
1679
JSClass js_WithClass = {
 
1680
    "With",
 
1681
    0,
 
1682
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
 
1683
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
 
1684
    with_getObjectOps,
 
1685
    0,0,0,0,0,0,0
 
1686
};
 
1687
 
 
1688
#if JS_HAS_OBJ_PROTO_PROP
 
1689
static JSBool
 
1690
With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
1691
{
 
1692
    JSObject *parent, *proto;
 
1693
    jsval v;
 
1694
 
 
1695
    if (!JS_ReportErrorFlagsAndNumber(cx,
 
1696
                                      JSREPORT_WARNING | JSREPORT_STRICT,
 
1697
                                      js_GetErrorMessage, NULL,
 
1698
                                      JSMSG_DEPRECATED_USAGE,
 
1699
                                      js_WithClass.name)) {
 
1700
        return JS_FALSE;
 
1701
    }
 
1702
 
 
1703
    if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
 
1704
        obj = js_NewObject(cx, &js_WithClass, NULL, NULL);
 
1705
        if (!obj)
 
1706
            return JS_FALSE;
 
1707
        *rval = OBJECT_TO_JSVAL(obj);
 
1708
    }
 
1709
 
 
1710
    parent = cx->fp->scopeChain;
 
1711
    if (argc > 0) {
 
1712
        if (!js_ValueToObject(cx, argv[0], &proto))
 
1713
            return JS_FALSE;
 
1714
        v = OBJECT_TO_JSVAL(proto);
 
1715
        if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v))
 
1716
            return JS_FALSE;
 
1717
        if (argc > 1) {
 
1718
            if (!js_ValueToObject(cx, argv[1], &parent))
 
1719
                return JS_FALSE;
 
1720
        }
 
1721
    }
 
1722
    v = OBJECT_TO_JSVAL(parent);
 
1723
    return obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PARENT), &v);
 
1724
}
 
1725
#endif
 
1726
 
 
1727
JSObject *
 
1728
js_InitObjectClass(JSContext *cx, JSObject *obj)
 
1729
{
 
1730
    JSObject *proto;
 
1731
    jsval eval;
 
1732
 
 
1733
#if JS_HAS_SHARP_VARS
 
1734
    JS_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1);
 
1735
#endif
 
1736
 
 
1737
    proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1,
 
1738
                         object_props, object_methods, NULL, NULL);
 
1739
    if (!proto)
 
1740
        return NULL;
 
1741
 
 
1742
#if JS_HAS_OBJ_PROTO_PROP
 
1743
    if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0,
 
1744
                      NULL, NULL, NULL, NULL)) {
 
1745
        return NULL;
 
1746
    }
 
1747
#endif
 
1748
 
 
1749
    /* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */
 
1750
    if (!OBJ_GET_PROPERTY(cx, proto, (jsid)cx->runtime->atomState.evalAtom,
 
1751
                          &eval)) {
 
1752
        return NULL;
 
1753
    }
 
1754
    if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.evalAtom,
 
1755
                             eval, NULL, NULL, 0, NULL)) {
 
1756
        return NULL;
 
1757
    }
 
1758
 
 
1759
    return proto;
 
1760
}
 
1761
 
 
1762
void
 
1763
js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
 
1764
                 JSClass *clasp)
 
1765
{
 
1766
    map->nrefs = nrefs;
 
1767
    map->ops = ops;
 
1768
    map->nslots = JS_INITIAL_NSLOTS;
 
1769
    map->freeslot = JSSLOT_FREE(clasp);
 
1770
}
 
1771
 
 
1772
JSObjectMap *
 
1773
js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
 
1774
                JSClass *clasp, JSObject *obj)
 
1775
{
 
1776
    return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);
 
1777
}
 
1778
 
 
1779
void
 
1780
js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)
 
1781
{
 
1782
    js_DestroyScope(cx, (JSScope *)map);
 
1783
}
 
1784
 
 
1785
JSObjectMap *
 
1786
js_HoldObjectMap(JSContext *cx, JSObjectMap *map)
 
1787
{
 
1788
    JS_ASSERT(map->nrefs >= 0);
 
1789
    JS_ATOMIC_INCREMENT(&map->nrefs);
 
1790
    return map;
 
1791
}
 
1792
 
 
1793
JSObjectMap *
 
1794
js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)
 
1795
{
 
1796
    JS_ASSERT(map->nrefs > 0);
 
1797
    JS_ATOMIC_DECREMENT(&map->nrefs);
 
1798
    if (map->nrefs == 0) {
 
1799
        map->ops->destroyObjectMap(cx, map);
 
1800
        return NULL;
 
1801
    }
 
1802
    if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)
 
1803
        ((JSScope *)map)->object = NULL;
 
1804
    return map;
 
1805
}
 
1806
 
 
1807
static JSBool
 
1808
GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
 
1809
                  JSObject **protop);
 
1810
 
 
1811
JSObject *
 
1812
js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
 
1813
{
 
1814
    JSObject *obj, *ctor;
 
1815
    JSObjectOps *ops;
 
1816
    JSObjectMap *map;
 
1817
    jsval cval;
 
1818
    uint32 nslots, i;
 
1819
    jsval *newslots;
 
1820
 
 
1821
    /* Allocate an object from the GC heap and zero it. */
 
1822
    obj = (JSObject *) js_AllocGCThing(cx, GCX_OBJECT);
 
1823
    if (!obj)
 
1824
        return NULL;
 
1825
 
 
1826
    /* Bootstrap the ur-object, and make it the default prototype object. */
 
1827
    if (!proto) {
 
1828
        if (!GetClassPrototype(cx, parent, clasp->name, &proto))
 
1829
            goto bad;
 
1830
        if (!proto && !GetClassPrototype(cx, parent, js_Object_str, &proto))
 
1831
            goto bad;
 
1832
    }
 
1833
 
 
1834
    /* Always call the class's getObjectOps hook if it has one. */
 
1835
    ops = clasp->getObjectOps
 
1836
          ? clasp->getObjectOps(cx, clasp)
 
1837
          : &js_ObjectOps;
 
1838
 
 
1839
    /*
 
1840
     * Share proto's map only if it has the same JSObjectOps, and only if
 
1841
     * proto's class has the same private and reserved slots, as obj's map
 
1842
     * and class have.
 
1843
     */
 
1844
    if (proto &&
 
1845
        (map = proto->map)->ops == ops &&
 
1846
        ((clasp->flags ^ OBJ_GET_CLASS(cx, proto)->flags) &
 
1847
         (JSCLASS_HAS_PRIVATE |
 
1848
          (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT)))
 
1849
        == 0) {
 
1850
        /* Default parent to the parent of the prototype's constructor. */
 
1851
        if (!parent) {
 
1852
            if (!OBJ_GET_PROPERTY(cx, proto,
 
1853
                                  (jsid)cx->runtime->atomState.constructorAtom,
 
1854
                                  &cval)) {
 
1855
                goto bad;
 
1856
            }
 
1857
            if (JSVAL_IS_OBJECT(cval) && (ctor = JSVAL_TO_OBJECT(cval)) != NULL)
 
1858
                parent = OBJ_GET_PARENT(cx, ctor);
 
1859
        }
 
1860
 
 
1861
        /* Share the given prototype's map. */
 
1862
        obj->map = js_HoldObjectMap(cx, map);
 
1863
 
 
1864
        /* Ensure that obj starts with the minimum slots for clasp. */
 
1865
        nslots = JS_INITIAL_NSLOTS;
 
1866
    } else {
 
1867
        /* Leave parent alone.  Allocate a new map for obj. */
 
1868
        map = ops->newObjectMap(cx, 1, ops, clasp, obj);
 
1869
        if (!map)
 
1870
            goto bad;
 
1871
        obj->map = map;
 
1872
 
 
1873
        /* Let ops->newObjectMap set nslots so as to reserve slots. */
 
1874
        nslots = map->nslots;
 
1875
    }
 
1876
 
 
1877
    /* Allocate a slots vector, with a -1'st element telling its length. */
 
1878
    newslots = (jsval *) JS_malloc(cx, (nslots + 1) * sizeof(jsval));
 
1879
    if (!newslots) {
 
1880
        js_DropObjectMap(cx, obj->map, obj);
 
1881
        obj->map = NULL;
 
1882
        goto bad;
 
1883
    }
 
1884
    newslots[0] = nslots;
 
1885
    newslots++;
 
1886
 
 
1887
    /* Set the proto, parent, and class properties. */
 
1888
    newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
 
1889
    newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
 
1890
    newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
 
1891
 
 
1892
    /* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */
 
1893
    for (i = JSSLOT_CLASS + 1; i < nslots; i++)
 
1894
        newslots[i] = JSVAL_VOID;
 
1895
 
 
1896
    /* Store newslots after initializing all of 'em, just in case. */
 
1897
    obj->slots = newslots;
 
1898
 
 
1899
    if (cx->runtime->objectHook)
 
1900
        cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
 
1901
 
 
1902
    return obj;
 
1903
 
 
1904
bad:
 
1905
    cx->newborn[GCX_OBJECT] = NULL;
 
1906
    return NULL;
 
1907
}
 
1908
 
 
1909
static JSBool
 
1910
FindConstructor(JSContext *cx, JSObject *scope, const char *name, jsval *vp)
 
1911
{
 
1912
    JSAtom *atom;
 
1913
    JSObject *obj;
 
1914
    JSObject *pobj;
 
1915
    JSScopeProperty *sprop;
 
1916
 
 
1917
    atom = js_Atomize(cx, name, strlen(name), 0);
 
1918
    if (!atom)
 
1919
        return JS_FALSE;
 
1920
 
 
1921
    if (scope || (cx->fp && (scope = cx->fp->scopeChain) != NULL)) {
 
1922
        /* Find the topmost object in the scope chain. */
 
1923
        do {
 
1924
            obj = scope;
 
1925
            scope = OBJ_GET_PARENT(cx, obj);
 
1926
        } while (scope);
 
1927
    } else {
 
1928
        obj = cx->globalObject;
 
1929
        if (!obj) {
 
1930
            *vp = JSVAL_VOID;
 
1931
            return JS_TRUE;
 
1932
        }
 
1933
    }
 
1934
 
 
1935
    if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, (JSProperty**)&sprop))
 
1936
        return JS_FALSE;
 
1937
    if (!sprop)  {
 
1938
        *vp = JSVAL_VOID;
 
1939
        return JS_TRUE;
 
1940
    }
 
1941
 
 
1942
    JS_ASSERT(OBJ_IS_NATIVE(pobj));
 
1943
    JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
 
1944
    *vp = OBJ_GET_SLOT(cx, pobj, sprop->slot);
 
1945
    OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
 
1946
    return JS_TRUE;
 
1947
}
 
1948
 
 
1949
JSObject *
 
1950
js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
 
1951
                   JSObject *parent, uintN argc, jsval *argv)
 
1952
{
 
1953
    jsval cval, rval;
 
1954
    JSObject *obj, *ctor;
 
1955
 
 
1956
    if (!FindConstructor(cx, parent, clasp->name, &cval))
 
1957
        return NULL;
 
1958
    if (JSVAL_IS_PRIMITIVE(cval)) {
 
1959
        js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
 
1960
        return NULL;
 
1961
    }
 
1962
 
 
1963
    /*
 
1964
     * If proto or parent are NULL, set them to Constructor.prototype and/or
 
1965
     * Constructor.__parent__, just like JSOP_NEW does.
 
1966
     */
 
1967
    ctor = JSVAL_TO_OBJECT(cval);
 
1968
    if (!parent)
 
1969
        parent = OBJ_GET_PARENT(cx, ctor);
 
1970
    if (!proto) {
 
1971
        if (!OBJ_GET_PROPERTY(cx, ctor,
 
1972
                              (jsid)cx->runtime->atomState.classPrototypeAtom,
 
1973
                              &rval)) {
 
1974
            return NULL;
 
1975
        }
 
1976
        if (JSVAL_IS_OBJECT(rval))
 
1977
            proto = JSVAL_TO_OBJECT(rval);
 
1978
    }
 
1979
 
 
1980
    obj = js_NewObject(cx, clasp, proto, parent);
 
1981
    if (!obj)
 
1982
        return NULL;
 
1983
 
 
1984
    if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval))
 
1985
        goto bad;
 
1986
    return JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : obj;
 
1987
bad:
 
1988
    cx->newborn[GCX_OBJECT] = NULL;
 
1989
    return NULL;
 
1990
}
 
1991
 
 
1992
void
 
1993
js_FinalizeObject(JSContext *cx, JSObject *obj)
 
1994
{
 
1995
    JSObjectMap *map;
 
1996
 
 
1997
    /* Cope with stillborn objects that have no map. */
 
1998
    map = obj->map;
 
1999
    if (!map)
 
2000
        return;
 
2001
    JS_ASSERT(obj->slots);
 
2002
 
 
2003
    if (cx->runtime->objectHook)
 
2004
        cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData);
 
2005
 
 
2006
    /* Remove all watchpoints with weak links to obj. */
 
2007
    JS_ClearWatchPointsForObject(cx, obj);
 
2008
 
 
2009
    /*
 
2010
     * Finalize obj first, in case it needs map and slots.  Optimized to use
 
2011
     * LOCKED_OBJ_GET_CLASS instead of OBJ_GET_CLASS, so we avoid "promoting"
 
2012
     * obj's scope from lock-free to lock-full (see jslock.c:ClaimScope) when
 
2013
     * we're called from the GC.  Only the GC should call js_FinalizeObject,
 
2014
     * and no other threads run JS (and possibly racing to update obj->slots)
 
2015
     * while the GC is running.
 
2016
     */
 
2017
    LOCKED_OBJ_GET_CLASS(obj)->finalize(cx, obj);
 
2018
 
 
2019
    /* Drop map and free slots. */
 
2020
    js_DropObjectMap(cx, map, obj);
 
2021
    obj->map = NULL;
 
2022
    JS_free(cx, obj->slots - 1);
 
2023
    obj->slots = NULL;
 
2024
}
 
2025
 
 
2026
/* XXXbe if one adds props, deletes earlier props, adds more, the last added
 
2027
         won't recycle the deleted props' slots. */
 
2028
JSBool
 
2029
js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
 
2030
{
 
2031
    JSObjectMap *map;
 
2032
    uint32 nslots, i;
 
2033
    size_t nbytes;
 
2034
    jsval *newslots;
 
2035
 
 
2036
    map = obj->map;
 
2037
    JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
 
2038
    nslots = map->nslots;
 
2039
    if (map->freeslot >= nslots) {
 
2040
        nslots = map->freeslot;
 
2041
        JS_ASSERT(nslots >= JS_INITIAL_NSLOTS);
 
2042
        nslots += (nslots + 1) / 2;
 
2043
 
 
2044
        nbytes = (nslots + 1) * sizeof(jsval);
 
2045
#if defined _MSC_VER && _MSC_VER <= 800
 
2046
        if (nbytes > 60000U) {
 
2047
            JS_ReportOutOfMemory(cx);
 
2048
            return JS_FALSE;
 
2049
        }
 
2050
#endif
 
2051
 
 
2052
        newslots = (jsval *) JS_realloc(cx, obj->slots - 1, nbytes);
 
2053
        if (!newslots)
 
2054
            return JS_FALSE;
 
2055
        for (i = 1 + newslots[0]; i <= nslots; i++)
 
2056
            newslots[i] = JSVAL_VOID;
 
2057
        newslots[0] = map->nslots = nslots;
 
2058
        obj->slots = newslots + 1;
 
2059
    }
 
2060
 
 
2061
#ifdef TOO_MUCH_GC
 
2062
    obj->slots[map->freeslot] = JSVAL_VOID;
 
2063
#endif
 
2064
    *slotp = map->freeslot++;
 
2065
    return JS_TRUE;
 
2066
}
 
2067
 
 
2068
void
 
2069
js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
 
2070
{
 
2071
    JSObjectMap *map;
 
2072
    uint32 nslots;
 
2073
    size_t nbytes;
 
2074
    jsval *newslots;
 
2075
 
 
2076
    OBJ_CHECK_SLOT(obj, slot);
 
2077
    obj->slots[slot] = JSVAL_VOID;
 
2078
    map = obj->map;
 
2079
    JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
 
2080
    if (map->freeslot == slot + 1)
 
2081
        map->freeslot = slot;
 
2082
    nslots = map->nslots;
 
2083
    if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) {
 
2084
        nslots = map->freeslot;
 
2085
        nslots += nslots / 2;
 
2086
        if (nslots < JS_INITIAL_NSLOTS)
 
2087
            nslots = JS_INITIAL_NSLOTS;
 
2088
        nbytes = (nslots + 1) * sizeof(jsval);
 
2089
        newslots = (jsval *) JS_realloc(cx, obj->slots - 1, nbytes);
 
2090
        if (!newslots)
 
2091
            return;
 
2092
        newslots[0] = map->nslots = nslots;
 
2093
        obj->slots = newslots + 1;
 
2094
    }
 
2095
}
 
2096
 
 
2097
#if JS_BUG_EMPTY_INDEX_ZERO
 
2098
#define CHECK_FOR_EMPTY_INDEX(id)                                             \
 
2099
    JS_BEGIN_MACRO                                                            \
 
2100
        if (JSSTRING_LENGTH(_str) == 0)                                       \
 
2101
            id = JSVAL_ZERO;                                                  \
 
2102
    JS_END_MACRO
 
2103
#else
 
2104
#define CHECK_FOR_EMPTY_INDEX(id) /* nothing */
 
2105
#endif
 
2106
 
 
2107
/* JSVAL_INT_MAX as a string */
 
2108
#define JSVAL_INT_MAX_STRING "1073741823"
 
2109
 
 
2110
#define CHECK_FOR_FUNNY_INDEX(id)                                             \
 
2111
    JS_BEGIN_MACRO                                                            \
 
2112
        if (!JSVAL_IS_INT(id)) {                                              \
 
2113
            JSAtom *atom_ = (JSAtom *)id;                                     \
 
2114
            JSString *str_ = ATOM_TO_STRING(atom_);                           \
 
2115
            const jschar *cp_ = str_->chars;                                  \
 
2116
            JSBool negative_ = (*cp_ == '-');                                 \
 
2117
            if (negative_) cp_++;                                             \
 
2118
            if (JS7_ISDEC(*cp_) &&                                            \
 
2119
                str_->length - negative_ <= sizeof(JSVAL_INT_MAX_STRING)-1) { \
 
2120
                id = CheckForFunnyIndex(id, cp_, negative_);                  \
 
2121
            } else {                                                          \
 
2122
                CHECK_FOR_EMPTY_INDEX(id);                                    \
 
2123
            }                                                                 \
 
2124
        }                                                                     \
 
2125
    JS_END_MACRO
 
2126
 
 
2127
static jsid
 
2128
CheckForFunnyIndex(jsid id, const jschar *cp, JSBool negative)
 
2129
{
 
2130
    jsuint index = JS7_UNDEC(*cp++);
 
2131
    jsuint oldIndex = 0;
 
2132
    jsuint c = 0;
 
2133
 
 
2134
    if (index != 0) {
 
2135
        while (JS7_ISDEC(*cp)) {
 
2136
            oldIndex = index;
 
2137
            c = JS7_UNDEC(*cp);
 
2138
            index = 10 * index + c;
 
2139
            cp++;
 
2140
        }
 
2141
    }
 
2142
    if (*cp == 0 &&
 
2143
        (oldIndex < (JSVAL_INT_MAX / 10) ||
 
2144
         (oldIndex == (JSVAL_INT_MAX / 10) &&
 
2145
          c <= (JSVAL_INT_MAX % 10)))) {
 
2146
        if (negative)
 
2147
            index = 0 - index;
 
2148
        id = INT_TO_JSVAL((jsint)index);
 
2149
    }
 
2150
    return id;
 
2151
}
 
2152
 
 
2153
JSScopeProperty *
 
2154
js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
 
2155
                     JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
 
2156
                     uintN attrs, uintN flags, intN shortid)
 
2157
{
 
2158
    JSScope *scope;
 
2159
    JSScopeProperty *sprop;
 
2160
 
 
2161
    JS_LOCK_OBJ(cx, obj);
 
2162
    scope = js_GetMutableScope(cx, obj);
 
2163
    if (!scope) {
 
2164
        sprop = NULL;
 
2165
    } else {
 
2166
        /*
 
2167
         * Handle old bug that took empty string as zero index.  Also convert
 
2168
         * string indices to integers if appropriate.
 
2169
         */
 
2170
        CHECK_FOR_FUNNY_INDEX(id);
 
2171
        sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
 
2172
                                    flags, shortid);
 
2173
    }
 
2174
    JS_UNLOCK_OBJ(cx, obj);
 
2175
    return sprop;
 
2176
}
 
2177
 
 
2178
JSScopeProperty *
 
2179
js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
 
2180
                             JSScopeProperty *sprop, uintN attrs, uintN mask,
 
2181
                             JSPropertyOp getter, JSPropertyOp setter)
 
2182
{
 
2183
    JSScope *scope;
 
2184
 
 
2185
    JS_LOCK_OBJ(cx, obj);
 
2186
    scope = js_GetMutableScope(cx, obj);
 
2187
    if (!scope) {
 
2188
        sprop = NULL;
 
2189
    } else {
 
2190
        sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask,
 
2191
                                            getter, setter);
 
2192
        if (sprop) {
 
2193
            PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, sprop->id,
 
2194
                                sprop);
 
2195
        }
 
2196
    }
 
2197
    JS_UNLOCK_OBJ(cx, obj);
 
2198
    return sprop;
 
2199
}
 
2200
 
 
2201
JSBool
 
2202
js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
 
2203
                  JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
 
2204
                  JSProperty **propp)
 
2205
{
 
2206
    return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs,
 
2207
                                   0, 0, propp);
 
2208
}
 
2209
 
 
2210
JSBool
 
2211
js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
 
2212
                        JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
 
2213
                        uintN flags, intN shortid, JSProperty **propp)
 
2214
{
 
2215
    JSClass *clasp;
 
2216
    JSScope *scope;
 
2217
    JSScopeProperty *sprop;
 
2218
 
 
2219
    /*
 
2220
     * Handle old bug that took empty string as zero index.  Also convert
 
2221
     * string indices to integers if appropriate.
 
2222
     */
 
2223
    CHECK_FOR_FUNNY_INDEX(id);
 
2224
 
 
2225
#if JS_HAS_GETTER_SETTER
 
2226
    /*
 
2227
     * If defining a getter or setter, we must check for its counterpart and
 
2228
     * update the attributes and property ops.  A getter or setter is really
 
2229
     * only half of a property.
 
2230
     */
 
2231
    if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
 
2232
        JSObject *pobj;
 
2233
 
 
2234
        /*
 
2235
         * If JS_THREADSAFE and id is found, js_LookupProperty returns with
 
2236
         * sprop non-null and pobj locked.  If pobj == obj, the property is
 
2237
         * already in obj and obj has its own (mutable) scope.  So if we are
 
2238
         * defining a getter whose setter was already defined, or vice versa,
 
2239
         * finish the job via js_ChangeScopePropertyAttributes, and refresh
 
2240
         * the property cache line for (obj, id) to map sprop.
 
2241
         */
 
2242
        if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop))
 
2243
            return JS_FALSE;
 
2244
        if (sprop &&
 
2245
            pobj == obj &&
 
2246
            (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
 
2247
            sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop,
 
2248
                                                attrs, sprop->attrs,
 
2249
                                                (attrs & JSPROP_GETTER)
 
2250
                                                ? getter
 
2251
                                                : sprop->getter,
 
2252
                                                (attrs & JSPROP_SETTER)
 
2253
                                                ? setter
 
2254
                                                : sprop->setter);
 
2255
 
 
2256
            /* NB: obj == pobj, so we can share unlock code at the bottom. */
 
2257
            if (!sprop)
 
2258
                goto bad;
 
2259
            goto out;
 
2260
        }
 
2261
 
 
2262
        if (sprop) {
 
2263
            /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */
 
2264
            OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
 
2265
            sprop = NULL;
 
2266
        }
 
2267
    }
 
2268
#endif /* JS_HAS_GETTER_SETTER */
 
2269
 
 
2270
    /* Lock if object locking is required by this implementation. */
 
2271
    JS_LOCK_OBJ(cx, obj);
 
2272
 
 
2273
    /* Use the object's class getter and setter by default. */
 
2274
    clasp = LOCKED_OBJ_GET_CLASS(obj);
 
2275
    if (!getter)
 
2276
        getter = clasp->getProperty;
 
2277
    if (!setter)
 
2278
        setter = clasp->setProperty;
 
2279
 
 
2280
    /* Get obj's own scope if it has one, or create a new one for obj. */
 
2281
    scope = js_GetMutableScope(cx, obj);
 
2282
    if (!scope)
 
2283
        goto bad;
 
2284
 
 
2285
    /* Add the property to scope, or replace an existing one of the same id. */
 
2286
    if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
 
2287
        attrs |= JSPROP_SHARED;
 
2288
    sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
 
2289
                                SPROP_INVALID_SLOT, attrs, flags, shortid);
 
2290
    if (!sprop)
 
2291
        goto bad;
 
2292
 
 
2293
    /* XXXbe called with lock held */
 
2294
    if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), &value)) {
 
2295
        (void) js_RemoveScopeProperty(cx, scope, id);
 
2296
        goto bad;
 
2297
    }
 
2298
 
 
2299
    if (SPROP_HAS_VALID_SLOT(sprop, scope))
 
2300
        LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
 
2301
 
 
2302
#if JS_HAS_GETTER_SETTER
 
2303
out:
 
2304
#endif
 
2305
    PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
 
2306
    if (propp)
 
2307
        *propp = (JSProperty *) sprop;
 
2308
    else
 
2309
        JS_UNLOCK_OBJ(cx, obj);
 
2310
    return JS_TRUE;
 
2311
 
 
2312
bad:
 
2313
    JS_UNLOCK_OBJ(cx, obj);
 
2314
    return JS_FALSE;
 
2315
}
 
2316
 
 
2317
#if defined JS_THREADSAFE && defined DEBUG
 
2318
JS_FRIEND_API(JSBool)
 
2319
_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
 
2320
                   JSProperty **propp, const char *file, uintN line)
 
2321
#else
 
2322
JS_FRIEND_API(JSBool)
 
2323
js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
 
2324
                  JSProperty **propp)
 
2325
#endif
 
2326
{
 
2327
    JSObject *start, *obj2, *proto;
 
2328
    JSScope *scope;
 
2329
    JSScopeProperty *sprop;
 
2330
    JSClass *clasp;
 
2331
    JSResolveOp resolve;
 
2332
    JSResolvingKey key;
 
2333
    JSResolvingEntry *entry;
 
2334
    uint32 generation;
 
2335
    JSNewResolveOp newresolve;
 
2336
    uintN flags;
 
2337
    uint32 format;
 
2338
    JSBool ok;
 
2339
 
 
2340
    /*
 
2341
     * Handle old bug that took empty string as zero index.  Also convert
 
2342
     * string indices to integers if appropriate.
 
2343
     */
 
2344
    CHECK_FOR_FUNNY_INDEX(id);
 
2345
 
 
2346
    /* Search scopes starting with obj and following the prototype link. */
 
2347
    start = obj;
 
2348
    for (;;) {
 
2349
        JS_LOCK_OBJ(cx, obj);
 
2350
        SET_OBJ_INFO(obj, file, line);
 
2351
        scope = OBJ_SCOPE(obj);
 
2352
        if (scope->object == obj) {
 
2353
            sprop = SCOPE_GET_PROPERTY(scope, id);
 
2354
        } else {
 
2355
            /* Shared prototype scope: try resolve before lookup. */
 
2356
            sprop = NULL;
 
2357
        }
 
2358
 
 
2359
        /* Try obj's class resolve hook if id was not found in obj's scope. */
 
2360
        if (!sprop) {
 
2361
            clasp = LOCKED_OBJ_GET_CLASS(obj);
 
2362
            resolve = clasp->resolve;
 
2363
            if (resolve != JS_ResolveStub) {
 
2364
                /* Avoid recursion on (obj, id) already being resolved on cx. */
 
2365
                key.obj = obj;
 
2366
                key.id = id;
 
2367
 
 
2368
                /*
 
2369
                 * Once we have successfully added an entry for (obj, key) to
 
2370
                 * cx->resolvingTable, control must go through cleanup: before
 
2371
                 * returning.  But note that JS_DHASH_ADD may find an existing
 
2372
                 * entry, in which case we bail to suppress runaway recursion.
 
2373
                 */
 
2374
                if (!StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
 
2375
                    JS_UNLOCK_OBJ(cx, obj);
 
2376
                    return JS_FALSE;
 
2377
                }
 
2378
                if (!entry) {
 
2379
                    /* Already resolving id in obj -- dampen recursion. */
 
2380
                    JS_UNLOCK_OBJ(cx, obj);
 
2381
                    goto out;
 
2382
                }
 
2383
                generation = cx->resolvingTable->generation;
 
2384
 
 
2385
                /* Null *propp here so we can test it at cleanup: safely. */
 
2386
                *propp = NULL;
 
2387
 
 
2388
                if (clasp->flags & JSCLASS_NEW_RESOLVE) {
 
2389
                    newresolve = (JSNewResolveOp)resolve;
 
2390
                    flags = 0;
 
2391
                    if (cx->fp && cx->fp->pc) {
 
2392
                        format = js_CodeSpec[*cx->fp->pc].format;
 
2393
                        if ((format & JOF_MODEMASK) != JOF_NAME)
 
2394
                            flags |= JSRESOLVE_QUALIFIED;
 
2395
                        if ((format & JOF_ASSIGNING) ||
 
2396
                            (cx->fp->flags & JSFRAME_ASSIGNING)) {
 
2397
                            flags |= JSRESOLVE_ASSIGNING;
 
2398
                        }
 
2399
                    }
 
2400
                    obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START)
 
2401
                           ? start
 
2402
                           : NULL;
 
2403
                    JS_UNLOCK_OBJ(cx, obj);
 
2404
 
 
2405
                    /* Protect id and all atoms from a GC nested in resolve. */
 
2406
                    JS_KEEP_ATOMS(cx->runtime);
 
2407
                    ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2);
 
2408
                    JS_UNKEEP_ATOMS(cx->runtime);
 
2409
                    if (!ok)
 
2410
                        goto cleanup;
 
2411
 
 
2412
                    JS_LOCK_OBJ(cx, obj);
 
2413
                    SET_OBJ_INFO(obj, file, line);
 
2414
                    if (obj2) {
 
2415
                        /* Resolved: juggle locks and lookup id again. */
 
2416
                        if (obj2 != obj) {
 
2417
                            JS_UNLOCK_OBJ(cx, obj);
 
2418
                            JS_LOCK_OBJ(cx, obj2);
 
2419
                        }
 
2420
                        scope = OBJ_SCOPE(obj2);
 
2421
                        if (!MAP_IS_NATIVE(&scope->map)) {
 
2422
                            /* Whoops, newresolve handed back a foreign obj2. */
 
2423
                            JS_ASSERT(obj2 != obj);
 
2424
                            JS_UNLOCK_OBJ(cx, obj2);
 
2425
                            ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp);
 
2426
                            if (!ok || *propp)
 
2427
                                goto cleanup;
 
2428
                            JS_LOCK_OBJ(cx, obj2);
 
2429
                        } else {
 
2430
                            /*
 
2431
                             * Require that obj2 have its own scope now, as we
 
2432
                             * do for old-style resolve.  If it doesn't, then
 
2433
                             * id was not truly resolved, and we'll find it in
 
2434
                             * the proto chain, or miss it if obj2's proto is
 
2435
                             * not on obj's proto chain.  That last case is a
 
2436
                             * "too bad!" case.
 
2437
                             */
 
2438
                            if (scope->object == obj2)
 
2439
                                sprop = SCOPE_GET_PROPERTY(scope, id);
 
2440
                        }
 
2441
                        if (obj2 != obj && !sprop) {
 
2442
                            JS_UNLOCK_OBJ(cx, obj2);
 
2443
                            JS_LOCK_OBJ(cx, obj);
 
2444
                        }
 
2445
                    }
 
2446
                } else {
 
2447
                    /*
 
2448
                     * Old resolve always requires id re-lookup if obj owns
 
2449
                     * its scope after resolve returns.
 
2450
                     */
 
2451
                    JS_UNLOCK_OBJ(cx, obj);
 
2452
                    ok = resolve(cx, obj, ID_TO_VALUE(id));
 
2453
                    if (!ok)
 
2454
                        goto cleanup;
 
2455
                    JS_LOCK_OBJ(cx, obj);
 
2456
                    SET_OBJ_INFO(obj, file, line);
 
2457
                    scope = OBJ_SCOPE(obj);
 
2458
                    JS_ASSERT(MAP_IS_NATIVE(&scope->map));
 
2459
                    if (scope->object == obj)
 
2460
                        sprop = SCOPE_GET_PROPERTY(scope, id);
 
2461
                }
 
2462
 
 
2463
            cleanup:
 
2464
                StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
 
2465
                if (!ok || *propp)
 
2466
                    return ok;
 
2467
            }
 
2468
        }
 
2469
 
 
2470
        if (sprop) {
 
2471
            JS_ASSERT(OBJ_SCOPE(obj) == scope);
 
2472
            *objp = scope->object;      /* XXXbe hide in jsscope.[ch] */
 
2473
 
 
2474
            *propp = (JSProperty *) sprop;
 
2475
            return JS_TRUE;
 
2476
        }
 
2477
 
 
2478
        proto = LOCKED_OBJ_GET_PROTO(obj);
 
2479
        JS_UNLOCK_OBJ(cx, obj);
 
2480
        if (!proto)
 
2481
            break;
 
2482
        if (!OBJ_IS_NATIVE(proto))
 
2483
            return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
 
2484
        obj = proto;
 
2485
    }
 
2486
 
 
2487
out:
 
2488
    *objp = NULL;
 
2489
    *propp = NULL;
 
2490
    return JS_TRUE;
 
2491
}
 
2492
 
 
2493
JS_FRIEND_API(JSBool)
 
2494
js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
 
2495
                JSProperty **propp)
 
2496
{
 
2497
    JSRuntime *rt;
 
2498
    JSObject *obj, *pobj, *lastobj;
 
2499
    JSScopeProperty *sprop;
 
2500
    JSProperty *prop;
 
2501
 
 
2502
    rt = cx->runtime;
 
2503
    obj = cx->fp->scopeChain;
 
2504
    do {
 
2505
        /* Try the property cache and return immediately on cache hit. */
 
2506
        if (OBJ_IS_NATIVE(obj)) {
 
2507
            JS_LOCK_OBJ(cx, obj);
 
2508
            PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop);
 
2509
            if (sprop) {
 
2510
                JS_ASSERT(OBJ_IS_NATIVE(obj));
 
2511
                *objp = obj;
 
2512
                *pobjp = obj;
 
2513
                *propp = (JSProperty *) sprop;
 
2514
                return JS_TRUE;
 
2515
            }
 
2516
            JS_UNLOCK_OBJ(cx, obj);
 
2517
        }
 
2518
 
 
2519
        /* If cache miss, take the slow path. */
 
2520
        if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
 
2521
            return JS_FALSE;
 
2522
        if (prop) {
 
2523
            if (OBJ_IS_NATIVE(pobj)) {
 
2524
                sprop = (JSScopeProperty *) prop;
 
2525
                PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop);
 
2526
            }
 
2527
            *objp = obj;
 
2528
            *pobjp = pobj;
 
2529
            *propp = prop;
 
2530
            return JS_TRUE;
 
2531
        }
 
2532
        lastobj = obj;
 
2533
    } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
 
2534
 
 
2535
    *objp = lastobj;
 
2536
    *pobjp = NULL;
 
2537
    *propp = NULL;
 
2538
    return JS_TRUE;
 
2539
}
 
2540
 
 
2541
JSObject *
 
2542
js_FindIdentifierBase(JSContext *cx, jsid id)
 
2543
{
 
2544
    JSObject *obj, *pobj;
 
2545
    JSProperty *prop;
 
2546
 
 
2547
    /*
 
2548
     * Look for id's property along the "with" statement chain and the
 
2549
     * statically-linked scope chain.
 
2550
     */
 
2551
    if (!js_FindProperty(cx, id, &obj, &pobj, &prop))
 
2552
        return NULL;
 
2553
    if (prop) {
 
2554
        OBJ_DROP_PROPERTY(cx, pobj, prop);
 
2555
        return obj;
 
2556
    }
 
2557
 
 
2558
    /*
 
2559
     * Use the top-level scope from the scope chain, which won't end in the
 
2560
     * same scope as cx->globalObject for cross-context function calls.
 
2561
     */
 
2562
    JS_ASSERT(obj);
 
2563
 
 
2564
    /*
 
2565
     * Property not found.  Give a strict warning if binding an undeclared
 
2566
     * top-level variable.
 
2567
     */
 
2568
    if (JS_HAS_STRICT_OPTION(cx)) {
 
2569
        JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id));
 
2570
        if (!JS_ReportErrorFlagsAndNumber(cx,
 
2571
                                          JSREPORT_WARNING | JSREPORT_STRICT,
 
2572
                                          js_GetErrorMessage, NULL,
 
2573
                                          JSMSG_UNDECLARED_VAR,
 
2574
                                          JS_GetStringBytes(str))) {
 
2575
            return NULL;
 
2576
        }
 
2577
    }
 
2578
    return obj;
 
2579
}
 
2580
 
 
2581
JSBool
 
2582
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 
2583
{
 
2584
    JSObject *obj2;
 
2585
    JSScopeProperty *sprop;
 
2586
    JSScope *scope;
 
2587
    uint32 slot;
 
2588
 
 
2589
    /*
 
2590
     * Handle old bug that took empty string as zero index.  Also convert
 
2591
     * string indices to integers if appropriate.
 
2592
     */
 
2593
    CHECK_FOR_FUNNY_INDEX(id);
 
2594
 
 
2595
    if (!js_LookupProperty(cx, obj, id, &obj2, (JSProperty **)&sprop))
 
2596
        return JS_FALSE;
 
2597
    if (!sprop) {
 
2598
        jsval default_val;
 
2599
 
 
2600
#if JS_BUG_NULL_INDEX_PROPS
 
2601
        /* Indexed properties defaulted to null in old versions. */
 
2602
        default_val = (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0)
 
2603
                      ? JSVAL_NULL
 
2604
                      : JSVAL_VOID;
 
2605
#else
 
2606
        default_val = JSVAL_VOID;
 
2607
#endif
 
2608
        *vp = default_val;
 
2609
 
 
2610
        if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp))
 
2611
            return JS_FALSE;
 
2612
 
 
2613
        /*
 
2614
         * Give a strict warning if foo.bar is evaluated by a script for an
 
2615
         * object foo with no property named 'bar'.
 
2616
         */
 
2617
        if (JS_HAS_STRICT_OPTION(cx) &&
 
2618
            *vp == default_val &&
 
2619
            cx->fp && cx->fp->pc &&
 
2620
            (*cx->fp->pc == JSOP_GETPROP || *cx->fp->pc == JSOP_GETELEM))
 
2621
        {
 
2622
            jsbytecode *pc, *endpc;
 
2623
            JSString *str;
 
2624
 
 
2625
            /* Kludge to allow (typeof foo == "undefined") tests. */
 
2626
            JS_ASSERT(cx->fp->script);
 
2627
            pc = cx->fp->pc;
 
2628
            pc += js_CodeSpec[*pc].length;
 
2629
            endpc = cx->fp->script->code + cx->fp->script->length;
 
2630
            while (pc < endpc) {
 
2631
                if (*pc == JSOP_TYPEOF)
 
2632
                    return JS_TRUE;
 
2633
                if (*pc != JSOP_GROUP)
 
2634
                    break;
 
2635
                pc++;
 
2636
            }
 
2637
 
 
2638
            /* Ok, bad undefined property reference: whine about it. */
 
2639
            str = js_DecompileValueGenerator(cx, JS_FALSE, ID_TO_VALUE(id),
 
2640
                                             NULL);
 
2641
            if (!str ||
 
2642
                !JS_ReportErrorFlagsAndNumber(cx,
 
2643
                                              JSREPORT_WARNING|JSREPORT_STRICT,
 
2644
                                              js_GetErrorMessage, NULL,
 
2645
                                              JSMSG_UNDEFINED_PROP,
 
2646
                                              JS_GetStringBytes(str))) {
 
2647
                return JS_FALSE;
 
2648
            }
 
2649
        }
 
2650
        return JS_TRUE;
 
2651
    }
 
2652
 
 
2653
    if (!OBJ_IS_NATIVE(obj2)) {
 
2654
        OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
 
2655
        return OBJ_GET_PROPERTY(cx, obj2, id, vp);
 
2656
    }
 
2657
 
 
2658
    /* Unlock obj2 before calling getter, relock after to avoid deadlock. */
 
2659
    scope = OBJ_SCOPE(obj2);
 
2660
    slot = sprop->slot;
 
2661
    if (slot != SPROP_INVALID_SLOT) {
 
2662
        JS_ASSERT(slot < obj2->map->freeslot);
 
2663
        *vp = LOCKED_OBJ_GET_SLOT(obj2, slot);
 
2664
 
 
2665
        /* If sprop has a stub getter, we're done. */
 
2666
        if (!sprop->getter)
 
2667
            goto out;
 
2668
    } else {
 
2669
        *vp = JSVAL_VOID;
 
2670
    }
 
2671
 
 
2672
    JS_UNLOCK_SCOPE(cx, scope);
 
2673
    if (!SPROP_GET(cx, sprop, obj, obj2, vp))
 
2674
        return JS_FALSE;
 
2675
    JS_LOCK_SCOPE(cx, scope);
 
2676
 
 
2677
    if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
 
2678
        LOCKED_OBJ_SET_SLOT(obj2, slot, *vp);
 
2679
        PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop);
 
2680
    }
 
2681
 
 
2682
out:
 
2683
    JS_UNLOCK_SCOPE(cx, scope);
 
2684
    return JS_TRUE;
 
2685
}
 
2686
 
 
2687
JSBool
 
2688
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 
2689
{
 
2690
    JSObject *pobj;
 
2691
    JSScopeProperty *sprop;
 
2692
    JSScope *scope;
 
2693
    uintN attrs, flags;
 
2694
    intN shortid;
 
2695
    JSClass *clasp;
 
2696
    JSPropertyOp getter, setter;
 
2697
    jsval pval;
 
2698
    uint32 slot;
 
2699
 
 
2700
    /*
 
2701
     * Handle old bug that took empty string as zero index.  Also convert
 
2702
     * string indices to integers if appropriate.
 
2703
     */
 
2704
    CHECK_FOR_FUNNY_INDEX(id);
 
2705
 
 
2706
    if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop))
 
2707
        return JS_FALSE;
 
2708
 
 
2709
    if (sprop && !OBJ_IS_NATIVE(pobj)) {
 
2710
        OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
 
2711
        sprop = NULL;
 
2712
    }
 
2713
 
 
2714
    /*
 
2715
     * Now either sprop is null, meaning id was not found in obj or one of its
 
2716
     * prototypes; or sprop is non-null, meaning id was found in pobj's scope.
 
2717
     * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop
 
2718
     * is held: we must OBJ_DROP_PROPERTY or JS_UNLOCK_SCOPE before we return
 
2719
     * (the two are equivalent for native objects, but we use JS_UNLOCK_SCOPE
 
2720
     * because it is cheaper).
 
2721
     */
 
2722
    attrs = JSPROP_ENUMERATE;
 
2723
    flags = 0;
 
2724
    shortid = 0;
 
2725
    clasp = OBJ_GET_CLASS(cx, obj);
 
2726
    getter = clasp->getProperty;
 
2727
    setter = clasp->setProperty;
 
2728
 
 
2729
    if (sprop) {
 
2730
        /*
 
2731
         * Set scope for use below.  It was locked by js_LookupProperty, and
 
2732
         * we know pobj owns it (i.e., scope->object == pobj).  Therefore we
 
2733
         * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope).
 
2734
         */
 
2735
        scope = OBJ_SCOPE(pobj);
 
2736
 
 
2737
        attrs = sprop->attrs;
 
2738
        if ((attrs & JSPROP_READONLY) ||
 
2739
            (SCOPE_IS_SEALED(scope) && pobj == obj)) {
 
2740
            JS_UNLOCK_SCOPE(cx, scope);
 
2741
            if ((attrs & JSPROP_READONLY) && JSVERSION_IS_ECMA(cx->version))
 
2742
                return JS_TRUE;
 
2743
            goto read_only_error;
 
2744
        }
 
2745
 
 
2746
        if (pobj != obj) {
 
2747
            /*
 
2748
             * We found id in a prototype object: prepare to share or shadow.
 
2749
             * NB: Thanks to the immutable, garbage-collected property tree
 
2750
             * maintained by jsscope.c in cx->runtime, we needn't worry about
 
2751
             * sprop going away behind our back after we've unlocked scope.
 
2752
             */
 
2753
            JS_UNLOCK_SCOPE(cx, scope);
 
2754
 
 
2755
            /* Don't clone a shared prototype property. */
 
2756
            if (attrs & JSPROP_SHARED)
 
2757
                return SPROP_SET(cx, sprop, obj, pobj, vp);
 
2758
 
 
2759
            /* Restore attrs to the ECMA default for new properties. */
 
2760
            attrs = JSPROP_ENUMERATE;
 
2761
 
 
2762
            /*
 
2763
             * Preserve the shortid, getter, and setter when shadowing any
 
2764
             * property that has a shortid.  An old API convention requires
 
2765
             * that the property's getter and setter functions receive the
 
2766
             * shortid, not id, when they are called on the shadow we are
 
2767
             * about to create in obj's scope.
 
2768
             */
 
2769
            if (sprop->flags & SPROP_HAS_SHORTID) {
 
2770
                flags = SPROP_HAS_SHORTID;
 
2771
                shortid = sprop->shortid;
 
2772
                getter = sprop->getter;
 
2773
                setter = sprop->setter;
 
2774
            }
 
2775
 
 
2776
            /*
 
2777
             * Forget we found the proto-property now that we've copied any
 
2778
             * needed member values.
 
2779
             */
 
2780
            sprop = NULL;
 
2781
        }
 
2782
#ifdef __GNUC__         /* suppress bogus gcc warnings */
 
2783
    } else {
 
2784
        scope = NULL;
 
2785
#endif
 
2786
    }
 
2787
 
 
2788
    if (!sprop) {
 
2789
        if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj)
 
2790
            goto read_only_error;
 
2791
 
 
2792
        /* Find or make a property descriptor with the right heritage. */
 
2793
        JS_LOCK_OBJ(cx, obj);
 
2794
        scope = js_GetMutableScope(cx, obj);
 
2795
        if (!scope) {
 
2796
            JS_UNLOCK_OBJ(cx, obj);
 
2797
            return JS_FALSE;
 
2798
        }
 
2799
        if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
 
2800
            attrs |= JSPROP_SHARED;
 
2801
        sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
 
2802
                                    SPROP_INVALID_SLOT, attrs, flags, shortid);
 
2803
        if (!sprop) {
 
2804
            JS_UNLOCK_SCOPE(cx, scope);
 
2805
            return JS_FALSE;
 
2806
        }
 
2807
 
 
2808
        /* XXXbe called with obj locked */
 
2809
        if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), vp)) {
 
2810
            (void) js_RemoveScopeProperty(cx, scope, id);
 
2811
            JS_UNLOCK_SCOPE(cx, scope);
 
2812
            return JS_FALSE;
 
2813
        }
 
2814
 
 
2815
        /* Initialize new property value (passed to setter) to undefined. */
 
2816
        if (SPROP_HAS_VALID_SLOT(sprop, scope))
 
2817
            LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);
 
2818
 
 
2819
        PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
 
2820
    }
 
2821
 
 
2822
    /* Get the current property value from its slot. */
 
2823
    slot = sprop->slot;
 
2824
    if (slot != SPROP_INVALID_SLOT) {
 
2825
        JS_ASSERT(slot < obj->map->freeslot);
 
2826
        pval = LOCKED_OBJ_GET_SLOT(obj, slot);
 
2827
 
 
2828
        /* If sprop has a stub setter, keep scope locked and just store *vp. */
 
2829
        if (!sprop->setter)
 
2830
            goto set_slot;
 
2831
    }
 
2832
 
 
2833
    /* Avoid deadlock by unlocking obj's scope while calling sprop's setter. */
 
2834
    JS_UNLOCK_SCOPE(cx, scope);
 
2835
 
 
2836
    /* Let the setter modify vp before copying from it to obj->slots[slot]. */
 
2837
    if (!SPROP_SET(cx, sprop, obj, obj, vp))
 
2838
        return JS_FALSE;
 
2839
 
 
2840
    /* Relock obj's scope until we are done with sprop. */
 
2841
    JS_LOCK_SCOPE(cx, scope);
 
2842
 
 
2843
    /*
 
2844
     * Check whether sprop is still around (was not deleted), and whether it
 
2845
     * has a slot (it may never have had one, or we may have lost a race with
 
2846
     * someone who cleared scope).
 
2847
     */
 
2848
    if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
 
2849
  set_slot:
 
2850
        GC_POKE(cx, pval);
 
2851
        LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
 
2852
    }
 
2853
    JS_UNLOCK_SCOPE(cx, scope);
 
2854
    return JS_TRUE;
 
2855
 
 
2856
  read_only_error: {
 
2857
    JSString *str = js_DecompileValueGenerator(cx,
 
2858
                                               JSDVG_IGNORE_STACK,
 
2859
                                               ID_TO_VALUE(id),
 
2860
                                               NULL);
 
2861
    if (str) {
 
2862
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
2863
                             JSMSG_READ_ONLY,
 
2864
                             JS_GetStringBytes(str));
 
2865
    }
 
2866
    return JS_FALSE;
 
2867
  }
 
2868
}
 
2869
 
 
2870
JSBool
 
2871
js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
 
2872
                 uintN *attrsp)
 
2873
{
 
2874
    JSBool noprop, ok;
 
2875
    JSScopeProperty *sprop;
 
2876
 
 
2877
    noprop = !prop;
 
2878
    if (noprop) {
 
2879
        if (!js_LookupProperty(cx, obj, id, &obj, &prop))
 
2880
            return JS_FALSE;
 
2881
        if (!prop) {
 
2882
            *attrsp = 0;
 
2883
            return JS_TRUE;
 
2884
        }
 
2885
        if (!OBJ_IS_NATIVE(obj)) {
 
2886
            ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp);
 
2887
            OBJ_DROP_PROPERTY(cx, obj, prop);
 
2888
            return ok;
 
2889
        }
 
2890
    }
 
2891
    sprop = (JSScopeProperty *)prop;
 
2892
    *attrsp = sprop->attrs;
 
2893
    if (noprop)
 
2894
        OBJ_DROP_PROPERTY(cx, obj, prop);
 
2895
    return JS_TRUE;
 
2896
}
 
2897
 
 
2898
JSBool
 
2899
js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
 
2900
                 uintN *attrsp)
 
2901
{
 
2902
    JSBool noprop, ok;
 
2903
    JSScopeProperty *sprop;
 
2904
 
 
2905
    noprop = !prop;
 
2906
    if (noprop) {
 
2907
        if (!js_LookupProperty(cx, obj, id, &obj, &prop))
 
2908
            return JS_FALSE;
 
2909
        if (!prop)
 
2910
            return JS_TRUE;
 
2911
        if (!OBJ_IS_NATIVE(obj)) {
 
2912
            ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp);
 
2913
            OBJ_DROP_PROPERTY(cx, obj, prop);
 
2914
            return ok;
 
2915
        }
 
2916
    }
 
2917
    sprop = (JSScopeProperty *)prop;
 
2918
    sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
 
2919
                                         *attrsp &
 
2920
                                         ~(JSPROP_GETTER | JSPROP_SETTER), 0,
 
2921
                                         sprop->getter, sprop->setter);
 
2922
    if (noprop)
 
2923
        OBJ_DROP_PROPERTY(cx, obj, prop);
 
2924
    return (sprop != NULL);
 
2925
}
 
2926
 
 
2927
JSBool
 
2928
js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
 
2929
{
 
2930
#if JS_HAS_PROP_DELETE
 
2931
 
 
2932
    JSObject *proto;
 
2933
    JSProperty *prop;
 
2934
    JSScopeProperty *sprop;
 
2935
    JSString *str;
 
2936
    JSScope *scope;
 
2937
    JSBool ok;
 
2938
 
 
2939
    *rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID;
 
2940
 
 
2941
    /*
 
2942
     * Handle old bug that took empty string as zero index.  Also convert
 
2943
     * string indices to integers if appropriate.
 
2944
     */
 
2945
    CHECK_FOR_FUNNY_INDEX(id);
 
2946
 
 
2947
    if (!js_LookupProperty(cx, obj, id, &proto, &prop))
 
2948
        return JS_FALSE;
 
2949
    if (!prop || proto != obj) {
 
2950
        /*
 
2951
         * If the property was found in a native prototype, check whether it's
 
2952
         * shared and permanent.  Such a property stands for direct properties
 
2953
         * in all delegating objects, matching ECMA semantics without bloating
 
2954
         * each delegating object.
 
2955
         */
 
2956
        if (prop) {
 
2957
            if (OBJ_IS_NATIVE(proto)) {
 
2958
                sprop = (JSScopeProperty *)prop;
 
2959
                if (SPROP_IS_SHARED_PERMANENT(sprop))
 
2960
                    *rval = JSVAL_FALSE;
 
2961
            }
 
2962
            OBJ_DROP_PROPERTY(cx, proto, prop);
 
2963
            if (*rval == JSVAL_FALSE)
 
2964
                return JS_TRUE;
 
2965
        }
 
2966
 
 
2967
        /*
 
2968
         * If no property, or the property comes unshared or impermanent from
 
2969
         * a prototype, call the class's delProperty hook, passing rval as the
 
2970
         * result parameter.
 
2971
         */
 
2972
        return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id),
 
2973
                                                   rval);
 
2974
    }
 
2975
 
 
2976
    sprop = (JSScopeProperty *)prop;
 
2977
    if (sprop->attrs & JSPROP_PERMANENT) {
 
2978
        OBJ_DROP_PROPERTY(cx, obj, prop);
 
2979
        if (JSVERSION_IS_ECMA(cx->version)) {
 
2980
            *rval = JSVAL_FALSE;
 
2981
            return JS_TRUE;
 
2982
        }
 
2983
        str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
 
2984
                                         ID_TO_VALUE(id), NULL);
 
2985
        if (str) {
 
2986
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
2987
                                 JSMSG_PERMANENT, JS_GetStringBytes(str));
 
2988
        }
 
2989
        return JS_FALSE;
 
2990
    }
 
2991
 
 
2992
    /* XXXbe called with obj locked */
 
2993
    if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop),
 
2994
                                                rval)) {
 
2995
        OBJ_DROP_PROPERTY(cx, obj, prop);
 
2996
        return JS_FALSE;
 
2997
    }
 
2998
 
 
2999
    scope = OBJ_SCOPE(obj);
 
3000
    if (SPROP_HAS_VALID_SLOT(sprop, scope))
 
3001
        GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));
 
3002
 
 
3003
    PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL);
 
3004
    ok = js_RemoveScopeProperty(cx, scope, id);
 
3005
    OBJ_DROP_PROPERTY(cx, obj, prop);
 
3006
    return ok;
 
3007
 
 
3008
#else  /* !JS_HAS_PROP_DELETE */
 
3009
 
 
3010
    jsval null = JSVAL_NULL;
 
3011
 
 
3012
    *rval = JSVAL_VOID;
 
3013
    return js_SetProperty(cx, obj, id, &null);
 
3014
 
 
3015
#endif /* !JS_HAS_PROP_DELETE */
 
3016
}
 
3017
 
 
3018
JSBool
 
3019
js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
 
3020
{
 
3021
    jsval v;
 
3022
    JSString *str;
 
3023
 
 
3024
    v = OBJECT_TO_JSVAL(obj);
 
3025
    switch (hint) {
 
3026
      case JSTYPE_STRING:
 
3027
        /*
 
3028
         * Propagate the exception if js_TryMethod finds an appropriate
 
3029
         * method, and calling that method returned failure.
 
3030
         */
 
3031
        if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL,
 
3032
                          &v))
 
3033
            return JS_FALSE;
 
3034
 
 
3035
        if (!JSVAL_IS_PRIMITIVE(v)) {
 
3036
            if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
 
3037
                return JS_FALSE;
 
3038
 
 
3039
            /*
 
3040
             * JS1.2 never failed (except for malloc failure) to convert an
 
3041
             * object to a string.  ECMA requires an error if both toString
 
3042
             * and valueOf fail to produce a primitive value.
 
3043
             */
 
3044
            if (!JSVAL_IS_PRIMITIVE(v) && cx->version == JSVERSION_1_2) {
 
3045
                char *bytes = JS_smprintf("[object %s]",
 
3046
                                          OBJ_GET_CLASS(cx, obj)->name);
 
3047
                if (!bytes)
 
3048
                    return JS_FALSE;
 
3049
                str = JS_NewString(cx, bytes, strlen(bytes));
 
3050
                if (!str) {
 
3051
                    free(bytes);
 
3052
                    return JS_FALSE;
 
3053
                }
 
3054
                v = STRING_TO_JSVAL(str);
 
3055
                goto out;
 
3056
            }
 
3057
        }
 
3058
        break;
 
3059
 
 
3060
      default:
 
3061
        if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
 
3062
            return JS_FALSE;
 
3063
        if (!JSVAL_IS_PRIMITIVE(v)) {
 
3064
            JSType type = JS_TypeOfValue(cx, v);
 
3065
            if (type == hint ||
 
3066
                (type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) {
 
3067
                goto out;
 
3068
            }
 
3069
            /* Don't convert to string (source object literal) for JS1.2. */
 
3070
            if (cx->version == JSVERSION_1_2 && hint == JSTYPE_BOOLEAN)
 
3071
                goto out;
 
3072
            if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0,
 
3073
                              NULL, &v))
 
3074
                return JS_FALSE;
 
3075
        }
 
3076
        break;
 
3077
    }
 
3078
    if (!JSVAL_IS_PRIMITIVE(v)) {
 
3079
        /* Avoid recursive death through js_DecompileValueGenerator. */
 
3080
        if (hint == JSTYPE_STRING) {
 
3081
            str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name);
 
3082
            if (!str)
 
3083
                return JS_FALSE;
 
3084
        } else {
 
3085
            str = NULL;
 
3086
        }
 
3087
        *vp = OBJECT_TO_JSVAL(obj);
 
3088
        str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, str);
 
3089
        if (str) {
 
3090
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
3091
                                 JSMSG_CANT_CONVERT_TO,
 
3092
                                 JS_GetStringBytes(str),
 
3093
                                 (hint == JSTYPE_VOID)
 
3094
                                 ? "primitive type"
 
3095
                                 : js_type_str[hint]);
 
3096
        }
 
3097
        return JS_FALSE;
 
3098
    }
 
3099
out:
 
3100
    *vp = v;
 
3101
    return JS_TRUE;
 
3102
}
 
3103
 
 
3104
JSIdArray *
 
3105
js_NewIdArray(JSContext *cx, jsint length)
 
3106
{
 
3107
    JSIdArray *ida;
 
3108
 
 
3109
    ida = (JSIdArray *)
 
3110
        JS_malloc(cx, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));
 
3111
    if (ida)
 
3112
        ida->length = length;
 
3113
    return ida;
 
3114
}
 
3115
 
 
3116
JSIdArray *
 
3117
js_GrowIdArray(JSContext *cx, JSIdArray *ida, jsint length)
 
3118
{
 
3119
    ida = (JSIdArray *)
 
3120
        JS_realloc(cx, ida, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));
 
3121
    if (ida)
 
3122
        ida->length = length;
 
3123
    return ida;
 
3124
}
 
3125
 
 
3126
/* Private type used to iterate over all properties of a native JS object */
 
3127
typedef struct JSNativeIteratorState {
 
3128
    jsint next_index;   /* index into jsid array */
 
3129
    JSIdArray *ida;     /* All property ids in enumeration */
 
3130
} JSNativeIteratorState;
 
3131
 
 
3132
/*
 
3133
 * This function is used to enumerate the properties of native JSObjects
 
3134
 * and those host objects that do not define a JSNewEnumerateOp-style iterator
 
3135
 * function.
 
3136
 */
 
3137
JSBool
 
3138
js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
 
3139
             jsval *statep, jsid *idp)
 
3140
{
 
3141
    JSObject *proto_obj;
 
3142
    JSClass *clasp;
 
3143
    JSEnumerateOp enumerate;
 
3144
    JSScopeProperty *sprop, *lastProp;
 
3145
    jsint i, length;
 
3146
    JSScope *scope;
 
3147
    JSIdArray *ida;
 
3148
    JSNativeIteratorState *state;
 
3149
 
 
3150
    clasp = OBJ_GET_CLASS(cx, obj);
 
3151
    enumerate = clasp->enumerate;
 
3152
    if (clasp->flags & JSCLASS_NEW_ENUMERATE)
 
3153
        return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
 
3154
 
 
3155
    switch (enum_op) {
 
3156
 
 
3157
    case JSENUMERATE_INIT:
 
3158
        if (!enumerate(cx, obj))
 
3159
            goto init_error;
 
3160
        length = 0;
 
3161
 
 
3162
        /*
 
3163
         * The set of all property ids is pre-computed when the iterator
 
3164
         * is initialized so as to avoid problems with properties being
 
3165
         * deleted during the iteration.
 
3166
         */
 
3167
        JS_LOCK_OBJ(cx, obj);
 
3168
        scope = OBJ_SCOPE(obj);
 
3169
 
 
3170
        /*
 
3171
         * If this object shares a scope with its prototype, don't enumerate
 
3172
         * its properties.  Otherwise they will be enumerated a second time
 
3173
         * when the prototype object is enumerated.
 
3174
         */
 
3175
        proto_obj = OBJ_GET_PROTO(cx, obj);
 
3176
        if (proto_obj && scope == OBJ_SCOPE(proto_obj)) {
 
3177
            ida = js_NewIdArray(cx, 0);
 
3178
            if (!ida) {
 
3179
                JS_UNLOCK_OBJ(cx, obj);
 
3180
                goto init_error;
 
3181
            }
 
3182
        } else {
 
3183
            /* Object has a private scope; Enumerate all props in scope. */
 
3184
            for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop;
 
3185
                 sprop = sprop->parent) {
 
3186
                if ((sprop->attrs & JSPROP_ENUMERATE) &&
 
3187
                    !(sprop->flags & SPROP_IS_ALIAS) &&
 
3188
                    (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
 
3189
                     SCOPE_HAS_PROPERTY(scope, sprop))) {
 
3190
                    length++;
 
3191
                }
 
3192
            }
 
3193
            ida = js_NewIdArray(cx, length);
 
3194
            if (!ida) {
 
3195
                JS_UNLOCK_OBJ(cx, obj);
 
3196
                goto init_error;
 
3197
            }
 
3198
            i = length;
 
3199
            for (sprop = lastProp; sprop; sprop = sprop->parent) {
 
3200
                if ((sprop->attrs & JSPROP_ENUMERATE) &&
 
3201
                    !(sprop->flags & SPROP_IS_ALIAS) &&
 
3202
                    (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
 
3203
                     SCOPE_HAS_PROPERTY(scope, sprop))) {
 
3204
                    JS_ASSERT(i > 0);
 
3205
                    ida->vector[--i] = sprop->id;
 
3206
                }
 
3207
            }
 
3208
        }
 
3209
        JS_UNLOCK_OBJ(cx, obj);
 
3210
 
 
3211
        state = (JSNativeIteratorState *)
 
3212
            JS_malloc(cx, sizeof(JSNativeIteratorState));
 
3213
        if (!state) {
 
3214
            JS_DestroyIdArray(cx, ida);
 
3215
            goto init_error;
 
3216
        }
 
3217
        state->ida = ida;
 
3218
        state->next_index = 0;
 
3219
        *statep = PRIVATE_TO_JSVAL(state);
 
3220
        if (idp)
 
3221
            *idp = INT_TO_JSVAL(length);
 
3222
        return JS_TRUE;
 
3223
 
 
3224
    case JSENUMERATE_NEXT:
 
3225
        state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
 
3226
        ida = state->ida;
 
3227
        length = ida->length;
 
3228
        if (state->next_index != length) {
 
3229
            *idp = ida->vector[state->next_index++];
 
3230
            return JS_TRUE;
 
3231
        }
 
3232
 
 
3233
        /* Fall through ... */
 
3234
 
 
3235
    case JSENUMERATE_DESTROY:
 
3236
        state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
 
3237
        JS_DestroyIdArray(cx, state->ida);
 
3238
        JS_free(cx, state);
 
3239
        *statep = JSVAL_NULL;
 
3240
        return JS_TRUE;
 
3241
 
 
3242
    default:
 
3243
        JS_ASSERT(0);
 
3244
        return JS_FALSE;
 
3245
    }
 
3246
 
 
3247
init_error:
 
3248
    *statep = JSVAL_NULL;
 
3249
    return JS_FALSE;
 
3250
}
 
3251
 
 
3252
JSBool
 
3253
js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
 
3254
               jsval *vp, uintN *attrsp)
 
3255
{
 
3256
    JSObject *pobj;
 
3257
    JSProperty *prop;
 
3258
    JSScopeProperty *sprop;
 
3259
    JSClass *clasp;
 
3260
    JSBool ok;
 
3261
 
 
3262
    if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
 
3263
        return JS_FALSE;
 
3264
    if (!prop) {
 
3265
        *vp = JSVAL_VOID;
 
3266
        *attrsp = 0;
 
3267
        clasp = OBJ_GET_CLASS(cx, obj);
 
3268
        return !clasp->checkAccess ||
 
3269
               clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
 
3270
    }
 
3271
    if (!OBJ_IS_NATIVE(pobj)) {
 
3272
        OBJ_DROP_PROPERTY(cx, pobj, prop);
 
3273
        return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp);
 
3274
    }
 
3275
    sprop = (JSScopeProperty *)prop;
 
3276
    *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)))
 
3277
          ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
 
3278
          : JSVAL_VOID;
 
3279
    *attrsp = sprop->attrs;
 
3280
    clasp = LOCKED_OBJ_GET_CLASS(obj);
 
3281
    if (clasp->checkAccess) {
 
3282
        JS_UNLOCK_OBJ(cx, pobj);
 
3283
        ok = clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
 
3284
        JS_LOCK_OBJ(cx, pobj);
 
3285
    } else {
 
3286
        ok = JS_TRUE;
 
3287
    }
 
3288
    OBJ_DROP_PROPERTY(cx, pobj, prop);
 
3289
    return ok;
 
3290
}
 
3291
 
 
3292
#ifdef JS_THREADSAFE
 
3293
void
 
3294
js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
 
3295
{
 
3296
    JS_UNLOCK_OBJ(cx, obj);
 
3297
}
 
3298
#endif
 
3299
 
 
3300
static void
 
3301
ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
 
3302
{
 
3303
    /*
 
3304
     * The decompiler may need to access the args of the function in
 
3305
     * progress rather than the one we had hoped to call.
 
3306
     * So we switch the cx->fp to the frame below us. We stick the
 
3307
     * current frame in the dormantFrameChain to protect it from gc.
 
3308
     */
 
3309
 
 
3310
    JSStackFrame *fp = cx->fp;
 
3311
    if (fp->down) {
 
3312
        JS_ASSERT(!fp->dormantNext);
 
3313
        fp->dormantNext = cx->dormantFrameChain;
 
3314
        cx->dormantFrameChain = fp;
 
3315
        cx->fp = fp->down;
 
3316
    }
 
3317
 
 
3318
    js_ReportIsNotFunction(cx, vp, flags);
 
3319
 
 
3320
    if (fp->down) {
 
3321
        JS_ASSERT(cx->dormantFrameChain == fp);
 
3322
        cx->dormantFrameChain = fp->dormantNext;
 
3323
        fp->dormantNext = NULL;
 
3324
        cx->fp = fp;
 
3325
    }
 
3326
}
 
3327
 
 
3328
JSBool
 
3329
js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 
3330
{
 
3331
    JSClass *clasp;
 
3332
 
 
3333
    clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
 
3334
    if (!clasp->call) {
 
3335
        ReportIsNotFunction(cx, &argv[-2], 0);
 
3336
        return JS_FALSE;
 
3337
    }
 
3338
    return clasp->call(cx, obj, argc, argv, rval);
 
3339
}
 
3340
 
 
3341
JSBool
 
3342
js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
3343
             jsval *rval)
 
3344
{
 
3345
    JSClass *clasp;
 
3346
 
 
3347
    clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
 
3348
    if (!clasp->construct) {
 
3349
        ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT);
 
3350
        return JS_FALSE;
 
3351
    }
 
3352
    return clasp->construct(cx, obj, argc, argv, rval);
 
3353
}
 
3354
 
 
3355
JSBool
 
3356
js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
 
3357
{
 
3358
    JSClass *clasp;
 
3359
 
 
3360
    clasp = OBJ_GET_CLASS(cx, obj);
 
3361
    if (clasp->hasInstance)
 
3362
        return clasp->hasInstance(cx, obj, v, bp);
 
3363
    *bp = JS_FALSE;
 
3364
    return JS_TRUE;
 
3365
}
 
3366
 
 
3367
JSBool
 
3368
js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
 
3369
{
 
3370
    JSObject *obj2;
 
3371
 
 
3372
    *bp = JS_FALSE;
 
3373
    if (JSVAL_IS_PRIMITIVE(v))
 
3374
        return JS_TRUE;
 
3375
    obj2 = JSVAL_TO_OBJECT(v);
 
3376
    while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) {
 
3377
        if (obj2 == obj) {
 
3378
            *bp = JS_TRUE;
 
3379
            break;
 
3380
        }
 
3381
    }
 
3382
    return JS_TRUE;
 
3383
}
 
3384
 
 
3385
JSBool
 
3386
js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop)
 
3387
{
 
3388
    return GetClassPrototype(cx, NULL, name, protop);
 
3389
}
 
3390
 
 
3391
static JSBool
 
3392
GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
 
3393
                  JSObject **protop)
 
3394
{
 
3395
    jsval v;
 
3396
    JSObject *ctor;
 
3397
 
 
3398
    if (!FindConstructor(cx, scope, name, &v))
 
3399
        return JS_FALSE;
 
3400
    if (JSVAL_IS_FUNCTION(cx, v)) {
 
3401
        ctor = JSVAL_TO_OBJECT(v);
 
3402
        if (!OBJ_GET_PROPERTY(cx, ctor,
 
3403
                              (jsid)cx->runtime->atomState.classPrototypeAtom,
 
3404
                              &v)) {
 
3405
            return JS_FALSE;
 
3406
        }
 
3407
    }
 
3408
    *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
 
3409
    return JS_TRUE;
 
3410
}
 
3411
 
 
3412
JSBool
 
3413
js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
 
3414
                     uintN attrs)
 
3415
{
 
3416
    /*
 
3417
     * Use the given attributes for the prototype property of the constructor,
 
3418
     * as user-defined constructors have a DontEnum | DontDelete prototype (it
 
3419
     * may be reset), while native or "system" constructors require DontEnum |
 
3420
     * ReadOnly | DontDelete.
 
3421
     */
 
3422
    if (!OBJ_DEFINE_PROPERTY(cx, ctor,
 
3423
                             (jsid)cx->runtime->atomState.classPrototypeAtom,
 
3424
                             OBJECT_TO_JSVAL(proto), NULL, NULL,
 
3425
                             attrs, NULL)) {
 
3426
        return JS_FALSE;
 
3427
    }
 
3428
 
 
3429
    /*
 
3430
     * ECMA says that Object.prototype.constructor, or f.prototype.constructor
 
3431
     * for a user-defined function f, is DontEnum.
 
3432
     */
 
3433
    return OBJ_DEFINE_PROPERTY(cx, proto,
 
3434
                               (jsid)cx->runtime->atomState.constructorAtom,
 
3435
                               OBJECT_TO_JSVAL(ctor), NULL, NULL,
 
3436
                               0, NULL);
 
3437
}
 
3438
 
 
3439
JSBool
 
3440
js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
 
3441
{
 
3442
    JSObject *obj;
 
3443
 
 
3444
    if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
 
3445
        obj = NULL;
 
3446
    } else if (JSVAL_IS_OBJECT(v)) {
 
3447
        obj = JSVAL_TO_OBJECT(v);
 
3448
        if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v))
 
3449
            return JS_FALSE;
 
3450
        if (JSVAL_IS_OBJECT(v))
 
3451
            obj = JSVAL_TO_OBJECT(v);
 
3452
    } else {
 
3453
        if (JSVAL_IS_STRING(v)) {
 
3454
            obj = js_StringToObject(cx, JSVAL_TO_STRING(v));
 
3455
        } else if (JSVAL_IS_INT(v)) {
 
3456
            obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v));
 
3457
        } else if (JSVAL_IS_DOUBLE(v)) {
 
3458
            obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v));
 
3459
        } else {
 
3460
            JS_ASSERT(JSVAL_IS_BOOLEAN(v));
 
3461
            obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v));
 
3462
        }
 
3463
        if (!obj)
 
3464
            return JS_FALSE;
 
3465
    }
 
3466
    *objp = obj;
 
3467
    return JS_TRUE;
 
3468
}
 
3469
 
 
3470
JSObject *
 
3471
js_ValueToNonNullObject(JSContext *cx, jsval v)
 
3472
{
 
3473
    JSObject *obj;
 
3474
    JSString *str;
 
3475
 
 
3476
    if (!js_ValueToObject(cx, v, &obj))
 
3477
        return NULL;
 
3478
    if (!obj) {
 
3479
        str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
 
3480
        if (str) {
 
3481
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
3482
                                 JSMSG_NO_PROPERTIES, JS_GetStringBytes(str));
 
3483
        }
 
3484
    }
 
3485
    return obj;
 
3486
}
 
3487
 
 
3488
JSBool
 
3489
js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval)
 
3490
{
 
3491
#if JS_HAS_VALUEOF_HINT
 
3492
    jsval argv[1];
 
3493
 
 
3494
    argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]);
 
3495
    return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv,
 
3496
                        rval);
 
3497
#else
 
3498
    return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 0, NULL,
 
3499
                        rval);
 
3500
#endif
 
3501
}
 
3502
 
 
3503
JSBool
 
3504
js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
 
3505
             uintN argc, jsval *argv, jsval *rval)
 
3506
{
 
3507
    JSErrorReporter older;
 
3508
    jsval fval;
 
3509
    JSBool ok;
 
3510
 
 
3511
    /*
 
3512
     * Report failure only if an appropriate method was found, and calling it
 
3513
     * returned failure.  We propagate failure in this case to make exceptions
 
3514
     * behave properly.
 
3515
     */
 
3516
    older = JS_SetErrorReporter(cx, NULL);
 
3517
    if (OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &fval) &&
 
3518
        !JSVAL_IS_PRIMITIVE(fval)) {
 
3519
        ok = js_InternalCall(cx, obj, fval, argc, argv, rval);
 
3520
    } else {
 
3521
        ok = JS_TRUE;
 
3522
    }
 
3523
    JS_SetErrorReporter(cx, older);
 
3524
    return ok;
 
3525
}
 
3526
 
 
3527
#if JS_HAS_XDR
 
3528
 
 
3529
#include "jsxdrapi.h"
 
3530
 
 
3531
JSBool
 
3532
js_XDRObject(JSXDRState *xdr, JSObject **objp)
 
3533
{
 
3534
    JSContext *cx;
 
3535
    JSClass *clasp;
 
3536
    const char *className;
 
3537
    uint32 classId, classDef;
 
3538
    JSBool ok;
 
3539
    JSObject *proto;
 
3540
 
 
3541
    cx = xdr->cx;
 
3542
    if (xdr->mode == JSXDR_ENCODE) {
 
3543
        clasp = OBJ_GET_CLASS(cx, *objp);
 
3544
        className = clasp->name;
 
3545
        classId = JS_XDRFindClassIdByName(xdr, className);
 
3546
        classDef = !classId;
 
3547
        if (classDef && !JS_XDRRegisterClass(xdr, clasp, &classId))
 
3548
            return JS_FALSE;
 
3549
    } else {
 
3550
        classDef = 0;
 
3551
        className = NULL;
 
3552
        clasp = NULL;           /* quell GCC overwarning */
 
3553
    }
 
3554
 
 
3555
    /* XDR a flag word followed (if true) by the class name. */
 
3556
    if (!JS_XDRUint32(xdr, &classDef))
 
3557
        return JS_FALSE;
 
3558
    if (classDef && !JS_XDRCString(xdr, (char **) &className))
 
3559
        return JS_FALSE;
 
3560
 
 
3561
    /* From here on, return through out: to free className if it was set. */
 
3562
    ok = JS_XDRUint32(xdr, &classId);
 
3563
    if (!ok)
 
3564
        goto out;
 
3565
 
 
3566
    if (xdr->mode != JSXDR_ENCODE) {
 
3567
        if (classDef) {
 
3568
            ok = js_GetClassPrototype(cx, className, &proto);
 
3569
            if (!ok)
 
3570
                goto out;
 
3571
            clasp = OBJ_GET_CLASS(cx, proto);
 
3572
            ok = JS_XDRRegisterClass(xdr, clasp, &classId);
 
3573
            if (!ok)
 
3574
                goto out;
 
3575
        } else {
 
3576
            clasp = JS_XDRFindClassById(xdr, classId);
 
3577
            if (!clasp) {
 
3578
                char numBuf[12];
 
3579
                JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
 
3580
                JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
3581
                                     JSMSG_CANT_FIND_CLASS, numBuf);
 
3582
                ok = JS_FALSE;
 
3583
                goto out;
 
3584
            }
 
3585
        }
 
3586
    }
 
3587
 
 
3588
    if (!clasp->xdrObject) {
 
3589
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 
3590
                             JSMSG_CANT_XDR_CLASS, clasp->name);
 
3591
        ok = JS_FALSE;
 
3592
    } else {
 
3593
        ok = clasp->xdrObject(xdr, objp);
 
3594
    }
 
3595
out:
 
3596
    if (xdr->mode != JSXDR_ENCODE && className)
 
3597
        JS_free(cx, (void *)className);
 
3598
    return ok;
 
3599
}
 
3600
 
 
3601
#endif /* JS_HAS_XDR */
 
3602
 
 
3603
#ifdef DEBUG_brendan
 
3604
 
 
3605
#include <stdio.h>
 
3606
#include <math.h>
 
3607
 
 
3608
uint32 js_entry_count_max;
 
3609
uint32 js_entry_count_sum;
 
3610
double js_entry_count_sqsum;
 
3611
uint32 js_entry_count_hist[11];
 
3612
 
 
3613
static void
 
3614
MeterEntryCount(uintN count)
 
3615
{
 
3616
    if (count) {
 
3617
        js_entry_count_sum += count;
 
3618
        js_entry_count_sqsum += (double)count * count;
 
3619
        if (count > js_entry_count_max)
 
3620
            js_entry_count_max = count;
 
3621
    }
 
3622
    js_entry_count_hist[JS_MIN(count, 10)]++;
 
3623
}
 
3624
 
 
3625
void
 
3626
js_DumpScopeMeters(JSRuntime *rt)
 
3627
{
 
3628
    static FILE *logfp;
 
3629
    if (!logfp)
 
3630
        logfp = fopen("/tmp/scope.stats", "a");
 
3631
 
 
3632
    {
 
3633
        double mean = 0., var = 0., sigma = 0.;
 
3634
        double nscopes = rt->liveScopes;
 
3635
        double nentrys = js_entry_count_sum;
 
3636
        if (nscopes > 0 && nentrys >= 0) {
 
3637
            mean = nentrys / nscopes;
 
3638
            var = nscopes * js_entry_count_sqsum - nentrys * nentrys;
 
3639
            if (var < 0.0 || nscopes <= 1)
 
3640
                var = 0.0;
 
3641
            else
 
3642
                var /= nscopes * (nscopes - 1);
 
3643
 
 
3644
            /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
 
3645
            sigma = (var != 0.) ? sqrt(var) : 0.;
 
3646
        }
 
3647
 
 
3648
        fprintf(logfp,
 
3649
                "scopes %g entries %g mean %g sigma %g max %u",
 
3650
                nscopes, nentrys, mean, sigma, js_entry_count_max);
 
3651
    }
 
3652
 
 
3653
    fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u\n",
 
3654
            js_entry_count_hist[0], js_entry_count_hist[1],
 
3655
            js_entry_count_hist[2], js_entry_count_hist[3],
 
3656
            js_entry_count_hist[4], js_entry_count_hist[5],
 
3657
            js_entry_count_hist[6], js_entry_count_hist[7],
 
3658
            js_entry_count_hist[8], js_entry_count_hist[9],
 
3659
            js_entry_count_hist[10]);
 
3660
    js_entry_count_sum = js_entry_count_max = 0;
 
3661
    js_entry_count_sqsum = 0;
 
3662
    memset(js_entry_count_hist, 0, sizeof js_entry_count_hist);
 
3663
    fflush(logfp);
 
3664
}
 
3665
 
 
3666
#endif /* DEBUG_brendan */
 
3667
 
 
3668
uint32
 
3669
js_Mark(JSContext *cx, JSObject *obj, void *arg)
 
3670
{
 
3671
    JSScope *scope;
 
3672
    JSScopeProperty *sprop;
 
3673
    JSClass *clasp;
 
3674
 
 
3675
    JS_ASSERT(OBJ_IS_NATIVE(obj));
 
3676
    scope = OBJ_SCOPE(obj);
 
3677
#ifdef DEBUG_brendan
 
3678
    if (scope->object == obj)
 
3679
        MeterEntryCount(scope->entryCount);
 
3680
#endif
 
3681
 
 
3682
    JS_ASSERT(!SCOPE_LAST_PROP(scope) ||
 
3683
              SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope)));
 
3684
 
 
3685
    for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
 
3686
        if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
 
3687
            continue;
 
3688
        MARK_SCOPE_PROPERTY(sprop);
 
3689
        if (!JSVAL_IS_INT(sprop->id))
 
3690
            GC_MARK_ATOM(cx, (JSAtom *)sprop->id, arg);
 
3691
 
 
3692
#if JS_HAS_GETTER_SETTER
 
3693
        if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
 
3694
#ifdef GC_MARK_DEBUG
 
3695
            char buf[64];
 
3696
            JSAtom *atom = (JSAtom *)sprop->id;
 
3697
            const char *id = (atom && ATOM_IS_STRING(atom))
 
3698
                             ? JS_GetStringBytes(ATOM_TO_STRING(atom))
 
3699
                             : "unknown";
 
3700
#endif
 
3701
 
 
3702
            if (sprop->attrs & JSPROP_GETTER) {
 
3703
#ifdef GC_MARK_DEBUG
 
3704
                JS_snprintf(buf, sizeof buf, "%s %s",
 
3705
                            id, js_getter_str);
 
3706
#endif
 
3707
                GC_MARK(cx,
 
3708
                        JSVAL_TO_GCTHING((jsval) sprop->getter),
 
3709
                        buf,
 
3710
                        arg);
 
3711
            }
 
3712
            if (sprop->attrs & JSPROP_SETTER) {
 
3713
#ifdef GC_MARK_DEBUG
 
3714
                JS_snprintf(buf, sizeof buf, "%s %s",
 
3715
                            id, js_setter_str);
 
3716
#endif
 
3717
                GC_MARK(cx,
 
3718
                        JSVAL_TO_GCTHING((jsval) sprop->setter),
 
3719
                        buf,
 
3720
                        arg);
 
3721
            }
 
3722
        }
 
3723
#endif /* JS_HAS_GETTER_SETTER */
 
3724
    }
 
3725
 
 
3726
    /* No one runs while the GC is running, so we can use LOCKED_... here. */
 
3727
    clasp = LOCKED_OBJ_GET_CLASS(obj);
 
3728
    if (clasp->mark)
 
3729
        (void) clasp->mark(cx, obj, arg);
 
3730
 
 
3731
    if (scope->object != obj) {
 
3732
        /*
 
3733
         * An unmutated object that shares a prototype's scope.  We can't tell
 
3734
         * how many slots are allocated and in use at obj->slots by looking at
 
3735
         * scope, so we get obj->slots' length from its -1'st element.
 
3736
         */
 
3737
        return (uint32) obj->slots[-1];
 
3738
    }
 
3739
    return JS_MIN(obj->map->freeslot, obj->map->nslots);
 
3740
}
 
3741
 
 
3742
void
 
3743
js_Clear(JSContext *cx, JSObject *obj)
 
3744
{
 
3745
    JSScope *scope;
 
3746
    JSRuntime *rt;
 
3747
    JSScopeProperty *sprop;
 
3748
    uint32 i, n;
 
3749
 
 
3750
    /*
 
3751
     * Clear our scope and the property cache of all obj's properties only if
 
3752
     * obj owns the scope (i.e., not if obj is unmutated and therefore sharing
 
3753
     * its prototype's scope).  NB: we do not clear any reserved slots lying
 
3754
     * below JSSLOT_FREE(clasp).
 
3755
     */
 
3756
    JS_LOCK_OBJ(cx, obj);
 
3757
    scope = OBJ_SCOPE(obj);
 
3758
    if (scope->object == obj) {
 
3759
        /* Clear the property cache before we clear the scope. */
 
3760
        rt = cx->runtime;
 
3761
        for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
 
3762
            if (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
 
3763
                SCOPE_HAS_PROPERTY(scope, sprop)) {
 
3764
                PROPERTY_CACHE_FILL(&rt->propertyCache, obj, sprop->id, NULL);
 
3765
            }
 
3766
        }
 
3767
 
 
3768
        /* Now that we're done using scope->lastProp/table, clear scope. */
 
3769
        js_ClearScope(cx, scope);
 
3770
 
 
3771
        /* Clear slot values and reset freeslot so we're consistent. */
 
3772
        i = scope->map.nslots;
 
3773
        n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj));
 
3774
        while (--i >= n)
 
3775
            obj->slots[i] = JSVAL_VOID;
 
3776
        scope->map.freeslot = n;
 
3777
    }
 
3778
    JS_UNLOCK_OBJ(cx, obj);
 
3779
}
 
3780
 
 
3781
jsval
 
3782
js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot)
 
3783
{
 
3784
    jsval v;
 
3785
 
 
3786
    JS_LOCK_OBJ(cx, obj);
 
3787
    v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID;
 
3788
    JS_UNLOCK_OBJ(cx, obj);
 
3789
    return v;
 
3790
}
 
3791
 
 
3792
void
 
3793
js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
 
3794
{
 
3795
    uint32 nslots, rlimit, i;
 
3796
    JSClass *clasp;
 
3797
    jsval *newslots;
 
3798
 
 
3799
    JS_LOCK_OBJ(cx, obj);
 
3800
    nslots = (uint32) obj->slots[-1];
 
3801
    if (slot >= nslots) {
 
3802
        clasp = LOCKED_OBJ_GET_CLASS(obj);
 
3803
        rlimit = JSSLOT_START(clasp) + JSCLASS_RESERVED_SLOTS(clasp);
 
3804
        JS_ASSERT(slot < rlimit);
 
3805
        if (rlimit > nslots)
 
3806
            nslots = rlimit;
 
3807
 
 
3808
        newslots = (jsval *)
 
3809
            JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
 
3810
        if (!newslots) {
 
3811
            JS_UNLOCK_OBJ(cx, obj);
 
3812
            return;
 
3813
        }
 
3814
        for (i = 1 + newslots[0]; i <= rlimit; i++)
 
3815
            newslots[i] = JSVAL_VOID;
 
3816
        newslots[0] = nslots;
 
3817
        if (OBJ_SCOPE(obj)->object == obj)
 
3818
            obj->map->nslots = nslots;
 
3819
        obj->slots = newslots + 1;
 
3820
    }
 
3821
 
 
3822
    obj->slots[slot] = v;
 
3823
    JS_UNLOCK_OBJ(cx, obj);
 
3824
}
 
3825
 
 
3826
#ifdef DEBUG
 
3827
 
 
3828
/* Routines to print out values during debugging. */
 
3829
 
 
3830
void printChar(jschar *cp) {
 
3831
    fprintf(stderr, "jschar* (0x%p) \"", (void *)cp);
 
3832
    while (*cp)
 
3833
        fputc(*cp++, stderr);
 
3834
    fputc('"', stderr);
 
3835
    fputc('\n', stderr);
 
3836
}
 
3837
 
 
3838
void printString(JSString *str) {
 
3839
    size_t i, n;
 
3840
    jschar *s;
 
3841
    fprintf(stderr, "string (0x%p) \"", (void *)str);
 
3842
    s = JSSTRING_CHARS(str);
 
3843
    for (i=0, n=JSSTRING_LENGTH(str); i < n; i++)
 
3844
        fputc(s[i], stderr);
 
3845
    fputc('"', stderr);
 
3846
    fputc('\n', stderr);
 
3847
}
 
3848
 
 
3849
void printVal(JSContext *cx, jsval val);
 
3850
 
 
3851
void printObj(JSContext *cx, JSObject *jsobj) {
 
3852
    jsuint i;
 
3853
    jsval val;
 
3854
    JSClass *clasp;
 
3855
 
 
3856
    fprintf(stderr, "object 0x%p\n", (void *)jsobj);
 
3857
    clasp = OBJ_GET_CLASS(cx, jsobj);
 
3858
    fprintf(stderr, "class 0x%p %s\n", (void *)clasp, clasp->name);
 
3859
    for (i=0; i < jsobj->map->nslots; i++) {
 
3860
        fprintf(stderr, "slot %3d ", i);
 
3861
        val = jsobj->slots[i];
 
3862
        if (JSVAL_IS_OBJECT(val))
 
3863
            fprintf(stderr, "object 0x%p\n", (void *)JSVAL_TO_OBJECT(val));
 
3864
        else
 
3865
            printVal(cx, val);
 
3866
    }
 
3867
}
 
3868
 
 
3869
void printVal(JSContext *cx, jsval val) {
 
3870
    fprintf(stderr, "val %d (0x%p) = ", (int)val, (void *)val);
 
3871
    if (JSVAL_IS_NULL(val)) {
 
3872
        fprintf(stderr, "null\n");
 
3873
    } else if (JSVAL_IS_VOID(val)) {
 
3874
        fprintf(stderr, "undefined\n");
 
3875
    } else if (JSVAL_IS_OBJECT(val)) {
 
3876
        printObj(cx, JSVAL_TO_OBJECT(val));
 
3877
    } else if (JSVAL_IS_INT(val)) {
 
3878
        fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val));
 
3879
    } else if (JSVAL_IS_STRING(val)) {
 
3880
        printString(JSVAL_TO_STRING(val));
 
3881
    } else if (JSVAL_IS_DOUBLE(val)) {
 
3882
        fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val));
 
3883
    } else {
 
3884
        JS_ASSERT(JSVAL_IS_BOOLEAN(val));
 
3885
        fprintf(stderr, "(boolean) %s\n",
 
3886
                JSVAL_TO_BOOLEAN(val) ? "true" : "false");
 
3887
    }
 
3888
    fflush(stderr);
 
3889
}
 
3890
 
 
3891
void printId(JSContext *cx, jsid id) {
 
3892
    fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id);
 
3893
    printVal(cx, ID_TO_VALUE(id));
 
3894
}
 
3895
 
 
3896
void printAtom(JSAtom *atom) {
 
3897
    printString(ATOM_TO_STRING(atom));
 
3898
}
 
3899
 
 
3900
#endif