~centralelyon2010/inkscape/imagelinks2

« back to all changes in this revision

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

  • Committer: ishmal
  • Date: 2006-04-12 13:25:21 UTC
  • Revision ID: ishmal@users.sourceforge.net-20060412132521-5ynoezpwbzq4d1c3
Add new rearranged /dom directory

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