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

Annotation of /trunk/js/jsobj.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 399 - (hide annotations)
Tue Dec 9 03:37:47 2008 UTC (11 years, 9 months ago) by siliconforks
File size: 174575 byte(s)
Use SpiderMonkey from Firefox 3.1b2.

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