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

Contents of /trunk/js/jsobj.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

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