~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

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