~ubuntu-branches/ubuntu/trusty/xulrunner/trusty

1 by Mike Hommey
Import upstream version 1.8.0.4
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
 * vim: set ts=8 sw=4 et tw=78:
3
 *
4
 * ***** BEGIN LICENSE BLOCK *****
5
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6
 *
7
 * The contents of this file are subject to the Mozilla Public License Version
8
 * 1.1 (the "License"); you may not use this file except in compliance with
9
 * the License. You may obtain a copy of the License at
10
 * http://www.mozilla.org/MPL/
11
 *
12
 * Software distributed under the License is distributed on an "AS IS" basis,
13
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14
 * for the specific language governing rights and limitations under the
15
 * License.
16
 *
17
 * The Original Code is Mozilla Communicator client code, released
18
 * March 31, 1998.
19
 *
20
 * The Initial Developer of the Original Code is
21
 * Netscape Communications Corporation.
22
 * Portions created by the Initial Developer are Copyright (C) 1998
23
 * the Initial Developer. All Rights Reserved.
24
 *
25
 * Contributor(s):
26
 *
27
 * Alternatively, the contents of this file may be used under the terms of
28
 * either of the GNU General Public License Version 2 or later (the "GPL"),
29
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30
 * in which case the provisions of the GPL or the LGPL are applicable instead
31
 * of those above. If you wish to allow use of your version of this file only
32
 * under the terms of either the GPL or the LGPL, and not to allow others to
33
 * use your version of this file under the terms of the MPL, indicate your
34
 * decision by deleting the provisions above and replace them with the notice
35
 * and other provisions required by the GPL or the LGPL. If you do not delete
36
 * the provisions above, a recipient may use your version of this file under
37
 * the terms of any one of the MPL, the GPL or the LGPL.
38
 *
39
 * ***** END LICENSE BLOCK ***** */
40
41
/*
42
 * JS object implementation.
43
 */
44
#include "jsstddef.h"
45
#include <stdlib.h>
46
#include <string.h>
47
#include "jstypes.h"
48
#include "jsarena.h" /* Added by JSIFY */
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
49
#include "jsbit.h"
1 by Mike Hommey
Import upstream version 1.8.0.4
50
#include "jsutil.h" /* Added by JSIFY */
51
#include "jshash.h" /* Added by JSIFY */
52
#include "jsdhash.h"
53
#include "jsprf.h"
54
#include "jsapi.h"
55
#include "jsarray.h"
56
#include "jsatom.h"
57
#include "jsbool.h"
58
#include "jscntxt.h"
59
#include "jsconfig.h"
60
#include "jsfun.h"
61
#include "jsgc.h"
62
#include "jsinterp.h"
63
#include "jslock.h"
64
#include "jsnum.h"
65
#include "jsobj.h"
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
66
#include "jsscan.h"
1 by Mike Hommey
Import upstream version 1.8.0.4
67
#include "jsscope.h"
68
#include "jsscript.h"
69
#include "jsstr.h"
70
#include "jsopcode.h"
71
72
#include "jsdbgapi.h"   /* whether or not JS_HAS_OBJ_WATCHPOINT */
73
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
74
#if JS_HAS_GENERATORS
75
#include "jsiter.h"
76
#endif
77
1 by Mike Hommey
Import upstream version 1.8.0.4
78
#if JS_HAS_XML_SUPPORT
79
#include "jsxml.h"
80
#endif
81
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
82
#if JS_HAS_XDR
83
#include "jsxdrapi.h"
84
#endif
85
1 by Mike Hommey
Import upstream version 1.8.0.4
86
#ifdef JS_THREADSAFE
87
#define NATIVE_DROP_PROPERTY js_DropProperty
88
89
extern void
90
js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
91
#else
92
#define NATIVE_DROP_PROPERTY NULL
93
#endif
94
95
JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
96
    js_NewObjectMap,        js_DestroyObjectMap,
97
    js_LookupProperty,      js_DefineProperty,
98
    js_GetProperty,         js_SetProperty,
99
    js_GetAttributes,       js_SetAttributes,
100
    js_DeleteProperty,      js_DefaultValue,
101
    js_Enumerate,           js_CheckAccess,
102
    NULL,                   NATIVE_DROP_PROPERTY,
103
    js_Call,                js_Construct,
104
    NULL,                   js_HasInstance,
105
    js_SetProtoOrParent,    js_SetProtoOrParent,
106
    js_Mark,                js_Clear,
107
    js_GetRequiredSlot,     js_SetRequiredSlot
108
};
109
110
JSClass js_ObjectClass = {
111
    js_Object_str,
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
112
    JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
1 by Mike Hommey
Import upstream version 1.8.0.4
113
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
114
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
115
    JSCLASS_NO_OPTIONAL_MEMBERS
116
};
117
118
#if JS_HAS_OBJ_PROTO_PROP
119
120
static JSBool
121
obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
122
123
static JSBool
124
obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
125
126
static JSBool
127
obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
128
129
static JSPropertySpec object_props[] = {
130
    /* These two must come first; see object_props[slot].name usage below. */
131
    {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED,
132
                                                  obj_getSlot,  obj_setSlot},
133
    {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
134
                                                  obj_getSlot,  obj_setSlot},
135
    {js_count_str, 0,            JSPROP_PERMANENT,obj_getCount, obj_getCount},
136
    {0,0,0,0,0}
137
};
138
139
/* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
140
#define JSSLOT_COUNT 2
141
142
static JSBool
143
ReportStrictSlot(JSContext *cx, uint32 slot)
144
{
145
    if (slot == JSSLOT_PROTO)
146
        return JS_TRUE;
147
    return JS_ReportErrorFlagsAndNumber(cx,
148
                                        JSREPORT_WARNING | JSREPORT_STRICT,
149
                                        js_GetErrorMessage, NULL,
150
                                        JSMSG_DEPRECATED_USAGE,
151
                                        object_props[slot].name);
152
}
153
154
static JSBool
155
obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
156
{
157
    uint32 slot;
158
    jsid propid;
159
    JSAccessMode mode;
160
    uintN attrs;
161
    JSObject *pobj;
162
    JSClass *clasp;
163
    JSExtendedClass *xclasp;
164
165
    slot = (uint32) JSVAL_TO_INT(id);
166
    if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
167
        propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
168
        mode = JSACC_PROTO;
169
    } else {
170
        propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
171
        mode = JSACC_PARENT;
172
    }
173
174
    /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */
175
    if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs))
176
        return JS_FALSE;
177
178
    pobj = JSVAL_TO_OBJECT(*vp);
179
    if (pobj) {
180
        clasp = OBJ_GET_CLASS(cx, pobj);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
181
        if (clasp == &js_CallClass || clasp == &js_BlockClass) {
182
            /* Censor activations and lexical scopes per ECMA-262. */
183
            *vp = JSVAL_NULL;
184
        } else if (clasp->flags & JSCLASS_IS_EXTENDED) {
1 by Mike Hommey
Import upstream version 1.8.0.4
185
            xclasp = (JSExtendedClass *) clasp;
186
            if (xclasp->outerObject) {
187
                pobj = xclasp->outerObject(cx, pobj);
188
                if (!pobj)
189
                    return JS_FALSE;
190
                *vp = OBJECT_TO_JSVAL(pobj);
191
            }
192
        }
193
    }
194
    return JS_TRUE;
195
}
196
197
static JSBool
198
obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
199
{
200
    JSObject *pobj;
201
    uint32 slot;
202
    jsid propid;
203
    uintN attrs;
204
205
    if (!JSVAL_IS_OBJECT(*vp))
206
        return JS_TRUE;
207
    pobj = JSVAL_TO_OBJECT(*vp);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
208
209
    if (pobj) {
210
        /*
211
         * Innerize pobj here to avoid sticking unwanted properties on the
212
         * outer object. This ensures that any with statements only grant
213
         * access to the inner object.
214
         */
215
        OBJ_TO_INNER_OBJECT(cx, pobj);
216
        if (!pobj)
217
            return JS_FALSE;
218
    }
1 by Mike Hommey
Import upstream version 1.8.0.4
219
    slot = (uint32) JSVAL_TO_INT(id);
220
    if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
221
        return JS_FALSE;
222
223
    /* __parent__ is readonly and permanent, only __proto__ may be set. */
224
    propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
225
    if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_PROTO|JSACC_WRITE, vp, &attrs))
226
        return JS_FALSE;
227
228
    return js_SetProtoOrParent(cx, obj, slot, pobj);
229
}
230
231
static JSBool
232
obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
233
{
234
    jsval iter_state;
235
    jsid num_properties;
236
    JSBool ok;
237
238
    if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
239
        return JS_FALSE;
240
241
    /* Get the number of properties to enumerate. */
242
    iter_state = JSVAL_NULL;
243
    ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
244
    if (!ok)
245
        goto out;
246
247
    if (!JSVAL_IS_INT(num_properties)) {
248
        JS_ASSERT(0);
249
        *vp = JSVAL_ZERO;
250
        goto out;
251
    }
252
    *vp = num_properties;
253
254
out:
255
    if (iter_state != JSVAL_NULL)
256
        ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
257
    return ok;
258
}
259
260
#else  /* !JS_HAS_OBJ_PROTO_PROP */
261
262
#define object_props NULL
263
264
#endif /* !JS_HAS_OBJ_PROTO_PROP */
265
266
JSBool
267
js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
268
{
269
    JSRuntime *rt;
270
    JSObject *obj2, *oldproto;
271
    JSScope *scope, *newscope;
272
273
    /*
274
     * Serialize all proto and parent setting in order to detect cycles.
275
     * We nest locks in this function, and only here, in the following orders:
276
     *
277
     * (1)  rt->setSlotLock < pobj's scope lock;
278
     *      rt->setSlotLock < pobj's proto-or-parent's scope lock;
279
     *      rt->setSlotLock < pobj's grand-proto-or-parent's scope lock;
280
     *      etc...
281
     * (2)  rt->setSlotLock < obj's scope lock < pobj's scope lock.
282
     *
283
     * We avoid AB-BA deadlock by restricting obj from being on pobj's parent
284
     * or proto chain (pobj may already be on obj's parent or proto chain; it
285
     * could be moving up or down).  We finally order obj with respect to pobj
286
     * at the bottom of this routine (just before releasing rt->setSlotLock),
287
     * by making pobj be obj's prototype or parent.
288
     *
289
     * After we have set the slot and released rt->setSlotLock, another call
290
     * to js_SetProtoOrParent could nest locks according to the first order
291
     * list above, but it cannot deadlock with any other thread.  For there
292
     * to be a deadlock, other parts of the engine would have to nest scope
293
     * locks in the opposite order.  XXXbe ensure they don't!
294
     */
295
    rt = cx->runtime;
296
#ifdef JS_THREADSAFE
297
298
    JS_ACQUIRE_LOCK(rt->setSlotLock);
299
    while (rt->setSlotBusy) {
300
        jsrefcount saveDepth;
301
302
        /* Take pains to avoid nesting rt->gcLock inside rt->setSlotLock! */
303
        JS_RELEASE_LOCK(rt->setSlotLock);
304
        saveDepth = JS_SuspendRequest(cx);
305
        JS_ACQUIRE_LOCK(rt->setSlotLock);
306
        if (rt->setSlotBusy)
307
            JS_WAIT_CONDVAR(rt->setSlotDone, JS_NO_TIMEOUT);
308
        JS_RELEASE_LOCK(rt->setSlotLock);
309
        JS_ResumeRequest(cx, saveDepth);
310
        JS_ACQUIRE_LOCK(rt->setSlotLock);
311
    }
312
    rt->setSlotBusy = JS_TRUE;
313
    JS_RELEASE_LOCK(rt->setSlotLock);
314
315
#define SET_SLOT_DONE(rt)                                                     \
316
    JS_BEGIN_MACRO                                                            \
317
        JS_ACQUIRE_LOCK((rt)->setSlotLock);                                   \
318
        (rt)->setSlotBusy = JS_FALSE;                                         \
319
        JS_NOTIFY_ALL_CONDVAR((rt)->setSlotDone);                             \
320
        JS_RELEASE_LOCK((rt)->setSlotLock);                                   \
321
    JS_END_MACRO
322
323
#else
324
325
#define SET_SLOT_DONE(rt)       /* nothing */
326
327
#endif
328
329
    obj2 = pobj;
330
    while (obj2) {
331
        if (obj2 == obj) {
332
            SET_SLOT_DONE(rt);
333
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
334
                                 JSMSG_CYCLIC_VALUE,
335
#if JS_HAS_OBJ_PROTO_PROP
336
                                 object_props[slot].name
337
#else
338
                                 (slot == JSSLOT_PROTO) ? js_proto_str
339
                                                        : js_parent_str
340
#endif
341
                                 );
342
            return JS_FALSE;
343
        }
344
        obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot));
345
    }
346
347
    if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) {
348
        /* Check to see whether obj shares its prototype's scope. */
349
        JS_LOCK_OBJ(cx, obj);
350
        scope = OBJ_SCOPE(obj);
351
        oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO));
352
        if (oldproto && OBJ_SCOPE(oldproto) == scope) {
353
            /* Either obj needs a new empty scope, or it should share pobj's. */
354
            if (!pobj ||
355
                !OBJ_IS_NATIVE(pobj) ||
356
                OBJ_GET_CLASS(cx, pobj) != LOCKED_OBJ_GET_CLASS(oldproto)) {
357
                /*
358
                 * With no proto and no scope of its own, obj is truly empty.
359
                 *
360
                 * If pobj is not native, obj needs its own empty scope -- it
361
                 * should not continue to share oldproto's scope once oldproto
362
                 * is not on obj's prototype chain.  That would put properties
363
                 * from oldproto's scope ahead of properties defined by pobj,
364
                 * in lookup order.
365
                 *
366
                 * If pobj's class differs from oldproto's, we may need a new
367
                 * scope to handle differences in private and reserved slots,
368
                 * so we suboptimally but safely make one.
369
                 */
370
                scope = js_GetMutableScope(cx, obj);
371
                if (!scope) {
372
                    JS_UNLOCK_OBJ(cx, obj);
373
                    SET_SLOT_DONE(rt);
374
                    return JS_FALSE;
375
                }
376
            } else if (OBJ_SCOPE(pobj) != scope) {
377
#ifdef JS_THREADSAFE
378
                /*
379
                 * We are about to nest scope locks.  Help jslock.c:ShareScope
380
                 * keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while
381
                 * avoiding deadlock, by recording scope in rt->setSlotScope.
382
                 */
383
                if (scope->ownercx) {
384
                    JS_ASSERT(scope->ownercx == cx);
385
                    rt->setSlotScope = scope;
386
                }
387
#endif
388
389
                /* We can't deadlock because we checked for cycles above (2). */
390
                JS_LOCK_OBJ(cx, pobj);
391
                newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map);
392
                obj->map = &newscope->map;
393
                js_DropObjectMap(cx, &scope->map, obj);
394
                JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
395
                scope = newscope;
396
#ifdef JS_THREADSAFE
397
                rt->setSlotScope = NULL;
398
#endif
399
            }
400
        }
401
        LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj));
402
        JS_UNLOCK_SCOPE(cx, scope);
403
    } else {
404
        OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj));
405
    }
406
407
    SET_SLOT_DONE(rt);
408
    return JS_TRUE;
409
410
#undef SET_SLOT_DONE
411
}
412
413
JS_STATIC_DLL_CALLBACK(JSHashNumber)
414
js_hash_object(const void *key)
415
{
416
    return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS;
417
}
418
419
static JSHashEntry *
420
MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
421
{
422
    JSSharpObjectMap *map;
423
    JSHashTable *table;
424
    JSHashNumber hash;
425
    JSHashEntry **hep, *he;
426
    jsatomid sharpid;
427
    JSIdArray *ida;
428
    JSBool ok;
429
    jsint i, length;
430
    jsid id;
431
#if JS_HAS_GETTER_SETTER
432
    JSObject *obj2;
433
    JSProperty *prop;
434
    uintN attrs;
435
#endif
436
    jsval val;
437
    int stackDummy;
438
439
    if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
440
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
441
        return NULL;
442
    }
443
444
    map = &cx->sharpObjectMap;
445
    table = map->table;
446
    hash = js_hash_object(obj);
447
    hep = JS_HashTableRawLookup(table, hash, obj);
448
    he = *hep;
449
    if (!he) {
450
        sharpid = 0;
451
        he = JS_HashTableRawAdd(table, hep, hash, obj,
452
                                JS_UINT32_TO_PTR(sharpid));
453
        if (!he) {
454
            JS_ReportOutOfMemory(cx);
455
            return NULL;
456
        }
457
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
458
        /*
1 by Mike Hommey
Import upstream version 1.8.0.4
459
         * Increment map->depth to protect js_EnterSharpObject from reentering
460
         * itself badly.  Without this fix, if we reenter the basis case where
461
         * map->depth == 0, when unwinding the inner call we will destroy the
462
         * newly-created hash table and crash.
463
         */
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
464
        ++map->depth;
1 by Mike Hommey
Import upstream version 1.8.0.4
465
        ida = JS_Enumerate(cx, obj);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
466
        --map->depth;
1 by Mike Hommey
Import upstream version 1.8.0.4
467
        if (!ida)
468
            return NULL;
469
470
        ok = JS_TRUE;
471
        for (i = 0, length = ida->length; i < length; i++) {
472
            id = ida->vector[i];
473
#if JS_HAS_GETTER_SETTER
474
            ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
475
            if (!ok)
476
                break;
477
            if (!prop)
478
                continue;
479
            ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
480
            if (ok) {
481
                if (OBJ_IS_NATIVE(obj2) &&
482
                    (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
483
                    val = JSVAL_NULL;
484
                    if (attrs & JSPROP_GETTER)
485
                        val = (jsval) ((JSScopeProperty*)prop)->getter;
486
                    if (attrs & JSPROP_SETTER) {
487
                        if (val != JSVAL_NULL) {
488
                            /* Mark the getter, then set val to setter. */
489
                            ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
490
                                                   NULL)
491
                                  != NULL);
492
                        }
493
                        val = (jsval) ((JSScopeProperty*)prop)->setter;
494
                    }
495
                } else {
496
                    ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
497
                }
498
            }
499
            OBJ_DROP_PROPERTY(cx, obj2, prop);
500
#else
501
            ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
502
#endif
503
            if (!ok)
504
                break;
505
            if (!JSVAL_IS_PRIMITIVE(val) &&
506
                !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
507
                ok = JS_FALSE;
508
                break;
509
            }
510
        }
511
        if (!ok || !idap)
512
            JS_DestroyIdArray(cx, ida);
513
        if (!ok)
514
            return NULL;
515
    } else {
516
        sharpid = JS_PTR_TO_UINT32(he->value);
517
        if (sharpid == 0) {
518
            sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
519
            he->value = JS_UINT32_TO_PTR(sharpid);
520
        }
521
        ida = NULL;
522
    }
523
    if (idap)
524
        *idap = ida;
525
    return he;
526
}
527
528
JSHashEntry *
529
js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
530
                    jschar **sp)
531
{
532
    JSSharpObjectMap *map;
533
    JSHashTable *table;
534
    JSIdArray *ida;
535
    JSHashNumber hash;
536
    JSHashEntry *he, **hep;
537
    jsatomid sharpid;
538
    char buf[20];
539
    size_t len;
540
541
    if (JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) &&
542
        cx->branchCallback &&
543
        !cx->branchCallback(cx, NULL)) {
544
        return NULL;
545
    }
546
547
    /* Set to null in case we return an early error. */
548
    *sp = NULL;
549
    map = &cx->sharpObjectMap;
550
    table = map->table;
551
    if (!table) {
552
        table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
553
                                JS_CompareValues, NULL, NULL);
554
        if (!table) {
555
            JS_ReportOutOfMemory(cx);
556
            return NULL;
557
        }
558
        map->table = table;
1.1.3 by Mike Hommey
Import upstream version 1.8.0.8
559
        JS_KEEP_ATOMS(cx->runtime);
1 by Mike Hommey
Import upstream version 1.8.0.4
560
    }
561
1.1.3 by Mike Hommey
Import upstream version 1.8.0.8
562
    /* From this point the control must flow either through out: or bad:. */
1 by Mike Hommey
Import upstream version 1.8.0.4
563
    ida = NULL;
564
    if (map->depth == 0) {
565
        he = MarkSharpObjects(cx, obj, &ida);
566
        if (!he)
567
            goto bad;
568
        JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0);
569
        if (!idap) {
570
            JS_DestroyIdArray(cx, ida);
571
            ida = NULL;
572
        }
573
    } else {
574
        hash = js_hash_object(obj);
575
        hep = JS_HashTableRawLookup(table, hash, obj);
576
        he = *hep;
577
578
        /*
579
         * It's possible that the value of a property has changed from the
580
         * first time the object's properties are traversed (when the property
581
         * ids are entered into the hash table) to the second (when they are
582
         * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not
583
         * idempotent.
584
         */
585
        if (!he) {
586
            he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
587
            if (!he) {
588
                JS_ReportOutOfMemory(cx);
589
                goto bad;
590
            }
591
            sharpid = 0;
592
            goto out;
593
        }
594
    }
595
596
    sharpid = JS_PTR_TO_UINT32(he->value);
597
    if (sharpid != 0) {
598
        len = JS_snprintf(buf, sizeof buf, "#%u%c",
599
                          sharpid >> SHARP_ID_SHIFT,
600
                          (sharpid & SHARP_BIT) ? '#' : '=');
1.1.4 by Mike Hommey
Import upstream version 1.8.0.9
601
        *sp = js_InflateString(cx, buf, &len);
1 by Mike Hommey
Import upstream version 1.8.0.4
602
        if (!*sp) {
603
            if (ida)
604
                JS_DestroyIdArray(cx, ida);
605
            goto bad;
606
        }
607
    }
608
609
out:
610
    JS_ASSERT(he);
611
    if ((sharpid & SHARP_BIT) == 0) {
612
        if (idap && !ida) {
613
            ida = JS_Enumerate(cx, obj);
614
            if (!ida) {
615
                if (*sp) {
616
                    JS_free(cx, *sp);
617
                    *sp = NULL;
618
                }
619
                goto bad;
620
            }
621
        }
622
        map->depth++;
623
    }
624
625
    if (idap)
626
        *idap = ida;
627
    return he;
628
629
bad:
630
    /* Clean up the sharpObjectMap table on outermost error. */
631
    if (map->depth == 0) {
1.1.3 by Mike Hommey
Import upstream version 1.8.0.8
632
        JS_UNKEEP_ATOMS(cx->runtime);
1 by Mike Hommey
Import upstream version 1.8.0.4
633
        map->sharpgen = 0;
634
        JS_HashTableDestroy(map->table);
635
        map->table = NULL;
636
    }
637
    return NULL;
638
}
639
640
void
641
js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
642
{
643
    JSSharpObjectMap *map;
644
    JSIdArray *ida;
645
646
    map = &cx->sharpObjectMap;
647
    JS_ASSERT(map->depth > 0);
648
    if (--map->depth == 0) {
1.1.3 by Mike Hommey
Import upstream version 1.8.0.8
649
        JS_UNKEEP_ATOMS(cx->runtime);
1 by Mike Hommey
Import upstream version 1.8.0.4
650
        map->sharpgen = 0;
651
        JS_HashTableDestroy(map->table);
652
        map->table = NULL;
653
    }
654
    if (idap) {
655
        ida = *idap;
656
        if (ida) {
657
            JS_DestroyIdArray(cx, ida);
658
            *idap = NULL;
659
        }
660
    }
661
}
662
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
663
JS_STATIC_DLL_CALLBACK(intN)
664
gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
665
{
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
666
    GC_MARK((JSContext *)arg, (JSObject *)he->key, "sharp table entry");
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
667
    return JS_DHASH_NEXT;
668
}
669
670
void
671
js_GCMarkSharpMap(JSContext *cx, JSSharpObjectMap *map)
672
{
673
    JS_ASSERT(map->depth > 0);
674
    JS_ASSERT(map->table);
675
676
    /*
677
     * During recursive calls to MarkSharpObjects a non-native object or
678
     * object with a custom getProperty method can potentially return an
679
     * unrooted value or even cut from the object graph an argument of one of
680
     * MarkSharpObjects recursive invocations. So we must protect map->table
681
     * entries against GC.
682
     *
683
     * We can not simply use JSTempValueRooter to mark the obj argument of
684
     * MarkSharpObjects during recursion as we have to protect *all* entries
685
     * in JSSharpObjectMap including those that contains otherwise unreachable
686
     * objects just allocated through custom getProperty. Otherwise newer
687
     * allocations can re-use the address of an object stored in the hashtable
688
     * confusing js_EnterSharpObject. So to address the problem we simply
689
     * mark all objects from map->table.
690
     *
691
     * An alternative "proper" solution is to use JSTempValueRooter in
692
     * MarkSharpObjects with code to remove during finalization entries
693
     * with otherwise unreachable objects. But this is way too complex
694
     * to justify spending efforts.
695
     */
696
    JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, cx);
