/[jscoverage]/trunk/js/jsobj.cpp
ViewVC logotype

Annotation of /trunk/js/jsobj.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (hide annotations)
Thu Oct 23 19:03:33 2008 UTC (11 years, 9 months ago) by siliconforks
File size: 173220 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


1 siliconforks 332 /* -*- 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 */
49     #include "jsbit.h"
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 "jsversion.h"
60     #include "jsemit.h"
61     #include "jsfun.h"
62     #include "jsgc.h"
63     #include "jsinterp.h"
64     #include "jslock.h"
65     #include "jsnum.h"
66     #include "jsobj.h"
67     #include "jsopcode.h"
68     #include "jsparse.h"
69     #include "jsscope.h"
70     #include "jsscript.h"
71     #include "jsstr.h"
72     #include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */
73     #include "jsstaticcheck.h"
74    
75     #if JS_HAS_GENERATORS
76     #include "jsiter.h"
77     #endif
78    
79     #if JS_HAS_XML_SUPPORT
80     #include "jsxml.h"
81     #endif
82    
83     #if JS_HAS_XDR
84     #include "jsxdrapi.h"
85     #endif
86    
87     #ifdef INCLUDE_MOZILLA_DTRACE
88     #include "jsdtracef.h"
89     #endif
90    
91     #include "jsautooplen.h"
92    
93     #ifdef JS_THREADSAFE
94     #define NATIVE_DROP_PROPERTY js_DropProperty
95    
96     extern void
97     js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
98     #else
99     #define NATIVE_DROP_PROPERTY NULL
100     #endif
101    
102     JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
103     js_NewObjectMap, js_DestroyObjectMap,
104     js_LookupProperty, js_DefineProperty,
105     js_GetProperty, js_SetProperty,
106     js_GetAttributes, js_SetAttributes,
107     js_DeleteProperty, js_DefaultValue,
108     js_Enumerate, js_CheckAccess,
109     NULL, NATIVE_DROP_PROPERTY,
110     js_Call, js_Construct,
111     NULL, js_HasInstance,
112     js_SetProtoOrParent, js_SetProtoOrParent,
113     js_TraceObject, js_Clear,
114     js_GetRequiredSlot, js_SetRequiredSlot
115     };
116    
117     JSClass js_ObjectClass = {
118     js_Object_str,
119     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
120     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
121     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
122     JSCLASS_NO_OPTIONAL_MEMBERS
123     };
124    
125     #if JS_HAS_OBJ_PROTO_PROP
126    
127     static JSBool
128     obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
129    
130     static JSBool
131     obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
132    
133     static JSBool
134     obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
135    
136     static JSPropertySpec object_props[] = {
137     /* These two must come first; see object_props[slot].name usage below. */
138     {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED,
139     obj_getSlot, obj_setSlot},
140     {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
141     obj_getSlot, obj_setSlot},
142     {js_count_str, 0, JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
143     obj_getCount, NULL},
144     {0,0,0,0,0}
145     };
146    
147     /* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
148     #define JSSLOT_COUNT 2
149    
150     static JSBool
151     ReportStrictSlot(JSContext *cx, uint32 slot)
152     {
153     if (slot == JSSLOT_PROTO)
154     return JS_TRUE;
155     return JS_ReportErrorFlagsAndNumber(cx,
156     JSREPORT_WARNING | JSREPORT_STRICT,
157     js_GetErrorMessage, NULL,
158     JSMSG_DEPRECATED_USAGE,
159     object_props[slot].name);
160     }
161    
162     static JSBool
163     obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
164     {
165     uint32 slot;
166     jsid propid;
167     JSAccessMode mode;
168     uintN attrs;
169     JSObject *pobj;
170     JSClass *clasp;
171     JSExtendedClass *xclasp;
172    
173     slot = (uint32) JSVAL_TO_INT(id);
174     if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
175     propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
176     mode = JSACC_PROTO;
177     } else {
178     propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
179     mode = JSACC_PARENT;
180     }
181    
182     /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */
183     if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs))
184     return JS_FALSE;
185    
186     pobj = JSVAL_TO_OBJECT(*vp);
187     if (pobj) {
188     clasp = OBJ_GET_CLASS(cx, pobj);
189     if (clasp == &js_CallClass || clasp == &js_BlockClass) {
190     /* Censor activations and lexical scopes per ECMA-262. */
191     *vp = JSVAL_NULL;
192     } else if (clasp->flags & JSCLASS_IS_EXTENDED) {
193     xclasp = (JSExtendedClass *) clasp;
194     if (xclasp->outerObject) {
195     pobj = xclasp->outerObject(cx, pobj);
196     if (!pobj)
197     return JS_FALSE;
198     *vp = OBJECT_TO_JSVAL(pobj);
199     }
200     }
201     }
202     return JS_TRUE;
203     }
204    
205     static JSBool
206     obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
207     {
208     JSObject *pobj;
209     uint32 slot;
210     jsid propid;
211     uintN attrs;
212    
213     if (!JSVAL_IS_OBJECT(*vp))
214     return JS_TRUE;
215     pobj = JSVAL_TO_OBJECT(*vp);
216    
217     if (pobj) {
218     /*
219     * Innerize pobj here to avoid sticking unwanted properties on the
220     * outer object. This ensures that any with statements only grant
221     * access to the inner object.
222     */
223     OBJ_TO_INNER_OBJECT(cx, pobj);
224     if (!pobj)
225     return JS_FALSE;
226     }
227     slot = (uint32) JSVAL_TO_INT(id);
228     if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
229     return JS_FALSE;
230    
231     /* __parent__ is readonly and permanent, only __proto__ may be set. */
232     propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
233     if (!OBJ_CHECK_ACCESS(cx, obj, propid,
234     (JSAccessMode)(JSACC_PROTO|JSACC_WRITE), vp,
235     &attrs)) {
236     return JS_FALSE;
237     }
238    
239     return js_SetProtoOrParent(cx, obj, slot, pobj);
240     }
241    
242     static JSBool
243     obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
244     {
245     jsval iter_state;
246     jsid num_properties;
247     JSBool ok;
248    
249     if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
250     return JS_FALSE;
251    
252     /* Get the number of properties to enumerate. */
253     iter_state = JSVAL_NULL;
254     ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
255     if (!ok)
256     goto out;
257    
258     if (!JSVAL_IS_INT(num_properties)) {
259     JS_ASSERT(0);
260     *vp = JSVAL_ZERO;
261     goto out;
262     }
263     *vp = num_properties;
264    
265     out:
266     if (iter_state != JSVAL_NULL)
267     ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
268     return ok;
269     }
270    
271     #else /* !JS_HAS_OBJ_PROTO_PROP */
272    
273     #define object_props NULL
274    
275     #endif /* !JS_HAS_OBJ_PROTO_PROP */
276    
277     JSBool
278     js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
279     {
280     JSSetSlotRequest ssr;
281     JSRuntime *rt;
282    
283     /* Optimize the null case to avoid the unnecessary overhead of js_GC. */
284     if (!pobj) {
285     JS_LOCK_OBJ(cx, obj);
286     if (slot == JSSLOT_PROTO && !js_GetMutableScope(cx, obj)) {
287     JS_UNLOCK_OBJ(cx, obj);
288     return JS_FALSE;
289     }
290     LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_NULL);
291     JS_UNLOCK_OBJ(cx, obj);
292     return JS_TRUE;
293     }
294    
295     ssr.obj = obj;
296     ssr.pobj = pobj;
297     ssr.slot = (uint16) slot;
298     ssr.errnum = (uint16) JSMSG_NOT_AN_ERROR;
299    
300     rt = cx->runtime;
301     JS_LOCK_GC(rt);
302     ssr.next = rt->setSlotRequests;
303     rt->setSlotRequests = &ssr;
304     for (;;) {
305     js_GC(cx, GC_SET_SLOT_REQUEST);
306     JS_UNLOCK_GC(rt);
307     if (!rt->setSlotRequests)
308     break;
309     JS_LOCK_GC(rt);
310     }
311    
312     if (ssr.errnum != JSMSG_NOT_AN_ERROR) {
313     if (ssr.errnum == JSMSG_OUT_OF_MEMORY) {
314     JS_ReportOutOfMemory(cx);
315     } else {
316     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, ssr.errnum,
317     #if JS_HAS_OBJ_PROTO_PROP
318     object_props[slot].name
319     #else
320     (slot == JSSLOT_PROTO) ? js_proto_str
321     : js_parent_str
322     #endif
323     );
324     }
325     return JS_FALSE;
326     }
327     return JS_TRUE;
328     }
329    
330     static JSHashNumber
331     js_hash_object(const void *key)
332     {
333     return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS;
334     }
335    
336     static JSHashEntry *
337     MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
338     {
339     JSSharpObjectMap *map;
340     JSHashTable *table;
341     JSHashNumber hash;
342     JSHashEntry **hep, *he;
343     jsatomid sharpid;
344     JSIdArray *ida;
345     JSBool ok;
346     jsint i, length;
347     jsid id;
348     #if JS_HAS_GETTER_SETTER
349     JSObject *obj2;
350     JSProperty *prop;
351     uintN attrs;
352     #endif
353     jsval val;
354    
355     JS_CHECK_RECURSION(cx, return NULL);
356    
357     map = &cx->sharpObjectMap;
358     table = map->table;
359     hash = js_hash_object(obj);
360     hep = JS_HashTableRawLookup(table, hash, obj);
361     he = *hep;
362     if (!he) {
363     sharpid = 0;
364     he = JS_HashTableRawAdd(table, hep, hash, obj,
365     JS_UINT32_TO_PTR(sharpid));
366     if (!he) {
367     JS_ReportOutOfMemory(cx);
368     return NULL;
369     }
370    
371     /*
372     * Increment map->depth to protect js_EnterSharpObject from reentering
373     * itself badly. Without this fix, if we reenter the basis case where
374     * map->depth == 0, when unwinding the inner call we will destroy the
375     * newly-created hash table and crash.
376     */
377     ++map->depth;
378     ida = JS_Enumerate(cx, obj);
379     --map->depth;
380     if (!ida)
381     return NULL;
382    
383     ok = JS_TRUE;
384     for (i = 0, length = ida->length; i < length; i++) {
385     id = ida->vector[i];
386     #if JS_HAS_GETTER_SETTER
387     ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
388     if (!ok)
389     break;
390     if (!prop)
391     continue;
392     ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
393     if (ok) {
394     if (OBJ_IS_NATIVE(obj2) &&
395     (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
396     val = JSVAL_NULL;
397     if (attrs & JSPROP_GETTER)
398     val = (jsval) ((JSScopeProperty*)prop)->getter;
399     if (attrs & JSPROP_SETTER) {
400     if (val != JSVAL_NULL) {
401     /* Mark the getter, then set val to setter. */
402     ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
403     NULL)
404     != NULL);
405     }
406     val = (jsval) ((JSScopeProperty*)prop)->setter;
407     }
408     } else {
409     ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
410     }
411     }
412     OBJ_DROP_PROPERTY(cx, obj2, prop);
413     #else
414     ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
415     #endif
416     if (!ok)
417     break;
418     if (!JSVAL_IS_PRIMITIVE(val) &&
419     !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
420     ok = JS_FALSE;
421     break;
422     }
423     }
424     if (!ok || !idap)
425     JS_DestroyIdArray(cx, ida);
426     if (!ok)
427     return NULL;
428     } else {
429     sharpid = JS_PTR_TO_UINT32(he->value);
430     if (sharpid == 0) {
431     sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
432     he->value = JS_UINT32_TO_PTR(sharpid);
433     }
434     ida = NULL;
435     }
436     if (idap)
437     *idap = ida;
438     return he;
439     }
440    
441     JSHashEntry *
442     js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
443     jschar **sp)
444     {
445     JSSharpObjectMap *map;
446     JSHashTable *table;
447     JSIdArray *ida;
448     JSHashNumber hash;
449     JSHashEntry *he, **hep;
450     jsatomid sharpid;
451     char buf[20];
452     size_t len;
453    
454     if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_ENTER_SHARP))
455     return NULL;
456    
457     /* Set to null in case we return an early error. */
458     *sp = NULL;
459     map = &cx->sharpObjectMap;
460     table = map->table;
461     if (!table) {
462     table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
463     JS_CompareValues, NULL, NULL);
464     if (!table) {
465     JS_ReportOutOfMemory(cx);
466     return NULL;
467     }
468     map->table = table;
469     JS_KEEP_ATOMS(cx->runtime);
470     }
471    
472     /* From this point the control must flow either through out: or bad:. */
473     ida = NULL;
474     if (map->depth == 0) {
475     he = MarkSharpObjects(cx, obj, &ida);
476     if (!he)
477     goto bad;
478     JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0);
479     if (!idap) {
480     JS_DestroyIdArray(cx, ida);
481     ida = NULL;
482     }
483     } else {
484     hash = js_hash_object(obj);
485     hep = JS_HashTableRawLookup(table, hash, obj);
486     he = *hep;
487    
488     /*
489     * It's possible that the value of a property has changed from the
490     * first time the object's properties are traversed (when the property
491     * ids are entered into the hash table) to the second (when they are
492     * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not
493     * idempotent.
494     */
495     if (!he) {
496     he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
497     if (!he) {
498     JS_ReportOutOfMemory(cx);
499     goto bad;
500     }
501     sharpid = 0;
502     goto out;
503     }
504     }
505    
506     sharpid = JS_PTR_TO_UINT32(he->value);
507     if (sharpid != 0) {
508     len = JS_snprintf(buf, sizeof buf, "#%u%c",
509     sharpid >> SHARP_ID_SHIFT,
510     (sharpid & SHARP_BIT) ? '#' : '=');
511     *sp = js_InflateString(cx, buf, &len);
512     if (!*sp) {
513     if (ida)
514     JS_DestroyIdArray(cx, ida);
515     goto bad;
516     }
517     }
518    
519     out:
520     JS_ASSERT(he);
521     if ((sharpid & SHARP_BIT) == 0) {
522     if (idap && !ida) {
523     ida = JS_Enumerate(cx, obj);
524     if (!ida) {
525     if (*sp) {
526     JS_free(cx, *sp);
527     *sp = NULL;
528     }
529     goto bad;
530     }
531     }
532     map->depth++;
533     }
534    
535     if (idap)
536     *idap = ida;
537     return he;
538    
539     bad:
540     /* Clean up the sharpObjectMap table on outermost error. */
541     if (map->depth == 0) {
542     JS_UNKEEP_ATOMS(cx->runtime);
543     map->sharpgen = 0;
544     JS_HashTableDestroy(map->table);
545     map->table = NULL;
546     }
547     return NULL;
548     }
549    
550     void
551     js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
552     {
553     JSSharpObjectMap *map;
554     JSIdArray *ida;
555    
556     map = &cx->sharpObjectMap;
557     JS_ASSERT(map->depth > 0);
558     if (--map->depth == 0) {
559     JS_UNKEEP_ATOMS(cx->runtime);
560     map->sharpgen = 0;
561     JS_HashTableDestroy(map->table);
562     map->table = NULL;
563     }
564     if (idap) {
565     ida = *idap;
566     if (ida) {
567     JS_DestroyIdArray(cx, ida);
568     *idap = NULL;
569     }
570     }
571     }
572    
573     static intN
574     gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
575     {
576     JS_CALL_OBJECT_TRACER((JSTracer *)arg, (JSObject *)he->key,
577     "sharp table entry");
578     return JS_DHASH_NEXT;
579     }
580    
581     void
582     js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
583     {
584     JS_ASSERT(map->depth > 0);
585     JS_ASSERT(map->table);
586    
587     /*
588     * During recursive calls to MarkSharpObjects a non-native object or
589     * object with a custom getProperty method can potentially return an
590     * unrooted value or even cut from the object graph an argument of one of
591     * MarkSharpObjects recursive invocations. So we must protect map->table
592     * entries against GC.
593     *
594     * We can not simply use JSTempValueRooter to mark the obj argument of
595     * MarkSharpObjects during recursion as we have to protect *all* entries
596     * in JSSharpObjectMap including those that contains otherwise unreachable
597     * objects just allocated through custom getProperty. Otherwise newer
598     * allocations can re-use the address of an object stored in the hashtable
599     * confusing js_EnterSharpObject. So to address the problem we simply
600     * mark all objects from map->table.
601     *
602     * An alternative "proper" solution is to use JSTempValueRooter in
603     * MarkSharpObjects with code to remove during finalization entries
604     * with otherwise unreachable objects. But this is way too complex
605     * to justify spending efforts.
606     */
607     JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, trc);
608     }
609    
610     #if JS_HAS_TOSOURCE
611     static JSBool
612     obj_toSource(JSContext *cx, uintN argc, jsval *vp)
613     {
614     JSBool ok, outermost;
615     JSObject *obj;
616     JSHashEntry *he;
617     JSIdArray *ida;
618     jschar *chars, *ochars, *vsharp;
619     const jschar *idstrchars, *vchars;
620     size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
621     const char *comma;
622     jsint i, j, length, valcnt;
623     jsid id;
624     #if JS_HAS_GETTER_SETTER
625     JSObject *obj2;
626     JSProperty *prop;
627     uintN attrs;
628     #endif
629     jsval *val;
630     jsval localroot[4] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL, JSVAL_NULL};
631     JSTempValueRooter tvr;
632     JSString *gsopold[2];
633     JSString *gsop[2];
634     JSString *idstr, *valstr, *str;
635    
636     JS_CHECK_RECURSION(cx, return JS_FALSE);
637    
638     MUST_FLOW_THROUGH("out");
639     JS_PUSH_TEMP_ROOT(cx, 4, localroot, &tvr);
640    
641     /* If outermost, we need parentheses to be an expression, not a block. */
642     outermost = (cx->sharpObjectMap.depth == 0);
643     obj = JS_THIS_OBJECT(cx, vp);
644     if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) {
645     ok = JS_FALSE;
646     goto out;
647     }
648     if (IS_SHARP(he)) {
649     /*
650     * We didn't enter -- obj is already "sharp", meaning we've visited it
651     * already in our depth first search, and therefore chars contains a
652     * string of the form "#n#".
653     */
654     JS_ASSERT(!ida);
655     #if JS_HAS_SHARP_VARS
656     nchars = js_strlen(chars);
657     #else
658     chars[0] = '{';
659     chars[1] = '}';
660     chars[2] = 0;
661     nchars = 2;
662     #endif
663     goto make_string;
664     }
665     JS_ASSERT(ida);
666     ok = JS_TRUE;
667    
668     if (!chars) {
669     /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
670     chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
671     nchars = 0;
672     if (!chars)
673     goto error;
674     if (outermost)
675     chars[nchars++] = '(';
676     } else {
677     /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
678     MAKE_SHARP(he);
679     nchars = js_strlen(chars);
680     chars = (jschar *)
681     realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
682     if (!chars) {
683     free(ochars);
684     goto error;
685     }
686     if (outermost) {
687     /*
688     * No need for parentheses around the whole shebang, because #n=
689     * unambiguously begins an object initializer, and never a block
690     * statement.
691     */
692     outermost = JS_FALSE;
693     }
694     }
695    
696     chars[nchars++] = '{';
697    
698     comma = NULL;
699    
700     /*
701     * We have four local roots for cooked and raw value GC safety. Hoist the
702     * "localroot + 2" out of the loop using the val local, which refers to
703     * the raw (unconverted, "uncooked") values.
704     */
705     val = localroot + 2;
706    
707     for (i = 0, length = ida->length; i < length; i++) {
708     JSBool idIsLexicalIdentifier, needOldStyleGetterSetter;
709    
710     /* Get strings for id and value and GC-root them via vp. */
711     id = ida->vector[i];
712    
713     #if JS_HAS_GETTER_SETTER
714     ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
715     if (!ok)
716     goto error;
717     #endif
718    
719     /*
720     * Convert id to a jsval and then to a string. Decide early whether we
721     * prefer get/set or old getter/setter syntax.
722     */
723     idstr = js_ValueToString(cx, ID_TO_VALUE(id));
724     if (!idstr) {
725     ok = JS_FALSE;
726     OBJ_DROP_PROPERTY(cx, obj2, prop);
727     goto error;
728     }
729     *vp = STRING_TO_JSVAL(idstr); /* local root */
730     idIsLexicalIdentifier = js_IsIdentifier(idstr);
731     needOldStyleGetterSetter =
732     !idIsLexicalIdentifier ||
733     js_CheckKeyword(JSSTRING_CHARS(idstr),
734     JSSTRING_LENGTH(idstr)) != TOK_EOF;
735    
736     #if JS_HAS_GETTER_SETTER
737    
738     valcnt = 0;
739     if (prop) {
740     ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
741     if (!ok) {
742     OBJ_DROP_PROPERTY(cx, obj2, prop);
743     goto error;
744     }
745     if (OBJ_IS_NATIVE(obj2) &&
746     (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
747     if (attrs & JSPROP_GETTER) {
748     val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter;
749     gsopold[valcnt] =
750     ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
751     gsop[valcnt] =
752     ATOM_TO_STRING(cx->runtime->atomState.getAtom);
753    
754     valcnt++;
755     }
756     if (attrs & JSPROP_SETTER) {
757     val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
758     gsopold[valcnt] =
759     ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
760     gsop[valcnt] =
761     ATOM_TO_STRING(cx->runtime->atomState.setAtom);
762    
763     valcnt++;
764     }
765     } else {
766     valcnt = 1;
767     gsop[0] = NULL;
768     gsopold[0] = NULL;
769     ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
770     }
771     OBJ_DROP_PROPERTY(cx, obj2, prop);
772     }
773    
774     #else /* !JS_HAS_GETTER_SETTER */
775    
776     /*
777     * We simplify the source code at the price of minor dead code bloat in
778     * the ECMA version (for testing only, see jsversion.h). The null
779     * default values in gsop[j] suffice to disable non-ECMA getter and
780     * setter code.
781     */
782     valcnt = 1;
783     gsop[0] = NULL;
784     gsopold[0] = NULL;
785     ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
786    
787     #endif /* !JS_HAS_GETTER_SETTER */
788    
789     if (!ok)
790     goto error;
791    
792     /*
793     * If id is a string that's not an identifier, then it needs to be
794     * quoted. Also, negative integer ids must be quoted.
795     */
796     if (JSID_IS_ATOM(id)
797     ? !idIsLexicalIdentifier
798     : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
799     idstr = js_QuoteString(cx, idstr, (jschar)'\'');
800     if (!idstr) {
801     ok = JS_FALSE;
802     goto error;
803     }
804     *vp = STRING_TO_JSVAL(idstr); /* local root */
805     }
806     JSSTRING_CHARS_AND_LENGTH(idstr, idstrchars, idstrlength);
807    
808     for (j = 0; j < valcnt; j++) {
809     /* Convert val[j] to its canonical source form. */
810     valstr = js_ValueToSource(cx, val[j]);
811     if (!valstr) {
812     ok = JS_FALSE;
813     goto error;
814     }
815     localroot[j] = STRING_TO_JSVAL(valstr); /* local root */
816     JSSTRING_CHARS_AND_LENGTH(valstr, vchars, vlength);
817    
818     if (vchars[0] == '#')
819     needOldStyleGetterSetter = JS_TRUE;
820    
821     if (needOldStyleGetterSetter)
822     gsop[j] = gsopold[j];
823    
824     /* If val[j] is a non-sharp object, consider sharpening it. */
825     vsharp = NULL;
826     vsharplength = 0;
827     #if JS_HAS_SHARP_VARS
828     if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
829     he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
830     &vsharp);
831     if (!he) {
832     ok = JS_FALSE;
833     goto error;
834     }
835     if (IS_SHARP(he)) {
836     vchars = vsharp;
837     vlength = js_strlen(vchars);
838     needOldStyleGetterSetter = JS_TRUE;
839     gsop[j] = gsopold[j];
840     } else {
841     if (vsharp) {
842     vsharplength = js_strlen(vsharp);
843     MAKE_SHARP(he);
844     needOldStyleGetterSetter = JS_TRUE;
845     gsop[j] = gsopold[j];
846     }
847     js_LeaveSharpObject(cx, NULL);
848     }
849     }
850     #endif
851    
852     #ifndef OLD_GETTER_SETTER
853     /*
854     * Remove '(function ' from the beginning of valstr and ')' from the
855     * end so that we can put "get" in front of the function definition.
856     */
857     if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) &&
858     !needOldStyleGetterSetter) {
859     JSFunction *fun = JS_ValueToFunction(cx, val[j]);
860     const jschar *start = vchars;
861     const jschar *end = vchars + vlength;
862    
863     uint8 parenChomp = 0;
864     if (vchars[0] == '(') {
865     vchars++;
866     parenChomp = 1;
867     }
868    
869     /*
870     * Try to jump "getter" or "setter" keywords, if we suspect
871     * they might appear here. This code can be confused by people
872     * defining Function.prototype.toString, so let's be cautious.
873     */
874     if (JSFUN_GETTER_TEST(fun->flags) ||
875     JSFUN_SETTER_TEST(fun->flags)) { /* skip "getter/setter" */
876     const jschar *tmp = js_strchr_limit(vchars, ' ', end);
877     if (tmp)
878     vchars = tmp + 1;
879     }
880    
881     /* Try to jump "function" keyword. */
882     if (vchars)
883     vchars = js_strchr_limit(vchars, ' ', end);
884    
885     if (vchars) {
886     if (*vchars == ' ')
887     vchars++;
888     vlength = end - vchars - parenChomp;
889     } else {
890     gsop[j] = NULL;
891     vchars = start;
892     }
893     }
894     #else
895     needOldStyleGetterSetter = JS_TRUE;
896     gsop[j] = gsopold[j];
897     #endif
898    
899     #define SAFE_ADD(n) \
900     JS_BEGIN_MACRO \
901     size_t n_ = (n); \
902     curlen += n_; \
903     if (curlen < n_) \
904     goto overflow; \
905     JS_END_MACRO
906    
907     curlen = nchars;
908     if (comma)
909     SAFE_ADD(2);
910     SAFE_ADD(idstrlength + 1);
911     if (gsop[j])
912     SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1);
913     SAFE_ADD(vsharplength);
914     SAFE_ADD(vlength);
915     /* Account for the trailing null. */
916     SAFE_ADD((outermost ? 2 : 1) + 1);
917     #undef SAFE_ADD
918    
919     if (curlen > (size_t)-1 / sizeof(jschar))
920     goto overflow;
921    
922     /* Allocate 1 + 1 at end for closing brace and terminating 0. */
923     chars = (jschar *)
924     realloc((ochars = chars), curlen * sizeof(jschar));
925     if (!chars) {
926     /* Save code space on error: let JS_free ignore null vsharp. */
927     JS_free(cx, vsharp);
928     free(ochars);
929     goto error;
930     }
931    
932     if (comma) {
933     chars[nchars++] = comma[0];
934     chars[nchars++] = comma[1];
935     }
936     comma = ", ";
937    
938     if (needOldStyleGetterSetter) {
939     js_strncpy(&chars[nchars], idstrchars, idstrlength);
940     nchars += idstrlength;
941     if (gsop[j]) {
942     chars[nchars++] = ' ';
943     gsoplength = JSSTRING_LENGTH(gsop[j]);
944     js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]),
945     gsoplength);
946     nchars += gsoplength;
947     }
948     chars[nchars++] = ':';
949     } else { /* New style "decompilation" */
950     if (gsop[j]) {
951     gsoplength = JSSTRING_LENGTH(gsop[j]);
952     js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]),
953     gsoplength);
954     nchars += gsoplength;
955     chars[nchars++] = ' ';
956     }
957     js_strncpy(&chars[nchars], idstrchars, idstrlength);
958     nchars += idstrlength;
959     /* Extraneous space after id here will be extracted later */
960     chars[nchars++] = gsop[j] ? ' ' : ':';
961     }
962    
963     if (vsharplength) {
964     js_strncpy(&chars[nchars], vsharp, vsharplength);
965     nchars += vsharplength;
966     }
967     js_strncpy(&chars[nchars], vchars, vlength);
968     nchars += vlength;
969    
970     if (vsharp)
971     JS_free(cx, vsharp);
972     }
973     }
974    
975     chars[nchars++] = '}';
976     if (outermost)
977     chars[nchars++] = ')';
978     chars[nchars] = 0;
979    
980     error:
981     js_LeaveSharpObject(cx, &ida);
982    
983     if (!ok) {
984     if (chars)
985     free(chars);
986     goto out;
987     }
988    
989     if (!chars) {
990     JS_ReportOutOfMemory(cx);
991     ok = JS_FALSE;
992     goto out;
993     }
994     make_string:
995     str = js_NewString(cx, chars, nchars);
996     if (!str) {
997     free(chars);
998     ok = JS_FALSE;
999     goto out;
1000     }
1001     *vp = STRING_TO_JSVAL(str);
1002     ok = JS_TRUE;
1003     out:
1004     JS_POP_TEMP_ROOT(cx, &tvr);
1005     return ok;
1006    
1007     overflow:
1008     JS_free(cx, vsharp);
1009     free(chars);
1010     chars = NULL;
1011     goto error;
1012     }
1013     #endif /* JS_HAS_TOSOURCE */
1014    
1015     static JSBool
1016     obj_toString(JSContext *cx, uintN argc, jsval *vp)
1017     {
1018     JSObject *obj;
1019     jschar *chars;
1020     size_t nchars;
1021     const char *clazz, *prefix;
1022     JSString *str;
1023    
1024     obj = JS_THIS_OBJECT(cx, vp);
1025     if (!obj)
1026     return JS_FALSE;
1027     obj = js_GetWrappedObject(cx, obj);
1028     clazz = OBJ_GET_CLASS(cx, obj)->name;
1029     nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
1030     chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
1031     if (!chars)
1032     return JS_FALSE;
1033    
1034     prefix = "[object ";
1035     nchars = 0;
1036     while ((chars[nchars] = (jschar)*prefix) != 0)
1037     nchars++, prefix++;
1038     while ((chars[nchars] = (jschar)*clazz) != 0)
1039     nchars++, clazz++;
1040     chars[nchars++] = ']';
1041     chars[nchars] = 0;
1042    
1043     str = js_NewString(cx, chars, nchars);
1044     if (!str) {
1045     JS_free(cx, chars);
1046     return JS_FALSE;
1047     }
1048     *vp = STRING_TO_JSVAL(str);
1049     return JS_TRUE;
1050     }
1051    
1052     static JSBool
1053     obj_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
1054     {
1055     jsval thisv;
1056     JSString *str;
1057    
1058     thisv = JS_THIS(cx, vp);
1059     if (JSVAL_IS_NULL(thisv))
1060     return JS_FALSE;
1061    
1062     str = js_ValueToString(cx, thisv);
1063     if (!str)
1064     return JS_FALSE;
1065    
1066     *vp = STRING_TO_JSVAL(str);
1067     return JS_TRUE;
1068     }
1069    
1070     static JSBool
1071     obj_valueOf(JSContext *cx, uintN argc, jsval *vp)
1072     {
1073     *vp = JS_THIS(cx, vp);
1074     return !JSVAL_IS_NULL(*vp);
1075     }
1076    
1077     /*
1078     * Check whether principals subsumes scopeobj's principals, and return true
1079     * if so (or if scopeobj has no principals, for backward compatibility with
1080     * the JS API, which does not require principals), and false otherwise.
1081     */
1082     JSBool
1083     js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
1084     JSPrincipals *principals, JSAtom *caller)
1085     {
1086     JSSecurityCallbacks *callbacks;
1087     JSPrincipals *scopePrincipals;
1088     const char *callerstr;
1089    
1090     callbacks = JS_GetSecurityCallbacks(cx);
1091     if (callbacks && callbacks->findObjectPrincipals) {
1092     scopePrincipals = callbacks->findObjectPrincipals(cx, scopeobj);
1093     if (!principals || !scopePrincipals ||
1094     !principals->subsume(principals, scopePrincipals)) {
1095     callerstr = js_AtomToPrintableString(cx, caller);
1096     if (!callerstr)
1097     return JS_FALSE;
1098     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1099     JSMSG_BAD_INDIRECT_CALL, callerstr);
1100     return JS_FALSE;
1101     }
1102     }
1103     return JS_TRUE;
1104     }
1105    
1106     JSObject *
1107     js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller)
1108     {
1109     JSClass *clasp;
1110     JSExtendedClass *xclasp;
1111     JSObject *inner;
1112    
1113     if (!scopeobj)
1114     goto bad;
1115    
1116     OBJ_TO_INNER_OBJECT(cx, scopeobj);
1117     if (!scopeobj)
1118     return NULL;
1119    
1120     inner = scopeobj;
1121    
1122     /* XXX This is an awful gross hack. */
1123     while (scopeobj) {
1124     clasp = OBJ_GET_CLASS(cx, scopeobj);
1125     if (clasp->flags & JSCLASS_IS_EXTENDED) {
1126     xclasp = (JSExtendedClass*)clasp;
1127     if (xclasp->innerObject &&
1128     xclasp->innerObject(cx, scopeobj) != scopeobj) {
1129     goto bad;
1130     }
1131     }
1132    
1133     scopeobj = OBJ_GET_PARENT(cx, scopeobj);
1134     }
1135    
1136     return inner;
1137    
1138     bad:
1139     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1140     JSMSG_BAD_INDIRECT_CALL, caller);
1141     return NULL;
1142     }
1143    
1144     const char *
1145     js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
1146     JSPrincipals *principals, uintN *linenop)
1147     {
1148     uint32 flags;
1149     #ifdef DEBUG
1150     JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
1151     #endif
1152    
1153     JS_ASSERT(principals || !(callbacks && callbacks->findObjectPrincipals));
1154     flags = JS_GetScriptFilenameFlags(caller->script);
1155     if ((flags & JSFILENAME_PROTECTED) &&
1156     principals &&
1157     strcmp(principals->codebase, "[System Principal]")) {
1158     *linenop = 0;
1159     return principals->codebase;
1160     }
1161    
1162     if (caller->regs && *caller->regs->pc == JSOP_EVAL) {
1163     JS_ASSERT(caller->regs->pc[JSOP_EVAL_LENGTH] == JSOP_RESUME);
1164     JS_ASSERT(caller->regs->pc[JSOP_EVAL_LENGTH + JSOP_RESUME_LENGTH] == JSOP_LINENO);
1165     *linenop = GET_UINT16(caller->regs->pc + JSOP_EVAL_LENGTH + JSOP_RESUME_LENGTH);
1166     } else {
1167     *linenop = js_PCToLineNumber(cx, caller->script,
1168     caller->regs ? caller->regs->pc : NULL);
1169     }
1170     return caller->script->filename;
1171     }
1172    
1173     JSBool
1174     js_obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1175     {
1176     JSStackFrame *fp, *caller;
1177     JSBool indirectCall;
1178     JSObject *scopeobj;
1179     JSString *str;
1180     const char *file;
1181     uintN line;
1182     JSPrincipals *principals;
1183     uint32 tcflags;
1184     JSScript *script;
1185     JSBool ok;
1186     #if JS_HAS_EVAL_THIS_SCOPE
1187     JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
1188     JSObject *setCallerScopeChain = NULL;
1189     JSBool setCallerVarObj = JS_FALSE;
1190     #endif
1191    
1192     fp = cx->fp;
1193     caller = JS_GetScriptedCaller(cx, fp);
1194     indirectCall = (caller && caller->regs && *caller->regs->pc != JSOP_EVAL);
1195    
1196     /*
1197     * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and
1198     * calls that attempt to use a non-global object as the "with" object in
1199     * the former indirect case.
1200     */
1201     scopeobj = OBJ_GET_PARENT(cx, obj);
1202     if (scopeobj) {
1203     scopeobj = js_GetWrappedObject(cx, obj);
1204     scopeobj = OBJ_GET_PARENT(cx, scopeobj);
1205     }
1206     if (indirectCall || scopeobj) {
1207     uintN flags = scopeobj
1208     ? JSREPORT_ERROR
1209     : JSREPORT_STRICT | JSREPORT_WARNING;
1210     if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL,
1211     JSMSG_BAD_INDIRECT_CALL,
1212     js_eval_str)) {
1213     return JS_FALSE;
1214     }
1215     }
1216    
1217     if (!JSVAL_IS_STRING(argv[0])) {
1218     *rval = argv[0];
1219     return JS_TRUE;
1220     }
1221    
1222     /*
1223     * If the caller is a lightweight function and doesn't have a variables
1224     * object, then we need to provide one for the compiler to stick any
1225     * declared (var) variables into.
1226     */
1227     if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL))
1228     return JS_FALSE;
1229    
1230     /* eval no longer takes an optional trailing argument. */
1231     if (argc >= 2 &&
1232     !JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT,
1233     js_GetErrorMessage, NULL,
1234     JSMSG_EVAL_ARITY)) {
1235     return JS_FALSE;
1236     }
1237    
1238     /* From here on, control must exit through label out with ok set. */
1239     MUST_FLOW_THROUGH("out");
1240     if (!scopeobj) {
1241     #if JS_HAS_EVAL_THIS_SCOPE
1242     /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
1243     if (indirectCall) {
1244     callerScopeChain = js_GetScopeChain(cx, caller);
1245     if (!callerScopeChain) {
1246     ok = JS_FALSE;
1247     goto out;
1248     }
1249     OBJ_TO_INNER_OBJECT(cx, obj);
1250     if (!obj) {
1251     ok = JS_FALSE;
1252     goto out;
1253     }
1254     if (obj != callerScopeChain) {
1255     ok = js_CheckPrincipalsAccess(cx, obj,
1256     caller->script->principals,
1257     cx->runtime->atomState.evalAtom);
1258     if (!ok)
1259     goto out;
1260    
1261     scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1);
1262     if (!scopeobj) {
1263     ok = JS_FALSE;
1264     goto out;
1265     }
1266    
1267     /* Set fp->scopeChain too, for the compiler. */
1268     caller->scopeChain = fp->scopeChain = scopeobj;
1269    
1270     /* Remember scopeobj so we can null its private when done. */
1271     setCallerScopeChain = scopeobj;
1272     }
1273    
1274     callerVarObj = caller->varobj;
1275     if (obj != callerVarObj) {
1276     /* Set fp->varobj too, for the compiler. */
1277     caller->varobj = fp->varobj = obj;
1278     setCallerVarObj = JS_TRUE;
1279     }
1280     }
1281     #endif
1282    
1283     /*
1284     * Compile using caller's current scope object.
1285     *
1286     * NB: This means that native callers (who reach this point through
1287     * the C API) must use the two parameter form.
1288     */
1289     if (caller) {
1290     scopeobj = js_GetScopeChain(cx, caller);
1291     if (!scopeobj) {
1292     ok = JS_FALSE;
1293     goto out;
1294     }
1295     }
1296     }
1297    
1298     /* Ensure we compile this eval with the right object in the scope chain. */
1299     scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str);
1300     if (!scopeobj) {
1301     ok = JS_FALSE;
1302     goto out;
1303     }
1304    
1305     str = JSVAL_TO_STRING(argv[0]);
1306     if (caller) {
1307     principals = JS_EvalFramePrincipals(cx, fp, caller);
1308     file = js_ComputeFilename(cx, caller, principals, &line);
1309     } else {
1310     file = NULL;
1311     line = 0;
1312     principals = NULL;
1313     }
1314    
1315     tcflags = TCF_COMPILE_N_GO;
1316     if (caller)
1317     tcflags |= TCF_PUT_STATIC_DEPTH(caller->script->staticDepth + 1);
1318     script = js_CompileScript(cx, scopeobj, caller, principals, tcflags,
1319     JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
1320     NULL, file, line);
1321     if (!script) {
1322     ok = JS_FALSE;
1323     goto out;
1324     }
1325    
1326     if (argc < 2) {
1327     /* Execute using caller's new scope object (might be a Call object). */
1328     if (caller)
1329     scopeobj = caller->scopeChain;
1330     }
1331    
1332     /*
1333     * Belt-and-braces: check that the lesser of eval's principals and the
1334     * caller's principals has access to scopeobj.
1335     */
1336     ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
1337     cx->runtime->atomState.evalAtom);
1338     if (ok)
1339     ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
1340    
1341     script->u.nextToGC = JS_SCRIPTS_TO_GC(cx);
1342     JS_SCRIPTS_TO_GC(cx) = script;
1343     #ifdef CHECK_SCRIPT_OWNER
1344     script->owner = NULL;
1345     #endif
1346    
1347     out:
1348     #if JS_HAS_EVAL_THIS_SCOPE
1349     /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
1350     if (setCallerScopeChain) {
1351     caller->scopeChain = callerScopeChain;
1352     JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass);
1353     JS_SetPrivate(cx, setCallerScopeChain, NULL);
1354     }
1355     if (setCallerVarObj)
1356     caller->varobj = callerVarObj;
1357     #endif
1358     return ok;
1359     }
1360    
1361     #if JS_HAS_OBJ_WATCHPOINT
1362    
1363     static JSBool
1364     obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
1365     void *closure)
1366     {
1367     JSObject *callable;
1368     JSSecurityCallbacks *callbacks;
1369     JSStackFrame *caller;
1370     JSPrincipals *subject, *watcher;
1371     JSResolvingKey key;
1372     JSResolvingEntry *entry;
1373     uint32 generation;
1374     jsval argv[3];
1375     JSBool ok;
1376    
1377     callable = (JSObject *) closure;
1378    
1379     callbacks = JS_GetSecurityCallbacks(cx);
1380     if (callbacks && callbacks->findObjectPrincipals) {
1381     /* Skip over any obj_watch_* frames between us and the real subject. */
1382     caller = JS_GetScriptedCaller(cx, cx->fp);
1383     if (caller) {
1384     /*
1385     * Only call the watch handler if the watcher is allowed to watch
1386     * the currently executing script.
1387     */
1388     watcher = callbacks->findObjectPrincipals(cx, callable);
1389     subject = JS_StackFramePrincipals(cx, caller);
1390    
1391     if (watcher && subject && !watcher->subsume(watcher, subject)) {
1392     /* Silently don't call the watch handler. */
1393     return JS_TRUE;
1394     }
1395     }
1396     }
1397    
1398     /* Avoid recursion on (obj, id) already being watched on cx. */
1399     key.obj = obj;
1400     key.id = id;
1401     if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1402     return JS_FALSE;
1403     if (!entry)
1404     return JS_TRUE;
1405     generation = cx->resolvingTable->generation;
1406    
1407     argv[0] = id;
1408     argv[1] = old;
1409     argv[2] = *nvp;
1410     ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp);
1411     js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1412     return ok;
1413     }
1414    
1415     static JSBool
1416     obj_watch(JSContext *cx, uintN argc, jsval *vp)
1417     {
1418     JSObject *callable;
1419     jsval userid, value;
1420     jsid propid;
1421     JSObject *obj;
1422     uintN attrs;
1423    
1424     if (argc <= 1) {
1425     js_ReportMissingArg(cx, vp, 1);
1426     return JS_FALSE;
1427     }
1428    
1429     callable = js_ValueToCallableObject(cx, &vp[3], 0);
1430     if (!callable)
1431     return JS_FALSE;
1432    
1433     /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1434     userid = vp[2];
1435     if (!JS_ValueToId(cx, userid, &propid))
1436     return JS_FALSE;
1437    
1438     obj = JS_THIS_OBJECT(cx, vp);
1439     if (!obj || !OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
1440     return JS_FALSE;
1441     if (attrs & JSPROP_READONLY)
1442     return JS_TRUE;
1443     *vp = JSVAL_VOID;
1444    
1445     if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_MakeArraySlow(cx, obj))
1446     return JS_FALSE;
1447     return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable);
1448     }
1449    
1450     static JSBool
1451     obj_unwatch(JSContext *cx, uintN argc, jsval *vp)
1452     {
1453     JSObject *obj;
1454    
1455     obj = JS_THIS_OBJECT(cx, vp);
1456     if (!obj)
1457     return JS_FALSE;
1458     *vp = JSVAL_VOID;
1459     return JS_ClearWatchPoint(cx, obj, argc != 0 ? vp[2] : JSVAL_VOID,
1460     NULL, NULL);
1461     }
1462    
1463     #endif /* JS_HAS_OBJ_WATCHPOINT */
1464    
1465     /*
1466     * Prototype and property query methods, to complement the 'in' and
1467     * 'instanceof' operators.
1468     */
1469    
1470     /* Proposed ECMA 15.2.4.5. */
1471     JSBool
1472     js_obj_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp)
1473     {
1474     JSObject *obj;
1475    
1476     obj = JS_THIS_OBJECT(cx, vp);
1477     return obj &&
1478     js_HasOwnPropertyHelper(cx, obj->map->ops->lookupProperty, argc, vp);
1479     }
1480    
1481     JSBool
1482     js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc,
1483     jsval *vp)
1484     {
1485     jsid id;
1486     JSObject *obj;
1487    
1488     if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
1489     return JS_FALSE;
1490     obj = JS_THIS_OBJECT(cx, vp);
1491     return obj && js_HasOwnProperty(cx, lookup, obj, id, vp);
1492     }
1493    
1494     JSBool
1495     js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
1496     jsval *vp)
1497     {
1498     JSObject *obj2;
1499     JSProperty *prop;
1500     JSScopeProperty *sprop;
1501    
1502     if (!lookup(cx, obj, id, &obj2, &prop))
1503     return JS_FALSE;
1504     if (!prop) {
1505     *vp = JSVAL_FALSE;
1506     } else if (obj2 == obj) {
1507     *vp = JSVAL_TRUE;
1508     } else {
1509     JSClass *clasp;
1510     JSExtendedClass *xclasp;
1511     JSObject *outer;
1512    
1513     clasp = OBJ_GET_CLASS(cx, obj2);
1514     if (!(clasp->flags & JSCLASS_IS_EXTENDED) ||
1515     !(xclasp = (JSExtendedClass *) clasp)->outerObject) {
1516     outer = NULL;
1517     } else {
1518     outer = xclasp->outerObject(cx, obj2);
1519     if (!outer)
1520     return JS_FALSE;
1521     }
1522     if (outer == obj) {
1523     *vp = JSVAL_TRUE;
1524     } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj) == clasp) {
1525     /*
1526     * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1527     * delegated property makes that property appear to be direct in
1528     * all delegating instances of the same native class. This hack
1529     * avoids bloating every function instance with its own 'length'
1530     * (AKA 'arity') property. But it must not extend across class
1531     * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1532     *
1533     * It's not really a hack, of course: a permanent property can't
1534     * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1535     * any instance, prototype or delegating". Without a slot, and
1536     * without the ability to remove and recreate (with differences)
1537     * the property, there is no way to tell whether it is directly
1538     * owned, or indirectly delegated.
1539     */
1540     sprop = (JSScopeProperty *)prop;
1541     *vp = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
1542     } else {
1543     *vp = JSVAL_FALSE;
1544     }
1545     }
1546     if (prop)
1547     OBJ_DROP_PROPERTY(cx, obj2, prop);
1548     return JS_TRUE;
1549     }
1550    
1551     /* Proposed ECMA 15.2.4.6. */
1552     static JSBool
1553     obj_isPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
1554     {
1555     JSBool b;
1556    
1557     if (!js_IsDelegate(cx, JS_THIS_OBJECT(cx, vp),
1558     argc != 0 ? vp[2] : JSVAL_VOID, &b)) {
1559     return JS_FALSE;
1560     }
1561     *vp = BOOLEAN_TO_JSVAL(b);
1562     return JS_TRUE;
1563     }
1564    
1565     /* Proposed ECMA 15.2.4.7. */
1566     JSBool
1567     js_obj_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp)
1568     {
1569     jsid id;
1570     JSObject *obj;
1571    
1572     if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
1573     return JS_FALSE;
1574    
1575     obj = JS_THIS_OBJECT(cx, vp);
1576     return obj && js_PropertyIsEnumerable(cx, obj, id, vp);
1577     }
1578    
1579     JSBool
1580     js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1581     {
1582     JSObject *pobj;
1583     uintN attrs;
1584     JSProperty *prop;
1585     JSBool ok;
1586    
1587     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1588     return JS_FALSE;
1589    
1590     if (!prop) {
1591     *vp = JSVAL_FALSE;
1592     return JS_TRUE;
1593     }
1594    
1595     /*
1596     * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1597     * The ECMA spec really should be fixed so propertyIsEnumerable and the
1598     * for..in loop agree on whether prototype properties are enumerable,
1599     * obviously by fixing this method (not by breaking the for..in loop!).
1600     *
1601     * We check here for shared permanent prototype properties, which should
1602     * be treated as if they are local to obj. They are an implementation
1603     * technique used to satisfy ECMA requirements; users should not be able
1604     * to distinguish a shared permanent proto-property from a local one.
1605     */
1606     if (pobj != obj &&
1607     !(OBJ_IS_NATIVE(pobj) &&
1608     SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
1609     OBJ_DROP_PROPERTY(cx, pobj, prop);
1610     *vp = JSVAL_FALSE;
1611     return JS_TRUE;
1612     }
1613    
1614     ok = OBJ_GET_ATTRIBUTES(cx, pobj, id, prop, &attrs);
1615     OBJ_DROP_PROPERTY(cx, pobj, prop);
1616     if (ok)
1617     *vp = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
1618     return ok;
1619     }
1620    
1621     #if JS_HAS_GETTER_SETTER
1622     static JSBool
1623     obj_defineGetter(JSContext *cx, uintN argc, jsval *vp)
1624     {
1625     jsval fval, junk;
1626     jsid id;
1627     JSObject *obj;
1628     uintN attrs;
1629    
1630     if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) {
1631     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1632     JSMSG_BAD_GETTER_OR_SETTER,
1633     js_getter_str);
1634     return JS_FALSE;
1635     }
1636     fval = vp[3];
1637    
1638     if (!JS_ValueToId(cx, vp[2], &id))
1639     return JS_FALSE;
1640     obj = JS_THIS_OBJECT(cx, vp);
1641     if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
1642     return JS_FALSE;
1643     /*
1644     * Getters and setters are just like watchpoints from an access
1645     * control point of view.
1646     */
1647     if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1648     return JS_FALSE;
1649     *vp = JSVAL_VOID;
1650     return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1651     (JSPropertyOp) JSVAL_TO_OBJECT(fval),
1652     JS_PropertyStub,
1653     JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED,
1654     NULL);
1655     }
1656    
1657     static JSBool
1658     obj_defineSetter(JSContext *cx, uintN argc, jsval *vp)
1659     {
1660     jsval fval, junk;
1661     jsid id;
1662     JSObject *obj;
1663     uintN attrs;
1664    
1665     if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) {
1666     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1667     JSMSG_BAD_GETTER_OR_SETTER,
1668     js_setter_str);
1669     return JS_FALSE;
1670     }
1671     fval = vp[3];
1672    
1673     if (!JS_ValueToId(cx, vp[2], &id))
1674     return JS_FALSE;
1675     obj = JS_THIS_OBJECT(cx, vp);
1676     if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
1677     return JS_FALSE;
1678     /*
1679     * Getters and setters are just like watchpoints from an access
1680     * control point of view.
1681     */
1682     if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1683     return JS_FALSE;
1684     *vp = JSVAL_VOID;
1685     return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1686     JS_PropertyStub,
1687     (JSPropertyOp) JSVAL_TO_OBJECT(fval),
1688     JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED,
1689     NULL);
1690     }
1691    
1692     static JSBool
1693     obj_lookupGetter(JSContext *cx, uintN argc, jsval *vp)
1694     {
1695     jsid id;
1696     JSObject *obj, *pobj;
1697     JSProperty *prop;
1698     JSScopeProperty *sprop;
1699    
1700     if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
1701     return JS_FALSE;
1702     obj = JS_THIS_OBJECT(cx, vp);
1703     if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1704     return JS_FALSE;
1705     *vp = JSVAL_VOID;
1706     if (prop) {
1707     if (OBJ_IS_NATIVE(pobj)) {
1708     sprop = (JSScopeProperty *) prop;
1709     if (sprop->attrs & JSPROP_GETTER)
1710     *vp = OBJECT_TO_JSVAL(sprop->getter);
1711     }
1712     OBJ_DROP_PROPERTY(cx, pobj, prop);
1713     }
1714     return JS_TRUE;
1715     }
1716    
1717     static JSBool
1718     obj_lookupSetter(JSContext *cx, uintN argc, jsval *vp)
1719     {
1720     jsid id;
1721     JSObject *obj, *pobj;
1722     JSProperty *prop;
1723     JSScopeProperty *sprop;
1724    
1725     if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
1726     return JS_FALSE;
1727     obj = JS_THIS_OBJECT(cx, vp);
1728     if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1729     return JS_FALSE;
1730     *vp = JSVAL_VOID;
1731     if (prop) {
1732     if (OBJ_IS_NATIVE(pobj)) {
1733     sprop = (JSScopeProperty *) prop;
1734     if (sprop->attrs & JSPROP_SETTER)
1735     *vp = OBJECT_TO_JSVAL(sprop->setter);
1736     }
1737     OBJ_DROP_PROPERTY(cx, pobj, prop);
1738     }
1739     return JS_TRUE;
1740     }
1741     #endif /* JS_HAS_GETTER_SETTER */
1742    
1743     JSBool
1744     obj_getPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
1745     {
1746     JSObject *obj;
1747     uintN attrs;
1748    
1749     if (argc == 0) {
1750     js_ReportMissingArg(cx, vp, 0);
1751     return JS_FALSE;
1752     }
1753    
1754     obj = js_ValueToNonNullObject(cx, vp[2]);
1755     if (!obj)
1756     return JS_FALSE;
1757     vp[2] = OBJECT_TO_JSVAL(obj);
1758    
1759     return OBJ_CHECK_ACCESS(cx, obj,
1760     ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
1761     JSACC_PROTO, vp, &attrs);
1762     }
1763    
1764     #if JS_HAS_OBJ_WATCHPOINT
1765     const char js_watch_str[] = "watch";
1766     const char js_unwatch_str[] = "unwatch";
1767     #endif
1768     const char js_hasOwnProperty_str[] = "hasOwnProperty";
1769     const char js_isPrototypeOf_str[] = "isPrototypeOf";
1770     const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
1771     #if JS_HAS_GETTER_SETTER
1772     const char js_defineGetter_str[] = "__defineGetter__";
1773     const char js_defineSetter_str[] = "__defineSetter__";
1774     const char js_lookupGetter_str[] = "__lookupGetter__";
1775     const char js_lookupSetter_str[] = "__lookupSetter__";
1776     #endif
1777    
1778     static JSFunctionSpec object_methods[] = {
1779     #if JS_HAS_TOSOURCE
1780     JS_FN(js_toSource_str, obj_toSource, 0,0),
1781     #endif
1782     JS_FN(js_toString_str, obj_toString, 0,0),
1783     JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0),
1784     JS_FN(js_valueOf_str, obj_valueOf, 0,0),
1785     #if JS_HAS_OBJ_WATCHPOINT
1786     JS_FN(js_watch_str, obj_watch, 2,0),
1787     JS_FN(js_unwatch_str, obj_unwatch, 1,0),
1788     #endif
1789     JS_FN(js_hasOwnProperty_str, js_obj_hasOwnProperty, 1,0),
1790     JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
1791     JS_FN(js_propertyIsEnumerable_str, js_obj_propertyIsEnumerable, 1,0),
1792     #if JS_HAS_GETTER_SETTER
1793     JS_FN(js_defineGetter_str, obj_defineGetter, 2,0),
1794     JS_FN(js_defineSetter_str, obj_defineSetter, 2,0),
1795     JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0),
1796     JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0),
1797     #endif
1798     JS_FS_END
1799     };
1800    
1801     static JSFunctionSpec object_static_methods[] = {
1802     JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
1803     JS_FS_END
1804     };
1805    
1806     JSBool
1807     js_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, 0);
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_TraceObject, 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",
1950     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
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    
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, 0);
1963     if (!obj)
1964     return NULL;
1965     STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(cx->fp));
1966     OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
1967     return obj;
1968     }
1969    
1970     JSObject *
1971     js_NewBlockObject(JSContext *cx)
1972     {
1973     JSObject *obj;
1974     JSBool ok;
1975    
1976     /*
1977     * Null obj's proto slot so that Object.prototype.* does not pollute block
1978     * scopes. Make sure obj has its own scope too, since clearing proto does
1979     * not affect OBJ_SCOPE(obj).
1980     */
1981     obj = js_NewObject(cx, &js_BlockClass, NULL, NULL, 0);
1982     if (!obj)
1983     return NULL;
1984     JS_LOCK_OBJ(cx, obj);
1985     ok = js_GetMutableScope(cx, obj) != NULL;
1986     JS_UNLOCK_OBJ(cx, obj);
1987     if (!ok)
1988     return NULL;
1989     OBJ_CLEAR_PROTO(cx, obj);
1990     return obj;
1991     }
1992    
1993     JSObject *
1994     js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent,
1995     JSStackFrame *fp)
1996     {
1997     JSObject *clone;
1998    
1999     JS_ASSERT(STOBJ_GET_CLASS(proto) == &js_BlockClass);
2000     JS_ASSERT(!OBJ_IS_CLONED_BLOCK(proto));
2001     clone = js_NewObject(cx, &js_BlockClass, proto, parent, 0);
2002     if (!clone)
2003     return NULL;
2004     STOBJ_SET_SLOT(clone, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(fp));
2005     STOBJ_SET_SLOT(clone, JSSLOT_BLOCK_DEPTH,
2006     OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH));
2007     JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone));
2008     return clone;
2009     }
2010    
2011     JSBool
2012     js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
2013     {
2014     JSStackFrame *fp;
2015     JSObject *obj;
2016     uintN depth, count;
2017    
2018     /* Blocks have one fixed slot available for the first local.*/
2019     JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2);
2020    
2021     fp = cx->fp;
2022     obj = fp->scopeChain;
2023     JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
2024     JS_ASSERT(OBJ_GET_PRIVATE(cx, obj) == cx->fp);
2025     JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
2026    
2027     /*
2028     * Block objects should never be exposed to scripts. Thus the clone should
2029     * not own the property map and rather always share it with the prototype
2030     * object. This allows to skip updating OBJ_SCOPE(obj)->map.freeslot after
2031     * we copy the stack slots into reserved slots.
2032     */
2033     JS_ASSERT(OBJ_SCOPE(obj)->object != obj);
2034    
2035     /* Block objects should not have reserved slots before they are put. */
2036     JS_ASSERT(STOBJ_NSLOTS(obj) == JS_INITIAL_NSLOTS);
2037    
2038     /* The block and its locals must be on the current stack for GC safety. */
2039     depth = OBJ_BLOCK_DEPTH(cx, obj);
2040     count = OBJ_BLOCK_COUNT(cx, obj);
2041     JS_ASSERT(depth <= (size_t) (fp->regs->sp - StackBase(fp)));
2042     JS_ASSERT(count <= (size_t) (fp->regs->sp - StackBase(fp) - depth));
2043    
2044     /* See comments in CheckDestructuring from jsparse.c. */
2045     JS_ASSERT(count >= 1);
2046    
2047     depth += fp->script->nfixed;
2048     obj->fslots[JSSLOT_BLOCK_DEPTH + 1] = fp->slots[depth];
2049     if (normalUnwind && count > 1) {
2050     --count;
2051     JS_LOCK_OBJ(cx, obj);
2052     if (!js_ReallocSlots(cx, obj, JS_INITIAL_NSLOTS + count, JS_TRUE))
2053     normalUnwind = JS_FALSE;
2054     else
2055     memcpy(obj->dslots, fp->slots + depth + 1, count * sizeof(jsval));
2056     JS_UNLOCK_OBJ(cx, obj);
2057     }
2058    
2059     /* We must clear the private slot even with errors. */
2060     JS_SetPrivate(cx, obj, NULL);
2061     fp->scopeChain = OBJ_GET_PARENT(cx, obj);
2062     return normalUnwind;
2063     }
2064    
2065     static JSBool
2066     block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2067     {
2068     uintN index;
2069     JSStackFrame *fp;
2070    
2071     JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL));
2072     JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
2073     if (!JSVAL_IS_INT(id))
2074     return JS_TRUE;
2075    
2076     index = (uint16) JSVAL_TO_INT(id);
2077     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
2078     if (fp) {
2079     index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj);
2080     JS_ASSERT(index < fp->script->nslots);
2081     *vp = fp->slots[index];
2082     return JS_TRUE;
2083     }
2084    
2085     /* Reserve slots start with the first slot after the private. */
2086     index += JSSLOT_BLOCK_DEPTH - JSSLOT_PRIVATE;
2087     return JS_GetReservedSlot(cx, obj, index, vp);
2088     }
2089    
2090     static JSBool
2091     block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2092     {
2093     uintN index;
2094     JSStackFrame *fp;
2095    
2096     JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL));
2097     if (!JSVAL_IS_INT(id))
2098     return JS_TRUE;
2099    
2100     index = (uint16) JSVAL_TO_INT(id);
2101     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
2102     if (fp) {
2103     index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj);
2104     JS_ASSERT(index < fp->script->nslots);
2105     fp->slots[index] = *vp;
2106     return JS_TRUE;
2107     }
2108    
2109     /* Reserve slots start with the first slot after the private. */
2110     index += JSSLOT_BLOCK_DEPTH - JSSLOT_PRIVATE;
2111     return JS_SetReservedSlot(cx, obj, index, *vp);
2112     }
2113    
2114     #if JS_HAS_XDR
2115    
2116     #define NO_PARENT_INDEX ((uint32)-1)
2117    
2118     uint32
2119     FindObjectIndex(JSObjectArray *array, JSObject *obj)
2120     {
2121     size_t i;
2122    
2123     if (array) {
2124     i = array->length;
2125     do {
2126    
2127     if (array->vector[--i] == obj)
2128     return i;
2129     } while (i != 0);
2130     }
2131    
2132     return NO_PARENT_INDEX;
2133     }
2134    
2135     static JSBool
2136     block_xdrObject(JSXDRState *xdr, JSObject **objp)
2137     {
2138     JSContext *cx;
2139     uint32 parentId;
2140     JSObject *obj, *parent;
2141     uint16 depth, count, i;
2142     uint32 tmp;
2143     JSTempValueRooter tvr;
2144     JSScopeProperty *sprop;
2145     jsid propid;
2146     JSAtom *atom;
2147     int16 shortid;
2148     JSBool ok;
2149    
2150     cx = xdr->cx;
2151     #ifdef __GNUC__
2152     obj = NULL; /* quell GCC overwarning */
2153     #endif
2154    
2155     if (xdr->mode == JSXDR_ENCODE) {
2156     obj = *objp;
2157     parent = OBJ_GET_PARENT(cx, obj);
2158     parentId = (xdr->script->objectsOffset == 0)
2159     ? NO_PARENT_INDEX
2160     : FindObjectIndex(JS_SCRIPT_OBJECTS(xdr->script), parent);
2161     depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj);
2162     count = (uint16)OBJ_BLOCK_COUNT(cx, obj);
2163     tmp = (uint32)(depth << 16) | count;
2164     }
2165     #ifdef __GNUC__ /* suppress bogus gcc warnings */
2166     else count = 0;
2167     #endif
2168    
2169     /* First, XDR the parent atomid. */
2170     if (!JS_XDRUint32(xdr, &parentId))
2171     return JS_FALSE;
2172    
2173     if (xdr->mode == JSXDR_DECODE) {
2174     obj = js_NewBlockObject(cx);
2175     if (!obj)
2176     return JS_FALSE;
2177     *objp = obj;
2178    
2179     /*
2180     * If there's a parent id, then get the parent out of our script's
2181     * object array. We know that we XDR block object in outer-to-inner
2182     * order, which means that getting the parent now will work.
2183     */
2184     if (parentId == NO_PARENT_INDEX)
2185     parent = NULL;
2186     else
2187     JS_GET_SCRIPT_OBJECT(xdr->script, parentId, parent);
2188     STOBJ_SET_PARENT(obj, parent);
2189     }
2190    
2191     JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr);
2192    
2193     if (!JS_XDRUint32(xdr, &tmp)) {
2194     JS_POP_TEMP_ROOT(cx, &tvr);
2195     return JS_FALSE;
2196     }
2197    
2198     if (xdr->mode == JSXDR_DECODE) {
2199     depth = (uint16)(tmp >> 16);
2200     count = (uint16)tmp;
2201     STOBJ_SET_SLOT(obj, JSSLOT_BLOCK_DEPTH, INT_TO_JSVAL(depth));
2202     }
2203    
2204     /*
2205     * XDR the block object's properties. We know that there are 'count'
2206     * properties to XDR, stored as id/shortid pairs. We do not XDR any
2207     * non-native properties, only those that the compiler created.
2208     */
2209     sprop = NULL;
2210     ok = JS_TRUE;
2211     for (i = 0; i < count; i++) {
2212     if (xdr->mode == JSXDR_ENCODE) {
2213     /* Find a property to XDR. */
2214     do {
2215     /* If sprop is NULL, this is the first property. */
2216     sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProp;
2217     } while (!(sprop->flags & SPROP_HAS_SHORTID));
2218    
2219     JS_ASSERT(sprop->getter == js_BlockClass.getProperty);
2220     propid = sprop->id;
2221     JS_ASSERT(JSID_IS_ATOM(propid));
2222     atom = JSID_TO_ATOM(propid);
2223     shortid = sprop->shortid;
2224     JS_ASSERT(shortid >= 0);
2225     }
2226    
2227     /* XDR the real id, then the shortid. */
2228     if (!js_XDRStringAtom(xdr, &atom) ||
2229     !JS_XDRUint16(xdr, (uint16 *)&shortid)) {
2230     ok = JS_FALSE;
2231     break;
2232     }
2233    
2234     if (xdr->mode == JSXDR_DECODE) {
2235     if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom),
2236     JSVAL_VOID, NULL, NULL,
2237     JSPROP_ENUMERATE | JSPROP_PERMANENT,
2238     SPROP_HAS_SHORTID, shortid, NULL)) {
2239     ok = JS_FALSE;
2240     break;
2241     }
2242     }
2243     }
2244    
2245     JS_POP_TEMP_ROOT(cx, &tvr);
2246     return ok;
2247     }
2248    
2249     #else
2250     # define block_xdrObject NULL
2251     #endif
2252    
2253     static uint32
2254     block_reserveSlots(JSContext *cx, JSObject *obj)
2255     {
2256     return OBJ_IS_CLONED_BLOCK(obj) ? OBJ_BLOCK_COUNT(cx, obj) : 0;
2257     }
2258    
2259     JSClass js_BlockClass = {
2260     "Block",
2261     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) |
2262     JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_CACHED_PROTO(JSProto_Block),
2263     JS_PropertyStub, JS_PropertyStub, block_getProperty, block_setProperty,
2264     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
2265     NULL, NULL, NULL, NULL, block_xdrObject, NULL, NULL, block_reserveSlots
2266     };
2267    
2268     JSObject*
2269     js_InitBlockClass(JSContext *cx, JSObject* obj)
2270     {
2271     JSObject *proto;
2272    
2273     proto = JS_InitClass(cx, obj, NULL, &js_BlockClass, NULL, 0, NULL,
2274     NULL, NULL, NULL);
2275     if (!proto)
2276     return NULL;
2277    
2278     OBJ_CLEAR_PROTO(cx, proto);
2279     return proto;
2280     }
2281    
2282     JSObject *
2283     js_InitEval(JSContext *cx, JSObject *obj)
2284     {
2285     /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
2286     if (!js_DefineFunction(cx, obj, cx->runtime->atomState.evalAtom,
2287     js_obj_eval, 1, 0)) {
2288     return NULL;
2289     }
2290    
2291     return obj;
2292     }
2293    
2294     JSObject *
2295     js_InitObjectClass(JSContext *cx, JSObject *obj)
2296     {
2297     return JS_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1,
2298     object_props, object_methods, NULL,
2299     object_static_methods);
2300     }
2301    
2302     void
2303     js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
2304     JSClass *clasp)
2305     {
2306     map->nrefs = nrefs;
2307     map->ops = ops;
2308     map->freeslot = JSSLOT_FREE(clasp);
2309     }
2310    
2311     JSObjectMap *
2312     js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
2313     JSClass *clasp, JSObject *obj)
2314     {
2315     return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);
2316     }
2317    
2318     void
2319     js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)
2320     {
2321     js_DestroyScope(cx, (JSScope *)map);
2322     }
2323    
2324     JSObjectMap *
2325     js_HoldObjectMap(JSContext *cx, JSObjectMap *map)
2326     {
2327     JS_ASSERT(map->nrefs >= 0);
2328     JS_ATOMIC_INCREMENT(&map->nrefs);
2329     return map;
2330     }
2331    
2332     JSObjectMap *
2333     js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)
2334     {
2335     JS_ASSERT(map->nrefs > 0);
2336     JS_ATOMIC_DECREMENT(&map->nrefs);
2337     if (map->nrefs == 0) {
2338     map->ops->destroyObjectMap(cx, map);
2339     return NULL;
2340     }
2341     if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)
2342     ((JSScope *)map)->object = NULL;
2343     return map;
2344     }
2345    
2346     static void
2347     FreeSlots(JSContext *cx, JSObject *obj)
2348     {
2349     if (obj->dslots) {
2350     JS_ASSERT((uint32)obj->dslots[-1] > JS_INITIAL_NSLOTS);
2351     JS_free(cx, obj->dslots - 1);
2352     obj->dslots = NULL;
2353     }
2354     }
2355    
2356     #define SLOTS_TO_DYNAMIC_WORDS(nslots) \
2357     (JS_ASSERT((nslots) > JS_INITIAL_NSLOTS), (nslots) + 1 - JS_INITIAL_NSLOTS)
2358    
2359     #define DYNAMIC_WORDS_TO_SLOTS(words) \
2360     (JS_ASSERT((words) > 1), (words) - 1 + JS_INITIAL_NSLOTS)
2361    
2362     JSBool
2363     js_ReallocSlots(JSContext *cx, JSObject *obj, uint32 nslots,
2364     JSBool exactAllocation)
2365     {
2366     jsval *old, *slots;
2367     uint32 oslots, nwords, owords, log, i;
2368    
2369     /*
2370     * Minimal number of dynamic slots to allocate.
2371     */
2372     #define MIN_DYNAMIC_WORDS 4
2373    
2374     /*
2375     * The limit to switch to linear allocation strategy from the power of 2
2376     * growth no to waste too much memory.
2377     */
2378     #define LINEAR_GROWTH_STEP JS_BIT(16)
2379    
2380     old = obj->dslots;
2381     if (nslots <= JS_INITIAL_NSLOTS) {
2382     if (old &&
2383     (exactAllocation ||
2384     SLOTS_TO_DYNAMIC_WORDS((uint32)old[-1]) != MIN_DYNAMIC_WORDS ||
2385     nslots <= (JS_INITIAL_NSLOTS +
2386     JSSLOT_FREE(STOBJ_GET_CLASS(obj))) / 2)) {
2387     /*
2388     * We do not want to free dynamic slots when allocation is a hint,
2389     * we reached minimal allocation and almost all fixed slots are
2390     * used. It avoids allocating dynamic slots again when properties
2391     * are added to the object.
2392     *
2393     * If there were no private or reserved slots, the condition to
2394     * free the slots would be
2395     *
2396     * nslots <= JS_INITIAL_NSLOTS / 2
2397     *
2398     * but to account for never removed slots before JSSLOT_FREE(class)
2399     * we need to subtract it from the slot counts which gives
2400     *
2401     * nslots - JSSLOT_FREE <= (JS_INITIAL_NSLOTS - JSSLOT_FREE) / 2
2402     *
2403     * or
2404     *
2405     * nslots <= (JS_INITIAL_NSLOTS + JSSLOT_FREE) / 2
2406     */
2407     FreeSlots(cx, obj);
2408     }
2409     return JS_TRUE;
2410     }
2411    
2412     oslots = (old) ? (uint32)*--old : JS_INITIAL_NSLOTS;
2413     nwords = SLOTS_TO_DYNAMIC_WORDS(nslots);
2414    
2415     if (nslots > oslots) {
2416     if (!exactAllocation) {
2417     /*
2418     * Round up nslots so the number of bytes in dslots array is power
2419     * of 2 to ensure exponential grouth.
2420     */
2421     if (nwords <= MIN_DYNAMIC_WORDS) {
2422     nwords = MIN_DYNAMIC_WORDS;
2423     } else if (nwords < LINEAR_GROWTH_STEP) {
2424     JS_CEILING_LOG2(log, nwords);
2425     nwords = JS_BIT(log);
2426     } else {
2427     nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP);
2428     }
2429     }
2430     slots = (jsval *)JS_realloc(cx, old, nwords * sizeof(jsval));
2431     if (!slots)
2432     return JS_FALSE;
2433     } else {
2434     JS_ASSERT(nslots < oslots);
2435     if (!exactAllocation) {
2436     owords = DYNAMIC_WORDS_TO_SLOTS(oslots);
2437     if (owords <= MIN_DYNAMIC_WORDS)
2438     return JS_TRUE;
2439     if (owords < LINEAR_GROWTH_STEP * 2) {
2440     /*
2441     * Shrink only if 1/4 of slots are left and we need to grow
2442     * the array at least twice to reach the current capacity. It
2443     * prevents frequent capacity growth/shrinking when slots are
2444     * often removed and added.
2445     */
2446     if (nwords > owords / 4)
2447     return JS_TRUE;
2448     JS_CEILING_LOG2(log, nwords);
2449     nwords = JS_BIT(log);
2450     if (nwords < MIN_DYNAMIC_WORDS)
2451     nwords = MIN_DYNAMIC_WORDS;
2452     } else {
2453     /*
2454     * Shrink only if we free at least 2 linear allocation
2455     * segments, to prevent growth/shrinking resonance.
2456     */
2457     if (nwords > owords - LINEAR_GROWTH_STEP * 2)
2458     return JS_TRUE;
2459     nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP);
2460     }
2461     }
2462    
2463     /* We avoid JS_realloc not to report a failed shrink attempt. */
2464     slots = (jsval *)realloc(old, nwords * sizeof(jsval));
2465     if (!slots)
2466     slots = old;
2467     }
2468    
2469     nslots = DYNAMIC_WORDS_TO_SLOTS(nwords);
2470     *slots++ = (jsval)nslots;
2471     obj->dslots = slots;
2472    
2473     /* If we're extending an allocation, initialize free slots. */
2474     for (i = oslots; i < nslots; i++)
2475     slots[i - JS_INITIAL_NSLOTS] = JSVAL_VOID;
2476    
2477     return JS_TRUE;
2478    
2479     #undef LINEAR_GROWTH_STEP
2480     #undef MIN_DYNAMIC_WORDS
2481     }
2482    
2483     extern JSBool
2484     js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp)
2485     {
2486     JSProtoKey key;
2487     JSAtom *atom;
2488    
2489     key = JSCLASS_CACHED_PROTO_KEY(clasp);
2490     if (key != JSProto_Null) {
2491     *idp = INT_TO_JSID(key);
2492     } else if (clasp->flags & JSCLASS_IS_ANONYMOUS) {
2493     *idp = INT_TO_JSID(JSProto_Object);
2494     } else {
2495     atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
2496     if (!atom)
2497     return JS_FALSE;
2498     *idp = ATOM_TO_JSID(atom);
2499     }
2500     return JS_TRUE;
2501     }
2502    
2503     JSObject *
2504     js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent,
2505     uintN objectSize)
2506     {
2507     jsid id;
2508    
2509     /* Bootstrap the ur-object, and make it the default prototype object. */
2510     if (!proto) {
2511     if (!js_GetClassId(cx, clasp, &id))
2512     return NULL;
2513     if (!js_GetClassPrototype(cx, parent, id, &proto))
2514     return NULL;
2515     if (!proto &&
2516     !js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object),
2517     &proto)) {
2518     return NULL;
2519     }
2520     }
2521    
2522     return js_NewObjectWithGivenProto(cx, clasp, proto, parent, objectSize);
2523     }
2524    
2525     JSObject *
2526     js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto,
2527     JSObject *parent, uintN objectSize)
2528     {
2529     JSObject *obj;
2530     JSObjectOps *ops;
2531     JSObjectMap *map;
2532     JSClass *protoclasp;
2533     uint32 nslots, i;
2534     JSTempValueRooter tvr;
2535    
2536     #ifdef INCLUDE_MOZILLA_DTRACE
2537     if (JAVASCRIPT_OBJECT_CREATE_START_ENABLED())
2538     jsdtrace_object_create_start(cx->fp, clasp);
2539     #endif
2540    
2541     /* Currently only functions can have non-standard allocation size. */
2542     if (clasp == &js_FunctionClass) {
2543     if (objectSize == 0)
2544     objectSize = sizeof(JSFunction);
2545     else
2546     JS_ASSERT(objectSize == sizeof(JSObject));
2547     } else {
2548     JS_ASSERT(objectSize == 0);
2549     objectSize = sizeof(JSObject);
2550     }
2551    
2552     /*
2553     * Allocate an object from the GC heap and initialize all its fields before
2554     * doing any operation that can potentially trigger GC.
2555     */
2556     obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, objectSize);
2557     if (!obj)
2558     goto earlybad;
2559    
2560     obj->map = NULL;
2561     obj->dslots = NULL;
2562    
2563     /*
2564     * Set the class slot with the initial value of the system and delegate
2565     * flags set to false.
2566     */
2567     JS_ASSERT(((jsuword) clasp & 3) == 0);
2568     obj->classword = jsuword(clasp);
2569     JS_ASSERT(!STOBJ_IS_DELEGATE(obj));
2570     JS_ASSERT(!STOBJ_IS_SYSTEM(obj));
2571    
2572     /* Set the proto and parent properties. */
2573     STOBJ_SET_PROTO(obj, proto);
2574     STOBJ_SET_PARENT(obj, parent);
2575    
2576     /* Initialize the remaining fixed slots. */
2577     for (i = JSSLOT_PRIVATE; i != JS_INITIAL_NSLOTS; ++i)
2578     obj->fslots[i] = JSVAL_VOID;
2579    
2580