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

Annotation of /trunk/js/jsobj.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 460 - (hide annotations)
Sat Sep 26 23:15:22 2009 UTC (10 years ago) by siliconforks
File size: 197148 byte(s)
Upgrade to SpiderMonkey from Firefox 3.5.3.

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