697
}
698
1 by Mike Hommey
Import upstream version 1.8.0.4
699
#define OBJ_TOSTRING_EXTRA      4       /* for 4 local GC roots */
700
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
701
#if JS_HAS_TOSOURCE
1 by Mike Hommey
Import upstream version 1.8.0.4
702
JSBool
703
js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
704
                jsval *rval)
705
{
706
    JSBool ok, outermost;
707
    JSHashEntry *he;
708
    JSIdArray *ida;
709
    jschar *chars, *ochars, *vsharp;
710
    const jschar *idstrchars, *vchars;
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
711
    size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
1 by Mike Hommey
Import upstream version 1.8.0.4
712
    char *comma;
713
    jsint i, j, length, valcnt;
714
    jsid id;
715
#if JS_HAS_GETTER_SETTER
716
    JSObject *obj2;
717
    JSProperty *prop;
718
    uintN attrs;
719
#endif
720
    jsval *val;
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
721
    JSString *gsopold[2];
1 by Mike Hommey
Import upstream version 1.8.0.4
722
    JSString *gsop[2];
723
    JSAtom *atom;
724
    JSString *idstr, *valstr, *str;
725
    int stackDummy;
726
727
    if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
728
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
729
        return JS_FALSE;
730
    }
731
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
732
    /* If outermost, we need parentheses to be an expression, not a block. */
733
    outermost = (cx->sharpObjectMap.depth == 0);
1 by Mike Hommey
Import upstream version 1.8.0.4
734
    he = js_EnterSharpObject(cx, obj, &ida, &chars);
735
    if (!he)
736
        return JS_FALSE;
737
    if (IS_SHARP(he)) {
738
        /*
739
         * We didn't enter -- obj is already "sharp", meaning we've visited it
740
         * already in our depth first search, and therefore chars contains a
741
         * string of the form "#n#".
742
         */
743
        JS_ASSERT(!ida);
744
#if JS_HAS_SHARP_VARS
745
        nchars = js_strlen(chars);
746
#else
747
        chars[0] = '{';
748
        chars[1] = '}';
749
        chars[2] = 0;
750
        nchars = 2;
751
#endif
752
        goto make_string;
753
    }
754
    JS_ASSERT(ida);
755
    ok = JS_TRUE;
756
757
    if (!chars) {
758
        /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
759
        chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
760
        nchars = 0;
761
        if (!chars)
762
            goto error;
763
        if (outermost)
764
            chars[nchars++] = '(';
765
    } else {
766
        /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
767
        MAKE_SHARP(he);
768
        nchars = js_strlen(chars);
769
        chars = (jschar *)
770
            realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
771
        if (!chars) {
772
            free(ochars);
773
            goto error;
774
        }
775
        if (outermost) {
776
            /*
777
             * No need for parentheses around the whole shebang, because #n=
778
             * unambiguously begins an object initializer, and never a block
779
             * statement.
780
             */
781
            outermost = JS_FALSE;
782
        }
783
    }
784
785
#ifdef DUMP_CALL_TABLE
786
    if (cx->options & JSOPTION_LOGCALL_TOSOURCE) {
787
        const char *classname = OBJ_GET_CLASS(cx, obj)->name;
788
        size_t classnchars = strlen(classname);
789
        static const char classpropid[] = "C";
790
        const char *cp;
791
        size_t onchars = nchars;
792
793
        /* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */
794
        classnchars += sizeof classpropid - 1 + 2 + 2;
795
        if (ida->length)
796
            classnchars += 2;
797
798
        /* 2 for the braces, 1 for the terminator */
799
        chars = (jschar *)
800
            realloc((ochars = chars),
801
                    (nchars + classnchars + 2 + 1) * sizeof(jschar));
802
        if (!chars) {
803
            free(ochars);
804
            goto error;
805
        }
806
807
        chars[nchars++] = '{';          /* 1 from the 2 braces */
808
        for (cp = classpropid; *cp; cp++)
809
            chars[nchars++] = (jschar) *cp;
810
        chars[nchars++] = ':';
811
        chars[nchars++] = ' ';          /* 2 for ': ' */
812
        chars[nchars++] = '"';
813
        for (cp = classname; *cp; cp++)
814
            chars[nchars++] = (jschar) *cp;
815
        chars[nchars++] = '"';          /* 2 quotes */
816
        if (ida->length) {
817
            chars[nchars++] = ',';
818
            chars[nchars++] = ' ';      /* 2 for ', ' */
819
        }
820
821
        JS_ASSERT(nchars - onchars == 1 + classnchars);
822
    } else
823
#endif
824
    chars[nchars++] = '{';
825
826
    comma = NULL;
827
828
    /*
829
     * We have four local roots for cooked and raw value GC safety.  Hoist the
830
     * "argv + 2" out of the loop using the val local, which refers to the raw
831
     * (unconverted, "uncooked") values.
832
     */
833
    val = argv + 2;
834
835
    for (i = 0, length = ida->length; i < length; i++) {
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
836
        JSBool idIsLexicalIdentifier, needOldStyleGetterSetter;
837
1 by Mike Hommey
Import upstream version 1.8.0.4
838
        /* Get strings for id and value and GC-root them via argv. */
839
        id = ida->vector[i];
840
841
#if JS_HAS_GETTER_SETTER
842
        ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
843
        if (!ok)
844
            goto error;
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
845
#endif
846
847
        /*
848
         * Convert id to a jsval and then to a string.  Decide early whether we
849
         * prefer get/set or old getter/setter syntax.
850
         */
851
        atom = JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL;
852
        idstr = js_ValueToString(cx, ID_TO_VALUE(id));
853
        if (!idstr) {
854
            ok = JS_FALSE;
855
            OBJ_DROP_PROPERTY(cx, obj2, prop);
856
            goto error;
857
        }
858
        *rval = STRING_TO_JSVAL(idstr);         /* local root */
859
        idIsLexicalIdentifier = js_IsIdentifier(idstr);
860
        needOldStyleGetterSetter =
861
            !idIsLexicalIdentifier ||
862
            js_CheckKeyword(JSSTRING_CHARS(idstr),
863
                            JSSTRING_LENGTH(idstr)) != TOK_EOF;
864
865
#if JS_HAS_GETTER_SETTER
866
1 by Mike Hommey
Import upstream version 1.8.0.4
867
        valcnt = 0;
868
        if (prop) {
869
            ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
870
            if (!ok) {
871
                OBJ_DROP_PROPERTY(cx, obj2, prop);
872
                goto error;
873
            }
874
            if (OBJ_IS_NATIVE(obj2) &&
875
                (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
876
                if (attrs & JSPROP_GETTER) {
877
                    val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter;
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
878
                    gsopold[valcnt] =
1 by Mike Hommey
Import upstream version 1.8.0.4
879
                        ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
880
                    gsop[valcnt] =
881
                        ATOM_TO_STRING(cx->runtime->atomState.getAtom);
882
                    valcnt++;
883
                }
884
                if (attrs & JSPROP_SETTER) {
885
                    val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
886
                    gsopold[valcnt] =
1 by Mike Hommey
Import upstream version 1.8.0.4
887
                        ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
888
                    gsop[valcnt] =
889
                        ATOM_TO_STRING(cx->runtime->atomState.setAtom);
890
                    valcnt++;
891
                }
892
            } else {
893
                valcnt = 1;
894
                gsop[0] = NULL;
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
895
                gsopold[0] = NULL;
1 by Mike Hommey
Import upstream version 1.8.0.4
896
                ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
897
            }
898
            OBJ_DROP_PROPERTY(cx, obj2, prop);
899
        }
900
901
#else  /* !JS_HAS_GETTER_SETTER */
902
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
903
        /*
904
         * We simplify the source code at the price of minor dead code bloat in
905
         * the ECMA version (for testing only, see jsconfig.h).  The null
906
         * default values in gsop[j] suffice to disable non-ECMA getter and
907
         * setter code.
908
         */
1 by Mike Hommey
Import upstream version 1.8.0.4
909
        valcnt = 1;
910
        gsop[0] = NULL;
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
911
        gsopold[0] = NULL;
1 by Mike Hommey
Import upstream version 1.8.0.4
912
        ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
913
914
#endif /* !JS_HAS_GETTER_SETTER */
915
916
        if (!ok)
917
            goto error;
918
919
        /*
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
920
         * If id is a string that's not an identifier, then it needs to be
921
         * quoted.  Also, negative integer ids must be quoted.
1 by Mike Hommey
Import upstream version 1.8.0.4
922
         */
923
        if (atom
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
924
            ? !idIsLexicalIdentifier
1 by Mike Hommey
Import upstream version 1.8.0.4
925
            : (JSID_IS_OBJECT(id) || JSID_TO_INT(id) < 0)) {
926
            idstr = js_QuoteString(cx, idstr, (jschar)'\'');
927
            if (!idstr) {
928
                ok = JS_FALSE;
929
                goto error;
930
            }
931
            *rval = STRING_TO_JSVAL(idstr);     /* local root */
932
        }
933
        idstrchars = JSSTRING_CHARS(idstr);
934
        idstrlength = JSSTRING_LENGTH(idstr);
935
936
        for (j = 0; j < valcnt; j++) {
937
            /* Convert val[j] to its canonical source form. */
938
            valstr = js_ValueToSource(cx, val[j]);
939
            if (!valstr) {
940
                ok = JS_FALSE;
941
                goto error;
942
            }
943
            argv[j] = STRING_TO_JSVAL(valstr);  /* local root */
944
            vchars = JSSTRING_CHARS(valstr);
945
            vlength = JSSTRING_LENGTH(valstr);
946
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
947
            if (vchars[0] == '#')
948
                needOldStyleGetterSetter = JS_TRUE;
949
950
            if (needOldStyleGetterSetter)
951
                gsop[j] = gsopold[j];
952
1 by Mike Hommey
Import upstream version 1.8.0.4
953
#ifndef OLD_GETTER_SETTER
954
            /*
955
             * Remove '(function ' from the beginning of valstr and ')' from the
956
             * end so that we can put "get" in front of the function definition.
957
             */
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
958
            if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) &&
959
                !needOldStyleGetterSetter) {
960
                const jschar *start = vchars;
961
                if (vchars[0] == '(')
962
                    vchars++;
963
                vchars = js_strchr_limit(vchars, '(', vchars + vlength);
964
                if (vchars) {
965
                    vlength -= vchars - start + 1;
966
                } else {
967
                    gsop[j] = NULL;
968
                    vchars = start;
969
                }
1 by Mike Hommey
Import upstream version 1.8.0.4
970
            }
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
971
#else
972
            needOldStyleGetterSetter = JS_TRUE;
973
            gsop[j] = gsopold[j];
1 by Mike Hommey
Import upstream version 1.8.0.4
974
#endif
975
976
            /* If val[j] is a non-sharp object, consider sharpening it. */
977
            vsharp = NULL;
978
            vsharplength = 0;
979
#if JS_HAS_SHARP_VARS
980
            if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
981
                he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
982
                                         &vsharp);
983
                if (!he) {
984
                    ok = JS_FALSE;
985
                    goto error;
986
                }
987
                if (IS_SHARP(he)) {
988
                    vchars = vsharp;
989
                    vlength = js_strlen(vchars);
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
990
                    needOldStyleGetterSetter = JS_TRUE;
991
                    gsop[j] = gsopold[j];
1 by Mike Hommey
Import upstream version 1.8.0.4
992
                } else {
993
                    if (vsharp) {
994
                        vsharplength = js_strlen(vsharp);
995
                        MAKE_SHARP(he);
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
996
                        needOldStyleGetterSetter = JS_TRUE;
997
                        gsop[j] = gsopold[j];
1 by Mike Hommey
Import upstream version 1.8.0.4
998
                    }
999
                    js_LeaveSharpObject(cx, NULL);
1000
                }
1001
            }
1002
#endif
1003
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
1004
#define SAFE_ADD(n)                                                          \
1005
    JS_BEGIN_MACRO                                                           \
1006
        size_t n_ = (n);                                                     \
1007
        curlen += n_;                                                        \
1008
        if (curlen < n_)                                                     \
1009
            goto overflow;                                                   \
1010
    JS_END_MACRO
1011
1012
            curlen = nchars;
1013
            if (comma)
1014
                SAFE_ADD(2);
1015
            SAFE_ADD(idstrlength + 1);
1016
            if (gsop[j])
1017
                SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1);
1018
            SAFE_ADD(vsharplength);
1019
            SAFE_ADD(vlength);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
1020
            /* Account for the trailing null. */
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
1021
            SAFE_ADD((outermost ? 2 : 1) + 1);
1022
#undef SAFE_ADD
1023
1024
            if (curlen > (size_t)-1 / sizeof(jschar))
1025
                goto overflow;
1026
1 by Mike Hommey
Import upstream version 1.8.0.4
1027
            /* Allocate 1 + 1 at end for closing brace and terminating 0. */
1028
            chars = (jschar *)
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
1029
                realloc((ochars = chars), curlen * sizeof(jschar));
1 by Mike Hommey
Import upstream version 1.8.0.4
1030
            if (!chars) {
1031
                /* Save code space on error: let JS_free ignore null vsharp. */
1032
                JS_free(cx, vsharp);
1033
                free(ochars);
1034
                goto error;
1035
            }
1036
1037
            if (comma) {
1038
                chars[nchars++] = comma[0];
1039
                chars[nchars++] = comma[1];
1040
            }
1041
            comma = ", ";
1042
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
1043
            if (needOldStyleGetterSetter) {
1044
                js_strncpy(&chars[nchars], idstrchars, idstrlength);
1045
                nchars += idstrlength;
1046
                if (gsop[j]) {
1047
                    chars[nchars++] = ' ';
1048
                    gsoplength = JSSTRING_LENGTH(gsop[j]);
1049
                    js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]),
1050
                               gsoplength);
1051
                    nchars += gsoplength;
1052
                }
1053
                chars[nchars++] = ':';
1054
            } else {  /* New style "decompilation" */
1055
                if (gsop[j]) {
1056
                    gsoplength = JSSTRING_LENGTH(gsop[j]);
1057
                    js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]),
1058
                               gsoplength);
1059
                    nchars += gsoplength;
1060
                    chars[nchars++] = ' ';
1061
                }
1062
                js_strncpy(&chars[nchars], idstrchars, idstrlength);
1063
                nchars += idstrlength;
1064
                /* Extraneous space after id here will be extracted later */
1065
                chars[nchars++] = gsop[j] ? ' ' : ':';
1066
            }
1067
1 by Mike Hommey
Import upstream version 1.8.0.4
1068
            if (vsharplength) {
1069
                js_strncpy(&chars[nchars], vsharp, vsharplength);
1070
                nchars += vsharplength;
1071
            }
1072
            js_strncpy(&chars[nchars], vchars, vlength);
1073
            nchars += vlength;
1074
1075
            if (vsharp)
1076
                JS_free(cx, vsharp);
1077
#ifdef DUMP_CALL_TABLE
1078
            if (outermost && nchars >= js_LogCallToSourceLimit)
1079
                break;
1080
#endif
1081
        }
1082
    }
1083
1084
    chars[nchars++] = '}';
1085
    if (outermost)
1086
        chars[nchars++] = ')';
1087
    chars[nchars] = 0;
1088
1089
  error:
1090
    js_LeaveSharpObject(cx, &ida);
1091
1092
    if (!ok) {
1093
        if (chars)
1094
            free(chars);
1095
        return ok;
1096
    }
1097
1098
    if (!chars) {
1099
        JS_ReportOutOfMemory(cx);
1100
        return JS_FALSE;
1101
    }
1102
  make_string:
1103
    str = js_NewString(cx, chars, nchars, 0);
1104
    if (!str) {
1105
        free(chars);
1106
        return JS_FALSE;
1107
    }
1108
    *rval = STRING_TO_JSVAL(str);
1109
    return JS_TRUE;
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
1110
1111
  overflow:
1112
    JS_free(cx, vsharp);
1113
    free(chars);
1114
    chars = NULL;
1115
    goto error;
1 by Mike Hommey
Import upstream version 1.8.0.4
1116
}
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
1117
#endif /* JS_HAS_TOSOURCE */
1 by Mike Hommey
Import upstream version 1.8.0.4
1118
1119
JSBool
1120
js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1121
                jsval *rval)
1122
{
1123
    jschar *chars;
1124
    size_t nchars;
1125
    const char *clazz, *prefix;
1126
    JSString *str;
1127
1128
    clazz = OBJ_GET_CLASS(cx, obj)->name;
1129
    nchars = 9 + strlen(clazz);         /* 9 for "[object ]" */
1130
    chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
1131
    if (!chars)
1132
        return JS_FALSE;
1133
1134
    prefix = "[object ";
1135
    nchars = 0;
1136
    while ((chars[nchars] = (jschar)*prefix) != 0)
1137
        nchars++, prefix++;
1138
    while ((chars[nchars] = (jschar)*clazz) != 0)
1139
        nchars++, clazz++;
1140
    chars[nchars++] = ']';
1141
    chars[nchars] = 0;
1142
1143
    str = js_NewString(cx, chars, nchars, 0);
1144
    if (!str) {
1145
        JS_free(cx, chars);
1146
        return JS_FALSE;
1147
    }
1148
    *rval = STRING_TO_JSVAL(str);
1149
    return JS_TRUE;
1150
}
1151
1152
static JSBool
1153
js_obj_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1154
                      jsval *rval)
1155
{
1156
    JSString *str;
1157
1158
    str = js_ValueToString(cx, argv[-1]);
1159
    if (!str)
1160
        return JS_FALSE;
1161
1162
    *rval = STRING_TO_JSVAL(str);
1163
    return JS_TRUE;
1164
}
1165
1166
static JSBool
1167
obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1168
{
1169
    *rval = OBJECT_TO_JSVAL(obj);
1170
    return JS_TRUE;
1171
}
1172
1173
/*
1174
 * Check whether principals subsumes scopeobj's principals, and return true
1175
 * if so (or if scopeobj has no principals, for backward compatibility with
1176
 * the JS API, which does not require principals), and false otherwise.
1177
 */
1178
JSBool
1179
js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
1180
                         JSPrincipals *principals, JSAtom *caller)
1 by Mike Hommey
Import upstream version 1.8.0.4
1181
{
1182
    JSRuntime *rt;
1183
    JSPrincipals *scopePrincipals;
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
1184
    const char *callerstr;
1 by Mike Hommey
Import upstream version 1.8.0.4
1185
1186
    rt = cx->runtime;
1187
    if (rt->findObjectPrincipals) {
1188
        scopePrincipals = rt->findObjectPrincipals(cx, scopeobj);
1189
        if (!principals || !scopePrincipals ||
1190
            !principals->subsume(principals, scopePrincipals)) {
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
1191
            callerstr = js_AtomToPrintableString(cx, caller);
1192
            if (!callerstr)
1193
                return JS_FALSE;
1 by Mike Hommey
Import upstream version 1.8.0.4
1194
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
1195
                                 JSMSG_BAD_INDIRECT_CALL, callerstr);
1 by Mike Hommey
Import upstream version 1.8.0.4
1196
            return JS_FALSE;
1197
        }
1198
    }
1199
    return JS_TRUE;
1200
}
1201
1202
JSObject *
1203
js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller)
1204
{
1205
    JSClass *clasp;
1206
    JSExtendedClass *xclasp;
1207
    JSObject *inner;
1208
1209
    if (!scopeobj)
1210
        goto bad;
1211
1212
    OBJ_TO_INNER_OBJECT(cx, scopeobj);
1213
    if (!scopeobj)
1214
        return NULL;
1215
1216
    inner = scopeobj;
1217
1218
    /* XXX This is an awful gross hack. */
1219
    while (scopeobj) {
1220
        clasp = OBJ_GET_CLASS(cx, scopeobj);
1221
        if (clasp->flags & JSCLASS_IS_EXTENDED) {
1222
            xclasp = (JSExtendedClass*)clasp;
1223
            if (xclasp->innerObject &&
1224
                xclasp->innerObject(cx, scopeobj) != scopeobj) {
1225
                goto bad;
1226
            }
1227
        }
1228
1229
        scopeobj = OBJ_GET_PARENT(cx, scopeobj);
1230
    }
1231
1232
    return inner;
1233
1234
bad:
1235
    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1236
                         JSMSG_BAD_INDIRECT_CALL, caller);
1237
    return NULL;
1238
}
1239
1240
static JSBool
1241
obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1242
{
1243
    JSStackFrame *fp, *caller;
1244
    JSBool indirectCall;
1245
    JSObject *scopeobj;
1246
    JSString *str;
1247
    const char *file;
1248
    uintN line;
1249
    JSPrincipals *principals;
1250
    JSScript *script;
1251
    JSBool ok;
1252
#if JS_HAS_EVAL_THIS_SCOPE
1253
    JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
1.1.2 by Mike Hommey
Import upstream version 1.8.0.7
1254
    JSObject *setCallerScopeChain = NULL;
1255
    JSBool setCallerVarObj = JS_FALSE;
1 by Mike Hommey
Import upstream version 1.8.0.4
1256
#endif
1257
1258
    fp = cx->fp;
1259
    caller = JS_GetScriptedCaller(cx, fp);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
1260
    JS_ASSERT(!caller || caller->pc);
1261
    indirectCall = (caller && *caller->pc != JSOP_EVAL);
1 by Mike Hommey
Import upstream version 1.8.0.4
1262
1.1.10 by Fabien Tassin
Import upstream version 1.8.1.13+nobinonly
1263
    /* 
1264
     * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and
1265
     * calls that attempt to use a non-global object as the "with" object in
1266
     * the former indirect case.
1267
     */
1268
    scopeobj = OBJ_GET_PARENT(cx, obj);
1269
    if (indirectCall || scopeobj) {
1270
        uintN flags = scopeobj
1271
                      ? JSREPORT_ERROR
1272
                      : JSREPORT_STRICT | JSREPORT_WARNING;
1273
        if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL,
1274
                                          JSMSG_BAD_INDIRECT_CALL,
1275
                                          js_eval_str)) {
1276
            return JS_FALSE;
1277
        }
1 by Mike Hommey
Import upstream version 1.8.0.4
1278
    }
1279
1280
    if (!JSVAL_IS_STRING(argv[0])) {
1281
        *rval = argv[0];
1282
        return JS_TRUE;
1283
    }
1284
1285
    /*
1286
     * If the caller is a lightweight function and doesn't have a variables
1287
     * object, then we need to provide one for the compiler to stick any
1288
     * declared (var) variables into.
1289
     */
1290
    if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL))
1291
        return JS_FALSE;
1292
1293
#if JS_HAS_SCRIPT_OBJECT
1294
    /*
1295
     * Script.prototype.compile/exec and Object.prototype.eval all take an
1296
     * optional trailing argument that overrides the scope object.
1297
     */
1298
    scopeobj = NULL;
1299
    if (argc >= 2) {
1300
        if (!js_ValueToObject(cx, argv[1], &scopeobj))
1301
            return JS_FALSE;
1302
        argv[1] = OBJECT_TO_JSVAL(scopeobj);
1303
    }
1304
    if (!scopeobj)
