1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=79:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla Communicator client code, released
18 : * March 31, 1998.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Netscape Communications Corporation.
22 : * Portions created by the Initial Developer are Copyright (C) 1998
23 : * the Initial Developer. All Rights Reserved.
24 : *
25 : * Contributor(s):
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /*
42 : * JS object implementation.
43 : */
44 : #include <stdlib.h>
45 : #include <string.h>
46 :
47 : #include "mozilla/Util.h"
48 :
49 : #include "jstypes.h"
50 : #include "jsutil.h"
51 : #include "jshash.h"
52 : #include "jsprf.h"
53 : #include "jsapi.h"
54 : #include "jsarray.h"
55 : #include "jsatom.h"
56 : #include "jsbool.h"
57 : #include "jscntxt.h"
58 : #include "jsversion.h"
59 : #include "jsfun.h"
60 : #include "jsgc.h"
61 : #include "jsgcmark.h"
62 : #include "jsinterp.h"
63 : #include "jsiter.h"
64 : #include "jslock.h"
65 : #include "jsnum.h"
66 : #include "jsobj.h"
67 : #include "jsonparser.h"
68 : #include "jsopcode.h"
69 : #include "jsprobes.h"
70 : #include "jsproxy.h"
71 : #include "jsscope.h"
72 : #include "jsscript.h"
73 : #include "jsstr.h"
74 : #include "jsdbgapi.h"
75 : #include "json.h"
76 : #include "jswatchpoint.h"
77 : #include "jswrapper.h"
78 : #include "jsxml.h"
79 :
80 : #include "builtin/MapObject.h"
81 : #include "frontend/BytecodeCompiler.h"
82 : #include "frontend/BytecodeEmitter.h"
83 : #include "frontend/Parser.h"
84 : #include "js/MemoryMetrics.h"
85 : #include "vm/StringBuffer.h"
86 : #include "vm/Xdr.h"
87 :
88 : #include "jsarrayinlines.h"
89 : #include "jsatominlines.h"
90 : #include "jsinterpinlines.h"
91 : #include "jsobjinlines.h"
92 : #include "jsscopeinlines.h"
93 : #include "jsscriptinlines.h"
94 :
95 : #include "vm/MethodGuard-inl.h"
96 :
97 : #include "jsautooplen.h"
98 :
99 : using namespace mozilla;
100 : using namespace js;
101 : using namespace js::gc;
102 : using namespace js::types;
103 :
104 : JS_STATIC_ASSERT(int32_t((JSObject::NELEMENTS_LIMIT - 1) * sizeof(Value)) == int64_t((JSObject::NELEMENTS_LIMIT - 1) * sizeof(Value)));
105 :
106 : Class js::ObjectClass = {
107 : js_Object_str,
108 : JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
109 : JS_PropertyStub, /* addProperty */
110 : JS_PropertyStub, /* delProperty */
111 : JS_PropertyStub, /* getProperty */
112 : JS_StrictPropertyStub, /* setProperty */
113 : JS_EnumerateStub,
114 : JS_ResolveStub,
115 : JS_ConvertStub
116 : };
117 :
118 : JS_FRIEND_API(JSObject *)
119 22996 : JS_ObjectToInnerObject(JSContext *cx, JSObject *obj)
120 : {
121 22996 : if (!obj) {
122 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
123 0 : return NULL;
124 : }
125 22996 : OBJ_TO_INNER_OBJECT(cx, obj);
126 22996 : return obj;
127 : }
128 :
129 : JS_FRIEND_API(JSObject *)
130 0 : JS_ObjectToOuterObject(JSContext *cx, JSObject *obj)
131 : {
132 0 : OBJ_TO_OUTER_OBJECT(cx, obj);
133 0 : return obj;
134 : }
135 :
136 : #if JS_HAS_OBJ_PROTO_PROP
137 :
138 : static JSBool
139 : obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp);
140 :
141 : static JSBool
142 : obj_setProto(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp);
143 :
144 : JSPropertySpec object_props[] = {
145 : {js_proto_str, 0, JSPROP_PERMANENT|JSPROP_SHARED, obj_getProto, obj_setProto},
146 : {0,0,0,0,0}
147 : };
148 :
149 : static JSBool
150 1305 : obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
151 : {
152 : /* Let CheckAccess get the slot's value, based on the access mode. */
153 : unsigned attrs;
154 1305 : id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
155 1305 : return CheckAccess(cx, obj, id, JSACC_PROTO, vp, &attrs);
156 : }
157 :
158 : size_t sSetProtoCalled = 0;
159 :
160 : static JSBool
161 864 : obj_setProto(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
162 : {
163 864 : if (!cx->runningWithTrustedPrincipals())
164 864 : ++sSetProtoCalled;
165 :
166 : /* ECMAScript 5 8.6.2 forbids changing [[Prototype]] if not [[Extensible]]. */
167 864 : if (!obj->isExtensible()) {
168 0 : obj->reportNotExtensible(cx);
169 0 : return false;
170 : }
171 :
172 864 : if (!vp->isObjectOrNull())
173 144 : return true;
174 :
175 720 : JSObject *pobj = vp->toObjectOrNull();
176 : unsigned attrs;
177 720 : id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
178 720 : if (!CheckAccess(cx, obj, id, JSAccessMode(JSACC_PROTO|JSACC_WRITE), vp, &attrs))
179 0 : return false;
180 :
181 720 : return SetProto(cx, obj, pobj, true);
182 : }
183 :
184 : #else /* !JS_HAS_OBJ_PROTO_PROP */
185 :
186 : #define object_props NULL
187 :
188 : #endif /* !JS_HAS_OBJ_PROTO_PROP */
189 :
190 : static bool
191 26290 : MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap, JSSharpInfo *value)
192 : {
193 26290 : JS_CHECK_RECURSION(cx, return NULL);
194 :
195 : JSIdArray *ida;
196 :
197 26290 : JSSharpObjectMap *map = &cx->sharpObjectMap;
198 26290 : JS_ASSERT(map->depth >= 1);
199 26290 : JSSharpInfo sharpid;
200 26290 : JSSharpTable::Ptr p = map->table.lookup(obj);
201 26290 : if (!p) {
202 25210 : if (!map->table.put(obj, sharpid))
203 0 : return false;
204 :
205 25210 : ida = JS_Enumerate(cx, obj);
206 25210 : if (!ida)
207 0 : return false;
208 :
209 25210 : bool ok = true;
210 52219 : for (int i = 0, length = ida->length; i < length; i++) {
211 27009 : jsid id = ida->vector[i];
212 : JSObject *obj2;
213 : JSProperty *prop;
214 27009 : ok = obj->lookupGeneric(cx, id, &obj2, &prop);
215 27009 : if (!ok)
216 0 : break;
217 27009 : if (!prop)
218 0 : continue;
219 : bool hasGetter, hasSetter;
220 54018 : AutoValueRooter v(cx);
221 54018 : AutoValueRooter setter(cx);
222 27009 : if (obj2->isNative()) {
223 2772 : const Shape *shape = (Shape *) prop;
224 2772 : hasGetter = shape->hasGetterValue();
225 2772 : hasSetter = shape->hasSetterValue();
226 2772 : if (hasGetter)
227 0 : v.set(shape->getterValue());
228 2772 : if (hasSetter)
229 0 : setter.set(shape->setterValue());
230 : } else {
231 24237 : hasGetter = hasSetter = false;
232 : }
233 27009 : if (hasSetter) {
234 : /* Mark the getter, then set val to setter. */
235 0 : if (hasGetter && v.value().isObject()) {
236 0 : ok = MarkSharpObjects(cx, &v.value().toObject(), NULL, NULL);
237 0 : if (!ok)
238 : break;
239 : }
240 0 : v.set(setter.value());
241 27009 : } else if (!hasGetter) {
242 27009 : ok = obj->getGeneric(cx, id, v.addr());
243 27009 : if (!ok)
244 : break;
245 : }
246 27009 : if (v.value().isObject() && !MarkSharpObjects(cx, &v.value().toObject(), NULL, NULL)) {
247 0 : ok = false;
248 : break;
249 : }
250 : }
251 25210 : if (!ok || !idap)
252 24201 : JS_DestroyIdArray(cx, ida);
253 25210 : if (!ok)
254 0 : return false;
255 : } else {
256 1080 : if (!p->value.hasGen && !p->value.isSharp) {
257 270 : p->value.hasGen = true;
258 : }
259 1080 : sharpid = p->value;
260 1080 : ida = NULL;
261 : }
262 26290 : if (idap)
263 1009 : *idap = ida;
264 26290 : if (value)
265 1009 : *value = sharpid;
266 26290 : return true;
267 : }
268 :
269 : bool
270 1045 : js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, bool *alreadySeen, bool *isSharp)
271 : {
272 1045 : if (!JS_CHECK_OPERATION_LIMIT(cx))
273 0 : return false;
274 :
275 1045 : *alreadySeen = false;
276 :
277 1045 : JSSharpObjectMap *map = &cx->sharpObjectMap;
278 :
279 1045 : JS_ASSERT_IF(map->depth == 0, map->table.count() == 0);
280 1045 : JS_ASSERT_IF(map->table.count() == 0, map->depth == 0);
281 :
282 1045 : JSSharpTable::Ptr p;
283 1045 : JSSharpInfo sharpid;
284 1045 : JSIdArray *ida = NULL;
285 :
286 : /* From this point the control must flow either through out: or bad:. */
287 1045 : if (map->depth == 0) {
288 1009 : JS_KEEP_ATOMS(cx->runtime);
289 :
290 : /*
291 : * Although MarkSharpObjects tries to avoid invoking getters,
292 : * it ends up doing so anyway under some circumstances; for
293 : * example, if a wrapped object has getters, the wrapper will
294 : * prevent MarkSharpObjects from recognizing them as such.
295 : * This could lead to js_LeaveSharpObject being called while
296 : * MarkSharpObjects is still working.
297 : *
298 : * Increment map->depth while we call MarkSharpObjects, to
299 : * ensure that such a call doesn't free the hash table we're
300 : * still using.
301 : */
302 1009 : map->depth = 1;
303 1009 : bool success = MarkSharpObjects(cx, obj, &ida, &sharpid);
304 1009 : JS_ASSERT(map->depth == 1);
305 1009 : map->depth = 0;
306 1009 : if (!success)
307 0 : goto bad;
308 1009 : JS_ASSERT(!sharpid.isSharp);
309 1009 : if (!idap) {
310 144 : JS_DestroyIdArray(cx, ida);
311 144 : ida = NULL;
312 : }
313 : } else {
314 : /*
315 : * It's possible that the value of a property has changed from the
316 : * first time the object's properties are traversed (when the property
317 : * ids are entered into the hash table) to the second (when they are
318 : * converted to strings), i.e., the JSObject::getProperty() call is not
319 : * idempotent.
320 : */
321 36 : p = map->table.lookup(obj);
322 36 : if (!p) {
323 9 : if (!map->table.put(obj, sharpid))
324 0 : goto bad;
325 9 : goto out;
326 : }
327 27 : sharpid = p->value;
328 : }
329 :
330 1036 : if (sharpid.isSharp || sharpid.hasGen)
331 0 : *alreadySeen = true;
332 :
333 : out:
334 1045 : if (!sharpid.isSharp) {
335 1045 : if (idap && !ida) {
336 36 : ida = JS_Enumerate(cx, obj);
337 36 : if (!ida)
338 0 : goto bad;
339 : }
340 1045 : map->depth++;
341 : }
342 :
343 1045 : if (idap)
344 901 : *idap = ida;
345 1045 : *isSharp = sharpid.isSharp;
346 1045 : return true;
347 :
348 : bad:
349 : /* Clean up the sharpObjectMap table on outermost error. */
350 0 : if (map->depth == 0) {
351 0 : JS_UNKEEP_ATOMS(cx->runtime);
352 0 : map->sharpgen = 0;
353 0 : map->table.clear();
354 : }
355 0 : return false;
356 : }
357 :
358 : void
359 1045 : js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
360 : {
361 1045 : JSSharpObjectMap *map = &cx->sharpObjectMap;
362 1045 : JS_ASSERT(map->depth > 0);
363 1045 : if (--map->depth == 0) {
364 1009 : JS_UNKEEP_ATOMS(cx->runtime);
365 1009 : map->sharpgen = 0;
366 1009 : map->table.clear();
367 : }
368 1045 : if (idap) {
369 901 : if (JSIdArray *ida = *idap) {
370 901 : JS_DestroyIdArray(cx, ida);
371 901 : *idap = NULL;
372 : }
373 : }
374 1045 : }
375 :
376 : void
377 2 : js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
378 : {
379 2 : JS_ASSERT(map->depth > 0);
380 :
381 : /*
382 : * During recursive calls to MarkSharpObjects a non-native object or
383 : * object with a custom getProperty method can potentially return an
384 : * unrooted value or even cut from the object graph an argument of one of
385 : * MarkSharpObjects recursive invocations. So we must protect map->table
386 : * entries against GC.
387 : *
388 : * We can not simply use JSTempValueRooter to mark the obj argument of
389 : * MarkSharpObjects during recursion as we have to protect *all* entries
390 : * in JSSharpObjectMap including those that contains otherwise unreachable
391 : * objects just allocated through custom getProperty. Otherwise newer
392 : * allocations can re-use the address of an object stored in the hashtable
393 : * confusing js_EnterSharpObject. So to address the problem we simply
394 : * mark all objects from map->table.
395 : *
396 : * An alternative "proper" solution is to use JSTempValueRooter in
397 : * MarkSharpObjects with code to remove during finalization entries
398 : * with otherwise unreachable objects. But this is way too complex
399 : * to justify spending efforts.
400 : */
401 8 : for (JSSharpTable::Range r = map->table.all(); !r.empty(); r.popFront()) {
402 6 : JSObject *tmp = r.front().key;
403 6 : MarkObjectRoot(trc, &tmp, "sharp table entry");
404 6 : JS_ASSERT(tmp == r.front().key);
405 : }
406 2 : }
407 :
408 : #if JS_HAS_TOSOURCE
409 : static JSBool
410 901 : obj_toSource(JSContext *cx, unsigned argc, Value *vp)
411 : {
412 901 : bool comma = false;
413 : const jschar *vchars;
414 : size_t vlength;
415 : Value *val;
416 : JSString *gsop[2];
417 :
418 901 : JS_CHECK_RECURSION(cx, return JS_FALSE);
419 :
420 : Value localroot[4];
421 901 : PodArrayZero(localroot);
422 1802 : AutoArrayRooter tvr(cx, ArrayLength(localroot), localroot);
423 :
424 : /* If outermost, we need parentheses to be an expression, not a block. */
425 901 : bool outermost = (cx->sharpObjectMap.depth == 0);
426 :
427 901 : JSObject *obj = ToObject(cx, &vp[1]);
428 901 : if (!obj)
429 0 : return false;
430 :
431 : JSIdArray *ida;
432 901 : bool alreadySeen = false;
433 901 : bool isSharp = false;
434 901 : if (!js_EnterSharpObject(cx, obj, &ida, &alreadySeen, &isSharp))
435 0 : return false;
436 :
437 901 : if (!ida) {
438 : /*
439 : * We've already seen obj, so don't serialize it again (particularly as
440 : * we might recur in the process): just serialize an empty object.
441 : */
442 0 : JS_ASSERT(alreadySeen);
443 0 : JSString *str = js_NewStringCopyZ(cx, "{}");
444 0 : if (!str)
445 0 : return false;
446 0 : vp->setString(str);
447 0 : return true;
448 : }
449 :
450 901 : JS_ASSERT(!isSharp);
451 901 : if (alreadySeen) {
452 0 : JSSharpTable::Ptr p = cx->sharpObjectMap.table.lookup(obj);
453 0 : JS_ASSERT(p);
454 0 : JS_ASSERT(!p->value.isSharp);
455 0 : p->value.isSharp = true;
456 : }
457 :
458 : /* Automatically call js_LeaveSharpObject when we leave this frame. */
459 : class AutoLeaveSharpObject {
460 : JSContext *cx;
461 : JSIdArray *ida;
462 : public:
463 901 : AutoLeaveSharpObject(JSContext *cx, JSIdArray *ida) : cx(cx), ida(ida) {}
464 901 : ~AutoLeaveSharpObject() {
465 901 : js_LeaveSharpObject(cx, &ida);
466 901 : }
467 1802 : } autoLeaveSharpObject(cx, ida);
468 :
469 1802 : StringBuffer buf(cx);
470 901 : if (outermost && !buf.append('('))
471 0 : return false;
472 901 : if (!buf.append('{'))
473 0 : return false;
474 :
475 : /*
476 : * We have four local roots for cooked and raw value GC safety. Hoist the
477 : * "localroot + 2" out of the loop using the val local, which refers to
478 : * the raw (unconverted, "uncooked") values.
479 : */
480 901 : val = localroot + 2;
481 :
482 3646 : for (int i = 0; i < ida->length; i++) {
483 : /* Get strings for id and value and GC-root them via vp. */
484 2763 : jsid id = ida->vector[i];
485 : JSLinearString *idstr;
486 :
487 : JSObject *obj2;
488 : JSProperty *prop;
489 2763 : if (!obj->lookupGeneric(cx, id, &obj2, &prop))
490 0 : return false;
491 :
492 : /*
493 : * Convert id to a value and then to a string. Decide early whether we
494 : * prefer get/set or old getter/setter syntax.
495 : */
496 2763 : JSString *s = ToString(cx, IdToValue(id));
497 2763 : if (!s || !(idstr = s->ensureLinear(cx)))
498 0 : return false;
499 :
500 2763 : int valcnt = 0;
501 2763 : if (prop) {
502 2763 : bool doGet = true;
503 2763 : if (obj2->isNative()) {
504 2763 : const Shape *shape = (Shape *) prop;
505 2763 : unsigned attrs = shape->attributes();
506 2763 : if (attrs & JSPROP_GETTER) {
507 0 : doGet = false;
508 0 : val[valcnt] = shape->getterValue();
509 0 : gsop[valcnt] = cx->runtime->atomState.getAtom;
510 0 : valcnt++;
511 : }
512 2763 : if (attrs & JSPROP_SETTER) {
513 0 : doGet = false;
514 0 : val[valcnt] = shape->setterValue();
515 0 : gsop[valcnt] = cx->runtime->atomState.setAtom;
516 0 : valcnt++;
517 : }
518 : }
519 2763 : if (doGet) {
520 2763 : valcnt = 1;
521 2763 : gsop[0] = NULL;
522 2763 : if (!obj->getGeneric(cx, id, &val[0]))
523 0 : return false;
524 : }
525 : }
526 :
527 : /*
528 : * If id is a string that's not an identifier, or if it's a negative
529 : * integer, then it must be quoted.
530 : */
531 5526 : if (JSID_IS_ATOM(id)
532 2763 : ? !IsIdentifier(idstr)
533 0 : : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
534 0 : s = js_QuoteString(cx, idstr, jschar('\''));
535 0 : if (!s || !(idstr = s->ensureLinear(cx)))
536 0 : return false;
537 : }
538 :
539 5508 : for (int j = 0; j < valcnt; j++) {
540 : /*
541 : * Censor an accessor descriptor getter or setter part if it's
542 : * undefined.
543 : */
544 2763 : if (gsop[j] && val[j].isUndefined())
545 0 : continue;
546 :
547 : /* Convert val[j] to its canonical source form. */
548 2763 : JSString *valstr = js_ValueToSource(cx, val[j]);
549 2763 : if (!valstr)
550 18 : return false;
551 2745 : localroot[j].setString(valstr); /* local root */
552 2745 : vchars = valstr->getChars(cx);
553 2745 : if (!vchars)
554 0 : return false;
555 2745 : vlength = valstr->length();
556 :
557 : /*
558 : * Remove '(function ' from the beginning of valstr and ')' from the
559 : * end so that we can put "get" in front of the function definition.
560 : */
561 2745 : if (gsop[j] && IsFunctionObject(val[j])) {
562 0 : const jschar *start = vchars;
563 0 : const jschar *end = vchars + vlength;
564 :
565 0 : uint8_t parenChomp = 0;
566 0 : if (vchars[0] == '(') {
567 0 : vchars++;
568 0 : parenChomp = 1;
569 : }
570 :
571 : /* Try to jump "function" keyword. */
572 0 : if (vchars)
573 0 : vchars = js_strchr_limit(vchars, ' ', end);
574 :
575 : /*
576 : * Jump over the function's name: it can't be encoded as part
577 : * of an ECMA getter or setter.
578 : */
579 0 : if (vchars)
580 0 : vchars = js_strchr_limit(vchars, '(', end);
581 :
582 0 : if (vchars) {
583 0 : if (*vchars == ' ')
584 0 : vchars++;
585 0 : vlength = end - vchars - parenChomp;
586 : } else {
587 0 : gsop[j] = NULL;
588 0 : vchars = start;
589 : }
590 : }
591 :
592 2745 : if (comma && !buf.append(", "))
593 0 : return false;
594 2745 : comma = true;
595 :
596 2745 : if (gsop[j])
597 0 : if (!buf.append(gsop[j]) || !buf.append(' '))
598 0 : return false;
599 :
600 2745 : if (!buf.append(idstr))
601 0 : return false;
602 2745 : if (!buf.append(gsop[j] ? ' ' : ':'))
603 0 : return false;
604 :
605 2745 : if (!buf.append(vchars, vlength))
606 0 : return false;
607 : }
608 : }
609 :
610 883 : if (!buf.append('}'))
611 0 : return false;
612 883 : if (outermost && !buf.append(')'))
613 0 : return false;
614 :
615 883 : JSString *str = buf.finishString();
616 883 : if (!str)
617 0 : return false;
618 883 : vp->setString(str);
619 883 : return true;
620 : }
621 : #endif /* JS_HAS_TOSOURCE */
622 :
623 : namespace js {
624 :
625 : JSString *
626 36588 : obj_toStringHelper(JSContext *cx, JSObject *obj)
627 : {
628 36588 : if (obj->isProxy())
629 1944 : return Proxy::obj_toString(cx, obj);
630 :
631 69288 : StringBuffer sb(cx);
632 34644 : const char *className = obj->getClass()->name;
633 69288 : if (!sb.append("[object ") || !sb.appendInflated(className, strlen(className)) ||
634 34644 : !sb.append("]"))
635 : {
636 0 : return NULL;
637 : }
638 34644 : return sb.finishString();
639 : }
640 :
641 : JSObject *
642 6282 : NonNullObject(JSContext *cx, const Value &v)
643 : {
644 6282 : if (v.isPrimitive()) {
645 198 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
646 198 : return NULL;
647 : }
648 6084 : return &v.toObject();
649 : }
650 :
651 : const char *
652 2276 : InformalValueTypeName(const Value &v)
653 : {
654 2276 : if (v.isObject())
655 2114 : return v.toObject().getClass()->name;
656 162 : if (v.isString())
657 0 : return "string";
658 162 : if (v.isNumber())
659 0 : return "number";
660 162 : if (v.isBoolean())
661 0 : return "boolean";
662 162 : if (v.isNull())
663 72 : return "null";
664 90 : if (v.isUndefined())
665 90 : return "undefined";
666 0 : return "value";
667 : }
668 :
669 : } /* namespace js */
670 :
671 : /* ES5 15.2.4.2. Note steps 1 and 2 are errata. */
672 : static JSBool
673 34644 : obj_toString(JSContext *cx, unsigned argc, Value *vp)
674 : {
675 34644 : Value &thisv = vp[1];
676 :
677 : /* Step 1. */
678 34644 : if (thisv.isUndefined()) {
679 0 : vp->setString(cx->runtime->atomState.objectUndefinedAtom);
680 0 : return true;
681 : }
682 :
683 : /* Step 2. */
684 34644 : if (thisv.isNull()) {
685 0 : vp->setString(cx->runtime->atomState.objectNullAtom);
686 0 : return true;
687 : }
688 :
689 : /* Step 3. */
690 34644 : JSObject *obj = ToObject(cx, &thisv);
691 34644 : if (!obj)
692 0 : return false;
693 :
694 : /* Steps 4-5. */
695 34644 : JSString *str = js::obj_toStringHelper(cx, obj);
696 34644 : if (!str)
697 0 : return false;
698 34644 : vp->setString(str);
699 34644 : return true;
700 : }
701 :
702 : /* ES5 15.2.4.3. */
703 : static JSBool
704 9 : obj_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
705 : {
706 9 : JS_CHECK_RECURSION(cx, return false);
707 :
708 : /* Step 1. */
709 9 : JSObject *obj = ToObject(cx, &vp[1]);
710 9 : if (!obj)
711 0 : return false;
712 :
713 : /* Steps 2-4. */
714 9 : return obj->callMethod(cx, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), 0, NULL, vp);
715 : }
716 :
717 : static JSBool
718 350662 : obj_valueOf(JSContext *cx, unsigned argc, Value *vp)
719 : {
720 350662 : JSObject *obj = ToObject(cx, &vp[1]);
721 350662 : if (!obj)
722 0 : return false;
723 350662 : vp->setObject(*obj);
724 350662 : return true;
725 : }
726 :
727 : /* We should be able to assert this for *any* fp->scopeChain(). */
728 : static void
729 97609 : AssertInnerizedScopeChain(JSContext *cx, JSObject &scopeobj)
730 : {
731 : #ifdef DEBUG
732 297055 : for (JSObject *o = &scopeobj; o; o = o->enclosingScope()) {
733 199446 : if (JSObjectOp op = o->getClass()->ext.innerObject)
734 0 : JS_ASSERT(op(cx, o) == o);
735 : }
736 : #endif
737 97609 : }
738 :
739 : #ifndef EVAL_CACHE_CHAIN_LIMIT
740 : # define EVAL_CACHE_CHAIN_LIMIT 4
741 : #endif
742 :
743 : static inline JSScript **
744 92813 : EvalCacheHash(JSContext *cx, JSLinearString *str)
745 : {
746 92813 : const jschar *s = str->chars();
747 92813 : size_t n = str->length();
748 :
749 92813 : if (n > 100)
750 5760 : n = 100;
751 : uint32_t h;
752 1759551 : for (h = 0; n; s++, n--)
753 1666738 : h = JS_ROTATE_LEFT32(h, 4) ^ *s;
754 :
755 92813 : h *= JS_GOLDEN_RATIO;
756 92813 : h >>= 32 - JS_EVAL_CACHE_SHIFT;
757 92813 : return &cx->compartment->evalCache[h];
758 : }
759 :
760 : static JS_ALWAYS_INLINE JSScript *
761 77769 : EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, unsigned staticLevel,
762 : JSPrincipals *principals, JSObject &scopeobj, JSScript **bucket)
763 : {
764 : /*
765 : * Cache local eval scripts indexed by source qualified by scope.
766 : *
767 : * An eval cache entry should never be considered a hit unless its
768 : * strictness matches that of the new eval code. The existing code takes
769 : * care of this, because hits are qualified by the function from which
770 : * eval was called, whose strictness doesn't change. (We don't cache evals
771 : * in eval code, so the calling function corresponds to the calling script,
772 : * and its strictness never varies.) Scripts produced by calls to eval from
773 : * global code aren't cached.
774 : *
775 : * FIXME bug 620141: Qualify hits by calling script rather than function.
776 : * Then we wouldn't need the unintuitive !isEvalFrame() hack in EvalKernel
777 : * to avoid caching nested evals in functions (thus potentially mismatching
778 : * on strict mode), and we could cache evals in global code if desired.
779 : */
780 77769 : unsigned count = 0;
781 77769 : JSScript **scriptp = bucket;
782 :
783 77769 : JSVersion version = cx->findVersion();
784 : JSScript *script;
785 77769 : JSSubsumePrincipalsOp subsume = cx->runtime->securityCallbacks->subsumePrincipals;
786 204375 : while ((script = *scriptp) != NULL) {
787 353112 : if (script->savedCallerFun &&
788 : script->staticLevel == staticLevel &&
789 117299 : script->getVersion() == version &&
790 117299 : !script->hasSingletons &&
791 : (!subsume || script->principals == principals ||
792 0 : (subsume(principals, script->principals) &&
793 0 : subsume(script->principals, principals)))) {
794 : /*
795 : * Get the prior (cache-filling) eval's saved caller function.
796 : * See frontend::CompileScript.
797 : */
798 98566 : JSFunction *fun = script->getCallerFunction();
799 :
800 98566 : if (fun == caller->fun()) {
801 : /*
802 : * Get the source string passed for safekeeping in the atom map
803 : * by the prior eval to frontend::CompileScript.
804 : */
805 96609 : JSAtom *src = script->atoms[0];
806 :
807 96609 : if (src == str || EqualStrings(src, str)) {
808 : /*
809 : * Source matches. Make sure there are no inner objects
810 : * which might use the wrong parent and/or call scope by
811 : * reusing the previous eval's script. Skip the script's
812 : * first object, which entrains the eval's scope.
813 : */
814 69038 : JS_ASSERT(script->objects()->length >= 1);
815 127635 : if (script->objects()->length == 1 &&
816 58597 : !JSScript::isValidOffset(script->regexpsOffset)) {
817 58597 : JS_ASSERT(staticLevel == script->staticLevel);
818 58597 : *scriptp = script->evalHashLink();
819 58597 : script->evalHashLink() = NULL;
820 58597 : return script;
821 : }
822 : }
823 : }
824 : }
825 :
826 59917 : if (++count == EVAL_CACHE_CHAIN_LIMIT)
827 11080 : return NULL;
828 48837 : scriptp = &script->evalHashLink();
829 : }
830 8092 : return NULL;
831 : }
832 :
833 : /*
834 : * There are two things we want to do with each script executed in EvalKernel:
835 : * 1. notify jsdbgapi about script creation/destruction
836 : * 2. add the script to the eval cache when EvalKernel is finished
837 : *
838 : * NB: Although the eval cache keeps a script alive wrt to the JS engine, from
839 : * a jsdbgapi user's perspective, we want each eval() to create and destroy a
840 : * script. This hides implementation details and means we don't have to deal
841 : * with calls to JS_GetScriptObject for scripts in the eval cache (currently,
842 : * script->object aliases script->evalHashLink()).
843 : */
844 : class EvalScriptGuard
845 : {
846 : JSContext *cx_;
847 : JSLinearString *str_;
848 : JSScript **bucket_;
849 : JSScript *script_;
850 :
851 : public:
852 92813 : EvalScriptGuard(JSContext *cx, JSLinearString *str)
853 : : cx_(cx),
854 : str_(str),
855 92813 : script_(NULL) {
856 92813 : bucket_ = EvalCacheHash(cx, str);
857 92813 : }
858 :
859 92813 : ~EvalScriptGuard() {
860 92813 : if (script_) {
861 92588 : CallDestroyScriptHook(cx_->runtime->defaultFreeOp(), script_);
862 92588 : script_->isActiveEval = false;
863 92588 : script_->isCachedEval = true;
864 92588 : script_->evalHashLink() = *bucket_;
865 92588 : *bucket_ = script_;
866 : }
867 92813 : }
868 :
869 77769 : void lookupInEvalCache(StackFrame *caller, unsigned staticLevel,
870 : JSPrincipals *principals, JSObject &scopeobj) {
871 77769 : if (JSScript *found = EvalCacheLookup(cx_, str_, caller, staticLevel,
872 77769 : principals, scopeobj, bucket_)) {
873 58597 : js_CallNewScriptHook(cx_, found, NULL);
874 58597 : script_ = found;
875 58597 : script_->isCachedEval = false;
876 58597 : script_->isActiveEval = true;
877 : }
878 77769 : }
879 :
880 33991 : void setNewScript(JSScript *script) {
881 : /* NewScriptFromEmitter has already called js_CallNewScriptHook. */
882 33991 : JS_ASSERT(!script_ && script);
883 33991 : script_ = script;
884 33991 : script_->isActiveEval = true;
885 33991 : }
886 :
887 92813 : bool foundScript() {
888 92813 : return !!script_;
889 : }
890 :
891 92588 : JSScript *script() const {
892 92588 : JS_ASSERT(script_);
893 92588 : return script_;
894 : }
895 : };
896 :
897 : /* Define subset of ExecuteType so that casting performs the injection. */
898 : enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL };
899 :
900 : /*
901 : * Common code implementing direct and indirect eval.
902 : *
903 : * Evaluate call.argv[2], if it is a string, in the context of the given calling
904 : * frame, with the provided scope chain, with the semantics of either a direct
905 : * or indirect eval (see ES5 10.4.2). If this is an indirect eval, scopeobj
906 : * must be a global object.
907 : *
908 : * On success, store the completion value in call.rval and return true.
909 : */
910 : static bool
911 97609 : EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *caller,
912 : JSObject &scopeobj)
913 : {
914 97609 : JS_ASSERT((evalType == INDIRECT_EVAL) == (caller == NULL));
915 97609 : AssertInnerizedScopeChain(cx, scopeobj);
916 :
917 97609 : if (!scopeobj.global().isRuntimeCodeGenEnabled(cx)) {
918 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_EVAL);
919 0 : return false;
920 : }
921 :
922 : /* ES5 15.1.2.1 step 1. */
923 97609 : if (args.length() < 1) {
924 2546 : args.rval().setUndefined();
925 2546 : return true;
926 : }
927 95063 : if (!args[0].isString()) {
928 225 : args.rval() = args[0];
929 225 : return true;
930 : }
931 94838 : JSString *str = args[0].toString();
932 :
933 : /* ES5 15.1.2.1 steps 2-8. */
934 :
935 : /*
936 : * Per ES5, indirect eval runs in the global scope. (eval is specified this
937 : * way so that the compiler can make assumptions about what bindings may or
938 : * may not exist in the current frame if it doesn't see 'eval'.)
939 : */
940 : unsigned staticLevel;
941 : Value thisv;
942 94838 : if (evalType == DIRECT_EVAL) {
943 80799 : staticLevel = caller->script()->staticLevel + 1;
944 :
945 : /*
946 : * Direct calls to eval are supposed to see the caller's |this|. If we
947 : * haven't wrapped that yet, do so now, before we make a copy of it for
948 : * the eval code to use.
949 : */
950 80799 : if (!ComputeThis(cx, caller))
951 0 : return false;
952 80799 : thisv = caller->thisValue();
953 :
954 : #ifdef DEBUG
955 80799 : jsbytecode *callerPC = caller->pcQuadratic(cx);
956 80799 : JS_ASSERT(callerPC && JSOp(*callerPC) == JSOP_EVAL);
957 : #endif
958 : } else {
959 14039 : JS_ASSERT(args.callee().global() == scopeobj);
960 14039 : staticLevel = 0;
961 :
962 : /* Use the global as 'this', modulo outerization. */
963 14039 : JSObject *thisobj = scopeobj.thisObject(cx);
964 14039 : if (!thisobj)
965 0 : return false;
966 14039 : thisv = ObjectValue(*thisobj);
967 : }
968 :
969 94838 : JSLinearString *linearStr = str->ensureLinear(cx);
970 94838 : if (!linearStr)
971 0 : return false;
972 94838 : const jschar *chars = linearStr->chars();
973 94838 : size_t length = linearStr->length();
974 :
975 : /*
976 : * If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON.
977 : * Try the JSON parser first because it's much faster. If the eval string
978 : * isn't JSON, JSON parsing will probably fail quickly, so little time
979 : * will be lost.
980 : *
981 : * Don't use the JSON parser if the caller is strict mode code, because in
982 : * strict mode object literals must not have repeated properties, and the
983 : * JSON parser cheerfully (and correctly) accepts them. If you're parsing
984 : * JSON with eval and using strict mode, you deserve to be slow.
985 : */
986 296882 : if (length > 2 &&
987 97375 : ((chars[0] == '[' && chars[length - 1] == ']') ||
988 99844 : (chars[0] == '(' && chars[length - 1] == ')')) &&
989 4825 : (!caller || !caller->script()->strictModeCode))
990 : {
991 : /*
992 : * Remarkably, JavaScript syntax is not a superset of JSON syntax:
993 : * strings in JavaScript cannot contain the Unicode line and paragraph
994 : * terminator characters U+2028 and U+2029, but strings in JSON can.
995 : * Rather than force the JSON parser to handle this quirk when used by
996 : * eval, we simply don't use the JSON parser when either character
997 : * appears in the provided string. See bug 657367.
998 : */
999 1677851 : for (const jschar *cp = &chars[1], *end = &chars[length - 2]; ; cp++) {
1000 1677851 : if (*cp == 0x2028 || *cp == 0x2029)
1001 0 : break;
1002 :
1003 1677851 : if (cp == end) {
1004 4953 : bool isArray = (chars[0] == '[');
1005 : JSONParser parser(cx, isArray ? chars : chars + 1, isArray ? length : length - 2,
1006 4953 : JSONParser::StrictJSON, JSONParser::NoError);
1007 : Value tmp;
1008 4953 : if (!parser.parse(&tmp))
1009 0 : return false;
1010 4953 : if (tmp.isUndefined())
1011 2928 : break;
1012 2025 : args.rval() = tmp;
1013 2025 : return true;
1014 : }
1015 : }
1016 : }
1017 :
1018 185626 : EvalScriptGuard esg(cx, linearStr);
1019 :
1020 92813 : JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
1021 :
1022 92813 : if (evalType == DIRECT_EVAL && caller->isNonEvalFunctionFrame())
1023 77769 : esg.lookupInEvalCache(caller, staticLevel, principals, scopeobj);
1024 :
1025 92813 : if (!esg.foundScript()) {
1026 : unsigned lineno;
1027 : const char *filename;
1028 : JSPrincipals *originPrincipals;
1029 : CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals,
1030 : evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL
1031 34216 : : NOT_CALLED_FROM_JSOP_EVAL);
1032 34216 : uint32_t tcflags = TCF_COMPILE_N_GO | TCF_COMPILE_FOR_EVAL;
1033 : JSScript *compiled = frontend::CompileScript(cx, &scopeobj, caller,
1034 : principals, originPrincipals,
1035 : tcflags, chars, length, filename,
1036 : lineno, cx->findVersion(), linearStr,
1037 34216 : staticLevel);
1038 34216 : if (!compiled)
1039 225 : return false;
1040 :
1041 33991 : esg.setNewScript(compiled);
1042 : }
1043 :
1044 : return ExecuteKernel(cx, esg.script(), scopeobj, thisv, ExecuteType(evalType),
1045 92588 : NULL /* evalInFrame */, &args.rval());
1046 : }
1047 :
1048 : /*
1049 : * We once supported a second argument to eval to use as the scope chain
1050 : * when evaluating the code string. Warn when such uses are seen so that
1051 : * authors will know that support for eval(s, o) has been removed.
1052 : */
1053 : static inline bool
1054 97609 : WarnOnTooManyArgs(JSContext *cx, const CallArgs &args)
1055 : {
1056 97609 : if (args.length() > 1) {
1057 99 : if (JSScript *script = cx->stack.currentScript()) {
1058 90 : if (!script->warnedAboutTwoArgumentEval) {
1059 : static const char TWO_ARGUMENT_WARNING[] =
1060 : "Support for eval(code, scopeObject) has been removed. "
1061 : "Use |with (scopeObject) eval(code);| instead.";
1062 63 : if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING))
1063 0 : return false;
1064 63 : script->warnedAboutTwoArgumentEval = true;
1065 : }
1066 : } else {
1067 : /*
1068 : * In the case of an indirect call without a caller frame, avoid a
1069 : * potential warning-flood by doing nothing.
1070 : */
1071 : }
1072 : }
1073 :
1074 97609 : return true;
1075 : }
1076 :
1077 : namespace js {
1078 :
1079 : /*
1080 : * ES5 15.1.2.1.
1081 : *
1082 : * NB: This method handles only indirect eval.
1083 : */
1084 : JSBool
1085 14039 : eval(JSContext *cx, unsigned argc, Value *vp)
1086 : {
1087 14039 : CallArgs args = CallArgsFromVp(argc, vp);
1088 14039 : return WarnOnTooManyArgs(cx, args) &&
1089 14039 : EvalKernel(cx, args, INDIRECT_EVAL, NULL, args.callee().global());
1090 : }
1091 :
1092 : bool
1093 83570 : DirectEval(JSContext *cx, const CallArgs &args)
1094 : {
1095 : /* Direct eval can assume it was called from an interpreted frame. */
1096 83570 : StackFrame *caller = cx->fp();
1097 83570 : JS_ASSERT(caller->isScriptFrame());
1098 83570 : JS_ASSERT(IsBuiltinEvalForScope(&caller->scopeChain(), args.calleev()));
1099 83570 : JS_ASSERT(JSOp(*cx->regs().pc) == JSOP_EVAL);
1100 :
1101 167140 : AutoFunctionCallProbe callProbe(cx, args.callee().toFunction(), caller->script());
1102 :
1103 83570 : JSObject *scopeChain = GetScopeChain(cx, caller);
1104 83570 : if (!scopeChain)
1105 0 : return false;
1106 :
1107 83570 : if (!WarnOnTooManyArgs(cx, args))
1108 0 : return false;
1109 :
1110 83570 : return EvalKernel(cx, args, DIRECT_EVAL, caller, *scopeChain);
1111 : }
1112 :
1113 : bool
1114 167167 : IsBuiltinEvalForScope(JSObject *scopeChain, const Value &v)
1115 : {
1116 167167 : return scopeChain->global().getOriginalEval() == v;
1117 : }
1118 :
1119 : bool
1120 103425 : IsAnyBuiltinEval(JSFunction *fun)
1121 : {
1122 103425 : return fun->maybeNative() == eval;
1123 : }
1124 :
1125 : JSPrincipals *
1126 103425 : PrincipalsForCompiledCode(const CallReceiver &call, JSContext *cx)
1127 : {
1128 114037 : JS_ASSERT(IsAnyBuiltinEval(call.callee().toFunction()) ||
1129 114037 : IsBuiltinFunctionConstructor(call.callee().toFunction()));
1130 :
1131 : /*
1132 : * To compute the principals of the compiled eval/Function code, we simply
1133 : * use the callee's principals. To see why the caller's principals are
1134 : * ignored, consider first that, in the capability-model we assume, the
1135 : * high-privileged eval/Function should never have escaped to the
1136 : * low-privileged caller. (For the Mozilla embedding, this is brute-enforced
1137 : * by explicit filtering by wrappers.) Thus, the caller's privileges should
1138 : * subsume the callee's.
1139 : *
1140 : * In the converse situation, where the callee has lower privileges than the
1141 : * caller, we might initially guess that the caller would want to retain
1142 : * their higher privileges in the generated code. However, since the
1143 : * compiled code will be run with the callee's scope chain, this would make
1144 : * fp->script()->compartment() != fp->compartment().
1145 : */
1146 :
1147 103425 : return call.callee().principals(cx);
1148 : }
1149 :
1150 : } /* namespace js */
1151 :
1152 : #if JS_HAS_OBJ_WATCHPOINT
1153 :
1154 : static JSBool
1155 3825 : obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old,
1156 : jsval *nvp, void *closure)
1157 : {
1158 3825 : JSObject *callable = (JSObject *) closure;
1159 3825 : if (JSSubsumePrincipalsOp subsume = cx->runtime->securityCallbacks->subsumePrincipals) {
1160 0 : if (JSPrincipals *watcher = callable->principals(cx)) {
1161 0 : if (JSObject *scopeChain = cx->stack.currentScriptedScopeChain()) {
1162 0 : if (JSPrincipals *subject = scopeChain->principals(cx)) {
1163 0 : if (!subsume(watcher, subject)) {
1164 : /* Silently don't call the watch handler. */
1165 0 : return true;
1166 : }
1167 : }
1168 : }
1169 : }
1170 : }
1171 :
1172 : /* Avoid recursion on (obj, id) already being watched on cx. */
1173 7650 : AutoResolving resolving(cx, obj, id, AutoResolving::WATCH);
1174 3825 : if (resolving.alreadyStarted())
1175 0 : return true;
1176 :
1177 3825 : Value argv[] = { IdToValue(id), old, *nvp };
1178 3825 : return Invoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable), ArrayLength(argv), argv, nvp);
1179 : }
1180 :
1181 : static JSBool
1182 3681 : obj_watch(JSContext *cx, unsigned argc, Value *vp)
1183 : {
1184 3681 : if (argc <= 1) {
1185 0 : js_ReportMissingArg(cx, *vp, 1);
1186 0 : return false;
1187 : }
1188 :
1189 3681 : JSObject *callable = js_ValueToCallableObject(cx, &vp[3], 0);
1190 3681 : if (!callable)
1191 9 : return false;
1192 :
1193 : jsid propid;
1194 3672 : if (!ValueToId(cx, vp[2], &propid))
1195 0 : return false;
1196 :
1197 3672 : JSObject *obj = ToObject(cx, &vp[1]);
1198 3672 : if (!obj)
1199 0 : return false;
1200 :
1201 : Value tmp;
1202 : unsigned attrs;
1203 3672 : if (!CheckAccess(cx, obj, propid, JSACC_WATCH, &tmp, &attrs))
1204 0 : return false;
1205 :
1206 3672 : vp->setUndefined();
1207 :
1208 3672 : if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
1209 0 : return false;
1210 3672 : return JS_SetWatchPoint(cx, obj, propid, obj_watch_handler, callable);
1211 : }
1212 :
1213 : static JSBool
1214 945 : obj_unwatch(JSContext *cx, unsigned argc, Value *vp)
1215 : {
1216 945 : JSObject *obj = ToObject(cx, &vp[1]);
1217 945 : if (!obj)
1218 0 : return false;
1219 945 : vp->setUndefined();
1220 : jsid id;
1221 945 : if (argc != 0) {
1222 9 : if (!ValueToId(cx, vp[2], &id))
1223 0 : return false;
1224 : } else {
1225 936 : id = JSID_VOID;
1226 : }
1227 945 : return JS_ClearWatchPoint(cx, obj, id, NULL, NULL);
1228 : }
1229 :
1230 : #endif /* JS_HAS_OBJ_WATCHPOINT */
1231 :
1232 : /*
1233 : * Prototype and property query methods, to complement the 'in' and
1234 : * 'instanceof' operators.
1235 : */
1236 :
1237 : /* Proposed ECMA 15.2.4.5. */
1238 : static JSBool
1239 90388 : obj_hasOwnProperty(JSContext *cx, unsigned argc, Value *vp)
1240 : {
1241 90388 : JSObject *obj = ToObject(cx, &vp[1]);
1242 90388 : if (!obj)
1243 0 : return false;
1244 90388 : return js_HasOwnPropertyHelper(cx, obj->getOps()->lookupGeneric, argc, vp);
1245 : }
1246 :
1247 : JSBool
1248 90388 : js_HasOwnPropertyHelper(JSContext *cx, LookupGenericOp lookup, unsigned argc,
1249 : Value *vp)
1250 : {
1251 : jsid id;
1252 90388 : if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1253 0 : return JS_FALSE;
1254 :
1255 90388 : JSObject *obj = ToObject(cx, &vp[1]);
1256 90388 : if (!obj)
1257 0 : return false;
1258 : JSObject *obj2;
1259 : JSProperty *prop;
1260 90388 : if (obj->isProxy()) {
1261 : bool has;
1262 0 : if (!Proxy::hasOwn(cx, obj, id, &has))
1263 0 : return false;
1264 0 : vp->setBoolean(has);
1265 0 : return true;
1266 : }
1267 90388 : if (!js_HasOwnProperty(cx, lookup, obj, id, &obj2, &prop))
1268 0 : return JS_FALSE;
1269 90388 : vp->setBoolean(!!prop);
1270 90388 : return JS_TRUE;
1271 : }
1272 :
1273 : JSBool
1274 399692 : js_HasOwnProperty(JSContext *cx, LookupGenericOp lookup, JSObject *obj, jsid id,
1275 : JSObject **objp, JSProperty **propp)
1276 : {
1277 799384 : JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING);
1278 399692 : if (!(lookup ? lookup : js_LookupProperty)(cx, obj, id, objp, propp))
1279 0 : return false;
1280 399692 : if (!*propp)
1281 297101 : return true;
1282 :
1283 102591 : if (*objp == obj)
1284 80064 : return true;
1285 :
1286 22527 : JSObject *outer = NULL;
1287 22527 : if (JSObjectOp op = (*objp)->getClass()->ext.outerObject) {
1288 0 : outer = op(cx, *objp);
1289 0 : if (!outer)
1290 0 : return false;
1291 : }
1292 :
1293 22527 : if (outer != *objp)
1294 22527 : *propp = NULL;
1295 22527 : return true;
1296 : }
1297 :
1298 : /* ES5 15.2.4.6. */
1299 : static JSBool
1300 0 : obj_isPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
1301 : {
1302 : /* Step 1. */
1303 0 : if (argc < 1 || !vp[2].isObject()) {
1304 0 : vp->setBoolean(false);
1305 0 : return true;
1306 : }
1307 :
1308 : /* Step 2. */
1309 0 : JSObject *obj = ToObject(cx, &vp[1]);
1310 0 : if (!obj)
1311 0 : return false;
1312 :
1313 : /* Step 3. */
1314 0 : vp->setBoolean(js_IsDelegate(cx, obj, vp[2]));
1315 0 : return true;
1316 : }
1317 :
1318 : /* ES5 15.2.4.7. */
1319 : static JSBool
1320 9 : obj_propertyIsEnumerable(JSContext *cx, unsigned argc, Value *vp)
1321 : {
1322 : /* Step 1. */
1323 : jsid id;
1324 9 : if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1325 0 : return false;
1326 :
1327 : /* Step 2. */
1328 9 : JSObject *obj = ToObject(cx, &vp[1]);
1329 9 : if (!obj)
1330 9 : return false;
1331 :
1332 : /* Steps 3-5. */
1333 0 : return js_PropertyIsEnumerable(cx, obj, id, vp);
1334 : }
1335 :
1336 : JSBool
1337 0 : js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1338 : {
1339 : JSObject *pobj;
1340 : JSProperty *prop;
1341 0 : if (!obj->lookupGeneric(cx, id, &pobj, &prop))
1342 0 : return false;
1343 :
1344 0 : if (!prop) {
1345 0 : vp->setBoolean(false);
1346 0 : return true;
1347 : }
1348 :
1349 : /*
1350 : * ECMA spec botch: return false unless hasOwnProperty. Leaving "own" out
1351 : * of propertyIsEnumerable's name was a mistake.
1352 : */
1353 0 : if (pobj != obj) {
1354 0 : vp->setBoolean(false);
1355 0 : return true;
1356 : }
1357 :
1358 : unsigned attrs;
1359 0 : if (!pobj->getGenericAttributes(cx, id, &attrs))
1360 0 : return false;
1361 :
1362 0 : vp->setBoolean((attrs & JSPROP_ENUMERATE) != 0);
1363 0 : return true;
1364 : }
1365 :
1366 : #if OLD_GETTER_SETTER_METHODS
1367 :
1368 : const char js_defineGetter_str[] = "__defineGetter__";
1369 : const char js_defineSetter_str[] = "__defineSetter__";
1370 : const char js_lookupGetter_str[] = "__lookupGetter__";
1371 : const char js_lookupSetter_str[] = "__lookupSetter__";
1372 :
1373 : enum DefineType { Getter, Setter };
1374 :
1375 : template<DefineType Type>
1376 : static bool
1377 729 : DefineAccessor(JSContext *cx, unsigned argc, Value *vp)
1378 : {
1379 729 : CallArgs args = CallArgsFromVp(argc, vp);
1380 729 : if (!BoxNonStrictThis(cx, args))
1381 0 : return false;
1382 :
1383 729 : if (args.length() < 2 || !js_IsCallable(args[1])) {
1384 9 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1385 : JSMSG_BAD_GETTER_OR_SETTER,
1386 : Type == Getter ? js_getter_str : js_setter_str);
1387 9 : return false;
1388 : }
1389 :
1390 : jsid id;
1391 720 : if (!ValueToId(cx, args[0], &id))
1392 0 : return false;
1393 :
1394 720 : JSObject *descObj = NewBuiltinClassInstance(cx, &ObjectClass);
1395 720 : if (!descObj)
1396 0 : return false;
1397 :
1398 720 : JSAtomState &state = cx->runtime->atomState;
1399 : /* enumerable: true */
1400 720 : if (!descObj->defineProperty(cx, state.enumerableAtom, BooleanValue(true)))
1401 0 : return false;
1402 :
1403 : /* configurable: true */
1404 720 : if (!descObj->defineProperty(cx, state.configurableAtom, BooleanValue(true)))
1405 0 : return false;
1406 :
1407 : /* enumerable: true */
1408 720 : PropertyName *acc = (Type == Getter) ? state.getAtom : state.setAtom;
1409 720 : if (!descObj->defineProperty(cx, acc, args[1]))
1410 0 : return false;
1411 :
1412 : JSBool dummy;
1413 720 : if (!js_DefineOwnProperty(cx, &args.thisv().toObject(), id, ObjectValue(*descObj), &dummy))
1414 0 : return false;
1415 720 : args.rval().setUndefined();
1416 720 : return true;
1417 : }
1418 :
1419 : JS_FRIEND_API(JSBool)
1420 288 : js::obj_defineGetter(JSContext *cx, unsigned argc, Value *vp)
1421 : {
1422 288 : return DefineAccessor<Getter>(cx, argc, vp);
1423 : }
1424 :
1425 : JS_FRIEND_API(JSBool)
1426 441 : js::obj_defineSetter(JSContext *cx, unsigned argc, Value *vp)
1427 : {
1428 441 : return DefineAccessor<Setter>(cx, argc, vp);
1429 : }
1430 :
1431 : static JSBool
1432 0 : obj_lookupGetter(JSContext *cx, unsigned argc, Value *vp)
1433 : {
1434 : jsid id;
1435 0 : if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1436 0 : return JS_FALSE;
1437 0 : JSObject *obj = ToObject(cx, &vp[1]);
1438 0 : if (!obj)
1439 0 : return JS_FALSE;
1440 : JSObject *pobj;
1441 : JSProperty *prop;
1442 0 : if (!obj->lookupGeneric(cx, id, &pobj, &prop))
1443 0 : return JS_FALSE;
1444 0 : vp->setUndefined();
1445 0 : if (prop) {
1446 0 : if (pobj->isNative()) {
1447 0 : Shape *shape = (Shape *) prop;
1448 0 : if (shape->hasGetterValue())
1449 0 : *vp = shape->getterValue();
1450 : }
1451 : }
1452 0 : return JS_TRUE;
1453 : }
1454 :
1455 : static JSBool
1456 0 : obj_lookupSetter(JSContext *cx, unsigned argc, Value *vp)
1457 : {
1458 : jsid id;
1459 0 : if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1460 0 : return JS_FALSE;
1461 0 : JSObject *obj = ToObject(cx, &vp[1]);
1462 0 : if (!obj)
1463 0 : return JS_FALSE;
1464 : JSObject *pobj;
1465 : JSProperty *prop;
1466 0 : if (!obj->lookupGeneric(cx, id, &pobj, &prop))
1467 0 : return JS_FALSE;
1468 0 : vp->setUndefined();
1469 0 : if (prop) {
1470 0 : if (pobj->isNative()) {
1471 0 : Shape *shape = (Shape *) prop;
1472 0 : if (shape->hasSetterValue())
1473 0 : *vp = shape->setterValue();
1474 : }
1475 : }
1476 0 : return JS_TRUE;
1477 : }
1478 : #endif /* OLD_GETTER_SETTER_METHODS */
1479 :
1480 : JSBool
1481 684 : obj_getPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
1482 : {
1483 684 : if (argc == 0) {
1484 0 : js_ReportMissingArg(cx, *vp, 0);
1485 0 : return JS_FALSE;
1486 : }
1487 :
1488 684 : if (vp[2].isPrimitive()) {
1489 9 : char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, vp[2], NULL);
1490 9 : if (!bytes)
1491 0 : return JS_FALSE;
1492 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1493 9 : JSMSG_UNEXPECTED_TYPE, bytes, "not an object");
1494 9 : JS_free(cx, bytes);
1495 9 : return JS_FALSE;
1496 : }
1497 :
1498 675 : JSObject *obj = &vp[2].toObject();
1499 : unsigned attrs;
1500 675 : return CheckAccess(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
1501 675 : JSACC_PROTO, vp, &attrs);
1502 : }
1503 :
1504 : namespace js {
1505 :
1506 : bool
1507 13988 : NewPropertyDescriptorObject(JSContext *cx, const PropertyDescriptor *desc, Value *vp)
1508 : {
1509 13988 : if (!desc->obj) {
1510 432 : vp->setUndefined();
1511 432 : return true;
1512 : }
1513 :
1514 : /* We have our own property, so start creating the descriptor. */
1515 13556 : PropDesc d;
1516 13556 : d.initFromPropertyDescriptor(*desc);
1517 13556 : if (!d.makeObject(cx))
1518 0 : return false;
1519 13556 : *vp = d.pd;
1520 13556 : return true;
1521 : }
1522 :
1523 : void
1524 13556 : PropDesc::initFromPropertyDescriptor(const PropertyDescriptor &desc)
1525 : {
1526 13556 : pd.setUndefined();
1527 13556 : attrs = uint8_t(desc.attrs);
1528 13556 : JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
1529 13556 : if (desc.attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1530 2000 : hasGet = true;
1531 : get = ((desc.attrs & JSPROP_GETTER) && desc.getter)
1532 1991 : ? CastAsObjectJsval(desc.getter)
1533 3991 : : UndefinedValue();
1534 2000 : hasSet = true;
1535 : set = ((desc.attrs & JSPROP_SETTER) && desc.setter)
1536 63 : ? CastAsObjectJsval(desc.setter)
1537 2063 : : UndefinedValue();
1538 2000 : hasValue = false;
1539 2000 : value.setUndefined();
1540 2000 : hasWritable = false;
1541 : } else {
1542 11556 : hasGet = false;
1543 11556 : get.setUndefined();
1544 11556 : hasSet = false;
1545 11556 : set.setUndefined();
1546 11556 : hasValue = true;
1547 11556 : value = desc.value;
1548 11556 : hasWritable = true;
1549 : }
1550 13556 : hasEnumerable = true;
1551 13556 : hasConfigurable = true;
1552 13556 : }
1553 :
1554 : bool
1555 13565 : PropDesc::makeObject(JSContext *cx)
1556 : {
1557 13565 : JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass);
1558 13565 : if (!obj)
1559 0 : return false;
1560 :
1561 13565 : const JSAtomState &atomState = cx->runtime->atomState;
1562 67825 : if ((hasConfigurable &&
1563 : !obj->defineProperty(cx, atomState.configurableAtom,
1564 13565 : BooleanValue((attrs & JSPROP_PERMANENT) == 0))) ||
1565 : (hasEnumerable &&
1566 : !obj->defineProperty(cx, atomState.enumerableAtom,
1567 13565 : BooleanValue((attrs & JSPROP_ENUMERATE) != 0))) ||
1568 : (hasGet &&
1569 2000 : !obj->defineProperty(cx, atomState.getAtom, get)) ||
1570 : (hasSet &&
1571 2000 : !obj->defineProperty(cx, atomState.setAtom, set)) ||
1572 : (hasValue &&
1573 11565 : !obj->defineProperty(cx, atomState.valueAtom, value)) ||
1574 : (hasWritable &&
1575 : !obj->defineProperty(cx, atomState.writableAtom,
1576 11565 : BooleanValue((attrs & JSPROP_READONLY) == 0))))
1577 : {
1578 0 : return false;
1579 : }
1580 :
1581 13565 : pd.setObject(*obj);
1582 13565 : return true;
1583 : }
1584 :
1585 : bool
1586 12231 : GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, PropertyDescriptor *desc)
1587 : {
1588 12231 : if (obj->isProxy())
1589 45 : return Proxy::getOwnPropertyDescriptor(cx, obj, id, false, desc);
1590 :
1591 : JSObject *pobj;
1592 : JSProperty *prop;
1593 12186 : if (!js_HasOwnProperty(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &prop))
1594 0 : return false;
1595 12186 : if (!prop) {
1596 432 : desc->obj = NULL;
1597 432 : return true;
1598 : }
1599 :
1600 11754 : bool doGet = true;
1601 11754 : if (pobj->isNative()) {
1602 11619 : Shape *shape = (Shape *) prop;
1603 11619 : desc->attrs = shape->attributes();
1604 11619 : if (desc->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1605 234 : doGet = false;
1606 234 : if (desc->attrs & JSPROP_GETTER)
1607 234 : desc->getter = CastAsPropertyOp(shape->getterObject());
1608 234 : if (desc->attrs & JSPROP_SETTER)
1609 63 : desc->setter = CastAsStrictPropertyOp(shape->setterObject());
1610 : }
1611 : } else {
1612 135 : if (!pobj->getGenericAttributes(cx, id, &desc->attrs))
1613 0 : return false;
1614 : }
1615 :
1616 11754 : if (doGet && !obj->getGeneric(cx, id, &desc->value))
1617 0 : return false;
1618 :
1619 11754 : desc->obj = obj;
1620 11754 : return true;
1621 : }
1622 :
1623 : bool
1624 11394 : GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1625 : {
1626 22788 : AutoPropertyDescriptorRooter desc(cx);
1627 11394 : return GetOwnPropertyDescriptor(cx, obj, id, &desc) &&
1628 11394 : NewPropertyDescriptorObject(cx, &desc, vp);
1629 : }
1630 :
1631 : }
1632 :
1633 : static bool
1634 312521 : GetFirstArgumentAsObject(JSContext *cx, unsigned argc, Value *vp, const char *method, JSObject **objp)
1635 : {
1636 312521 : if (argc == 0) {
1637 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1638 0 : method, "0", "s");
1639 0 : return false;
1640 : }
1641 :
1642 312521 : const Value &v = vp[2];
1643 312521 : if (!v.isObject()) {
1644 36 : char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
1645 36 : if (!bytes)
1646 0 : return false;
1647 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
1648 36 : bytes, "not an object");
1649 36 : JS_free(cx, bytes);
1650 36 : return false;
1651 : }
1652 :
1653 312485 : *objp = &v.toObject();
1654 312485 : return true;
1655 : }
1656 :
1657 : static JSBool
1658 11394 : obj_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
1659 : {
1660 : JSObject *obj;
1661 11394 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyDescriptor", &obj))
1662 0 : return JS_FALSE;
1663 22788 : AutoIdRooter nameidr(cx);
1664 11394 : if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), nameidr.addr()))
1665 0 : return JS_FALSE;
1666 11394 : return GetOwnPropertyDescriptor(cx, obj, nameidr.id(), vp);
1667 : }
1668 :
1669 : static JSBool
1670 486 : obj_keys(JSContext *cx, unsigned argc, Value *vp)
1671 : {
1672 : JSObject *obj;
1673 486 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.keys", &obj))
1674 0 : return false;
1675 :
1676 972 : AutoIdVector props(cx);
1677 486 : if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &props))
1678 0 : return false;
1679 :
1680 972 : AutoValueVector vals(cx);
1681 486 : if (!vals.reserve(props.length()))
1682 0 : return false;
1683 1179 : for (size_t i = 0, len = props.length(); i < len; i++) {
1684 693 : jsid id = props[i];
1685 693 : if (JSID_IS_STRING(id)) {
1686 225 : vals.infallibleAppend(StringValue(JSID_TO_STRING(id)));
1687 468 : } else if (JSID_IS_INT(id)) {
1688 468 : JSString *str = js_IntToString(cx, JSID_TO_INT(id));
1689 468 : if (!str)
1690 0 : return false;
1691 468 : vals.infallibleAppend(StringValue(str));
1692 : } else {
1693 0 : JS_ASSERT(JSID_IS_OBJECT(id));
1694 : }
1695 : }
1696 :
1697 486 : JS_ASSERT(props.length() <= UINT32_MAX);
1698 486 : JSObject *aobj = NewDenseCopiedArray(cx, uint32_t(vals.length()), vals.begin());
1699 486 : if (!aobj)
1700 0 : return false;
1701 486 : vp->setObject(*aobj);
1702 :
1703 486 : return true;
1704 : }
1705 :
1706 : static bool
1707 1809174 : HasProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, bool *foundp)
1708 : {
1709 1809174 : if (!obj->hasProperty(cx, id, foundp, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING))
1710 0 : return false;
1711 1809174 : if (!*foundp) {
1712 1201102 : vp->setUndefined();
1713 1201102 : return true;
1714 : }
1715 :
1716 : /*
1717 : * We must go through the method read barrier in case id is 'get' or 'set'.
1718 : * There is no obvious way to defer cloning a joined function object whose
1719 : * identity will be used by DefinePropertyOnObject, e.g., or reflected via
1720 : * js::GetOwnPropertyDescriptor, as the getter or setter callable object.
1721 : */
1722 608072 : return !!obj->getGeneric(cx, id, vp);
1723 : }
1724 :
1725 315085 : PropDesc::PropDesc()
1726 : : pd(UndefinedValue()),
1727 : value(UndefinedValue()),
1728 : get(UndefinedValue()),
1729 : set(UndefinedValue()),
1730 : attrs(0),
1731 : hasGet(false),
1732 : hasSet(false),
1733 : hasValue(false),
1734 : hasWritable(false),
1735 : hasEnumerable(false),
1736 315085 : hasConfigurable(false)
1737 : {
1738 315085 : }
1739 :
1740 : bool
1741 301529 : PropDesc::initialize(JSContext *cx, const Value &origval, bool checkAccessors)
1742 : {
1743 301529 : Value v = origval;
1744 :
1745 : /* 8.10.5 step 1 */
1746 301529 : if (v.isPrimitive()) {
1747 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
1748 0 : return false;
1749 : }
1750 301529 : JSObject *desc = &v.toObject();
1751 :
1752 : /* Make a copy of the descriptor. We might need it later. */
1753 301529 : pd = v;
1754 :
1755 : /* Start with the proper defaults. */
1756 301529 : attrs = JSPROP_PERMANENT | JSPROP_READONLY;
1757 :
1758 : bool found;
1759 :
1760 : /* 8.10.5 step 3 */
1761 : #ifdef __GNUC__ /* quell GCC overwarning */
1762 301529 : found = false;
1763 : #endif
1764 301529 : if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.enumerableAtom), &v, &found))
1765 0 : return false;
1766 301529 : if (found) {
1767 299305 : hasEnumerable = JS_TRUE;
1768 299305 : if (js_ValueToBoolean(v))
1769 295812 : attrs |= JSPROP_ENUMERATE;
1770 : }
1771 :
1772 : /* 8.10.5 step 4 */
1773 301529 : if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.configurableAtom), &v, &found))
1774 0 : return false;
1775 301529 : if (found) {
1776 4366 : hasConfigurable = JS_TRUE;
1777 4366 : if (js_ValueToBoolean(v))
1778 864 : attrs &= ~JSPROP_PERMANENT;
1779 : }
1780 :
1781 : /* 8.10.5 step 5 */
1782 301529 : if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.valueAtom), &v, &found))
1783 0 : return false;
1784 301529 : if (found) {
1785 747 : hasValue = true;
1786 747 : value = v;
1787 : }
1788 :
1789 : /* 8.10.6 step 6 */
1790 301529 : if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.writableAtom), &v, &found))
1791 0 : return false;
1792 301529 : if (found) {
1793 216 : hasWritable = JS_TRUE;
1794 216 : if (js_ValueToBoolean(v))
1795 108 : attrs &= ~JSPROP_READONLY;
1796 : }
1797 :
1798 : /* 8.10.7 step 7 */
1799 301529 : if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.getAtom), &v, &found))
1800 0 : return false;
1801 301529 : if (found) {
1802 299107 : hasGet = true;
1803 299107 : get = v;
1804 299107 : attrs |= JSPROP_GETTER | JSPROP_SHARED;
1805 299107 : attrs &= ~JSPROP_READONLY;
1806 299107 : if (checkAccessors && !checkGetter(cx))
1807 0 : return false;
1808 : }
1809 :
1810 : /* 8.10.7 step 8 */
1811 301529 : if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.setAtom), &v, &found))
1812 0 : return false;
1813 301529 : if (found) {
1814 4331 : hasSet = true;
1815 4331 : set = v;
1816 4331 : attrs |= JSPROP_SETTER | JSPROP_SHARED;
1817 4331 : attrs &= ~JSPROP_READONLY;
1818 4331 : if (checkAccessors && !checkSetter(cx))
1819 0 : return false;
1820 : }
1821 :
1822 : /* 8.10.7 step 9 */
1823 301529 : if ((hasGet || hasSet) && (hasValue || hasWritable)) {
1824 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INVALID_DESCRIPTOR);
1825 0 : return false;
1826 : }
1827 :
1828 301529 : JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
1829 :
1830 301529 : return true;
1831 : }
1832 :
1833 : namespace js {
1834 :
1835 : bool
1836 783 : Throw(JSContext *cx, jsid id, unsigned errorNumber)
1837 : {
1838 783 : JS_ASSERT(js_ErrorFormatString[errorNumber].argCount == 1);
1839 :
1840 783 : JSString *idstr = IdToString(cx, id);
1841 783 : if (!idstr)
1842 0 : return false;
1843 1566 : JSAutoByteString bytes(cx, idstr);
1844 783 : if (!bytes)
1845 0 : return false;
1846 783 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber, bytes.ptr());
1847 783 : return false;
1848 : }
1849 :
1850 : bool
1851 0 : Throw(JSContext *cx, JSObject *obj, unsigned errorNumber)
1852 : {
1853 0 : if (js_ErrorFormatString[errorNumber].argCount == 1) {
1854 : js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,
1855 : JSDVG_IGNORE_STACK, ObjectValue(*obj),
1856 0 : NULL, NULL, NULL);
1857 : } else {
1858 0 : JS_ASSERT(js_ErrorFormatString[errorNumber].argCount == 0);
1859 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber);
1860 : }
1861 0 : return false;
1862 : }
1863 :
1864 : } /* namespace js */
1865 :
1866 : static JSBool
1867 27 : Reject(JSContext *cx, unsigned errorNumber, bool throwError, jsid id, bool *rval)
1868 : {
1869 27 : if (throwError)
1870 27 : return Throw(cx, id, errorNumber);
1871 :
1872 0 : *rval = false;
1873 0 : return true;
1874 : }
1875 :
1876 : static JSBool
1877 0 : Reject(JSContext *cx, JSObject *obj, unsigned errorNumber, bool throwError, bool *rval)
1878 : {
1879 0 : if (throwError)
1880 0 : return Throw(cx, obj, errorNumber);
1881 :
1882 0 : *rval = false;
1883 0 : return JS_TRUE;
1884 : }
1885 :
1886 : static JSBool
1887 297118 : DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc,
1888 : bool throwError, bool *rval)
1889 : {
1890 : /* 8.12.9 step 1. */
1891 : JSProperty *current;
1892 : JSObject *obj2;
1893 297118 : JS_ASSERT(!obj->getOps()->lookupGeneric);
1894 297118 : if (!js_HasOwnProperty(cx, NULL, obj, id, &obj2, ¤t))
1895 0 : return JS_FALSE;
1896 :
1897 297118 : JS_ASSERT(!obj->getOps()->defineProperty);
1898 :
1899 : /* 8.12.9 steps 2-4. */
1900 297118 : if (!current) {
1901 296461 : if (!obj->isExtensible())
1902 0 : return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
1903 :
1904 296461 : *rval = true;
1905 :
1906 296461 : if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
1907 432 : JS_ASSERT(!obj->getOps()->defineProperty);
1908 : return js_DefineProperty(cx, obj, id, &desc.value,
1909 432 : JS_PropertyStub, JS_StrictPropertyStub, desc.attrs);
1910 : }
1911 :
1912 296029 : JS_ASSERT(desc.isAccessorDescriptor());
1913 :
1914 : /*
1915 : * Getters and setters are just like watchpoints from an access
1916 : * control point of view.
1917 : */
1918 : Value dummy;
1919 : unsigned dummyAttrs;
1920 296029 : if (!CheckAccess(cx, obj, id, JSACC_WATCH, &dummy, &dummyAttrs))
1921 0 : return JS_FALSE;
1922 :
1923 296029 : Value tmp = UndefinedValue();
1924 : return js_DefineProperty(cx, obj, id, &tmp,
1925 296029 : desc.getter(), desc.setter(), desc.attrs);
1926 : }
1927 :
1928 : /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
1929 657 : Value v = UndefinedValue();
1930 :
1931 657 : JS_ASSERT(obj == obj2);
1932 :
1933 657 : const Shape *shape = reinterpret_cast<Shape *>(current);
1934 : do {
1935 657 : if (desc.isAccessorDescriptor()) {
1936 270 : if (!shape->isAccessorDescriptor())
1937 108 : break;
1938 :
1939 162 : if (desc.hasGet) {
1940 : bool same;
1941 9 : if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same))
1942 0 : return false;
1943 9 : if (!same)
1944 9 : break;
1945 : }
1946 :
1947 153 : if (desc.hasSet) {
1948 : bool same;
1949 153 : if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
1950 0 : return false;
1951 153 : if (!same)
1952 153 : break;
1953 : }
1954 : } else {
1955 : /*
1956 : * Determine the current value of the property once, if the current
1957 : * value might actually need to be used or preserved later. NB: we
1958 : * guard on whether the current property is a data descriptor to
1959 : * avoid calling a getter; we won't need the value if it's not a
1960 : * data descriptor.
1961 : */
1962 387 : if (shape->isDataDescriptor()) {
1963 : /*
1964 : * We must rule out a non-configurable js::PropertyOp-guarded
1965 : * property becoming a writable unguarded data property, since
1966 : * such a property can have its value changed to one the getter
1967 : * and setter preclude.
1968 : *
1969 : * A desc lacking writable but with value is a data descriptor
1970 : * and we must reject it as if it had writable: true if current
1971 : * is writable.
1972 : */
1973 459 : if (!shape->configurable() &&
1974 90 : (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()) &&
1975 0 : desc.isDataDescriptor() &&
1976 0 : (desc.hasWritable ? desc.writable() : shape->writable()))
1977 : {
1978 0 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
1979 : }
1980 :
1981 369 : if (!js_NativeGet(cx, obj, obj2, shape, 0, &v))
1982 0 : return JS_FALSE;
1983 : }
1984 :
1985 387 : if (desc.isDataDescriptor()) {
1986 360 : if (!shape->isDataDescriptor())
1987 9 : break;
1988 :
1989 : bool same;
1990 351 : if (desc.hasValue) {
1991 324 : if (!SameValue(cx, desc.value, v, &same))
1992 0 : return false;
1993 324 : if (!same) {
1994 : /*
1995 : * Insist that a non-configurable js::PropertyOp data
1996 : * property is frozen at exactly the last-got value.
1997 : *
1998 : * Duplicate the first part of the big conjunction that
1999 : * we tested above, rather than add a local bool flag.
2000 : * Likewise, don't try to keep shape->writable() in a
2001 : * flag we veto from true to false for non-configurable
2002 : * PropertyOp-based data properties and test before the
2003 : * SameValue check later on in order to re-use that "if
2004 : * (!SameValue) Reject" logic.
2005 : *
2006 : * This function is large and complex enough that it
2007 : * seems best to repeat a small bit of code and return
2008 : * Reject(...) ASAP, instead of being clever.
2009 : */
2010 378 : if (!shape->configurable() &&
2011 54 : (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()))
2012 : {
2013 0 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
2014 : }
2015 324 : break;
2016 : }
2017 : }
2018 27 : if (desc.hasWritable && desc.writable() != shape->writable())
2019 27 : break;
2020 : } else {
2021 : /* The only fields in desc will be handled below. */
2022 27 : JS_ASSERT(desc.isGenericDescriptor());
2023 : }
2024 : }
2025 :
2026 27 : if (desc.hasConfigurable && desc.configurable() != shape->configurable())
2027 18 : break;
2028 9 : if (desc.hasEnumerable && desc.enumerable() != shape->enumerable())
2029 9 : break;
2030 :
2031 : /* The conditions imposed by step 5 or step 6 apply. */
2032 0 : *rval = true;
2033 0 : return JS_TRUE;
2034 : } while (0);
2035 :
2036 : /* 8.12.9 step 7. */
2037 657 : if (!shape->configurable()) {
2038 : /*
2039 : * Since [[Configurable]] defaults to false, we don't need to check
2040 : * whether it was specified. We can't do likewise for [[Enumerable]]
2041 : * because its putative value is used in a comparison -- a comparison
2042 : * whose result must always be false per spec if the [[Enumerable]]
2043 : * field is not present. Perfectly pellucid logic, eh?
2044 : */
2045 45 : JS_ASSERT_IF(!desc.hasConfigurable, !desc.configurable());
2046 45 : if (desc.configurable() ||
2047 0 : (desc.hasEnumerable && desc.enumerable() != shape->enumerable())) {
2048 0 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
2049 : }
2050 : }
2051 :
2052 657 : bool callDelProperty = false;
2053 :
2054 657 : if (desc.isGenericDescriptor()) {
2055 : /* 8.12.9 step 8, no validation required */
2056 630 : } else if (desc.isDataDescriptor() != shape->isDataDescriptor()) {
2057 : /* 8.12.9 step 9. */
2058 117 : if (!shape->configurable())
2059 0 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
2060 513 : } else if (desc.isDataDescriptor()) {
2061 : /* 8.12.9 step 10. */
2062 351 : JS_ASSERT(shape->isDataDescriptor());
2063 351 : if (!shape->configurable() && !shape->writable()) {
2064 27 : if (desc.hasWritable && desc.writable())
2065 0 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
2066 27 : if (desc.hasValue) {
2067 : bool same;
2068 27 : if (!SameValue(cx, desc.value, v, &same))
2069 0 : return false;
2070 27 : if (!same)
2071 27 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
2072 : }
2073 : }
2074 :
2075 324 : callDelProperty = !shape->hasDefaultGetter() || !shape->hasDefaultSetter();
2076 : } else {
2077 : /* 8.12.9 step 11. */
2078 162 : JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor());
2079 162 : if (!shape->configurable()) {
2080 0 : if (desc.hasSet) {
2081 : bool same;
2082 0 : if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
2083 0 : return false;
2084 0 : if (!same)
2085 0 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
2086 : }
2087 :
2088 0 : if (desc.hasGet) {
2089 : bool same;
2090 0 : if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same))
2091 0 : return false;
2092 0 : if (!same)
2093 0 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
2094 : }
2095 : }
2096 : }
2097 :
2098 : /* 8.12.9 step 12. */
2099 : unsigned attrs;
2100 : PropertyOp getter;
2101 : StrictPropertyOp setter;
2102 630 : if (desc.isGenericDescriptor()) {
2103 27 : unsigned changed = 0;
2104 27 : if (desc.hasConfigurable)
2105 18 : changed |= JSPROP_PERMANENT;
2106 27 : if (desc.hasEnumerable)
2107 9 : changed |= JSPROP_ENUMERATE;
2108 :
2109 27 : attrs = (shape->attributes() & ~changed) | (desc.attrs & changed);
2110 27 : getter = shape->getter();
2111 27 : setter = shape->setter();
2112 603 : } else if (desc.isDataDescriptor()) {
2113 333 : unsigned unchanged = 0;
2114 333 : if (!desc.hasConfigurable)
2115 333 : unchanged |= JSPROP_PERMANENT;
2116 333 : if (!desc.hasEnumerable)
2117 315 : unchanged |= JSPROP_ENUMERATE;
2118 : /* Watch out for accessor -> data transformations here. */
2119 333 : if (!desc.hasWritable && shape->isDataDescriptor())
2120 279 : unchanged |= JSPROP_READONLY;
2121 :
2122 333 : if (desc.hasValue)
2123 306 : v = desc.value;
2124 333 : attrs = (desc.attrs & ~unchanged) | (shape->attributes() & unchanged);
2125 333 : getter = JS_PropertyStub;
2126 333 : setter = JS_StrictPropertyStub;
2127 : } else {
2128 270 : JS_ASSERT(desc.isAccessorDescriptor());
2129 :
2130 : /*
2131 : * Getters and setters are just like watchpoints from an access
2132 : * control point of view.
2133 : */
2134 : Value dummy;
2135 270 : if (!CheckAccess(cx, obj2, id, JSACC_WATCH, &dummy, &attrs))
2136 0 : return JS_FALSE;
2137 :
2138 : /* 8.12.9 step 12. */
2139 270 : unsigned changed = 0;
2140 270 : if (desc.hasConfigurable)
2141 243 : changed |= JSPROP_PERMANENT;
2142 270 : if (desc.hasEnumerable)
2143 234 : changed |= JSPROP_ENUMERATE;
2144 270 : if (desc.hasGet)
2145 72 : changed |= JSPROP_GETTER | JSPROP_SHARED | JSPROP_READONLY;
2146 270 : if (desc.hasSet)
2147 198 : changed |= JSPROP_SETTER | JSPROP_SHARED | JSPROP_READONLY;
2148 :
2149 270 : attrs = (desc.attrs & changed) | (shape->attributes() & ~changed);
2150 270 : if (desc.hasGet) {
2151 72 : getter = desc.getter();
2152 : } else {
2153 387 : getter = (shape->hasDefaultGetter() && !shape->hasGetterValue())
2154 : ? JS_PropertyStub
2155 387 : : shape->getter();
2156 : }
2157 270 : if (desc.hasSet) {
2158 198 : setter = desc.setter();
2159 : } else {
2160 144 : setter = (shape->hasDefaultSetter() && !shape->hasSetterValue())
2161 : ? JS_StrictPropertyStub
2162 144 : : shape->setter();
2163 : }
2164 : }
2165 :
2166 630 : *rval = true;
2167 :
2168 : /*
2169 : * Since "data" properties implemented using native C functions may rely on
2170 : * side effects during setting, we must make them aware that they have been
2171 : * "assigned"; deleting the property before redefining it does the trick.
2172 : * See bug 539766, where we ran into problems when we redefined
2173 : * arguments.length without making the property aware that its value had
2174 : * been changed (which would have happened if we had deleted it before
2175 : * redefining it or we had invoked its setter to change its value).
2176 : */
2177 630 : if (callDelProperty) {
2178 18 : Value dummy = UndefinedValue();
2179 18 : if (!CallJSPropertyOp(cx, obj2->getClass()->delProperty, obj2, id, &dummy))
2180 0 : return false;
2181 : }
2182 :
2183 630 : return js_DefineProperty(cx, obj, id, &v, getter, setter, attrs);
2184 : }
2185 :
2186 : static JSBool
2187 225 : DefinePropertyOnArray(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc,
2188 : bool throwError, bool *rval)
2189 : {
2190 : /*
2191 : * We probably should optimize dense array property definitions where
2192 : * the descriptor describes a traditional array property (enumerable,
2193 : * configurable, writable, numeric index or length without altering its
2194 : * attributes). Such definitions are probably unlikely, so we don't bother
2195 : * for now.
2196 : */
2197 225 : if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
2198 0 : return JS_FALSE;
2199 :
2200 225 : uint32_t oldLen = obj->getArrayLength();
2201 :
2202 225 : if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
2203 : /*
2204 : * Our optimization of storage of the length property of arrays makes
2205 : * it very difficult to properly implement defining the property. For
2206 : * now simply throw an exception (NB: not merely Reject) on any attempt
2207 : * to define the "length" property, rather than attempting to implement
2208 : * some difficult-for-authors-to-grasp subset of that functionality.
2209 : */
2210 9 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_DEFINE_ARRAY_LENGTH);
2211 9 : return JS_FALSE;
2212 : }
2213 :
2214 : uint32_t index;
2215 216 : if (js_IdIsIndex(id, &index)) {
2216 : /*
2217 : // Disabled until we support defining "length":
2218 : if (index >= oldLen && lengthPropertyNotWritable())
2219 : return ThrowTypeError(cx, JSMSG_CANT_APPEND_TO_ARRAY);
2220 : */
2221 216 : if (!DefinePropertyOnObject(cx, obj, id, desc, false, rval))
2222 0 : return JS_FALSE;
2223 216 : if (!*rval)
2224 0 : return Reject(cx, obj, JSMSG_CANT_DEFINE_ARRAY_INDEX, throwError, rval);
2225 :
2226 216 : if (index >= oldLen) {
2227 81 : JS_ASSERT(index != UINT32_MAX);
2228 81 : obj->setArrayLength(cx, index + 1);
2229 : }
2230 :
2231 216 : *rval = true;
2232 216 : return JS_TRUE;
2233 : }
2234 :
2235 0 : return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval);
2236 : }
2237 :
2238 : namespace js {
2239 :
2240 : bool
2241 298926 : DefineProperty(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc, bool throwError,
2242 : bool *rval)
2243 : {
2244 298926 : if (obj->isArray())
2245 225 : return DefinePropertyOnArray(cx, obj, id, desc, throwError, rval);
2246 :
2247 298701 : if (obj->getOps()->lookupGeneric) {
2248 1799 : if (obj->isProxy())
2249 1799 : return Proxy::defineProperty(cx, obj, id, desc.pd);
2250 0 : return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
2251 : }
2252 :
2253 296902 : return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval);
2254 : }
2255 :
2256 : } /* namespace js */
2257 :
2258 : JSBool
2259 298476 : js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id, const Value &descriptor, JSBool *bp)
2260 : {
2261 596952 : AutoPropDescArrayRooter descs(cx);
2262 298476 : PropDesc *desc = descs.append();
2263 298476 : if (!desc || !desc->initialize(cx, descriptor))
2264 0 : return false;
2265 :
2266 : bool rval;
2267 298476 : if (!DefineProperty(cx, obj, id, *desc, true, &rval))
2268 1763 : return false;
2269 296713 : *bp = !!rval;
2270 296713 : return true;
2271 : }
2272 :
2273 : /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
2274 : static JSBool
2275 297783 : obj_defineProperty(JSContext *cx, unsigned argc, Value *vp)
2276 : {
2277 : JSObject *obj;
2278 297783 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperty", &obj))
2279 27 : return false;
2280 :
2281 : jsid id;
2282 297756 : if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), &id))
2283 0 : return JS_FALSE;
2284 :
2285 297756 : const Value descval = argc >= 3 ? vp[4] : UndefinedValue();
2286 :
2287 : JSBool junk;
2288 297756 : if (!js_DefineOwnProperty(cx, obj, id, descval, &junk))
2289 1763 : return false;
2290 :
2291 295993 : vp->setObject(*obj);
2292 295993 : return true;
2293 : }
2294 :
2295 : namespace js {
2296 :
2297 : bool
2298 270 : ReadPropertyDescriptors(JSContext *cx, JSObject *props, bool checkAccessors,
2299 : AutoIdVector *ids, AutoPropDescArrayRooter *descs)
2300 : {
2301 270 : if (!GetPropertyNames(cx, props, JSITER_OWNONLY, ids))
2302 0 : return false;
2303 :
2304 585 : for (size_t i = 0, len = ids->length(); i < len; i++) {
2305 315 : jsid id = (*ids)[i];
2306 315 : PropDesc* desc = descs->append();
2307 : Value v;
2308 315 : if (!desc || !props->getGeneric(cx, id, &v) || !desc->initialize(cx, v, checkAccessors))
2309 0 : return false;
2310 : }
2311 270 : return true;
2312 : }
2313 :
2314 : } /* namespace js */
2315 :
2316 : static bool
2317 180 : DefineProperties(JSContext *cx, JSObject *obj, JSObject *props)
2318 : {
2319 360 : AutoIdVector ids(cx);
2320 360 : AutoPropDescArrayRooter descs(cx);
2321 180 : if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs))
2322 0 : return false;
2323 :
2324 : bool dummy;
2325 369 : for (size_t i = 0, len = ids.length(); i < len; i++) {
2326 198 : if (!DefineProperty(cx, obj, ids[i], descs[i], true, &dummy))
2327 9 : return false;
2328 : }
2329 :
2330 171 : return true;
2331 : }
2332 :
2333 : extern JSBool
2334 36 : js_PopulateObject(JSContext *cx, JSObject *newborn, JSObject *props)
2335 : {
2336 36 : return DefineProperties(cx, newborn, props);
2337 : }
2338 :
2339 : /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
2340 : static JSBool
2341 81 : obj_defineProperties(JSContext *cx, unsigned argc, Value *vp)
2342 : {
2343 : /* Steps 1 and 7. */
2344 : JSObject *obj;
2345 81 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperties", &obj))
2346 0 : return false;
2347 81 : vp->setObject(*obj);
2348 :
2349 : /* Step 2. */
2350 81 : if (argc < 2) {
2351 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
2352 0 : "Object.defineProperties", "0", "s");
2353 0 : return false;
2354 : }
2355 81 : JSObject *props = ToObject(cx, &vp[3]);
2356 81 : if (!props)
2357 0 : return false;
2358 :
2359 : /* Steps 3-6. */
2360 81 : return DefineProperties(cx, obj, props);
2361 : }
2362 :
2363 : /* ES5 15.2.3.5: Object.create(O [, Properties]) */
2364 : static JSBool
2365 396 : obj_create(JSContext *cx, unsigned argc, Value *vp)
2366 : {
2367 396 : if (argc == 0) {
2368 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
2369 0 : "Object.create", "0", "s");
2370 0 : return false;
2371 : }
2372 :
2373 396 : CallArgs args = CallArgsFromVp(argc, vp);
2374 396 : const Value &v = args[0];
2375 396 : if (!v.isObjectOrNull()) {
2376 27 : char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
2377 27 : if (!bytes)
2378 0 : return false;
2379 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
2380 27 : bytes, "not an object or null");
2381 27 : JS_free(cx, bytes);
2382 27 : return false;
2383 : }
2384 :
2385 369 : JSObject *proto = v.toObjectOrNull();
2386 369 : if (proto && proto->isXML()) {
2387 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XML_PROTO_FORBIDDEN);
2388 0 : return false;
2389 : }
2390 :
2391 : /*
2392 : * Use the callee's global as the parent of the new object to avoid dynamic
2393 : * scoping (i.e., using the caller's global).
2394 : */
2395 369 : JSObject *obj = NewObjectWithGivenProto(cx, &ObjectClass, proto, &args.callee().global());
2396 369 : if (!obj)
2397 0 : return false;
2398 :
2399 : /* Don't track types or array-ness for objects created here. */
2400 369 : MarkTypeObjectUnknownProperties(cx, obj->type());
2401 :
2402 : /* 15.2.3.5 step 4. */
2403 369 : if (args.hasDefined(1)) {
2404 63 : if (args[1].isPrimitive()) {
2405 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
2406 0 : return false;
2407 : }
2408 :
2409 63 : if (!DefineProperties(cx, obj, &args[1].toObject()))
2410 0 : return false;
2411 : }
2412 :
2413 : /* 5. Return obj. */
2414 369 : args.rval().setObject(*obj);
2415 369 : return true;
2416 : }
2417 :
2418 : static JSBool
2419 828 : obj_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp)
2420 : {
2421 : JSObject *obj;
2422 828 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyNames", &obj))
2423 0 : return false;
2424 :
2425 1656 : AutoIdVector keys(cx);
2426 828 : if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &keys))
2427 0 : return false;
2428 :
2429 1656 : AutoValueVector vals(cx);
2430 828 : if (!vals.resize(keys.length()))
2431 0 : return false;
2432 :
2433 14652 : for (size_t i = 0, len = keys.length(); i < len; i++) {
2434 13824 : jsid id = keys[i];
2435 13824 : if (JSID_IS_INT(id)) {
2436 261 : JSString *str = js_IntToString(cx, JSID_TO_INT(id));
2437 261 : if (!str)
2438 0 : return false;
2439 261 : vals[i].setString(str);
2440 13563 : } else if (JSID_IS_ATOM(id)) {
2441 13563 : vals[i].setString(JSID_TO_STRING(id));
2442 : } else {
2443 0 : vals[i].setObject(*JSID_TO_OBJECT(id));
2444 : }
2445 : }
2446 :
2447 828 : JSObject *aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
2448 828 : if (!aobj)
2449 0 : return false;
2450 :
2451 828 : vp->setObject(*aobj);
2452 828 : return true;
2453 : }
2454 :
2455 : static JSBool
2456 846 : obj_isExtensible(JSContext *cx, unsigned argc, Value *vp)
2457 : {
2458 : JSObject *obj;
2459 846 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isExtensible", &obj))
2460 0 : return false;
2461 :
2462 846 : vp->setBoolean(obj->isExtensible());
2463 846 : return true;
2464 : }
2465 :
2466 : static JSBool
2467 81 : obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
2468 : {
2469 : JSObject *obj;
2470 81 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj))
2471 0 : return false;
2472 :
2473 81 : vp->setObject(*obj);
2474 81 : if (!obj->isExtensible())
2475 0 : return true;
2476 :
2477 162 : AutoIdVector props(cx);
2478 81 : return obj->preventExtensions(cx, &props);
2479 : }
2480 :
2481 : /* static */ inline unsigned
2482 3702903 : JSObject::getSealedOrFrozenAttributes(unsigned attrs, ImmutabilityType it)
2483 : {
2484 : /* Make all attributes permanent; if freezing, make data attributes read-only. */
2485 3702903 : if (it == FREEZE && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
2486 3699843 : return JSPROP_PERMANENT | JSPROP_READONLY;
2487 3060 : return JSPROP_PERMANENT;
2488 : }
2489 :
2490 : bool
2491 1219674 : JSObject::sealOrFreeze(JSContext *cx, ImmutabilityType it)
2492 : {
2493 1219674 : assertSameCompartment(cx, this);
2494 1219674 : JS_ASSERT(it == SEAL || it == FREEZE);
2495 :
2496 2439348 : RootedVarObject self(cx, this);
2497 :
2498 2439348 : AutoIdVector props(cx);
2499 1219674 : if (isExtensible()) {
2500 1219242 : if (!preventExtensions(cx, &props))
2501 0 : return false;
2502 : } else {
2503 432 : if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, &props))
2504 0 : return false;
2505 : }
2506 :
2507 : /* preventExtensions must slowify dense arrays, so we can assign to holes without checks. */
2508 1219674 : JS_ASSERT(!self->isDenseArray());
2509 :
2510 1219674 : if (isNative() && !inDictionaryMode()) {
2511 : /*
2512 : * Seal/freeze non-dictionary objects by constructing a new shape
2513 : * hierarchy mirroring the original one, which can be shared if many
2514 : * objects with the same structure are sealed/frozen. If we use the
2515 : * generic path below then any non-empty object will be converted to
2516 : * dictionary mode.
2517 : */
2518 1206714 : Shape *last = EmptyShape::getInitialShape(cx, self->getClass(),
2519 1206714 : self->getProto(),
2520 : self->getParent(),
2521 1206714 : self->getAllocKind(),
2522 4826856 : self->lastProperty()->getObjectFlags());
2523 1206714 : if (!last)
2524 0 : return false;
2525 :
2526 : /* Get an in order list of the shapes in this object. */
2527 2413428 : AutoShapeVector shapes(cx);
2528 4838733 : for (Shape::Range r = self->lastProperty()->all(); !r.empty(); r.popFront()) {
2529 3632019 : if (!shapes.append(&r.front()))
2530 0 : return false;
2531 : }
2532 1206714 : Reverse(shapes.begin(), shapes.end());
2533 :
2534 4838733 : for (size_t i = 0; i < shapes.length(); i++) {
2535 3632019 : StackShape child(shapes[i]);
2536 3632019 : child.attrs |= getSealedOrFrozenAttributes(child.attrs, it);
2537 :
2538 3632019 : if (!JSID_IS_EMPTY(child.propid))
2539 3632019 : MarkTypePropertyConfigured(cx, self, child.propid);
2540 :
2541 3632019 : last = JS_PROPERTY_TREE(cx).getChild(cx, last, self->numFixedSlots(), child);
2542 3632019 : if (!last)
2543 0 : return NULL;
2544 : }
2545 :
2546 1206714 : JS_ASSERT(self->lastProperty()->slotSpan() == last->slotSpan());
2547 1206714 : JS_ALWAYS_TRUE(setLastProperty(cx, last));
2548 : } else {
2549 83844 : for (size_t i = 0; i < props.length(); i++) {
2550 70884 : jsid id = props[i];
2551 :
2552 : unsigned attrs;
2553 70884 : if (!self->getGenericAttributes(cx, id, &attrs))
2554 0 : return false;
2555 :
2556 70884 : unsigned new_attrs = getSealedOrFrozenAttributes(attrs, it);
2557 :
2558 : /* If we already have the attributes we need, skip the setAttributes call. */
2559 70884 : if ((attrs | new_attrs) == attrs)
2560 38970 : continue;
2561 :
2562 31914 : attrs |= new_attrs;
2563 31914 : if (!self->setGenericAttributes(cx, id, &attrs))
2564 0 : return false;
2565 : }
2566 : }
2567 :
2568 1219674 : return true;
2569 : }
2570 :
2571 : bool
2572 1139 : JSObject::isSealedOrFrozen(JSContext *cx, ImmutabilityType it, bool *resultp)
2573 : {
2574 1139 : if (isExtensible()) {
2575 252 : *resultp = false;
2576 252 : return true;
2577 : }
2578 :
2579 1774 : AutoIdVector props(cx);
2580 887 : if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, &props))
2581 0 : return false;
2582 :
2583 11766 : for (size_t i = 0, len = props.length(); i < len; i++) {
2584 11113 : jsid id = props[i];
2585 :
2586 : unsigned attrs;
2587 11113 : if (!getGenericAttributes(cx, id, &attrs))
2588 0 : return false;
2589 :
2590 : /*
2591 : * If the property is configurable, this object is neither sealed nor
2592 : * frozen. If the property is a writable data property, this object is
2593 : * not frozen.
2594 : */
2595 15368 : if (!(attrs & JSPROP_PERMANENT) ||
2596 4255 : (it == FREEZE && !(attrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER))))
2597 : {
2598 234 : *resultp = false;
2599 234 : return true;
2600 : }
2601 : }
2602 :
2603 : /* All properties checked out. This object is sealed/frozen. */
2604 653 : *resultp = true;
2605 653 : return true;
2606 : }
2607 :
2608 : static JSBool
2609 126 : obj_freeze(JSContext *cx, unsigned argc, Value *vp)
2610 : {
2611 : JSObject *obj;
2612 126 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.freeze", &obj))
2613 0 : return false;
2614 :
2615 126 : vp->setObject(*obj);
2616 :
2617 126 : return obj->freeze(cx);
2618 : }
2619 :
2620 : static JSBool
2621 329 : obj_isFrozen(JSContext *cx, unsigned argc, Value *vp)
2622 : {
2623 : JSObject *obj;
2624 329 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj))
2625 9 : return false;
2626 :
2627 : bool frozen;
2628 320 : if (!obj->isFrozen(cx, &frozen))
2629 0 : return false;
2630 320 : vp->setBoolean(frozen);
2631 320 : return true;
2632 : }
2633 :
2634 : static JSBool
2635 252 : obj_seal(JSContext *cx, unsigned argc, Value *vp)
2636 : {
2637 : JSObject *obj;
2638 252 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.seal", &obj))
2639 0 : return false;
2640 :
2641 252 : vp->setObject(*obj);
2642 :
2643 252 : return obj->seal(cx);
2644 : }
2645 :
2646 : static JSBool
2647 315 : obj_isSealed(JSContext *cx, unsigned argc, Value *vp)
2648 : {
2649 : JSObject *obj;
2650 315 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isSealed", &obj))
2651 0 : return false;
2652 :
2653 : bool sealed;
2654 315 : if (!obj->isSealed(cx, &sealed))
2655 0 : return false;
2656 315 : vp->setBoolean(sealed);
2657 315 : return true;
2658 : }
2659 :
2660 : #if JS_HAS_OBJ_WATCHPOINT
2661 : const char js_watch_str[] = "watch";
2662 : const char js_unwatch_str[] = "unwatch";
2663 : #endif
2664 : const char js_hasOwnProperty_str[] = "hasOwnProperty";
2665 : const char js_isPrototypeOf_str[] = "isPrototypeOf";
2666 : const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
2667 :
2668 : JSFunctionSpec object_methods[] = {
2669 : #if JS_HAS_TOSOURCE
2670 : JS_FN(js_toSource_str, obj_toSource, 0,0),
2671 : #endif
2672 : JS_FN(js_toString_str, obj_toString, 0,0),
2673 : JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0),
2674 : JS_FN(js_valueOf_str, obj_valueOf, 0,0),
2675 : #if JS_HAS_OBJ_WATCHPOINT
2676 : JS_FN(js_watch_str, obj_watch, 2,0),
2677 : JS_FN(js_unwatch_str, obj_unwatch, 1,0),
2678 : #endif
2679 : JS_FN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0),
2680 : JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
2681 : JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0),
2682 : #if OLD_GETTER_SETTER_METHODS
2683 : JS_FN(js_defineGetter_str, js::obj_defineGetter, 2,0),
2684 : JS_FN(js_defineSetter_str, js::obj_defineSetter, 2,0),
2685 : JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0),
2686 : JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0),
2687 : #endif
2688 : JS_FS_END
2689 : };
2690 :
2691 : JSFunctionSpec object_static_methods[] = {
2692 : JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
2693 : JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0),
2694 : JS_FN("keys", obj_keys, 1,0),
2695 : JS_FN("defineProperty", obj_defineProperty, 3,0),
2696 : JS_FN("defineProperties", obj_defineProperties, 2,0),
2697 : JS_FN("create", obj_create, 2,0),
2698 : JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1,0),
2699 : JS_FN("isExtensible", obj_isExtensible, 1,0),
2700 : JS_FN("preventExtensions", obj_preventExtensions, 1,0),
2701 : JS_FN("freeze", obj_freeze, 1,0),
2702 : JS_FN("isFrozen", obj_isFrozen, 1,0),
2703 : JS_FN("seal", obj_seal, 1,0),
2704 : JS_FN("isSealed", obj_isSealed, 1,0),
2705 : JS_FS_END
2706 : };
2707 :
2708 : JSBool
2709 4042 : js_Object(JSContext *cx, unsigned argc, Value *vp)
2710 : {
2711 : JSObject *obj;
2712 4042 : if (argc == 0) {
2713 : /* Trigger logic below to construct a blank object. */
2714 3673 : obj = NULL;
2715 : } else {
2716 : /* If argv[0] is null or undefined, obj comes back null. */
2717 369 : if (!js_ValueToObjectOrNull(cx, vp[2], &obj))
2718 0 : return JS_FALSE;
2719 : }
2720 4042 : if (!obj) {
2721 : /* Make an object whether this was called with 'new' or not. */
2722 3673 : JS_ASSERT(!argc || vp[2].isNull() || vp[2].isUndefined());
2723 3673 : gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass);
2724 3673 : obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
2725 3673 : if (!obj)
2726 0 : return JS_FALSE;
2727 3673 : TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Object);
2728 3673 : if (!type)
2729 0 : return JS_FALSE;
2730 3673 : obj->setType(type);
2731 : }
2732 4042 : vp->setObject(*obj);
2733 4042 : return JS_TRUE;
2734 : }
2735 :
2736 : static inline JSObject *
2737 4942285 : NewObject(JSContext *cx, Class *clasp, types::TypeObject *type, JSObject *parent,
2738 : gc::AllocKind kind)
2739 : {
2740 4942285 : JS_ASSERT(clasp != &ArrayClass);
2741 0 : JS_ASSERT_IF(clasp == &FunctionClass,
2742 4942285 : kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
2743 :
2744 9884570 : RootTypeObject typeRoot(cx, &type);
2745 :
2746 9884570 : RootedVarShape shape(cx);
2747 4942285 : shape = EmptyShape::getInitialShape(cx, clasp, type->proto, parent, kind);
2748 4942285 : if (!shape)
2749 0 : return NULL;
2750 :
2751 : HeapSlot *slots;
2752 4942285 : if (!PreallocateObjectDynamicSlots(cx, shape, &slots))
2753 0 : return NULL;
2754 :
2755 4942285 : JSObject *obj = JSObject::create(cx, kind, shape, typeRoot, slots);
2756 4942285 : if (!obj)
2757 0 : return NULL;
2758 :
2759 : /*
2760 : * This will cancel an already-running incremental GC from doing any more
2761 : * slices, and it will prevent any future incremental GCs.
2762 : */
2763 4942285 : if (clasp->trace && !(clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS))
2764 0 : cx->runtime->gcIncrementalEnabled = false;
2765 :
2766 4942285 : Probes::createObject(cx, obj);
2767 4942285 : return obj;
2768 : }
2769 :
2770 : JSObject *
2771 2281918 : js::NewObjectWithGivenProto(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent,
2772 : gc::AllocKind kind)
2773 : {
2774 2281918 : if (CanBeFinalizedInBackground(kind, clasp))
2775 1223213 : kind = GetBackgroundAllocKind(kind);
2776 :
2777 2281918 : NewObjectCache &cache = cx->compartment->newObjectCache;
2778 :
2779 2281918 : NewObjectCache::EntryIndex entry = -1;
2780 2281918 : if (proto && (!parent || parent == proto->getParent()) && !proto->isGlobal()) {
2781 2084102 : if (cache.lookupProto(clasp, proto, kind, &entry))
2782 1802129 : return cache.newObjectFromHit(cx, entry);
2783 : }
2784 :
2785 959578 : RootObject protoRoot(cx, &proto);
2786 959578 : RootObject parentRoot(cx, &parent);
2787 :
2788 479789 : types::TypeObject *type = proto ? proto->getNewType(cx) : cx->compartment->getEmptyType(cx);
2789 479789 : if (!type)
2790 0 : return NULL;
2791 :
2792 : /*
2793 : * Default parent to the parent of the prototype, which was set from
2794 : * the parent of the prototype's constructor.
2795 : */
2796 479789 : if (!parent && proto)
2797 17490 : parent = proto->getParent();
2798 :
2799 479789 : JSObject *obj = NewObject(cx, clasp, type, parent, kind);
2800 479789 : if (!obj)
2801 0 : return NULL;
2802 :
2803 479789 : if (entry != -1 && !obj->hasDynamicSlots())
2804 281973 : cache.fillProto(entry, clasp, proto, kind, obj);
2805 :
2806 479789 : return obj;
2807 : }
2808 :
2809 : JSObject *
2810 18920036 : js::NewObjectWithClassProto(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent,
2811 : gc::AllocKind kind)
2812 : {
2813 18920036 : if (proto)
2814 1772958 : return NewObjectWithGivenProto(cx, clasp, proto, parent, kind);
2815 :
2816 17147078 : if (CanBeFinalizedInBackground(kind, clasp))
2817 6471587 : kind = GetBackgroundAllocKind(kind);
2818 :
2819 17147078 : if (!parent)
2820 8914741 : parent = GetCurrentGlobal(cx);
2821 :
2822 : /*
2823 : * Use the object cache, except for classes without a cached proto key.
2824 : * On these objects, FindProto will do a dynamic property lookup to get
2825 : * global[className].prototype, where changes to either the className or
2826 : * prototype property would render the cached lookup incorrect. For classes
2827 : * with a proto key, the prototype created during class initialization is
2828 : * stored in an immutable slot on the global (except for ClearScope, which
2829 : * will flush the new object cache).
2830 : */
2831 17147078 : JSProtoKey protoKey = GetClassProtoKey(clasp);
2832 :
2833 17147078 : NewObjectCache &cache = cx->compartment->newObjectCache;
2834 :
2835 17147078 : NewObjectCache::EntryIndex entry = -1;
2836 17147078 : if (parent->isGlobal() && protoKey != JSProto_Null) {
2837 13007130 : if (cache.lookupGlobal(clasp, &parent->asGlobal(), kind, &entry))
2838 12687870 : return cache.newObjectFromHit(cx, entry);
2839 : }
2840 :
2841 8918416 : RootObject parentRoot(cx, &parent);
2842 :
2843 4459208 : if (!FindProto(cx, clasp, parentRoot, &proto))
2844 0 : return NULL;
2845 :
2846 4459208 : types::TypeObject *type = proto->getNewType(cx);
2847 4459208 : if (!type)
2848 0 : return NULL;
2849 :
2850 4459208 : JSObject *obj = NewObject(cx, clasp, type, parent, kind);
2851 4459208 : if (!obj)
2852 0 : return NULL;
2853 :
2854 4459208 : if (entry != -1 && !obj->hasDynamicSlots())
2855 319260 : cache.fillGlobal(entry, clasp, &parent->asGlobal(), kind, obj);
2856 :
2857 4459208 : return obj;
2858 : }
2859 :
2860 : JSObject *
2861 2519619 : js::NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::AllocKind kind)
2862 : {
2863 2519619 : JS_ASSERT(type->proto->hasNewType(type));
2864 2519619 : JS_ASSERT(parent);
2865 :
2866 2519619 : if (CanBeFinalizedInBackground(kind, &ObjectClass))
2867 2519619 : kind = GetBackgroundAllocKind(kind);
2868 :
2869 2519619 : NewObjectCache &cache = cx->compartment->newObjectCache;
2870 :
2871 2519619 : NewObjectCache::EntryIndex entry = -1;
2872 2519619 : if (parent == type->proto->getParent()) {
2873 2519619 : if (cache.lookupType(&ObjectClass, type, kind, &entry))
2874 2516331 : return cache.newObjectFromHit(cx, entry);
2875 : }
2876 :
2877 3288 : JSObject *obj = NewObject(cx, &ObjectClass, type, parent, kind);
2878 3288 : if (!obj)
2879 0 : return NULL;
2880 :
2881 3288 : if (entry != -1 && !obj->hasDynamicSlots())
2882 3288 : cache.fillType(entry, &ObjectClass, type, kind, obj);
2883 :
2884 3288 : return obj;
2885 : }
2886 :
2887 : JSObject *
2888 596 : js::NewReshapedObject(JSContext *cx, TypeObject *type, JSObject *parent,
2889 : gc::AllocKind kind, const Shape *shape)
2890 : {
2891 596 : JSObject *res = NewObjectWithType(cx, type, parent, kind);
2892 596 : if (!res)
2893 0 : return NULL;
2894 :
2895 596 : if (shape->isEmptyShape())
2896 0 : return res;
2897 :
2898 : /* Get all the ids in the object, in order. */
2899 1192 : js::AutoIdVector ids(cx);
2900 1626 : for (unsigned i = 0; i <= shape->slot(); i++) {
2901 1030 : if (!ids.append(JSID_VOID))
2902 0 : return NULL;
2903 : }
2904 596 : const js::Shape *nshape = shape;
2905 2222 : while (!nshape->isEmptyShape()) {
2906 1030 : ids[nshape->slot()] = nshape->propid();
2907 1030 : nshape = nshape->previous();
2908 : }
2909 :
2910 : /* Construct the new shape. */
2911 1626 : for (unsigned i = 0; i < ids.length(); i++) {
2912 2060 : if (!DefineNativeProperty(cx, res, ids[i], js::UndefinedValue(), NULL, NULL,
2913 2060 : JSPROP_ENUMERATE, 0, 0, DNP_SKIP_TYPE)) {
2914 0 : return NULL;
2915 : }
2916 : }
2917 596 : JS_ASSERT(!res->inDictionaryMode());
2918 :
2919 596 : return res;
2920 : }
2921 :
2922 : JSObject*
2923 19 : js_CreateThis(JSContext *cx, Class *newclasp, JSObject *callee)
2924 : {
2925 : Value protov;
2926 19 : if (!callee->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &protov))
2927 0 : return NULL;
2928 :
2929 19 : JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL;
2930 19 : JSObject *parent = callee->getParent();
2931 19 : gc::AllocKind kind = NewObjectGCKind(cx, newclasp);
2932 19 : return NewObjectWithClassProto(cx, newclasp, proto, parent, kind);
2933 : }
2934 :
2935 : static inline JSObject *
2936 2519023 : CreateThisForFunctionWithType(JSContext *cx, types::TypeObject *type, JSObject *parent)
2937 : {
2938 2519023 : if (type->newScript) {
2939 : /*
2940 : * Make an object with the type's associated finalize kind and shape,
2941 : * which reflects any properties that will definitely be added to the
2942 : * object before it is read from.
2943 : */
2944 349882 : gc::AllocKind kind = type->newScript->allocKind;
2945 349882 : JSObject *res = NewObjectWithType(cx, type, parent, kind);
2946 349882 : if (res)
2947 349882 : JS_ALWAYS_TRUE(res->setLastProperty(cx, (Shape *) type->newScript->shape.get()));
2948 349882 : return res;
2949 : }
2950 :
2951 2169141 : gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass);
2952 2169141 : return NewObjectWithType(cx, type, parent, kind);
2953 : }
2954 :
2955 : JSObject *
2956 2519113 : js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto)
2957 : {
2958 : JSObject *res;
2959 :
2960 2519113 : if (proto) {
2961 2519023 : types::TypeObject *type = proto->getNewType(cx, callee->toFunction());
2962 2519023 : if (!type)
2963 0 : return NULL;
2964 2519023 : res = CreateThisForFunctionWithType(cx, type, callee->getParent());
2965 : } else {
2966 90 : gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass);
2967 90 : res = NewObjectWithClassProto(cx, &ObjectClass, proto, callee->getParent(), kind);
2968 : }
2969 :
2970 2519113 : if (res && cx->typeInferenceEnabled())
2971 780679 : TypeScript::SetThis(cx, callee->toFunction()->script(), types::Type::ObjectType(res));
2972 :
2973 2519113 : return res;
2974 : }
2975 :
2976 : JSObject *
2977 872149 : js_CreateThisForFunction(JSContext *cx, JSObject *callee, bool newType)
2978 : {
2979 : Value protov;
2980 872149 : if (!callee->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &protov))
2981 0 : return NULL;
2982 : JSObject *proto;
2983 872149 : if (protov.isObject())
2984 872099 : proto = &protov.toObject();
2985 : else
2986 50 : proto = NULL;
2987 872149 : JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
2988 :
2989 872149 : if (obj && newType) {
2990 : /*
2991 : * Reshape the object and give it a (lazily instantiated) singleton
2992 : * type before passing it as the 'this' value for the call.
2993 : */
2994 10 : obj->clear(cx);
2995 10 : if (!obj->setSingletonType(cx))
2996 0 : return NULL;
2997 :
2998 10 : JSScript *calleeScript = callee->toFunction()->script();
2999 10 : TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(obj));
3000 : }
3001 :
3002 872149 : return obj;
3003 : }
3004 :
3005 : /*
3006 : * Given pc pointing after a property accessing bytecode, return true if the
3007 : * access is "object-detecting" in the sense used by web scripts, e.g., when
3008 : * checking whether document.all is defined.
3009 : */
3010 : static bool
3011 670326 : Detecting(JSContext *cx, jsbytecode *pc)
3012 : {
3013 : /* General case: a branch or equality op follows the access. */
3014 670326 : JSOp op = JSOp(*pc);
3015 670326 : if (js_CodeSpec[op].format & JOF_DETECTING)
3016 47654 : return true;
3017 :
3018 : JSAtom *atom;
3019 :
3020 622672 : JSScript *script = cx->stack.currentScript();
3021 622672 : jsbytecode *endpc = script->code + script->length;
3022 622672 : JS_ASSERT(script->code <= pc && pc < endpc);
3023 :
3024 622672 : if (op == JSOP_NULL) {
3025 : /*
3026 : * Special case #1: handle (document.all == null). Don't sweat
3027 : * about JS1.2's revision of the equality operators here.
3028 : */
3029 36 : if (++pc < endpc) {
3030 36 : op = JSOp(*pc);
3031 36 : return op == JSOP_EQ || op == JSOP_NE;
3032 : }
3033 0 : return false;
3034 : }
3035 :
3036 622636 : if (op == JSOP_GETGNAME || op == JSOP_NAME) {
3037 : /*
3038 : * Special case #2: handle (document.all == undefined). Don't worry
3039 : * about a local variable named |undefined| shadowing the immutable
3040 : * global binding...because, really?
3041 : */
3042 82609 : atom = script->getAtom(GET_UINT32_INDEX(pc));
3043 83171 : if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
3044 562 : (pc += js_CodeSpec[op].length) < endpc) {
3045 562 : op = JSOp(*pc);
3046 562 : return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
3047 : }
3048 : }
3049 :
3050 622074 : return false;
3051 : }
3052 :
3053 : /*
3054 : * Infer lookup flags from the currently executing bytecode. This does
3055 : * not attempt to infer JSRESOLVE_WITH, because the current bytecode
3056 : * does not indicate whether we are in a with statement. Return defaultFlags
3057 : * if a currently executing bytecode cannot be determined.
3058 : */
3059 : unsigned
3060 803178 : js_InferFlags(JSContext *cx, unsigned defaultFlags)
3061 : {
3062 : const JSCodeSpec *cs;
3063 : uint32_t format;
3064 803178 : unsigned flags = 0;
3065 :
3066 : jsbytecode *pc;
3067 803178 : JSScript *script = cx->stack.currentScript(&pc);
3068 803178 : if (!script || !pc)
3069 15842 : return defaultFlags;
3070 :
3071 787336 : cs = &js_CodeSpec[*pc];
3072 787336 : format = cs->format;
3073 787336 : if (JOF_MODE(format) != JOF_NAME)
3074 286233 : flags |= JSRESOLVE_QUALIFIED;
3075 787336 : if (format & JOF_SET) {
3076 116830 : flags |= JSRESOLVE_ASSIGNING;
3077 670506 : } else if (cs->length >= 0) {
3078 670506 : pc += cs->length;
3079 670506 : if (pc < script->code + script->length && Detecting(cx, pc))
3080 47829 : flags |= JSRESOLVE_DETECTING;
3081 : }
3082 787336 : if (format & JOF_DECLARING)
3083 60471 : flags |= JSRESOLVE_DECLARING;
3084 787336 : return flags;
3085 : }
3086 :
3087 : JSBool
3088 1761478 : JSObject::nonNativeSetProperty(JSContext *cx, jsid id, js::Value *vp, JSBool strict)
3089 : {
3090 1761478 : if (JS_UNLIKELY(watched())) {
3091 0 : id = js_CheckForStringIndex(id);
3092 0 : WatchpointMap *wpmap = cx->compartment->watchpointMap;
3093 0 : if (wpmap && !wpmap->triggerWatchpoint(cx, this, id, vp))
3094 0 : return false;
3095 : }
3096 1761478 : return getOps()->setGeneric(cx, this, id, vp, strict);
3097 : }
3098 :
3099 : JSBool
3100 22238 : JSObject::nonNativeSetElement(JSContext *cx, uint32_t index, js::Value *vp, JSBool strict)
3101 : {
3102 22238 : if (JS_UNLIKELY(watched())) {
3103 : jsid id;
3104 0 : if (!IndexToId(cx, index, &id))
3105 0 : return false;
3106 0 : JS_ASSERT(id == js_CheckForStringIndex(id));
3107 0 : WatchpointMap *wpmap = cx->compartment->watchpointMap;
3108 0 : if (wpmap && !wpmap->triggerWatchpoint(cx, this, id, vp))
3109 0 : return false;
3110 : }
3111 22238 : return getOps()->setElement(cx, this, index, vp, strict);
3112 : }
3113 :
3114 : bool
3115 2565 : JSObject::deleteByValue(JSContext *cx, const Value &property, Value *rval, bool strict)
3116 : {
3117 : uint32_t index;
3118 2565 : if (IsDefinitelyIndex(property, &index))
3119 2169 : return deleteElement(cx, index, rval, strict);
3120 :
3121 396 : Value propval = property;
3122 396 : SpecialId sid;
3123 396 : if (ValueIsSpecial(this, &propval, &sid, cx))
3124 0 : return deleteSpecial(cx, sid, rval, strict);
3125 :
3126 : JSAtom *name;
3127 396 : if (!js_ValueToAtom(cx, propval, &name))
3128 0 : return false;
3129 :
3130 396 : if (name->isIndex(&index))
3131 36 : return deleteElement(cx, index, rval, false);
3132 :
3133 360 : return deleteProperty(cx, name->asPropertyName(), rval, false);
3134 : }
3135 :
3136 : JS_FRIEND_API(bool)
3137 0 : JS_CopyPropertiesFrom(JSContext *cx, JSObject *target, JSObject *obj)
3138 : {
3139 : // If we're not native, then we cannot copy properties.
3140 0 : JS_ASSERT(target->isNative() == obj->isNative());
3141 0 : if (!target->isNative())
3142 0 : return true;
3143 :
3144 0 : AutoShapeVector shapes(cx);
3145 0 : for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
3146 0 : if (!shapes.append(&r.front()))
3147 0 : return false;
3148 : }
3149 :
3150 0 : size_t n = shapes.length();
3151 0 : while (n > 0) {
3152 0 : const Shape *shape = shapes[--n];
3153 0 : unsigned attrs = shape->attributes();
3154 0 : PropertyOp getter = shape->getter();
3155 0 : if ((attrs & JSPROP_GETTER) && !cx->compartment->wrap(cx, &getter))
3156 0 : return false;
3157 0 : StrictPropertyOp setter = shape->setter();
3158 0 : if ((attrs & JSPROP_SETTER) && !cx->compartment->wrap(cx, &setter))
3159 0 : return false;
3160 0 : Value v = shape->hasSlot() ? obj->getSlot(shape->slot()) : UndefinedValue();
3161 0 : if (!cx->compartment->wrap(cx, &v))
3162 0 : return false;
3163 0 : if (!target->defineGeneric(cx, shape->propid(), v, getter, setter, attrs))
3164 0 : return false;
3165 : }
3166 0 : return true;
3167 : }
3168 :
3169 : static bool
3170 0 : CopySlots(JSContext *cx, JSObject *from, JSObject *to)
3171 : {
3172 0 : JS_ASSERT(!from->isNative() && !to->isNative());
3173 0 : JS_ASSERT(from->getClass() == to->getClass());
3174 :
3175 0 : size_t n = 0;
3176 0 : if (from->isWrapper() &&
3177 0 : (Wrapper::wrapperHandler(from)->flags() & Wrapper::CROSS_COMPARTMENT)) {
3178 0 : to->setSlot(0, from->getSlot(0));
3179 0 : to->setSlot(1, from->getSlot(1));
3180 0 : n = 2;
3181 : }
3182 :
3183 0 : size_t span = JSCLASS_RESERVED_SLOTS(from->getClass());
3184 0 : for (; n < span; ++n) {
3185 0 : Value v = from->getSlot(n);
3186 0 : if (!cx->compartment->wrap(cx, &v))
3187 0 : return false;
3188 0 : to->setSlot(n, v);
3189 : }
3190 0 : return true;
3191 : }
3192 :
3193 : JS_FRIEND_API(JSObject *)
3194 0 : JS_CloneObject(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent)
3195 : {
3196 : /*
3197 : * We can only clone native objects and proxies. Dense arrays are slowified if
3198 : * we try to clone them.
3199 : */
3200 0 : if (!obj->isNative()) {
3201 0 : if (obj->isDenseArray()) {
3202 0 : if (!obj->makeDenseArraySlow(cx))
3203 0 : return NULL;
3204 0 : } else if (!obj->isProxy()) {
3205 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3206 0 : JSMSG_CANT_CLONE_OBJECT);
3207 0 : return NULL;
3208 : }
3209 : }
3210 0 : JSObject *clone = NewObjectWithGivenProto(cx, obj->getClass(), proto, parent, obj->getAllocKind());
3211 0 : if (!clone)
3212 0 : return NULL;
3213 0 : if (obj->isNative()) {
3214 0 : if (clone->isFunction() && (obj->compartment() != clone->compartment())) {
3215 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3216 0 : JSMSG_CANT_CLONE_OBJECT);
3217 0 : return NULL;
3218 : }
3219 :
3220 0 : if (obj->hasPrivate())
3221 0 : clone->setPrivate(obj->getPrivate());
3222 : } else {
3223 0 : JS_ASSERT(obj->isProxy());
3224 0 : if (!CopySlots(cx, obj, clone))
3225 0 : return NULL;
3226 : }
3227 :
3228 0 : return clone;
3229 : }
3230 :
3231 : struct JSObject::TradeGutsReserved {
3232 : JSContext *cx;
3233 : Vector<Value> avals;
3234 : Vector<Value> bvals;
3235 : int newafixed;
3236 : int newbfixed;
3237 : Shape *newashape;
3238 : Shape *newbshape;
3239 : HeapSlot *newaslots;
3240 : HeapSlot *newbslots;
3241 :
3242 40 : TradeGutsReserved(JSContext *cx)
3243 : : cx(cx), avals(cx), bvals(cx),
3244 : newafixed(0), newbfixed(0),
3245 : newashape(NULL), newbshape(NULL),
3246 40 : newaslots(NULL), newbslots(NULL)
3247 40 : {}
3248 :
3249 40 : ~TradeGutsReserved()
3250 40 : {
3251 40 : if (newaslots)
3252 0 : cx->free_(newaslots);
3253 40 : if (newbslots)
3254 0 : cx->free_(newbslots);
3255 40 : }
3256 : };
3257 :
3258 : bool
3259 40 : JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b,
3260 : TradeGutsReserved &reserved)
3261 : {
3262 : /*
3263 : * When performing multiple swaps between objects which may have different
3264 : * numbers of fixed slots, we reserve all space ahead of time so that the
3265 : * swaps can be performed infallibly.
3266 : */
3267 :
3268 40 : if (a->sizeOfThis() == b->sizeOfThis())
3269 40 : return true;
3270 :
3271 : /*
3272 : * If either object is native, it needs a new shape to preserve the
3273 : * invariant that objects with the same shape have the same number of
3274 : * inline slots. The fixed slots will be updated in place during TradeGuts.
3275 : * Non-native objects need to be reshaped according to the new count.
3276 : */
3277 0 : if (a->isNative()) {
3278 0 : if (!a->generateOwnShape(cx))
3279 0 : return false;
3280 : } else {
3281 : reserved.newbshape = EmptyShape::getInitialShape(cx, a->getClass(),
3282 : a->getProto(), a->getParent(),
3283 0 : b->getAllocKind());
3284 0 : if (!reserved.newbshape)
3285 0 : return false;
3286 : }
3287 0 : if (b->isNative()) {
3288 0 : if (!b->generateOwnShape(cx))
3289 0 : return false;
3290 : } else {
3291 : reserved.newashape = EmptyShape::getInitialShape(cx, b->getClass(),
3292 : b->getProto(), b->getParent(),
3293 0 : a->getAllocKind());
3294 0 : if (!reserved.newashape)
3295 0 : return false;
3296 : }
3297 :
3298 : /* The avals/bvals vectors hold all original values from the objects. */
3299 :
3300 0 : if (!reserved.avals.reserve(a->slotSpan()))
3301 0 : return false;
3302 0 : if (!reserved.bvals.reserve(b->slotSpan()))
3303 0 : return false;
3304 :
3305 0 : JS_ASSERT(a->elements == emptyObjectElements);
3306 0 : JS_ASSERT(b->elements == emptyObjectElements);
3307 :
3308 : /*
3309 : * The newafixed/newbfixed hold the number of fixed slots in the objects
3310 : * after the swap. Adjust these counts according to whether the objects
3311 : * use their last fixed slot for storing private data.
3312 : */
3313 :
3314 0 : reserved.newafixed = a->numFixedSlots();
3315 0 : reserved.newbfixed = b->numFixedSlots();
3316 :
3317 0 : if (a->hasPrivate()) {
3318 0 : reserved.newafixed++;
3319 0 : reserved.newbfixed--;
3320 : }
3321 0 : if (b->hasPrivate()) {
3322 0 : reserved.newbfixed++;
3323 0 : reserved.newafixed--;
3324 : }
3325 :
3326 0 : JS_ASSERT(reserved.newafixed >= 0);
3327 0 : JS_ASSERT(reserved.newbfixed >= 0);
3328 :
3329 : /*
3330 : * The newaslots/newbslots arrays hold any dynamic slots for the objects
3331 : * if they do not have enough fixed slots to accomodate the slots in the
3332 : * other object.
3333 : */
3334 :
3335 0 : unsigned adynamic = dynamicSlotsCount(reserved.newafixed, b->slotSpan());
3336 0 : unsigned bdynamic = dynamicSlotsCount(reserved.newbfixed, a->slotSpan());
3337 :
3338 0 : if (adynamic) {
3339 0 : reserved.newaslots = (HeapSlot *) cx->malloc_(sizeof(HeapSlot) * adynamic);
3340 0 : if (!reserved.newaslots)
3341 0 : return false;
3342 0 : Debug_SetSlotRangeToCrashOnTouch(reserved.newaslots, adynamic);
3343 : }
3344 0 : if (bdynamic) {
3345 0 : reserved.newbslots = (HeapSlot *) cx->malloc_(sizeof(HeapSlot) * bdynamic);
3346 0 : if (!reserved.newbslots)
3347 0 : return false;
3348 0 : Debug_SetSlotRangeToCrashOnTouch(reserved.newbslots, bdynamic);
3349 : }
3350 :
3351 0 : return true;
3352 : }
3353 :
3354 : void
3355 40 : JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &reserved)
3356 : {
3357 40 : JS_ASSERT(a->compartment() == b->compartment());
3358 40 : JS_ASSERT(a->isFunction() == b->isFunction());
3359 :
3360 : /* Don't try to swap a JSFunction for a plain function JSObject. */
3361 40 : JS_ASSERT_IF(a->isFunction(), a->sizeOfThis() == b->sizeOfThis());
3362 :
3363 : /*
3364 : * Regexp guts are more complicated -- we would need to migrate the
3365 : * refcounted JIT code blob for them across compartments instead of just
3366 : * swapping guts.
3367 : */
3368 40 : JS_ASSERT(!a->isRegExp() && !b->isRegExp());
3369 :
3370 : /*
3371 : * Callers should not try to swap dense arrays or ArrayBuffer objects,
3372 : * these use a different slot representation from other objects.
3373 : */
3374 40 : JS_ASSERT(!a->isDenseArray() && !b->isDenseArray());
3375 40 : JS_ASSERT(!a->isArrayBuffer() && !b->isArrayBuffer());
3376 :
3377 : #ifdef JSGC_INCREMENTAL
3378 : /*
3379 : * We need a write barrier here. If |a| was marked and |b| was not, then
3380 : * after the swap, |b|'s guts would never be marked. The write barrier
3381 : * solves this.
3382 : */
3383 40 : JSCompartment *comp = a->compartment();
3384 40 : if (comp->needsBarrier()) {
3385 0 : MarkChildren(comp->barrierTracer(), a);
3386 0 : MarkChildren(comp->barrierTracer(), b);
3387 : }
3388 : #endif
3389 :
3390 : /* Trade the guts of the objects. */
3391 40 : const size_t size = a->sizeOfThis();
3392 40 : if (size == b->sizeOfThis()) {
3393 : /*
3394 : * If the objects are the same size, then we make no assumptions about
3395 : * whether they have dynamically allocated slots and instead just copy
3396 : * them over wholesale.
3397 : */
3398 : char tmp[tl::Max<sizeof(JSFunction), sizeof(JSObject_Slots16)>::result];
3399 40 : JS_ASSERT(size <= sizeof(tmp));
3400 :
3401 40 : js_memcpy(tmp, a, size);
3402 40 : js_memcpy(a, b, size);
3403 40 : js_memcpy(b, tmp, size);
3404 :
3405 : #ifdef JSGC_GENERATIONAL
3406 : /*
3407 : * Trigger post barriers for fixed slots. JSObject bits are barriered
3408 : * below, in common with the other case.
3409 : */
3410 : JSCompartment *comp = cx->compartment;
3411 : for (size_t i = 0; i < a->numFixedSlots(); ++i) {
3412 : HeapSlot::writeBarrierPost(comp, a, i);
3413 : HeapSlot::writeBarrierPost(comp, b, i);
3414 : }
3415 : #endif
3416 : } else {
3417 : /*
3418 : * If the objects are of differing sizes, use the space we reserved
3419 : * earlier to save the slots from each object and then copy them into
3420 : * the new layout for the other object.
3421 : */
3422 :
3423 0 : unsigned acap = a->slotSpan();
3424 0 : unsigned bcap = b->slotSpan();
3425 :
3426 0 : for (size_t i = 0; i < acap; i++)
3427 0 : reserved.avals.infallibleAppend(a->getSlot(i));
3428 :
3429 0 : for (size_t i = 0; i < bcap; i++)
3430 0 : reserved.bvals.infallibleAppend(b->getSlot(i));
3431 :
3432 : /* Done with the dynamic slots. */
3433 0 : if (a->hasDynamicSlots())
3434 0 : cx->free_(a->slots);
3435 0 : if (b->hasDynamicSlots())
3436 0 : cx->free_(b->slots);
3437 :
3438 0 : void *apriv = a->hasPrivate() ? a->getPrivate() : NULL;
3439 0 : void *bpriv = b->hasPrivate() ? b->getPrivate() : NULL;
3440 :
3441 : char tmp[sizeof(JSObject)];
3442 0 : js_memcpy(&tmp, a, sizeof tmp);
3443 0 : js_memcpy(a, b, sizeof tmp);
3444 0 : js_memcpy(b, &tmp, sizeof tmp);
3445 :
3446 0 : if (a->isNative())
3447 0 : a->shape_->setNumFixedSlots(reserved.newafixed);
3448 : else
3449 0 : a->shape_ = reserved.newashape;
3450 :
3451 0 : a->slots = reserved.newaslots;
3452 0 : a->initSlotRange(0, reserved.bvals.begin(), bcap);
3453 0 : if (a->hasPrivate())
3454 0 : a->initPrivate(bpriv);
3455 :
3456 0 : if (b->isNative())
3457 0 : b->shape_->setNumFixedSlots(reserved.newbfixed);
3458 : else
3459 0 : b->shape_ = reserved.newbshape;
3460 :
3461 0 : b->slots = reserved.newbslots;
3462 0 : b->initSlotRange(0, reserved.avals.begin(), acap);
3463 0 : if (b->hasPrivate())
3464 0 : b->initPrivate(apriv);
3465 :
3466 : /* Make sure the destructor for reserved doesn't free the slots. */
3467 0 : reserved.newaslots = NULL;
3468 0 : reserved.newbslots = NULL;
3469 : }
3470 :
3471 : #ifdef JSGC_GENERATIONAL
3472 : Shape::writeBarrierPost(a->shape_, &a->shape_);
3473 : Shape::writeBarrierPost(b->shape_, &b->shape_);
3474 : types::TypeObject::writeBarrierPost(a->type_, &a->type_);
3475 : types::TypeObject::writeBarrierPost(b->type_, &b->type_);
3476 : #endif
3477 :
3478 40 : if (a->inDictionaryMode())
3479 0 : a->lastProperty()->listp = &a->shape_;
3480 40 : if (b->inDictionaryMode())
3481 0 : b->lastProperty()->listp = &b->shape_;
3482 40 : }
3483 :
3484 : /*
3485 : * Use this method with extreme caution. It trades the guts of two objects and updates
3486 : * scope ownership. This operation is not thread-safe, just as fast array to slow array
3487 : * transitions are inherently not thread-safe. Don't perform a swap operation on objects
3488 : * shared across threads or, or bad things will happen. You have been warned.
3489 : */
3490 : bool
3491 40 : JSObject::swap(JSContext *cx, JSObject *other)
3492 : {
3493 40 : if (this->compartment() == other->compartment()) {
3494 80 : TradeGutsReserved reserved(cx);
3495 40 : if (!ReserveForTradeGuts(cx, this, other, reserved))
3496 0 : return false;
3497 40 : TradeGuts(cx, this, other, reserved);
3498 40 : return true;
3499 : }
3500 :
3501 : JSObject *thisClone;
3502 : JSObject *otherClone;
3503 : {
3504 0 : AutoCompartment ac(cx, other);
3505 0 : if (!ac.enter())
3506 0 : return false;
3507 0 : thisClone = JS_CloneObject(cx, this, other->getProto(), other->getParent());
3508 0 : if (!thisClone || !JS_CopyPropertiesFrom(cx, thisClone, this))
3509 0 : return false;
3510 : }
3511 : {
3512 0 : AutoCompartment ac(cx, this);
3513 0 : if (!ac.enter())
3514 0 : return false;
3515 0 : otherClone = JS_CloneObject(cx, other, other->getProto(), other->getParent());
3516 0 : if (!otherClone || !JS_CopyPropertiesFrom(cx, otherClone, other))
3517 0 : return false;
3518 : }
3519 :
3520 0 : TradeGutsReserved reservedThis(cx);
3521 0 : TradeGutsReserved reservedOther(cx);
3522 :
3523 0 : if (!ReserveForTradeGuts(cx, this, otherClone, reservedThis) ||
3524 0 : !ReserveForTradeGuts(cx, other, thisClone, reservedOther)) {
3525 0 : return false;
3526 : }
3527 :
3528 0 : TradeGuts(cx, this, otherClone, reservedThis);
3529 0 : TradeGuts(cx, other, thisClone, reservedOther);
3530 :
3531 0 : return true;
3532 : }
3533 :
3534 : static bool
3535 205304 : DefineStandardSlot(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom,
3536 : const Value &v, uint32_t attrs, bool &named)
3537 : {
3538 205304 : jsid id = ATOM_TO_JSID(atom);
3539 :
3540 205304 : if (key != JSProto_Null) {
3541 : /*
3542 : * Initializing an actual standard class on a global object. If the
3543 : * property is not yet present, force it into a new one bound to a
3544 : * reserved slot. Otherwise, go through the normal property path.
3545 : */
3546 0 : JS_ASSERT(obj->isGlobal());
3547 0 : JS_ASSERT(obj->isNative());
3548 :
3549 0 : const Shape *shape = obj->nativeLookup(cx, id);
3550 0 : if (!shape) {
3551 0 : uint32_t slot = 2 * JSProto_LIMIT + key;
3552 0 : obj->setReservedSlot(slot, v);
3553 0 : if (!obj->addProperty(cx, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0, 0))
3554 0 : return false;
3555 0 : AddTypePropertyId(cx, obj, id, v);
3556 :
3557 0 : named = true;
3558 0 : return true;
3559 : }
3560 : }
3561 :
3562 205304 : named = obj->defineGeneric(cx, id, v, JS_PropertyStub, JS_StrictPropertyStub, attrs);
3563 205304 : return named;
3564 : }
3565 :
3566 : namespace js {
3567 :
3568 : static void
3569 0 : SetClassObject(JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto)
3570 : {
3571 0 : JS_ASSERT(!obj->getParent());
3572 0 : if (!obj->isGlobal())
3573 0 : return;
3574 :
3575 0 : obj->setReservedSlot(key, ObjectOrNullValue(cobj));
3576 0 : obj->setReservedSlot(JSProto_LIMIT + key, ObjectOrNullValue(proto));
3577 : }
3578 :
3579 : static void
3580 0 : ClearClassObject(JSContext *cx, JSObject *obj, JSProtoKey key)
3581 : {
3582 0 : JS_ASSERT(!obj->getParent());
3583 0 : if (!obj->isGlobal())
3584 0 : return;
3585 :
3586 0 : obj->setSlot(key, UndefinedValue());
3587 0 : obj->setSlot(JSProto_LIMIT + key, UndefinedValue());
3588 : }
3589 :
3590 : JSObject *
3591 205304 : DefineConstructorAndPrototype(JSContext *cx, HandleObject obj, JSProtoKey key, HandleAtom atom,
3592 : JSObject *protoProto, Class *clasp,
3593 : Native constructor, unsigned nargs,
3594 : JSPropertySpec *ps, JSFunctionSpec *fs,
3595 : JSPropertySpec *static_ps, JSFunctionSpec *static_fs,
3596 : JSObject **ctorp, AllocKind ctorKind)
3597 : {
3598 : /*
3599 : * Create a prototype object for this class.
3600 : *
3601 : * FIXME: lazy standard (built-in) class initialization and even older
3602 : * eager boostrapping code rely on all of these properties:
3603 : *
3604 : * 1. NewObject attempting to compute a default prototype object when
3605 : * passed null for proto; and
3606 : *
3607 : * 2. NewObject tolerating no default prototype (null proto slot value)
3608 : * due to this js_InitClass call coming from js_InitFunctionClass on an
3609 : * otherwise-uninitialized global.
3610 : *
3611 : * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
3612 : * &FunctionClass, not a JSObject-sized (smaller) GC-thing.
3613 : *
3614 : * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
3615 : * be &FunctionClass (we could break compatibility easily). But fixing
3616 : * (3) is not enough without addressing the bootstrapping dependency on (1)
3617 : * and (2).
3618 : */
3619 :
3620 : /*
3621 : * Create the prototype object. (GlobalObject::createBlankPrototype isn't
3622 : * used because it parents the prototype object to the global and because
3623 : * it uses WithProto::Given. FIXME: Undo dependencies on this parentage
3624 : * [which already needs to happen for bug 638316], figure out nicer
3625 : * semantics for null-protoProto, and use createBlankPrototype.)
3626 : */
3627 410608 : RootedVarObject proto(cx);
3628 205304 : proto = NewObjectWithClassProto(cx, clasp, protoProto, obj);
3629 205304 : if (!proto)
3630 0 : return NULL;
3631 :
3632 205304 : if (!proto->setSingletonType(cx))
3633 0 : return NULL;
3634 :
3635 205304 : if (clasp == &ArrayClass && !proto->makeDenseArraySlow(cx))
3636 0 : return NULL;
3637 :
3638 : /* After this point, control must exit via label bad or out. */
3639 410608 : RootedVarObject ctor(cx);
3640 205304 : bool named = false;
3641 205304 : bool cached = false;
3642 205304 : if (!constructor) {
3643 : /*
3644 : * Lacking a constructor, name the prototype (e.g., Math) unless this
3645 : * class (a) is anonymous, i.e. for internal use only; (b) the class
3646 : * of obj (the global object) is has a reserved slot indexed by key;
3647 : * and (c) key is not the null key.
3648 : */
3649 2 : if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) || !obj->isGlobal() || key == JSProto_Null) {
3650 : uint32_t attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
3651 : ? JSPROP_READONLY | JSPROP_PERMANENT
3652 2 : : 0;
3653 2 : if (!DefineStandardSlot(cx, obj, key, atom, ObjectValue(*proto), attrs, named))
3654 0 : goto bad;
3655 : }
3656 :
3657 2 : ctor = proto;
3658 : } else {
3659 : /*
3660 : * Create the constructor, not using GlobalObject::createConstructor
3661 : * because the constructor currently must have |obj| as its parent.
3662 : * (FIXME: remove this dependency on the exact identity of the parent,
3663 : * perhaps as part of bug 638316.)
3664 : */
3665 410604 : RootedVarFunction fun(cx);
3666 : fun = js_NewFunction(cx, NULL, constructor, nargs, JSFUN_CONSTRUCTOR, obj, atom,
3667 205302 : ctorKind);
3668 205302 : if (!fun)
3669 : goto bad;
3670 :
3671 : /*
3672 : * Set the class object early for standard class constructors. Type
3673 : * inference may need to access these, and js_GetClassPrototype will
3674 : * fail if it tries to do a reentrant reconstruction of the class.
3675 : */
3676 205302 : if (key != JSProto_Null) {
3677 0 : SetClassObject(obj, key, fun, proto);
3678 0 : cached = true;
3679 : }
3680 :
3681 410604 : AutoValueRooter tvr2(cx, ObjectValue(*fun));
3682 205302 : if (!DefineStandardSlot(cx, obj, key, atom, tvr2.value(), 0, named))
3683 : goto bad;
3684 :
3685 : /*
3686 : * Optionally construct the prototype object, before the class has
3687 : * been fully initialized. Allow the ctor to replace proto with a
3688 : * different object, as is done for operator new -- and as at least
3689 : * XML support requires.
3690 : */
3691 205302 : ctor = fun;
3692 205302 : if (!LinkConstructorAndPrototype(cx, ctor, proto))
3693 : goto bad;
3694 :
3695 : /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
3696 205302 : if (ctor->getClass() == clasp && !ctor->splicePrototype(cx, proto))
3697 : goto bad;
3698 : }
3699 :
3700 615910 : if (!DefinePropertiesAndBrand(cx, proto, ps, fs) ||
3701 410606 : (ctor != proto && !DefinePropertiesAndBrand(cx, ctor, static_ps, static_fs)))
3702 : {
3703 0 : goto bad;
3704 : }
3705 :
3706 205304 : if (clasp->flags & (JSCLASS_FREEZE_PROTO|JSCLASS_FREEZE_CTOR)) {
3707 0 : JS_ASSERT_IF(ctor == proto, !(clasp->flags & JSCLASS_FREEZE_CTOR));
3708 0 : if (proto && (clasp->flags & JSCLASS_FREEZE_PROTO) && !proto->freeze(cx))
3709 0 : goto bad;
3710 0 : if (ctor && (clasp->flags & JSCLASS_FREEZE_CTOR) && !ctor->freeze(cx))
3711 0 : goto bad;
3712 : }
3713 :
3714 : /* If this is a standard class, cache its prototype. */
3715 205304 : if (!cached && key != JSProto_Null)
3716 0 : SetClassObject(obj, key, ctor, proto);
3717 :
3718 205304 : if (ctorp)
3719 23330 : *ctorp = ctor;
3720 205304 : return proto;
3721 :
3722 : bad:
3723 0 : if (named) {
3724 : Value rval;
3725 0 : obj->deleteByValue(cx, StringValue(atom), &rval, false);
3726 : }
3727 0 : if (cached)
3728 0 : ClearClassObject(cx, obj, key);
3729 0 : return NULL;
3730 : }
3731 :
3732 : /*
3733 : * Lazy standard classes need a way to indicate if they have been initialized.
3734 : * Otherwise, when we delete them, we might accidentally recreate them via a
3735 : * lazy initialization. We use the presence of a ctor or proto in the
3736 : * globalObject's slot to indicate that they've been constructed, but this only
3737 : * works for classes which have a proto and ctor. Classes which don't have one
3738 : * can call MarkStandardClassInitializedNoProto(), and we can always check
3739 : * whether a class is initialized by calling IsStandardClassResolved().
3740 : */
3741 : bool
3742 2756403 : IsStandardClassResolved(JSObject *obj, js::Class *clasp)
3743 : {
3744 2756403 : JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
3745 :
3746 : /* If the constructor is undefined, then it hasn't been initialized. */
3747 2756403 : return (obj->getReservedSlot(key) != UndefinedValue());
3748 : }
3749 :
3750 : void
3751 4737 : MarkStandardClassInitializedNoProto(JSObject *obj, js::Class *clasp)
3752 : {
3753 4737 : JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
3754 :
3755 : /*
3756 : * We use True so that it's obvious what we're doing (instead of, say,
3757 : * Null, which might be miscontrued as an error in setting Undefined).
3758 : */
3759 4737 : if (obj->getReservedSlot(key) == UndefinedValue())
3760 2439 : obj->setSlot(key, BooleanValue(true));
3761 4737 : }
3762 :
3763 : }
3764 :
3765 : JSObject *
3766 205304 : js_InitClass(JSContext *cx, HandleObject obj, JSObject *protoProto,
3767 : Class *clasp, Native constructor, unsigned nargs,
3768 : JSPropertySpec *ps, JSFunctionSpec *fs,
3769 : JSPropertySpec *static_ps, JSFunctionSpec *static_fs,
3770 : JSObject **ctorp, AllocKind ctorKind)
3771 : {
3772 410608 : RootObject rootProto(cx, &protoProto);
3773 :
3774 410608 : RootedVarAtom atom(cx);
3775 205304 : atom = js_Atomize(cx, clasp->name, strlen(clasp->name));
3776 205304 : if (!atom)
3777 0 : return NULL;
3778 :
3779 : /*
3780 : * All instances of the class will inherit properties from the prototype
3781 : * object we are about to create (in DefineConstructorAndPrototype), which
3782 : * in turn will inherit from protoProto.
3783 : *
3784 : * When initializing a standard class (other than Object), if protoProto is
3785 : * null, default to the Object prototype object. The engine's internal uses
3786 : * of js_InitClass depend on this nicety. Note that in
3787 : * js_InitFunctionAndObjectClasses, we specially hack the resolving table
3788 : * and then depend on js_GetClassPrototype here leaving protoProto NULL and
3789 : * returning true.
3790 : */
3791 205304 : JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
3792 205304 : if (key != JSProto_Null &&
3793 0 : !protoProto &&
3794 0 : !js_GetClassPrototype(cx, obj, JSProto_Object, &protoProto)) {
3795 0 : return NULL;
3796 : }
3797 :
3798 : return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
3799 205304 : ps, fs, static_ps, static_fs, ctorp, ctorKind);
3800 : }
3801 :
3802 : inline bool
3803 21409261 : JSObject::updateSlotsForSpan(JSContext *cx, size_t oldSpan, size_t newSpan)
3804 : {
3805 21409261 : JS_ASSERT(oldSpan != newSpan);
3806 :
3807 21409261 : size_t oldCount = dynamicSlotsCount(numFixedSlots(), oldSpan);
3808 21409261 : size_t newCount = dynamicSlotsCount(numFixedSlots(), newSpan);
3809 :
3810 21409261 : if (oldSpan < newSpan) {
3811 21408811 : if (oldCount < newCount && !growSlots(cx, oldCount, newCount))
3812 0 : return false;
3813 :
3814 21408811 : if (newSpan == oldSpan + 1)
3815 18489638 : initSlotUnchecked(oldSpan, UndefinedValue());
3816 : else
3817 2919173 : initializeSlotRange(oldSpan, newSpan - oldSpan);
3818 : } else {
3819 : /* Trigger write barriers on the old slots before reallocating. */
3820 450 : prepareSlotRangeForOverwrite(newSpan, oldSpan);
3821 450 : invalidateSlotRange(newSpan, oldSpan - newSpan);
3822 :
3823 450 : if (oldCount > newCount)
3824 0 : shrinkSlots(cx, oldCount, newCount);
3825 : }
3826 :
3827 21409261 : return true;
3828 : }
3829 :
3830 : bool
3831 32618299 : JSObject::setLastProperty(JSContext *cx, const js::Shape *shape)
3832 : {
3833 32618299 : JS_ASSERT(!inDictionaryMode());
3834 32618299 : JS_ASSERT(!shape->inDictionary());
3835 32618299 : JS_ASSERT(shape->compartment() == compartment());
3836 32618299 : JS_ASSERT(shape->numFixedSlots() == numFixedSlots());
3837 :
3838 32618299 : size_t oldSpan = lastProperty()->slotSpan();
3839 32618299 : size_t newSpan = shape->slotSpan();
3840 :
3841 32618299 : if (oldSpan == newSpan) {
3842 12504694 : shape_ = const_cast<js::Shape *>(shape);
3843 12504694 : return true;
3844 : }
3845 :
3846 20113605 : if (!updateSlotsForSpan(cx, oldSpan, newSpan))
3847 0 : return false;
3848 :
3849 20113605 : shape_ = const_cast<js::Shape *>(shape);
3850 20113605 : return true;
3851 : }
3852 :
3853 : bool
3854 1295656 : JSObject::setSlotSpan(JSContext *cx, uint32_t span)
3855 : {
3856 1295656 : JS_ASSERT(inDictionaryMode());
3857 1295656 : js::BaseShape *base = lastProperty()->base();
3858 :
3859 1295656 : size_t oldSpan = base->slotSpan();
3860 :
3861 1295656 : if (oldSpan == span)
3862 0 : return true;
3863 :
3864 1295656 : if (!updateSlotsForSpan(cx, oldSpan, span))
3865 0 : return false;
3866 :
3867 1295656 : base->setSlotSpan(span);
3868 1295656 : return true;
3869 : }
3870 :
3871 : bool
3872 4465699 : JSObject::growSlots(JSContext *cx, uint32_t oldCount, uint32_t newCount)
3873 : {
3874 4465699 : JS_ASSERT(newCount > oldCount);
3875 4465699 : JS_ASSERT(newCount >= SLOT_CAPACITY_MIN);
3876 4465699 : JS_ASSERT(!isDenseArray());
3877 :
3878 : /*
3879 : * Slots are only allocated for call objects when new properties are
3880 : * added to them, which can only happen while the call is still on the
3881 : * stack (and an eval, DEFFUN, etc. happens). We thus do not need to
3882 : * worry about updating any active outer function args/vars.
3883 : */
3884 4465699 : JS_ASSERT_IF(isCall(), asCall().maybeStackFrame() != NULL);
3885 :
3886 : /*
3887 : * Slot capacities are determined by the span of allocated objects. Due to
3888 : * the limited number of bits to store shape slots, object growth is
3889 : * throttled well before the slot capacity can overflow.
3890 : */
3891 4465699 : JS_ASSERT(newCount < NELEMENTS_LIMIT);
3892 :
3893 4465699 : size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
3894 4465699 : size_t newSize = oldSize + (newCount - oldCount) * sizeof(Value);
3895 :
3896 : /*
3897 : * If we are allocating slots for an object whose type is always created
3898 : * by calling 'new' on a particular script, bump the GC kind for that
3899 : * type to give these objects a larger number of fixed slots when future
3900 : * objects are constructed.
3901 : */
3902 4465699 : if (!hasLazyType() && !oldCount && type()->newScript) {
3903 22 : gc::AllocKind kind = type()->newScript->allocKind;
3904 22 : unsigned newScriptSlots = gc::GetGCKindSlots(kind);
3905 22 : if (newScriptSlots == numFixedSlots() && gc::TryIncrementAllocKind(&kind)) {
3906 : JSObject *obj = NewReshapedObject(cx, type(), getParent(), kind,
3907 17 : type()->newScript->shape);
3908 17 : if (!obj)
3909 0 : return false;
3910 :
3911 17 : type()->newScript->allocKind = kind;
3912 17 : type()->newScript->shape = obj->lastProperty();
3913 17 : type()->markStateChange(cx);
3914 : }
3915 : }
3916 :
3917 4465699 : if (!oldCount) {
3918 4006912 : slots = (HeapSlot *) cx->malloc_(newCount * sizeof(HeapSlot));
3919 4006912 : if (!slots)
3920 0 : return false;
3921 4006912 : Debug_SetSlotRangeToCrashOnTouch(slots, newCount);
3922 4006912 : if (Probes::objectResizeActive())
3923 0 : Probes::resizeObject(cx, this, oldSize, newSize);
3924 4006912 : return true;
3925 : }
3926 :
3927 : HeapSlot *newslots = (HeapSlot*) cx->realloc_(slots, oldCount * sizeof(HeapSlot),
3928 458787 : newCount * sizeof(HeapSlot));
3929 458787 : if (!newslots)
3930 0 : return false; /* Leave slots at its old size. */
3931 :
3932 458787 : bool changed = slots != newslots;
3933 458787 : slots = newslots;
3934 :
3935 458787 : Debug_SetSlotRangeToCrashOnTouch(slots + oldCount, newCount - oldCount);
3936 :
3937 : /* Changes in the slots of global objects can trigger recompilation. */
3938 458787 : if (changed && isGlobal())
3939 23700 : types::MarkObjectStateChange(cx, this);
3940 :
3941 458787 : if (Probes::objectResizeActive())
3942 0 : Probes::resizeObject(cx, this, oldSize, newSize);
3943 :
3944 458787 : return true;
3945 : }
3946 :
3947 : void
3948 0 : JSObject::shrinkSlots(JSContext *cx, uint32_t oldCount, uint32_t newCount)
3949 : {
3950 0 : JS_ASSERT(newCount < oldCount);
3951 0 : JS_ASSERT(!isDenseArray());
3952 :
3953 : /*
3954 : * Refuse to shrink slots for call objects. This only happens in a very
3955 : * obscure situation (deleting names introduced by a direct 'eval') and
3956 : * allowing the slots pointer to change may require updating pointers in
3957 : * the function's active args/vars information.
3958 : */
3959 0 : if (isCall())
3960 0 : return;
3961 :
3962 0 : size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
3963 0 : size_t newSize = oldSize - (oldCount - newCount) * sizeof(Value);
3964 :
3965 0 : if (newCount == 0) {
3966 0 : cx->free_(slots);
3967 0 : slots = NULL;
3968 0 : if (Probes::objectResizeActive())
3969 0 : Probes::resizeObject(cx, this, oldSize, newSize);
3970 0 : return;
3971 : }
3972 :
3973 0 : JS_ASSERT(newCount >= SLOT_CAPACITY_MIN);
3974 :
3975 0 : HeapSlot *newslots = (HeapSlot *) cx->realloc_(slots, newCount * sizeof(HeapSlot));
3976 0 : if (!newslots)
3977 0 : return; /* Leave slots at its old size. */
3978 :
3979 0 : bool changed = slots != newslots;
3980 0 : slots = newslots;
3981 :
3982 : /* Watch for changes in global object slots, as for growSlots. */
3983 0 : if (changed && isGlobal())
3984 0 : types::MarkObjectStateChange(cx, this);
3985 :
3986 0 : if (Probes::objectResizeActive())
3987 0 : Probes::resizeObject(cx, this, oldSize, newSize);
3988 : }
3989 :
3990 : bool
3991 56560 : JSObject::growElements(JSContext *cx, unsigned newcap)
3992 : {
3993 56560 : JS_ASSERT(isDenseArray());
3994 :
3995 : /*
3996 : * When an object with CAPACITY_DOUBLING_MAX or fewer elements needs to
3997 : * grow, double its capacity, to add N elements in amortized O(N) time.
3998 : *
3999 : * Above this limit, grow by 12.5% each time. Speed is still amortized
4000 : * O(N), with a higher constant factor, and we waste less space.
4001 : */
4002 : static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024;
4003 : static const size_t CAPACITY_CHUNK = CAPACITY_DOUBLING_MAX / sizeof(Value);
4004 :
4005 56560 : uint32_t oldcap = getDenseArrayCapacity();
4006 56560 : JS_ASSERT(oldcap <= newcap);
4007 :
4008 56560 : size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
4009 :
4010 : uint32_t nextsize = (oldcap <= CAPACITY_DOUBLING_MAX)
4011 : ? oldcap * 2
4012 56560 : : oldcap + (oldcap >> 3);
4013 :
4014 56560 : uint32_t actualCapacity = JS_MAX(newcap, nextsize);
4015 56560 : if (actualCapacity >= CAPACITY_CHUNK)
4016 27 : actualCapacity = JS_ROUNDUP(actualCapacity, CAPACITY_CHUNK);
4017 56533 : else if (actualCapacity < SLOT_CAPACITY_MIN)
4018 4392 : actualCapacity = SLOT_CAPACITY_MIN;
4019 :
4020 : /* Don't let nelements get close to wrapping around uint32_t. */
4021 56560 : if (actualCapacity >= NELEMENTS_LIMIT || actualCapacity < oldcap || actualCapacity < newcap) {
4022 0 : JS_ReportOutOfMemory(cx);
4023 0 : return false;
4024 : }
4025 :
4026 56560 : uint32_t initlen = getDenseArrayInitializedLength();
4027 56560 : uint32_t newAllocated = actualCapacity + ObjectElements::VALUES_PER_HEADER;
4028 :
4029 : ObjectElements *newheader;
4030 56560 : if (hasDynamicElements()) {
4031 12274 : uint32_t oldAllocated = oldcap + ObjectElements::VALUES_PER_HEADER;
4032 : newheader = (ObjectElements *)
4033 12274 : cx->realloc_(getElementsHeader(), oldAllocated * sizeof(Value),
4034 24548 : newAllocated * sizeof(Value));
4035 12274 : if (!newheader)
4036 0 : return false; /* Leave elements as its old size. */
4037 : } else {
4038 44286 : newheader = (ObjectElements *) cx->malloc_(newAllocated * sizeof(Value));
4039 44286 : if (!newheader)
4040 0 : return false; /* Ditto. */
4041 44286 : js_memcpy(newheader, getElementsHeader(),
4042 88572 : (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
4043 : }
4044 :
4045 56560 : newheader->capacity = actualCapacity;
4046 56560 : elements = newheader->elements();
4047 :
4048 56560 : Debug_SetSlotRangeToCrashOnTouch(elements + initlen, actualCapacity - initlen);
4049 :
4050 56560 : if (Probes::objectResizeActive())
4051 0 : Probes::resizeObject(cx, this, oldSize, computedSizeOfThisSlotsElements());
4052 :
4053 56560 : return true;
4054 : }
4055 :
4056 : void
4057 227 : JSObject::shrinkElements(JSContext *cx, unsigned newcap)
4058 : {
4059 227 : JS_ASSERT(isDenseArray());
4060 :
4061 227 : uint32_t oldcap = getDenseArrayCapacity();
4062 227 : JS_ASSERT(newcap <= oldcap);
4063 :
4064 227 : size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
4065 :
4066 : /* Don't shrink elements below the minimum capacity. */
4067 227 : if (oldcap <= SLOT_CAPACITY_MIN || !hasDynamicElements())
4068 209 : return;
4069 :
4070 18 : newcap = Max(newcap, SLOT_CAPACITY_MIN);
4071 :
4072 18 : uint32_t newAllocated = newcap + ObjectElements::VALUES_PER_HEADER;
4073 :
4074 : ObjectElements *newheader = (ObjectElements *)
4075 18 : cx->realloc_(getElementsHeader(), newAllocated * sizeof(Value));
4076 18 : if (!newheader)
4077 0 : return; /* Leave elements at its old size. */
4078 :
4079 18 : newheader->capacity = newcap;
4080 18 : elements = newheader->elements();
4081 :
4082 18 : if (Probes::objectResizeActive())
4083 0 : Probes::resizeObject(cx, this, oldSize, computedSizeOfThisSlotsElements());
4084 : }
4085 :
4086 : static JSObject *
4087 0 : js_InitNullClass(JSContext *cx, JSObject *obj)
4088 : {
4089 0 : JS_ASSERT(0);
4090 0 : return NULL;
4091 : }
4092 :
4093 : #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
4094 : #include "jsproto.tbl"
4095 : #undef JS_PROTO
4096 :
4097 : static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = {
4098 : #define JS_PROTO(name,code,init) init,
4099 : #include "jsproto.tbl"
4100 : #undef JS_PROTO
4101 : };
4102 :
4103 : namespace js {
4104 :
4105 : bool
4106 24048 : SetProto(JSContext *cx, JSObject *obj, JSObject *proto, bool checkForCycles)
4107 : {
4108 24048 : JS_ASSERT_IF(!checkForCycles, obj != proto);
4109 24048 : JS_ASSERT(obj->isExtensible());
4110 :
4111 24048 : if (proto && proto->isXML()) {
4112 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XML_PROTO_FORBIDDEN);
4113 0 : return false;
4114 : }
4115 :
4116 : /*
4117 : * Regenerate shapes for all of the scopes along the old prototype chain,
4118 : * in case any entries were filled by looking up through obj. Stop when a
4119 : * non-native object is found, prototype lookups will not be cached across
4120 : * these.
4121 : *
4122 : * How this shape change is done is very delicate; the change can be made
4123 : * either by marking the object's prototype as uncacheable (such that the
4124 : * property cache and JIT'ed ICs cannot assume the shape determines the
4125 : * prototype) or by just generating a new shape for the object. Choosing
4126 : * the former is bad if the object is on the prototype chain of other
4127 : * objects, as the uncacheable prototype can inhibit iterator caches on
4128 : * those objects and slow down prototype accesses. Choosing the latter is
4129 : * bad if there are many similar objects to this one which will have their
4130 : * prototype mutated, as the generateOwnShape forces the object into
4131 : * dictionary mode and similar property lineages will be repeatedly cloned.
4132 : *
4133 : * :XXX: bug 707717 make this code less brittle.
4134 : */
4135 24048 : JSObject *oldproto = obj;
4136 119466 : while (oldproto && oldproto->isNative()) {
4137 71370 : if (oldproto->hasSingletonType()) {
4138 39430 : if (!oldproto->generateOwnShape(cx))
4139 0 : return false;
4140 : } else {
4141 31940 : if (!oldproto->setUncacheableProto(cx))
4142 0 : return false;
4143 : }
4144 71370 : oldproto = oldproto->getProto();
4145 : }
4146 :
4147 24048 : if (checkForCycles) {
4148 1746 : for (JSObject *obj2 = proto; obj2; obj2 = obj2->getProto()) {
4149 1026 : if (obj2 == obj) {
4150 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CYCLIC_VALUE,
4151 0 : js_proto_str);
4152 0 : return false;
4153 : }
4154 : }
4155 : }
4156 :
4157 24048 : if (obj->hasSingletonType()) {
4158 : /*
4159 : * Just splice the prototype, but mark the properties as unknown for
4160 : * consistent behavior.
4161 : */
4162 13230 : if (!obj->splicePrototype(cx, proto))
4163 0 : return false;
4164 13230 : MarkTypeObjectUnknownProperties(cx, obj->type());
4165 13230 : return true;
4166 : }
4167 :
4168 10818 : if (proto && !proto->setNewTypeUnknown(cx))
4169 0 : return false;
4170 :
4171 : TypeObject *type = proto
4172 : ? proto->getNewType(cx, NULL)
4173 10818 : : cx->compartment->getEmptyType(cx);
4174 10818 : if (!type)
4175 0 : return false;
4176 :
4177 : /*
4178 : * Setting __proto__ on an object that has escaped and may be referenced by
4179 : * other heap objects can only be done if the properties of both objects
4180 : * are unknown. Type sets containing this object will contain the original
4181 : * type but not the new type of the object, so we need to go and scan the
4182 : * entire compartment for type sets which have these objects and mark them
4183 : * as containing generic objects.
4184 : */
4185 10818 : MarkTypeObjectUnknownProperties(cx, obj->type(), true);
4186 10818 : MarkTypeObjectUnknownProperties(cx, type, true);
4187 :
4188 10818 : obj->setType(type);
4189 10818 : return true;
4190 : }
4191 :
4192 : }
4193 :
4194 : JSBool
4195 76673 : js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
4196 : JSObject **objp)
4197 : {
4198 153346 : RootObject objRoot(cx, &obj);
4199 :
4200 76673 : obj = &obj->global();
4201 76673 : if (!obj->isGlobal()) {
4202 0 : *objp = NULL;
4203 0 : return true;
4204 : }
4205 :
4206 76673 : Value v = obj->getReservedSlot(key);
4207 76673 : if (v.isObject()) {
4208 21229 : *objp = &v.toObject();
4209 21229 : return true;
4210 : }
4211 :
4212 110888 : AutoResolving resolving(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]));
4213 55444 : if (resolving.alreadyStarted()) {
4214 : /* Already caching id in obj -- suppress recursion. */
4215 4587 : *objp = NULL;
4216 4587 : return true;
4217 : }
4218 :
4219 50857 : JSObject *cobj = NULL;
4220 50857 : if (JSObjectOp init = lazy_prototype_init[key]) {
4221 50857 : if (!init(cx, obj))
4222 0 : return false;
4223 50857 : v = obj->getReservedSlot(key);
4224 50857 : if (v.isObject())
4225 49660 : cobj = &v.toObject();
4226 : }
4227 :
4228 50857 : *objp = cobj;
4229 50857 : return true;
4230 : }
4231 :
4232 : JSBool
4233 338102 : js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey,
4234 : Value *vp, Class *clasp)
4235 : {
4236 : JSObject *cobj, *pobj;
4237 : jsid id;
4238 : JSProperty *prop;
4239 : const Shape *shape;
4240 :
4241 676204 : RootedVarObject obj(cx);
4242 :
4243 338102 : if (start) {
4244 314640 : obj = &start->global();
4245 314640 : OBJ_TO_INNER_OBJECT(cx, *obj.address());
4246 : } else {
4247 23462 : obj = GetGlobalForScopeChain(cx);
4248 : }
4249 338102 : if (!obj)
4250 0 : return false;
4251 :
4252 338102 : if (protoKey != JSProto_Null) {
4253 72034 : JS_ASSERT(JSProto_Null < protoKey);
4254 72034 : JS_ASSERT(protoKey < JSProto_LIMIT);
4255 72034 : if (!js_GetClassObject(cx, obj, protoKey, &cobj))
4256 0 : return false;
4257 72034 : if (cobj) {
4258 68398 : vp->setObject(*cobj);
4259 68398 : return JS_TRUE;
4260 : }
4261 3636 : id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[protoKey]);
4262 : } else {
4263 266068 : JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name));
4264 266068 : if (!atom)
4265 0 : return false;
4266 266068 : id = ATOM_TO_JSID(atom);
4267 : }
4268 :
4269 269704 : JS_ASSERT(obj->isNative());
4270 269704 : if (!LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME, &pobj, &prop))
4271 0 : return false;
4272 269704 : Value v = UndefinedValue();
4273 269704 : if (prop && pobj->isNative()) {
4274 2433 : shape = (Shape *) prop;
4275 2433 : if (shape->hasSlot()) {
4276 2433 : v = pobj->nativeGetSlot(shape->slot());
4277 2433 : if (v.isPrimitive())
4278 0 : v.setUndefined();
4279 : }
4280 : }
4281 269704 : *vp = v;
4282 269704 : return true;
4283 : }
4284 :
4285 : bool
4286 18130383 : JSObject::allocSlot(JSContext *cx, uint32_t *slotp)
4287 : {
4288 18130383 : uint32_t slot = slotSpan();
4289 18130383 : JS_ASSERT(slot >= JSSLOT_FREE(getClass()));
4290 :
4291 : /*
4292 : * If this object is in dictionary mode, try to pull a free slot from the
4293 : * property table's slot-number freelist.
4294 : */
4295 18130383 : if (inDictionaryMode()) {
4296 1295257 : PropertyTable &table = lastProperty()->table();
4297 1295257 : uint32_t last = table.freelist;
4298 1295257 : if (last != SHAPE_INVALID_SLOT) {
4299 : #ifdef DEBUG
4300 1836 : JS_ASSERT(last < slot);
4301 1836 : uint32_t next = getSlot(last).toPrivateUint32();
4302 1836 : JS_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot);
4303 : #endif
4304 :
4305 1836 : *slotp = last;
4306 :
4307 1836 : const Value &vref = getSlot(last);
4308 1836 : table.freelist = vref.toPrivateUint32();
4309 1836 : setSlot(last, UndefinedValue());
4310 1836 : return true;
4311 : }
4312 : }
4313 :
4314 18128547 : if (slot >= SHAPE_MAXIMUM_SLOT) {
4315 0 : js_ReportOutOfMemory(cx);
4316 0 : return false;
4317 : }
4318 :
4319 18128547 : *slotp = slot;
4320 :
4321 18128547 : if (inDictionaryMode() && !setSlotSpan(cx, slot + 1))
4322 0 : return false;
4323 :
4324 18128547 : return true;
4325 : }
4326 :
4327 : void
4328 2763 : JSObject::freeSlot(JSContext *cx, uint32_t slot)
4329 : {
4330 2763 : JS_ASSERT(slot < slotSpan());
4331 :
4332 2763 : if (inDictionaryMode()) {
4333 2412 : uint32_t &last = lastProperty()->table().freelist;
4334 :
4335 : /* Can't afford to check the whole freelist, but let's check the head. */
4336 2412 : JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan() && last != slot);
4337 :
4338 : /*
4339 : * Place all freed slots other than reserved slots (bug 595230) on the
4340 : * dictionary's free list.
4341 : */
4342 2412 : if (JSSLOT_FREE(getClass()) <= slot) {
4343 2196 : JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan());
4344 2196 : setSlot(slot, PrivateUint32Value(last));
4345 2196 : last = slot;
4346 2196 : return;
4347 : }
4348 : }
4349 567 : setSlot(slot, UndefinedValue());
4350 : }
4351 :
4352 : static bool
4353 627081 : PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id)
4354 : {
4355 : const Shape *shape;
4356 :
4357 1254162 : RootObject objRoot(cx, &obj);
4358 1254162 : RootId idRoot(cx, &id);
4359 :
4360 1455164 : while (obj) {
4361 271988 : if (!obj->isNative()) {
4362 20 : obj = obj->getProto();
4363 20 : continue;
4364 : }
4365 271968 : shape = obj->nativeLookup(cx, id);
4366 271968 : if (shape) {
4367 70986 : if (!obj->shadowingShapeChange(cx, *shape))
4368 0 : return false;
4369 :
4370 70986 : obj->shadowingShapeChange(cx, *shape);
4371 70986 : return true;
4372 : }
4373 200982 : obj = obj->getProto();
4374 : }
4375 :
4376 556095 : return true;
4377 : }
4378 :
4379 : bool
4380 626973 : js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id)
4381 : {
4382 1253946 : RootObject objRoot(cx, &obj);
4383 1253946 : RootId idRoot(cx, &id);
4384 :
4385 626973 : JS_ASSERT(obj->isDelegate());
4386 626973 : PurgeProtoChain(cx, obj->getProto(), id);
4387 :
4388 : /*
4389 : * We must purge the scope chain only for Call objects as they are the only
4390 : * kind of cacheable non-global object that can gain properties after outer
4391 : * properties with the same names have been cached or traced. Call objects
4392 : * may gain such properties via eval introducing new vars; see bug 490364.
4393 : */
4394 626973 : if (obj->isCall()) {
4395 252 : while ((obj = obj->enclosingScope()) != NULL) {
4396 108 : if (!PurgeProtoChain(cx, obj, id))
4397 0 : return false;
4398 : }
4399 : }
4400 :
4401 626973 : return true;
4402 : }
4403 :
4404 : Shape *
4405 18 : js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
4406 : PropertyOp getter, StrictPropertyOp setter, uint32_t slot,
4407 : unsigned attrs, unsigned flags, int shortid)
4408 : {
4409 : /* Convert string indices to integers if appropriate. */
4410 18 : id = js_CheckForStringIndex(id);
4411 :
4412 : /*
4413 : * Purge the property cache of now-shadowed id in obj's scope chain. Do
4414 : * this optimistically (assuming no failure below) before locking obj, so
4415 : * we can lock the shadowed scope.
4416 : */
4417 18 : if (!js_PurgeScopeChain(cx, obj, id))
4418 0 : return NULL;
4419 :
4420 18 : return obj->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid);
4421 : }
4422 :
4423 : JSBool
4424 13736320 : js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
4425 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
4426 : {
4427 13736320 : return !!DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs, 0, 0);
4428 : }
4429 :
4430 : JSBool
4431 1117505 : js_DefineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *value,
4432 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
4433 : {
4434 : jsid id;
4435 1117505 : if (!IndexToId(cx, index, &id))
4436 0 : return false;
4437 1117505 : return !!DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs, 0, 0);
4438 : }
4439 :
4440 : /*
4441 : * Backward compatibility requires allowing addProperty hooks to mutate the
4442 : * nominal initial value of a slotful property, while GC safety wants that
4443 : * value to be stored before the call-out through the hook. Optimize to do
4444 : * both while saving cycles for classes that stub their addProperty hook.
4445 : */
4446 : static inline bool
4447 31574642 : CallAddPropertyHook(JSContext *cx, Class *clasp, JSObject *obj, const Shape *shape, Value *vp)
4448 : {
4449 31574642 : if (clasp->addProperty != JS_PropertyStub) {
4450 2179328 : Value nominal = *vp;
4451 :
4452 2179328 : if (!CallJSPropertyOp(cx, clasp->addProperty, obj, shape->propid(), vp))
4453 0 : return false;
4454 2179328 : if (*vp != nominal) {
4455 0 : if (shape->hasSlot())
4456 0 : obj->nativeSetSlotWithType(cx, shape, *vp);
4457 : }
4458 : }
4459 31574642 : return true;
4460 : }
4461 :
4462 : namespace js {
4463 :
4464 : const Shape *
4465 29358075 : DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value_,
4466 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
4467 : unsigned flags, int shortid, unsigned defineHow /* = 0 */)
4468 : {
4469 0 : JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_DONT_PURGE |
4470 29358075 : DNP_SKIP_TYPE)) == 0);
4471 29358075 : JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
4472 :
4473 58716150 : RootObject objRoot(cx, &obj);
4474 58716150 : RootId idRoot(cx, &id);
4475 :
4476 : /* Make a local copy of value so addProperty can mutate its inout parameter. */
4477 58716150 : RootedVarValue value(cx);
4478 29358075 : value = value_;
4479 :
4480 : /* Convert string indices to integers if appropriate. */
4481 29358075 : id = js_CheckForStringIndex(id);
4482 :
4483 : /*
4484 : * If defining a getter or setter, we must check for its counterpart and
4485 : * update the attributes and property ops. A getter or setter is really
4486 : * only half of a property.
4487 : */
4488 29358075 : Shape *shape = NULL;
4489 29358075 : if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
4490 : JSObject *pobj;
4491 : JSProperty *prop;
4492 :
4493 : /* Type information for getter/setter properties is unknown. */
4494 1045597 : AddTypePropertyId(cx, obj, id, types::Type::UnknownType());
4495 1045597 : MarkTypePropertyConfigured(cx, obj, id);
4496 :
4497 : /*
4498 : * If we are defining a getter whose setter was already defined, or
4499 : * vice versa, finish the job via obj->changeProperty, and refresh the
4500 : * property cache line for (obj, id) to map shape.
4501 : */
4502 1045597 : if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
4503 0 : return NULL;
4504 1045597 : if (prop && pobj == obj) {
4505 316 : shape = (Shape *) prop;
4506 316 : if (shape->isAccessorDescriptor()) {
4507 : shape = obj->changeProperty(cx, shape, attrs,
4508 : JSPROP_GETTER | JSPROP_SETTER,
4509 : (attrs & JSPROP_GETTER)
4510 : ? getter
4511 : : shape->getter(),
4512 : (attrs & JSPROP_SETTER)
4513 : ? setter
4514 208 : : shape->setter());
4515 208 : if (!shape)
4516 0 : return NULL;
4517 : } else {
4518 108 : shape = NULL;
4519 : }
4520 : }
4521 : }
4522 :
4523 : /*
4524 : * Purge the property cache of any properties named by id that are about
4525 : * to be shadowed in obj's scope chain unless it is known a priori that it
4526 : * is not possible. We do this before locking obj to avoid nesting locks.
4527 : */
4528 29358075 : if (!(defineHow & DNP_DONT_PURGE)) {
4529 29357490 : if (!js_PurgeScopeChain(cx, obj, id))
4530 0 : return NULL;
4531 : }
4532 :
4533 : /* Use the object's class getter and setter by default. */
4534 29358075 : Class *clasp = obj->getClass();
4535 29358075 : if (!getter && !(attrs & JSPROP_GETTER))
4536 4255069 : getter = clasp->getProperty;
4537 29358075 : if (!setter && !(attrs & JSPROP_SETTER))
4538 15218501 : setter = clasp->setProperty;
4539 :
4540 29358075 : if ((getter == JS_PropertyStub) && !(defineHow & DNP_SKIP_TYPE)) {
4541 : /*
4542 : * Type information for normal native properties should reflect the
4543 : * initial value of the property.
4544 : */
4545 17310486 : AddTypePropertyId(cx, obj, id, value);
4546 17310486 : if (attrs & JSPROP_READONLY)
4547 7765045 : MarkTypePropertyConfigured(cx, obj, id);
4548 : }
4549 :
4550 29358075 : if (!shape) {
4551 : shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
4552 29357867 : attrs, flags, shortid);
4553 29357867 : if (!shape)
4554 0 : return NULL;
4555 : }
4556 :
4557 : /* Store valueCopy before calling addProperty, in case the latter GC's. */
4558 29358075 : if (shape->hasSlot())
4559 18087336 : obj->nativeSetSlot(shape->slot(), value);
4560 :
4561 29358075 : if (!CallAddPropertyHook(cx, clasp, obj, shape, value.address())) {
4562 0 : obj->removeProperty(cx, id);
4563 0 : return NULL;
4564 : }
4565 :
4566 29358075 : return shape;
4567 : }
4568 :
4569 : } /* namespace js */
4570 :
4571 : /*
4572 : * Call obj's resolve hook.
4573 : *
4574 : * cx, start, id, and flags are the parameters initially passed to the ongoing
4575 : * lookup; objp and propp are its out parameters. obj is an object along
4576 : * start's prototype chain.
4577 : *
4578 : * There are four possible outcomes:
4579 : *
4580 : * - On failure, report an error or exception and return false.
4581 : *
4582 : * - If we are already resolving a property of *curobjp, set *recursedp = true,
4583 : * and return true.
4584 : *
4585 : * - If the resolve hook finds or defines the sought property, set *objp and
4586 : * *propp appropriately, set *recursedp = false, and return true.
4587 : *
4588 : * - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
4589 : * and return true.
4590 : */
4591 : static JSBool
4592 3104347 : CallResolveOp(JSContext *cx, JSObject *start, HandleObject obj, HandleId id, unsigned flags,
4593 : JSObject **objp, JSProperty **propp, bool *recursedp)
4594 : {
4595 3104347 : Class *clasp = obj->getClass();
4596 3104347 : JSResolveOp resolve = clasp->resolve;
4597 :
4598 : /*
4599 : * Avoid recursion on (obj, id) already being resolved on cx.
4600 : *
4601 : * Once we have successfully added an entry for (obj, key) to
4602 : * cx->resolvingTable, control must go through cleanup: before
4603 : * returning. But note that JS_DHASH_ADD may find an existing
4604 : * entry, in which case we bail to suppress runaway recursion.
4605 : */
4606 6208694 : AutoResolving resolving(cx, obj, id);
4607 3104347 : if (resolving.alreadyStarted()) {
4608 : /* Already resolving id in obj -- suppress recursion. */
4609 2362 : *recursedp = true;
4610 2362 : return true;
4611 : }
4612 3101985 : *recursedp = false;
4613 :
4614 3101985 : *propp = NULL;
4615 :
4616 3101985 : if (clasp->flags & JSCLASS_NEW_RESOLVE) {
4617 3101985 : JSNewResolveOp newresolve = reinterpret_cast<JSNewResolveOp>(resolve);
4618 3101985 : if (flags == RESOLVE_INFER)
4619 799929 : flags = js_InferFlags(cx, 0);
4620 :
4621 6203970 : RootedVarObject obj2(cx);
4622 3101985 : obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL;
4623 3101985 : if (!newresolve(cx, obj, id, flags, obj2.address()))
4624 0 : return false;
4625 :
4626 : /*
4627 : * We trust the new style resolve hook to set obj2 to NULL when
4628 : * the id cannot be resolved. But, when obj2 is not null, we do
4629 : * not assume that id must exist and do full nativeLookup for
4630 : * compatibility.
4631 : */
4632 3101985 : if (!obj2)
4633 2136706 : return true;
4634 :
4635 965279 : if (!obj2->isNative()) {
4636 : /* Whoops, newresolve handed back a foreign obj2. */
4637 0 : JS_ASSERT(obj2 != obj);
4638 0 : return obj2->lookupGeneric(cx, id, objp, propp);
4639 : }
4640 4067264 : obj = obj2;
4641 : } else {
4642 0 : if (!resolve(cx, obj, id))
4643 0 : return false;
4644 : }
4645 :
4646 965279 : if (!obj->nativeEmpty()) {
4647 965279 : if (const Shape *shape = obj->nativeLookup(cx, id)) {
4648 965099 : *objp = obj;
4649 965099 : *propp = (JSProperty *) shape;
4650 : }
4651 : }
4652 :
4653 965279 : return true;
4654 : }
4655 :
4656 : static JS_ALWAYS_INLINE bool
4657 31594185 : LookupPropertyWithFlagsInline(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
4658 : JSObject **objp, JSProperty **propp)
4659 : {
4660 : /* We should not get string indices which aren't already integers here. */
4661 31594185 : JS_ASSERT(id == js_CheckForStringIndex(id));
4662 :
4663 63188370 : RootObject objRoot(cx, &obj);
4664 63188370 : RootId idRoot(cx, &id);
4665 :
4666 : /* Search scopes starting with obj and following the prototype link. */
4667 31594185 : JSObject *start = obj;
4668 10521142 : while (true) {
4669 42115327 : const Shape *shape = obj->nativeLookup(cx, id);
4670 42115327 : if (shape) {
4671 22838714 : *objp = obj;
4672 22838714 : *propp = (JSProperty *) shape;
4673 22838714 : return true;
4674 : }
4675 :
4676 : /* Try obj's class resolve hook if id was not found in obj's scope. */
4677 19276613 : if (obj->getClass()->resolve != JS_ResolveStub) {
4678 : bool recursed;
4679 3104347 : if (!CallResolveOp(cx, start, objRoot, idRoot, flags, objp, propp, &recursed))
4680 0 : return false;
4681 3104347 : if (recursed)
4682 2362 : break;
4683 3101985 : if (*propp) {
4684 : /*
4685 : * For stats we do not recalculate protoIndex even if it was
4686 : * resolved on some other object.
4687 : */
4688 965099 : return true;
4689 : }
4690 : }
4691 :
4692 18309152 : JSObject *proto = obj->getProto();
4693 18309152 : if (!proto)
4694 7787276 : break;
4695 10521876 : if (!proto->isNative()) {
4696 734 : if (!proto->lookupGeneric(cx, id, objp, propp))
4697 0 : return false;
4698 : #ifdef DEBUG
4699 : /*
4700 : * Non-native objects must have either non-native lookup results,
4701 : * or else native results from the non-native's prototype chain.
4702 : *
4703 : * See StackFrame::getValidCalleeObject, where we depend on this
4704 : * fact to force a prototype-delegated joined method accessed via
4705 : * arguments.callee through the delegating |this| object's method
4706 : * read barrier.
4707 : */
4708 734 : if (*propp && (*objp)->isNative()) {
4709 194 : while ((proto = proto->getProto()) != *objp)
4710 20 : JS_ASSERT(proto);
4711 : }
4712 : #endif
4713 734 : return true;
4714 : }
4715 :
4716 10521142 : obj = proto;
4717 : }
4718 :
4719 7789638 : *objp = NULL;
4720 7789638 : *propp = NULL;
4721 7789638 : return true;
4722 : }
4723 :
4724 : JS_FRIEND_API(JSBool)
4725 6660060 : js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
4726 : JSProperty **propp)
4727 : {
4728 : /* Convert string indices to integers if appropriate. */
4729 6660060 : id = js_CheckForStringIndex(id);
4730 :
4731 6660060 : return LookupPropertyWithFlagsInline(cx, obj, id, cx->resolveFlags, objp, propp);
4732 : }
4733 :
4734 : JS_FRIEND_API(JSBool)
4735 0 : js_LookupElement(JSContext *cx, JSObject *obj, uint32_t index, JSObject **objp, JSProperty **propp)
4736 : {
4737 : jsid id;
4738 0 : if (!IndexToId(cx, index, &id))
4739 0 : return false;
4740 :
4741 0 : return LookupPropertyWithFlagsInline(cx, obj, id, cx->resolveFlags, objp, propp);
4742 : }
4743 :
4744 : bool
4745 4346579 : js::LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
4746 : JSObject **objp, JSProperty **propp)
4747 : {
4748 : /* Convert string indices to integers if appropriate. */
4749 4346579 : id = js_CheckForStringIndex(id);
4750 :
4751 4346579 : return LookupPropertyWithFlagsInline(cx, obj, id, flags, objp, propp);
4752 : }
4753 :
4754 : bool
4755 1068661 : js::FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, JSObject *scopeChain,
4756 : JSObject **objp, JSObject **pobjp, JSProperty **propp)
4757 : {
4758 1068661 : jsid id = ATOM_TO_JSID(name);
4759 : JSObject *obj, *parent, *pobj;
4760 : int scopeIndex;
4761 : JSProperty *prop;
4762 :
4763 : /* Scan entries on the scope chain that we can cache across. */
4764 1068661 : obj = scopeChain;
4765 1068661 : parent = obj->enclosingScope();
4766 2489589 : for (scopeIndex = 0;
4767 : parent
4768 : ? IsCacheableNonGlobalScope(obj)
4769 929623 : : !obj->getOps()->lookupProperty;
4770 : ++scopeIndex) {
4771 1556627 : if (!LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, &pobj, &prop))
4772 0 : return false;
4773 :
4774 1556627 : if (prop) {
4775 : #ifdef DEBUG
4776 1063692 : if (parent) {
4777 135699 : JS_ASSERT(pobj->isNative());
4778 135699 : JS_ASSERT(pobj->getClass() == obj->getClass());
4779 135699 : if (obj->isBlock()) {
4780 : /*
4781 : * A block instance on the scope chain is immutable and
4782 : * shares its shape with the compile-time prototype. Thus
4783 : * we cannot find any property on the prototype.
4784 : */
4785 740 : JS_ASSERT(pobj->isClonedBlock());
4786 : } else {
4787 : /* Call and DeclEnvClass objects have no prototypes. */
4788 134959 : JS_ASSERT(!obj->getProto());
4789 : }
4790 135699 : JS_ASSERT(pobj == obj);
4791 : } else {
4792 927993 : JS_ASSERT(obj->isNative());
4793 : }
4794 : #endif
4795 :
4796 : /*
4797 : * We must check if pobj is native as a global object can have
4798 : * non-native prototype.
4799 : */
4800 1063692 : if (cacheResult && pobj->isNative()) {
4801 : JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, pobj,
4802 943786 : (Shape *) prop);
4803 : }
4804 :
4805 1063692 : goto out;
4806 : }
4807 :
4808 492935 : if (!parent) {
4809 1630 : pobj = NULL;
4810 1630 : goto out;
4811 : }
4812 491305 : obj = parent;
4813 491305 : parent = obj->enclosingScope();
4814 : }
4815 :
4816 2808 : for (;;) {
4817 6147 : if (!obj->lookupGeneric(cx, id, &pobj, &prop))
4818 0 : return false;
4819 6147 : if (prop)
4820 3312 : goto out;
4821 :
4822 : /*
4823 : * We conservatively assume that a resolve hook could mutate the scope
4824 : * chain during JSObject::lookupGeneric. So we read parent here again.
4825 : */
4826 2835 : parent = obj->enclosingScope();
4827 2835 : if (!parent) {
4828 27 : pobj = NULL;
4829 27 : break;
4830 : }
4831 2808 : obj = parent;
4832 : }
4833 :
4834 : out:
4835 1068661 : JS_ASSERT(!!pobj == !!prop);
4836 1068661 : *objp = obj;
4837 1068661 : *pobjp = pobj;
4838 1068661 : *propp = prop;
4839 1068661 : return true;
4840 : }
4841 :
4842 : /*
4843 : * On return, if |*pobjp| is a native object, then |*propp| is a |Shape *|.
4844 : * Otherwise, its type and meaning depends on the host object's implementation.
4845 : */
4846 : bool
4847 23397 : js::FindProperty(JSContext *cx, PropertyName *name, JSObject *scopeChain,
4848 : JSObject **objp, JSObject **pobjp, JSProperty **propp)
4849 : {
4850 23397 : return !!FindPropertyHelper(cx, name, false, scopeChain, objp, pobjp, propp);
4851 : }
4852 :
4853 : JSObject *
4854 25733 : js::FindIdentifierBase(JSContext *cx, JSObject *scopeChain, PropertyName *name)
4855 : {
4856 : /*
4857 : * This function should not be called for a global object or from the
4858 : * trace and should have a valid cache entry for native scopeChain.
4859 : */
4860 25733 : JS_ASSERT(scopeChain->enclosingScope() != NULL);
4861 :
4862 25733 : JSObject *obj = scopeChain;
4863 :
4864 : /*
4865 : * Loop over cacheable objects on the scope chain until we find a
4866 : * property. We also stop when we reach the global object skipping any
4867 : * farther checks or lookups. For details see the JSOP_BINDNAME case of
4868 : * js_Interpret.
4869 : *
4870 : * The test order here matters because IsCacheableNonGlobalScope
4871 : * must not be passed a global object (i.e. one with null parent).
4872 : */
4873 92328 : for (int scopeIndex = 0;
4874 59950 : obj->isGlobal() || IsCacheableNonGlobalScope(obj);
4875 : scopeIndex++) {
4876 : JSObject *pobj;
4877 : JSProperty *prop;
4878 31667 : if (!LookupPropertyWithFlags(cx, obj, name, cx->resolveFlags, &pobj, &prop))
4879 0 : return NULL;
4880 31667 : if (prop) {
4881 24599 : if (!pobj->isNative()) {
4882 0 : JS_ASSERT(obj->isGlobal());
4883 0 : return obj;
4884 : }
4885 24599 : JS_ASSERT_IF(obj->isScope(), pobj->getClass() == obj->getClass());
4886 24599 : JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, pobj, (Shape *) prop);
4887 24599 : return obj;
4888 : }
4889 :
4890 7068 : JSObject *parent = obj->enclosingScope();
4891 7068 : if (!parent)
4892 423 : return obj;
4893 6645 : obj = parent;
4894 : }
4895 :
4896 : /* Loop until we find a property or reach the global object. */
4897 693 : do {
4898 : JSObject *pobj;
4899 : JSProperty *prop;
4900 1071 : if (!obj->lookupProperty(cx, name, &pobj, &prop))
4901 0 : return NULL;
4902 1071 : if (prop)
4903 378 : break;
4904 :
4905 : /*
4906 : * We conservatively assume that a resolve hook could mutate the scope
4907 : * chain during JSObject::lookupGeneric. So we must check if parent is
4908 : * not null here even if it wasn't before the lookup.
4909 : */
4910 693 : JSObject *parent = obj->enclosingScope();
4911 693 : if (!parent)
4912 0 : break;
4913 693 : obj = parent;
4914 693 : } while (!obj->isGlobal());
4915 711 : return obj;
4916 : }
4917 :
4918 : static JS_ALWAYS_INLINE JSBool
4919 20532192 : js_NativeGetInline(JSContext *cx, JSObject *receiver, JSObject *obj, JSObject *pobj,
4920 : const Shape *shape, unsigned getHow, Value *vp)
4921 : {
4922 20532192 : JS_ASSERT(pobj->isNative());
4923 :
4924 20532192 : if (shape->hasSlot()) {
4925 20189532 : *vp = pobj->nativeGetSlot(shape->slot());
4926 20189532 : JS_ASSERT(!vp->isMagic());
4927 40584921 : JS_ASSERT_IF(!pobj->hasSingletonType() && shape->hasDefaultGetter(),
4928 40584921 : js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), *vp));
4929 : } else {
4930 342660 : vp->setUndefined();
4931 : }
4932 20532192 : if (shape->hasDefaultGetter())
4933 19359741 : return true;
4934 :
4935 : jsbytecode *pc;
4936 1172451 : JSScript *script = cx->stack.currentScript(&pc);
4937 1172451 : if (script && script->hasAnalysis()) {
4938 1139504 : analyze::Bytecode *code = script->analysis()->maybeCode(pc);
4939 1139504 : if (code)
4940 1139504 : code->accessGetter = true;
4941 : }
4942 :
4943 1172451 : if (!shape->get(cx, receiver, obj, pobj, vp))
4944 3448 : return false;
4945 :
4946 : /* Update slotful shapes according to the value produced by the getter. */
4947 1169003 : if (shape->hasSlot() && pobj->nativeContains(cx, *shape))
4948 829962 : pobj->nativeSetSlot(shape->slot(), *vp);
4949 :
4950 1169003 : return true;
4951 : }
4952 :
4953 : JSBool
4954 865052 : js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, unsigned getHow,
4955 : Value *vp)
4956 : {
4957 865052 : return js_NativeGetInline(cx, obj, obj, pobj, shape, getHow, vp);
4958 : }
4959 :
4960 : JSBool
4961 2498298 : js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, bool strict, Value *vp)
4962 : {
4963 2498298 : AddTypePropertyId(cx, obj, shape->propid(), *vp);
4964 :
4965 2498298 : JS_ASSERT(obj->isNative());
4966 :
4967 2498298 : if (shape->hasSlot()) {
4968 2491524 : uint32_t slot = shape->slot();
4969 :
4970 : /* If shape has a stub setter, just store *vp. */
4971 2491524 : if (shape->hasDefaultSetter()) {
4972 2472048 : obj->nativeSetSlot(slot, *vp);
4973 2472048 : return true;
4974 : }
4975 : } else {
4976 : /*
4977 : * Allow API consumers to create shared properties with stub setters.
4978 : * Such properties effectively function as data descriptors which are
4979 : * not writable, so attempting to set such a property should do nothing
4980 : * or throw if we're in strict mode.
4981 : */
4982 6774 : if (!shape->hasGetterValue() && shape->hasDefaultSetter())
4983 2 : return js_ReportGetterOnlyAssignment(cx);
4984 : }
4985 :
4986 26248 : int32_t sample = cx->runtime->propertyRemovals;
4987 26248 : if (!shape->set(cx, obj, strict, vp))
4988 135 : return false;
4989 :
4990 : /*
4991 : * Update any slot for the shape with the value produced by the setter,
4992 : * unless the setter deleted the shape.
4993 : */
4994 45589 : if (shape->hasSlot() &&
4995 19476 : (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
4996 0 : obj->nativeContains(cx, *shape))) {
4997 19476 : obj->setSlot(shape->slot(), *vp);
4998 : }
4999 :
5000 26113 : return true;
5001 : }
5002 :
5003 : static JS_ALWAYS_INLINE JSBool
5004 20587546 : js_GetPropertyHelperInline(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id,
5005 : uint32_t getHow, Value *vp)
5006 : {
5007 : JSObject *aobj, *obj2;
5008 : JSProperty *prop;
5009 : const Shape *shape;
5010 :
5011 : /* Convert string indices to integers if appropriate. */
5012 20587546 : id = js_CheckForStringIndex(id);
5013 :
5014 20587546 : aobj = js_GetProtoIfDenseArray(obj);
5015 : /* This call site is hot -- use the always-inlined variant of LookupPropertyWithFlags(). */
5016 20587546 : if (!LookupPropertyWithFlagsInline(cx, aobj, id, cx->resolveFlags, &obj2, &prop))
5017 0 : return false;
5018 :
5019 20587546 : if (!prop) {
5020 919961 : vp->setUndefined();
5021 :
5022 919961 : if (!CallJSPropertyOp(cx, obj->getClass()->getProperty, obj, id, vp))
5023 0 : return JS_FALSE;
5024 :
5025 : /* Record non-undefined values produced by the class getter hook. */
5026 919961 : if (!vp->isUndefined())
5027 0 : AddTypePropertyId(cx, obj, id, *vp);
5028 :
5029 : /*
5030 : * Give a strict warning if foo.bar is evaluated by a script for an
5031 : * object foo with no property named 'bar'.
5032 : */
5033 : jsbytecode *pc;
5034 919961 : if (vp->isUndefined() && ((pc = js_GetCurrentBytecodePC(cx)) != NULL)) {
5035 919600 : JSOp op = (JSOp) *pc;
5036 :
5037 919600 : if (op == JSOP_GETXPROP) {
5038 : /* Undefined property during a name lookup, report an error. */
5039 0 : JSAutoByteString printable;
5040 0 : if (js_ValueToPrintable(cx, IdToValue(id), &printable))
5041 0 : js_ReportIsNotDefined(cx, printable.ptr());
5042 0 : return false;
5043 : }
5044 :
5045 919626 : if (!cx->hasStrictOption() ||
5046 26 : cx->stack.currentScript()->warnedAboutUndefinedProp ||
5047 : (op != JSOP_GETPROP && op != JSOP_GETELEM)) {
5048 919591 : return JS_TRUE;
5049 : }
5050 :
5051 : /*
5052 : * XXX do not warn about missing __iterator__ as the function
5053 : * may be called from JS_GetMethodById. See bug 355145.
5054 : */
5055 9 : if (JSID_IS_ATOM(id, cx->runtime->atomState.iteratorAtom))
5056 0 : return JS_TRUE;
5057 :
5058 : /* Do not warn about tests like (obj[prop] == undefined). */
5059 9 : if (cx->resolveFlags == RESOLVE_INFER) {
5060 9 : pc += js_CodeSpec[op].length;
5061 9 : if (Detecting(cx, pc))
5062 9 : return JS_TRUE;
5063 0 : } else if (cx->resolveFlags & JSRESOLVE_DETECTING) {
5064 0 : return JS_TRUE;
5065 : }
5066 :
5067 0 : unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT;
5068 0 : cx->stack.currentScript()->warnedAboutUndefinedProp = true;
5069 :
5070 : /* Ok, bad undefined property reference: whine about it. */
5071 0 : if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP,
5072 : JSDVG_IGNORE_STACK, IdToValue(id),
5073 0 : NULL, NULL, NULL))
5074 : {
5075 0 : return false;
5076 : }
5077 : }
5078 361 : return JS_TRUE;
5079 : }
5080 :
5081 19667585 : if (!obj2->isNative()) {
5082 445 : return obj2->isProxy()
5083 58 : ? Proxy::get(cx, obj2, receiver, id, vp)
5084 503 : : obj2->getGeneric(cx, id, vp);
5085 : }
5086 :
5087 19667140 : shape = (Shape *) prop;
5088 :
5089 19667140 : if (getHow & JSGET_CACHE_RESULT)
5090 506593 : JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, obj2, shape);
5091 :
5092 : /* This call site is hot -- use the always-inlined variant of js_NativeGet(). */
5093 19667140 : if (!js_NativeGetInline(cx, receiver, obj, obj2, shape, getHow, vp))
5094 153 : return JS_FALSE;
5095 :
5096 19666987 : return JS_TRUE;
5097 : }
5098 :
5099 : bool
5100 1839688 : js::GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uint32_t getHow, Value *vp)
5101 : {
5102 1839688 : return !!js_GetPropertyHelperInline(cx, obj, obj, id, getHow, vp);
5103 : }
5104 :
5105 : JSBool
5106 18747858 : js_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
5107 : {
5108 : /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
5109 18747858 : return js_GetPropertyHelperInline(cx, obj, receiver, id, 0, vp);
5110 : }
5111 :
5112 : JSBool
5113 0 : js_GetElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp)
5114 : {
5115 : jsid id;
5116 0 : if (!IndexToId(cx, index, &id))
5117 0 : return false;
5118 :
5119 : /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
5120 0 : return js_GetPropertyHelperInline(cx, obj, receiver, id, 0, vp);
5121 : }
5122 :
5123 : JSBool
5124 4 : js::GetPropertyDefault(JSContext *cx, JSObject *obj, jsid id, const Value &def, Value *vp)
5125 : {
5126 : JSProperty *prop;
5127 : JSObject *obj2;
5128 4 : if (!LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop))
5129 0 : return false;
5130 :
5131 4 : if (!prop) {
5132 2 : *vp = def;
5133 2 : return true;
5134 : }
5135 :
5136 2 : return js_GetProperty(cx, obj2, id, vp);
5137 : }
5138 :
5139 : JSBool
5140 1508440 : js_GetMethod(JSContext *cx, JSObject *obj, jsid id, unsigned getHow, Value *vp)
5141 : {
5142 3016880 : JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
5143 :
5144 1508440 : GenericIdOp op = obj->getOps()->getGeneric;
5145 1508440 : if (!op) {
5146 : #if JS_HAS_XML_SUPPORT
5147 1327234 : JS_ASSERT(!obj->isXML());
5148 : #endif
5149 1327234 : return GetPropertyHelper(cx, obj, id, getHow, vp);
5150 : }
5151 : #if JS_HAS_XML_SUPPORT
5152 181206 : if (obj->isXML())
5153 99 : return js_GetXMLMethod(cx, obj, id, vp);
5154 : #endif
5155 181107 : return op(cx, obj, obj, id, vp);
5156 : }
5157 :
5158 : JS_FRIEND_API(bool)
5159 702 : js::CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname)
5160 : {
5161 702 : StackFrame *const fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL);
5162 702 : if (!fp)
5163 0 : return true;
5164 :
5165 : /* If neither cx nor the code is strict, then no check is needed. */
5166 1377 : if (!(fp->isScriptFrame() && fp->script()->strictModeCode) &&
5167 675 : !cx->hasStrictOption()) {
5168 675 : return true;
5169 : }
5170 :
5171 54 : JSAutoByteString bytes(cx, propname);
5172 27 : return !!bytes &&
5173 : JS_ReportErrorFlagsAndNumber(cx,
5174 : (JSREPORT_WARNING | JSREPORT_STRICT
5175 : | JSREPORT_STRICT_MODE_ERROR),
5176 : js_GetErrorMessage, NULL,
5177 27 : JSMSG_UNDECLARED_VAR, bytes.ptr());
5178 : }
5179 :
5180 : static bool
5181 18 : ReportReadOnly(JSContext *cx, jsid id, unsigned report)
5182 : {
5183 : return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
5184 : JSDVG_IGNORE_STACK, IdToValue(id), NULL,
5185 18 : NULL, NULL);
5186 : }
5187 :
5188 : bool
5189 9 : JSObject::reportNotConfigurable(JSContext *cx, jsid id, unsigned report)
5190 : {
5191 : return js_ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE,
5192 : JSDVG_IGNORE_STACK, IdToValue(id), NULL,
5193 9 : NULL, NULL);
5194 : }
5195 :
5196 : bool
5197 18 : JSObject::reportNotExtensible(JSContext *cx, unsigned report)
5198 : {
5199 : return js_ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE,
5200 : JSDVG_IGNORE_STACK, ObjectValue(*this),
5201 18 : NULL, NULL, NULL);
5202 : }
5203 :
5204 : bool
5205 9 : JSObject::callMethod(JSContext *cx, jsid id, unsigned argc, Value *argv, Value *vp)
5206 : {
5207 : Value fval;
5208 9 : return js_GetMethod(cx, this, id, 0, &fval) &&
5209 9 : Invoke(cx, ObjectValue(*this), fval, argc, argv, vp);
5210 : }
5211 :
5212 : JSBool
5213 2486099 : js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, unsigned defineHow,
5214 : Value *vp, JSBool strict)
5215 : {
5216 : JSObject *pobj;
5217 : JSProperty *prop;
5218 : const Shape *shape;
5219 : unsigned attrs, flags;
5220 : int shortid;
5221 : Class *clasp;
5222 : PropertyOp getter;
5223 : StrictPropertyOp setter;
5224 : bool added;
5225 :
5226 2486099 : JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_UNQUALIFIED)) == 0);
5227 :
5228 : /* Convert string indices to integers if appropriate. */
5229 2486099 : id = js_CheckForStringIndex(id);
5230 :
5231 2486099 : if (JS_UNLIKELY(obj->watched())) {
5232 : /* Fire watchpoints, if any. */
5233 4439 : WatchpointMap *wpmap = cx->compartment->watchpointMap;
5234 4439 : if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
5235 2862 : return false;
5236 : }
5237 :
5238 2483237 : if (!LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, &pobj, &prop))
5239 0 : return false;
5240 2483237 : if (prop) {
5241 764649 : if (!pobj->isNative()) {
5242 18 : if (pobj->isProxy()) {
5243 0 : AutoPropertyDescriptorRooter pd(cx);
5244 0 : if (!Proxy::getPropertyDescriptor(cx, pobj, id, true, &pd))
5245 0 : return false;
5246 :
5247 0 : if ((pd.attrs & (JSPROP_SHARED | JSPROP_SHADOWABLE)) == JSPROP_SHARED) {
5248 0 : return !pd.setter ||
5249 0 : CallSetter(cx, obj, id, pd.setter, pd.attrs, pd.shortid, strict, vp);
5250 : }
5251 :
5252 0 : if (pd.attrs & JSPROP_READONLY) {
5253 0 : if (strict)
5254 0 : return ReportReadOnly(cx, id, JSREPORT_ERROR);
5255 0 : if (cx->hasStrictOption())
5256 0 : return ReportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
5257 0 : return true;
5258 : }
5259 : }
5260 :
5261 18 : prop = NULL;
5262 : }
5263 : } else {
5264 : /* We should never add properties to lexical blocks. */
5265 1718588 : JS_ASSERT(!obj->isBlock());
5266 :
5267 1719290 : if (obj->isGlobal() &&
5268 : (defineHow & DNP_UNQUALIFIED) &&
5269 702 : !js::CheckUndeclaredVarAssignment(cx, JSID_TO_STRING(id))) {
5270 27 : return JS_FALSE;
5271 : }
5272 : }
5273 2483210 : shape = (Shape *) prop;
5274 :
5275 : /*
5276 : * Now either shape is null, meaning id was not found in obj or one of its
5277 : * prototypes; or shape is non-null, meaning id was found directly in pobj.
5278 : */
5279 2483210 : attrs = JSPROP_ENUMERATE;
5280 2483210 : flags = 0;
5281 2483210 : shortid = 0;
5282 2483210 : clasp = obj->getClass();
5283 2483210 : getter = clasp->getProperty;
5284 2483210 : setter = clasp->setProperty;
5285 :
5286 2483210 : if (shape) {
5287 : /* ES5 8.12.4 [[Put]] step 2. */
5288 764631 : if (shape->isAccessorDescriptor()) {
5289 5293 : if (shape->hasDefaultSetter())
5290 54 : return js_ReportGetterOnlyAssignment(cx);
5291 : } else {
5292 759338 : JS_ASSERT(shape->isDataDescriptor());
5293 :
5294 759338 : if (!shape->writable()) {
5295 : /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5296 855 : if (strict)
5297 18 : return ReportReadOnly(cx, id, JSREPORT_ERROR);
5298 837 : if (cx->hasStrictOption())
5299 0 : return ReportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
5300 837 : return JS_TRUE;
5301 : }
5302 : }
5303 :
5304 763722 : attrs = shape->attributes();
5305 763722 : if (pobj != obj) {
5306 : /*
5307 : * We found id in a prototype object: prepare to share or shadow.
5308 : */
5309 503601 : if (!shape->shadowable()) {
5310 5604 : if (defineHow & DNP_CACHE_RESULT)
5311 4488 : JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, pobj, shape);
5312 :
5313 5604 : if (shape->hasDefaultSetter() && !shape->hasGetterValue())
5314 0 : return JS_TRUE;
5315 :
5316 5604 : return shape->set(cx, obj, strict, vp);
5317 : }
5318 :
5319 : /*
5320 : * Preserve attrs except JSPROP_SHARED, getter, and setter when
5321 : * shadowing any property that has no slot (is shared). We must
5322 : * clear the shared attribute for the shadowing shape so that the
5323 : * property in obj that it defines has a slot to retain the value
5324 : * being set, in case the setter simply cannot operate on instances
5325 : * of obj's class by storing the value in some class-specific
5326 : * location.
5327 : *
5328 : * A subset of slotless shared properties is the set of properties
5329 : * with shortids, which must be preserved too. An old API requires
5330 : * that the property's getter and setter receive the shortid, not
5331 : * id, when they are called on the shadowing property that we are
5332 : * about to create in obj.
5333 : */
5334 497997 : if (!shape->hasSlot()) {
5335 0 : if (shape->hasShortID()) {
5336 0 : flags = Shape::HAS_SHORTID;
5337 0 : shortid = shape->shortid();
5338 : }
5339 0 : attrs &= ~JSPROP_SHARED;
5340 0 : getter = shape->getter();
5341 0 : setter = shape->setter();
5342 : } else {
5343 : /* Restore attrs to the ECMA default for new properties. */
5344 497997 : attrs = JSPROP_ENUMERATE;
5345 : }
5346 :
5347 : /*
5348 : * Forget we found the proto-property now that we've copied any
5349 : * needed member values.
5350 : */
5351 497997 : shape = NULL;
5352 : }
5353 : }
5354 :
5355 2476697 : added = false;
5356 2476697 : if (!shape) {
5357 2216576 : if (!obj->isExtensible()) {
5358 : /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5359 9 : if (strict)
5360 9 : return obj->reportNotExtensible(cx);
5361 0 : if (cx->hasStrictOption())
5362 0 : return obj->reportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING);
5363 0 : return JS_TRUE;
5364 : }
5365 :
5366 : /*
5367 : * Purge the property cache of now-shadowed id in obj's scope chain.
5368 : * Do this early, before locking obj to avoid nesting locks.
5369 : */
5370 2216567 : if (!js_PurgeScopeChain(cx, obj, id))
5371 0 : return JS_FALSE;
5372 :
5373 : shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
5374 2216567 : attrs, flags, shortid);
5375 2216567 : if (!shape)
5376 0 : return JS_FALSE;
5377 :
5378 : /*
5379 : * Initialize the new property value (passed to setter) to undefined.
5380 : * Note that we store before calling addProperty, to match the order
5381 : * in DefineNativeProperty.
5382 : */
5383 2216567 : if (shape->hasSlot())
5384 2216567 : obj->nativeSetSlot(shape->slot(), UndefinedValue());
5385 :
5386 : /* XXXbe called with obj locked */
5387 2216567 : if (!CallAddPropertyHook(cx, clasp, obj, shape, vp)) {
5388 0 : obj->removeProperty(cx, id);
5389 0 : return JS_FALSE;
5390 : }
5391 2216567 : added = true;
5392 : }
5393 :
5394 2476688 : if ((defineHow & DNP_CACHE_RESULT) && !added)
5395 192285 : JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, obj, shape);
5396 :
5397 2476688 : return js_NativeSet(cx, obj, shape, added, strict, vp);
5398 : }
5399 :
5400 : JSBool
5401 1 : js_SetElementHelper(JSContext *cx, JSObject *obj, uint32_t index, unsigned defineHow,
5402 : Value *vp, JSBool strict)
5403 : {
5404 : jsid id;
5405 1 : if (!IndexToId(cx, index, &id))
5406 0 : return false;
5407 1 : return js_SetPropertyHelper(cx, obj, id, defineHow, vp, strict);
5408 : }
5409 :
5410 : JSBool
5411 83124 : js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
5412 : {
5413 : JSProperty *prop;
5414 83124 : if (!js_LookupProperty(cx, obj, id, &obj, &prop))
5415 0 : return false;
5416 83124 : if (!prop) {
5417 0 : *attrsp = 0;
5418 0 : return true;
5419 : }
5420 83124 : if (!obj->isNative())
5421 0 : return obj->getGenericAttributes(cx, id, attrsp);
5422 :
5423 83124 : const Shape *shape = (Shape *)prop;
5424 83124 : *attrsp = shape->attributes();
5425 83124 : return true;
5426 : }
5427 :
5428 : JSBool
5429 0 : js_GetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
5430 : {
5431 : JSProperty *prop;
5432 0 : if (!js_LookupElement(cx, obj, index, &obj, &prop))
5433 0 : return false;
5434 0 : if (!prop) {
5435 0 : *attrsp = 0;
5436 0 : return true;
5437 : }
5438 0 : if (!obj->isNative())
5439 0 : return obj->getElementAttributes(cx, index, attrsp);
5440 :
5441 0 : const Shape *shape = (Shape *)prop;
5442 0 : *attrsp = shape->attributes();
5443 0 : return true;
5444 : }
5445 :
5446 : JSBool
5447 31914 : js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
5448 : {
5449 : JSProperty *prop;
5450 31914 : if (!js_LookupProperty(cx, obj, id, &obj, &prop))
5451 0 : return false;
5452 31914 : if (!prop)
5453 0 : return true;
5454 31914 : return obj->isNative()
5455 31914 : ? obj->changePropertyAttributes(cx, (Shape *) prop, *attrsp)
5456 63828 : : obj->setGenericAttributes(cx, id, attrsp);
5457 : }
5458 :
5459 : JSBool
5460 0 : js_SetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
5461 : {
5462 : JSProperty *prop;
5463 0 : if (!js_LookupElement(cx, obj, index, &obj, &prop))
5464 0 : return false;
5465 0 : if (!prop)
5466 0 : return true;
5467 0 : return obj->isNative()
5468 0 : ? obj->changePropertyAttributes(cx, (Shape *) prop, *attrsp)
5469 0 : : obj->setElementAttributes(cx, index, attrsp);
5470 : }
5471 :
5472 : JSBool
5473 195354 : js_DeleteGeneric(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
5474 : {
5475 : JSObject *proto;
5476 : JSProperty *prop;
5477 : const Shape *shape;
5478 :
5479 195354 : rval->setBoolean(true);
5480 :
5481 : /* Convert string indices to integers if appropriate. */
5482 195354 : id = js_CheckForStringIndex(id);
5483 :
5484 195354 : if (!js_LookupProperty(cx, obj, id, &proto, &prop))
5485 0 : return false;
5486 195354 : if (!prop || proto != obj) {
5487 : /*
5488 : * If no property, or the property comes from a prototype, call the
5489 : * class's delProperty hook, passing rval as the result parameter.
5490 : */
5491 191736 : return CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, id, rval);
5492 : }
5493 :
5494 3618 : shape = (Shape *)prop;
5495 3618 : if (!shape->configurable()) {
5496 27 : if (strict)
5497 9 : return obj->reportNotConfigurable(cx, id);
5498 18 : rval->setBoolean(false);
5499 18 : return true;
5500 : }
5501 :
5502 3591 : if (shape->hasSlot()) {
5503 2709 : const Value &v = obj->nativeGetSlot(shape->slot());
5504 2709 : GCPoke(cx->runtime, v);
5505 : }
5506 :
5507 3591 : if (!CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, shape->getUserId(), rval))
5508 0 : return false;
5509 3591 : if (rval->isFalse())
5510 0 : return true;
5511 :
5512 3591 : return obj->removeProperty(cx, id) && js_SuppressDeletedProperty(cx, obj, id);
5513 : }
5514 :
5515 : JSBool
5516 1377 : js_DeleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict)
5517 : {
5518 1377 : return js_DeleteGeneric(cx, obj, ATOM_TO_JSID(name), rval, strict);
5519 : }
5520 :
5521 : JSBool
5522 193536 : js_DeleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict)
5523 : {
5524 : jsid id;
5525 193536 : if (!IndexToId(cx, index, &id))
5526 0 : return false;
5527 193536 : return js_DeleteGeneric(cx, obj, id, rval, strict);
5528 : }
5529 :
5530 : JSBool
5531 0 : js_DeleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict)
5532 : {
5533 0 : return js_DeleteGeneric(cx, obj, SPECIALID_TO_JSID(sid), rval, strict);
5534 : }
5535 :
5536 : namespace js {
5537 :
5538 : bool
5539 1102635 : HasDataProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
5540 : {
5541 1102635 : JS_ASSERT(id == js_CheckForStringIndex(id));
5542 1102635 : if (const Shape *shape = obj->nativeLookup(cx, id)) {
5543 918000 : if (shape->hasDefaultGetter() && shape->hasSlot()) {
5544 917973 : *vp = obj->nativeGetSlot(shape->slot());
5545 917973 : return true;
5546 : }
5547 : }
5548 :
5549 184662 : return false;
5550 : }
5551 :
5552 : /*
5553 : * Gets |obj[id]|. If that value's not callable, returns true and stores a
5554 : * non-primitive value in *vp. If it's callable, calls it with no arguments
5555 : * and |obj| as |this|, returning the result in *vp.
5556 : *
5557 : * This is a mini-abstraction for ES5 8.12.8 [[DefaultValue]], either steps 1-2
5558 : * or steps 3-4.
5559 : */
5560 : static bool
5561 1082142 : MaybeCallMethod(JSContext *cx, JSObject *obj, jsid id, Value *vp)
5562 : {
5563 1082142 : if (!js_GetMethod(cx, obj, id, 0, vp))
5564 135 : return false;
5565 1082007 : if (!js_IsCallable(*vp)) {
5566 20259 : *vp = ObjectValue(*obj);
5567 20259 : return true;
5568 : }
5569 1061748 : return Invoke(cx, ObjectValue(*obj), *vp, 0, NULL, vp);
5570 : }
5571 :
5572 : JSBool
5573 892049 : DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp)
5574 : {
5575 892049 : JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
5576 892049 : JS_ASSERT(!obj->isXML());
5577 :
5578 892049 : Class *clasp = obj->getClass();
5579 892049 : if (hint == JSTYPE_STRING) {
5580 : /* Optimize (new String(...)).toString(). */
5581 43246 : if (clasp == &StringClass &&
5582 : ClassMethodIsNative(cx, obj,
5583 : &StringClass,
5584 72 : ATOM_TO_JSID(cx->runtime->atomState.toStringAtom),
5585 72 : js_str_toString)) {
5586 72 : *vp = StringValue(obj->asString().unbox());
5587 72 : return true;
5588 : }
5589 :
5590 43102 : if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), vp))
5591 5433 : return false;
5592 37669 : if (vp->isPrimitive())
5593 37120 : return true;
5594 :
5595 549 : if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), vp))
5596 0 : return false;
5597 549 : if (vp->isPrimitive())
5598 540 : return true;
5599 : } else {
5600 : /* Optimize (new String(...)).valueOf(). */
5601 1030693 : if ((clasp == &StringClass &&
5602 : ClassMethodIsNative(cx, obj, &StringClass,
5603 180486 : ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom),
5604 180486 : js_str_toString)) ||
5605 : (clasp == &NumberClass &&
5606 : ClassMethodIsNative(cx, obj, &NumberClass,
5607 1332 : ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom),
5608 1332 : js_num_valueOf))) {
5609 181800 : *vp = obj->isString()
5610 180468 : ? StringValue(obj->asString().unbox())
5611 362268 : : NumberValue(obj->asNumber().unbox());
5612 181800 : return true;
5613 : }
5614 :
5615 667075 : if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), vp))
5616 36 : return false;
5617 667039 : if (vp->isPrimitive())
5618 295623 : return true;
5619 :
5620 371416 : if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), vp))
5621 27 : return false;
5622 371389 : if (vp->isPrimitive())
5623 371317 : return true;
5624 : }
5625 :
5626 : /* Avoid recursive death when decompiling in js_ReportValueError. */
5627 : JSString *str;
5628 81 : if (hint == JSTYPE_STRING) {
5629 9 : str = JS_InternString(cx, clasp->name);
5630 9 : if (!str)
5631 0 : return false;
5632 : } else {
5633 72 : str = NULL;
5634 : }
5635 :
5636 81 : js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, JSDVG_SEARCH_STACK, ObjectValue(*obj), str,
5637 81 : (hint == JSTYPE_VOID) ? "primitive type" : JS_TYPE_STR(hint));
5638 81 : return false;
5639 : }
5640 :
5641 : } /* namespace js */
5642 :
5643 : JS_FRIEND_API(JSBool)
5644 18 : JS_EnumerateState(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp)
5645 : {
5646 : /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
5647 18 : Class *clasp = obj->getClass();
5648 18 : JSEnumerateOp enumerate = clasp->enumerate;
5649 18 : if (clasp->flags & JSCLASS_NEW_ENUMERATE) {
5650 0 : JS_ASSERT(enumerate != JS_EnumerateStub);
5651 0 : return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
5652 : }
5653 :
5654 18 : if (!enumerate(cx, obj))
5655 0 : return false;
5656 :
5657 : /* Tell InitNativeIterator to treat us like a native object. */
5658 18 : JS_ASSERT(enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL);
5659 18 : statep->setMagic(JS_NATIVE_ENUMERATE);
5660 18 : return true;
5661 : }
5662 :
5663 : namespace js {
5664 :
5665 : JSBool
5666 302968 : CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
5667 : Value *vp, unsigned *attrsp)
5668 : {
5669 : JSBool writing;
5670 : JSObject *pobj;
5671 : JSProperty *prop;
5672 : const Shape *shape;
5673 :
5674 605936 : while (JS_UNLIKELY(obj->isWith()))
5675 0 : obj = obj->getProto();
5676 :
5677 302968 : writing = (mode & JSACC_WRITE) != 0;
5678 302968 : switch (mode & JSACC_TYPEMASK) {
5679 : case JSACC_PROTO:
5680 2700 : pobj = obj;
5681 2700 : if (!writing)
5682 1980 : vp->setObjectOrNull(obj->getProto());
5683 2700 : *attrsp = JSPROP_PERMANENT;
5684 2700 : break;
5685 :
5686 : case JSACC_PARENT:
5687 0 : JS_ASSERT(!writing);
5688 0 : pobj = obj;
5689 0 : vp->setObject(*obj->getParent());
5690 0 : *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
5691 0 : break;
5692 :
5693 : default:
5694 300268 : if (!obj->lookupGeneric(cx, id, &pobj, &prop))
5695 0 : return JS_FALSE;
5696 300268 : if (!prop) {
5697 299368 : if (!writing)
5698 299368 : vp->setUndefined();
5699 299368 : *attrsp = 0;
5700 299368 : pobj = obj;
5701 299368 : break;
5702 : }
5703 :
5704 900 : if (!pobj->isNative()) {
5705 0 : if (!writing) {
5706 0 : vp->setUndefined();
5707 0 : *attrsp = 0;
5708 : }
5709 0 : break;
5710 : }
5711 :
5712 900 : shape = (Shape *)prop;
5713 900 : *attrsp = shape->attributes();
5714 900 : if (!writing) {
5715 900 : if (shape->hasSlot())
5716 675 : *vp = pobj->nativeGetSlot(shape->slot());
5717 : else
5718 225 : vp->setUndefined();
5719 : }
5720 : }
5721 :
5722 302968 : JS_ASSERT_IF(*attrsp & JSPROP_READONLY, !(*attrsp & (JSPROP_GETTER | JSPROP_SETTER)));
5723 :
5724 : /*
5725 : * If obj's class has a stub (null) checkAccess hook, use the per-runtime
5726 : * checkObjectAccess callback, if configured.
5727 : *
5728 : * We don't want to require all classes to supply a checkAccess hook; we
5729 : * need that hook only for certain classes used when precompiling scripts
5730 : * and functions ("brutal sharing"). But for general safety of built-in
5731 : * magic properties like __proto__, we route all access checks, even for
5732 : * classes that stub out checkAccess, through the global checkObjectAccess
5733 : * hook. This covers precompilation-based sharing and (possibly
5734 : * unintended) runtime sharing across trust boundaries.
5735 : */
5736 302968 : JSCheckAccessOp check = pobj->getClass()->checkAccess;
5737 302968 : if (!check)
5738 302968 : check = cx->runtime->securityCallbacks->checkObjectAccess;
5739 302968 : return !check || check(cx, pobj, id, mode, vp);
5740 : }
5741 :
5742 : }
5743 :
5744 : JSType
5745 27822 : js_TypeOf(JSContext *cx, JSObject *obj)
5746 : {
5747 27822 : return obj->isCallable() ? JSTYPE_FUNCTION : JSTYPE_OBJECT;
5748 : }
5749 :
5750 : bool
5751 1050251 : js_IsDelegate(JSContext *cx, JSObject *obj, const Value &v)
5752 : {
5753 1050251 : if (v.isPrimitive())
5754 261591 : return false;
5755 788660 : JSObject *obj2 = &v.toObject();
5756 1577592 : while ((obj2 = obj2->getProto()) != NULL) {
5757 788867 : if (obj2 == obj)
5758 788595 : return true;
5759 : }
5760 65 : return false;
5761 : }
5762 :
5763 : bool
5764 316767 : js::FindClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey,
5765 : JSObject **protop, Class *clasp)
5766 : {
5767 : Value v;
5768 316767 : if (!js_FindClassObject(cx, scopeobj, protoKey, &v, clasp))
5769 0 : return false;
5770 :
5771 316767 : if (IsFunctionObject(v)) {
5772 47063 : JSObject *ctor = &v.toObject();
5773 47063 : if (!ctor->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &v))
5774 0 : return false;
5775 : }
5776 :
5777 316767 : *protop = v.isObject() ? &v.toObject() : NULL;
5778 316767 : return true;
5779 : }
5780 :
5781 : /*
5782 : * The first part of this function has been hand-expanded and optimized into
5783 : * NewBuiltinClassInstance in jsobjinlines.h.
5784 : */
5785 : JSBool
5786 5050041 : js_GetClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey,
5787 : JSObject **protop, Class *clasp)
5788 : {
5789 5050041 : JS_ASSERT(JSProto_Null <= protoKey);
5790 5050041 : JS_ASSERT(protoKey < JSProto_LIMIT);
5791 :
5792 5050041 : if (protoKey != JSProto_Null) {
5793 : GlobalObject *global;
5794 4783973 : if (scopeobj) {
5795 4710206 : global = &scopeobj->global();
5796 : } else {
5797 73767 : global = GetCurrentGlobal(cx);
5798 73767 : if (!global) {
5799 0 : *protop = NULL;
5800 0 : return true;
5801 : }
5802 : }
5803 4783973 : const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey);
5804 4783973 : if (v.isObject()) {
5805 4733274 : *protop = &v.toObject();
5806 4733274 : return true;
5807 : }
5808 : }
5809 :
5810 316767 : return FindClassPrototype(cx, scopeobj, protoKey, protop, clasp);
5811 : }
5812 :
5813 : JSObject *
5814 842698 : PrimitiveToObject(JSContext *cx, const Value &v)
5815 : {
5816 842698 : if (v.isString())
5817 680331 : return StringObject::create(cx, v.toString());
5818 162367 : if (v.isNumber())
5819 156276 : return NumberObject::create(cx, v.toNumber());
5820 :
5821 6091 : JS_ASSERT(v.isBoolean());
5822 6091 : return BooleanObject::create(cx, v.toBoolean());
5823 : }
5824 :
5825 : JSBool
5826 447 : js_PrimitiveToObject(JSContext *cx, Value *vp)
5827 : {
5828 447 : JSObject *obj = PrimitiveToObject(cx, *vp);
5829 447 : if (!obj)
5830 0 : return false;
5831 :
5832 447 : vp->setObject(*obj);
5833 447 : return true;
5834 : }
5835 :
5836 : JSBool
5837 843670 : js_ValueToObjectOrNull(JSContext *cx, const Value &v, JSObject **objp)
5838 : {
5839 : JSObject *obj;
5840 :
5841 843670 : if (v.isObjectOrNull()) {
5842 1135 : obj = v.toObjectOrNull();
5843 842535 : } else if (v.isUndefined()) {
5844 574 : obj = NULL;
5845 : } else {
5846 841961 : obj = PrimitiveToObject(cx, v);
5847 841961 : if (!obj)
5848 0 : return false;
5849 : }
5850 843670 : *objp = obj;
5851 843670 : return true;
5852 : }
5853 :
5854 : namespace js {
5855 :
5856 : /* Callers must handle the already-object case . */
5857 : JSObject *
5858 310 : ToObjectSlow(JSContext *cx, Value *vp)
5859 : {
5860 310 : JS_ASSERT(!vp->isMagic());
5861 310 : JS_ASSERT(!vp->isObject());
5862 :
5863 310 : if (vp->isNullOrUndefined()) {
5864 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO,
5865 20 : vp->isNull() ? "null" : "undefined", "object");
5866 20 : return NULL;
5867 : }
5868 :
5869 290 : JSObject *obj = PrimitiveToObject(cx, *vp);
5870 290 : if (obj)
5871 290 : vp->setObject(*obj);
5872 290 : return obj;
5873 : }
5874 :
5875 : }
5876 :
5877 : JSObject *
5878 842634 : js_ValueToNonNullObject(JSContext *cx, const Value &v)
5879 : {
5880 : JSObject *obj;
5881 :
5882 842634 : if (!js_ValueToObjectOrNull(cx, v, &obj))
5883 0 : return NULL;
5884 842634 : if (!obj)
5885 313 : js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, v, NULL);
5886 842634 : return obj;
5887 : }
5888 :
5889 : #ifdef DEBUG
5890 : void
5891 363240 : js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
5892 : {
5893 363240 : JS_ASSERT(trc->debugPrinter == js_PrintObjectSlotName);
5894 :
5895 363240 : JSObject *obj = (JSObject *)trc->debugPrintArg;
5896 363240 : uint32_t slot = uint32_t(trc->debugPrintIndex);
5897 :
5898 : const Shape *shape;
5899 363240 : if (obj->isNative()) {
5900 363240 : shape = obj->lastProperty();
5901 5526720 : while (shape && (!shape->hasSlot() || shape->slot() != slot))
5902 4800240 : shape = shape->previous();
5903 : } else {
5904 0 : shape = NULL;
5905 : }
5906 :
5907 363240 : if (!shape) {
5908 77220 : const char *slotname = NULL;
5909 77220 : if (obj->isGlobal()) {
5910 : #define JS_PROTO(name,code,init) \
5911 : if ((code) == slot) { slotname = js_##name##_str; goto found; }
5912 : #include "jsproto.tbl"
5913 : #undef JS_PROTO
5914 : }
5915 : found:
5916 77220 : if (slotname)
5917 2160 : JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname);
5918 : else
5919 75060 : JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot);
5920 : } else {
5921 286020 : jsid propid = shape->propid();
5922 286020 : if (JSID_IS_INT(propid)) {
5923 0 : JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(propid));
5924 286020 : } else if (JSID_IS_ATOM(propid)) {
5925 286020 : PutEscapedString(buf, bufsize, JSID_TO_ATOM(propid), 0);
5926 : } else {
5927 0 : JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**");
5928 : }
5929 : }
5930 363240 : }
5931 : #endif
5932 :
5933 : static const Shape *
5934 0 : LastConfigurableShape(JSObject *obj)
5935 : {
5936 0 : for (Shape::Range r(obj->lastProperty()->all()); !r.empty(); r.popFront()) {
5937 0 : const Shape *shape = &r.front();
5938 0 : if (shape->configurable())
5939 0 : return shape;
5940 : }
5941 0 : return NULL;
5942 : }
5943 :
5944 : bool
5945 0 : js_ClearNative(JSContext *cx, JSObject *obj)
5946 : {
5947 : /* Remove all configurable properties from obj. */
5948 0 : while (const Shape *shape = LastConfigurableShape(obj)) {
5949 0 : if (!obj->removeProperty(cx, shape->propid()))
5950 0 : return false;
5951 : }
5952 :
5953 : /* Set all remaining writable plain data properties to undefined. */
5954 0 : for (Shape::Range r(obj->lastProperty()->all()); !r.empty(); r.popFront()) {
5955 0 : const Shape *shape = &r.front();
5956 0 : if (shape->isDataDescriptor() &&
5957 0 : shape->writable() &&
5958 0 : shape->hasDefaultSetter() &&
5959 0 : shape->hasSlot()) {
5960 0 : obj->nativeSetSlot(shape->slot(), UndefinedValue());
5961 : }
5962 : }
5963 0 : return true;
5964 : }
5965 :
5966 : JSBool
5967 56 : js_ReportGetterOnlyAssignment(JSContext *cx)
5968 : {
5969 : return JS_ReportErrorFlagsAndNumber(cx,
5970 : JSREPORT_WARNING | JSREPORT_STRICT |
5971 : JSREPORT_STRICT_MODE_ERROR,
5972 : js_GetErrorMessage, NULL,
5973 56 : JSMSG_GETTER_ONLY);
5974 : }
5975 :
5976 : JS_FRIEND_API(JSBool)
5977 0 : js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
5978 : {
5979 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_GETTER_ONLY);
5980 0 : return JS_FALSE;
5981 : }
5982 :
5983 : #ifdef DEBUG
5984 :
5985 : /*
5986 : * Routines to print out values during debugging. These are FRIEND_API to help
5987 : * the debugger find them and to support temporarily hacking js_Dump* calls
5988 : * into other code.
5989 : */
5990 :
5991 : void
5992 0 : dumpValue(const Value &v)
5993 : {
5994 0 : if (v.isNull())
5995 0 : fprintf(stderr, "null");
5996 0 : else if (v.isUndefined())
5997 0 : fprintf(stderr, "undefined");
5998 0 : else if (v.isInt32())
5999 0 : fprintf(stderr, "%d", v.toInt32());
6000 0 : else if (v.isDouble())
6001 0 : fprintf(stderr, "%g", v.toDouble());
6002 0 : else if (v.isString())
6003 0 : v.toString()->dump();
6004 0 : else if (v.isObject() && v.toObject().isFunction()) {
6005 0 : JSFunction *fun = v.toObject().toFunction();
6006 0 : if (fun->atom) {
6007 0 : fputs("<function ", stderr);
6008 0 : FileEscapedString(stderr, fun->atom, 0);
6009 : } else {
6010 0 : fputs("<unnamed function", stderr);
6011 : }
6012 0 : if (fun->isInterpreted()) {
6013 0 : JSScript *script = fun->script();
6014 : fprintf(stderr, " (%s:%u)",
6015 0 : script->filename ? script->filename : "", script->lineno);
6016 : }
6017 0 : fprintf(stderr, " at %p>", (void *) fun);
6018 0 : } else if (v.isObject()) {
6019 0 : JSObject *obj = &v.toObject();
6020 0 : Class *clasp = obj->getClass();
6021 : fprintf(stderr, "<%s%s at %p>",
6022 : clasp->name,
6023 : (clasp == &ObjectClass) ? "" : " object",
6024 0 : (void *) obj);
6025 0 : } else if (v.isBoolean()) {
6026 0 : if (v.toBoolean())
6027 0 : fprintf(stderr, "true");
6028 : else
6029 0 : fprintf(stderr, "false");
6030 0 : } else if (v.isMagic()) {
6031 0 : fprintf(stderr, "<invalid");
6032 : #ifdef DEBUG
6033 0 : switch (v.whyMagic()) {
6034 0 : case JS_ARRAY_HOLE: fprintf(stderr, " array hole"); break;
6035 0 : case JS_NATIVE_ENUMERATE: fprintf(stderr, " native enumeration"); break;
6036 0 : case JS_NO_ITER_VALUE: fprintf(stderr, " no iter value"); break;
6037 0 : case JS_GENERATOR_CLOSING: fprintf(stderr, " generator closing"); break;
6038 0 : default: fprintf(stderr, " ?!"); break;
6039 : }
6040 : #endif
6041 0 : fprintf(stderr, ">");
6042 : } else {
6043 0 : fprintf(stderr, "unexpected value");
6044 : }
6045 0 : }
6046 :
6047 : JS_FRIEND_API(void)
6048 0 : js_DumpValue(const Value &val)
6049 : {
6050 0 : dumpValue(val);
6051 0 : fputc('\n', stderr);
6052 0 : }
6053 :
6054 : JS_FRIEND_API(void)
6055 0 : js_DumpId(jsid id)
6056 : {
6057 0 : fprintf(stderr, "jsid %p = ", (void *) JSID_BITS(id));
6058 0 : dumpValue(IdToValue(id));
6059 0 : fputc('\n', stderr);
6060 0 : }
6061 :
6062 : static void
6063 0 : DumpProperty(JSObject *obj, const Shape &shape)
6064 : {
6065 0 : jsid id = shape.propid();
6066 0 : uint8_t attrs = shape.attributes();
6067 :
6068 0 : fprintf(stderr, " ((Shape *) %p) ", (void *) &shape);
6069 0 : if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate ");
6070 0 : if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly ");
6071 0 : if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent ");
6072 0 : if (attrs & JSPROP_SHARED) fprintf(stderr, "shared ");
6073 :
6074 0 : if (shape.hasGetterValue())
6075 0 : fprintf(stderr, "getterValue=%p ", (void *) shape.getterObject());
6076 0 : else if (!shape.hasDefaultGetter())
6077 0 : fprintf(stderr, "getterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.getterOp()));
6078 :
6079 0 : if (shape.hasSetterValue())
6080 0 : fprintf(stderr, "setterValue=%p ", (void *) shape.setterObject());
6081 0 : else if (!shape.hasDefaultSetter())
6082 0 : fprintf(stderr, "setterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.setterOp()));
6083 :
6084 0 : if (JSID_IS_ATOM(id))
6085 0 : JSID_TO_STRING(id)->dump();
6086 0 : else if (JSID_IS_INT(id))
6087 0 : fprintf(stderr, "%d", (int) JSID_TO_INT(id));
6088 : else
6089 0 : fprintf(stderr, "unknown jsid %p", (void *) JSID_BITS(id));
6090 :
6091 0 : uint32_t slot = shape.hasSlot() ? shape.maybeSlot() : SHAPE_INVALID_SLOT;
6092 0 : fprintf(stderr, ": slot %d", slot);
6093 0 : if (shape.hasSlot()) {
6094 0 : fprintf(stderr, " = ");
6095 0 : dumpValue(obj->getSlot(slot));
6096 0 : } else if (slot != SHAPE_INVALID_SLOT) {
6097 0 : fprintf(stderr, " (INVALID!)");
6098 : }
6099 0 : fprintf(stderr, "\n");
6100 0 : }
6101 :
6102 : void
6103 0 : JSObject::dump()
6104 : {
6105 0 : JSObject *obj = this;
6106 0 : fprintf(stderr, "object %p\n", (void *) obj);
6107 0 : Class *clasp = obj->getClass();
6108 0 : fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name);
6109 :
6110 0 : fprintf(stderr, "flags:");
6111 0 : if (obj->isDelegate()) fprintf(stderr, " delegate");
6112 0 : if (obj->isSystem()) fprintf(stderr, " system");
6113 0 : if (!obj->isExtensible()) fprintf(stderr, " not_extensible");
6114 0 : if (obj->isIndexed()) fprintf(stderr, " indexed");
6115 :
6116 0 : if (obj->isNative()) {
6117 0 : if (obj->inDictionaryMode())
6118 0 : fprintf(stderr, " inDictionaryMode");
6119 0 : if (obj->hasPropertyTable())
6120 0 : fprintf(stderr, " hasPropertyTable");
6121 : }
6122 0 : fprintf(stderr, "\n");
6123 :
6124 0 : if (obj->isDenseArray()) {
6125 0 : unsigned slots = obj->getDenseArrayInitializedLength();
6126 0 : fprintf(stderr, "elements\n");
6127 0 : for (unsigned i = 0; i < slots; i++) {
6128 0 : fprintf(stderr, " %3d: ", i);
6129 0 : dumpValue(obj->getDenseArrayElement(i));
6130 0 : fprintf(stderr, "\n");
6131 0 : fflush(stderr);
6132 : }
6133 0 : return;
6134 : }
6135 :
6136 0 : fprintf(stderr, "proto ");
6137 0 : dumpValue(ObjectOrNullValue(obj->getProto()));
6138 0 : fputc('\n', stderr);
6139 :
6140 0 : fprintf(stderr, "parent ");
6141 0 : dumpValue(ObjectOrNullValue(obj->getParent()));
6142 0 : fputc('\n', stderr);
6143 :
6144 0 : if (clasp->flags & JSCLASS_HAS_PRIVATE)
6145 0 : fprintf(stderr, "private %p\n", obj->getPrivate());
6146 :
6147 0 : if (!obj->isNative())
6148 0 : fprintf(stderr, "not native\n");
6149 :
6150 0 : unsigned reservedEnd = JSCLASS_RESERVED_SLOTS(clasp);
6151 0 : unsigned slots = obj->slotSpan();
6152 0 : unsigned stop = obj->isNative() ? reservedEnd : slots;
6153 0 : if (stop > 0)
6154 0 : fprintf(stderr, obj->isNative() ? "reserved slots:\n" : "slots:\n");
6155 0 : for (unsigned i = 0; i < stop; i++) {
6156 0 : fprintf(stderr, " %3d ", i);
6157 0 : if (i < reservedEnd)
6158 0 : fprintf(stderr, "(reserved) ");
6159 0 : fprintf(stderr, "= ");
6160 0 : dumpValue(obj->getSlot(i));
6161 0 : fputc('\n', stderr);
6162 : }
6163 :
6164 0 : if (obj->isNative()) {
6165 0 : fprintf(stderr, "properties:\n");
6166 0 : Vector<const Shape *, 8, SystemAllocPolicy> props;
6167 0 : for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront())
6168 0 : props.append(&r.front());
6169 0 : for (size_t i = props.length(); i-- != 0;)
6170 0 : DumpProperty(obj, *props[i]);
6171 : }
6172 0 : fputc('\n', stderr);
6173 : }
6174 :
6175 : static void
6176 0 : MaybeDumpObject(const char *name, JSObject *obj)
6177 : {
6178 0 : if (obj) {
6179 0 : fprintf(stderr, " %s: ", name);
6180 0 : dumpValue(ObjectValue(*obj));
6181 0 : fputc('\n', stderr);
6182 : }
6183 0 : }
6184 :
6185 : static void
6186 0 : MaybeDumpValue(const char *name, const Value &v)
6187 : {
6188 0 : if (!v.isNull()) {
6189 0 : fprintf(stderr, " %s: ", name);
6190 0 : dumpValue(v);
6191 0 : fputc('\n', stderr);
6192 : }
6193 0 : }
6194 :
6195 : JS_FRIEND_API(void)
6196 0 : js_DumpStackFrame(JSContext *cx, StackFrame *start)
6197 : {
6198 : /* This should only called during live debugging. */
6199 0 : FrameRegsIter i(cx, StackIter::GO_THROUGH_SAVED);
6200 0 : if (!start) {
6201 0 : if (i.done()) {
6202 0 : fprintf(stderr, "no stack for cx = %p\n", (void*) cx);
6203 0 : return;
6204 : }
6205 : } else {
6206 0 : while (!i.done() && i.fp() != start)
6207 0 : ++i;
6208 :
6209 0 : if (i.done()) {
6210 : fprintf(stderr, "fp = %p not found in cx = %p\n",
6211 0 : (void *)start, (void *)cx);
6212 0 : return;
6213 : }
6214 : }
6215 :
6216 0 : for (; !i.done(); ++i) {
6217 0 : StackFrame *const fp = i.fp();
6218 :
6219 0 : fprintf(stderr, "StackFrame at %p\n", (void *) fp);
6220 0 : if (fp->isFunctionFrame()) {
6221 0 : fprintf(stderr, "callee fun: ");
6222 0 : dumpValue(ObjectValue(fp->callee()));
6223 : } else {
6224 0 : fprintf(stderr, "global frame, no callee");
6225 : }
6226 0 : fputc('\n', stderr);
6227 :
6228 0 : if (fp->isScriptFrame()) {
6229 : fprintf(stderr, "file %s line %u\n",
6230 0 : fp->script()->filename, (unsigned) fp->script()->lineno);
6231 : }
6232 :
6233 0 : if (jsbytecode *pc = i.pc()) {
6234 0 : if (!fp->isScriptFrame()) {
6235 0 : fprintf(stderr, "*** pc && !script, skipping frame\n\n");
6236 0 : continue;
6237 : }
6238 0 : fprintf(stderr, " pc = %p\n", pc);
6239 0 : fprintf(stderr, " current op: %s\n", js_CodeName[*pc]);
6240 : }
6241 0 : Value *sp = i.sp();
6242 0 : fprintf(stderr, " slots: %p\n", (void *) fp->slots());
6243 0 : fprintf(stderr, " sp: %p = slots + %u\n", (void *) sp, (unsigned) (sp - fp->slots()));
6244 0 : if (sp - fp->slots() < 10000) { // sanity
6245 0 : for (Value *p = fp->slots(); p < sp; p++) {
6246 0 : fprintf(stderr, " %p: ", (void *) p);
6247 0 : dumpValue(*p);
6248 0 : fputc('\n', stderr);
6249 : }
6250 : }
6251 0 : if (fp->hasArgs()) {
6252 0 : fprintf(stderr, " actuals: %p (%u) ", (void *) fp->actualArgs(), (unsigned) fp->numActualArgs());
6253 0 : fprintf(stderr, " formals: %p (%u)\n", (void *) fp->formalArgs(), (unsigned) fp->numFormalArgs());
6254 : }
6255 0 : if (fp->hasCallObj()) {
6256 0 : fprintf(stderr, " has call obj: ");
6257 0 : dumpValue(ObjectValue(fp->callObj()));
6258 0 : fprintf(stderr, "\n");
6259 : }
6260 0 : MaybeDumpObject("argsobj", fp->maybeArgsObj());
6261 0 : MaybeDumpObject("blockChain", fp->maybeBlockChain());
6262 0 : if (!fp->isDummyFrame()) {
6263 0 : MaybeDumpValue("this", fp->thisValue());
6264 0 : fprintf(stderr, " rval: ");
6265 0 : dumpValue(fp->returnValue());
6266 : } else {
6267 0 : fprintf(stderr, "dummy frame");
6268 : }
6269 0 : fputc('\n', stderr);
6270 :
6271 0 : fprintf(stderr, " flags:");
6272 0 : if (fp->isConstructing())
6273 0 : fprintf(stderr, " constructing");
6274 0 : if (fp->isDebuggerFrame())
6275 0 : fprintf(stderr, " debugger");
6276 0 : if (fp->isEvalFrame())
6277 0 : fprintf(stderr, " eval");
6278 0 : if (fp->isYielding())
6279 0 : fprintf(stderr, " yielding");
6280 0 : if (fp->isGeneratorFrame())
6281 0 : fprintf(stderr, " generator");
6282 0 : fputc('\n', stderr);
6283 :
6284 0 : fprintf(stderr, " scopeChain: (JSObject *) %p\n", (void *) &fp->scopeChain());
6285 :
6286 0 : fputc('\n', stderr);
6287 : }
6288 : }
6289 :
6290 : #endif /* DEBUG */
6291 :
|