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

Contents of /trunk/js/jsobj.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (show annotations)
Sun Jan 10 07:23:34 2010 UTC (9 years, 11 months ago) by siliconforks
File size: 196791 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=79:
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 <stdlib.h>
45 #include <string.h>
46 #include "jstypes.h"
47 #include "jsstdint.h"
48 #include "jsarena.h" /* Added by JSIFY */
49 #include "jsbit.h"
50 #include "jsutil.h" /* Added by JSIFY */
51 #include "jshash.h" /* Added by JSIFY */
52 #include "jsdhash.h"
53 #include "jsprf.h"
54 #include "jsapi.h"
55 #include "jsarray.h"
56 #include "jsatom.h"
57 #include "jsbool.h"
58 #include "jsbuiltins.h"
59 #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 "jsscriptinlines.h"
73 #include "jsstaticcheck.h"
74 #include "jsstr.h"
75 #include "jstracer.h"
76 #include "jsdbgapi.h"
77
78 #if JS_HAS_GENERATORS
79 #include "jsiter.h"
80 #endif
81
82 #if JS_HAS_XML_SUPPORT
83 #include "jsxml.h"
84 #endif
85
86 #if JS_HAS_XDR
87 #include "jsxdrapi.h"
88 #endif
89
90 #ifdef INCLUDE_MOZILLA_DTRACE
91 #include "jsdtracef.h"
92 #endif
93
94 #include "jsatominlines.h"
95 #include "jsobjinlines.h"
96 #include "jsscriptinlines.h"
97
98 #include "jsautooplen.h"
99
100 #ifdef JS_THREADSAFE
101 #define NATIVE_DROP_PROPERTY js_DropProperty
102
103 extern void
104 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
105 #else
106 #define NATIVE_DROP_PROPERTY NULL
107 #endif
108
109 JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
110 NULL,
111 js_LookupProperty, js_DefineProperty,
112 js_GetProperty, js_SetProperty,
113 js_GetAttributes, js_SetAttributes,
114 js_DeleteProperty, js_DefaultValue,
115 js_Enumerate, js_CheckAccess,
116 NULL, NATIVE_DROP_PROPERTY,
117 js_Call, js_Construct,
118 js_HasInstance, js_TraceObject,
119 js_Clear
120 };
121
122 JSClass js_ObjectClass = {
123 js_Object_str,
124 JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
125 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
126 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
127 JSCLASS_NO_OPTIONAL_MEMBERS
128 };
129
130 #if JS_HAS_OBJ_PROTO_PROP
131
132 static JSBool
133 obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
134
135 static JSBool
136 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
137
138 static JSBool
139 obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
140
141 static JSPropertySpec object_props[] = {
142 /* These two must come first; see object_props[slot].name usage below. */
143 {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED,
144 obj_getSlot, obj_setSlot},
145 {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
146 obj_getSlot, obj_setSlot},
147 {js_count_str, 0, JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
148 obj_getCount, NULL},
149 {0,0,0,0,0}
150 };
151
152 /* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
153 #define JSSLOT_COUNT 2
154
155 static JSBool
156 ReportStrictSlot(JSContext *cx, uint32 slot)
157 {
158 if (slot == JSSLOT_PROTO)
159 return JS_TRUE;
160 return JS_ReportErrorFlagsAndNumber(cx,
161 JSREPORT_WARNING | JSREPORT_STRICT,
162 js_GetErrorMessage, NULL,
163 JSMSG_DEPRECATED_USAGE,
164 object_props[slot].name);
165 }
166
167 static JSBool
168 obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
169 {
170 uint32 slot;
171 jsid propid;
172 JSAccessMode mode;
173 uintN attrs;
174 JSObject *pobj;
175 JSClass *clasp;
176
177 slot = (uint32) JSVAL_TO_INT(id);
178 if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
179 propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
180 mode = JSACC_PROTO;
181 } else {
182 propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
183 mode = JSACC_PARENT;
184 }
185
186 /* Let obj->checkAccess get the slot's value, based on the access mode. */
187 if (!obj->checkAccess(cx, propid, mode, vp, &attrs))
188 return JS_FALSE;
189
190 pobj = JSVAL_TO_OBJECT(*vp);
191 if (pobj) {
192 clasp = OBJ_GET_CLASS(cx, pobj);
193 if (clasp == &js_CallClass || clasp == &js_BlockClass) {
194 /* Censor activations and lexical scopes per ECMA-262. */
195 *vp = JSVAL_NULL;
196 } else {
197 /*
198 * DeclEnv only exists as a parent for a Call object which we
199 * censor. So it cannot escape to scripts.
200 */
201 JS_ASSERT(clasp != &js_DeclEnvClass);
202 if (pobj->map->ops->thisObject) {
203 pobj = pobj->map->ops->thisObject(cx, pobj);
204 if (!pobj)
205 return JS_FALSE;
206 *vp = OBJECT_TO_JSVAL(pobj);
207 }
208 }
209 }
210 return JS_TRUE;
211 }
212
213 static JSBool
214 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
215 {
216 JSObject *pobj;
217 uint32 slot;
218 jsid propid;
219 uintN attrs;
220
221 if (!JSVAL_IS_OBJECT(*vp))
222 return JS_TRUE;
223 pobj = JSVAL_TO_OBJECT(*vp);
224
225 if (pobj) {
226 /*
227 * Innerize pobj here to avoid sticking unwanted properties on the
228 * outer object. This ensures that any with statements only grant
229 * access to the inner object.
230 */
231 OBJ_TO_INNER_OBJECT(cx, pobj);
232 if (!pobj)
233 return JS_FALSE;
234 }
235 slot = (uint32) JSVAL_TO_INT(id);
236 if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
237 return JS_FALSE;
238
239 /* __parent__ is readonly and permanent, only __proto__ may be set. */
240 propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
241 if (!obj->checkAccess(cx, propid, (JSAccessMode)(JSACC_PROTO|JSACC_WRITE), vp, &attrs))
242 return JS_FALSE;
243
244 return js_SetProtoOrParent(cx, obj, slot, pobj, JS_TRUE);
245 }
246
247 static JSBool
248 obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
249 {
250 jsval iter_state;
251 jsid num_properties;
252 JSBool ok;
253
254 if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
255 return JS_FALSE;
256
257 iter_state = JSVAL_NULL;
258 JSAutoEnumStateRooter tvr(cx, obj, &iter_state);
259
260 /* Get the number of properties to enumerate. */
261 ok = obj->enumerate(cx, JSENUMERATE_INIT, &iter_state, &num_properties);
262 if (!ok)
263 goto out;
264
265 if (!JSVAL_IS_INT(num_properties)) {
266 JS_ASSERT(0);
267 *vp = JSVAL_ZERO;
268 goto out;
269 }
270 *vp = num_properties;
271
272 out:
273 if (!JSVAL_IS_NULL(iter_state))
274 ok &= obj->enumerate(cx, JSENUMERATE_DESTROY, &iter_state, 0);
275 return ok;
276 }
277
278 #else /* !JS_HAS_OBJ_PROTO_PROP */
279
280 #define object_props NULL
281
282 #endif /* !JS_HAS_OBJ_PROTO_PROP */
283
284 JSBool
285 js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj,
286 JSBool checkForCycles)
287 {
288 JS_ASSERT(slot == JSSLOT_PARENT || slot == JSSLOT_PROTO);
289 JS_ASSERT_IF(!checkForCycles, obj != pobj);
290
291 if (slot == JSSLOT_PROTO) {
292 if (OBJ_IS_NATIVE(obj)) {
293 JS_LOCK_OBJ(cx, obj);
294 bool ok = !!js_GetMutableScope(cx, obj);
295 JS_UNLOCK_OBJ(cx, obj);
296 if (!ok)
297 return JS_FALSE;
298 }
299
300 /*
301 * Regenerate property cache shape ids for all of the scopes along the
302 * old prototype chain to invalidate their property cache entries, in
303 * case any entries were filled by looking up through obj.
304 */
305 JSObject *oldproto = obj;
306 while (oldproto && OBJ_IS_NATIVE(oldproto)) {
307 JS_LOCK_OBJ(cx, oldproto);
308 JSScope *scope = OBJ_SCOPE(oldproto);
309 scope->protoShapeChange(cx);
310 JSObject *tmp = STOBJ_GET_PROTO(oldproto);
311 JS_UNLOCK_OBJ(cx, oldproto);
312 oldproto = tmp;
313 }
314 }
315
316 if (!pobj || !checkForCycles) {
317 if (slot == JSSLOT_PROTO)
318 obj->setProto(pobj);
319 else
320 obj->setParent(pobj);
321 } else {
322 /*
323 * Use the GC machinery to serialize access to all objects on the
324 * prototype or parent chain.
325 */
326 JSSetSlotRequest ssr;
327 ssr.obj = obj;
328 ssr.pobj = pobj;
329 ssr.slot = (uint16) slot;
330 ssr.cycle = false;
331
332 JSRuntime *rt = cx->runtime;
333 JS_LOCK_GC(rt);
334 ssr.next = rt->setSlotRequests;
335 rt->setSlotRequests = &ssr;
336 for (;;) {
337 js_GC(cx, GC_SET_SLOT_REQUEST);
338 JS_UNLOCK_GC(rt);
339 if (!rt->setSlotRequests)
340 break;
341 JS_LOCK_GC(rt);
342 }
343
344 if (ssr.cycle) {
345 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
346 JSMSG_CYCLIC_VALUE,
347 #if JS_HAS_OBJ_PROTO_PROP
348 object_props[slot].name
349 #else
350 (slot == JSSLOT_PROTO) ? js_proto_str
351 : js_parent_str
352 #endif
353 );
354 return JS_FALSE;
355 }
356 }
357 return JS_TRUE;
358 }
359
360 static JSHashNumber
361 js_hash_object(const void *key)
362 {
363 return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS;
364 }
365
366 static JSHashEntry *
367 MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
368 {
369 JSSharpObjectMap *map;
370 JSHashTable *table;
371 JSHashNumber hash;
372 JSHashEntry **hep, *he;
373 jsatomid sharpid;
374 JSIdArray *ida;
375 JSBool ok;
376 jsint i, length;
377 jsid id;
378 #if JS_HAS_GETTER_SETTER
379 JSObject *obj2;
380 JSProperty *prop;
381 uintN attrs;
382 #endif
383 jsval val;
384
385 JS_CHECK_RECURSION(cx, return NULL);
386
387 map = &cx->sharpObjectMap;
388 JS_ASSERT(map->depth >= 1);
389 table = map->table;
390 hash = js_hash_object(obj);
391 hep = JS_HashTableRawLookup(table, hash, obj);
392 he = *hep;
393 if (!he) {
394 sharpid = 0;
395 he = JS_HashTableRawAdd(table, hep, hash, obj,
396 JS_UINT32_TO_PTR(sharpid));
397 if (!he) {
398 JS_ReportOutOfMemory(cx);
399 return NULL;
400 }
401
402 ida = JS_Enumerate(cx, obj);
403 if (!ida)
404 return NULL;
405
406 ok = JS_TRUE;
407 for (i = 0, length = ida->length; i < length; i++) {
408 id = ida->vector[i];
409 #if JS_HAS_GETTER_SETTER
410 ok = obj->lookupProperty(cx, id, &obj2, &prop);
411 if (!ok)
412 break;
413 if (!prop)
414 continue;
415 ok = obj2->getAttributes(cx, id, prop, &attrs);
416 if (ok) {
417 if (OBJ_IS_NATIVE(obj2) &&
418 (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
419 JSScopeProperty *sprop = (JSScopeProperty *) prop;
420 val = JSVAL_NULL;
421 if (attrs & JSPROP_GETTER)
422 val = js_CastAsObjectJSVal(sprop->getter);
423 if (attrs & JSPROP_SETTER) {
424 if (val != JSVAL_NULL) {
425 /* Mark the getter, then set val to setter. */
426 ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
427 NULL)
428 != NULL);
429 }
430 val = js_CastAsObjectJSVal(sprop->setter);
431 }
432 } else {
433 ok = obj->getProperty(cx, id, &val);
434 }
435 }
436 obj2->dropProperty(cx, prop);
437 #else
438 ok = obj->getProperty(cx, id, &val);
439 #endif
440 if (!ok)
441 break;
442 if (!JSVAL_IS_PRIMITIVE(val) &&
443 !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
444 ok = JS_FALSE;
445 break;
446 }
447 }
448 if (!ok || !idap)
449 JS_DestroyIdArray(cx, ida);
450 if (!ok)
451 return NULL;
452 } else {
453 sharpid = JS_PTR_TO_UINT32(he->value);
454 if (sharpid == 0) {
455 sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
456 he->value = JS_UINT32_TO_PTR(sharpid);
457 }
458 ida = NULL;
459 }
460 if (idap)
461 *idap = ida;
462 return he;
463 }
464
465 JSHashEntry *
466 js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
467 jschar **sp)
468 {
469 JSSharpObjectMap *map;
470 JSHashTable *table;
471 JSIdArray *ida;
472 JSHashNumber hash;
473 JSHashEntry *he, **hep;
474 jsatomid sharpid;
475 char buf[20];
476 size_t len;
477
478 if (!JS_CHECK_OPERATION_LIMIT(cx))
479 return NULL;
480
481 /* Set to null in case we return an early error. */
482 *sp = NULL;
483 map = &cx->sharpObjectMap;
484 table = map->table;
485 if (!table) {
486 table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
487 JS_CompareValues, NULL, NULL);
488 if (!table) {
489 JS_ReportOutOfMemory(cx);
490 return NULL;
491 }
492 map->table = table;
493 JS_KEEP_ATOMS(cx->runtime);
494 }
495
496 /* From this point the control must flow either through out: or bad:. */
497 ida = NULL;
498 if (map->depth == 0) {
499 /*
500 * Although MarkSharpObjects tries to avoid invoking getters,
501 * it ends up doing so anyway under some circumstances; for
502 * example, if a wrapped object has getters, the wrapper will
503 * prevent MarkSharpObjects from recognizing them as such.
504 * This could lead to js_LeaveSharpObject being called while
505 * MarkSharpObjects is still working.
506 *
507 * Increment map->depth while we call MarkSharpObjects, to
508 * ensure that such a call doesn't free the hash table we're
509 * still using.
510 */
511 ++map->depth;
512 he = MarkSharpObjects(cx, obj, &ida);
513 --map->depth;
514 if (!he)
515 goto bad;
516 JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0);
517 if (!idap) {
518 JS_DestroyIdArray(cx, ida);
519 ida = NULL;
520 }
521 } else {
522 hash = js_hash_object(obj);
523 hep = JS_HashTableRawLookup(table, hash, obj);
524 he = *hep;
525
526 /*
527 * It's possible that the value of a property has changed from the
528 * first time the object's properties are traversed (when the property
529 * ids are entered into the hash table) to the second (when they are
530 * converted to strings), i.e., the JSObject::getProperty() call is not
531 * idempotent.
532 */
533 if (!he) {
534 he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
535 if (!he) {
536 JS_ReportOutOfMemory(cx);
537 goto bad;
538 }
539 sharpid = 0;
540 goto out;
541 }
542 }
543
544 sharpid = JS_PTR_TO_UINT32(he->value);
545 if (sharpid != 0) {
546 len = JS_snprintf(buf, sizeof buf, "#%u%c",
547 sharpid >> SHARP_ID_SHIFT,
548 (sharpid & SHARP_BIT) ? '#' : '=');
549 *sp = js_InflateString(cx, buf, &len);
550 if (!*sp) {
551 if (ida)
552 JS_DestroyIdArray(cx, ida);
553 goto bad;
554 }
555 }
556
557 out:
558 JS_ASSERT(he);
559 if ((sharpid & SHARP_BIT) == 0) {
560 if (idap && !ida) {
561 ida = JS_Enumerate(cx, obj);
562 if (!ida) {
563 if (*sp) {
564 cx->free(*sp);
565 *sp = NULL;
566 }
567 goto bad;
568 }
569 }
570 map->depth++;
571 }
572
573 if (idap)
574 *idap = ida;
575 return he;
576
577 bad:
578 /* Clean up the sharpObjectMap table on outermost error. */
579 if (map->depth == 0) {
580 JS_UNKEEP_ATOMS(cx->runtime);
581 map->sharpgen = 0;
582 JS_HashTableDestroy(map->table);
583 map->table = NULL;
584 }
585 return NULL;
586 }
587
588 void
589 js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
590 {
591 JSSharpObjectMap *map;
592 JSIdArray *ida;
593
594 map = &cx->sharpObjectMap;
595 JS_ASSERT(map->depth > 0);
596 if (--map->depth == 0) {
597 JS_UNKEEP_ATOMS(cx->runtime);
598 map->sharpgen = 0;
599 JS_HashTableDestroy(map->table);
600 map->table = NULL;
601 }
602 if (idap) {
603 ida = *idap;
604 if (ida) {
605 JS_DestroyIdArray(cx, ida);
606 *idap = NULL;
607 }
608 }
609 }
610
611 static intN
612 gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
613 {
614 JS_CALL_OBJECT_TRACER((JSTracer *)arg, (JSObject *)he->key,
615 "sharp table entry");
616 return JS_DHASH_NEXT;
617 }
618
619 void
620 js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
621 {
622 JS_ASSERT(map->depth > 0);
623 JS_ASSERT(map->table);
624
625 /*
626 * During recursive calls to MarkSharpObjects a non-native object or
627 * object with a custom getProperty method can potentially return an
628 * unrooted value or even cut from the object graph an argument of one of
629 * MarkSharpObjects recursive invocations. So we must protect map->table
630 * entries against GC.
631 *
632 * We can not simply use JSTempValueRooter to mark the obj argument of
633 * MarkSharpObjects during recursion as we have to protect *all* entries
634 * in JSSharpObjectMap including those that contains otherwise unreachable
635 * objects just allocated through custom getProperty. Otherwise newer
636 * allocations can re-use the address of an object stored in the hashtable
637 * confusing js_EnterSharpObject. So to address the problem we simply
638 * mark all objects from map->table.
639 *
640 * An alternative "proper" solution is to use JSTempValueRooter in
641 * MarkSharpObjects with code to remove during finalization entries
642 * with otherwise unreachable objects. But this is way too complex
643 * to justify spending efforts.
644 */
645 JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, trc);
646 }
647
648 #if JS_HAS_TOSOURCE
649 static JSBool
650 obj_toSource(JSContext *cx, uintN argc, jsval *vp)
651 {
652 JSBool ok, outermost;
653 JSObject *obj;
654 JSHashEntry *he;
655 JSIdArray *ida;
656 jschar *chars, *ochars, *vsharp;
657 const jschar *idstrchars, *vchars;
658 size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
659 const char *comma;
660 jsint i, j, length, valcnt;
661 jsid id;
662 #if JS_HAS_GETTER_SETTER
663 JSObject *obj2;
664 JSProperty *prop;
665 uintN attrs;
666 #endif
667 jsval *val;
668 jsval localroot[4] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL, JSVAL_NULL};
669 JSTempValueRooter tvr;
670 JSString *gsopold[2];
671 JSString *gsop[2];
672 JSString *idstr, *valstr, *str;
673
674 JS_CHECK_RECURSION(cx, return JS_FALSE);
675
676 MUST_FLOW_THROUGH("out");
677 JS_PUSH_TEMP_ROOT(cx, 4, localroot, &tvr);
678
679 /* If outermost, we need parentheses to be an expression, not a block. */
680 outermost = (cx->sharpObjectMap.depth == 0);
681 obj = JS_THIS_OBJECT(cx, vp);
682 if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) {
683 ok = JS_FALSE;
684 goto out;
685 }
686 if (IS_SHARP(he)) {
687 /*
688 * We didn't enter -- obj is already "sharp", meaning we've visited it
689 * already in our depth first search, and therefore chars contains a
690 * string of the form "#n#".
691 */
692 JS_ASSERT(!ida);
693 #if JS_HAS_SHARP_VARS
694 nchars = js_strlen(chars);
695 #else
696 chars[0] = '{';
697 chars[1] = '}';
698 chars[2] = 0;
699 nchars = 2;
700 #endif
701 goto make_string;
702 }
703 JS_ASSERT(ida);
704 ok = JS_TRUE;
705
706 if (!chars) {
707 /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
708 chars = (jschar *) js_malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
709 nchars = 0;
710 if (!chars)
711 goto error;
712 if (outermost)
713 chars[nchars++] = '(';
714 } else {
715 /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
716 MAKE_SHARP(he);
717 nchars = js_strlen(chars);
718 chars = (jschar *)
719 js_realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
720 if (!chars) {
721 js_free(ochars);
722 goto error;
723 }
724 if (outermost) {
725 /*
726 * No need for parentheses around the whole shebang, because #n=
727 * unambiguously begins an object initializer, and never a block
728 * statement.
729 */
730 outermost = JS_FALSE;
731 }
732 }
733
734 chars[nchars++] = '{';
735
736 comma = NULL;
737
738 /*
739 * We have four local roots for cooked and raw value GC safety. Hoist the
740 * "localroot + 2" out of the loop using the val local, which refers to
741 * the raw (unconverted, "uncooked") values.
742 */
743 val = localroot + 2;
744
745 for (i = 0, length = ida->length; i < length; i++) {
746 JSBool idIsLexicalIdentifier, needOldStyleGetterSetter;
747
748 /* Get strings for id and value and GC-root them via vp. */
749 id = ida->vector[i];
750
751 #if JS_HAS_GETTER_SETTER
752 ok = obj->lookupProperty(cx, id, &obj2, &prop);
753 if (!ok)
754 goto error;
755 #endif
756
757 /*
758 * Convert id to a jsval and then to a string. Decide early whether we
759 * prefer get/set or old getter/setter syntax.
760 */
761 idstr = js_ValueToString(cx, ID_TO_VALUE(id));
762 if (!idstr) {
763 ok = JS_FALSE;
764 obj2->dropProperty(cx, prop);
765 goto error;
766 }
767 *vp = STRING_TO_JSVAL(idstr); /* local root */
768 idIsLexicalIdentifier = js_IsIdentifier(idstr);
769 needOldStyleGetterSetter =
770 !idIsLexicalIdentifier ||
771 js_CheckKeyword(idstr->chars(), idstr->length()) != TOK_EOF;
772
773 #if JS_HAS_GETTER_SETTER
774
775 valcnt = 0;
776 if (prop) {
777 ok = obj2->getAttributes(cx, id, prop, &attrs);
778 if (!ok) {
779 obj2->dropProperty(cx, prop);
780 goto error;
781 }
782 if (OBJ_IS_NATIVE(obj2) &&
783 (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
784 JSScopeProperty *sprop = (JSScopeProperty *) prop;
785 if (attrs & JSPROP_GETTER) {
786 val[valcnt] = js_CastAsObjectJSVal(sprop->getter);
787 gsopold[valcnt] =
788 ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
789 gsop[valcnt] =
790 ATOM_TO_STRING(cx->runtime->atomState.getAtom);
791
792 valcnt++;
793 }
794 if (attrs & JSPROP_SETTER) {
795 val[valcnt] = js_CastAsObjectJSVal(sprop->setter);
796 gsopold[valcnt] =
797 ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
798 gsop[valcnt] =
799 ATOM_TO_STRING(cx->runtime->atomState.setAtom);
800
801 valcnt++;
802 }
803 } else {
804 valcnt = 1;
805 gsop[0] = NULL;
806 gsopold[0] = NULL;
807 ok = obj->getProperty(cx, id, &val[0]);
808 }
809 obj2->dropProperty(cx, prop);
810 }
811
812 #else /* !JS_HAS_GETTER_SETTER */
813
814 /*
815 * We simplify the source code at the price of minor dead code bloat in
816 * the ECMA version (for testing only, see jsversion.h). The null
817 * default values in gsop[j] suffice to disable non-ECMA getter and
818 * setter code.
819 */
820 valcnt = 1;
821 gsop[0] = NULL;
822 gsopold[0] = NULL;
823 ok = obj->getProperty(cx, id, &val[0]);
824
825 #endif /* !JS_HAS_GETTER_SETTER */
826
827 if (!ok)
828 goto error;
829
830 /*
831 * If id is a string that's not an identifier, then it needs to be
832 * quoted. Also, negative integer ids must be quoted.
833 */
834 if (JSID_IS_ATOM(id)
835 ? !idIsLexicalIdentifier
836 : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
837 idstr = js_QuoteString(cx, idstr, (jschar)'\'');
838 if (!idstr) {
839 ok = JS_FALSE;
840 goto error;
841 }
842 *vp = STRING_TO_JSVAL(idstr); /* local root */
843 }
844 idstr->getCharsAndLength(idstrchars, idstrlength);
845
846 for (j = 0; j < valcnt; j++) {
847 /* Convert val[j] to its canonical source form. */
848 valstr = js_ValueToSource(cx, val[j]);
849 if (!valstr) {
850 ok = JS_FALSE;
851 goto error;
852 }
853 localroot[j] = STRING_TO_JSVAL(valstr); /* local root */
854 valstr->getCharsAndLength(vchars, vlength);
855
856 if (vchars[0] == '#')
857 needOldStyleGetterSetter = JS_TRUE;
858
859 if (needOldStyleGetterSetter)
860 gsop[j] = gsopold[j];
861
862 /* If val[j] is a non-sharp object, consider sharpening it. */
863 vsharp = NULL;
864 vsharplength = 0;
865 #if JS_HAS_SHARP_VARS
866 if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
867 he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
868 &vsharp);
869 if (!he) {
870 ok = JS_FALSE;
871 goto error;
872 }
873 if (IS_SHARP(he)) {
874 vchars = vsharp;
875 vlength = js_strlen(vchars);
876 needOldStyleGetterSetter = JS_TRUE;
877 gsop[j] = gsopold[j];
878 } else {
879 if (vsharp) {
880 vsharplength = js_strlen(vsharp);
881 MAKE_SHARP(he);
882 needOldStyleGetterSetter = JS_TRUE;
883 gsop[j] = gsopold[j];
884 }
885 js_LeaveSharpObject(cx, NULL);
886 }
887 }
888 #endif
889
890 #ifndef OLD_GETTER_SETTER
891 /*
892 * Remove '(function ' from the beginning of valstr and ')' from the
893 * end so that we can put "get" in front of the function definition.
894 */
895 if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) &&
896 !needOldStyleGetterSetter) {
897 JSFunction *fun = JS_ValueToFunction(cx, val[j]);
898 const jschar *start = vchars;
899 const jschar *end = vchars + vlength;
900
901 uint8 parenChomp = 0;
902 if (vchars[0] == '(') {
903 vchars++;
904 parenChomp = 1;
905 }
906
907 /*
908 * Try to jump "getter" or "setter" keywords, if we suspect
909 * they might appear here. This code can be confused by people
910 * defining Function.prototype.toString, so let's be cautious.
911 */
912 if (JSFUN_GETTER_TEST(fun->flags) ||
913 JSFUN_SETTER_TEST(fun->flags)) { /* skip "getter/setter" */
914 const jschar *tmp = js_strchr_limit(vchars, ' ', end);
915 if (tmp)
916 vchars = tmp + 1;
917 }
918
919 /* Try to jump "function" keyword. */
920 if (vchars)
921 vchars = js_strchr_limit(vchars, ' ', end);
922
923 if (vchars) {
924 if (*vchars == ' ')
925 vchars++;
926 vlength = end - vchars - parenChomp;
927 } else {
928 gsop[j] = NULL;
929 vchars = start;
930 }
931 }
932 #else
933 needOldStyleGetterSetter = JS_TRUE;
934 gsop[j] = gsopold[j];
935 #endif
936
937 #define SAFE_ADD(n) \
938 JS_BEGIN_MACRO \
939 size_t n_ = (n); \
940 curlen += n_; \
941 if (curlen < n_) \
942 goto overflow; \
943 JS_END_MACRO
944
945 curlen = nchars;
946 if (comma)
947 SAFE_ADD(2);
948 SAFE_ADD(idstrlength + 1);
949 if (gsop[j])
950 SAFE_ADD(gsop[j]->length() + 1);
951 SAFE_ADD(vsharplength);
952 SAFE_ADD(vlength);
953 /* Account for the trailing null. */
954 SAFE_ADD((outermost ? 2 : 1) + 1);
955 #undef SAFE_ADD
956
957 if (curlen > (size_t)-1 / sizeof(jschar))
958 goto overflow;
959
960 /* Allocate 1 + 1 at end for closing brace and terminating 0. */
961 chars = (jschar *)
962 js_realloc((ochars = chars), curlen * sizeof(jschar));
963 if (!chars) {
964 /* Save code space on error: let JS_free ignore null vsharp. */
965 cx->free(vsharp);
966 js_free(ochars);
967 goto error;
968 }
969
970 if (comma) {
971 chars[nchars++] = comma[0];
972 chars[nchars++] = comma[1];
973 }
974 comma = ", ";
975
976 if (needOldStyleGetterSetter) {
977 js_strncpy(&chars[nchars], idstrchars, idstrlength);
978 nchars += idstrlength;
979 if (gsop[j]) {
980 chars[nchars++] = ' ';
981 gsoplength = gsop[j]->length();
982 js_strncpy(&chars[nchars], gsop[j]->chars(),
983 gsoplength);
984 nchars += gsoplength;
985 }
986 chars[nchars++] = ':';
987 } else { /* New style "decompilation" */
988 if (gsop[j]) {
989 gsoplength = gsop[j]->length();
990 js_strncpy(&chars[nchars], gsop[j]->chars(),
991 gsoplength);
992 nchars += gsoplength;
993 chars[nchars++] = ' ';
994 }
995 js_strncpy(&chars[nchars], idstrchars, idstrlength);
996 nchars += idstrlength;
997 /* Extraneous space after id here will be extracted later */
998 chars[nchars++] = gsop[j] ? ' ' : ':';
999 }
1000
1001 if (vsharplength) {
1002 js_strncpy(&chars[nchars], vsharp, vsharplength);
1003 nchars += vsharplength;
1004 }
1005 js_strncpy(&chars[nchars], vchars, vlength);
1006 nchars += vlength;
1007
1008 if (vsharp)
1009 cx->free(vsharp);
1010 }
1011 }
1012
1013 chars[nchars++] = '}';
1014 if (outermost)
1015 chars[nchars++] = ')';
1016 chars[nchars] = 0;
1017
1018 error:
1019 js_LeaveSharpObject(cx, &ida);
1020
1021 if (!ok) {
1022 if (chars)
1023 js_free(chars);
1024 goto out;
1025 }
1026
1027 if (!chars) {
1028 JS_ReportOutOfMemory(cx);
1029 ok = JS_FALSE;
1030 goto out;
1031 }
1032 make_string:
1033 str = js_NewString(cx, chars, nchars);
1034 if (!str) {
1035 js_free(chars);
1036 ok = JS_FALSE;
1037 goto out;
1038 }
1039 *vp = STRING_TO_JSVAL(str);
1040 ok = JS_TRUE;
1041 out:
1042 JS_POP_TEMP_ROOT(cx, &tvr);
1043 return ok;
1044
1045 overflow:
1046 cx->free(vsharp);
1047 js_free(chars);
1048 chars = NULL;
1049 goto error;
1050 }
1051 #endif /* JS_HAS_TOSOURCE */
1052
1053 static JSBool
1054 obj_toString(JSContext *cx, uintN argc, jsval *vp)
1055 {
1056 JSObject *obj;
1057 jschar *chars;
1058 size_t nchars;
1059 const char *clazz, *prefix;
1060 JSString *str;
1061
1062 obj = JS_THIS_OBJECT(cx, vp);
1063 if (!obj)
1064 return JS_FALSE;
1065 obj = js_GetWrappedObject(cx, obj);
1066 clazz = OBJ_GET_CLASS(cx, obj)->name;
1067 nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
1068 chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
1069 if (!chars)
1070 return JS_FALSE;
1071
1072 prefix = "[object ";
1073 nchars = 0;
1074 while ((chars[nchars] = (jschar)*prefix) != 0)
1075 nchars++, prefix++;
1076 while ((chars[nchars] = (jschar)*clazz) != 0)
1077 nchars++, clazz++;
1078 chars[nchars++] = ']';
1079 chars[nchars] = 0;
1080
1081 str = js_NewString(cx, chars, nchars);
1082 if (!str) {
1083 cx->free(chars);
1084 return JS_FALSE;
1085 }
1086 *vp = STRING_TO_JSVAL(str);
1087 return JS_TRUE;
1088 }
1089
1090 static JSBool
1091 obj_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
1092 {
1093 jsval thisv;
1094 JSString *str;
1095
1096 thisv = JS_THIS(cx, vp);
1097 if (JSVAL_IS_NULL(thisv))
1098 return JS_FALSE;
1099
1100 str = js_ValueToString(cx, thisv);
1101 if (!str)
1102 return JS_FALSE;
1103
1104 *vp = STRING_TO_JSVAL(str);
1105 return JS_TRUE;
1106 }
1107
1108 static JSBool
1109 obj_valueOf(JSContext *cx, uintN argc, jsval *vp)
1110 {
1111 *vp = JS_THIS(cx, vp);
1112 return !JSVAL_IS_NULL(*vp);
1113 }
1114
1115 #ifdef JS_TRACER
1116 static jsval FASTCALL
1117 Object_p_valueOf(JSContext* cx, JSObject* obj, JSString *hint)
1118 {
1119 return OBJECT_TO_JSVAL(obj);
1120 }
1121 #endif
1122
1123 /*
1124 * Check whether principals subsumes scopeobj's principals, and return true
1125 * if so (or if scopeobj has no principals, for backward compatibility with
1126 * the JS API, which does not require principals), and false otherwise.
1127 */
1128 JSBool
1129 js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
1130 JSPrincipals *principals, JSAtom *caller)
1131 {
1132 JSSecurityCallbacks *callbacks;
1133 JSPrincipals *scopePrincipals;
1134 const char *callerstr;
1135
1136 callbacks = JS_GetSecurityCallbacks(cx);
1137 if (callbacks && callbacks->findObjectPrincipals) {
1138 scopePrincipals = callbacks->findObjectPrincipals(cx, scopeobj);
1139 if (!principals || !scopePrincipals ||
1140 !principals->subsume(principals, scopePrincipals)) {
1141 callerstr = js_AtomToPrintableString(cx, caller);
1142 if (!callerstr)
1143 return JS_FALSE;
1144 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1145 JSMSG_BAD_INDIRECT_CALL, callerstr);
1146 return JS_FALSE;
1147 }
1148 }
1149 return JS_TRUE;
1150 }
1151
1152 JSObject *
1153 js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller)
1154 {
1155 JSClass *clasp;
1156 JSExtendedClass *xclasp;
1157 JSObject *inner;
1158
1159 if (!scopeobj)
1160 goto bad;
1161
1162 OBJ_TO_INNER_OBJECT(cx, scopeobj);
1163 if (!scopeobj)
1164 return NULL;
1165
1166 inner = scopeobj;
1167
1168 /* XXX This is an awful gross hack. */
1169 while (scopeobj) {
1170 clasp = OBJ_GET_CLASS(cx, scopeobj);
1171 if (clasp->flags & JSCLASS_IS_EXTENDED) {
1172 xclasp = (JSExtendedClass*)clasp;
1173 if (xclasp->innerObject &&
1174 xclasp->innerObject(cx, scopeobj) != scopeobj) {
1175 goto bad;
1176 }
1177 }
1178
1179 scopeobj = OBJ_GET_PARENT(cx, scopeobj);
1180 }
1181
1182 return inner;
1183
1184 bad:
1185 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1186 JSMSG_BAD_INDIRECT_CALL, caller);
1187 return NULL;
1188 }
1189
1190 const char *
1191 js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
1192 JSPrincipals *principals, uintN *linenop)
1193 {
1194 uint32 flags;
1195 #ifdef DEBUG
1196 JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
1197 #endif
1198
1199 JS_ASSERT(principals || !(callbacks && callbacks->findObjectPrincipals));
1200 flags = JS_GetScriptFilenameFlags(caller->script);
1201 if ((flags & JSFILENAME_PROTECTED) &&
1202 principals &&
1203 strcmp(principals->codebase, "[System Principal]")) {
1204 *linenop = 0;
1205 return principals->codebase;
1206 }
1207
1208 if (caller->regs && js_GetOpcode(cx, caller->script, caller->regs->pc) == JSOP_EVAL) {
1209 JS_ASSERT(js_GetOpcode(cx, caller->script, caller->regs->pc + JSOP_EVAL_LENGTH) == JSOP_LINENO);
1210 *linenop = GET_UINT16(caller->regs->pc + JSOP_EVAL_LENGTH);
1211 } else {
1212 *linenop = js_FramePCToLineNumber(cx, caller);
1213 }
1214 return caller->script->filename;
1215 }
1216
1217 #ifndef EVAL_CACHE_CHAIN_LIMIT
1218 # define EVAL_CACHE_CHAIN_LIMIT 4
1219 #endif
1220
1221 static inline JSScript **
1222 EvalCacheHash(JSContext *cx, JSString *str)
1223 {
1224 const jschar *s;
1225 size_t n;
1226 uint32 h;
1227
1228 str->getCharsAndLength(s, n);
1229 if (n > 100)
1230 n = 100;
1231 for (h = 0; n; s++, n--)
1232 h = JS_ROTATE_LEFT32(h, 4) ^ *s;
1233
1234 h *= JS_GOLDEN_RATIO;
1235 h >>= 32 - JS_EVAL_CACHE_SHIFT;
1236 return &JS_SCRIPTS_TO_GC(cx)[h];
1237 }
1238
1239 static JSBool
1240 obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1241 {
1242 JSStackFrame *fp, *caller;
1243 JSBool indirectCall;
1244 uint32 tcflags;
1245 JSPrincipals *principals;
1246 const char *file;
1247 uintN line;
1248 JSString *str;
1249 JSScript *script;
1250 JSBool ok;
1251 JSScript **bucket = NULL; /* avoid GCC warning with early decl&init */
1252 #if JS_HAS_EVAL_THIS_SCOPE
1253 JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
1254 JSObject *setCallerScopeChain = NULL;
1255 JSBool setCallerVarObj = JS_FALSE;
1256 #endif
1257
1258 fp = js_GetTopStackFrame(cx);
1259 caller = js_GetScriptedCaller(cx, fp);
1260 indirectCall = (caller && caller->regs && *caller->regs->pc != JSOP_EVAL);
1261 uintN staticLevel = caller->script->staticLevel + 1;
1262
1263 /*
1264 * This call to js_GetWrappedObject is safe because of the security checks
1265 * we do below. However, the control flow below is confusing, so we double
1266 * check. There are two cases:
1267 * - Direct call: This object is never used. So unwrapping can't hurt.
1268 * - Indirect call: If this object isn't already the scope chain (which
1269 * we're guaranteed to be allowed to access) then we do a security
1270 * check.
1271 */
1272 obj = js_GetWrappedObject(cx, obj);
1273
1274 /*
1275 * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and
1276 * calls that attempt to use a non-global object as the "with" object in
1277 * the former indirect case.
1278 */
1279 {
1280 JSObject *parent = OBJ_GET_PARENT(cx, obj);
1281 if (indirectCall || parent) {
1282 uintN flags = parent
1283 ? JSREPORT_ERROR
1284 : JSREPORT_STRICT | JSREPORT_WARNING;
1285 if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL,
1286 JSMSG_BAD_INDIRECT_CALL,
1287 js_eval_str)) {
1288 return JS_FALSE;
1289 }
1290 }
1291 }
1292
1293 if (!JSVAL_IS_STRING(argv[0])) {
1294 *rval = argv[0];
1295 return JS_TRUE;
1296 }
1297
1298 /*
1299 * If the caller is a lightweight function and doesn't have a variables
1300 * object, then we need to provide one for the compiler to stick any
1301 * declared (var) variables into.
1302 */
1303 if (caller && !caller->varobj && !js_GetCallObject(cx, caller))
1304 return JS_FALSE;
1305
1306 /* Accept an optional trailing argument that overrides the scope object. */
1307 JSObject *scopeobj = NULL;
1308 if (argc >= 2) {
1309 if (!js_ValueToObject(cx, argv[1], &scopeobj))
1310 return JS_FALSE;
1311 argv[1] = OBJECT_TO_JSVAL(scopeobj);
1312 }
1313
1314 /* From here on, control must exit through label out with ok set. */
1315 MUST_FLOW_THROUGH("out");
1316 if (!scopeobj) {
1317 #if JS_HAS_EVAL_THIS_SCOPE
1318 /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
1319 if (indirectCall) {
1320 callerScopeChain = js_GetScopeChain(cx, caller);
1321 if (!callerScopeChain) {
1322 ok = JS_FALSE;
1323 goto out;
1324 }
1325 OBJ_TO_INNER_OBJECT(cx, obj);
1326 if (!obj) {
1327 ok = JS_FALSE;
1328 goto out;
1329 }
1330 if (obj != callerScopeChain) {
1331 ok = js_CheckPrincipalsAccess(cx, obj,
1332 JS_StackFramePrincipals(cx, caller),
1333 cx->runtime->atomState.evalAtom);
1334 if (!ok)
1335 goto out;
1336
1337 scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1);
1338 if (!scopeobj) {
1339 ok = JS_FALSE;
1340 goto out;
1341 }
1342
1343 /* Set fp->scopeChain too, for the compiler. */
1344 caller->scopeChain = fp->scopeChain = scopeobj;
1345
1346 /* Remember scopeobj so we can null its private when done. */
1347 setCallerScopeChain = scopeobj;
1348 }
1349
1350 callerVarObj = caller->varobj;
1351 if (obj != callerVarObj) {
1352 /* Set fp->varobj too, for the compiler. */
1353 caller->varobj = fp->varobj = obj;
1354 setCallerVarObj = JS_TRUE;
1355 }
1356 }
1357 #endif
1358
1359 /*
1360 * Compile using caller's current scope object.
1361 *
1362 * NB: This means that native callers (who reach this point through
1363 * the C API) must use the two parameter form.
1364 */
1365 if (caller) {
1366 scopeobj = js_GetScopeChain(cx, caller);
1367 if (!scopeobj) {
1368 ok = JS_FALSE;
1369 goto out;
1370 }
1371 }
1372 } else {
1373 scopeobj = js_GetWrappedObject(cx, scopeobj);
1374 OBJ_TO_INNER_OBJECT(cx, scopeobj);
1375 if (!scopeobj) {
1376 ok = JS_FALSE;
1377 goto out;
1378 }
1379 ok = js_CheckPrincipalsAccess(cx, scopeobj,
1380 JS_StackFramePrincipals(cx, caller),
1381 cx->runtime->atomState.evalAtom);
1382 if (!ok)
1383 goto out;
1384
1385 scopeobj = js_NewWithObject(cx, scopeobj,
1386 JS_GetGlobalForObject(cx, scopeobj), -1);
1387 if (!scopeobj) {
1388 ok = JS_FALSE;
1389 goto out;
1390 }
1391 argv[1] = OBJECT_TO_JSVAL(scopeobj);
1392 }
1393
1394 /* Ensure we compile this eval with the right object in the scope chain. */
1395 scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str);
1396 if (!scopeobj) {
1397 ok = JS_FALSE;
1398 goto out;
1399 }
1400
1401 tcflags = TCF_COMPILE_N_GO;
1402 if (caller) {
1403 tcflags |= TCF_PUT_STATIC_LEVEL(staticLevel);
1404 principals = JS_EvalFramePrincipals(cx, fp, caller);
1405 file = js_ComputeFilename(cx, caller, principals, &line);
1406 } else {
1407 principals = NULL;
1408 file = NULL;
1409 line = 0;
1410 }
1411
1412 str = JSVAL_TO_STRING(argv[0]);
1413 script = NULL;
1414
1415 /* Cache local eval scripts indexed by source qualified by scope. */
1416 bucket = EvalCacheHash(cx, str);
1417 if (!indirectCall && argc == 1 && caller->fun) {
1418 uintN count = 0;
1419 JSScript **scriptp = bucket;
1420
1421 EVAL_CACHE_METER(probe);
1422 while ((script = *scriptp) != NULL) {
1423 if ((script->flags & JSSF_SAVED_CALLER_FUN) &&
1424 script->staticLevel == staticLevel &&
1425 script->version == cx->version &&
1426 (script->principals == principals ||
1427 (principals->subsume(principals, script->principals) &&
1428 script->principals->subsume(script->principals, principals)))) {
1429 /*
1430 * Get the prior (cache-filling) eval's saved caller function.
1431 * See JSCompiler::compileScript in jsparse.cpp.
1432 */
1433 JSFunction *fun;
1434 fun = script->getFunction(0);
1435
1436 if (fun == caller->fun) {
1437 /*
1438 * Get the source string passed for safekeeping in the
1439 * atom map by the prior eval to JSCompiler::compileScript.
1440 */
1441 JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]);
1442
1443 if (src == str || js_EqualStrings(src, str)) {
1444 /*
1445 * Source matches, qualify by comparing scopeobj to the
1446 * COMPILE_N_GO-memoized parent of the first literal
1447 * function or regexp object if any. If none, then this
1448 * script has no compiled-in dependencies on the prior
1449 * eval's scopeobj.
1450 */
1451 JSObjectArray *objarray = script->objects();
1452 int i = 1;
1453 if (objarray->length == 1) {
1454 if (script->regexpsOffset != 0) {
1455 objarray = script->regexps();
1456 i = 0;
1457 } else {
1458 EVAL_CACHE_METER(noscope);
1459 i = -1;
1460 }
1461 }
1462 if (i < 0 ||
1463 STOBJ_GET_PARENT(objarray->vector[i]) == scopeobj) {
1464 JS_ASSERT(staticLevel == script->staticLevel);
1465 EVAL_CACHE_METER(hit);
1466 *scriptp = script->u.nextToGC;
1467 script->u.nextToGC = NULL;
1468 break;
1469 }
1470 }
1471 }
1472 }
1473
1474 if (++count == EVAL_CACHE_CHAIN_LIMIT) {
1475 script = NULL;
1476 break;
1477 }
1478 EVAL_CACHE_METER(step);
1479 scriptp = &script->u.nextToGC;
1480 }
1481 }
1482
1483 if (!script) {
1484 script = JSCompiler::compileScript(cx, scopeobj, caller, principals, tcflags,
1485 str->chars(), str->length(),
1486 NULL, file, line, str);
1487 if (!script) {
1488 ok = JS_FALSE;
1489 goto out;
1490 }
1491 }
1492
1493 if (argc < 2) {
1494 /* Execute using caller's new scope object (might be a Call object). */
1495 if (caller)
1496 scopeobj = caller->scopeChain;
1497 }
1498
1499 /*
1500 * Belt-and-braces: check that the lesser of eval's principals and the
1501 * caller's principals has access to scopeobj.
1502 */
1503 ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
1504 cx->runtime->atomState.evalAtom);
1505 if (ok)
1506 ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
1507
1508 script->u.nextToGC = *bucket;
1509 *bucket = script;
1510 #ifdef CHECK_SCRIPT_OWNER
1511 script->owner = NULL;
1512 #endif
1513
1514 out:
1515 #if JS_HAS_EVAL_THIS_SCOPE
1516 /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
1517 if (setCallerScopeChain) {
1518 caller->scopeChain = callerScopeChain;
1519 JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass);
1520 setCallerScopeChain->setPrivate(NULL);
1521 }
1522 if (setCallerVarObj)
1523 caller->varobj = callerVarObj;
1524 #endif
1525 return ok;
1526 }
1527
1528 #if JS_HAS_OBJ_WATCHPOINT
1529
1530 static JSBool
1531 obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
1532 void *closure)
1533 {
1534 JSObject *callable;
1535 JSSecurityCallbacks *callbacks;
1536 JSStackFrame *caller;
1537 JSPrincipals *subject, *watcher;
1538 JSResolvingKey key;
1539 JSResolvingEntry *entry;
1540 uint32 generation;
1541 jsval argv[3];
1542 JSBool ok;
1543
1544 callable = (JSObject *) closure;
1545
1546 callbacks = JS_GetSecurityCallbacks(cx);
1547 if (callbacks && callbacks->findObjectPrincipals) {
1548 /* Skip over any obj_watch_* frames between us and the real subject. */
1549 caller = js_GetScriptedCaller(cx, NULL);
1550 if (caller) {
1551 /*
1552 * Only call the watch handler if the watcher is allowed to watch
1553 * the currently executing script.
1554 */
1555 watcher = callbacks->findObjectPrincipals(cx, callable);
1556 subject = JS_StackFramePrincipals(cx, caller);
1557
1558 if (watcher && subject && !watcher->subsume(watcher, subject)) {
1559 /* Silently don't call the watch handler. */
1560 return JS_TRUE;
1561 }
1562 }
1563 }
1564
1565 /* Avoid recursion on (obj, id) already being watched on cx. */
1566 key.obj = obj;
1567 key.id = id;
1568 if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1569 return JS_FALSE;
1570 if (!entry)
1571 return JS_TRUE;
1572 generation = cx->resolvingTable->generation;
1573
1574 argv[0] = id;
1575 argv[1] = old;
1576 argv[2] = *nvp;
1577 ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp);
1578 js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1579 return ok;
1580 }
1581
1582 static JSBool
1583 obj_watch(JSContext *cx, uintN argc, jsval *vp)
1584 {
1585 JSObject *callable;
1586 jsval userid, value;
1587 jsid propid;
1588 JSObject *obj;
1589 uintN attrs;
1590
1591 if (argc <= 1) {
1592 js_ReportMissingArg(cx, vp, 1);
1593 return JS_FALSE;
1594 }
1595
1596 callable = js_ValueToCallableObject(cx, &vp[3], 0);
1597 if (!callable)
1598 return JS_FALSE;
1599
1600 /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1601 userid = vp[2];
1602 if (!JS_ValueToId(cx, userid, &propid))
1603 return JS_FALSE;
1604
1605 obj = JS_THIS_OBJECT(cx, vp);
1606 if (!obj || !obj->checkAccess(cx, propid, JSACC_WATCH, &value, &attrs))
1607 return JS_FALSE;
1608 if (attrs & JSPROP_READONLY)
1609 return JS_TRUE;
1610 *vp = JSVAL_VOID;
1611
1612 if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_MakeArraySlow(cx, obj))
1613 return JS_FALSE;
1614 return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable);
1615 }
1616
1617 static JSBool
1618 obj_unwatch(JSContext *cx, uintN argc, jsval *vp)
1619 {
1620 JSObject *obj;
1621
1622 obj = JS_THIS_OBJECT(cx, vp);
1623 if (!obj)
1624 return JS_FALSE;
1625 *vp = JSVAL_VOID;
1626 return JS_ClearWatchPoint(cx, obj, argc != 0 ? vp[2] : JSVAL_VOID,
1627 NULL, NULL);
1628 }
1629
1630 #endif /* JS_HAS_OBJ_WATCHPOINT */
1631
1632 /*
1633 * Prototype and property query methods, to complement the 'in' and
1634 * 'instanceof' operators.
1635 */
1636
1637 /* Proposed ECMA 15.2.4.5. */
1638 static JSBool
1639 obj_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp)
1640 {
1641 JSObject *obj;
1642
1643 obj = JS_THIS_OBJECT(cx, vp);
1644 return obj &&
1645 js_HasOwnPropertyHelper(cx, obj->map->ops->lookupProperty, argc, vp);
1646 }
1647
1648 JSBool
1649 js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc,
1650 jsval *vp)
1651 {
1652 jsid id;
1653 JSObject *obj;
1654
1655 if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
1656 return JS_FALSE;
1657 obj = JS_THIS_OBJECT(cx, vp);
1658 return obj && js_HasOwnProperty(cx, lookup, obj, id, vp);
1659 }
1660
1661 JSBool
1662 js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
1663 jsval *vp)
1664 {
1665 JSObject *obj2;
1666 JSProperty *prop;
1667 JSScopeProperty *sprop;
1668
1669 if (!lookup(cx, obj, id, &obj2, &prop))
1670 return JS_FALSE;
1671 if (!prop) {
1672 *vp = JSVAL_FALSE;
1673 } else if (obj2 == obj) {
1674 *vp = JSVAL_TRUE;
1675 } else {
1676 JSClass *clasp;
1677 JSExtendedClass *xclasp;
1678 JSObject *outer;
1679
1680 clasp = OBJ_GET_CLASS(cx, obj2);
1681 if (!(clasp->flags & JSCLASS_IS_EXTENDED) ||
1682 !(xclasp = (JSExtendedClass *) clasp)->outerObject) {
1683 outer = NULL;
1684 } else {
1685 outer = xclasp->outerObject(cx, obj2);
1686 if (!outer)
1687 return JS_FALSE;
1688 }
1689 if (outer == obj) {
1690 *vp = JSVAL_TRUE;
1691 } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj) == clasp) {
1692 /*
1693 * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1694 * delegated property makes that property appear to be direct in
1695 * all delegating instances of the same native class. This hack
1696 * avoids bloating every function instance with its own 'length'
1697 * (AKA 'arity') property. But it must not extend across class
1698 * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1699 *
1700 * It's not really a hack, of course: a permanent property can't
1701 * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1702 * any instance, prototype or delegating". Without a slot, and
1703 * without the ability to remove and recreate (with differences)
1704 * the property, there is no way to tell whether it is directly
1705 * owned, or indirectly delegated.
1706 */
1707 sprop = (JSScopeProperty *)prop;
1708 *vp = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
1709 } else {
1710 *vp = JSVAL_FALSE;
1711 }
1712 }
1713 if (prop)
1714 obj2->dropProperty(cx, prop);
1715 return JS_TRUE;
1716 }
1717
1718 #ifdef JS_TRACER
1719 static JSBool FASTCALL
1720 Object_p_hasOwnProperty(JSContext* cx, JSObject* obj, JSString *str)
1721 {
1722 jsid id;
1723 jsval v;
1724
1725 if (!js_ValueToStringId(cx, STRING_TO_JSVAL(str), &id) ||
1726 !js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v)) {
1727 js_SetBuiltinError(cx);
1728 return JSVAL_TO_BOOLEAN(JSVAL_VOID);
1729 }
1730
1731 JS_ASSERT(JSVAL_IS_BOOLEAN(v));
1732 return JSVAL_TO_BOOLEAN(v);
1733 }
1734 #endif
1735
1736 /* Proposed ECMA 15.2.4.6. */
1737 static JSBool
1738 obj_isPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
1739 {
1740 JSBool b;
1741
1742 if (!js_IsDelegate(cx, JS_THIS_OBJECT(cx, vp),
1743 argc != 0 ? vp[2] : JSVAL_VOID, &b)) {
1744 return JS_FALSE;
1745 }
1746 *vp = BOOLEAN_TO_JSVAL(b);
1747 return JS_TRUE;
1748 }
1749
1750 /* Proposed ECMA 15.2.4.7. */
1751 static JSBool
1752 obj_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp)
1753 {
1754 jsid id;
1755 JSObject *obj;
1756
1757 if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
1758 return JS_FALSE;
1759
1760 obj = JS_THIS_OBJECT(cx, vp);
1761 return obj && js_PropertyIsEnumerable(cx, obj, id, vp);
1762 }
1763
1764 #ifdef JS_TRACER
1765 static JSBool FASTCALL
1766 Object_p_propertyIsEnumerable(JSContext* cx, JSObject* obj, JSString *str)
1767 {
1768 jsid id = ATOM_TO_JSID(STRING_TO_JSVAL(str));
1769 jsval v;
1770
1771 if (!js_PropertyIsEnumerable(cx, obj, id, &v)) {
1772 js_SetBuiltinError(cx);
1773 return JSVAL_TO_BOOLEAN(JSVAL_VOID);
1774 }
1775
1776 JS_ASSERT(JSVAL_IS_BOOLEAN(v));
1777 return JSVAL_TO_BOOLEAN(v);
1778 }
1779 #endif
1780
1781 JSBool
1782 js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1783 {
1784 JSObject *pobj;
1785 uintN attrs;
1786 JSProperty *prop;
1787 JSBool ok;
1788
1789 if (!obj->lookupProperty(cx, id, &pobj, &prop))
1790 return JS_FALSE;
1791
1792 if (!prop) {
1793 *vp = JSVAL_FALSE;
1794 return JS_TRUE;
1795 }
1796
1797 /*
1798 * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1799 * The ECMA spec really should be fixed so propertyIsEnumerable and the
1800 * for..in loop agree on whether prototype properties are enumerable,
1801 * obviously by fixing this method (not by breaking the for..in loop!).
1802 *
1803 * We check here for shared permanent prototype properties, which should
1804 * be treated as if they are local to obj. They are an implementation
1805 * technique used to satisfy ECMA requirements; users should not be able
1806 * to distinguish a shared permanent proto-property from a local one.
1807 */
1808 if (pobj != obj &&
1809 !(OBJ_IS_NATIVE(pobj) &&
1810 SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
1811 pobj->dropProperty(cx, prop);
1812 *vp = JSVAL_FALSE;
1813 return JS_TRUE;
1814 }
1815
1816 ok = pobj->getAttributes(cx, id, prop, &attrs);
1817 pobj->dropProperty(cx, prop);
1818 if (ok)
1819 *vp = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
1820 return ok;
1821 }
1822
1823 #if JS_HAS_GETTER_SETTER
1824 JS_FRIEND_API(JSBool)
1825 js_obj_defineGetter(JSContext *cx, uintN argc, jsval *vp)
1826 {
1827 jsval fval, junk;
1828 jsid id;
1829 JSObject *obj;
1830 uintN attrs;
1831
1832 if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) {
1833 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1834 JSMSG_BAD_GETTER_OR_SETTER,
1835 js_getter_str);
1836 return JS_FALSE;
1837 }
1838 fval = vp[3];
1839
1840 if (!JS_ValueToId(cx, vp[2], &id))
1841 return JS_FALSE;
1842 obj = JS_THIS_OBJECT(cx, vp);
1843 if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
1844 return JS_FALSE;
1845 /*
1846 * Getters and setters are just like watchpoints from an access
1847 * control point of view.
1848 */
1849 if (!obj->checkAccess(cx, id, JSACC_WATCH, &junk, &attrs))
1850 return JS_FALSE;
1851 *vp = JSVAL_VOID;
1852 return obj->defineProperty(cx, id, JSVAL_VOID,
1853 js_CastAsPropertyOp(JSVAL_TO_OBJECT(fval)), JS_PropertyStub,
1854 JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED);
1855 }
1856
1857 JS_FRIEND_API(JSBool)
1858 js_obj_defineSetter(JSContext *cx, uintN argc, jsval *vp)
1859 {
1860 jsval fval, junk;
1861 jsid id;
1862 JSObject *obj;
1863 uintN attrs;
1864
1865 if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) {
1866 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1867 JSMSG_BAD_GETTER_OR_SETTER,
1868 js_setter_str);
1869 return JS_FALSE;
1870 }
1871 fval = vp[3];
1872
1873 if (!JS_ValueToId(cx, vp[2], &id))
1874 return JS_FALSE;
1875 obj = JS_THIS_OBJECT(cx, vp);
1876 if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
1877 return JS_FALSE;
1878 /*
1879 * Getters and setters are just like watchpoints from an access
1880 * control point of view.
1881 */
1882 if (!obj->checkAccess(cx, id, JSACC_WATCH, &junk, &attrs))
1883 return JS_FALSE;
1884 *vp = JSVAL_VOID;
1885 return obj->defineProperty(cx, id, JSVAL_VOID,
1886 JS_PropertyStub, js_CastAsPropertyOp(JSVAL_TO_OBJECT(fval)),
1887 JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED);
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->lookupProperty(cx, 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 *vp = js_CastAsObjectJSVal(sprop->getter);
1909 }
1910 pobj->dropProperty(cx, 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->lookupProperty(cx, 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 *vp = js_CastAsObjectJSVal(sprop->setter);
1934 }
1935 pobj->dropProperty(cx, 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->checkAccess(cx, ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
1958 JSACC_PROTO, vp, &attrs);
1959 }
1960
1961 #if JS_HAS_OBJ_WATCHPOINT
1962 const char js_watch_str[] = "watch";
1963 const char js_unwatch_str[] = "unwatch";
1964 #endif
1965 const char js_hasOwnProperty_str[] = "hasOwnProperty";
1966 const char js_isPrototypeOf_str[] = "isPrototypeOf";
1967 const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
1968 #if JS_HAS_GETTER_SETTER
1969 const char js_defineGetter_str[] = "__defineGetter__";
1970 const char js_defineSetter_str[] = "__defineSetter__";
1971 const char js_lookupGetter_str[] = "__lookupGetter__";
1972 const char js_lookupSetter_str[] = "__lookupSetter__";
1973 #endif
1974
1975 JS_DEFINE_TRCINFO_1(obj_valueOf,
1976 (3, (static, JSVAL, Object_p_valueOf, CONTEXT, THIS, STRING, 0, 0)))
1977 JS_DEFINE_TRCINFO_1(obj_hasOwnProperty,
1978 (3, (static, BOOL_FAIL, Object_p_hasOwnProperty, CONTEXT, THIS, STRING, 0, 0)))
1979 JS_DEFINE_TRCINFO_1(obj_propertyIsEnumerable,
1980 (3, (static, BOOL_FAIL, Object_p_propertyIsEnumerable, CONTEXT, THIS, STRING, 0, 0)))
1981
1982 static JSFunctionSpec object_methods[] = {
1983 #if JS_HAS_TOSOURCE
1984 JS_FN(js_toSource_str, obj_toSource, 0,0),
1985 #endif
1986 JS_FN(js_toString_str, obj_toString, 0,0),
1987 JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0),
1988 JS_TN(js_valueOf_str, obj_valueOf, 0,0, &obj_valueOf_trcinfo),
1989 #if JS_HAS_OBJ_WATCHPOINT
1990 JS_FN(js_watch_str, obj_watch, 2,0),
1991 JS_FN(js_unwatch_str, obj_unwatch, 1,0),
1992 #endif
1993 JS_TN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0, &obj_hasOwnProperty_trcinfo),
1994 JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
1995 JS_TN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0, &obj_propertyIsEnumerable_trcinfo),
1996 #if JS_HAS_GETTER_SETTER
1997 JS_FN(js_defineGetter_str, js_obj_defineGetter, 2,0),
1998 JS_FN(js_defineSetter_str, js_obj_defineSetter, 2,0),
1999 JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0),
2000 JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0),
2001 #endif
2002 JS_FS_END
2003 };
2004
2005 static JSFunctionSpec object_static_methods[] = {
2006 JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
2007 JS_FS_END
2008 };
2009
2010 static bool
2011 AllocSlots(JSContext *cx, JSObject *obj, size_t nslots);
2012
2013 static inline bool
2014 InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* ops)
2015 {
2016 JS_ASSERT(OPS_IS_NATIVE(ops));
2017 JS_ASSERT(proto == OBJ_GET_PROTO(cx, obj));
2018
2019 /* Share proto's emptyScope only if obj is similar to proto. */
2020 JSClass *clasp = OBJ_GET_CLASS(cx, obj);
2021 JSScope *scope;
2022 if (proto && OBJ_IS_NATIVE(proto) &&
2023 (scope = OBJ_SCOPE(proto))->canProvideEmptyScope(ops, clasp)) {
2024 scope = scope->getEmptyScope(cx, clasp);
2025 if (!scope)
2026 goto bad;
2027 } else {
2028 scope = JSScope::create(cx, ops, clasp, obj, js_GenerateShape(cx, false));
2029 if (!scope)
2030 goto bad;
2031
2032 /* Let JSScope::create set freeslot so as to reserve slots. */
2033 JS_ASSERT(scope->freeslot >= JSSLOT_PRIVATE);
2034 if (scope->freeslot > JS_INITIAL_NSLOTS &&
2035 !AllocSlots(cx, obj, scope->freeslot)) {
2036 JSScope::destroy(cx, scope);
2037 goto bad;
2038 }
2039 }
2040 obj->map = scope;
2041 return true;
2042
2043 bad:
2044 /* The GC nulls map initially. It should still be null on error. */
2045 JS_ASSERT(!obj->map);
2046 return false;
2047 }
2048
2049 JSObject *
2050 js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto,
2051 JSObject *parent, size_t objectSize)
2052 {
2053 #ifdef INCLUDE_MOZILLA_DTRACE
2054 if (JAVASCRIPT_OBJECT_CREATE_START_ENABLED())
2055 jsdtrace_object_create_start(cx->fp, clasp);
2056 #endif
2057
2058 /* Assert that the class is a proper class. */
2059 JS_ASSERT_IF(clasp->flags & JSCLASS_IS_EXTENDED,
2060 ((JSExtendedClass *)clasp)->equality);
2061
2062 /* Always call the class's getObjectOps hook if it has one. */
2063 JSObjectOps *ops = clasp->getObjectOps
2064 ? clasp->getObjectOps(cx, clasp)
2065 : &js_ObjectOps;
2066
2067 /*
2068 * Allocate an object from the GC heap and initialize all its fields before
2069 * doing any operation that can potentially trigger GC. Functions have a
2070 * larger non-standard allocation size.
2071 */
2072 JSObject* obj;
2073 if (clasp == &js_FunctionClass && !objectSize) {
2074 obj = (JSObject*) js_NewGCFunction(cx, GCX_OBJECT);
2075 #ifdef DEBUG
2076 memset((uint8 *) obj + sizeof(JSObject), JS_FREE_PATTERN,
2077 sizeof(JSFunction) - sizeof(JSObject));
2078 #endif
2079 } else {
2080 JS_ASSERT(!objectSize || objectSize == sizeof(JSObject));
2081 obj = js_NewGCObject(cx, GCX_OBJECT);
2082 }
2083 if (!obj)
2084 goto out;
2085
2086 /*
2087 * Default parent to the parent of the prototype, which was set from
2088 * the parent of the prototype's constructor.
2089 */
2090 obj->init(clasp,
2091 proto,
2092 (!parent && proto) ? proto->getParent() : parent,
2093 JSObject::defaultPrivate(clasp));
2094
2095 if (OPS_IS_NATIVE(ops)) {
2096 if (!InitScopeForObject(cx, obj, proto, ops)) {
2097 obj = NULL;
2098 goto out;
2099 }
2100 } else {
2101 JS_ASSERT(ops->objectMap->ops == ops);
2102 obj->map = const_cast<JSObjectMap *>(ops->objectMap);
2103 }
2104
2105 /* Check that the newborn root still holds the object. */
2106 JS_ASSERT_IF(!cx->localRootStack, cx->weakRoots.newborn[GCX_OBJECT] == obj);
2107
2108 /*
2109 * Do not call debug hooks on trace, because we might be in a non-_FAIL
2110 * builtin. See bug 481444.
2111 */
2112 if (cx->debugHooks->objectHook && !JS_ON_TRACE(cx)) {
2113 JSAutoTempValueRooter tvr(cx, obj);
2114 JS_KEEP_ATOMS(cx->runtime);
2115 cx->debugHooks->objectHook(cx, obj, JS_TRUE,
2116 cx->debugHooks->objectHookData);
2117 JS_UNKEEP_ATOMS(cx->runtime);
2118 cx->weakRoots.newborn[GCX_OBJECT] = obj;
2119 }
2120
2121 out:
2122 #ifdef INCLUDE_MOZILLA_DTRACE
2123 if (JAVASCRIPT_OBJECT_CREATE_ENABLED())
2124 jsdtrace_object_create(cx, clasp, obj);
2125 if (JAVASCRIPT_OBJECT_CREATE_DONE_ENABLED())
2126 jsdtrace_object_create_done(cx->fp, clasp);
2127 #endif
2128 return obj;
2129 }
2130
2131 JSObject *
2132 js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto,
2133 JSObject *parent, size_t objectSize)
2134 {
2135 jsid id;
2136
2137 /* Bootstrap the ur-object, and make it the default prototype object. */
2138 if (!proto) {
2139 if (!js_GetClassId(cx, clasp, &id))
2140 return NULL;
2141 if (!js_GetClassPrototype(cx, parent, id, &proto))
2142 return NULL;
2143 if (!proto &&
2144 !js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object),
2145 &proto)) {
2146 return NULL;
2147 }
2148 }
2149
2150 return js_NewObjectWithGivenProto(cx, clasp, proto, parent, objectSize);
2151 }
2152
2153 JSBool
2154 js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2155 {
2156 if (argc == 0) {
2157 /* Trigger logic below to construct a blank object. */
2158 obj = NULL;
2159 } else {
2160 /* If argv[0] is null or undefined, obj comes back null. */
2161 if (!js_ValueToObject(cx, argv[0], &obj))
2162 return JS_FALSE;
2163 }
2164 if (!obj) {
2165 JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
2166 if (JS_IsConstructing(cx))
2167 return JS_TRUE;
2168 obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
2169 if (!obj)
2170 return JS_FALSE;
2171 }
2172 *rval = OBJECT_TO_JSVAL(obj);
2173 return JS_TRUE;
2174 }
2175
2176 #ifdef JS_TRACER
2177
2178 JSObject*
2179 js_NewObjectWithClassProto(JSContext *cx, JSClass *clasp, JSObject *proto,
2180 jsval privateSlotValue)
2181 {
2182 JS_ASSERT(!clasp->getObjectOps);
2183 JS_ASSERT(proto->map->ops == &js_ObjectOps);
2184
2185 JSObject* obj = js_NewGCObject(cx, GCX_OBJECT);
2186 if (!obj)
2187 return NULL;
2188
2189 obj->initSharingEmptyScope(clasp, proto, proto->getParent(), privateSlotValue);
2190 return obj;
2191 }
2192
2193 JSObject* FASTCALL
2194 js_Object_tn(JSContext* cx, JSObject* proto)
2195 {
2196 JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE));
2197 return js_NewObjectWithClassProto(cx, &js_ObjectClass, proto, JSVAL_VOID);
2198 }
2199
2200 JS_DEFINE_TRCINFO_1(js_Object,
2201 (2, (extern, CONSTRUCTOR_RETRY, js_Object_tn, CONTEXT, CALLEE_PROTOTYPE, 0, 0)))
2202
2203 static inline JSObject*
2204 NewNativeObject(JSContext* cx, JSClass* clasp, JSObject* proto,
2205 JSObject *parent, jsval privateSlotValue)
2206 {
2207 JS_ASSERT(JS_ON_TRACE(cx));
2208 JSObject* obj = js_NewGCObject(cx, GCX_OBJECT);
2209 if (!obj)
2210 return NULL;
2211
2212 obj->init(clasp, proto, parent, privateSlotValue);
2213 return InitScopeForObject(cx, obj, proto, &js_ObjectOps) ? obj : NULL;
2214 }
2215
2216 JSObject* FASTCALL
2217 js_NewInstance(JSContext *cx, JSClass *clasp, JSObject *ctor)
2218 {
2219 JS_ASSERT(HAS_FUNCTION_CLASS(ctor));
2220
2221 JSAtom *atom = cx->runtime->atomState.classPrototypeAtom;
2222
2223 JSScope *scope = OBJ_SCOPE(ctor);
2224 #ifdef JS_THREADSAFE
2225 if (scope->title.ownercx != cx)
2226 return NULL;
2227 #endif
2228 if (!scope->owned()) {
2229 scope = js_GetMutableScope(cx, ctor);
2230 if (!scope)
2231 return NULL;
2232 }
2233
2234 JSScopeProperty *sprop = scope->lookup(ATOM_TO_JSID(atom));
2235 jsval pval = sprop ? STOBJ_GET_SLOT(ctor, sprop->slot) : JSVAL_HOLE;
2236
2237 JSObject *proto;
2238 if (!JSVAL_IS_PRIMITIVE(pval)) {
2239 /* An object in ctor.prototype, let's use it as the new instance's proto. */
2240 proto = JSVAL_TO_OBJECT(pval);
2241 } else if (pval == JSVAL_HOLE) {
2242 /* No ctor.prototype yet, inline and optimize fun_resolve's prototype code. */
2243 proto = js_NewObject(cx, clasp, NULL, OBJ_GET_PARENT(cx, ctor));
2244 if (!proto)
2245 return NULL;
2246 if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT))
2247 return NULL;
2248 } else {
2249 /* Primitive value in .prototype means we use Object.prototype for proto. */
2250 if (!js_GetClassPrototype(cx, JSVAL_TO_OBJECT(ctor->fslots[JSSLOT_PARENT]),
2251 INT_TO_JSID(JSProto_Object), &proto)) {
2252 return NULL;
2253 }
2254 }
2255
2256 return NewNativeObject(cx, clasp, proto, ctor->getParent(),
2257 JSObject::defaultPrivate(clasp));
2258 }
2259
2260 JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_NewInstance, CONTEXT, CLASS, OBJECT, 0, 0)
2261
2262 #else /* !JS_TRACER */
2263
2264 # define js_Object_trcinfo NULL
2265
2266 #endif /* !JS_TRACER */
2267
2268 /*
2269 * Given pc pointing after a property accessing bytecode, return true if the
2270 * access is "object-detecting" in the sense used by web scripts, e.g., when
2271 * checking whether document.all is defined.
2272 */
2273 JS_REQUIRES_STACK JSBool
2274 Detecting(JSContext *cx, jsbytecode *pc)
2275 {
2276 JSScript *script;
2277 jsbytecode *endpc;
2278 JSOp op;
2279 JSAtom *atom;
2280
2281 script = cx->fp->script;
2282 endpc = script->code + script->length;
2283 for (;; pc += js_CodeSpec[op].length) {
2284 JS_ASSERT_IF(!cx->fp->imacpc, script->code <= pc && pc < endpc);
2285
2286 /* General case: a branch or equality op follows the access. */
2287 op = js_GetOpcode(cx, script, pc);
2288 if (js_CodeSpec[op].format & JOF_DETECTING)
2289 return JS_TRUE;
2290
2291 switch (op) {
2292 case JSOP_NULL:
2293 /*
2294 * Special case #1: handle (document.all == null). Don't sweat
2295 * about JS1.2's revision of the equality operators here.
2296 */
2297 if (++pc < endpc) {
2298 op = js_GetOpcode(cx, script, pc);
2299 return *pc == JSOP_EQ || *pc == JSOP_NE;
2300 }
2301 return JS_FALSE;
2302
2303 case JSOP_NAME:
2304 /*
2305 * Special case #2: handle (document.all == undefined). Don't
2306 * worry about someone redefining undefined, which was added by
2307 * Edition 3, so is read/write for backward compatibility.
2308 */
2309 GET_ATOM_FROM_BYTECODE(script, pc, 0, atom);
2310 if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
2311 (pc += js_CodeSpec[op].length) < endpc) {
2312 op = js_GetOpcode(cx, script, pc);
2313 return op == JSOP_EQ || op == JSOP_NE ||
2314 op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
2315 }
2316 return JS_FALSE;
2317
2318 default:
2319 /*
2320 * At this point, anything but an extended atom index prefix means
2321 * we're not detecting.
2322 */
2323 if (!(js_CodeSpec[op].format & JOF_INDEXBASE))
2324 return JS_FALSE;
2325 break;
2326 }
2327 }
2328 }
2329
2330 /*
2331 * Infer lookup flags from the currently executing bytecode. This does
2332 * not attempt to infer JSRESOLVE_WITH, because the current bytecode
2333 * does not indicate whether we are in a with statement. Return defaultFlags
2334 * if a currently executing bytecode cannot be determined.
2335 */
2336 uintN
2337 js_InferFlags(JSContext *cx, uintN defaultFlags)
2338 {
2339 #ifdef JS_TRACER
2340 if (JS_ON_TRACE(cx))
2341 return cx->bailExit->lookupFlags;
2342 #endif
2343
2344 JS_ASSERT_NOT_ON_TRACE(cx);
2345
2346 JSStackFrame *fp;
2347 jsbytecode *pc;
2348 const JSCodeSpec *cs;
2349 uint32 format;
2350 uintN flags = 0;
2351
2352 fp = js_GetTopStackFrame(cx);
2353 if (!fp || !fp->regs)
2354 return defaultFlags;
2355 pc = fp->regs->pc;
2356 cs = &js_CodeSpec[js_GetOpcode(cx, fp->script, pc)];
2357 format = cs->format;
2358 if (JOF_MODE(format) != JOF_NAME)
2359 flags |= JSRESOLVE_QUALIFIED;
2360 if ((format & (JOF_SET | JOF_FOR)) ||
2361 (fp->flags & JSFRAME_ASSIGNING)) {
2362 flags |= JSRESOLVE_ASSIGNING;
2363 } else if (cs->length >= 0) {
2364 pc += cs->length;
2365 if (pc < cx->fp->script->code + cx->fp->script->length && Detecting(cx, pc))
2366 flags |= JSRESOLVE_DETECTING;
2367 }
2368 if (format & JOF_DECLARING)
2369 flags |= JSRESOLVE_DECLARING;
2370 return flags;
2371 }
2372
2373 /*
2374 * ObjectOps and Class for with-statement stack objects.
2375 */
2376 static JSBool
2377 with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2378 JSProperty **propp)
2379 {
2380 /* Fixes bug 463997 */
2381 uintN flags = cx->resolveFlags;
2382 if (flags == JSRESOLVE_INFER)
2383 flags = js_InferFlags(cx, flags);
2384 flags |= JSRESOLVE_WITH;
2385 JSAutoResolveFlags rf(cx, flags);
2386 JSObject *proto = OBJ_GET_PROTO(cx, obj);
2387 if (!proto)
2388 return js_LookupProperty(cx, obj, id, objp, propp);
2389 return proto->lookupProperty(cx, id, objp, propp);
2390 }
2391
2392 static JSBool
2393 with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2394 {
2395 JSObject *proto = OBJ_GET_PROTO(cx, obj);
2396 if (!proto)
2397 return js_GetProperty(cx, obj, id, vp);
2398 return proto->getProperty(cx, id, vp);
2399 }
2400
2401 static JSBool
2402 with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2403 {
2404 JSObject *proto = OBJ_GET_PROTO(cx, obj);
2405 if (!proto)
2406 return js_SetProperty(cx, obj, id, vp);
2407 return proto->setProperty(cx, id, vp);
2408 }
2409
2410 static JSBool
2411 with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
2412 uintN *attrsp)
2413 {
2414 JSObject *proto = OBJ_GET_PROTO(cx, obj);
2415 if (!proto)
2416 return js_GetAttributes(cx, obj, id, prop, attrsp);
2417 return proto->getAttributes(cx, id, prop, attrsp);
2418 }
2419
2420 static JSBool
2421 with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
2422 uintN *attrsp)
2423 {
2424 JSObject *proto = OBJ_GET_PROTO(cx, obj);
2425 if (!proto)
2426 return js_SetAttributes(cx, obj, id, prop, attrsp);
2427 return proto->setAttributes(cx, id, prop, attrsp);
2428 }
2429
2430 static JSBool
2431 with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
2432 {
2433 JSObject *proto = OBJ_GET_PROTO(cx, obj);
2434 if (!proto)
2435 return js_DeleteProperty(cx, obj, id, rval);
2436 return proto->deleteProperty(cx, id, rval);
2437 }
2438
2439 static JSBool
2440 with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
2441 {
2442 JSObject *proto = OBJ_GET_PROTO(cx, obj);
2443 if (!proto)
2444 return js_DefaultValue(cx, obj, hint, vp);
2445 return proto->defaultValue(cx, hint, vp);
2446 }
2447
2448 static JSBool
2449 with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
2450 jsval *statep, jsid *idp)
2451 {
2452 JSObject *proto = OBJ_GET_PROTO(cx, obj);
2453 if (!proto)
2454 return js_Enumerate(cx, obj, enum_op, statep, idp);
2455 return proto->enumerate(cx, enum_op, statep, idp);
2456 }
2457
2458 static JSBool
2459 with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
2460 jsval *vp, uintN *attrsp)
2461 {
2462 JSObject *proto = OBJ_GET_PROTO(cx, obj);
2463 if (!proto)
2464 return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
2465 return proto->checkAccess(cx, id, mode, vp, attrsp);
2466 }
2467
2468 static JSObject *
2469 with_ThisObject(JSContext *cx, JSObject *obj)
2470 {
2471 JSObject *proto = OBJ_GET_PROTO(cx, obj);
2472 if (!proto)
2473 return obj;
2474 return proto->thisObject(cx);
2475 }
2476
2477 JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
2478 NULL,
2479 with_LookupProperty, js_DefineProperty,
2480 with_GetProperty, with_SetProperty,
2481 with_GetAttributes, with_SetAttributes,
2482 with_DeleteProperty, with_DefaultValue,
2483 with_Enumerate, with_CheckAccess,
2484 with_ThisObject, NATIVE_DROP_PROPERTY,
2485 NULL, NULL,
2486 NULL, js_TraceObject,
2487 js_Clear
2488 };
2489
2490 static JSObjectOps *
2491 with_getObjectOps(JSContext *cx, JSClass *clasp)
2492 {
2493 return &js_WithObjectOps;
2494 }
2495
2496 JSClass js_WithClass = {
2497 "With",
2498 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
2499 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
2500 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
2501 with_getObjectOps,
2502 0,0,0,0,0,0,0
2503 };
2504
2505 JS_REQUIRES_STACK JSObject *
2506 js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
2507 {
2508 JSObject *obj;
2509
2510 obj = js_NewObject(cx, &js_WithClass, proto, parent);
2511 if (!obj)
2512 return NULL;
2513 obj->setPrivate(cx->fp);
2514 OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
2515 return obj;
2516 }
2517
2518 JSObject *
2519 js_NewBlockObject(JSContext *cx)
2520 {
2521 /*
2522 * Null obj's proto slot so that Object.prototype.* does not pollute block
2523 * scopes and to give the block object its own scope.
2524 */
2525 JSObject *blockObj = js_NewObjectWithGivenProto(cx, &js_BlockClass, NULL, NULL);
2526 JS_ASSERT_IF(blockObj, !OBJ_IS_CLONED_BLOCK(blockObj));
2527 return blockObj;
2528 }
2529
2530 JSObject *
2531 js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp)
2532 {
2533 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(proto));
2534 JS_ASSERT(STOBJ_GET_CLASS(proto) == &js_BlockClass);
2535
2536 JSObject *clone = js_NewGCObject(cx, GCX_OBJECT);
2537 if (!clone)
2538 return NULL;
2539
2540 JSScope *scope = OBJ_SCOPE(proto);
2541 scope->hold();
2542 JS_ASSERT(!scope->owned());
2543 clone->map = scope;
2544
2545 clone->classword = jsuword(&js_BlockClass);
2546 clone->setProto(proto);
2547 clone->setParent(NULL); // caller's responsibility
2548 clone->setPrivate(fp);
2549 clone->fslots[JSSLOT_BLOCK_DEPTH] = proto->fslots[JSSLOT_BLOCK_DEPTH];
2550 JS_ASSERT(scope->freeslot == JSSLOT_BLOCK_DEPTH + 1);
2551 for (uint32 i = JSSLOT_BLOCK_DEPTH + 1; i < JS_INITIAL_NSLOTS; ++i)
2552 clone->fslots[i] = JSVAL_VOID;
2553 clone->dslots = NULL;
2554 JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone));
2555 return clone;
2556 }
2557
2558 JS_REQUIRES_STACK JSBool
2559 js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
2560 {
2561 JSStackFrame *fp;
2562 JSObject *obj;
2563 uintN depth, count;
2564
2565 /* Blocks have one fixed slot available for the first local.*/
2566 JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2);
2567
2568 fp = cx->fp;
2569 obj = fp->scopeChain;
2570 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
2571 JS_ASSERT(obj->getPrivate() == cx->fp);
2572 JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
2573
2574 /*
2575 * Block objects should never be exposed to scripts. Thus the clone should
2576 * not own the property map and rather always share it with the prototype
2577 * object. This allows us to skip updating OBJ_SCOPE(obj)->freeslot after
2578 * we copy the stack slots into reserved slots.
2579 */
2580 JS_ASSERT(OBJ_SCOPE(obj)->object != obj);
2581
2582 /* Block objects should not have reserved slots before they are put. */
2583 JS_ASSERT(STOBJ_NSLOTS(obj) == JS_INITIAL_NSLOTS);
2584
2585 /* The block and its locals must be on the current stack for GC safety. */
2586 depth = OBJ_BLOCK_DEPTH(cx, obj);
2587 count = OBJ_BLOCK_COUNT(cx, obj);
2588 JS_ASSERT(depth <= (size_t) (fp->regs->sp - StackBase(fp)));
2589 JS_ASSERT(count <= (size_t) (fp->regs->sp - StackBase(fp) - depth));
2590
2591 /* See comments in CheckDestructuring from jsparse.c. */
2592 JS_ASSERT(count >= 1);
2593
2594 depth += fp->script->nfixed;
2595 obj->fslots[JSSLOT_BLOCK_DEPTH + 1] = fp->slots[depth];
2596 if (normalUnwind && count > 1) {
2597 --count;
2598 JS_LOCK_OBJ(cx, obj);
2599 if (!AllocSlots(cx, obj, JS_INITIAL_NSLOTS + count))
2600 normalUnwind = JS_FALSE;
2601 else
2602 memcpy(obj->dslots, fp->slots + depth + 1, count * sizeof(jsval));
2603 JS_UNLOCK_OBJ(cx, obj);
2604 }
2605
2606 /* We must clear the private slot even with errors. */
2607 obj->setPrivate(NULL);
2608 fp->scopeChain = OBJ_GET_PARENT(cx, obj);
2609 return normalUnwind;
2610 }
2611
2612 static JSBool
2613 block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2614 {
2615 /*
2616 * Block objects are never exposed to script, and the engine handles them
2617 * with care. So unlike other getters, this one can assert (rather than
2618 * check) certain invariants about obj.
2619 */
2620 JS_ASSERT(obj->getClass() == &js_BlockClass);
2621 JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
2622 uintN index = (uintN) JSVAL_TO_INT(id);
2623 JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj));
2624
2625 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
2626 if (fp) {
2627 index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj);
2628 JS_ASSERT(index < fp->script->nslots);
2629 *vp = fp->slots[index];
2630 return true;
2631 }
2632
2633 /* Values are in reserved slots immediately following DEPTH. */
2634 uint32 slot = JSSLOT_BLOCK_DEPTH + 1 + index;
2635 JS_LOCK_OBJ(cx, obj);
2636 JS_ASSERT(slot < STOBJ_NSLOTS(obj));
2637 *vp = STOBJ_GET_SLOT(obj, slot);
2638 JS_UNLOCK_OBJ(cx, obj);
2639 return true;
2640 }
2641
2642 static JSBool
2643 block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2644 {
2645 JS_ASSERT(obj->getClass() == &js_BlockClass);
2646 JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
2647 uintN index = (uintN) JSVAL_TO_INT(id);
2648 JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj));
2649
2650 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
2651 if (fp) {
2652 index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj);
2653 JS_ASSERT(index < fp->script->nslots);
2654 fp->slots[index] = *vp;
2655 return true;
2656 }
2657
2658 /* Values are in reserved slots immediately following DEPTH. */
2659 uint32 slot = JSSLOT_BLOCK_DEPTH + 1 + index;
2660 JS_LOCK_OBJ(cx, obj);
2661 JS_ASSERT(slot < STOBJ_NSLOTS(obj));
2662 STOBJ_SET_SLOT(obj, slot, *vp);
2663 JS_UNLOCK_OBJ(cx, obj);
2664 return true;
2665 }
2666
2667 JSBool
2668 js_DefineBlockVariable(JSContext *cx, JSObject *obj, jsid id, int16 index)
2669 {
2670 JS_ASSERT(obj->getClass() == &js_BlockClass);
2671 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj));
2672
2673 /* Use JSPROP_ENUMERATE to aid the disassembler. */
2674 return js_DefineNativeProperty(cx, obj, id, JSVAL_VOID,
2675 block_getProperty, block_setProperty,
2676 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED,
2677 SPROP_HAS_SHORTID, index, NULL);
2678 }
2679
2680 #if JS_HAS_XDR
2681
2682 #define NO_PARENT_INDEX ((uint32)-1)
2683
2684 uint32
2685 FindObjectIndex(JSObjectArray *array, JSObject *obj)
2686 {
2687 size_t i;
2688
2689 if (array) {
2690 i = array->length;
2691 do {
2692
2693 if (array->vector[--i] == obj)
2694 return i;
2695 } while (i != 0);
2696 }
2697
2698 return NO_PARENT_INDEX;
2699 }
2700
2701 JSBool
2702 js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
2703 {
2704 JSContext *cx;
2705 uint32 parentId;
2706 JSObject *obj, *parent;
2707 uint16 depth, count, i;
2708 uint32 tmp;
2709 JSScopeProperty *sprop;
2710 jsid propid;
2711 JSAtom *atom;
2712 int16 shortid;
2713 JSBool ok;
2714
2715 cx = xdr->cx;
2716 #ifdef __GNUC__
2717 obj = NULL; /* quell GCC overwarning */
2718 #endif
2719
2720 if (xdr->mode == JSXDR_ENCODE) {
2721 obj = *objp;
2722 parent = OBJ_GET_PARENT(cx, obj);
2723 parentId = (xdr->script->objectsOffset == 0)
2724 ? NO_PARENT_INDEX
2725 : FindObjectIndex(xdr->script->objects(), parent);
2726 depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj);
2727 count = (uint16)OBJ_BLOCK_COUNT(cx, obj);
2728 tmp = (uint32)(depth << 16) | count;
2729 }
2730 #ifdef __GNUC__ /* suppress bogus gcc warnings */
2731 else count = 0;
2732 #endif
2733
2734 /* First, XDR the parent atomid. */
2735 if (!JS_XDRUint32(xdr, &parentId))
2736 return JS_FALSE;
2737
2738 if (xdr->mode == JSXDR_DECODE) {
2739 obj = js_NewBlockObject(cx);
2740 if (!obj)
2741 return JS_FALSE;
2742 *objp = obj;
2743
2744 /*
2745 * If there's a parent id, then get the parent out of our script's
2746 * object array. We know that we XDR block object in outer-to-inner
2747 * order, which means that getting the parent now will work.
2748 */
2749 if (parentId == NO_PARENT_INDEX)
2750 parent = NULL;
2751 else
2752 parent = xdr->script->getObject(parentId);
2753 STOBJ_SET_PARENT(obj, parent);
2754 }
2755
2756 JSAutoTempValueRooter tvr(cx, obj);
2757
2758 if (!JS_XDRUint32(xdr, &tmp))
2759 return false;
2760
2761 if (xdr->mode == JSXDR_DECODE) {
2762 depth = (uint16)(tmp >> 16);
2763 count = (uint16)tmp;
2764 STOBJ_SET_SLOT(obj, JSSLOT_BLOCK_DEPTH, INT_TO_JSVAL(depth));
2765 }
2766
2767 /*
2768 * XDR the block object's properties. We know that there are 'count'
2769 * properties to XDR, stored as id/shortid pairs. We do not XDR any
2770 * non-native properties, only those that the compiler created.
2771 */
2772 sprop = NULL;
2773 ok = JS_TRUE;
2774 for (i = 0; i < count; i++) {
2775 if (xdr->mode == JSXDR_ENCODE) {
2776 /* Find a property to XDR. */
2777 do {
2778 /* If sprop is NULL, this is the first property. */
2779 sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProp;
2780 } while (!(sprop->flags & SPROP_HAS_SHORTID));
2781
2782 JS_ASSERT(sprop->getter == block_getProperty);
2783 propid = sprop->id;
2784 JS_ASSERT(JSID_IS_ATOM(propid));
2785 atom = JSID_TO_ATOM(propid);
2786 shortid = sprop->shortid;
2787 JS_ASSERT(shortid >= 0);
2788 }
2789
2790 /* XDR the real id, then the shortid. */
2791 if (!js_XDRStringAtom(xdr, &atom) ||
2792 !JS_XDRUint16(xdr, (uint16 *)&shortid)) {
2793 return false;
2794 }
2795
2796 if (xdr->mode == JSXDR_DECODE) {
2797 if (!js_DefineBlockVariable(cx, obj, ATOM_TO_JSID(atom), shortid))
2798 return false;
2799 }
2800 }
2801
2802 if (xdr->mode == JSXDR_DECODE) {
2803 /* Do as the parser does and make this block scope shareable. */
2804 OBJ_SCOPE(obj)->object = NULL;
2805 }
2806 return true;
2807 }
2808
2809 #endif
2810
2811 static uint32
2812 block_reserveSlots(JSContext *cx, JSObject *obj)
2813 {
2814 return OBJ_IS_CLONED_BLOCK(obj) ? OBJ_BLOCK_COUNT(cx, obj) : 0;
2815 }
2816
2817 JSClass js_BlockClass = {
2818 "Block",
2819 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
2820 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
2821 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
2822 NULL, NULL, NULL, NULL, NULL, NULL, NULL, block_reserveSlots
2823 };
2824
2825 JSObject *
2826 js_InitEval(JSContext *cx, JSObject *obj)
2827 {
2828 /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
2829 if (!js_DefineFunction(cx, obj, cx->runtime->atomState.evalAtom,
2830 obj_eval, 1, 0)) {
2831 return NULL;
2832 }
2833
2834 return obj;
2835 }
2836
2837 JSObject *
2838 js_InitObjectClass(JSContext *cx, JSObject *obj)
2839 {
2840 return js_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1,
2841 object_props, object_methods, NULL, object_static_methods);
2842 }
2843
2844 JSObject *
2845 js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
2846 JSClass *clasp, JSNative constructor, uintN nargs,
2847 JSPropertySpec *ps, JSFunctionSpec *fs,
2848 JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
2849 {
2850 JSAtom *atom;
2851 JSProtoKey key;
2852 JSObject *proto, *ctor;
2853 JSTempValueRooter tvr;
2854 jsval cval, rval;
2855 JSBool named;
2856 JSFunction *fun;
2857
2858 atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
2859 if (!atom)
2860 return NULL;
2861
2862 /*
2863 * When initializing a standard class, if no parent_proto (grand-proto of
2864 * instances of the class, parent-proto of the class's prototype object)
2865 * is given, we must use Object.prototype if it is available. Otherwise,
2866 * we could look up the wrong binding for a class name in obj. Example:
2867 *
2868 * String = Array;
2869 * print("hi there".join);
2870 *
2871 * should print undefined, not Array.prototype.join. This is required by
2872 * ECMA-262, alas. It might have been better to make String readonly and
2873 * permanent in the global object, instead -- but that's too big a change
2874 * to swallow at this point.
2875 */
2876 key = JSCLASS_CACHED_PROTO_KEY(clasp);
2877 if (key != JSProto_Null &&
2878 !parent_proto &&
2879 !js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object),
2880 &parent_proto)) {
2881 return NULL;
2882 }
2883
2884 /* Create a prototype object for this class. */
2885 proto = js_NewObject(cx, clasp, parent_proto, obj);
2886 if (!proto)
2887 return NULL;
2888
2889 /* After this point, control must exit via label bad or out. */
2890 JS_PUSH_TEMP_ROOT_OBJECT(cx, proto, &tvr);
2891
2892 if (!constructor) {
2893 /*
2894 * Lacking a constructor, name the prototype (e.g., Math) unless this
2895 * class (a) is anonymous, i.e. for internal use only; (b) the class
2896 * of obj (the global object) is has a reserved slot indexed by key;
2897 * and (c) key is not the null key.
2898 */
2899 if ((clasp->flags & JSCLASS_IS_ANONYMOUS) &&
2900 (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL) &&
2901 key != JSProto_Null) {
2902 named = JS_FALSE;
2903 } else {
2904 named = obj->defineProperty(cx, ATOM_TO_JSID(atom),
2905 OBJECT_TO_JSVAL(proto),
2906 JS_PropertyStub, JS_PropertyStub,
2907 (clasp->flags & JSCLASS_IS_ANONYMOUS)
2908 ? JSPROP_READONLY | JSPROP_PERMANENT
2909 : 0);
2910 if (!named)
2911 goto bad;
2912 }
2913
2914 ctor = proto;
2915 } else {
2916 /* Define the constructor function in obj's scope. */
2917 fun = js_DefineFunction(cx, obj, atom, constructor, nargs,
2918 JSFUN_STUB_GSOPS);
2919 named = (fun != NULL);
2920 if (!fun)
2921 goto bad;
2922
2923 /*
2924 * Remember the class this function is a constructor for so that
2925 * we know to create an object of this class when we call the
2926 * constructor.
2927 */
2928 FUN_CLASP(fun) = clasp;
2929
2930 /*
2931 * Optionally construct the prototype object, before the class has
2932 * been fully initialized. Allow the ctor to replace proto with a
2933 * different object, as is done for operator new -- and as at least
2934 * XML support requires.
2935 */
2936 ctor = FUN_OBJECT(fun);
2937 if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) {
2938 cval = OBJECT_TO_JSVAL(ctor);
2939 if (!js_InternalConstruct(cx, proto, cval, 0, NULL, &rval))
2940 goto bad;
2941 if (!JSVAL_IS_PRIMITIVE(rval) && JSVAL_TO_OBJECT(rval) != proto)
2942 proto = JSVAL_TO_OBJECT(rval);
2943 }
2944
2945 /* Connect constructor and prototype by named properties. */
2946 if (!js_SetClassPrototype(cx, ctor, proto,
2947 JSPROP_READONLY | JSPROP_PERMANENT)) {
2948 goto bad;
2949 }
2950
2951 /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
2952 if (OBJ_GET_CLASS(cx, ctor) == clasp)
2953 OBJ_SET_PROTO(cx, ctor, proto);
2954 }
2955
2956 /* Add properties and methods to the prototype and the constructor. */
2957 if ((ps && !JS_DefineProperties(cx, proto, ps)) ||
2958 (fs && !JS_DefineFunctions(cx, proto, fs)) ||
2959 (static_ps && !JS_DefineProperties(cx, ctor, static_ps)) ||
2960 (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) {
2961 goto bad;
2962 }
2963
2964 /*
2965 * Make sure proto's scope's emptyScope is available to be shared by
2966 * objects of this class. JSScope::emptyScope is a one-slot cache. If we
2967 * omit this, some other class could snap it up. (The risk is particularly
2968 * great for Object.prototype.)
2969 *
2970 * All callers of JSObject::initSharingEmptyScope depend on this.
2971 */
2972 if (OBJ_IS_NATIVE(proto)) {
2973 JSScope *scope = OBJ_SCOPE(proto)->getEmptyScope(cx, clasp);
2974 if (!scope)
2975 goto bad;
2976 scope->drop(cx, NULL);
2977 }
2978
2979 /* If this is a standard class, cache its prototype. */
2980 if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor))
2981 goto bad;
2982
2983 out:
2984 JS_POP_TEMP_ROOT(cx, &tvr);
2985 return proto;
2986
2987 bad:
2988 if (named)
2989 (void) obj->deleteProperty(cx, ATOM_TO_JSID(atom), &rval);
2990 proto = NULL;
2991 goto out;
2992 }
2993
2994 #define SLOTS_TO_DYNAMIC_WORDS(nslots) \
2995 (JS_ASSERT((nslots) > JS_INITIAL_NSLOTS), (nslots) + 1 - JS_INITIAL_NSLOTS)
2996
2997 #define DYNAMIC_WORDS_TO_SLOTS(words) \
2998 (JS_ASSERT((words) > 1), (words) - 1 + JS_INITIAL_NSLOTS)
2999
3000
3001 static bool
3002 AllocSlots(JSContext *cx, JSObject *obj, size_t nslots)
3003 {
3004 JS_ASSERT(!obj->dslots);
3005 JS_ASSERT(nslots > JS_INITIAL_NSLOTS);
3006
3007 jsval* slots;
3008 slots = (jsval*) cx->malloc(SLOTS_TO_DYNAMIC_WORDS(nslots) * sizeof(jsval));
3009 if (!slots)
3010 return true;
3011
3012 *slots++ = nslots;
3013 /* clear the newly allocated cells. */
3014 for (jsuint n = JS_INITIAL_NSLOTS; n < nslots; ++n)
3015 slots[n - JS_INITIAL_NSLOTS] = JSVAL_VOID;
3016 obj->dslots = slots;
3017
3018 return true;
3019 }
3020
3021 bool
3022 js_GrowSlots(JSContext *cx, JSObject *obj, size_t nslots)
3023 {
3024 /*
3025 * Minimal number of dynamic slots to allocate.
3026 */
3027 const size_t MIN_DYNAMIC_WORDS = 4;
3028
3029 /*
3030 * The limit to switch to linear allocation strategy from the power of 2
3031 * growth no to waste too much memory.
3032 */
3033 const size_t LINEAR_GROWTH_STEP = JS_BIT(16);
3034
3035 /* If we are allocating fslots, there is nothing to do. */
3036 if (nslots <= JS_INITIAL_NSLOTS)
3037 return JS_TRUE;
3038
3039 size_t nwords = SLOTS_TO_DYNAMIC_WORDS(nslots);
3040
3041 /*
3042 * Round up nslots so the number of bytes in dslots array is power
3043 * of 2 to ensure exponential grouth.
3044 */
3045 uintN log;
3046 if (nwords <= MIN_DYNAMIC_WORDS) {
3047 nwords = MIN_DYNAMIC_WORDS;
3048 } else if (nwords < LINEAR_GROWTH_STEP) {
3049 JS_CEILING_LOG2(log, nwords);
3050 nwords = JS_BIT(log);
3051 } else {
3052 nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP);
3053 }
3054 nslots = DYNAMIC_WORDS_TO_SLOTS(nwords);
3055
3056 /*
3057 * If nothing was allocated yet, treat it as initial allocation (but with
3058 * the exponential growth algorithm applied).
3059 */
3060 jsval* slots = obj->dslots;
3061 if (!slots)
3062 return AllocSlots(cx, obj, nslots);
3063
3064 size_t oslots = size_t(slots[-1]);
3065
3066 slots = (jsval*) cx->realloc(slots - 1, nwords * sizeof(jsval));
3067 *slots++ = nslots;
3068 obj->dslots = slots;
3069
3070 /* Initialize the additional slots we added. */
3071 JS_ASSERT(nslots > oslots);
3072 for (size_t i = oslots; i < nslots; i++)
3073 slots[i - JS_INITIAL_NSLOTS] = JSVAL_VOID;
3074
3075 return true;
3076 }
3077
3078 void
3079 js_ShrinkSlots(JSContext *cx, JSObject *obj, size_t nslots)
3080 {
3081 jsval* slots = obj->dslots;
3082
3083 /* Nothing to shrink? */
3084 if (!slots)
3085 return;
3086
3087 JS_ASSERT(size_t(slots[-1]) > JS_INITIAL_NSLOTS);
3088 JS_ASSERT(nslots <= size_t(slots[-1]));
3089
3090 if (nslots <= JS_INITIAL_NSLOTS) {
3091 cx->free(slots - 1);
3092 obj->dslots = NULL;
3093 } else {
3094 size_t nwords = SLOTS_TO_DYNAMIC_WORDS(nslots);
3095 slots = (jsval*) cx->realloc(slots - 1, nwords * sizeof(jsval));
3096 *slots++ = nslots;
3097 obj->dslots = slots;
3098 }
3099 }
3100
3101 bool
3102 js_EnsureReservedSlots(JSContext *cx, JSObject *obj, size_t nreserved)
3103 {
3104 JS_ASSERT(OBJ_IS_NATIVE(obj));
3105 JS_ASSERT(!obj->dslots);
3106
3107 uintN nslots = JSSLOT_FREE(STOBJ_GET_CLASS(obj)) + nreserved;
3108 if (nslots > STOBJ_NSLOTS(obj) && !AllocSlots(cx, obj, nslots))
3109 return false;
3110
3111 JSScope *scope = OBJ_SCOPE(obj);
3112 if (scope->owned()) {
3113 #ifdef JS_THREADSAFE
3114 JS_ASSERT(scope->title.ownercx->thread == cx->thread);
3115 #endif
3116 JS_ASSERT(scope->freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj)));
3117 if (scope->freeslot < nslots)
3118 scope->freeslot = nslots;
3119 }
3120 return true;
3121 }
3122
3123 extern JSBool
3124 js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp)
3125 {
3126 JSProtoKey key;
3127 JSAtom *atom;
3128
3129 key = JSCLASS_CACHED_PROTO_KEY(clasp);
3130 if (key != JSProto_Null) {
3131 *idp = INT_TO_JSID(key);
3132 } else if (clasp->flags & JSCLASS_IS_ANONYMOUS) {
3133 *idp = INT_TO_JSID(JSProto_Object);
3134 } else {
3135 atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
3136 if (!atom)
3137 return JS_FALSE;
3138 *idp = ATOM_TO_JSID(atom);
3139 }
3140 return JS_TRUE;
3141 }
3142
3143 JS_BEGIN_EXTERN_C
3144
3145 static JSObject *
3146 js_InitNullClass(JSContext *cx, JSObject *obj)
3147 {
3148 JS_ASSERT(0);
3149 return NULL;
3150 }
3151
3152 #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
3153 #include "jsproto.tbl"
3154 #undef JS_PROTO
3155
3156 static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = {
3157 #define JS_PROTO(name,code,init) init,
3158 #include "jsproto.tbl"
3159 #undef JS_PROTO
3160 };
3161
3162 JS_END_EXTERN_C
3163
3164 JSBool
3165 js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
3166 JSObject **objp)
3167 {
3168 JSBool ok;
3169 JSObject *tmp, *cobj;
3170 JSResolvingKey rkey;
3171 JSResolvingEntry *rentry;
3172 uint32 generation;
3173 JSObjectOp init;
3174 jsval v;
3175
3176 while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
3177 obj = tmp;
3178 if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) {
3179 *objp = NULL;
3180 return JS_TRUE;
3181 }
3182
3183 ok = JS_GetReservedSlot(cx, obj, key, &v);
3184 if (!ok)
3185 return JS_FALSE;
3186 if (!JSVAL_IS_PRIMITIVE(v)) {
3187 *objp = JSVAL_TO_OBJECT(v);
3188 return JS_TRUE;
3189 }
3190
3191 rkey.obj = obj;
3192 rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
3193 if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry))
3194 return JS_FALSE;
3195 if (!rentry) {
3196 /* Already caching key in obj -- suppress recursion. */
3197 *objp = NULL;
3198 return JS_TRUE;
3199 }
3200 generation = cx->resolvingTable->generation;
3201
3202 cobj = NULL;
3203 init = lazy_prototype_init[key];
3204 if (init) {
3205 if (!init(cx, obj)) {
3206 ok = JS_FALSE;
3207 } else {
3208 ok = JS_GetReservedSlot(cx, obj, key, &v);
3209 if (ok && !JSVAL_IS_PRIMITIVE(v))
3210 cobj = JSVAL_TO_OBJECT(v);
3211 }
3212 }
3213
3214 js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation);
3215 *objp = cobj;
3216 return ok;
3217 }
3218
3219 JSBool
3220 js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj)
3221 {
3222 JS_ASSERT(!OBJ_GET_PARENT(cx, obj));
3223 if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL))
3224 return JS_TRUE;
3225
3226 return JS_SetReservedSlot(cx, obj, key, OBJECT_TO_JSVAL(cobj));
3227 }
3228
3229 JSBool
3230 js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp)
3231 {
3232 JSStackFrame *fp;
3233 JSObject *obj, *cobj, *pobj;
3234 JSProtoKey key;
3235 JSProperty *prop;
3236 jsval v;
3237 JSScopeProperty *sprop;
3238
3239 /*
3240 * Find the global object. Use cx->fp directly to avoid falling off
3241 * trace; all JIT-elided stack frames have the same global object as
3242 * cx->fp.
3243 */
3244 VOUCH_DOES_NOT_REQUIRE_STACK();
3245 if (!start && (fp = cx->fp) != NULL)
3246 start = fp->scopeChain;
3247
3248 if (start) {
3249 /* Find the topmost object in the scope chain. */
3250 do {
3251 obj = start;
3252 start = OBJ_GET_PARENT(cx, obj);
3253 } while (start);
3254 } else {
3255 obj = cx->globalObject;
3256 if (!obj) {
3257 *vp = JSVAL_VOID;
3258 return JS_TRUE;
3259 }
3260 }
3261
3262 OBJ_TO_INNER_OBJECT(cx, obj);
3263 if (!obj)
3264 return JS_FALSE;
3265
3266 if (JSID_IS_INT(id)) {
3267 key = (JSProtoKey) JSID_TO_INT(id);
3268 JS_ASSERT(key != JSProto_Null);
3269 if (!js_GetClassObject(cx, obj, key, &cobj))
3270 return JS_FALSE;
3271 if (cobj) {
3272 *vp = OBJECT_TO_JSVAL(cobj);
3273 return JS_TRUE;
3274 }
3275 id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
3276 }
3277
3278 JS_ASSERT(OBJ_IS_NATIVE(obj));
3279 if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME,
3280 &pobj, &prop) < 0) {
3281 return JS_FALSE;
3282 }
3283 v = JSVAL_VOID;
3284 if (prop) {
3285 if (OBJ_IS_NATIVE(pobj)) {
3286 sprop = (JSScopeProperty *) prop;
3287 if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))) {
3288 v =