1305
#endif
1306
    {
1307
#if JS_HAS_EVAL_THIS_SCOPE
1308
        /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
1309
        if (indirectCall) {
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
1310
            callerScopeChain = js_GetScopeChain(cx, caller);
1311
            if (!callerScopeChain)
1312
                return JS_FALSE;
1313
            OBJ_TO_INNER_OBJECT(cx, obj);
1314
            if (!obj)
1315
                return JS_FALSE;
1 by Mike Hommey
Import upstream version 1.8.0.4
1316
            if (obj != callerScopeChain) {
1317
                if (!js_CheckPrincipalsAccess(cx, obj,
1318
                                              caller->script->principals,
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
1319
                                              cx->runtime->atomState.evalAtom))
1320
                {
1 by Mike Hommey
Import upstream version 1.8.0.4
1321
                    return JS_FALSE;
1322
                }
1323
1.1.2 by Mike Hommey
Import upstream version 1.8.0.7
1324
                scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1);
1 by Mike Hommey
Import upstream version 1.8.0.4
1325
                if (!scopeobj)
1326
                    return JS_FALSE;
1327
1328
                /* Set fp->scopeChain too, for the compiler. */
1329
                caller->scopeChain = fp->scopeChain = scopeobj;
1.1.2 by Mike Hommey
Import upstream version 1.8.0.7
1330
1331
                /* Remember scopeobj so we can null its private when done. */
1332
                setCallerScopeChain = scopeobj;
1 by Mike Hommey
Import upstream version 1.8.0.4
1333
            }
1334
1335
            callerVarObj = caller->varobj;
1336
            if (obj != callerVarObj) {
1337
                /* Set fp->varobj too, for the compiler. */
1338
                caller->varobj = fp->varobj = obj;
1339
                setCallerVarObj = JS_TRUE;
1340
            }
1341
        }
1342
        /* From here on, control must exit through label out with ok set. */
1343
#endif
1344
1345
        /* Compile using caller's current scope object. */
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
1346
        if (caller) {
1347
            scopeobj = js_GetScopeChain(cx, caller);
1348
            if (!scopeobj) {
1349
                ok = JS_FALSE;
1350
                goto out;
1351
            }
1352
        }
1 by Mike Hommey
Import upstream version 1.8.0.4
1353
    }
1354
1355
    /* Ensure we compile this eval with the right object in the scope chain. */
1356
    scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str);
1.1.10 by Fabien Tassin
Import upstream version 1.8.1.13+nobinonly
1357
    if (!scopeobj) {
1358
        ok = JS_FALSE;
1359
        goto out;
1360
    }
1 by Mike Hommey
Import upstream version 1.8.0.4
1361
1362
    str = JSVAL_TO_STRING(argv[0]);
1363
    if (caller) {
1364
        principals = JS_EvalFramePrincipals(cx, fp, caller);
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
1365
        if (principals == caller->script->principals) {
1366
            file = caller->script->filename;
1367
            line = js_PCToLineNumber(cx, caller->script, caller->pc);
1368
        } else {
1369
            file = principals->codebase;
1370
            line = 0;
1371
        }
1 by Mike Hommey
Import upstream version 1.8.0.4
1372
    } else {
1373
        file = NULL;
1374
        line = 0;
1375
        principals = NULL;
1376
    }
1377
1378
    /*
1379
     * Set JSFRAME_EVAL on fp and any frames (e.g., fun_call if eval.call was
1380
     * invoked) between fp and its scripted caller, to help the compiler easily
1381
     * find the same caller whose scope and var obj we've set.
1382
     *
1383
     * XXX this nonsense could, and perhaps should, go away with a better way
1384
     * to pass params to the compiler than via the top-most frame.
1385
     */
1386
    do {
1387
        fp->flags |= JSFRAME_EVAL;
1388
    } while ((fp = fp->down) != caller);
1389
1390
    script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
1391
                                             JSSTRING_CHARS(str),
1392
                                             JSSTRING_LENGTH(str),
1393
                                             file, line);
1394
    if (!script) {
1395
        ok = JS_FALSE;
1396
        goto out;
1397
    }
1398
1399
#if JS_HAS_SCRIPT_OBJECT
1400
    if (argc < 2)
1401
#endif
1402
    {
1403
        /* Execute using caller's new scope object (might be a Call object). */
1404
        if (caller)
1405
            scopeobj = caller->scopeChain;
1406
    }
1407
1408
    /*
1409
     * Belt-and-braces: check that the lesser of eval's principals and the
1410
     * caller's principals has access to scopeobj.
1411
     */
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
1412
    ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
1413
                                  cx->runtime->atomState.evalAtom);
1 by Mike Hommey
Import upstream version 1.8.0.4
1414
    if (ok)
1415
        ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
1416
1417
    JS_DestroyScript(cx, script);
1418
1419
out:
1420
#if JS_HAS_EVAL_THIS_SCOPE
1421
    /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
1.1.2 by Mike Hommey
Import upstream version 1.8.0.7
1422
    if (setCallerScopeChain) {
1 by Mike Hommey
Import upstream version 1.8.0.4
1423
        caller->scopeChain = callerScopeChain;
1.1.2 by Mike Hommey
Import upstream version 1.8.0.7
1424
        JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass);
1425
        JS_SetPrivate(cx, setCallerScopeChain, NULL);
1426
    }
1 by Mike Hommey
Import upstream version 1.8.0.4
1427
    if (setCallerVarObj)
1428
        caller->varobj = callerVarObj;
1429
#endif
1.1.10 by Fabien Tassin
Import upstream version 1.8.1.13+nobinonly
1430
1 by Mike Hommey
Import upstream version 1.8.0.4
1431
    return ok;
1432
}
1433
1434
#if JS_HAS_OBJ_WATCHPOINT
1435
1436
static JSBool
1437
obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
1438
                  void *closure)
1439
{
1.1.4 by Mike Hommey
Import upstream version 1.8.0.9
1440
    JSObject *callable;
1441
    JSRuntime *rt;
1442
    JSStackFrame *caller;
1443
    JSPrincipals *subject, *watcher;
1 by Mike Hommey
Import upstream version 1.8.0.4
1444
    JSResolvingKey key;
1445
    JSResolvingEntry *entry;
1446
    uint32 generation;
1447
    jsval argv[3];
1448
    JSBool ok;
1449
1.1.4 by Mike Hommey
Import upstream version 1.8.0.9
1450
    callable = (JSObject *) closure;
1451
1452
    rt = cx->runtime;
1453
    if (rt->findObjectPrincipals) {
1454
        /* Skip over any obj_watch_* frames between us and the real subject. */
1455
        caller = JS_GetScriptedCaller(cx, cx->fp);
1456
        if (caller) {
1457
            /*
1458
             * Only call the watch handler if the watcher is allowed to watch
1459
             * the currently executing script.
1460
             */
1461
            watcher = rt->findObjectPrincipals(cx, callable);
1462
            subject = JS_StackFramePrincipals(cx, caller);
1463
1464
            if (watcher && subject && !watcher->subsume(watcher, subject)) {
1465
                /* Silently don't call the watch handler. */
1466
                return JS_TRUE;
1467
            }
1468
        }
1469
    }
1470
1 by Mike Hommey
Import upstream version 1.8.0.4
1471
    /* Avoid recursion on (obj, id) already being watched on cx. */
1472
    key.obj = obj;
1473
    key.id = id;
1474
    if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1475
        return JS_FALSE;
1476
    if (!entry)
1477
        return JS_TRUE;
1478
    generation = cx->resolvingTable->generation;
1479
1480
    argv[0] = id;
1481
    argv[1] = old;
1482
    argv[2] = *nvp;
1.1.4 by Mike Hommey
Import upstream version 1.8.0.9
1483
    ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp);
1 by Mike Hommey
Import upstream version 1.8.0.4
1484
    js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1485
    return ok;
1486
}
1487
1488
static JSBool
1489
obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1490
{
1491
    JSObject *callable;
1492
    jsval userid, value;
1493
    jsid propid;
1494
    uintN attrs;
1495
1496
    callable = js_ValueToCallableObject(cx, &argv[1], 0);
1497
    if (!callable)
1498
        return JS_FALSE;
1499
1500
    /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1501
    userid = argv[0];
1502
    if (!JS_ValueToId(cx, userid, &propid))
1503
        return JS_FALSE;
1504
1505
    if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
1506
        return JS_FALSE;
1507
    if (attrs & JSPROP_READONLY)
1508
        return JS_TRUE;
1509
    return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable);
1510
}
1511
1512
static JSBool
1513
obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1514
{
1515
    return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);
1516
}
1517
1518
#endif /* JS_HAS_OBJ_WATCHPOINT */
1519
1520
/*
1521
 * Prototype and property query methods, to complement the 'in' and
1522
 * 'instanceof' operators.
1523
 */
1524
1525
/* Proposed ECMA 15.2.4.5. */
1526
static JSBool
1527
obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1528
                   jsval *rval)
1529
{
1530
    return js_HasOwnPropertyHelper(cx, obj, obj->map->ops->lookupProperty,
1531
                                   argc, argv, rval);
1532
}
1533
1534
JSBool
1535
js_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup,
1536
                        uintN argc, jsval *argv, jsval *rval)
1537
{
1538
    jsid id;
1539
    JSObject *obj2;
1540
    JSProperty *prop;
1541
    JSScopeProperty *sprop;
1542
1543
    if (!JS_ValueToId(cx, argv[0], &id))
1544
        return JS_FALSE;
1545
    if (!lookup(cx, obj, id, &obj2, &prop))
1546
        return JS_FALSE;
1547
    if (!prop) {
1548
        *rval = JSVAL_FALSE;
1549
    } else if (obj2 == obj) {
1550
        *rval = JSVAL_TRUE;
1551
    } else {
1552
        JSClass *clasp;
1553
        JSExtendedClass *xclasp;
1.1.10 by Fabien Tassin
Import upstream version 1.8.1.13+nobinonly
1554
        JSObject *outer;
1 by Mike Hommey
Import upstream version 1.8.0.4
1555
1.1.10 by Fabien Tassin
Import upstream version 1.8.1.13+nobinonly
1556
        clasp = OBJ_GET_CLASS(cx, obj2);
1557
        if (!(clasp->flags & JSCLASS_IS_EXTENDED) ||
1558
            !(xclasp = (JSExtendedClass *) clasp)->outerObject) {
1559
            outer = NULL;
1560
        } else {
1561
            outer = xclasp->outerObject(cx, obj2);
1562
            if (!outer)
1563
                return JS_FALSE;
1564
        }
1565
        if (outer == obj) {
1 by Mike Hommey
Import upstream version 1.8.0.4
1566
            *rval = JSVAL_TRUE;
1.1.10 by Fabien Tassin
Import upstream version 1.8.1.13+nobinonly
1567
        } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj) == clasp) {
1 by Mike Hommey
Import upstream version 1.8.0.4
1568
            /*
1569
             * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1570
             * delegated property makes that property appear to be direct in
1571
             * all delegating instances of the same native class.  This hack
1572
             * avoids bloating every function instance with its own 'length'
1573
             * (AKA 'arity') property.  But it must not extend across class
1574
             * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1575
             *
1576
             * It's not really a hack, of course: a permanent property can't
1577
             * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1578
             * any instance, prototype or delegating".  Without a slot, and
1579
             * without the ability to remove and recreate (with differences)
1580
             * the property, there is no way to tell whether it is directly
1581
             * owned, or indirectly delegated.
1582
             */
1583
            sprop = (JSScopeProperty *)prop;
1584
            *rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
1585
        } else {
1586
            *rval = JSVAL_FALSE;
1587
        }
1588
    }
1589
    if (prop)
1590
        OBJ_DROP_PROPERTY(cx, obj2, prop);
1591
    return JS_TRUE;
1592
}
1593
1594
/* Proposed ECMA 15.2.4.6. */
1595
static JSBool
1596
obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1597
                  jsval *rval)
1598
{
1599
    JSBool b;
1600
1601
    if (!js_IsDelegate(cx, obj, *argv, &b))
1602
        return JS_FALSE;
1603
    *rval = BOOLEAN_TO_JSVAL(b);
1604
    return JS_TRUE;
1605
}
1606
1607
/* Proposed ECMA 15.2.4.7. */
1608
static JSBool
1609
obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1610
                         jsval *rval)
1611
{
1612
    jsid id;
1613
    uintN attrs;
1614
    JSObject *obj2;
1615
    JSProperty *prop;
1616
    JSBool ok;
1617
1618
    if (!JS_ValueToId(cx, argv[0], &id))
1619
        return JS_FALSE;
1620
1621
    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1622
        return JS_FALSE;
1623
1624
    if (!prop) {
1625
        *rval = JSVAL_FALSE;
1626
        return JS_TRUE;
1627
    }
1628
1629
    /*
1630
     * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1631
     * The ECMA spec really should be fixed so propertyIsEnumerable and the
1632
     * for..in loop agree on whether prototype properties are enumerable,
1633
     * obviously by fixing this method (not by breaking the for..in loop!).
1634
     *
1635
     * We check here for shared permanent prototype properties, which should
1636
     * be treated as if they are local to obj.  They are an implementation
1637
     * technique used to satisfy ECMA requirements; users should not be able
1638
     * to distinguish a shared permanent proto-property from a local one.
1639
     */
1640
    if (obj2 != obj &&
1641
        !(OBJ_IS_NATIVE(obj2) &&
1642
          SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
1643
        OBJ_DROP_PROPERTY(cx, obj2, prop);
1644
        *rval = JSVAL_FALSE;
1645
        return JS_TRUE;
1646
    }
1647
1648
    ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
1649
    OBJ_DROP_PROPERTY(cx, obj2, prop);
1650
    if (ok)
1651
        *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
1652
    return ok;
1653
}
1654
1655
#if JS_HAS_GETTER_SETTER
1656
static JSBool
1657
obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1658
                 jsval *rval)
1659
{
1660
    jsval fval, junk;
1661
    jsid id;
1662
    uintN attrs;
1663
1664
    fval = argv[1];
1665
    if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
1666
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1667
                             JSMSG_BAD_GETTER_OR_SETTER,
1668
                             js_getter_str);
1669
        return JS_FALSE;
1670
    }
1671
1672
    if (!JS_ValueToId(cx, argv[0], &id))
1673
        return JS_FALSE;
1674
    if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
1675
        return JS_FALSE;
1676
    /*
1677
     * Getters and setters are just like watchpoints from an access
1678
     * control point of view.
1679
     */
1680
    if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1681
        return JS_FALSE;
1682
    return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1683
                               (JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL,
1684
                               JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED,
1685
                               NULL);
1686
}
1687
1688
static JSBool
1689
obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1690
                 jsval *rval)
1691
{
1692
    jsval fval, junk;
1693
    jsid id;
1694
    uintN attrs;
1695
1696
    fval = argv[1];
1697
    if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
1698
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1699
                             JSMSG_BAD_GETTER_OR_SETTER,
1700
                             js_setter_str);
1701
        return JS_FALSE;
1702
    }
1703
1704
    if (!JS_ValueToId(cx, argv[0], &id))
1705
        return JS_FALSE;
1706
    if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
1707
        return JS_FALSE;
1708
    /*
1709
     * Getters and setters are just like watchpoints from an access
1710
     * control point of view.
1711
     */
1712
    if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1713
        return JS_FALSE;
1714
    return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1715
                               NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval),
1716
                               JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED,
1717
                               NULL);
1718
}
1719
1720
static JSBool
1721
obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1722
                 jsval *rval)
1723
{
1724
    jsid id;
1725
    JSObject *pobj;
1726
    JSProperty *prop;
1727
    JSScopeProperty *sprop;
1728
1729
    if (!JS_ValueToId(cx, argv[0], &id))
1730
        return JS_FALSE;
1731
    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1732
        return JS_FALSE;
1733
    if (prop) {
1734
        if (OBJ_IS_NATIVE(pobj)) {
1735
            sprop = (JSScopeProperty *) prop;
1736
            if (sprop->attrs & JSPROP_GETTER)
1737
                *rval = OBJECT_TO_JSVAL(sprop->getter);
1738
        }
1739
        OBJ_DROP_PROPERTY(cx, pobj, prop);
1740
    }
1741
    return JS_TRUE;
1742
}
1743
1744
static JSBool
1745
obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1746
                 jsval *rval)
1747
{
1748
    jsid id;
1749
    JSObject *pobj;
1750
    JSProperty *prop;
1751
    JSScopeProperty *sprop;
1752
1753
    if (!JS_ValueToId(cx, argv[0], &id))
1754
        return JS_FALSE;
1755
    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1756
        return JS_FALSE;
1757
    if (prop) {
1758
        if (OBJ_IS_NATIVE(pobj)) {
1759
            sprop = (JSScopeProperty *) prop;
1760
            if (sprop->attrs & JSPROP_SETTER)
1761
                *rval = OBJECT_TO_JSVAL(sprop->setter);
1762
        }
1763
        OBJ_DROP_PROPERTY(cx, pobj, prop);
1764
    }
1765
    return JS_TRUE;
1766
}
1767
#endif /* JS_HAS_GETTER_SETTER */
1768
1769
#if JS_HAS_OBJ_WATCHPOINT
1770
const char js_watch_str[] = "watch";
1771
const char js_unwatch_str[] = "unwatch";
1772
#endif
1773
const char js_hasOwnProperty_str[] = "hasOwnProperty";
1774
const char js_isPrototypeOf_str[] = "isPrototypeOf";
1775
const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
1776
#if JS_HAS_GETTER_SETTER
1777
const char js_defineGetter_str[] = "__defineGetter__";
1778
const char js_defineSetter_str[] = "__defineSetter__";
1779
const char js_lookupGetter_str[] = "__lookupGetter__";
1780
const char js_lookupSetter_str[] = "__lookupSetter__";
1781
#endif
1782
1783
static JSFunctionSpec object_methods[] = {
1784
#if JS_HAS_TOSOURCE
1785
    {js_toSource_str,             js_obj_toSource,    0, 0, OBJ_TOSTRING_EXTRA},
1786
#endif
1787
    {js_toString_str,             js_obj_toString,    0, 0, OBJ_TOSTRING_EXTRA},
1788
    {js_toLocaleString_str,       js_obj_toLocaleString, 0, 0, OBJ_TOSTRING_EXTRA},
1789
    {js_valueOf_str,              obj_valueOf,        0,0,0},
1790
#if JS_HAS_OBJ_WATCHPOINT
1791
    {js_watch_str,                obj_watch,          2,0,0},
1792
    {js_unwatch_str,              obj_unwatch,        1,0,0},
1793
#endif
1794
    {js_hasOwnProperty_str,       obj_hasOwnProperty, 1,0,0},
1795
    {js_isPrototypeOf_str,        obj_isPrototypeOf,  1,0,0},
1796
    {js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0},
1797
#if JS_HAS_GETTER_SETTER
1798
    {js_defineGetter_str,         obj_defineGetter,   2,0,0},
1799
    {js_defineSetter_str,         obj_defineSetter,   2,0,0},
1800
    {js_lookupGetter_str,         obj_lookupGetter,   1,0,0},
1801
    {js_lookupSetter_str,         obj_lookupSetter,   1,0,0},
1802
#endif
1803
    {0,0,0,0,0}
1804
};
1805
1806
static JSBool
1807
Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1808
{
1809
    if (argc == 0) {
1810
        /* Trigger logic below to construct a blank object. */
1811
        obj = NULL;
1812
    } else {
1813
        /* If argv[0] is null or undefined, obj comes back null. */
1814
        if (!js_ValueToObject(cx, argv[0], &obj))
1815
            return JS_FALSE;
1816
    }
1817
    if (!obj) {
1818
        JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
1819
        if (cx->fp->flags & JSFRAME_CONSTRUCTING)
1820
            return JS_TRUE;
1821
        obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
1822
        if (!obj)
1823
            return JS_FALSE;
1824
    }
1825
    *rval = OBJECT_TO_JSVAL(obj);
1826
    return JS_TRUE;
1827
}
1828
1829
/*
1830
 * ObjectOps and Class for with-statement stack objects.
1831
 */
1832
static JSBool
1833
with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
1834
                    JSProperty **propp)
1835
{
1836
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
1837
    if (!proto)
1838
        return js_LookupProperty(cx, obj, id, objp, propp);
1839
    return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
1840
}
1841
1842
static JSBool
1843
with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1844
{
1845
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
1846
    if (!proto)
1847
        return js_GetProperty(cx, obj, id, vp);
1848
    return OBJ_GET_PROPERTY(cx, proto, id, vp);
1849
}
1850
1851
static JSBool
1852
with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1853
{
1854
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
1855
    if (!proto)
1856
        return js_SetProperty(cx, obj, id, vp);
1857
    return OBJ_SET_PROPERTY(cx, proto, id, vp);
1858
}
1859
1860
static JSBool
1861
with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1862
                   uintN *attrsp)
1863
{
1864
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
1865
    if (!proto)
1866
        return js_GetAttributes(cx, obj, id, prop, attrsp);
1867
    return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1868
}
1869
1870
static JSBool
1871
with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1872
                   uintN *attrsp)
1873
{
1874
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
1875
    if (!proto)
1876
        return js_SetAttributes(cx, obj, id, prop, attrsp);
1877
    return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1878
}
1879
1880
static JSBool
1881
with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
1882
{
1883
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
1884
    if (!proto)
1885
        return js_DeleteProperty(cx, obj, id, rval);
1886
    return OBJ_DELETE_PROPERTY(cx, proto, id, rval);
1887
}
1888
1889
static JSBool
1890
with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
1891
{
1892
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
1893
    if (!proto)
1894
        return js_DefaultValue(cx, obj, hint, vp);
1895
    return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);
1896
}
1897
1898
static JSBool
1899
with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
1900
               jsval *statep, jsid *idp)
1901
{
1902
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
1903
    if (!proto)
1904
        return js_Enumerate(cx, obj, enum_op, statep, idp);
1905
    return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);
1906
}
1907
1908
static JSBool
1909
with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
1910
                 jsval *vp, uintN *attrsp)
1911
{
1912
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
1913
    if (!proto)
1914
        return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
1915
    return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp);
1916
}
1917
1918
static JSObject *
1919
with_ThisObject(JSContext *cx, JSObject *obj)
1920
{
1921
    JSObject *proto = OBJ_GET_PROTO(cx, obj);
1922
    if (!proto)
1923
        return obj;
1924
    return OBJ_THIS_OBJECT(cx, proto);
1925
}
1926
1927
JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
1928
    js_NewObjectMap,        js_DestroyObjectMap,
1929
    with_LookupProperty,    js_DefineProperty,
1930
    with_GetProperty,       with_SetProperty,
1931
    with_GetAttributes,     with_SetAttributes,
1932
    with_DeleteProperty,    with_DefaultValue,
1933
    with_Enumerate,         with_CheckAccess,
1934
    with_ThisObject,        NATIVE_DROP_PROPERTY,
1935
    NULL,                   NULL,
1936
    NULL,                   NULL,
1937
    js_SetProtoOrParent,    js_SetProtoOrParent,
1938
    js_Mark,                js_Clear,
1939
    NULL,                   NULL
1940
};
1941
1942
static JSObjectOps *
1943
with_getObjectOps(JSContext *cx, JSClass *clasp)
1944
{
1945
    return &js_WithObjectOps;
1946
}
1947
1948
JSClass js_WithClass = {
1949
    "With",
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
1950
    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
1 by Mike Hommey
Import upstream version 1.8.0.4
1951
    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
1952
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
1953
    with_getObjectOps,
1954
    0,0,0,0,0,0,0
1955
};
1956
1.1.2 by Mike Hommey
Import upstream version 1.8.0.7
1957
JSObject *
1958
js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
1959
{
1960
    JSObject *obj;
1961
1962
    obj = js_NewObject(cx, &js_WithClass, proto, parent);
1963
    if (!obj)
1964
        return NULL;
1965
    obj->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(cx->fp);
1966
    OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
1967
    return obj;
1968
}
1969
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
1970
JSObject *
1971
js_NewBlockObject(JSContext *cx)
1972
{
1973
    JSObject *obj;
1974
1975
    /*
1976
     * Null obj's proto slot so that Object.prototype.* does not pollute block
1977
     * scopes.  Make sure obj has its own scope too, since clearing proto does
1978
     * not affect OBJ_SCOPE(obj).
1979
     */
1980
    obj = js_NewObject(cx, &js_BlockClass, NULL, NULL);
1981
    if (!obj || !js_GetMutableScope(cx, obj))
1982
        return NULL;
1983
    OBJ_SET_PROTO(cx, obj, NULL);
1984
    return obj;
1985
}
1986
1987
JSObject *
1988
js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent,
1989
                    JSStackFrame *fp)
1990
{
1991
    JSObject *clone;
1992
1993
    clone = js_NewObject(cx, &js_BlockClass, proto, parent);
1994
    if (!clone)
1995
        return NULL;
1996
    clone->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fp);
1997
    clone->slots[JSSLOT_BLOCK_DEPTH] =
1998
        OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH);
1999
    return clone;
2000
}
2001
2002
/*
2003
 * XXXblock this reverses a path in the property tree -- try to share
2004
 *          the prototype's scope harder!
2005
 */
2006
JSBool
2007
js_PutBlockObject(JSContext *cx, JSObject *obj)
2008
{
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
2009
    JSStackFrame *fp;
2010
    uintN depth, slot;
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2011
    JSScopeProperty *sprop;
1 by Mike Hommey
Import upstream version 1.8.0.4
2012
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
2013
    fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
2014
    JS_ASSERT(fp);
2015
    depth = OBJ_BLOCK_DEPTH(cx, obj);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2016
    for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) {
2017
        if (sprop->getter != js_BlockClass.getProperty)
2018
            continue;
2019
        if (!(sprop->flags & SPROP_HAS_SHORTID))
2020
            continue;
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
2021
        slot = depth + (uintN)sprop->shortid;
2022
        JS_ASSERT(slot < fp->script->depth);
2023
        if (!js_DefineNativeProperty(cx, obj, sprop->id,
2024
                                     fp->spbase[slot], NULL, NULL,
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2025
                                     JSPROP_ENUMERATE | JSPROP_PERMANENT,
2026
                                     SPROP_HAS_SHORTID, sprop->shortid,
2027
                                     NULL)) {
2028
            JS_SetPrivate(cx, obj, NULL);
2029
            return JS_FALSE;
2030
        }
2031
    }
2032
2033
    return JS_SetPrivate(cx, obj, NULL);
2034
}
2035
2036
static JSBool
2037
block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2038
{
2039
    JSStackFrame *fp;
2040
    jsint slot;
2041
2042
    JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL));
2043
    if (!JSVAL_IS_INT(id))
2044
        return JS_TRUE;
2045
2046
    fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
2047
    if (!fp)
2048
        return JS_TRUE;
2049
2050
    slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id);
2051
    JS_ASSERT((uintN)slot < fp->script->depth);
2052
    *vp = fp->spbase[slot];
2053
    return JS_TRUE;
2054
}
2055
2056
static JSBool
2057
block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2058
{
2059
    JSStackFrame *fp;
2060
    jsint slot;
2061
2062
    JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL));
2063
    if (!JSVAL_IS_INT(id))
2064
        return JS_TRUE;
2065
2066
    fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
2067
    if (!fp)
2068
        return JS_TRUE;
2069
2070
    slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id);
2071
    JS_ASSERT((uintN)slot < fp->script->depth);
2072
    fp->spbase[slot] = *vp;
2073
    return JS_TRUE;
2074
}
2075
2076
#if JS_HAS_XDR
2077
2078
#define NO_PARENT_INDEX (jsatomid)-1
2079
2080
jsatomid
2081
FindObjectAtomIndex(JSAtomMap *map, JSObject *obj)
2082
{
2083
    size_t i;
2084
    JSAtom *atom;
2085
2086
    for (i = 0; i < map->length; i++) {
2087
        atom = map->vector[i];
2088
        if (ATOM_KEY(atom) == OBJECT_TO_JSVAL(obj))
2089
            return i;
2090
    }
2091
2092
    return NO_PARENT_INDEX;
2093
}
2094
2095
static JSBool
2096
block_xdrObject(JSXDRState *xdr, JSObject **objp)
2097
{
2098
    JSContext *cx;
2099
    jsatomid parentId;
2100
    JSAtomMap *atomMap;
2101
    JSObject *obj, *parent;
2102
    uint16 depth, count, i;
2103
    uint32 tmp;
2104
    JSTempValueRooter tvr;
2105
    JSScopeProperty *sprop;
2106
    jsid propid;
2107
    JSAtom *atom;
2108
    int16 shortid;
2109
    JSBool ok;
2110
2111
    cx = xdr->cx;
2112
#ifdef __GNUC__
2113
    obj = NULL;         /* quell GCC overwarning */
2114
#endif
2115
2116
    atomMap = &xdr->script->atomMap;
2117
    if (xdr->mode == JSXDR_ENCODE) {
2118
        obj = *objp;
2119
        parent = OBJ_GET_PARENT(cx, obj);
2120
        parentId = FindObjectAtomIndex(atomMap, parent);
2121
        depth = OBJ_BLOCK_DEPTH(cx, obj);
2122
        count = OBJ_BLOCK_COUNT(cx, obj);
2123
        tmp = (uint32)(depth << 16) | count;
2124
    }
2125
#ifdef __GNUC__ /* suppress bogus gcc warnings */
2126
    else count = 0;
2127
#endif
2128
2129
    /* First, XDR the parent atomid. */
2130
    if (!JS_XDRUint32(xdr, &parentId))
1 by Mike Hommey
Import upstream version 1.8.0.4
2131
        return JS_FALSE;
2132
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2133
    if (xdr->mode == JSXDR_DECODE) {
2134
        obj = js_NewBlockObject(cx);
1 by Mike Hommey
Import upstream version 1.8.0.4
2135
        if (!obj)
2136
            return JS_FALSE;
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2137
        *objp = obj;
2138
2139
        /*
2140
         * If there's a parent id, then get the parent out of our script's
2141
         * atomMap. We know that we XDR block object in outer-to-inner order,
2142
         * which means that getting the parent now will work.
2143
         */
2144
        if (parentId == NO_PARENT_INDEX) {
2145
            parent = NULL;
2146
        } else {
2147
            atom = js_GetAtom(cx, atomMap, parentId);
2148
            JS_ASSERT(ATOM_IS_OBJECT(atom));
2149
            parent = ATOM_TO_OBJECT(atom);
2150
        }
2151
        obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
2152
    }
2153
2154
    JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr);
2155
2156
    if (!JS_XDRUint32(xdr, &tmp)) {
2157
        JS_POP_TEMP_ROOT(cx, &tvr);
2158
        return JS_FALSE;
2159
    }
2160
2161
    if (xdr->mode == JSXDR_DECODE) {
2162
        depth = (uint16)(tmp >> 16);
2163
        count = (uint16)tmp;
2164
        obj->slots[JSSLOT_BLOCK_DEPTH] = INT_TO_JSVAL(depth);
2165
    }
2166
2167
    /*
2168
     * XDR the block object's properties. We know that there are 'count'
2169
     * properties to XDR, stored as id/shortid pairs. We do not XDR any
2170
     * non-native properties, only those that the compiler created.
2171
     */
2172
    sprop = NULL;
2173
    ok = JS_TRUE;
2174
    for (i = 0; i < count; i++) {
2175
        if (xdr->mode == JSXDR_ENCODE) {
2176
            /* Find a property to XDR. */
2177
            do {
2178
                /* If sprop is NULL, this is the first property. */
2179
                sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProp;
2180
            } while (!(sprop->flags & SPROP_HAS_SHORTID));
2181
2182
            JS_ASSERT(sprop->getter == js_BlockClass.getProperty);
2183
            propid = sprop->id;
2184
            JS_ASSERT(JSID_IS_ATOM(propid));
2185
            atom = JSID_TO_ATOM(propid);
2186
            shortid = sprop->shortid;
2187
            JS_ASSERT(shortid >= 0);
2188
        }
2189
2190
        /* XDR the real id, then the shortid. */
2191
        if (!js_XDRStringAtom(xdr, &atom) ||
2192
            !JS_XDRUint16(xdr, (uint16 *)&shortid)) {
2193
            ok = JS_FALSE;
2194
            break;
2195
        }
2196
2197
        if (xdr->mode == JSXDR_DECODE) {
2198
            if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom),
2199
                                         JSVAL_VOID, NULL, NULL,
2200
                                         JSPROP_ENUMERATE | JSPROP_PERMANENT,
2201
                                         SPROP_HAS_SHORTID, shortid, NULL)) {
2202
                ok = JS_FALSE;
2203
                break;
2204
            }
2205
        }
2206
    }
2207
2208
    JS_POP_TEMP_ROOT(cx, &tvr);
2209
    return ok;
1 by Mike Hommey
Import upstream version 1.8.0.4
2210
}
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2211
2212
#else
2213
# define block_xdrObject NULL
1 by Mike Hommey
Import upstream version 1.8.0.4
2214
#endif
2215
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2216
JSClass js_BlockClass = {
2217
    "Block",
2218
    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) |
2219
    JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_CACHED_PROTO(JSProto_Block),
2220
    JS_PropertyStub,  JS_PropertyStub,  block_getProperty, block_setProperty,
2221
    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,    JS_FinalizeStub,
2222
    NULL, NULL, NULL, NULL, block_xdrObject, NULL, NULL, NULL
2223
};
2224
2225
JSObject*
2226
js_InitBlockClass(JSContext *cx, JSObject* obj)
2227
{
2228
    JSObject *proto;
2229
2230
    proto = JS_InitClass(cx, obj, NULL, &js_BlockClass, NULL, 0, NULL,
2231
                         NULL, NULL, NULL);
2232
    if (!proto)
2233
        return NULL;
2234
2235
    OBJ_SET_PROTO(cx, proto, NULL);
2236
    return proto;
2237
}
2238
1 by Mike Hommey
Import upstream version 1.8.0.4
2239
JSObject *
2240
js_InitObjectClass(JSContext *cx, JSObject *obj)
2241
{
2242
    JSObject *proto;
2243
2244
    proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1,
2245
                         object_props, object_methods, NULL, NULL);
2246
    if (!proto)
2247
        return NULL;
2248
1.1.10 by Fabien Tassin
Import upstream version 1.8.1.13+nobinonly
2249
    /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
2250
    if (!js_DefineFunction(cx, obj, cx->runtime->atomState.evalAtom,
2251
                           obj_eval, 1, 0)) {
1 by Mike Hommey
Import upstream version 1.8.0.4
2252
        return NULL;
2253
    }
2254
2255
    return proto;
2256
}
2257
2258
void
2259
js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
2260
                 JSClass *clasp)
2261
{
2262
    map->nrefs = nrefs;
2263
    map->ops = ops;
2264
    map->nslots = JS_INITIAL_NSLOTS;
2265
    map->freeslot = JSSLOT_FREE(clasp);
2266
}
2267
2268
JSObjectMap *
2269
js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
2270
                JSClass *clasp, JSObject *obj)
2271
{
2272
    return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);
2273
}
2274
2275
void
2276
js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)
2277
{
2278
    js_DestroyScope(cx, (JSScope *)map);
2279
}
2280
2281
JSObjectMap *
2282
js_HoldObjectMap(JSContext *cx, JSObjectMap *map)
2283
{
2284
    JS_ASSERT(map->nrefs >= 0);
2285
    JS_ATOMIC_INCREMENT(&map->nrefs);
2286
    return map;
2287
}
2288
2289
JSObjectMap *
2290
js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)
2291
{
2292
    JS_ASSERT(map->nrefs > 0);
2293
    JS_ATOMIC_DECREMENT(&map->nrefs);
2294
    if (map->nrefs == 0) {
2295
        map->ops->destroyObjectMap(cx, map);
2296
        return NULL;
2297
    }
2298
    if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)
2299
        ((JSScope *)map)->object = NULL;
2300
    return map;
2301
}
2302
2303
static jsval *
2304
AllocSlots(JSContext *cx, jsval *slots, uint32 nslots)
2305
{
2306
    size_t nbytes, obytes, minbytes;
2307
    uint32 i, oslots;
2308
    jsval *newslots;
2309
2310
    nbytes = (nslots + 1) * sizeof(jsval);
2311
    if (slots) {
2312
        oslots = slots[-1];
2313
        obytes = (oslots + 1) * sizeof(jsval);
2314
    } else {
2315
        oslots = 0;
2316
        obytes = 0;
2317
    }
2318
2319
    if (nbytes <= GC_NBYTES_MAX) {
2320
        newslots = (jsval *) js_NewGCThing(cx, GCX_PRIVATE, nbytes);
2321
    } else {
2322
        newslots = (jsval *)
2323
                   JS_realloc(cx,
2324
                              (obytes <= GC_NBYTES_MAX) ? NULL : slots - 1,
2325
                              nbytes);
2326
    }
2327
    if (!newslots)
2328
        return NULL;
2329
2330
    if (obytes != 0) {
2331
        /* If either nbytes or obytes fit in a GC-thing, we must copy. */
2332
        minbytes = JS_MIN(nbytes, obytes);
2333
        if (minbytes <= GC_NBYTES_MAX)
2334
            memcpy(newslots + 1, slots, minbytes - sizeof(jsval));
2335
2336
        /* If nbytes are in a GC-thing but obytes aren't, free obytes. */
2337
        if (nbytes <= GC_NBYTES_MAX && obytes > GC_NBYTES_MAX)
2338
            JS_free(cx, slots - 1);
2339
2340
        /* If we're extending an allocation, initialize free slots. */
2341
        if (nslots > oslots) {
2342
            for (i = 1 + oslots; i <= nslots; i++)
2343
                newslots[i] = JSVAL_VOID;
2344
        }
2345
    }
2346
2347
    newslots[0] = nslots;
2348
    return ++newslots;
2349
}
2350
2351
static void
2352
FreeSlots(JSContext *cx, jsval *slots)
2353
{
2354
    size_t nbytes;
2355
2356
    /*
2357
     * NB: We count on smaller GC-things being finalized before larger things
2358
     * that become garbage during the same GC.  Without this assumption, we
2359
     * couldn't load slots[-1] here without possibly loading a gcFreeList link
2360
     * (see struct JSGCThing in jsgc.h).
2361
     */
2362
    nbytes = (slots[-1] + 1) * sizeof(jsval);
2363
    if (nbytes > GC_NBYTES_MAX)
2364
        JS_free(cx, slots - 1);
2365
}
2366
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2367
extern JSBool
2368
js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp)
2369
{
2370
    JSProtoKey key;
2371
    JSAtom *atom;
2372
2373
    key = JSCLASS_CACHED_PROTO_KEY(clasp);
2374
    if (key != JSProto_Null) {
2375
        *idp = INT_TO_JSID(key);
2376
    } else if (clasp->flags & JSCLASS_IS_ANONYMOUS) {
2377
        *idp = INT_TO_JSID(JSProto_Object);
2378
    } else {
2379
        atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
2380
        if (!atom)
2381
            return JS_FALSE;
2382
        *idp = ATOM_TO_JSID(atom);
2383
    }
2384
    return JS_TRUE;
2385
}
2386
1 by Mike Hommey
Import upstream version 1.8.0.4
2387
JSObject *
2388
js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
2389
{
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2390
    jsid id;
1 by Mike Hommey
Import upstream version 1.8.0.4
2391
    JSObject *obj;
2392
    JSObjectOps *ops;
2393
    JSObjectMap *map;
2394
    JSClass *protoclasp;
2395
    uint32 nslots, i;
2396
    jsval *newslots;
2397
    JSTempValueRooter tvr;
2398
2399
    /* Bootstrap the ur-object, and make it the default prototype object. */
2400
    if (!proto) {
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2401
        if (!js_GetClassId(cx, clasp, &id))
2402
            return NULL;
2403
        if (!js_GetClassPrototype(cx, parent, id, &proto))
2404
            return NULL;
2405
        if (!proto &&
2406
            !js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object),
2407
                                  &proto)) {
2408
            return NULL;
2409
        }
1 by Mike Hommey
Import upstream version 1.8.0.4
2410
    }
2411
2412
    /* Always call the class's getObjectOps hook if it has one. */
2413
    ops = clasp->getObjectOps
2414
          ? clasp->getObjectOps(cx, clasp)
2415
          : &js_ObjectOps;
2416
2417
    /*
2418
     * Allocate a zeroed object from the GC heap.  Do this *after* any other
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2419
     * GC-thing allocations under js_GetClassPrototype or clasp->getObjectOps,
1 by Mike Hommey
Import upstream version 1.8.0.4
2420
     * to avoid displacing the newborn root for obj.
2421
     */
2422
    obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject));
2423
    if (!obj)
2424
        return NULL;
2425
2426
    /*
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2427
     * Root obj to prevent it from being collected out from under this call.
2428
     * to js_NewObject.  AllocSlots can trigger a finalizer from a last-ditch
2429
     * GC calling JS_ClearNewbornRoots. There's also the possibilty of things
2430
     * happening under the objectHook call-out further below.
1 by Mike Hommey
Import upstream version 1.8.0.4
2431
     */
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2432
    JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
1 by Mike Hommey
Import upstream version 1.8.0.4
2433
2434
    /*
2435
     * Share proto's map only if it has the same JSObjectOps, and only if
2436
     * proto's class has the same private and reserved slots as obj's map
2437
     * and class have.  We assume that if prototype and object are of the
2438
     * same class, they always have the same number of computed reserved
2439
     * slots (returned via clasp->reserveSlots); otherwise, prototype and
2440
     * object classes must have the same (null or not) reserveSlots hook.
2441
     */
2442
    if (proto &&
2443
        (map = proto->map)->ops == ops &&
2444
        ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp ||
2445
         (!((protoclasp->flags ^ clasp->flags) &
2446
            (JSCLASS_HAS_PRIVATE |
2447
             (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) &&
2448
          protoclasp->reserveSlots == clasp->reserveSlots)))
2449
    {
2450
        /*
2451
         * Default parent to the parent of the prototype, which was set from
2452
         * the parent of the prototype's constructor.
2453
         */
2454
        if (!parent)
2455
            parent = OBJ_GET_PARENT(cx, proto);
2456
2457
        /* Share the given prototype's map. */
2458
        obj->map = js_HoldObjectMap(cx, map);
2459
2460
        /* Ensure that obj starts with the minimum slots for clasp. */
2461
        nslots = JS_INITIAL_NSLOTS;
2462
    } else {
2463
        /* Leave parent alone.  Allocate a new map for obj. */
2464
        map = ops->newObjectMap(cx, 1, ops, clasp, obj);
2465
        if (!map)
2466
            goto bad;
2467
        obj->map = map;
2468
2469
        /* Let ops->newObjectMap set nslots so as to reserve slots. */
2470
        nslots = map->nslots;
2471
    }
2472
2473
    /* Allocate a slots vector, with a -1'st element telling its length. */
2474
    newslots = AllocSlots(cx, NULL, nslots);
2475
    if (!newslots) {
2476
        js_DropObjectMap(cx, obj->map, obj);
2477
        obj->map = NULL;
2478
        goto bad;
2479
    }
2480
2481
    /* Set the proto, parent, and class properties. */
2482
    newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
2483
    newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
2484
    newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
2485
2486
    /* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */
2487
    for (i = JSSLOT_CLASS + 1; i < nslots; i++)
2488
        newslots[i] = JSVAL_VOID;
2489
2490
    /* Store newslots after initializing all of 'em, just in case. */
2491
    obj->slots = newslots;
2492
2493
    if (cx->runtime->objectHook) {
2494
        JS_KEEP_ATOMS(cx->runtime);
2495
        cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
2496
        JS_UNKEEP_ATOMS(cx->runtime);
2497
    }
2498
2499
out:
2500
    JS_POP_TEMP_ROOT(cx, &tvr);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2501
    cx->weakRoots.newborn[GCX_OBJECT] = (JSGCThing *) obj;
1 by Mike Hommey
Import upstream version 1.8.0.4
2502
    return obj;
2503
2504
bad:
2505
    obj = NULL;
2506
    goto out;
2507
}
2508
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2509
JS_STATIC_DLL_CALLBACK(JSObject *)
2510
js_InitNullClass(JSContext *cx, JSObject *obj)
2511
{
2512
    JS_ASSERT(0);
2513
    return NULL;
2514
}
2515
2516
#define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
2517
#include "jsproto.tbl"
2518
#undef JS_PROTO
2519
2520
static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = {
2521
#define JS_PROTO(name,code,init) init,
2522
#include "jsproto.tbl"
2523
#undef JS_PROTO
2524
};
2525
2526
JSBool
2527
js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
2528
                  JSObject **objp)
2529
{
2530
    JSBool ok;
2531
    JSObject *tmp, *cobj;
2532
    JSResolvingKey rkey;
2533
    JSResolvingEntry *rentry;
2534
    uint32 generation;
2535
    JSObjectOp init;
2536
    jsval v;
2537
2538
    while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
2539
        obj = tmp;
2540
    if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) {
2541
        *objp = NULL;
2542
        return JS_TRUE;
2543
    }
2544
2545
    ok = JS_GetReservedSlot(cx, obj, key, &v);
2546
    if (!ok)
2547
        return JS_FALSE;
2548
    if (!JSVAL_IS_PRIMITIVE(v)) {
2549
        *objp = JSVAL_TO_OBJECT(v);
2550
        return JS_TRUE;
2551
    }
2552
2553
    rkey.obj = obj;
2554
    rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
2555
    if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry))
2556
        return JS_FALSE;
2557
    if (!rentry) {
2558
        /* Already caching key in obj -- suppress recursion. */
2559
        *objp = NULL;
2560
        return JS_TRUE;
2561
    }
2562
    generation = cx->resolvingTable->generation;
2563
2564
    cobj = NULL;
2565
    init = lazy_prototype_init[key];
2566
    if (init) {
2567
        if (!init(cx, obj)) {
2568
            ok = JS_FALSE;
2569
        } else {
2570
            ok = JS_GetReservedSlot(cx, obj, key, &v);
2571
            if (ok && !JSVAL_IS_PRIMITIVE(v))
2572
                cobj = JSVAL_TO_OBJECT(v);
2573
        }
2574
    }
2575
2576
    js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation);
2577
    *objp = cobj;
2578
    return ok;
2579
}
2580
2581
JSBool
2582
js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj)
2583
{
2584
    JS_ASSERT(!OBJ_GET_PARENT(cx, obj));
2585
    if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL))
2586
        return JS_TRUE;
2587
2588
    return JS_SetReservedSlot(cx, obj, key, OBJECT_TO_JSVAL(cobj));
2589
}
2590
2591
JSBool
2592
js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp)
2593
{
2594
    JSObject *obj, *cobj, *pobj;
2595
    JSProtoKey key;
1 by Mike Hommey
Import upstream version 1.8.0.4
2596
    JSProperty *prop;
1.1.10 by Fabien Tassin
Import upstream version 1.8.1.13+nobinonly
2597
    jsval v;
1 by Mike Hommey
Import upstream version 1.8.0.4
2598
    JSScopeProperty *sprop;
2599
2600
    if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) {
2601
        /* Find the topmost object in the scope chain. */
2602
        do {
2603
            obj = start;
2604
            start = OBJ_GET_PARENT(cx, obj);
2605
        } while (start);
2606
    } else {
2607
        obj = cx->globalObject;
2608
        if (!obj) {
2609
            *vp = JSVAL_VOID;
2610
            return JS_TRUE;
2611
        }
2612
    }
2613
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2614
    OBJ_TO_INNER_OBJECT(cx, obj);
2615
    if (!obj)
2616
        return JS_FALSE;
2617
2618
    if (JSID_IS_INT(id)) {
2619
        key = JSID_TO_INT(id);
2620
        JS_ASSERT(key != JSProto_Null);
2621
        if (!js_GetClassObject(cx, obj, key, &cobj))
2622
            return JS_FALSE;
2623
        if (cobj) {
2624
            *vp = OBJECT_TO_JSVAL(cobj);
2625
            return JS_TRUE;
2626
        }
2627
        id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
2628
    }
2629
1 by Mike Hommey
Import upstream version 1.8.0.4
2630
    JS_ASSERT(OBJ_IS_NATIVE(obj));
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2631
    if (!js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME,
2632
                                    &pobj, &prop)) {
1 by Mike Hommey
Import upstream version 1.8.0.4
2633
        return JS_FALSE;
2634
    }
1.1.10 by Fabien Tassin
Import upstream version 1.8.1.13+nobinonly
2635
    v = JSVAL_VOID;
2636
    if (prop)  {
2637
        if (OBJ_IS_NATIVE(pobj)) {
2638
            sprop = (JSScopeProperty *) prop;
2639
            if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))) {
2640
                v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
2641
                if (JSVAL_IS_PRIMITIVE(v))
2642
                    v = JSVAL_VOID; 
2643
            }
2644
        }
2645
        OBJ_DROP_PROPERTY(cx, pobj, prop);
1 by Mike Hommey
Import upstream version 1.8.0.4
2646
    }
1.1.10 by Fabien Tassin
Import upstream version 1.8.1.13+nobinonly
2647
    *vp = v;
1 by Mike Hommey
Import upstream version 1.8.0.4
2648
    return JS_TRUE;
2649
}
2650
2651
JSObject *
2652
js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
2653
                   JSObject *parent, uintN argc, jsval *argv)
2654
{
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2655
    jsid id;
1 by Mike Hommey
Import upstream version 1.8.0.4
2656
    jsval cval, rval;
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
2657
    JSTempValueRooter argtvr, tvr;
1 by Mike Hommey
Import upstream version 1.8.0.4
2658
    JSObject *obj, *ctor;
2659
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
2660
    JS_PUSH_TEMP_ROOT(cx, argc, argv, &argtvr);
2661
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2662
    if (!js_GetClassId(cx, clasp, &id) ||
2663
        !js_FindClassObject(cx, parent, id, &cval)) {
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
2664
        JS_POP_TEMP_ROOT(cx, &argtvr);
1 by Mike Hommey
Import upstream version 1.8.0.4
2665
        return NULL;
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
2666
    }
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2667
1 by Mike Hommey
Import upstream version 1.8.0.4
2668
    if (JSVAL_IS_PRIMITIVE(cval)) {
2669
        js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
2670
        JS_POP_TEMP_ROOT(cx, &argtvr);
1 by Mike Hommey
Import upstream version 1.8.0.4
2671
        return NULL;
2672
    }
2673
2674
    /*
2675
     * Protect cval in case a crazy getter for .prototype uproots it.  After
2676
     * this point, all control flow must exit through label out with obj set.
2677
     */
2678
    JS_PUSH_SINGLE_TEMP_ROOT(cx, cval, &tvr);
2679
2680
    /*
2681
     * If proto or parent are NULL, set them to Constructor.prototype and/or
2682
     * Constructor.__parent__, just like JSOP_NEW does.
2683
     */
2684
    ctor = JSVAL_TO_OBJECT(cval);
2685
    if (!parent)
2686
        parent = OBJ_GET_PARENT(cx, ctor);
2687
    if (!proto) {
2688
        if (!OBJ_GET_PROPERTY(cx, ctor,
2689
                              ATOM_TO_JSID(cx->runtime->atomState
2690
                                           .classPrototypeAtom),
2691
                              &rval)) {
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
2692
            obj = NULL;
1 by Mike Hommey
Import upstream version 1.8.0.4
2693
            goto out;
2694
        }
2695
        if (JSVAL_IS_OBJECT(rval))
2696
            proto = JSVAL_TO_OBJECT(rval);
2697
    }
2698
2699
    obj = js_NewObject(cx, clasp, proto, parent);
2700
    if (!obj)
2701
        goto out;
2702
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
2703
    if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval))
2704
        goto bad;
2705
2706
    if (JSVAL_IS_PRIMITIVE(rval))
1 by Mike Hommey
Import upstream version 1.8.0.4
2707
        goto out;
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
2708
    obj = JSVAL_TO_OBJECT(rval);
2709
2710
    /*
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2711
     * If the instance's class differs from what was requested, throw a type
2712
     * error.  If the given class has both the JSCLASS_HAS_PRIVATE and the
2713
     * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
2714
     * private data set at this point, then the constructor was replaced and
2715
     * we should throw a type error.
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
2716
     */
2717
    if (OBJ_GET_CLASS(cx, obj) != clasp ||
2718
        (!(~clasp->flags & (JSCLASS_HAS_PRIVATE |
2719
                            JSCLASS_CONSTRUCT_PROTOTYPE)) &&
2720
         !JS_GetPrivate(cx, obj))) {
2721
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2722
                             JSMSG_WRONG_CONSTRUCTOR, clasp->name);
2723
        goto bad;
1 by Mike Hommey
Import upstream version 1.8.0.4
2724
    }
2725
2726
out:
2727
    JS_POP_TEMP_ROOT(cx, &tvr);
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
2728
    JS_POP_TEMP_ROOT(cx, &argtvr);
1 by Mike Hommey
Import upstream version 1.8.0.4
2729
    return obj;
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
2730
2731
bad:
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2732
    cx->weakRoots.newborn[GCX_OBJECT] = NULL;
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
2733
    obj = NULL;
2734
    goto out;
1 by Mike Hommey
Import upstream version 1.8.0.4
2735
}
2736
2737
void
2738
js_FinalizeObject(JSContext *cx, JSObject *obj)
2739
{
2740
    JSObjectMap *map;
2741
2742
    /* Cope with stillborn objects that have no map. */
2743
    map = obj->map;
2744
    if (!map)
2745
        return;
2746
    JS_ASSERT(obj->slots);
2747
2748
    if (cx->runtime->objectHook)
2749
        cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData);
2750
2751
    /* Remove all watchpoints with weak links to obj. */
2752
    JS_ClearWatchPointsForObject(cx, obj);
2753
2754
    /*
2755
     * Finalize obj first, in case it needs map and slots.  Optimized to use
2756
     * LOCKED_OBJ_GET_CLASS instead of OBJ_GET_CLASS, so we avoid "promoting"
2757
     * obj's scope from lock-free to lock-full (see jslock.c:ClaimScope) when
2758
     * we're called from the GC.  Only the GC should call js_FinalizeObject,
2759
     * and no other threads run JS (and possibly racing to update obj->slots)
2760
     * while the GC is running.
2761
     */
2762
    LOCKED_OBJ_GET_CLASS(obj)->finalize(cx, obj);
2763
2764
    /* Drop map and free slots. */
2765
    js_DropObjectMap(cx, map, obj);
2766
    obj->map = NULL;
2767
    FreeSlots(cx, obj->slots);
2768
    obj->slots = NULL;
2769
}
2770
2771
/* XXXbe if one adds props, deletes earlier props, adds more, the last added
2772
         won't recycle the deleted props' slots. */
2773
JSBool
2774
js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
2775
{
2776
    JSObjectMap *map;
2777
    JSClass *clasp;
2778
    uint32 nslots;
2779
    jsval *newslots;
2780
2781
    map = obj->map;
2782
    JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2783
    clasp = LOCKED_OBJ_GET_CLASS(obj);
2784
    if (map->freeslot == JSSLOT_FREE(clasp)) {
2785
        /* Adjust map->freeslot to include computed reserved slots, if any. */
2786
        if (clasp->reserveSlots)
2787
            map->freeslot += clasp->reserveSlots(cx, obj);
2788
    }
2789
    nslots = map->nslots;
2790
    if (map->freeslot >= nslots) {
2791
        nslots = map->freeslot;
2792
        JS_ASSERT(nslots >= JS_INITIAL_NSLOTS);
2793
        nslots += (nslots + 1) / 2;
2794
2795
        newslots = AllocSlots(cx, obj->slots, nslots);
2796
        if (!newslots)
2797
            return JS_FALSE;
2798
        map->nslots = nslots;
2799
        obj->slots = newslots;
2800
    }
2801
2802
    *slotp = map->freeslot++;
2803
    return JS_TRUE;
2804
}
2805
2806
void
2807
js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
2808
{
2809
    JSObjectMap *map;
2810
    uint32 nslots;
2811
    jsval *newslots;
2812
2813
    OBJ_CHECK_SLOT(obj, slot);
2814
    obj->slots[slot] = JSVAL_VOID;
2815
    map = obj->map;
2816
    JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2817
    if (map->freeslot == slot + 1)
2818
        map->freeslot = slot;
2819
    nslots = map->nslots;
2820
    if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) {
2821
        nslots = map->freeslot;
2822
        nslots += nslots / 2;
2823
        if (nslots < JS_INITIAL_NSLOTS)
2824
            nslots = JS_INITIAL_NSLOTS;
2825
2826
        newslots = AllocSlots(cx, obj->slots, nslots);
2827
        if (!newslots)
2828
            return;
2829
        map->nslots = nslots;
2830
        obj->slots = newslots;
2831
    }
2832
}
2833
2834
/* JSVAL_INT_MAX as a string */
2835
#define JSVAL_INT_MAX_STRING "1073741823"
2836
2837
#define CHECK_FOR_STRING_INDEX(id)                                            \
2838
    JS_BEGIN_MACRO                                                            \
2839
        if (JSID_IS_ATOM(id)) {                                               \
2840
            JSAtom *atom_ = JSID_TO_ATOM(id);                                 \
2841
            JSString *str_ = ATOM_TO_STRING(atom_);                           \
2842
            const jschar *cp_ = str_->chars;                                  \
2843
            JSBool negative_ = (*cp_ == '-');                                 \
2844
            if (negative_) cp_++;                                             \
1.1.4 by Mike Hommey
Import upstream version 1.8.0.9
2845
            if (JS7_ISDEC(*cp_)) {                                            \
2846
                size_t n_ = str_->length - negative_;                         \
2847
                if (n_ <= sizeof(JSVAL_INT_MAX_STRING) - 1)                   \
2848
                    id = CheckForStringIndex(id, cp_, cp_ + n_, negative_);   \
1 by Mike Hommey
Import upstream version 1.8.0.4
2849
            }                                                                 \
2850
        }                                                                     \
2851
    JS_END_MACRO
2852
2853
static jsid
1.1.4 by Mike Hommey
Import upstream version 1.8.0.9
2854
CheckForStringIndex(jsid id, const jschar *cp, const jschar *end,
2855
                    JSBool negative)
1 by Mike Hommey
Import upstream version 1.8.0.4
2856
{
2857
    jsuint index = JS7_UNDEC(*cp++);
2858
    jsuint oldIndex = 0;
2859
    jsuint c = 0;
2860
2861
    if (index != 0) {
2862
        while (JS7_ISDEC(*cp)) {
2863
            oldIndex = index;
2864
            c = JS7_UNDEC(*cp);
2865
            index = 10 * index + c;
2866
            cp++;
2867
        }
2868
    }
1.1.4 by Mike Hommey
Import upstream version 1.8.0.9
2869
    if (cp == end &&
1 by Mike Hommey
Import upstream version 1.8.0.4
2870
        (oldIndex < (JSVAL_INT_MAX / 10) ||
2871
         (oldIndex == (JSVAL_INT_MAX / 10) &&
2872
          c <= (JSVAL_INT_MAX % 10)))) {
2873
        if (negative)
2874
            index = 0 - index;
2875
        id = INT_TO_JSID((jsint)index);
2876
    }
2877
    return id;
2878
}
2879
2880
static JSBool
2881
HidePropertyName(JSContext *cx, jsid *idp)
2882
{
2883
    jsid id;
2884
    JSAtom *atom, *hidden;
2885
2886
    id = *idp;
2887
    JS_ASSERT(JSID_IS_ATOM(id));
2888
2889
    atom = JSID_TO_ATOM(id);
2890
    JS_ASSERT(!(atom->flags & ATOM_HIDDEN));
2891
    JS_ASSERT(ATOM_IS_STRING(atom));
2892
2893
    hidden = js_AtomizeString(cx, ATOM_TO_STRING(atom), ATOM_HIDDEN);
2894
    if (!hidden)
2895
        return JS_FALSE;
2896
2897
    /*
2898
     * Link hidden to unhidden atom to optimize call_enumerate -- this means
2899
     * the GC must mark a hidden atom's unhidden counterpart (see js_MarkAtom
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
2900
     * in jsgc.c).  It uses the atom's entry.value member for this linkage.
1 by Mike Hommey
Import upstream version 1.8.0.4
2901
     */
2902
    hidden->entry.value = atom;
2903
    *idp = ATOM_TO_JSID(hidden);
2904
    return JS_TRUE;
2905
}
2906
2907
JSScopeProperty *
2908
js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id,
2909
                     JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
2910
                     uintN attrs, uintN flags, intN shortid)
2911
{
2912
    if (!HidePropertyName(cx, &id))
2913
        return NULL;
2914
2915
    flags |= SPROP_IS_HIDDEN;
2916
    return js_AddNativeProperty(cx, obj, id, getter, setter, slot, attrs,
2917
                                flags, shortid);
2918
}
2919
2920
JSBool
2921
js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2922
                        JSProperty **propp)
2923
{
2924
    return HidePropertyName(cx, &id) &&
1.1.8 by Fabien Tassin
Import upstream version 1.8.1.9
2925
           js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_HIDDEN,
2926
                                      objp, propp);
1 by Mike Hommey
Import upstream version 1.8.0.4
2927
}
2928
2929
JSScopeProperty *
2930
js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
2931
                     JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
2932
                     uintN attrs, uintN flags, intN shortid)
2933
{
2934
    JSScope *scope;
2935
    JSScopeProperty *sprop;
2936
2937
    JS_LOCK_OBJ(cx, obj);
2938
    scope = js_GetMutableScope(cx, obj);
2939
    if (!scope) {
2940
        sprop = NULL;
2941
    } else {
2942
        /*
2943
         * Handle old bug that took empty string as zero index.  Also convert
2944
         * string indices to integers if appropriate.
2945
         */
2946
        CHECK_FOR_STRING_INDEX(id);
2947
        sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
2948
                                    flags, shortid);
2949
    }
2950
    JS_UNLOCK_OBJ(cx, obj);
2951
    return sprop;
2952
}
2953
2954
JSScopeProperty *
2955
js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
2956
                             JSScopeProperty *sprop, uintN attrs, uintN mask,
2957
                             JSPropertyOp getter, JSPropertyOp setter)
2958
{
2959
    JSScope *scope;
2960
2961
    JS_LOCK_OBJ(cx, obj);
2962
    scope = js_GetMutableScope(cx, obj);
2963
    if (!scope) {
2964
        sprop = NULL;
2965
    } else {
2966
        sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask,
2967
                                            getter, setter);
2968
        if (sprop) {
2969
            PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, sprop->id,
2970
                                sprop);
2971
        }
2972
    }
2973
    JS_UNLOCK_OBJ(cx, obj);
2974
    return sprop;
2975
}
2976
2977
JSBool
2978
js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
2979
                  JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
2980
                  JSProperty **propp)
2981
{
2982
    return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs,
2983
                                   0, 0, propp);
2984
}
2985
2986
/*
2987
 * Backward compatibility requires allowing addProperty hooks to mutate the
2988
 * nominal initial value of a slot-full property, while GC safety wants that
2989
 * value to be stored before the call-out through the hook.  Optimize to do
2990
 * both while saving cycles for classes that stub their addProperty hook.
2991
 */
2992
#define ADD_PROPERTY_HELPER(cx,clasp,obj,scope,sprop,vp,cleanup)              \
2993
    JS_BEGIN_MACRO                                                            \
2994
        if ((clasp)->addProperty != JS_PropertyStub) {                        \
2995
            jsval nominal_ = *(vp);                                           \
2996
            if (!(clasp)->addProperty(cx, obj, SPROP_USERID(sprop), vp)) {    \
2997
                cleanup;                                                      \
2998
            }                                                                 \
2999
            if (*(vp) != nominal_) {                                          \
3000
                if (SPROP_HAS_VALID_SLOT(sprop, scope))                       \
3001
                    LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *(vp));           \
3002
            }                                                                 \
3003
        }                                                                     \
3004
    JS_END_MACRO
3005
3006
JSBool
3007
js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
3008
                        JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
3009
                        uintN flags, intN shortid, JSProperty **propp)
3010
{
3011
    JSClass *clasp;
3012
    JSScope *scope;
3013
    JSScopeProperty *sprop;
3014
3015
    /*
3016
     * Handle old bug that took empty string as zero index.  Also convert
3017
     * string indices to integers if appropriate.
3018
     */
3019
    CHECK_FOR_STRING_INDEX(id);
3020
3021
#if JS_HAS_GETTER_SETTER
3022
    /*
3023
     * If defining a getter or setter, we must check for its counterpart and
3024
     * update the attributes and property ops.  A getter or setter is really
3025
     * only half of a property.
3026
     */
3027
    if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
3028
        JSObject *pobj;
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3029
        JSProperty *prop;
1 by Mike Hommey
Import upstream version 1.8.0.4
3030
3031
        /*
3032
         * If JS_THREADSAFE and id is found, js_LookupProperty returns with
3033
         * sprop non-null and pobj locked.  If pobj == obj, the property is
3034
         * already in obj and obj has its own (mutable) scope.  So if we are
3035
         * defining a getter whose setter was already defined, or vice versa,
3036
         * finish the job via js_ChangeScopePropertyAttributes, and refresh
3037
         * the property cache line for (obj, id) to map sprop.
3038
         */
3039
        if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
3040
            return JS_FALSE;
3041
        sprop = (JSScopeProperty *) prop;
3042
        if (sprop &&
3043
            pobj == obj &&
3044
            (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
3045
            sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop,
3046
                                                attrs, sprop->attrs,
3047
                                                (attrs & JSPROP_GETTER)
3048
                                                ? getter
3049
                                                : sprop->getter,
3050
                                                (attrs & JSPROP_SETTER)
3051
                                                ? setter
3052
                                                : sprop->setter);
3053
3054
            /* NB: obj == pobj, so we can share unlock code at the bottom. */
3055
            if (!sprop)
3056
                goto bad;
3057
            goto out;
3058
        }
3059
3060
        if (prop) {
3061
            /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */
3062
            OBJ_DROP_PROPERTY(cx, pobj, prop);
3063
            prop = NULL;
3064
        }
3065
    }
3066
#endif /* JS_HAS_GETTER_SETTER */
3067
3068
    /* Lock if object locking is required by this implementation. */
3069
    JS_LOCK_OBJ(cx, obj);
3070
3071
    /* Use the object's class getter and setter by default. */
3072
    clasp = LOCKED_OBJ_GET_CLASS(obj);
3073
    if (!getter)
3074
        getter = clasp->getProperty;
3075
    if (!setter)
3076
        setter = clasp->setProperty;
3077
3078
    /* Get obj's own scope if it has one, or create a new one for obj. */
3079
    scope = js_GetMutableScope(cx, obj);
3080
    if (!scope)
3081
        goto bad;
3082
3083
    /* Add the property to scope, or replace an existing one of the same id. */
3084
    if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
3085
        attrs |= JSPROP_SHARED;
3086
    sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
3087
                                SPROP_INVALID_SLOT, attrs, flags, shortid);
3088
    if (!sprop)
3089
        goto bad;
3090
3091
    /* Store value before calling addProperty, in case the latter GC's. */
3092
    if (SPROP_HAS_VALID_SLOT(sprop, scope))
3093
        LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
3094
3095
    /* XXXbe called with lock held */
3096
    ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value,
3097
                        js_RemoveScopeProperty(cx, scope, id);
3098
                        goto bad);
3099
3100
#if JS_HAS_GETTER_SETTER
3101
out:
3102
#endif
3103
    PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
3104
    if (propp)
3105
        *propp = (JSProperty *) sprop;
3106
    else
3107
        JS_UNLOCK_OBJ(cx, obj);
3108
    return JS_TRUE;
3109
3110
bad:
3111
    JS_UNLOCK_OBJ(cx, obj);
3112
    return JS_FALSE;
3113
}
3114
3115
/*
3116
 * Given pc pointing after a property accessing bytecode, return true if the
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3117
 * access is "object-detecting" in the sense used by web scripts, e.g., when
1 by Mike Hommey
Import upstream version 1.8.0.4
3118
 * checking whether document.all is defined.
3119
 */
3120
static JSBool
3121
Detecting(JSContext *cx, jsbytecode *pc)
3122
{
3123
    JSScript *script;
3124
    jsbytecode *endpc;
3125
    JSOp op;
3126
    JSAtom *atom;
3127
3128
    if (!cx->fp)
3129
        return JS_FALSE;
3130
    script = cx->fp->script;
3131
    for (endpc = script->code + script->length; pc < endpc; pc++) {
3132
        /* General case: a branch or equality op follows the access. */
3133
        op = (JSOp) *pc;
3134
        if (js_CodeSpec[op].format & JOF_DETECTING)
3135
            return JS_TRUE;
3136
3137
        /*
3138
         * Special case #1: handle (document.all == null).  Don't sweat about
3139
         * JS1.2's revision of the equality operators here.
3140
         */
3141
        if (op == JSOP_NULL) {
3142
            if (++pc < endpc)
3143
                return *pc == JSOP_EQ || *pc == JSOP_NE;
3144
            break;
3145
        }
3146
3147
        /*
3148
         * Special case #2: handle (document.all == undefined).  Don't worry
3149
         * about someone redefining undefined, which was added by Edition 3,
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3150
         * so is read/write for backward compatibility.
1 by Mike Hommey
Import upstream version 1.8.0.4
3151
         */
3152
        if (op == JSOP_NAME) {
3153
            atom = GET_ATOM(cx, script, pc);
3154
            if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
3155
                (pc += js_CodeSpec[op].length) < endpc) {
3156
                op = (JSOp) *pc;
3157
                return op == JSOP_EQ || op == JSOP_NE ||
3158
                       op == JSOP_NEW_EQ || op == JSOP_NEW_NE;
3159
            }
3160
            break;
3161
        }
3162
3163
        /* At this point, anything but grouping means we're not detecting. */
3164
        if (op != JSOP_GROUP)
3165
            break;
3166
    }
3167
    return JS_FALSE;
3168
}
3169
3170
JS_FRIEND_API(JSBool)
3171
js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
3172
                  JSProperty **propp)
3173
{
3174
    return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp);
3175
}
3176
3177
JSBool
3178
js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
3179
                           JSObject **objp, JSProperty **propp)
3180
{
3181
    JSObject *start, *obj2, *proto;
3182
    JSScope *scope;
3183
    JSScopeProperty *sprop;
3184
    JSClass *clasp;
3185
    JSResolveOp resolve;
3186
    JSResolvingKey key;
3187
    JSResolvingEntry *entry;
3188
    uint32 generation;
3189
    JSNewResolveOp newresolve;
3190
    jsbytecode *pc;
3191
    const JSCodeSpec *cs;
3192
    uint32 format;
3193
    JSBool ok;
3194
3195
    /*
3196
     * Handle old bug that took empty string as zero index.  Also convert
3197
     * string indices to integers if appropriate.
3198
     */
3199
    CHECK_FOR_STRING_INDEX(id);
3200
3201
    /* Search scopes starting with obj and following the prototype link. */
3202
    start = obj;
3203
    for (;;) {
3204
        JS_LOCK_OBJ(cx, obj);
3205
        scope = OBJ_SCOPE(obj);
3206
        if (scope->object == obj) {
3207
            sprop = SCOPE_GET_PROPERTY(scope, id);
3208
        } else {
3209
            /* Shared prototype scope: try resolve before lookup. */
3210
            sprop = NULL;
3211
        }
3212
3213
        /* Try obj's class resolve hook if id was not found in obj's scope. */
3214
        if (!sprop) {
3215
            clasp = LOCKED_OBJ_GET_CLASS(obj);
3216
            resolve = clasp->resolve;
3217
            if (resolve != JS_ResolveStub) {
3218
                /* Avoid recursion on (obj, id) already being resolved on cx. */
3219
                key.obj = obj;
3220
                key.id = id;
3221
3222
                /*
3223
                 * Once we have successfully added an entry for (obj, key) to
3224
                 * cx->resolvingTable, control must go through cleanup: before
3225
                 * returning.  But note that JS_DHASH_ADD may find an existing
3226
                 * entry, in which case we bail to suppress runaway recursion.
3227
                 */
3228
                if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
3229
                    JS_UNLOCK_OBJ(cx, obj);
3230
                    return JS_FALSE;
3231
                }
3232
                if (!entry) {
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3233
                    /* Already resolving id in obj -- suppress recursion. */
1 by Mike Hommey
Import upstream version 1.8.0.4
3234
                    JS_UNLOCK_OBJ(cx, obj);
3235
                    goto out;
3236
                }
3237
                generation = cx->resolvingTable->generation;
3238
3239
                /* Null *propp here so we can test it at cleanup: safely. */
3240
                *propp = NULL;
3241
3242
                if (clasp->flags & JSCLASS_NEW_RESOLVE) {
3243
                    newresolve = (JSNewResolveOp)resolve;
3244
                    if (!(flags & JSRESOLVE_CLASSNAME) &&
3245
                        cx->fp &&
3246
                        (pc = cx->fp->pc)) {
3247
                        cs = &js_CodeSpec[*pc];
3248
                        format = cs->format;
3249
                        if ((format & JOF_MODEMASK) != JOF_NAME)
3250
                            flags |= JSRESOLVE_QUALIFIED;
3251
                        if ((format & JOF_ASSIGNING) ||
3252
                            (cx->fp->flags & JSFRAME_ASSIGNING)) {
3253
                            flags |= JSRESOLVE_ASSIGNING;
3254
                        } else {
3255
                            pc += cs->length;
3256
                            if (Detecting(cx, pc))
3257
                                flags |= JSRESOLVE_DETECTING;
3258
                        }
3259
                        if (format & JOF_DECLARING)
3260
                            flags |= JSRESOLVE_DECLARING;
3261
                    }
3262
                    obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START)
3263
                           ? start
3264
                           : NULL;
3265
                    JS_UNLOCK_OBJ(cx, obj);
3266
3267
                    /* Protect id and all atoms from a GC nested in resolve. */
3268
                    JS_KEEP_ATOMS(cx->runtime);
3269
                    ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2);
3270
                    JS_UNKEEP_ATOMS(cx->runtime);
3271
                    if (!ok)
3272
                        goto cleanup;
3273
3274
                    JS_LOCK_OBJ(cx, obj);
3275
                    if (obj2) {
3276
                        /* Resolved: juggle locks and lookup id again. */
3277
                        if (obj2 != obj) {
3278
                            JS_UNLOCK_OBJ(cx, obj);
3279
                            JS_LOCK_OBJ(cx, obj2);
3280
                        }
3281
                        scope = OBJ_SCOPE(obj2);
3282
                        if (!MAP_IS_NATIVE(&scope->map)) {
3283
                            /* Whoops, newresolve handed back a foreign obj2. */
3284
                            JS_ASSERT(obj2 != obj);
3285
                            JS_UNLOCK_OBJ(cx, obj2);
3286
                            ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp);
3287
                            if (!ok || *propp)
3288
                                goto cleanup;
3289
                            JS_LOCK_OBJ(cx, obj2);
3290
                        } else {
3291
                            /*
3292
                             * Require that obj2 have its own scope now, as we
3293
                             * do for old-style resolve.  If it doesn't, then
3294
                             * id was not truly resolved, and we'll find it in
3295
                             * the proto chain, or miss it if obj2's proto is
3296
                             * not on obj's proto chain.  That last case is a
3297
                             * "too bad!" case.
3298
                             */
3299
                            if (scope->object == obj2)
3300
                                sprop = SCOPE_GET_PROPERTY(scope, id);
3301
                        }
3302
                        if (sprop) {
3303
                            JS_ASSERT(obj2 == scope->object);
3304
                            obj = obj2;
3305
                        } else if (obj2 != obj) {
3306
                            JS_UNLOCK_OBJ(cx, obj2);
3307
                            JS_LOCK_OBJ(cx, obj);
3308
                        }
3309
                    }
3310
                } else {
3311
                    /*
3312
                     * Old resolve always requires id re-lookup if obj owns
3313
                     * its scope after resolve returns.
3314
                     */
3315
                    JS_UNLOCK_OBJ(cx, obj);
3316
                    ok = resolve(cx, obj, ID_TO_VALUE(id));
3317
                    if (!ok)
3318
                        goto cleanup;
3319
                    JS_LOCK_OBJ(cx, obj);
3320
                    scope = OBJ_SCOPE(obj);
3321
                    JS_ASSERT(MAP_IS_NATIVE(&scope->map));
3322
                    if (scope->object == obj)
3323
                        sprop = SCOPE_GET_PROPERTY(scope, id);
3324
                }
3325
3326
            cleanup:
3327
                js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
3328
                if (!ok || *propp)
3329
                    return ok;
3330
            }
3331
        }
3332
3333
        if (sprop) {
3334
            JS_ASSERT(OBJ_SCOPE(obj) == scope);
3335
            *objp = scope->object;      /* XXXbe hide in jsscope.[ch] */
3336
3337
            *propp = (JSProperty *) sprop;
3338
            return JS_TRUE;
3339
        }
3340
3341
        proto = LOCKED_OBJ_GET_PROTO(obj);
3342
        JS_UNLOCK_OBJ(cx, obj);
3343
        if (!proto)
3344
            break;
3345
        if (!OBJ_IS_NATIVE(proto))
3346
            return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
3347
        obj = proto;
3348
    }
3349
3350
out:
3351
    *objp = NULL;
3352
    *propp = NULL;
3353
    return JS_TRUE;
3354
}
3355
3356
JS_FRIEND_API(JSBool)
3357
js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
3358
                JSProperty **propp)
3359
{
3360
    JSRuntime *rt;
3361
    JSObject *obj, *pobj, *lastobj;
3362
    JSScopeProperty *sprop;
3363
    JSProperty *prop;
3364
3365
    rt = cx->runtime;
3366
    obj = cx->fp->scopeChain;
3367
    do {
3368
        /* Try the property cache and return immediately on cache hit. */
3369
        if (OBJ_IS_NATIVE(obj)) {
3370
            JS_LOCK_OBJ(cx, obj);
3371
            PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop);
3372
            if (sprop) {
3373
                JS_ASSERT(OBJ_IS_NATIVE(obj));
3374
                *objp = obj;
3375
                *pobjp = obj;
3376
                *propp = (JSProperty *) sprop;
3377
                return JS_TRUE;
3378
            }
3379
            JS_UNLOCK_OBJ(cx, obj);
3380
        }
3381
3382
        /* If cache miss, take the slow path. */
3383
        if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
3384
            return JS_FALSE;
3385
        if (prop) {
3386
            if (OBJ_IS_NATIVE(pobj)) {
3387
                sprop = (JSScopeProperty *) prop;
3388
                PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop);
3389
            }
3390
            *objp = obj;
3391
            *pobjp = pobj;
3392
            *propp = prop;
3393
            return JS_TRUE;
3394
        }
3395
        lastobj = obj;
3396
    } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
3397
3398
    *objp = lastobj;
3399
    *pobjp = NULL;
3400
    *propp = NULL;
3401
    return JS_TRUE;
3402
}
3403
3404
JSObject *
3405
js_FindIdentifierBase(JSContext *cx, jsid id)
3406
{
3407
    JSObject *obj, *pobj;
3408
    JSProperty *prop;
3409
3410
    /*
3411
     * Look for id's property along the "with" statement chain and the
3412
     * statically-linked scope chain.
3413
     */
3414
    if (!js_FindProperty(cx, id, &obj, &pobj, &prop))
3415
        return NULL;
3416
    if (prop) {
3417
        OBJ_DROP_PROPERTY(cx, pobj, prop);
3418
        return obj;
3419
    }
3420
3421
    /*
3422
     * Use the top-level scope from the scope chain, which won't end in the
3423
     * same scope as cx->globalObject for cross-context function calls.
3424
     */
3425
    JS_ASSERT(obj);
3426
3427
    /*
3428
     * Property not found.  Give a strict warning if binding an undeclared
3429
     * top-level variable.
3430
     */
3431
    if (JS_HAS_STRICT_OPTION(cx)) {
3432
        JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id));
3433
        if (!JS_ReportErrorFlagsAndNumber(cx,
3434
                                          JSREPORT_WARNING | JSREPORT_STRICT,
3435
                                          js_GetErrorMessage, NULL,
3436
                                          JSMSG_UNDECLARED_VAR,
3437
                                          JS_GetStringBytes(str))) {
3438
            return NULL;
3439
        }
3440
    }
3441
    return obj;
3442
}
3443
3444
JSBool
1.1.5 by Michael Bienia
Import upstream version 1.8.0.10
3445
js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
3446
             JSScopeProperty *sprop, jsval *vp)
3447
{
3448
    JSScope *scope;
3449
    uint32 slot;
3450
    int32 sample;
3451
    JSTempValueRooter tvr;
3452
    JSBool ok;
3453
3454
    JS_ASSERT(OBJ_IS_NATIVE(pobj));
3455
    JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj));
3456
    scope = OBJ_SCOPE(pobj);
3457
    JS_ASSERT(scope->object == pobj);
3458
3459
    slot = sprop->slot;
3460
    *vp = (slot != SPROP_INVALID_SLOT)
3461
          ? LOCKED_OBJ_GET_SLOT(pobj, slot)
3462
          : JSVAL_VOID;
3463
    if (SPROP_HAS_STUB_GETTER(sprop))
3464
        return JS_TRUE;
3465
3466
    sample = cx->runtime->propertyRemovals;
3467
    JS_UNLOCK_SCOPE(cx, scope);
3468
    JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
3469
    ok = SPROP_GET(cx, sprop, obj, pobj, vp);
3470
    JS_POP_TEMP_ROOT(cx, &tvr);
3471
    if (!ok)
3472
        return JS_FALSE;
3473
3474
    JS_LOCK_SCOPE(cx, scope);
3475
    JS_ASSERT(scope->object == pobj);
3476
    if (SLOT_IN_SCOPE(slot, scope) &&
3477
        (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
3478
         SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) {
3479
        LOCKED_OBJ_SET_SLOT(pobj, slot, *vp);
3480
    }
3481
3482
    return JS_TRUE;
3483
}
3484
3485
JSBool
3486
js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp)
3487
{
3488
    JSScope *scope;
3489
    uint32 slot;
3490
    jsval pval;
3491
    int32 sample;
3492
    JSTempValueRooter tvr;
3493
    JSBool ok;
3494
3495
    JS_ASSERT(OBJ_IS_NATIVE(obj));
3496
    JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
3497
    scope = OBJ_SCOPE(obj);
3498
    JS_ASSERT(scope->object == obj);
3499
3500
    slot = sprop->slot;
3501
    if (slot != SPROP_INVALID_SLOT) {
3502
        pval = LOCKED_OBJ_GET_SLOT(obj, slot);
3503
3504
        /* If sprop has a stub setter, keep scope locked and just store *vp. */
3505
        if (SPROP_HAS_STUB_SETTER(sprop))
3506
            goto set_slot;
3507
    } else {
3508
        /*
3509
         * Allow API consumers to create shared properties with stub setters.
3510
         * Such properties lack value storage, so setting them is like writing
3511
         * to /dev/null.
3512
         */
3513
        if (SPROP_HAS_STUB_SETTER(sprop))
3514
            return JS_TRUE;
3515
        pval = JSVAL_VOID;
3516
    }
3517
3518
    sample = cx->runtime->propertyRemovals;
3519
    JS_UNLOCK_SCOPE(cx, scope);
3520
    JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
3521
    ok = SPROP_SET(cx, sprop, obj, obj, vp);
3522
    JS_POP_TEMP_ROOT(cx, &tvr);
3523
    if (!ok)
3524
        return JS_FALSE;
3525
3526
    JS_LOCK_SCOPE(cx, scope);
3527
    JS_ASSERT(scope->object == obj);
3528
    if (SLOT_IN_SCOPE(slot, scope) &&
3529
        (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
3530
         SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) {
3531
  set_slot:
3532
        GC_POKE(cx, pval);
3533
        LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
3534
    }
3535
3536
    return JS_TRUE;
3537
}
3538
3539
JSBool
1 by Mike Hommey
Import upstream version 1.8.0.4
3540
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3541
{
3542
    JSObject *obj2;
3543
    JSProperty *prop;
3544
    JSScopeProperty *sprop;
3545
3546
    /*
3547
     * Handle old bug that took empty string as zero index.  Also convert
3548
     * string indices to integers if appropriate.
3549
     */
3550
    CHECK_FOR_STRING_INDEX(id);
3551
3552
    if (!js_LookupProperty(cx, obj, id, &obj2, &prop))
3553
        return JS_FALSE;
3554
    if (!prop) {
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3555
        jsbytecode *pc;
1 by Mike Hommey
Import upstream version 1.8.0.4
3556
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3557
        *vp = JSVAL_VOID;
1 by Mike Hommey
Import upstream version 1.8.0.4
3558
3559
        if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp))
3560
            return JS_FALSE;
3561
3562
        /*
3563
         * Give a strict warning if foo.bar is evaluated by a script for an
3564
         * object foo with no property named 'bar'.
3565
         */
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3566
        if (JSVAL_IS_VOID(*vp) && cx->fp && (pc = cx->fp->pc)) {
3567
            JSOp op;
3568
            uintN flags;
1 by Mike Hommey
Import upstream version 1.8.0.4
3569
            JSString *str;
3570
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3571
            op = *pc;
3572
            if (op == JSOP_GETXPROP || op == JSOP_GETXELEM) {
3573
                flags = JSREPORT_ERROR;
3574
            } else {
3575
                if (!JS_HAS_STRICT_OPTION(cx) ||
3576
                    (op != JSOP_GETPROP && op != JSOP_GETELEM)) {
3577
                    return JS_TRUE;
3578
                }
3579
3580
                /*
3581
                 * XXX do not warn about missing __iterator__ as the function
3582
                 * may be called from JS_GetMethodById. See bug 355145.
3583
                 */
3584
                if (id == ATOM_TO_JSID(cx->runtime->atomState.iteratorAtom))
3585
                    return JS_TRUE;
3586
3587
                /* Kludge to allow (typeof foo == "undefined") tests. */
3588
                JS_ASSERT(cx->fp->script);
3589
                pc += js_CodeSpec[op].length;
3590
                if (Detecting(cx, pc))
3591
                    return JS_TRUE;
3592
3593
                flags = JSREPORT_WARNING | JSREPORT_STRICT;
3594
            }
1 by Mike Hommey
Import upstream version 1.8.0.4
3595
3596
            /* Ok, bad undefined property reference: whine about it. */
3597
            str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
3598
                                             ID_TO_VALUE(id), NULL);
3599
            if (!str ||
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3600
                !JS_ReportErrorFlagsAndNumber(cx, flags,
1 by Mike Hommey
Import upstream version 1.8.0.4
3601
                                              js_GetErrorMessage, NULL,
3602
                                              JSMSG_UNDEFINED_PROP,
3603
                                              JS_GetStringBytes(str))) {
3604
                return JS_FALSE;
3605
            }
3606
        }
3607
        return JS_TRUE;
3608
    }
3609
3610
    if (!OBJ_IS_NATIVE(obj2)) {
3611
        OBJ_DROP_PROPERTY(cx, obj2, prop);
3612
        return OBJ_GET_PROPERTY(cx, obj2, id, vp);
3613
    }
3614
3615
    sprop = (JSScopeProperty *) prop;
1.1.5 by Michael Bienia
Import upstream version 1.8.0.10
3616
    if (!js_NativeGet(cx, obj, obj2, sprop, vp))
1 by Mike Hommey
Import upstream version 1.8.0.4
3617
        return JS_FALSE;
1.1.5 by Michael Bienia
Import upstream version 1.8.0.10
3618
3619
    PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop);
3620
    JS_UNLOCK_OBJ(cx, obj2);
1 by Mike Hommey
Import upstream version 1.8.0.4
3621
    return JS_TRUE;
3622
}
3623
3624
JSBool
3625
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
3626
{
3627
    JSObject *pobj;
3628
    JSProperty *prop;
3629
    JSScopeProperty *sprop;
3630
    JSScope *scope;
3631
    uintN attrs, flags;
3632
    intN shortid;
3633
    JSClass *clasp;
3634
    JSPropertyOp getter, setter;
3635
3636
    /*
3637
     * Handle old bug that took empty string as zero index.  Also convert
3638
     * string indices to integers if appropriate.
3639
     */
3640
    CHECK_FOR_STRING_INDEX(id);
3641
3642
    if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
3643
        return JS_FALSE;
3644
3645
    if (prop && !OBJ_IS_NATIVE(pobj)) {
3646
        OBJ_DROP_PROPERTY(cx, pobj, prop);
3647
        prop = NULL;
3648
    }
3649
    sprop = (JSScopeProperty *) prop;
3650
3651
    /*
3652
     * Now either sprop is null, meaning id was not found in obj or one of its
3653
     * prototypes; or sprop is non-null, meaning id was found in pobj's scope.
3654
     * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop
3655
     * is held: we must OBJ_DROP_PROPERTY or JS_UNLOCK_SCOPE before we return
3656
     * (the two are equivalent for native objects, but we use JS_UNLOCK_SCOPE
3657
     * because it is cheaper).
3658
     */
3659
    attrs = JSPROP_ENUMERATE;
3660
    flags = 0;
3661
    shortid = 0;
3662
    clasp = OBJ_GET_CLASS(cx, obj);
3663
    getter = clasp->getProperty;
3664
    setter = clasp->setProperty;
3665
3666
    if (sprop) {
3667
        /*
3668
         * Set scope for use below.  It was locked by js_LookupProperty, and
3669
         * we know pobj owns it (i.e., scope->object == pobj).  Therefore we
3670
         * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope).
3671
         */
3672
        scope = OBJ_SCOPE(pobj);
3673
3674
        attrs = sprop->attrs;
3675
        if ((attrs & JSPROP_READONLY) ||
3676
            (SCOPE_IS_SEALED(scope) && pobj == obj)) {
3677
            JS_UNLOCK_SCOPE(cx, scope);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3678
3679
            /*
3680
             * Here, we'll either return true or goto read_only_error, which
3681
             * reports a strict warning or throws an error.  So we redefine
3682
             * the |flags| local variable to be JSREPORT_* flags to pass to
3683
             * JS_ReportErrorFlagsAndNumberUC at label read_only_error.  We
3684
             * must likewise re-task flags further below for the other 'goto
3685
             * read_only_error;' case.
3686
             */
3687
            flags = JSREPORT_ERROR;
3688
            if ((attrs & JSPROP_READONLY) && JS_VERSION_IS_ECMA(cx)) {
3689
                if (!JS_HAS_STRICT_OPTION(cx)) {
3690
                    /* Just return true per ECMA if not in strict mode. */
3691
                    return JS_TRUE;
3692
                }
3693
3694
                /* Strict mode: report a read-only strict warning. */
3695
                flags = JSREPORT_STRICT | JSREPORT_WARNING;
3696
            }
1 by Mike Hommey
Import upstream version 1.8.0.4
3697
            goto read_only_error;
3698
        }
3699
3700
        if (pobj != obj) {
3701
            /*
3702
             * We found id in a prototype object: prepare to share or shadow.
3703
             * NB: Thanks to the immutable, garbage-collected property tree
3704
             * maintained by jsscope.c in cx->runtime, we needn't worry about
3705
             * sprop going away behind our back after we've unlocked scope.
3706
             */
3707
            JS_UNLOCK_SCOPE(cx, scope);
3708
3709
            /* Don't clone a shared prototype property. */
1.1.5 by Michael Bienia
Import upstream version 1.8.0.10
3710
            if (attrs & JSPROP_SHARED) {
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3711
                if (SPROP_HAS_STUB_SETTER(sprop) &&
3712
                    !(sprop->attrs & JSPROP_GETTER)) {
3713
                    return JS_TRUE;
3714
                }
1 by Mike Hommey
Import upstream version 1.8.0.4
3715
                return SPROP_SET(cx, sprop, obj, pobj, vp);
1.1.5 by Michael Bienia
Import upstream version 1.8.0.10
3716
            }
1 by Mike Hommey
Import upstream version 1.8.0.4
3717
3718
            /* Restore attrs to the ECMA default for new properties. */
3719
            attrs = JSPROP_ENUMERATE;
3720
3721
            /*
3722
             * Preserve the shortid, getter, and setter when shadowing any
3723
             * property that has a shortid.  An old API convention requires
3724
             * that the property's getter and setter functions receive the
3725
             * shortid, not id, when they are called on the shadow we are
3726
             * about to create in obj's scope.
3727
             */
3728
            if (sprop->flags & SPROP_HAS_SHORTID) {
3729
                flags = SPROP_HAS_SHORTID;
3730
                shortid = sprop->shortid;
3731
                getter = sprop->getter;
3732
                setter = sprop->setter;
3733
            }
3734
3735
            /*
3736
             * Forget we found the proto-property now that we've copied any
3737
             * needed member values.
3738
             */
3739
            sprop = NULL;
3740
        }
3741
#ifdef __GNUC__ /* suppress bogus gcc warnings */
3742
    } else {
3743
        scope = NULL;
3744
#endif
3745
    }
3746
3747
    if (!sprop) {
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3748
        if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj) {
3749
            flags = JSREPORT_ERROR;
1 by Mike Hommey
Import upstream version 1.8.0.4
3750
            goto read_only_error;
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3751
        }
1 by Mike Hommey
Import upstream version 1.8.0.4
3752
3753
        /* Find or make a property descriptor with the right heritage. */
3754
        JS_LOCK_OBJ(cx, obj);
3755
        scope = js_GetMutableScope(cx, obj);
3756
        if (!scope) {
3757
            JS_UNLOCK_OBJ(cx, obj);
3758
            return JS_FALSE;
3759
        }
3760
        if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
3761
            attrs |= JSPROP_SHARED;
3762
        sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
3763
                                    SPROP_INVALID_SLOT, attrs, flags, shortid);
3764
        if (!sprop) {
3765
            JS_UNLOCK_SCOPE(cx, scope);
3766
            return JS_FALSE;
3767
        }
3768
3769
        /*
3770
         * Initialize the new property value (passed to setter) to undefined.
3771
         * Note that we store before calling addProperty, to match the order
3772
         * in js_DefineNativeProperty.
3773
         */
3774
        if (SPROP_HAS_VALID_SLOT(sprop, scope))
3775
            LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);
3776
3777
        /* XXXbe called with obj locked */
3778
        ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, vp,
3779
                            js_RemoveScopeProperty(cx, scope, id);
3780
                            JS_UNLOCK_SCOPE(cx, scope);
3781
                            return JS_FALSE);
3782
3783
        PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
3784
    }
3785
1.1.5 by Michael Bienia
Import upstream version 1.8.0.10
3786
    if (!js_NativeSet(cx, obj, sprop, vp))
1 by Mike Hommey
Import upstream version 1.8.0.4
3787
        return JS_FALSE;
3788
    JS_UNLOCK_SCOPE(cx, scope);
3789
    return JS_TRUE;
3790
3791
  read_only_error: {
3792
    JSString *str = js_DecompileValueGenerator(cx,
3793
                                               JSDVG_IGNORE_STACK,
3794
                                               ID_TO_VALUE(id),
3795
                                               NULL);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3796
    if (!str)
3797
        return JS_FALSE;
3798
    return JS_ReportErrorFlagsAndNumberUC(cx, flags, js_GetErrorMessage,
3799
                                          NULL, JSMSG_READ_ONLY,
3800
                                          JS_GetStringChars(str));
1 by Mike Hommey
Import upstream version 1.8.0.4
3801
  }
3802
}
3803
3804
JSBool
3805
js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
3806
                 uintN *attrsp)
3807
{
3808
    JSBool noprop, ok;
3809
    JSScopeProperty *sprop;
3810
3811
    noprop = !prop;
3812
    if (noprop) {
3813
        if (!js_LookupProperty(cx, obj, id, &obj, &prop))
3814
            return JS_FALSE;
3815
        if (!prop) {
3816
            *attrsp = 0;
3817
            return JS_TRUE;
3818
        }
3819
        if (!OBJ_IS_NATIVE(obj)) {
3820
            ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp);
3821
            OBJ_DROP_PROPERTY(cx, obj, prop);
3822
            return ok;
3823
        }
3824
    }
3825
    sprop = (JSScopeProperty *)prop;
3826
    *attrsp = sprop->attrs;
3827
    if (noprop)
3828
        OBJ_DROP_PROPERTY(cx, obj, prop);
3829
    return JS_TRUE;
3830
}
3831
3832
JSBool
3833
js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
3834
                 uintN *attrsp)
3835
{
3836
    JSBool noprop, ok;
3837
    JSScopeProperty *sprop;
3838
3839
    noprop = !prop;
3840
    if (noprop) {
3841
        if (!js_LookupProperty(cx, obj, id, &obj, &prop))
3842
            return JS_FALSE;
3843
        if (!prop)
3844
            return JS_TRUE;
3845
        if (!OBJ_IS_NATIVE(obj)) {
3846
            ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp);
3847
            OBJ_DROP_PROPERTY(cx, obj, prop);
3848
            return ok;
3849
        }
3850
    }
3851
    sprop = (JSScopeProperty *)prop;
1.1.3 by Mike Hommey
Import upstream version 1.8.0.8
3852
    sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, *attrsp, 0,
1 by Mike Hommey
Import upstream version 1.8.0.4
3853
                                         sprop->getter, sprop->setter);
3854
    if (noprop)
3855
        OBJ_DROP_PROPERTY(cx, obj, prop);
3856
    return (sprop != NULL);
3857
}
3858
3859
JSBool
3860
js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
3861
{
3862
    JSObject *proto;
3863
    JSProperty *prop;
3864
    JSScopeProperty *sprop;
3865
    JSString *str;
3866
    JSScope *scope;
3867
    JSBool ok;
3868
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3869
    *rval = JSVAL_TRUE;
1 by Mike Hommey
Import upstream version 1.8.0.4
3870
3871
    /*
3872
     * Handle old bug that took empty string as zero index.  Also convert
3873
     * string indices to integers if appropriate.
3874
     */
3875
    CHECK_FOR_STRING_INDEX(id);
3876
3877
    if (!js_LookupProperty(cx, obj, id, &proto, &prop))
3878
        return JS_FALSE;
3879
    if (!prop || proto != obj) {
3880
        /*
3881
         * If the property was found in a native prototype, check whether it's
3882
         * shared and permanent.  Such a property stands for direct properties
3883
         * in all delegating objects, matching ECMA semantics without bloating
3884
         * each delegating object.
3885
         */
3886
        if (prop) {
3887
            if (OBJ_IS_NATIVE(proto)) {
3888
                sprop = (JSScopeProperty *)prop;
3889
                if (SPROP_IS_SHARED_PERMANENT(sprop))
3890
                    *rval = JSVAL_FALSE;
3891
            }
3892
            OBJ_DROP_PROPERTY(cx, proto, prop);
3893
            if (*rval == JSVAL_FALSE)
3894
                return JS_TRUE;
3895
        }
3896
3897
        /*
3898
         * If no property, or the property comes unshared or impermanent from
3899
         * a prototype, call the class's delProperty hook, passing rval as the
3900
         * result parameter.
3901
         */
3902
        return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id),
3903
                                                   rval);
3904
    }
3905
3906
    sprop = (JSScopeProperty *)prop;
3907
    if (sprop->attrs & JSPROP_PERMANENT) {
3908
        OBJ_DROP_PROPERTY(cx, obj, prop);
3909
        if (JS_VERSION_IS_ECMA(cx)) {
3910
            *rval = JSVAL_FALSE;
3911
            return JS_TRUE;
3912
        }
3913
        str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
3914
                                         ID_TO_VALUE(id), NULL);
3915
        if (str) {
3916
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3917
                                 JSMSG_PERMANENT, JS_GetStringBytes(str));
3918
        }
3919
        return JS_FALSE;
3920
    }
3921
3922
    /* XXXbe called with obj locked */
3923
    if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop),
3924
                                                rval)) {
3925
        OBJ_DROP_PROPERTY(cx, obj, prop);
3926
        return JS_FALSE;
3927
    }
3928
3929
    scope = OBJ_SCOPE(obj);
3930
    if (SPROP_HAS_VALID_SLOT(sprop, scope))
3931
        GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));
3932
3933
    PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL);
3934
    ok = js_RemoveScopeProperty(cx, scope, id);
3935
    OBJ_DROP_PROPERTY(cx, obj, prop);
3936
    return ok;
3937
}
3938
3939
JSBool
3940
js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
3941
{
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3942
    jsval v, save;
1 by Mike Hommey
Import upstream version 1.8.0.4
3943
    JSString *str;
3944
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3945
    v = save = OBJECT_TO_JSVAL(obj);
1 by Mike Hommey
Import upstream version 1.8.0.4
3946
    switch (hint) {
3947
      case JSTYPE_STRING:
3948
        /*
3949
         * Propagate the exception if js_TryMethod finds an appropriate
3950
         * method, and calling that method returned failure.
3951
         */
3952
        if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL,
3953
                          &v)) {
3954
            return JS_FALSE;
3955
        }
3956
3957
        if (!JSVAL_IS_PRIMITIVE(v)) {
3958
            if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
3959
                return JS_FALSE;
3960
        }
3961
        break;
3962
3963
      default:
3964
        if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
3965
            return JS_FALSE;
3966
        if (!JSVAL_IS_PRIMITIVE(v)) {
3967
            JSType type = JS_TypeOfValue(cx, v);
3968
            if (type == hint ||
3969
                (type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) {
3970
                goto out;
3971
            }
3972
            if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0,
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3973
                              NULL, &v)) {
1 by Mike Hommey
Import upstream version 1.8.0.4
3974
                return JS_FALSE;
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3975
            }
1 by Mike Hommey
Import upstream version 1.8.0.4
3976
        }
3977
        break;
3978
    }
3979
    if (!JSVAL_IS_PRIMITIVE(v)) {
3980
        /* Avoid recursive death through js_DecompileValueGenerator. */
3981
        if (hint == JSTYPE_STRING) {
3982
            str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name);
3983
            if (!str)
3984
                return JS_FALSE;
3985
        } else {
3986
            str = NULL;
3987
        }
3988
        *vp = OBJECT_TO_JSVAL(obj);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3989
        str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, save, str);
1 by Mike Hommey
Import upstream version 1.8.0.4
3990
        if (str) {
3991
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3992
                                 JSMSG_CANT_CONVERT_TO,
3993
                                 JS_GetStringBytes(str),
3994
                                 (hint == JSTYPE_VOID)
3995
                                 ? "primitive type"
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
3996
                                 : js_type_strs[hint]);
1 by Mike Hommey
Import upstream version 1.8.0.4
3997
        }
3998
        return JS_FALSE;
3999
    }
4000
out:
4001
    *vp = v;
4002
    return JS_TRUE;
4003
}
4004
4005
JSIdArray *
4006
js_NewIdArray(JSContext *cx, jsint length)
4007
{
4008
    JSIdArray *ida;
4009
4010
    ida = (JSIdArray *)
4011
          JS_malloc(cx, sizeof(JSIdArray) + (length-1) * sizeof(jsval));
4012
    if (ida)
4013
        ida->length = length;
4014
    return ida;
4015
}
4016
4017
JSIdArray *
4018
js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length)
4019
{
4020
    JSIdArray *rida;
4021
4022
    rida = (JSIdArray *)
4023
           JS_realloc(cx, ida, sizeof(JSIdArray) + (length-1) * sizeof(jsval));
4024
    if (!rida)
4025
        JS_DestroyIdArray(cx, ida);
4026
    else
4027
        rida->length = length;
4028
    return rida;
4029
}
4030
4031
/* Private type used to iterate over all properties of a native JS object */
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
4032
struct JSNativeIteratorState {
4033
    jsint                   next_index; /* index into jsid array */
4034
    JSIdArray               *ida;       /* all property ids in enumeration */
4035
    JSNativeIteratorState   *next;      /* double-linked list support */
4036
    JSNativeIteratorState   **prevp;
4037
};
1 by Mike Hommey
Import upstream version 1.8.0.4
4038
4039
/*
4040
 * This function is used to enumerate the properties of native JSObjects
4041
 * and those host objects that do not define a JSNewEnumerateOp-style iterator
4042
 * function.
4043
 */
4044
JSBool
4045
js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
4046
             jsval *statep, jsid *idp)
4047
{
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
4048
    JSRuntime *rt;
1 by Mike Hommey
Import upstream version 1.8.0.4
4049
    JSObject *proto;
4050
    JSClass *clasp;
4051
    JSEnumerateOp enumerate;
4052
    JSScopeProperty *sprop, *lastProp;
4053
    jsint i, length;
4054
    JSScope *scope;
4055
    JSIdArray *ida;
4056
    JSNativeIteratorState *state;
4057
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
4058
    rt = cx->runtime;
1 by Mike Hommey
Import upstream version 1.8.0.4
4059
    clasp = OBJ_GET_CLASS(cx, obj);
4060
    enumerate = clasp->enumerate;
4061
    if (clasp->flags & JSCLASS_NEW_ENUMERATE)
4062
        return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
4063
4064
    switch (enum_op) {
4065
      case JSENUMERATE_INIT:
4066
        if (!enumerate(cx, obj))
4067
            return JS_FALSE;
4068
        length = 0;
4069
4070
        /*
4071
         * The set of all property ids is pre-computed when the iterator
4072
         * is initialized so as to avoid problems with properties being
4073
         * deleted during the iteration.
4074
         */
4075
        JS_LOCK_OBJ(cx, obj);
4076
        scope = OBJ_SCOPE(obj);
4077
4078
        /*
4079
         * If this object shares a scope with its prototype, don't enumerate
4080
         * its properties.  Otherwise they will be enumerated a second time
4081
         * when the prototype object is enumerated.
4082
         */
4083
        proto = OBJ_GET_PROTO(cx, obj);
4084
        if (proto && scope == OBJ_SCOPE(proto)) {
4085
            ida = js_NewIdArray(cx, 0);
4086
            if (!ida) {
4087
                JS_UNLOCK_OBJ(cx, obj);
4088
                return JS_FALSE;
4089
            }
4090
        } else {
4091
            /* Object has a private scope; Enumerate all props in scope. */
4092
            for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop;
4093
                 sprop = sprop->parent) {
4094
                if ((
4095
#ifdef DUMP_CALL_TABLE
4096
                     (cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
4097
#endif
4098
                     (sprop->attrs & JSPROP_ENUMERATE)) &&
4099
                    !(sprop->flags & SPROP_IS_ALIAS) &&
4100
                    (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
4101
                     SCOPE_HAS_PROPERTY(scope, sprop))) {
4102
                    length++;
4103
                }
4104
            }
4105
            ida = js_NewIdArray(cx, length);
4106
            if (!ida) {
4107
                JS_UNLOCK_OBJ(cx, obj);
4108
                return JS_FALSE;
4109
            }
4110
            i = length;
4111
            for (sprop = lastProp; sprop; sprop = sprop->parent) {
4112
                if ((
4113
#ifdef DUMP_CALL_TABLE
4114
                     (cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
4115
#endif
4116
                     (sprop->attrs & JSPROP_ENUMERATE)) &&
4117
                    !(sprop->flags & SPROP_IS_ALIAS) &&
4118
                    (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
4119
                     SCOPE_HAS_PROPERTY(scope, sprop))) {
4120
                    JS_ASSERT(i > 0);
4121
                    ida->vector[--i] = sprop->id;
4122
                }
4123
            }
4124
        }
4125
        JS_UNLOCK_OBJ(cx, obj);
4126
4127
        state = (JSNativeIteratorState *)
4128
            JS_malloc(cx, sizeof(JSNativeIteratorState));
4129
        if (!state) {
4130
            JS_DestroyIdArray(cx, ida);
4131
            return JS_FALSE;
4132
        }
4133
        state->ida = ida;
4134
        state->next_index = 0;
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
4135
4136
        JS_LOCK_RUNTIME(rt);
4137
        state->next = rt->nativeIteratorStates;
4138
        if (state->next)
4139
            state->next->prevp = &state->next;
4140
        state->prevp = &rt->nativeIteratorStates;
4141
        *state->prevp = state;
4142
        JS_UNLOCK_RUNTIME(rt);
4143
1 by Mike Hommey
Import upstream version 1.8.0.4
4144
        *statep = PRIVATE_TO_JSVAL(state);
4145
        if (idp)
4146
            *idp = INT_TO_JSVAL(length);
4147
        break;
4148
4149
      case JSENUMERATE_NEXT:
4150
        state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
4151
        ida = state->ida;
4152
        length = ida->length;
4153
        if (state->next_index != length) {
4154
            *idp = ida->vector[state->next_index++];
4155
            break;
4156
        }
4157
        /* FALL THROUGH */
4158
4159
      case JSENUMERATE_DESTROY:
4160
        state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
4161
4162
        JS_LOCK_RUNTIME(rt);
4163
        JS_ASSERT(rt->nativeIteratorStates);
4164
        JS_ASSERT(*state->prevp == state);
4165
        if (state->next) {
4166
            JS_ASSERT(state->next->prevp == &state->next);
4167
            state->next->prevp = state->prevp;
4168
        }
4169
        *state->prevp = state->next;
4170
        JS_UNLOCK_RUNTIME(rt);
4171
1 by Mike Hommey
Import upstream version 1.8.0.4
4172
        JS_DestroyIdArray(cx, state->ida);
4173
        JS_free(cx, state);
4174
        *statep = JSVAL_NULL;
4175
        break;
4176
    }
4177
    return JS_TRUE;
4178
}
4179
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
4180
void
4181
js_MarkNativeIteratorStates(JSContext *cx)
4182
{
4183
    JSNativeIteratorState *state;
4184
    jsid *cursor, *end, id;
4185
4186
    state = cx->runtime->nativeIteratorStates;
4187
    if (!state)
4188
        return;
4189
4190
    do {
4191
        JS_ASSERT(*state->prevp == state);
4192
        cursor = state->ida->vector;
4193
        end = cursor + state->ida->length;
4194
        for (; cursor != end; ++cursor) {
4195
            id = *cursor;
1.1.5 by Michael Bienia
Import upstream version 1.8.0.10
4196
            MARK_ID(cx, id);
1.1.1 by Matthias Klose
Import upstream version 1.8.0.5
4197
        }
4198
    } while ((state = state->next) != NULL);
4199
}
4200
1 by Mike Hommey
Import upstream version 1.8.0.4
4201
JSBool
4202
js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
4203
               jsval *vp, uintN *attrsp)
4204
{
4205
    JSBool writing;
4206
    JSObject *pobj;
4207
    JSProperty *prop;
4208
    JSClass *clasp;
4209
    JSScopeProperty *sprop;
4210
    JSCheckAccessOp check;
4211
4212
    writing = (mode & JSACC_WRITE) != 0;
4213
    switch (mode & JSACC_TYPEMASK) {
4214
      case JSACC_PROTO:
4215
        pobj = obj;
4216
        if (!writing)
4217
            *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO);
4218
        *attrsp = JSPROP_PERMANENT;
4219
        break;
4220
4221
      case JSACC_PARENT:
4222
        JS_ASSERT(!writing);
4223
        pobj = obj;
4224
        *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT);
4225
        *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
4226
        break;
4227
4228
      default:
4229
        if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
4230
            return JS_FALSE;
4231
        if (!prop) {
4232
            if (!writing)
4233
                *vp = JSVAL_VOID;
4234
            *attrsp = 0;
4235
            clasp = OBJ_GET_CLASS(cx, obj);
4236
            return !clasp->checkAccess ||
4237
                   clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
4238
        }
4239
        if (!OBJ_IS_NATIVE(pobj)) {
4240
            OBJ_DROP_PROPERTY(cx, pobj, prop);
4241
            return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp);
4242
        }
4243
4244
        sprop = (JSScopeProperty *)prop;
4245
        *attrsp = sprop->attrs;
4246
        if (!writing) {
4247
            *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)))
4248
                  ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
4249
                  : JSVAL_VOID;
4250
        }
4251
        OBJ_DROP_PROPERTY(cx, pobj, prop);
4252
    }
4253
4254
    /*
4255
     * If obj's class has a stub (null) checkAccess hook, use the per-runtime
4256
     * checkObjectAccess callback, if configured.
4257
     *
4258
     * We don't want to require all classes to supply a checkAccess hook; we
4259
     * need that hook only for certain classes used when precompiling scripts
4260
     * and functions ("brutal sharing").  But for general safety of built-in
4261
     * magic properties such as __proto__ and __parent__, we route all access
4262
     * checks, even for classes that stub out checkAccess, through the global
4263
     * checkObjectAccess hook.  This covers precompilation-based sharing and
4264
     * (possibly unintended) runtime sharing across trust boundaries.
4265
     */
4266
    clasp = OBJ_GET_CLASS(cx, pobj);
4267
    check = clasp->checkAccess;
4268
    if (!check)
4269
        check = cx->runtime->checkObjectAccess;
4270
    return !check || check(cx, pobj, ID_TO_VALUE(id), mode, vp);
4271
}
4272
4273
#ifdef JS_THREADSAFE
4274
void
4275
js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
4276
{
4277
    JS_UNLOCK_OBJ(cx, obj);
4278
}
4279
#endif
4280
4281
static void
4282
ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
4283
{
4284
    /*
4285
     * The decompiler may need to access the args of the function in
4286
     * progress rather than the one we had hoped to call.
4287
     * So we switch the cx->fp to the frame below us. We stick the
4288
     * current frame in the dormantFrameChain to protect it from gc.
4289
     */
4290
4291
    JSStackFrame *fp = cx->fp;
4292
    if (fp->down) {
4293
        JS_ASSERT(!fp->dormantNext);
4294
        fp->dormantNext = cx->dormantFrameChain;
4295
        cx->dormantFrameChain = fp;
4296
        cx->fp = fp->down;
4297
    }
4298
4299
    js_ReportIsNotFunction(cx, vp, flags);
4300
4301
    if (fp->down) {
4302
        JS_ASSERT(cx->dormantFrameChain == fp);
4303
        cx->dormantFrameChain = fp->dormantNext;
4304
        fp->dormantNext = NULL;
4305
        cx->fp = fp;
4306
    }
4307
}
4308
4309
#ifdef NARCISSUS
4310
static JSBool
4311
GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval)
4312
{
4313
    JSObject *tmp;
4314
    jsval xcval;
4315
4316
    while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
4317
        obj = tmp;
4318
    if (!OBJ_GET_PROPERTY(cx, obj,
4319
                          ATOM_TO_JSID(cx->runtime->atomState
4320
                                       .ExecutionContextAtom),
4321
                          &xcval)) {
4322
        return JS_FALSE;
4323
    }
4324
    if (JSVAL_IS_PRIMITIVE(xcval)) {
4325
        JS_ReportError(cx, "invalid ExecutionContext in global object");
4326
        return JS_FALSE;
4327
    }
4328
    if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(xcval),
4329
                          ATOM_TO_JSID(cx->runtime->atomState.currentAtom),
4330
                          rval)) {
4331
        return JS_FALSE;
4332
    }
4333
    return JS_TRUE;
4334
}
4335
#endif
4336
4337
JSBool
4338
js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
4339
{
4340
    JSClass *clasp;
4341
4342
    clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
4343
    if (!clasp->call) {
4344
#ifdef NARCISSUS
4345
        JSObject *callee, *args;
4346
        jsval fval, nargv[3];
4347
        JSBool ok;
4348
4349
        callee = JSVAL_TO_OBJECT(argv[-2]);
4350
        if (!OBJ_GET_PROPERTY(cx, callee,
4351
                              ATOM_TO_JSID(cx->runtime->atomState.callAtom),
4352
                              &fval)) {
4353
            return JS_FALSE;
4354
        }
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4355
        if (VALUE_IS_FUNCTION(cx, fval)) {
1 by Mike Hommey
Import upstream version 1.8.0.4
4356
            if (!GetCurrentExecutionContext(cx, obj, &nargv[2]))
4357
                return JS_FALSE;
4358
            args = js_GetArgsObject(cx, cx->fp);
4359
            if (!args)
4360
                return JS_FALSE;
4361
            nargv[0] = OBJECT_TO_JSVAL(obj);
4362
            nargv[1] = OBJECT_TO_JSVAL(args);
4363
            return js_InternalCall(cx, callee, fval, 3, nargv, rval);
4364
        }
4365
        if (JSVAL_IS_OBJECT(fval) && JSVAL_TO_OBJECT(fval) != callee) {
4366
            argv[-2] = fval;
4367
            ok = js_Call(cx, obj, argc, argv, rval);
4368
            argv[-2] = OBJECT_TO_JSVAL(callee);
4369
            return ok;
4370
        }
4371
#endif
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4372
        ReportIsNotFunction(cx, &argv[-2], cx->fp->flags & JSFRAME_ITERATOR);
1 by Mike Hommey
Import upstream version 1.8.0.4
4373
        return JS_FALSE;
4374
    }
4375
    return clasp->call(cx, obj, argc, argv, rval);
4376
}
4377
4378
JSBool
4379
js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
4380
             jsval *rval)
4381
{
4382
    JSClass *clasp;
4383
4384
    clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
4385
    if (!clasp->construct) {
4386
#ifdef NARCISSUS
4387
        JSObject *callee, *args;
4388
        jsval cval, nargv[2];
4389
        JSBool ok;
4390
4391
        callee = JSVAL_TO_OBJECT(argv[-2]);
4392
        if (!OBJ_GET_PROPERTY(cx, callee,
4393
                              ATOM_TO_JSID(cx->runtime->atomState
4394
                                           .constructAtom),
4395
                              &cval)) {
4396
            return JS_FALSE;
4397
        }
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4398
        if (VALUE_IS_FUNCTION(cx, cval)) {
1 by Mike Hommey
Import upstream version 1.8.0.4
4399
            if (!GetCurrentExecutionContext(cx, obj, &nargv[1]))
4400
                return JS_FALSE;
4401
            args = js_GetArgsObject(cx, cx->fp);
4402
            if (!args)
4403
                return JS_FALSE;
4404
            nargv[0] = OBJECT_TO_JSVAL(args);
4405
            return js_InternalCall(cx, callee, cval, 2, nargv, rval);
4406
        }
4407
        if (JSVAL_IS_OBJECT(cval) && JSVAL_TO_OBJECT(cval) != callee) {
4408
            argv[-2] = cval;
4409
            ok = js_Call(cx, obj, argc, argv, rval);
4410
            argv[-2] = OBJECT_TO_JSVAL(callee);
4411
            return ok;
4412
        }
4413
#endif
4414
        ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT);
4415
        return JS_FALSE;
4416
    }
4417
    return clasp->construct(cx, obj, argc, argv, rval);
4418
}
4419
4420
JSBool
4421
js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
4422
{
4423
    JSClass *clasp;
4424
    JSString *str;
4425
4426
    clasp = OBJ_GET_CLASS(cx, obj);
4427
    if (clasp->hasInstance)
4428
        return clasp->hasInstance(cx, obj, v, bp);
4429
#ifdef NARCISSUS
4430
    {
4431
        jsval fval, rval;
4432
4433
        if (!OBJ_GET_PROPERTY(cx, obj,
4434
                              ATOM_TO_JSID(cx->runtime->atomState
4435
                                           .hasInstanceAtom),
4436
                              &fval)) {
4437
            return JS_FALSE;
4438
        }
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4439
        if (VALUE_IS_FUNCTION(cx, fval)) {
1 by Mike Hommey
Import upstream version 1.8.0.4
4440
            return js_InternalCall(cx, obj, fval, 1, &v, &rval) &&
4441
                   js_ValueToBoolean(cx, rval, bp);
4442
        }
4443
    }
4444
#endif
4445
    str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
4446
                                     OBJECT_TO_JSVAL(obj), NULL);
4447
    if (str) {
4448
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4449
                             JSMSG_BAD_INSTANCEOF_RHS,
4450
                             JS_GetStringBytes(str));
4451
    }
4452
    return JS_FALSE;
4453
}
4454
4455
JSBool
4456
js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
4457
{
4458
    JSObject *obj2;
4459
4460
    *bp = JS_FALSE;
4461
    if (JSVAL_IS_PRIMITIVE(v))
4462
        return JS_TRUE;
4463
    obj2 = JSVAL_TO_OBJECT(v);
4464
    while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) {
4465
        if (obj2 == obj) {
4466
            *bp = JS_TRUE;
4467
            break;
4468
        }
4469
    }
4470
    return JS_TRUE;
4471
}
4472
4473
JSBool
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4474
js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id,
4475
                     JSObject **protop)
1 by Mike Hommey
Import upstream version 1.8.0.4
4476
{
4477
    jsval v;
4478
    JSObject *ctor;
4479
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4480
    if (!js_FindClassObject(cx, scope, id, &v))
1 by Mike Hommey
Import upstream version 1.8.0.4
4481
        return JS_FALSE;
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4482
    if (VALUE_IS_FUNCTION(cx, v)) {
1 by Mike Hommey
Import upstream version 1.8.0.4
4483
        ctor = JSVAL_TO_OBJECT(v);
4484
        if (!OBJ_GET_PROPERTY(cx, ctor,
4485
                              ATOM_TO_JSID(cx->runtime->atomState
4486
                                           .classPrototypeAtom),
4487
                              &v)) {
4488
            return JS_FALSE;
4489
        }
4490
        if (!JSVAL_IS_PRIMITIVE(v)) {
4491
            /*
4492
             * Set the newborn root in case v is otherwise unreferenced.
4493
             * It's ok to overwrite newborn roots here, since the getter
4494
             * called just above could have.  Unlike the common GC rooting
4495
             * model, our callers do not have to protect protop thanks to
4496
             * this newborn root, since they all immediately create a new
4497
             * instance that delegates to this object, or just query the
4498
             * prototype for its class.
4499
             */
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4500
            cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(v);
1 by Mike Hommey
Import upstream version 1.8.0.4
4501
        }
4502
    }
4503
    *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
4504
    return JS_TRUE;
4505
}
4506
4507
/*
4508
 * For shared precompilation of function objects, we support cloning on entry
4509
 * to an execution context in which the function declaration or expression
4510
 * should be processed as if it were not precompiled, where the precompiled
4511
 * function's scope chain does not match the execution context's.  The cloned
4512
 * function object carries its execution-context scope in its parent slot; it
4513
 * links to the precompiled function (the "clone-parent") via its proto slot.
4514
 *
4515
 * Note that this prototype-based delegation leaves an unchecked access path
4516
 * from the clone to the clone-parent's 'constructor' property.  If the clone
4517
 * lives in a less privileged or shared scope than the clone-parent, this is
4518
 * a security hole, a sharing hazard, or both.  Therefore we check all such
4519
 * accesses with the following getter/setter pair, which we use when defining
4520
 * 'constructor' in f.prototype for all function objects f.
4521
 */
4522
static JSBool
4523
CheckCtorGetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4524
{
4525
    JSAtom *atom;
4526
    uintN attrs;
4527
4528
    atom = cx->runtime->atomState.constructorAtom;
4529
    JS_ASSERT(id == ATOM_KEY(atom));
4530
    return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_READ,
4531
                            vp, &attrs);
4532
}
4533
4534
static JSBool
4535
CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
4536
{
4537
    JSAtom *atom;
4538
    uintN attrs;
4539
4540
    atom = cx->runtime->atomState.constructorAtom;
4541
    JS_ASSERT(id == ATOM_KEY(atom));
4542
    return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE,
4543
                            vp, &attrs);
4544
}
4545
4546
JSBool
4547
js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
4548
                     uintN attrs)
4549
{
4550
    /*
4551
     * Use the given attributes for the prototype property of the constructor,
4552
     * as user-defined constructors have a DontDelete prototype (which may be
4553
     * reset), while native or "system" constructors have DontEnum | ReadOnly |
4554
     * DontDelete.
4555
     */
4556
    if (!OBJ_DEFINE_PROPERTY(cx, ctor,
4557
                             ATOM_TO_JSID(cx->runtime->atomState
4558
                                          .classPrototypeAtom),
4559
                             OBJECT_TO_JSVAL(proto),
4560
                             JS_PropertyStub, JS_PropertyStub,
4561
                             attrs, NULL)) {
4562
        return JS_FALSE;
4563
    }
4564
4565
    /*
4566
     * ECMA says that Object.prototype.constructor, or f.prototype.constructor
4567
     * for a user-defined function f, is DontEnum.
4568
     */
4569
    return OBJ_DEFINE_PROPERTY(cx, proto,
4570
                               ATOM_TO_JSID(cx->runtime->atomState
4571
                                            .constructorAtom),
4572
                               OBJECT_TO_JSVAL(ctor),
4573
                               CheckCtorGetAccess, CheckCtorSetAccess,
4574
                               0, NULL);
4575
}
4576
4577
JSBool
4578
js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
4579
{
4580
    JSObject *obj;
4581
4582
    if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
4583
        obj = NULL;
4584
    } else if (JSVAL_IS_OBJECT(v)) {
4585
        obj = JSVAL_TO_OBJECT(v);
4586
        if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v))
4587
            return JS_FALSE;
4588
        if (JSVAL_IS_OBJECT(v))
4589
            obj = JSVAL_TO_OBJECT(v);
4590
    } else {
4591
        if (JSVAL_IS_STRING(v)) {
4592
            obj = js_StringToObject(cx, JSVAL_TO_STRING(v));
4593
        } else if (JSVAL_IS_INT(v)) {
4594
            obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v));
4595
        } else if (JSVAL_IS_DOUBLE(v)) {
4596
            obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v));
4597
        } else {
4598
            JS_ASSERT(JSVAL_IS_BOOLEAN(v));
4599
            obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v));
4600
        }
4601
        if (!obj)
4602
            return JS_FALSE;
4603
    }
4604
    *objp = obj;
4605
    return JS_TRUE;
4606
}
4607
4608
JSObject *
4609
js_ValueToNonNullObject(JSContext *cx, jsval v)
4610
{
4611
    JSObject *obj;
4612
    JSString *str;
4613
4614
    if (!js_ValueToObject(cx, v, &obj))
4615
        return NULL;
4616
    if (!obj) {
4617
        str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
4618
        if (str) {
4619
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4620
                                 JSMSG_NO_PROPERTIES, JS_GetStringBytes(str));
4621
        }
4622
    }
4623
    return obj;
4624
}
4625
4626
JSBool
4627
js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval)
4628
{
4629
    jsval argv[1];
4630
4631
    argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]);
4632
    return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv,
4633
                        rval);
4634
}
4635
4636
JSBool
4637
js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
4638
             uintN argc, jsval *argv, jsval *rval)
4639
{
4640
    JSErrorReporter older;
4641
    jsid id;
4642
    jsval fval;
4643
    JSBool ok;
4644
    int stackDummy;
4645
4646
    if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
4647
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
4648
        return JS_FALSE;
4649
    }
4650
4651
    /*
4652
     * Report failure only if an appropriate method was found, and calling it
4653
     * returned failure.  We propagate failure in this case to make exceptions
4654
     * behave properly.
4655
     */
4656
    older = JS_SetErrorReporter(cx, NULL);
4657
    id = ATOM_TO_JSID(atom);
4658
    fval = JSVAL_VOID;
4659
#if JS_HAS_XML_SUPPORT
4660
    if (OBJECT_IS_XML(cx, obj)) {
4661
        JSXMLObjectOps *ops;
4662
4663
        ops = (JSXMLObjectOps *) obj->map->ops;
4664
        obj = ops->getMethod(cx, obj, id, &fval);
4665
        ok = (obj != NULL);
4666
    } else
4667
#endif
4668
    {
4669
        ok = OBJ_GET_PROPERTY(cx, obj, id, &fval);
4670
    }
4671
    if (!ok)
4672
        JS_ClearPendingException(cx);
4673
    JS_SetErrorReporter(cx, older);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4674
4675
    return JSVAL_IS_PRIMITIVE(fval) ||
4676
           js_InternalCall(cx, obj, fval, argc, argv, rval);
1 by Mike Hommey
Import upstream version 1.8.0.4
4677
}
4678
4679
#if JS_HAS_XDR
4680
4681
JSBool
4682
js_XDRObject(JSXDRState *xdr, JSObject **objp)
4683
{
4684
    JSContext *cx;
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4685
    JSAtom *atom;
1 by Mike Hommey
Import upstream version 1.8.0.4
4686
    JSClass *clasp;
4687
    uint32 classId, classDef;
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4688
    JSProtoKey protoKey;
4689
    jsid classKey;
1 by Mike Hommey
Import upstream version 1.8.0.4
4690
    JSObject *proto;
4691
4692
    cx = xdr->cx;
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4693
    atom = NULL;
1 by Mike Hommey
Import upstream version 1.8.0.4
4694
    if (xdr->mode == JSXDR_ENCODE) {
4695
        clasp = OBJ_GET_CLASS(cx, *objp);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4696
        classId = JS_XDRFindClassIdByName(xdr, clasp->name);
1 by Mike Hommey
Import upstream version 1.8.0.4
4697
        classDef = !classId;
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4698
        if (classDef) {
4699
            if (!JS_XDRRegisterClass(xdr, clasp, &classId))
4700
                return JS_FALSE;
4701
            protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
4702
            if (protoKey != JSProto_Null) {
4703
                classDef |= (protoKey << 1);
4704
            } else {
4705
                atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
4706
                if (!atom)
4707
                    return JS_FALSE;
4708
            }
4709
        }
1 by Mike Hommey
Import upstream version 1.8.0.4
4710
    } else {
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4711
        clasp = NULL;           /* quell GCC overwarning */
1 by Mike Hommey
Import upstream version 1.8.0.4
4712
        classDef = 0;
4713
    }
4714
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4715
    /*
4716
     * XDR a flag word, which could be 0 for a class use, in which case no
4717
     * name follows, only the id in xdr's class registry; 1 for a class def,
4718
     * in which case the flag word is followed by the class name transferred
4719
     * from or to atom; or a value greater than 1, an odd number that when
4720
     * divided by two yields the JSProtoKey for class.  In the last case, as
4721
     * in the 0 classDef case, no name is transferred via atom.
4722
     */
1 by Mike Hommey
Import upstream version 1.8.0.4
4723
    if (!JS_XDRUint32(xdr, &classDef))
4724
        return JS_FALSE;
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4725
    if (classDef == 1 && !js_XDRCStringAtom(xdr, &atom))
4726
        return JS_FALSE;
4727
4728
    if (!JS_XDRUint32(xdr, &classId))
4729
        return JS_FALSE;
4730
4731
    if (xdr->mode == JSXDR_DECODE) {
1 by Mike Hommey
Import upstream version 1.8.0.4
4732
        if (classDef) {
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4733
            /* NB: we know that JSProto_Null is 0 here, for backward compat. */
4734
            protoKey = classDef >> 1;
4735
            classKey = (protoKey != JSProto_Null)
4736
                       ? INT_TO_JSID(protoKey)
4737
                       : ATOM_TO_JSID(atom);
4738
            if (!js_GetClassPrototype(cx, NULL, classKey, &proto))
4739
                return JS_FALSE;
1 by Mike Hommey
Import upstream version 1.8.0.4
4740
            clasp = OBJ_GET_CLASS(cx, proto);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4741
            if (!JS_XDRRegisterClass(xdr, clasp, &classId))
4742
                return JS_FALSE;
1 by Mike Hommey
Import upstream version 1.8.0.4
4743
        } else {
4744
            clasp = JS_XDRFindClassById(xdr, classId);
4745
            if (!clasp) {
4746
                char numBuf[12];
4747
                JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
4748
                JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4749
                                     JSMSG_CANT_FIND_CLASS, numBuf);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4750
                return JS_FALSE;
1 by Mike Hommey
Import upstream version 1.8.0.4
4751
            }
4752
        }
4753
    }
4754
4755
    if (!clasp->xdrObject) {
4756
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4757
                             JSMSG_CANT_XDR_CLASS, clasp->name);
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4758
        return JS_FALSE;
1 by Mike Hommey
Import upstream version 1.8.0.4
4759
    }
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4760
    return clasp->xdrObject(xdr, objp);
1 by Mike Hommey
Import upstream version 1.8.0.4
4761
}
4762
4763
#endif /* JS_HAS_XDR */
4764
4765
#ifdef DEBUG_brendan
4766
4767
#include <stdio.h>
4768
#include <math.h>
4769
4770
uint32 js_entry_count_max;
4771
uint32 js_entry_count_sum;
4772
double js_entry_count_sqsum;
4773
uint32 js_entry_count_hist[11];
4774
4775
static void
4776
MeterEntryCount(uintN count)
4777
{
4778
    if (count) {
4779
        js_entry_count_sum += count;
4780
        js_entry_count_sqsum += (double)count * count;
4781
        if (count > js_entry_count_max)
4782
            js_entry_count_max = count;
4783
    }
4784
    js_entry_count_hist[JS_MIN(count, 10)]++;
4785
}
4786
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4787
#define DEBUG_scopemeters
4788
#endif /* DEBUG_brendan */
4789
4790
#ifdef DEBUG_scopemeters
1 by Mike Hommey
Import upstream version 1.8.0.4
4791
void
4792
js_DumpScopeMeters(JSRuntime *rt)
4793
{
4794
    static FILE *logfp;
4795
    if (!logfp)
4796
        logfp = fopen("/tmp/scope.stats", "a");
4797
4798
    {
4799
        double mean = 0., var = 0., sigma = 0.;
4800
        double nscopes = rt->liveScopes;
4801
        double nentrys = js_entry_count_sum;
4802
        if (nscopes > 0 && nentrys >= 0) {
4803
            mean = nentrys / nscopes;
4804
            var = nscopes * js_entry_count_sqsum - nentrys * nentrys;
4805
            if (var < 0.0 || nscopes <= 1)
4806
                var = 0.0;
4807
            else
4808
                var /= nscopes * (nscopes - 1);
4809
4810
            /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
4811
            sigma = (var != 0.) ? sqrt(var) : 0.;
4812
        }
4813
4814
        fprintf(logfp,
4815
                "scopes %g entries %g mean %g sigma %g max %u",
4816
                nscopes, nentrys, mean, sigma, js_entry_count_max);
4817
    }
4818
4819
    fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u\n",
4820
            js_entry_count_hist[0], js_entry_count_hist[1],
4821
            js_entry_count_hist[2], js_entry_count_hist[3],
4822
            js_entry_count_hist[4], js_entry_count_hist[5],
4823
            js_entry_count_hist[6], js_entry_count_hist[7],
4824
            js_entry_count_hist[8], js_entry_count_hist[9],
4825
            js_entry_count_hist[10]);
4826
    js_entry_count_sum = js_entry_count_max = 0;
4827
    js_entry_count_sqsum = 0;
4828
    memset(js_entry_count_hist, 0, sizeof js_entry_count_hist);
4829
    fflush(logfp);
4830
}
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4831
#endif
1 by Mike Hommey
Import upstream version 1.8.0.4
4832
4833
uint32
4834
js_Mark(JSContext *cx, JSObject *obj, void *arg)
4835
{
4836
    JSScope *scope;
4837
    JSScopeProperty *sprop;
4838
    JSClass *clasp;
4839
4840
    JS_ASSERT(OBJ_IS_NATIVE(obj));
4841
    scope = OBJ_SCOPE(obj);
4842
#ifdef DEBUG_brendan
4843
    if (scope->object == obj)
4844
        MeterEntryCount(scope->entryCount);
4845
#endif
4846
4847
    JS_ASSERT(!SCOPE_LAST_PROP(scope) ||
4848
              SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope)));
4849
4850
    for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
4851
        if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
4852
            continue;
1.1.5 by Michael Bienia
Import upstream version 1.8.0.10
4853
        MARK_SCOPE_PROPERTY(cx, sprop);
1 by Mike Hommey
Import upstream version 1.8.0.4
4854
    }
4855
4856
    /* No one runs while the GC is running, so we can use LOCKED_... here. */
4857
    clasp = LOCKED_OBJ_GET_CLASS(obj);
4858
    if (clasp->mark)
1.1.7 by Arthur Loiret
Import upstream version 1.8.1.4
4859
        (void) clasp->mark(cx, obj, NULL);
1 by Mike Hommey
Import upstream version 1.8.0.4
4860
4861
    if (scope->object != obj) {
4862
        /*
4863
         * An unmutated object that shares a prototype's scope.  We can't tell
4864
         * how many slots are allocated and in use at obj->slots by looking at
4865
         * scope, so we get obj->slots' length from its -1'st element.
4866
         */
4867
        return (uint32) obj->slots[-1];
4868
    }
4869
    return JS_MIN(scope->map.freeslot, scope->map.nslots);
4870
}
4871
4872
void
4873
js_Clear(JSContext *cx, JSObject *obj)
4874
{
4875
    JSScope *scope;
4876
    JSRuntime *rt;
4877
    JSScopeProperty *sprop;
4878
    uint32 i, n;
4879
4880
    /*
4881
     * Clear our scope and the property cache of all obj's properties only if
4882
     * obj owns the scope (i.e., not if obj is unmutated and therefore sharing
4883
     * its prototype's scope).  NB: we do not clear any reserved slots lying
4884
     * below JSSLOT_FREE(clasp).
4885
     */
4886
    JS_LOCK_OBJ(cx, obj);
4887
    scope = OBJ_SCOPE(obj);
4888
    if (scope->object == obj) {
4889
        /* Clear the property cache before we clear the scope. */
4890
        rt = cx->runtime;
4891
        for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
4892
            if (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
4893
                SCOPE_HAS_PROPERTY(scope, sprop)) {
4894
                PROPERTY_CACHE_FILL(&rt->propertyCache, obj, sprop->id, NULL);
4895
            }
4896
        }
4897
4898
        /* Now that we're done using scope->lastProp/table, clear scope. */
4899
        js_ClearScope(cx, scope);
4900
4901
        /* Clear slot values and reset freeslot so we're consistent. */
4902
        i = scope->map.nslots;
4903
        n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj));
4904
        while (--i >= n)
4905
            obj->slots[i] = JSVAL_VOID;
4906
        scope->map.freeslot = n;
4907
    }
4908
    JS_UNLOCK_OBJ(cx, obj);
4909
}
4910
4911
jsval
4912
js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot)
4913
{
4914
    jsval v;
4915
4916
    JS_LOCK_OBJ(cx, obj);
4917
    v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID;
4918
    JS_UNLOCK_OBJ(cx, obj);
4919
    return v;
4920
}
4921
4922
JSBool
4923
js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
4924
{
4925
    JSScope *scope;
4926
    uint32 nslots;
4927
    JSClass *clasp;
4928
    jsval *newslots;
4929
4930
    JS_LOCK_OBJ(cx, obj);
4931
    scope = OBJ_SCOPE(obj);
4932
    nslots = (uint32) obj->slots[-1];
4933
    if (slot >= nslots) {
4934
        /*
4935
         * At this point, obj may or may not own scope.  If some path calls
4936
         * js_GetMutableScope but does not add a slot-owning property, then
4937
         * scope->object == obj but nslots will be nominal.  If obj shares a
4938
         * prototype's scope, then we cannot update scope->map here, but we
4939
         * must update obj->slots[-1] when we grow obj->slots.
4940
         *
4941
         * See js_Mark, before the last return, where we make a special case
4942
         * for unmutated (scope->object != obj) objects.
4943
         */
4944
        JS_ASSERT(nslots == JS_INITIAL_NSLOTS);
4945
        clasp = LOCKED_OBJ_GET_CLASS(obj);
4946
        nslots = JSSLOT_FREE(clasp);
4947
        if (clasp->reserveSlots)
4948
            nslots += clasp->reserveSlots(cx, obj);
4949
        JS_ASSERT(slot < nslots);
4950
4951
        newslots = AllocSlots(cx, obj->slots, nslots);
4952
        if (!newslots) {
4953
            JS_UNLOCK_SCOPE(cx, scope);
4954
            return JS_FALSE;
4955
        }
4956
        if (scope->object == obj)
4957
            scope->map.nslots = nslots;
4958
        obj->slots = newslots;
4959
    }
4960
4961
    /* Whether or not we grew nslots, we may need to advance freeslot. */
4962
    if (scope->object == obj && slot >= scope->map.freeslot)
4963
        scope->map.freeslot = slot + 1;
4964
4965
    obj->slots[slot] = v;
4966
    JS_UNLOCK_SCOPE(cx, scope);
4967
    return JS_TRUE;
4968
}
4969
4970
#ifdef DEBUG
4971
4972
/* Routines to print out values during debugging. */
4973
4974
void printChar(jschar *cp) {
4975
    fprintf(stderr, "jschar* (0x%p) \"", (void *)cp);
4976
    while (*cp)
4977
        fputc(*cp++, stderr);
4978
    fputc('"', stderr);
4979
    fputc('\n', stderr);
4980
}
4981
4982
void printString(JSString *str) {
4983
    size_t i, n;
4984
    jschar *s;
4985
    fprintf(stderr, "string (0x%p) \"", (void *)str);
4986
    s = JSSTRING_CHARS(str);
4987
    for (i=0, n=JSSTRING_LENGTH(str); i < n; i++)
4988
        fputc(s[i], stderr);
4989
    fputc('"', stderr);
4990
    fputc('\n', stderr);
4991
}
4992
4993
void printVal(JSContext *cx, jsval val);
4994
4995
void printObj(JSContext *cx, JSObject *jsobj) {
4996
    jsuint i;
4997
    jsval val;
4998
    JSClass *clasp;
4999
5000
    fprintf(stderr, "object 0x%p\n", (void *)jsobj);
5001
    clasp = OBJ_GET_CLASS(cx, jsobj);
5002
    fprintf(stderr, "class 0x%p %s\n", (void *)clasp, clasp->name);
5003
    for (i=0; i < jsobj->map->nslots; i++) {
5004
        fprintf(stderr, "slot %3d ", i);
5005
        val = jsobj->slots[i];
5006
        if (JSVAL_IS_OBJECT(val))
5007
            fprintf(stderr, "object 0x%p\n", (void *)JSVAL_TO_OBJECT(val));
5008
        else
5009
            printVal(cx, val);
5010
    }
5011
}
5012
5013
void printVal(JSContext *cx, jsval val) {
5014
    fprintf(stderr, "val %d (0x%p) = ", (int)val, (void *)val);
5015
    if (JSVAL_IS_NULL(val)) {
5016
        fprintf(stderr, "null\n");
5017
    } else if (JSVAL_IS_VOID(val)) {
5018
        fprintf(stderr, "undefined\n");
5019
    } else if (JSVAL_IS_OBJECT(val)) {
5020
        printObj(cx, JSVAL_TO_OBJECT(val));
5021
    } else if (JSVAL_IS_INT(val)) {
5022
        fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val));
5023
    } else if (JSVAL_IS_STRING(val)) {
5024
        printString(JSVAL_TO_STRING(val));
5025
    } else if (JSVAL_IS_DOUBLE(val)) {
5026
        fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val));
5027
    } else {
5028
        JS_ASSERT(JSVAL_IS_BOOLEAN(val));
5029
        fprintf(stderr, "(boolean) %s\n",
5030
                JSVAL_TO_BOOLEAN(val) ? "true" : "false");
5031
    }
5032
    fflush(stderr);
5033
}
5034
5035
void printId(JSContext *cx, jsid id) {
5036
    fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id);
5037
    printVal(cx, ID_TO_VALUE(id));
5038
}
5039
5040
void printAtom(JSAtom *atom) {
5041
    printString(ATOM_TO_STRING(atom));
5042
}
5043
5044
#endif