Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * JS object implementation.
9 */
11 #include "jsobjinlines.h"
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/MathAlgorithms.h"
15 #include "mozilla/MemoryReporting.h"
16 #include "mozilla/TemplateLib.h"
18 #include <string.h>
20 #include "jsapi.h"
21 #include "jsarray.h"
22 #include "jsatom.h"
23 #include "jscntxt.h"
24 #include "jsfriendapi.h"
25 #include "jsfun.h"
26 #include "jsgc.h"
27 #include "jsiter.h"
28 #include "jsnum.h"
29 #include "jsopcode.h"
30 #include "jsprf.h"
31 #include "jsproxy.h"
32 #include "jsscript.h"
33 #include "jsstr.h"
34 #include "jstypes.h"
35 #include "jsutil.h"
36 #include "jswatchpoint.h"
37 #include "jswrapper.h"
39 #include "builtin/Object.h"
40 #include "frontend/BytecodeCompiler.h"
41 #include "gc/Marking.h"
42 #include "jit/AsmJSModule.h"
43 #include "jit/BaselineJIT.h"
44 #include "js/MemoryMetrics.h"
45 #include "js/OldDebugAPI.h"
46 #include "vm/ArgumentsObject.h"
47 #include "vm/Interpreter.h"
48 #include "vm/ProxyObject.h"
49 #include "vm/RegExpStaticsObject.h"
50 #include "vm/Shape.h"
52 #include "jsatominlines.h"
53 #include "jsboolinlines.h"
54 #include "jscntxtinlines.h"
55 #include "jscompartmentinlines.h"
57 #include "vm/ArrayObject-inl.h"
58 #include "vm/BooleanObject-inl.h"
59 #include "vm/NumberObject-inl.h"
60 #include "vm/ObjectImpl-inl.h"
61 #include "vm/Runtime-inl.h"
62 #include "vm/Shape-inl.h"
63 #include "vm/StringObject-inl.h"
65 using namespace js;
66 using namespace js::gc;
67 using namespace js::types;
69 using mozilla::Maybe;
70 using mozilla::RoundUpPow2;
72 JS_STATIC_ASSERT(int32_t((JSObject::NELEMENTS_LIMIT - 1) * sizeof(Value)) == int64_t((JSObject::NELEMENTS_LIMIT - 1) * sizeof(Value)));
74 const Class JSObject::class_ = {
75 js_Object_str,
76 JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
77 JS_PropertyStub, /* addProperty */
78 JS_DeletePropertyStub, /* delProperty */
79 JS_PropertyStub, /* getProperty */
80 JS_StrictPropertyStub, /* setProperty */
81 JS_EnumerateStub,
82 JS_ResolveStub,
83 JS_ConvertStub
84 };
86 const Class* const js::ObjectClassPtr = &JSObject::class_;
88 JS_FRIEND_API(JSObject *)
89 JS_ObjectToInnerObject(JSContext *cx, HandleObject obj)
90 {
91 if (!obj) {
92 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INACTIVE);
93 return nullptr;
94 }
95 return GetInnerObject(cx, obj);
96 }
98 JS_FRIEND_API(JSObject *)
99 JS_ObjectToOuterObject(JSContext *cx, HandleObject obj)
100 {
101 assertSameCompartment(cx, obj);
102 return GetOuterObject(cx, obj);
103 }
105 JSObject *
106 js::NonNullObject(JSContext *cx, const Value &v)
107 {
108 if (v.isPrimitive()) {
109 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
110 return nullptr;
111 }
112 return &v.toObject();
113 }
115 const char *
116 js::InformalValueTypeName(const Value &v)
117 {
118 if (v.isObject())
119 return v.toObject().getClass()->name;
120 if (v.isString())
121 return "string";
122 if (v.isNumber())
123 return "number";
124 if (v.isBoolean())
125 return "boolean";
126 if (v.isNull())
127 return "null";
128 if (v.isUndefined())
129 return "undefined";
130 return "value";
131 }
133 bool
134 js::NewPropertyDescriptorObject(JSContext *cx, Handle<PropertyDescriptor> desc,
135 MutableHandleValue vp)
136 {
137 if (!desc.object()) {
138 vp.setUndefined();
139 return true;
140 }
142 /* We have our own property, so start creating the descriptor. */
143 AutoPropDescRooter d(cx);
145 d.initFromPropertyDescriptor(desc);
146 if (!d.makeObject(cx))
147 return false;
148 vp.set(d.pd());
149 return true;
150 }
152 void
153 PropDesc::initFromPropertyDescriptor(Handle<PropertyDescriptor> desc)
154 {
155 isUndefined_ = false;
156 pd_.setUndefined();
157 attrs = uint8_t(desc.attributes());
158 JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
159 if (desc.hasGetterOrSetterObject()) {
160 hasGet_ = true;
161 get_ = desc.hasGetterObject() && desc.getterObject()
162 ? ObjectValue(*desc.getterObject())
163 : UndefinedValue();
164 hasSet_ = true;
165 set_ = desc.hasSetterObject() && desc.setterObject()
166 ? ObjectValue(*desc.setterObject())
167 : UndefinedValue();
168 hasValue_ = false;
169 value_.setUndefined();
170 hasWritable_ = false;
171 } else {
172 hasGet_ = false;
173 get_.setUndefined();
174 hasSet_ = false;
175 set_.setUndefined();
176 hasValue_ = true;
177 value_ = desc.value();
178 hasWritable_ = true;
179 }
180 hasEnumerable_ = true;
181 hasConfigurable_ = true;
182 }
184 bool
185 PropDesc::makeObject(JSContext *cx)
186 {
187 MOZ_ASSERT(!isUndefined());
189 RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
190 if (!obj)
191 return false;
193 const JSAtomState &names = cx->names();
194 RootedValue configurableVal(cx, BooleanValue((attrs & JSPROP_PERMANENT) == 0));
195 RootedValue enumerableVal(cx, BooleanValue((attrs & JSPROP_ENUMERATE) != 0));
196 RootedValue writableVal(cx, BooleanValue((attrs & JSPROP_READONLY) == 0));
197 if ((hasConfigurable() &&
198 !JSObject::defineProperty(cx, obj, names.configurable, configurableVal)) ||
199 (hasEnumerable() &&
200 !JSObject::defineProperty(cx, obj, names.enumerable, enumerableVal)) ||
201 (hasGet() &&
202 !JSObject::defineProperty(cx, obj, names.get, getterValue())) ||
203 (hasSet() &&
204 !JSObject::defineProperty(cx, obj, names.set, setterValue())) ||
205 (hasValue() &&
206 !JSObject::defineProperty(cx, obj, names.value, value())) ||
207 (hasWritable() &&
208 !JSObject::defineProperty(cx, obj, names.writable, writableVal)))
209 {
210 return false;
211 }
213 pd_.setObject(*obj);
214 return true;
215 }
217 bool
218 js::GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
219 MutableHandle<PropertyDescriptor> desc)
220 {
221 // FIXME: Call TrapGetOwnProperty directly once ScriptedIndirectProxies is removed
222 if (obj->is<ProxyObject>())
223 return Proxy::getOwnPropertyDescriptor(cx, obj, id, desc);
225 RootedObject pobj(cx);
226 RootedShape shape(cx);
227 if (!HasOwnProperty<CanGC>(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &shape))
228 return false;
229 if (!shape) {
230 desc.object().set(nullptr);
231 return true;
232 }
234 bool doGet = true;
235 if (pobj->isNative()) {
236 desc.setAttributes(GetShapeAttributes(pobj, shape));
237 if (desc.hasGetterOrSetterObject()) {
238 MOZ_ASSERT(desc.isShared());
239 doGet = false;
240 if (desc.hasGetterObject())
241 desc.setGetterObject(shape->getterObject());
242 if (desc.hasSetterObject())
243 desc.setSetterObject(shape->setterObject());
244 } else {
245 // This is either a straight-up data property or (rarely) a
246 // property with a JSPropertyOp getter/setter. The latter must be
247 // reported to the caller as a plain data property, so don't
248 // populate desc.getter/setter, and mask away the SHARED bit.
249 desc.attributesRef() &= ~JSPROP_SHARED;
250 }
251 } else {
252 if (!JSObject::getGenericAttributes(cx, pobj, id, &desc.attributesRef()))
253 return false;
254 }
256 RootedValue value(cx);
257 if (doGet && !JSObject::getGeneric(cx, obj, obj, id, &value))
258 return false;
260 desc.value().set(value);
261 desc.object().set(obj);
262 return true;
263 }
265 bool
266 js::GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
267 {
268 Rooted<PropertyDescriptor> desc(cx);
269 return GetOwnPropertyDescriptor(cx, obj, id, &desc) &&
270 NewPropertyDescriptorObject(cx, desc, vp);
271 }
273 bool
274 js::GetFirstArgumentAsObject(JSContext *cx, const CallArgs &args, const char *method,
275 MutableHandleObject objp)
276 {
277 if (args.length() == 0) {
278 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
279 method, "0", "s");
280 return false;
281 }
283 HandleValue v = args[0];
284 if (!v.isObject()) {
285 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NullPtr());
286 if (!bytes)
287 return false;
288 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
289 bytes, "not an object");
290 js_free(bytes);
291 return false;
292 }
294 objp.set(&v.toObject());
295 return true;
296 }
298 static bool
299 HasProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp, bool *foundp)
300 {
301 if (!JSObject::hasProperty(cx, obj, id, foundp))
302 return false;
303 if (!*foundp) {
304 vp.setUndefined();
305 return true;
306 }
308 /*
309 * We must go through the method read barrier in case id is 'get' or 'set'.
310 * There is no obvious way to defer cloning a joined function object whose
311 * identity will be used by DefinePropertyOnObject, e.g., or reflected via
312 * js::GetOwnPropertyDescriptor, as the getter or setter callable object.
313 */
314 return !!JSObject::getGeneric(cx, obj, obj, id, vp);
315 }
317 bool
318 PropDesc::initialize(JSContext *cx, const Value &origval, bool checkAccessors)
319 {
320 RootedValue v(cx, origval);
322 /* 8.10.5 step 1 */
323 if (v.isPrimitive()) {
324 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
325 return false;
326 }
327 RootedObject desc(cx, &v.toObject());
329 /* Make a copy of the descriptor. We might need it later. */
330 pd_ = v;
332 isUndefined_ = false;
334 /*
335 * Start with the proper defaults. XXX shouldn't be necessary when we get
336 * rid of PropDesc::attributes()
337 */
338 attrs = JSPROP_PERMANENT | JSPROP_READONLY;
340 bool found = false;
341 RootedId id(cx);
343 /* 8.10.5 step 3 */
344 id = NameToId(cx->names().enumerable);
345 if (!HasProperty(cx, desc, id, &v, &found))
346 return false;
347 if (found) {
348 hasEnumerable_ = true;
349 if (ToBoolean(v))
350 attrs |= JSPROP_ENUMERATE;
351 }
353 /* 8.10.5 step 4 */
354 id = NameToId(cx->names().configurable);
355 if (!HasProperty(cx, desc, id, &v, &found))
356 return false;
357 if (found) {
358 hasConfigurable_ = true;
359 if (ToBoolean(v))
360 attrs &= ~JSPROP_PERMANENT;
361 }
363 /* 8.10.5 step 5 */
364 id = NameToId(cx->names().value);
365 if (!HasProperty(cx, desc, id, &v, &found))
366 return false;
367 if (found) {
368 hasValue_ = true;
369 value_ = v;
370 }
372 /* 8.10.6 step 6 */
373 id = NameToId(cx->names().writable);
374 if (!HasProperty(cx, desc, id, &v, &found))
375 return false;
376 if (found) {
377 hasWritable_ = true;
378 if (ToBoolean(v))
379 attrs &= ~JSPROP_READONLY;
380 }
382 /* 8.10.7 step 7 */
383 id = NameToId(cx->names().get);
384 if (!HasProperty(cx, desc, id, &v, &found))
385 return false;
386 if (found) {
387 hasGet_ = true;
388 get_ = v;
389 attrs |= JSPROP_GETTER | JSPROP_SHARED;
390 attrs &= ~JSPROP_READONLY;
391 if (checkAccessors && !checkGetter(cx))
392 return false;
393 }
395 /* 8.10.7 step 8 */
396 id = NameToId(cx->names().set);
397 if (!HasProperty(cx, desc, id, &v, &found))
398 return false;
399 if (found) {
400 hasSet_ = true;
401 set_ = v;
402 attrs |= JSPROP_SETTER | JSPROP_SHARED;
403 attrs &= ~JSPROP_READONLY;
404 if (checkAccessors && !checkSetter(cx))
405 return false;
406 }
408 /* 8.10.7 step 9 */
409 if ((hasGet() || hasSet()) && (hasValue() || hasWritable())) {
410 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INVALID_DESCRIPTOR);
411 return false;
412 }
414 JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
416 return true;
417 }
419 void
420 PropDesc::complete()
421 {
422 if (isGenericDescriptor() || isDataDescriptor()) {
423 if (!hasValue_) {
424 hasValue_ = true;
425 value_.setUndefined();
426 }
427 if (!hasWritable_) {
428 hasWritable_ = true;
429 attrs |= JSPROP_READONLY;
430 }
431 } else {
432 if (!hasGet_) {
433 hasGet_ = true;
434 get_.setUndefined();
435 }
436 if (!hasSet_) {
437 hasSet_ = true;
438 set_.setUndefined();
439 }
440 }
441 if (!hasEnumerable_) {
442 hasEnumerable_ = true;
443 attrs &= ~JSPROP_ENUMERATE;
444 }
445 if (!hasConfigurable_) {
446 hasConfigurable_ = true;
447 attrs |= JSPROP_PERMANENT;
448 }
449 }
451 bool
452 js::Throw(JSContext *cx, jsid id, unsigned errorNumber)
453 {
454 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount == 1);
456 JSString *idstr = IdToString(cx, id);
457 if (!idstr)
458 return false;
459 JSAutoByteString bytes(cx, idstr);
460 if (!bytes)
461 return false;
462 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, errorNumber, bytes.ptr());
463 return false;
464 }
466 bool
467 js::Throw(JSContext *cx, JSObject *obj, unsigned errorNumber)
468 {
469 if (js_ErrorFormatString[errorNumber].argCount == 1) {
470 RootedValue val(cx, ObjectValue(*obj));
471 js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,
472 JSDVG_IGNORE_STACK, val, NullPtr(),
473 nullptr, nullptr);
474 } else {
475 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount == 0);
476 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, errorNumber);
477 }
478 return false;
479 }
481 static bool
482 Reject(JSContext *cx, unsigned errorNumber, bool throwError, jsid id, bool *rval)
483 {
484 if (throwError)
485 return Throw(cx, id, errorNumber);
487 *rval = false;
488 return true;
489 }
491 static bool
492 Reject(JSContext *cx, JSObject *obj, unsigned errorNumber, bool throwError, bool *rval)
493 {
494 if (throwError)
495 return Throw(cx, obj, errorNumber);
497 *rval = false;
498 return true;
499 }
501 static bool
502 Reject(JSContext *cx, HandleId id, unsigned errorNumber, bool throwError, bool *rval)
503 {
504 if (throwError)
505 return Throw(cx, id, errorNumber);
507 *rval = false;
508 return true;
509 }
511 // See comments on CheckDefineProperty in jsobj.h.
512 //
513 // DefinePropertyOnObject has its own implementation of these checks.
514 //
515 JS_FRIEND_API(bool)
516 js::CheckDefineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
517 PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
518 {
519 if (!obj->isNative())
520 return true;
522 // ES5 8.12.9 Step 1. Even though we know obj is native, we use generic
523 // APIs for shorter, more readable code.
524 Rooted<PropertyDescriptor> desc(cx);
525 if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
526 return false;
528 // This does not have to check obj's extensibility when !desc.obj (steps
529 // 2-3) because the low-level methods JSObject::{add,put}Property check
530 // for that.
531 if (desc.object() && desc.isPermanent()) {
532 // Steps 6-11, skipping step 10.a.ii. Prohibit redefining a permanent
533 // property with different metadata, except to make a writable property
534 // non-writable.
535 if (getter != desc.getter() ||
536 setter != desc.setter() ||
537 (attrs != desc.attributes() && attrs != (desc.attributes() | JSPROP_READONLY)))
538 {
539 return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
540 }
542 // Step 10.a.ii. Prohibit changing the value of a non-configurable,
543 // non-writable data property.
544 if ((desc.attributes() & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_READONLY)) == JSPROP_READONLY) {
545 bool same;
546 if (!SameValue(cx, value, desc.value(), &same))
547 return false;
548 if (!same)
549 return JSObject::reportReadOnly(cx, id);
550 }
551 }
552 return true;
553 }
555 static bool
556 DefinePropertyOnObject(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc,
557 bool throwError, bool *rval)
558 {
559 /* 8.12.9 step 1. */
560 RootedShape shape(cx);
561 RootedObject obj2(cx);
562 JS_ASSERT(!obj->getOps()->lookupGeneric);
563 if (!HasOwnProperty<CanGC>(cx, nullptr, obj, id, &obj2, &shape))
564 return false;
566 JS_ASSERT(!obj->getOps()->defineProperty);
568 /* 8.12.9 steps 2-4. */
569 if (!shape) {
570 bool extensible;
571 if (!JSObject::isExtensible(cx, obj, &extensible))
572 return false;
573 if (!extensible)
574 return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
576 *rval = true;
578 if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
579 JS_ASSERT(!obj->getOps()->defineProperty);
580 RootedValue v(cx, desc.hasValue() ? desc.value() : UndefinedValue());
581 return baseops::DefineGeneric(cx, obj, id, v,
582 JS_PropertyStub, JS_StrictPropertyStub,
583 desc.attributes());
584 }
586 JS_ASSERT(desc.isAccessorDescriptor());
588 return baseops::DefineGeneric(cx, obj, id, UndefinedHandleValue,
589 desc.getter(), desc.setter(), desc.attributes());
590 }
592 /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
593 RootedValue v(cx);
595 JS_ASSERT(obj == obj2);
597 bool shapeDataDescriptor = true,
598 shapeAccessorDescriptor = false,
599 shapeWritable = true,
600 shapeConfigurable = true,
601 shapeEnumerable = true,
602 shapeHasDefaultGetter = true,
603 shapeHasDefaultSetter = true,
604 shapeHasGetterValue = false,
605 shapeHasSetterValue = false;
606 uint8_t shapeAttributes = GetShapeAttributes(obj, shape);
607 if (!IsImplicitDenseOrTypedArrayElement(shape)) {
608 shapeDataDescriptor = shape->isDataDescriptor();
609 shapeAccessorDescriptor = shape->isAccessorDescriptor();
610 shapeWritable = shape->writable();
611 shapeConfigurable = shape->configurable();
612 shapeEnumerable = shape->enumerable();
613 shapeHasDefaultGetter = shape->hasDefaultGetter();
614 shapeHasDefaultSetter = shape->hasDefaultSetter();
615 shapeHasGetterValue = shape->hasGetterValue();
616 shapeHasSetterValue = shape->hasSetterValue();
617 shapeAttributes = shape->attributes();
618 }
620 do {
621 if (desc.isAccessorDescriptor()) {
622 if (!shapeAccessorDescriptor)
623 break;
625 if (desc.hasGet()) {
626 bool same;
627 if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same))
628 return false;
629 if (!same)
630 break;
631 }
633 if (desc.hasSet()) {
634 bool same;
635 if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
636 return false;
637 if (!same)
638 break;
639 }
640 } else {
641 /*
642 * Determine the current value of the property once, if the current
643 * value might actually need to be used or preserved later. NB: we
644 * guard on whether the current property is a data descriptor to
645 * avoid calling a getter; we won't need the value if it's not a
646 * data descriptor.
647 */
648 if (IsImplicitDenseOrTypedArrayElement(shape)) {
649 v = obj->getDenseOrTypedArrayElement(JSID_TO_INT(id));
650 } else if (shape->isDataDescriptor()) {
651 /*
652 * We must rule out a non-configurable js::PropertyOp-guarded
653 * property becoming a writable unguarded data property, since
654 * such a property can have its value changed to one the getter
655 * and setter preclude.
656 *
657 * A desc lacking writable but with value is a data descriptor
658 * and we must reject it as if it had writable: true if current
659 * is writable.
660 */
661 if (!shape->configurable() &&
662 (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()) &&
663 desc.isDataDescriptor() &&
664 (desc.hasWritable() ? desc.writable() : shape->writable()))
665 {
666 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
667 }
669 if (!NativeGet(cx, obj, obj2, shape, &v))
670 return false;
671 }
673 if (desc.isDataDescriptor()) {
674 if (!shapeDataDescriptor)
675 break;
677 bool same;
678 if (desc.hasValue()) {
679 if (!SameValue(cx, desc.value(), v, &same))
680 return false;
681 if (!same) {
682 /*
683 * Insist that a non-configurable js::PropertyOp data
684 * property is frozen at exactly the last-got value.
685 *
686 * Duplicate the first part of the big conjunction that
687 * we tested above, rather than add a local bool flag.
688 * Likewise, don't try to keep shape->writable() in a
689 * flag we veto from true to false for non-configurable
690 * PropertyOp-based data properties and test before the
691 * SameValue check later on in order to re-use that "if
692 * (!SameValue) Reject" logic.
693 *
694 * This function is large and complex enough that it
695 * seems best to repeat a small bit of code and return
696 * Reject(...) ASAP, instead of being clever.
697 */
698 if (!shapeConfigurable &&
699 (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()))
700 {
701 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
702 }
703 break;
704 }
705 }
706 if (desc.hasWritable() && desc.writable() != shapeWritable)
707 break;
708 } else {
709 /* The only fields in desc will be handled below. */
710 JS_ASSERT(desc.isGenericDescriptor());
711 }
712 }
714 if (desc.hasConfigurable() && desc.configurable() != shapeConfigurable)
715 break;
716 if (desc.hasEnumerable() && desc.enumerable() != shapeEnumerable)
717 break;
719 /* The conditions imposed by step 5 or step 6 apply. */
720 *rval = true;
721 return true;
722 } while (0);
724 /* 8.12.9 step 7. */
725 if (!shapeConfigurable) {
726 if ((desc.hasConfigurable() && desc.configurable()) ||
727 (desc.hasEnumerable() && desc.enumerable() != shape->enumerable())) {
728 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
729 }
730 }
732 bool callDelProperty = false;
734 if (desc.isGenericDescriptor()) {
735 /* 8.12.9 step 8, no validation required */
736 } else if (desc.isDataDescriptor() != shapeDataDescriptor) {
737 /* 8.12.9 step 9. */
738 if (!shapeConfigurable)
739 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
740 } else if (desc.isDataDescriptor()) {
741 /* 8.12.9 step 10. */
742 JS_ASSERT(shapeDataDescriptor);
743 if (!shapeConfigurable && !shape->writable()) {
744 if (desc.hasWritable() && desc.writable())
745 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
746 if (desc.hasValue()) {
747 bool same;
748 if (!SameValue(cx, desc.value(), v, &same))
749 return false;
750 if (!same)
751 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
752 }
753 }
755 callDelProperty = !shapeHasDefaultGetter || !shapeHasDefaultSetter;
756 } else {
757 /* 8.12.9 step 11. */
758 JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor());
759 if (!shape->configurable()) {
760 if (desc.hasSet()) {
761 bool same;
762 if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
763 return false;
764 if (!same)
765 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
766 }
768 if (desc.hasGet()) {
769 bool same;
770 if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same))
771 return false;
772 if (!same)
773 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
774 }
775 }
776 }
778 /* 8.12.9 step 12. */
779 unsigned attrs;
780 PropertyOp getter;
781 StrictPropertyOp setter;
782 if (desc.isGenericDescriptor()) {
783 unsigned changed = 0;
784 if (desc.hasConfigurable())
785 changed |= JSPROP_PERMANENT;
786 if (desc.hasEnumerable())
787 changed |= JSPROP_ENUMERATE;
789 attrs = (shapeAttributes & ~changed) | (desc.attributes() & changed);
790 getter = IsImplicitDenseOrTypedArrayElement(shape) ? JS_PropertyStub : shape->getter();
791 setter = IsImplicitDenseOrTypedArrayElement(shape) ? JS_StrictPropertyStub : shape->setter();
792 } else if (desc.isDataDescriptor()) {
793 unsigned unchanged = 0;
794 if (!desc.hasConfigurable())
795 unchanged |= JSPROP_PERMANENT;
796 if (!desc.hasEnumerable())
797 unchanged |= JSPROP_ENUMERATE;
798 /* Watch out for accessor -> data transformations here. */
799 if (!desc.hasWritable() && shapeDataDescriptor)
800 unchanged |= JSPROP_READONLY;
802 if (desc.hasValue())
803 v = desc.value();
804 attrs = (desc.attributes() & ~unchanged) | (shapeAttributes & unchanged);
805 getter = JS_PropertyStub;
806 setter = JS_StrictPropertyStub;
807 } else {
808 JS_ASSERT(desc.isAccessorDescriptor());
810 /* 8.12.9 step 12. */
811 unsigned changed = 0;
812 if (desc.hasConfigurable())
813 changed |= JSPROP_PERMANENT;
814 if (desc.hasEnumerable())
815 changed |= JSPROP_ENUMERATE;
816 if (desc.hasGet())
817 changed |= JSPROP_GETTER | JSPROP_SHARED | JSPROP_READONLY;
818 if (desc.hasSet())
819 changed |= JSPROP_SETTER | JSPROP_SHARED | JSPROP_READONLY;
821 attrs = (desc.attributes() & changed) | (shapeAttributes & ~changed);
822 if (desc.hasGet()) {
823 getter = desc.getter();
824 } else {
825 getter = (shapeHasDefaultGetter && !shapeHasGetterValue)
826 ? JS_PropertyStub
827 : shape->getter();
828 }
829 if (desc.hasSet()) {
830 setter = desc.setter();
831 } else {
832 setter = (shapeHasDefaultSetter && !shapeHasSetterValue)
833 ? JS_StrictPropertyStub
834 : shape->setter();
835 }
836 }
838 *rval = true;
840 /*
841 * Since "data" properties implemented using native C functions may rely on
842 * side effects during setting, we must make them aware that they have been
843 * "assigned"; deleting the property before redefining it does the trick.
844 * See bug 539766, where we ran into problems when we redefined
845 * arguments.length without making the property aware that its value had
846 * been changed (which would have happened if we had deleted it before
847 * redefining it or we had invoked its setter to change its value).
848 */
849 if (callDelProperty) {
850 bool succeeded;
851 if (!CallJSDeletePropertyOp(cx, obj2->getClass()->delProperty, obj2, id, &succeeded))
852 return false;
853 }
855 return baseops::DefineGeneric(cx, obj, id, v, getter, setter, attrs);
856 }
858 /* ES6 20130308 draft 8.4.2.1 [[DefineOwnProperty]] */
859 static bool
860 DefinePropertyOnArray(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, const PropDesc &desc,
861 bool throwError, bool *rval)
862 {
863 /* Step 2. */
864 if (id == NameToId(cx->names().length)) {
865 // Canonicalize value, if necessary, before proceeding any further. It
866 // would be better if this were always/only done by ArraySetLength.
867 // But canonicalization may throw a RangeError (or other exception, if
868 // the value is an object with user-defined conversion semantics)
869 // before other attributes are checked. So as long as our internal
870 // defineProperty hook doesn't match the ECMA one, this duplicate
871 // checking can't be helped.
872 RootedValue v(cx);
873 if (desc.hasValue()) {
874 uint32_t newLen;
875 if (!CanonicalizeArrayLengthValue<SequentialExecution>(cx, desc.value(), &newLen))
876 return false;
877 v.setNumber(newLen);
878 } else {
879 v.setNumber(arr->length());
880 }
882 if (desc.hasConfigurable() && desc.configurable())
883 return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval);
884 if (desc.hasEnumerable() && desc.enumerable())
885 return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval);
887 if (desc.isAccessorDescriptor())
888 return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval);
890 unsigned attrs = arr->nativeLookup(cx, id)->attributes();
891 if (!arr->lengthIsWritable()) {
892 if (desc.hasWritable() && desc.writable())
893 return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval);
894 } else {
895 if (desc.hasWritable() && !desc.writable())
896 attrs = attrs | JSPROP_READONLY;
897 }
899 return ArraySetLength<SequentialExecution>(cx, arr, id, attrs, v, throwError);
900 }
902 /* Step 3. */
903 uint32_t index;
904 if (js_IdIsIndex(id, &index)) {
905 /* Step 3b. */
906 uint32_t oldLen = arr->length();
908 /* Steps 3a, 3e. */
909 if (index >= oldLen && !arr->lengthIsWritable())
910 return Reject(cx, arr, JSMSG_CANT_APPEND_TO_ARRAY, throwError, rval);
912 /* Steps 3f-j. */
913 return DefinePropertyOnObject(cx, arr, id, desc, throwError, rval);
914 }
916 /* Step 4. */
917 return DefinePropertyOnObject(cx, arr, id, desc, throwError, rval);
918 }
920 bool
921 js::DefineProperty(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc,
922 bool throwError, bool *rval)
923 {
924 if (obj->is<ArrayObject>()) {
925 Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
926 return DefinePropertyOnArray(cx, arr, id, desc, throwError, rval);
927 }
929 if (obj->getOps()->lookupGeneric) {
930 /*
931 * FIXME: Once ScriptedIndirectProxies are removed, this code should call
932 * TrapDefineOwnProperty directly
933 */
934 if (obj->is<ProxyObject>()) {
935 RootedValue pd(cx, desc.pd());
936 return Proxy::defineProperty(cx, obj, id, pd);
937 }
938 return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
939 }
941 return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval);
942 }
944 bool
945 js::DefineOwnProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue descriptor,
946 bool *bp)
947 {
948 AutoPropDescArrayRooter descs(cx);
949 PropDesc *desc = descs.append();
950 if (!desc || !desc->initialize(cx, descriptor))
951 return false;
953 bool rval;
954 if (!DefineProperty(cx, obj, id, *desc, true, &rval))
955 return false;
956 *bp = !!rval;
957 return true;
958 }
960 bool
961 js::DefineOwnProperty(JSContext *cx, HandleObject obj, HandleId id,
962 Handle<PropertyDescriptor> descriptor, bool *bp)
963 {
964 AutoPropDescArrayRooter descs(cx);
965 PropDesc *desc = descs.append();
966 if (!desc)
967 return false;
969 desc->initFromPropertyDescriptor(descriptor);
971 bool rval;
972 if (!DefineProperty(cx, obj, id, *desc, true, &rval))
973 return false;
974 *bp = !!rval;
975 return true;
976 }
979 bool
980 js::ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccessors,
981 AutoIdVector *ids, AutoPropDescArrayRooter *descs)
982 {
983 if (!GetPropertyNames(cx, props, JSITER_OWNONLY, ids))
984 return false;
986 RootedId id(cx);
987 for (size_t i = 0, len = ids->length(); i < len; i++) {
988 id = (*ids)[i];
989 PropDesc* desc = descs->append();
990 RootedValue v(cx);
991 if (!desc ||
992 !JSObject::getGeneric(cx, props, props, id, &v) ||
993 !desc->initialize(cx, v, checkAccessors))
994 {
995 return false;
996 }
997 }
998 return true;
999 }
1001 bool
1002 js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
1003 {
1004 AutoIdVector ids(cx);
1005 AutoPropDescArrayRooter descs(cx);
1006 if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs))
1007 return false;
1009 if (obj->is<ArrayObject>()) {
1010 bool dummy;
1011 Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
1012 for (size_t i = 0, len = ids.length(); i < len; i++) {
1013 if (!DefinePropertyOnArray(cx, arr, ids.handleAt(i), descs[i], true, &dummy))
1014 return false;
1015 }
1016 return true;
1017 }
1019 if (obj->getOps()->lookupGeneric) {
1020 /*
1021 * FIXME: Once ScriptedIndirectProxies are removed, this code should call
1022 * TrapDefineOwnProperty directly
1023 */
1024 if (obj->is<ProxyObject>()) {
1025 for (size_t i = 0, len = ids.length(); i < len; i++) {
1026 RootedValue pd(cx, descs[i].pd());
1027 if (!Proxy::defineProperty(cx, obj, ids.handleAt(i), pd))
1028 return false;
1029 }
1030 return true;
1031 }
1032 bool dummy;
1033 return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, true, &dummy);
1034 }
1036 bool dummy;
1037 for (size_t i = 0, len = ids.length(); i < len; i++) {
1038 if (!DefinePropertyOnObject(cx, obj, ids.handleAt(i), descs[i], true, &dummy))
1039 return false;
1040 }
1042 return true;
1043 }
1045 extern bool
1046 js_PopulateObject(JSContext *cx, HandleObject newborn, HandleObject props)
1047 {
1048 return DefineProperties(cx, newborn, props);
1049 }
1051 js::types::TypeObject*
1052 JSObject::uninlinedGetType(JSContext *cx)
1053 {
1054 return getType(cx);
1055 }
1057 void
1058 JSObject::uninlinedSetType(js::types::TypeObject *newType)
1059 {
1060 setType(newType);
1061 }
1063 /* static */ inline unsigned
1064 JSObject::getSealedOrFrozenAttributes(unsigned attrs, ImmutabilityType it)
1065 {
1066 /* Make all attributes permanent; if freezing, make data attributes read-only. */
1067 if (it == FREEZE && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1068 return JSPROP_PERMANENT | JSPROP_READONLY;
1069 return JSPROP_PERMANENT;
1070 }
1072 /* static */ bool
1073 JSObject::sealOrFreeze(JSContext *cx, HandleObject obj, ImmutabilityType it)
1074 {
1075 assertSameCompartment(cx, obj);
1076 JS_ASSERT(it == SEAL || it == FREEZE);
1078 bool extensible;
1079 if (!JSObject::isExtensible(cx, obj, &extensible))
1080 return false;
1081 if (extensible && !JSObject::preventExtensions(cx, obj))
1082 return false;
1084 AutoIdVector props(cx);
1085 if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
1086 return false;
1088 /* preventExtensions must sparsify dense objects, so we can assign to holes without checks. */
1089 JS_ASSERT_IF(obj->isNative(), obj->getDenseCapacity() == 0);
1091 if (obj->isNative() && !obj->inDictionaryMode() && !obj->is<TypedArrayObject>()) {
1092 /*
1093 * Seal/freeze non-dictionary objects by constructing a new shape
1094 * hierarchy mirroring the original one, which can be shared if many
1095 * objects with the same structure are sealed/frozen. If we use the
1096 * generic path below then any non-empty object will be converted to
1097 * dictionary mode.
1098 */
1099 RootedShape last(cx, EmptyShape::getInitialShape(cx, obj->getClass(),
1100 obj->getTaggedProto(),
1101 obj->getParent(),
1102 obj->getMetadata(),
1103 obj->numFixedSlots(),
1104 obj->lastProperty()->getObjectFlags()));
1105 if (!last)
1106 return false;
1108 /* Get an in order list of the shapes in this object. */
1109 AutoShapeVector shapes(cx);
1110 for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) {
1111 if (!shapes.append(&r.front()))
1112 return false;
1113 }
1114 Reverse(shapes.begin(), shapes.end());
1116 for (size_t i = 0; i < shapes.length(); i++) {
1117 StackShape unrootedChild(shapes[i]);
1118 RootedGeneric<StackShape*> child(cx, &unrootedChild);
1119 child->attrs |= getSealedOrFrozenAttributes(child->attrs, it);
1121 if (!JSID_IS_EMPTY(child->propid) && it == FREEZE)
1122 MarkTypePropertyNonWritable(cx, obj, child->propid);
1124 last = cx->compartment()->propertyTree.getChild(cx, last, *child);
1125 if (!last)
1126 return false;
1127 }
1129 JS_ASSERT(obj->lastProperty()->slotSpan() == last->slotSpan());
1130 JS_ALWAYS_TRUE(setLastProperty(cx, obj, last));
1131 } else {
1132 RootedId id(cx);
1133 for (size_t i = 0; i < props.length(); i++) {
1134 id = props[i];
1136 unsigned attrs;
1137 if (!getGenericAttributes(cx, obj, id, &attrs))
1138 return false;
1140 unsigned new_attrs = getSealedOrFrozenAttributes(attrs, it);
1142 /* If we already have the attributes we need, skip the setAttributes call. */
1143 if ((attrs | new_attrs) == attrs)
1144 continue;
1146 attrs |= new_attrs;
1147 if (!setGenericAttributes(cx, obj, id, &attrs))
1148 return false;
1149 }
1150 }
1152 // Ordinarily ArraySetLength handles this, but we're going behind its back
1153 // right now, so we must do this manually. Neither the custom property
1154 // tree mutations nor the setGenericAttributes call in the above code will
1155 // do this for us.
1156 //
1157 // ArraySetLength also implements the capacity <= length invariant for
1158 // arrays with non-writable length. We don't need to do anything special
1159 // for that, because capacity was zeroed out by preventExtensions. (See
1160 // the assertion before the if-else above.)
1161 if (it == FREEZE && obj->is<ArrayObject>())
1162 obj->getElementsHeader()->setNonwritableArrayLength();
1164 return true;
1165 }
1167 /* static */ bool
1168 JSObject::isSealedOrFrozen(JSContext *cx, HandleObject obj, ImmutabilityType it, bool *resultp)
1169 {
1170 bool extensible;
1171 if (!JSObject::isExtensible(cx, obj, &extensible))
1172 return false;
1173 if (extensible) {
1174 *resultp = false;
1175 return true;
1176 }
1178 if (obj->is<TypedArrayObject>()) {
1179 if (it == SEAL) {
1180 // Typed arrays are always sealed.
1181 *resultp = true;
1182 } else {
1183 // Typed arrays cannot be frozen, but an empty typed array is
1184 // trivially frozen.
1185 *resultp = (obj->as<TypedArrayObject>().length() == 0);
1186 }
1187 return true;
1188 }
1190 AutoIdVector props(cx);
1191 if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
1192 return false;
1194 RootedId id(cx);
1195 for (size_t i = 0, len = props.length(); i < len; i++) {
1196 id = props[i];
1198 unsigned attrs;
1199 if (!getGenericAttributes(cx, obj, id, &attrs))
1200 return false;
1202 /*
1203 * If the property is configurable, this object is neither sealed nor
1204 * frozen. If the property is a writable data property, this object is
1205 * not frozen.
1206 */
1207 if (!(attrs & JSPROP_PERMANENT) ||
1208 (it == FREEZE && !(attrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER))))
1209 {
1210 *resultp = false;
1211 return true;
1212 }
1213 }
1215 /* All properties checked out. This object is sealed/frozen. */
1216 *resultp = true;
1217 return true;
1218 }
1220 /* static */
1221 const char *
1222 JSObject::className(JSContext *cx, HandleObject obj)
1223 {
1224 assertSameCompartment(cx, obj);
1226 if (obj->is<ProxyObject>())
1227 return Proxy::className(cx, obj);
1229 return obj->getClass()->name;
1230 }
1232 /*
1233 * Get the GC kind to use for scripted 'new' on the given class.
1234 * FIXME bug 547327: estimate the size from the allocation site.
1235 */
1236 static inline gc::AllocKind
1237 NewObjectGCKind(const js::Class *clasp)
1238 {
1239 if (clasp == &ArrayObject::class_)
1240 return gc::FINALIZE_OBJECT8;
1241 if (clasp == &JSFunction::class_)
1242 return gc::FINALIZE_OBJECT2;
1243 return gc::FINALIZE_OBJECT4;
1244 }
1246 static inline JSObject *
1247 NewObject(ExclusiveContext *cx, types::TypeObject *type_, JSObject *parent, gc::AllocKind kind,
1248 NewObjectKind newKind)
1249 {
1250 const Class *clasp = type_->clasp();
1252 JS_ASSERT(clasp != &ArrayObject::class_);
1253 JS_ASSERT_IF(clasp == &JSFunction::class_,
1254 kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
1255 JS_ASSERT_IF(parent, &parent->global() == cx->global());
1257 RootedTypeObject type(cx, type_);
1259 JSObject *metadata = nullptr;
1260 if (!NewObjectMetadata(cx, &metadata))
1261 return nullptr;
1263 // For objects which can have fixed data following the object, only use
1264 // enough fixed slots to cover the number of reserved slots in the object,
1265 // regardless of the allocation kind specified.
1266 size_t nfixed = ClassCanHaveFixedData(clasp)
1267 ? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp)
1268 : GetGCKindSlots(kind, clasp);
1270 RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, type->proto(),
1271 parent, metadata, nfixed));
1272 if (!shape)
1273 return nullptr;
1275 gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
1276 JSObject *obj = JSObject::create(cx, kind, heap, shape, type);
1277 if (!obj)
1278 return nullptr;
1280 if (newKind == SingletonObject) {
1281 RootedObject nobj(cx, obj);
1282 if (!JSObject::setSingletonType(cx, nobj))
1283 return nullptr;
1284 obj = nobj;
1285 }
1287 /*
1288 * This will cancel an already-running incremental GC from doing any more
1289 * slices, and it will prevent any future incremental GCs.
1290 */
1291 bool globalWithoutCustomTrace = clasp->trace == JS_GlobalObjectTraceHook &&
1292 !cx->compartment()->options().getTrace();
1293 if (clasp->trace &&
1294 !globalWithoutCustomTrace &&
1295 !(clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS))
1296 {
1297 if (!cx->shouldBeJSContext())
1298 return nullptr;
1299 JSRuntime *rt = cx->asJSContext()->runtime();
1300 rt->gcIncrementalEnabled = false;
1302 #ifdef DEBUG
1303 if (rt->gcMode() == JSGC_MODE_INCREMENTAL) {
1304 fprintf(stderr,
1305 "The class %s has a trace hook but does not declare the\n"
1306 "JSCLASS_IMPLEMENTS_BARRIERS flag. Please ensure that it correctly\n"
1307 "implements write barriers and then set the flag.\n",
1308 clasp->name);
1309 MOZ_CRASH();
1310 }
1311 #endif
1312 }
1314 probes::CreateObject(cx, obj);
1315 return obj;
1316 }
1318 void
1319 NewObjectCache::fillProto(EntryIndex entry, const Class *clasp, js::TaggedProto proto,
1320 gc::AllocKind kind, JSObject *obj)
1321 {
1322 JS_ASSERT_IF(proto.isObject(), !proto.toObject()->is<GlobalObject>());
1323 JS_ASSERT(obj->getTaggedProto() == proto);
1324 return fill(entry, clasp, proto.raw(), kind, obj);
1325 }
1327 JSObject *
1328 js::NewObjectWithGivenProto(ExclusiveContext *cxArg, const js::Class *clasp,
1329 js::TaggedProto protoArg, JSObject *parentArg,
1330 gc::AllocKind allocKind, NewObjectKind newKind)
1331 {
1332 if (CanBeFinalizedInBackground(allocKind, clasp))
1333 allocKind = GetBackgroundAllocKind(allocKind);
1335 NewObjectCache::EntryIndex entry = -1;
1336 if (JSContext *cx = cxArg->maybeJSContext()) {
1337 NewObjectCache &cache = cx->runtime()->newObjectCache;
1338 if (protoArg.isObject() &&
1339 newKind == GenericObject &&
1340 !cx->compartment()->hasObjectMetadataCallback() &&
1341 (!parentArg || parentArg == protoArg.toObject()->getParent()) &&
1342 !protoArg.toObject()->is<GlobalObject>())
1343 {
1344 if (cache.lookupProto(clasp, protoArg.toObject(), allocKind, &entry)) {
1345 JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, clasp));
1346 if (obj) {
1347 return obj;
1348 } else {
1349 Rooted<TaggedProto> proto(cxArg, protoArg);
1350 RootedObject parent(cxArg, parentArg);
1351 obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, clasp));
1352 JS_ASSERT(!obj);
1353 parentArg = parent;
1354 protoArg = proto;
1355 }
1356 }
1357 }
1358 }
1360 Rooted<TaggedProto> proto(cxArg, protoArg);
1361 RootedObject parent(cxArg, parentArg);
1363 types::TypeObject *type = cxArg->getNewType(clasp, proto, nullptr);
1364 if (!type)
1365 return nullptr;
1367 /*
1368 * Default parent to the parent of the prototype, which was set from
1369 * the parent of the prototype's constructor.
1370 */
1371 if (!parent && proto.isObject())
1372 parent = proto.toObject()->getParent();
1374 RootedObject obj(cxArg, NewObject(cxArg, type, parent, allocKind, newKind));
1375 if (!obj)
1376 return nullptr;
1378 if (entry != -1 && !obj->hasDynamicSlots()) {
1379 cxArg->asJSContext()->runtime()->newObjectCache.fillProto(entry, clasp,
1380 proto, allocKind, obj);
1381 }
1383 return obj;
1384 }
1386 JSObject *
1387 js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg,
1388 const js::Class *clasp, JSObject *protoArg, JSObject *parentArg,
1389 gc::AllocKind allocKind, NewObjectKind newKind)
1390 {
1391 if (protoArg)
1392 return NewObjectWithGivenProto(cxArg, clasp, protoArg, parentArg, allocKind, newKind);
1394 if (CanBeFinalizedInBackground(allocKind, clasp))
1395 allocKind = GetBackgroundAllocKind(allocKind);
1397 if (!parentArg)
1398 parentArg = cxArg->global();
1400 /*
1401 * Use the object cache, except for classes without a cached proto key.
1402 * On these objects, FindProto will do a dynamic property lookup to get
1403 * global[className].prototype, where changes to either the className or
1404 * prototype property would render the cached lookup incorrect. For classes
1405 * with a proto key, the prototype created during class initialization is
1406 * stored in an immutable slot on the global (except for ClearScope, which
1407 * will flush the new object cache).
1408 */
1409 JSProtoKey protoKey = GetClassProtoKey(clasp);
1411 NewObjectCache::EntryIndex entry = -1;
1412 if (JSContext *cx = cxArg->maybeJSContext()) {
1413 NewObjectCache &cache = cx->runtime()->newObjectCache;
1414 if (parentArg->is<GlobalObject>() &&
1415 protoKey != JSProto_Null &&
1416 newKind == GenericObject &&
1417 !cx->compartment()->hasObjectMetadataCallback())
1418 {
1419 if (cache.lookupGlobal(clasp, &parentArg->as<GlobalObject>(), allocKind, &entry)) {
1420 JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, clasp));
1421 if (obj) {
1422 return obj;
1423 } else {
1424 RootedObject parent(cxArg, parentArg);
1425 RootedObject proto(cxArg, protoArg);
1426 obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, clasp));
1427 JS_ASSERT(!obj);
1428 protoArg = proto;
1429 parentArg = parent;
1430 }
1431 }
1432 }
1433 }
1435 RootedObject parent(cxArg, parentArg);
1436 RootedObject proto(cxArg, protoArg);
1438 if (!FindProto(cxArg, clasp, &proto))
1439 return nullptr;
1441 types::TypeObject *type = cxArg->getNewType(clasp, proto.get());
1442 if (!type)
1443 return nullptr;
1445 JSObject *obj = NewObject(cxArg, type, parent, allocKind, newKind);
1446 if (!obj)
1447 return nullptr;
1449 if (entry != -1 && !obj->hasDynamicSlots()) {
1450 cxArg->asJSContext()->runtime()->newObjectCache.fillGlobal(entry, clasp,
1451 &parent->as<GlobalObject>(),
1452 allocKind, obj);
1453 }
1455 return obj;
1456 }
1458 /*
1459 * Create a plain object with the specified type. This bypasses getNewType to
1460 * avoid losing creation site information for objects made by scripted 'new'.
1461 */
1462 JSObject *
1463 js::NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind allocKind,
1464 NewObjectKind newKind)
1465 {
1466 JS_ASSERT(parent);
1468 JS_ASSERT(allocKind <= gc::FINALIZE_OBJECT_LAST);
1469 if (CanBeFinalizedInBackground(allocKind, type->clasp()))
1470 allocKind = GetBackgroundAllocKind(allocKind);
1472 NewObjectCache &cache = cx->runtime()->newObjectCache;
1474 NewObjectCache::EntryIndex entry = -1;
1475 if (parent == type->proto().toObject()->getParent() &&
1476 newKind == GenericObject &&
1477 !cx->compartment()->hasObjectMetadataCallback())
1478 {
1479 if (cache.lookupType(type, allocKind, &entry)) {
1480 JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, type->clasp()));
1481 if (obj) {
1482 return obj;
1483 } else {
1484 obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, type->clasp()));
1485 parent = type->proto().toObject()->getParent();
1486 }
1487 }
1488 }
1490 JSObject *obj = NewObject(cx, type, parent, allocKind, newKind);
1491 if (!obj)
1492 return nullptr;
1494 if (entry != -1 && !obj->hasDynamicSlots())
1495 cache.fillType(entry, type, allocKind, obj);
1497 return obj;
1498 }
1500 bool
1501 js::NewObjectScriptedCall(JSContext *cx, MutableHandleObject pobj)
1502 {
1503 jsbytecode *pc;
1504 RootedScript script(cx, cx->currentScript(&pc));
1505 gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_);
1506 NewObjectKind newKind = script
1507 ? UseNewTypeForInitializer(script, pc, &JSObject::class_)
1508 : GenericObject;
1509 RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind, newKind));
1510 if (!obj)
1511 return false;
1513 if (script) {
1514 /* Try to specialize the type of the object to the scripted call site. */
1515 if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind))
1516 return false;
1517 }
1519 pobj.set(obj);
1520 return true;
1521 }
1523 JSObject*
1524 js::CreateThis(JSContext *cx, const Class *newclasp, HandleObject callee)
1525 {
1526 RootedValue protov(cx);
1527 if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov))
1528 return nullptr;
1530 JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : nullptr;
1531 JSObject *parent = callee->getParent();
1532 gc::AllocKind kind = NewObjectGCKind(newclasp);
1533 return NewObjectWithClassProto(cx, newclasp, proto, parent, kind);
1534 }
1536 static inline JSObject *
1537 CreateThisForFunctionWithType(JSContext *cx, HandleTypeObject type, JSObject *parent,
1538 NewObjectKind newKind)
1539 {
1540 if (type->hasNewScript()) {
1541 /*
1542 * Make an object with the type's associated finalize kind and shape,
1543 * which reflects any properties that will definitely be added to the
1544 * object before it is read from.
1545 */
1546 RootedObject templateObject(cx, type->newScript()->templateObject);
1547 JS_ASSERT(templateObject->type() == type);
1549 RootedObject res(cx, CopyInitializerObject(cx, templateObject, newKind));
1550 if (!res)
1551 return nullptr;
1552 if (newKind == SingletonObject) {
1553 Rooted<TaggedProto> proto(cx, templateObject->getProto());
1554 if (!res->splicePrototype(cx, &JSObject::class_, proto))
1555 return nullptr;
1556 } else {
1557 res->setType(type);
1558 }
1559 return res;
1560 }
1562 gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_);
1563 return NewObjectWithType(cx, type, parent, allocKind, newKind);
1564 }
1566 JSObject *
1567 js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject *proto,
1568 NewObjectKind newKind /* = GenericObject */)
1569 {
1570 RootedObject res(cx);
1572 if (proto) {
1573 RootedTypeObject type(cx, cx->getNewType(&JSObject::class_, proto, &callee->as<JSFunction>()));
1574 if (!type)
1575 return nullptr;
1576 res = CreateThisForFunctionWithType(cx, type, callee->getParent(), newKind);
1577 } else {
1578 gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_);
1579 res = NewObjectWithClassProto(cx, &JSObject::class_, proto, callee->getParent(), allocKind, newKind);
1580 }
1582 if (res) {
1583 JSScript *script = callee->as<JSFunction>().getOrCreateScript(cx);
1584 if (!script)
1585 return nullptr;
1586 TypeScript::SetThis(cx, script, types::Type::ObjectType(res));
1587 }
1589 return res;
1590 }
1592 JSObject *
1593 js::CreateThisForFunction(JSContext *cx, HandleObject callee, NewObjectKind newKind)
1594 {
1595 RootedValue protov(cx);
1596 if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov))
1597 return nullptr;
1598 JSObject *proto;
1599 if (protov.isObject())
1600 proto = &protov.toObject();
1601 else
1602 proto = nullptr;
1603 JSObject *obj = CreateThisForFunctionWithProto(cx, callee, proto, newKind);
1605 if (obj && newKind == SingletonObject) {
1606 RootedObject nobj(cx, obj);
1608 /* Reshape the singleton before passing it as the 'this' value. */
1609 JSObject::clear(cx, nobj);
1611 JSScript *calleeScript = callee->as<JSFunction>().nonLazyScript();
1612 TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(nobj));
1614 return nobj;
1615 }
1617 return obj;
1618 }
1620 /*
1621 * Given pc pointing after a property accessing bytecode, return true if the
1622 * access is "object-detecting" in the sense used by web scripts, e.g., when
1623 * checking whether document.all is defined.
1624 */
1625 static bool
1626 Detecting(JSContext *cx, JSScript *script, jsbytecode *pc)
1627 {
1628 JS_ASSERT(script->containsPC(pc));
1630 /* General case: a branch or equality op follows the access. */
1631 JSOp op = JSOp(*pc);
1632 if (js_CodeSpec[op].format & JOF_DETECTING)
1633 return true;
1635 jsbytecode *endpc = script->codeEnd();
1637 if (op == JSOP_NULL) {
1638 /*
1639 * Special case #1: handle (document.all == null). Don't sweat
1640 * about JS1.2's revision of the equality operators here.
1641 */
1642 if (++pc < endpc) {
1643 op = JSOp(*pc);
1644 return op == JSOP_EQ || op == JSOP_NE;
1645 }
1646 return false;
1647 }
1649 if (op == JSOP_GETGNAME || op == JSOP_NAME) {
1650 /*
1651 * Special case #2: handle (document.all == undefined). Don't worry
1652 * about a local variable named |undefined| shadowing the immutable
1653 * global binding...because, really?
1654 */
1655 JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
1656 if (atom == cx->names().undefined &&
1657 (pc += js_CodeSpec[op].length) < endpc) {
1658 op = JSOp(*pc);
1659 return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
1660 }
1661 }
1663 return false;
1664 }
1666 /* static */ bool
1667 JSObject::nonNativeSetProperty(JSContext *cx, HandleObject obj,
1668 HandleId id, MutableHandleValue vp, bool strict)
1669 {
1670 if (MOZ_UNLIKELY(obj->watched())) {
1671 WatchpointMap *wpmap = cx->compartment()->watchpointMap;
1672 if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
1673 return false;
1674 }
1675 return obj->getOps()->setGeneric(cx, obj, id, vp, strict);
1676 }
1678 /* static */ bool
1679 JSObject::nonNativeSetElement(JSContext *cx, HandleObject obj,
1680 uint32_t index, MutableHandleValue vp, bool strict)
1681 {
1682 if (MOZ_UNLIKELY(obj->watched())) {
1683 RootedId id(cx);
1684 if (!IndexToId(cx, index, &id))
1685 return false;
1687 WatchpointMap *wpmap = cx->compartment()->watchpointMap;
1688 if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
1689 return false;
1690 }
1691 return obj->getOps()->setElement(cx, obj, index, vp, strict);
1692 }
1694 /* static */ bool
1695 JSObject::deleteByValue(JSContext *cx, HandleObject obj, const Value &property, bool *succeeded)
1696 {
1697 uint32_t index;
1698 if (IsDefinitelyIndex(property, &index))
1699 return deleteElement(cx, obj, index, succeeded);
1701 RootedValue propval(cx, property);
1703 JSAtom *name = ToAtom<CanGC>(cx, propval);
1704 if (!name)
1705 return false;
1707 if (name->isIndex(&index))
1708 return deleteElement(cx, obj, index, succeeded);
1710 Rooted<PropertyName*> propname(cx, name->asPropertyName());
1711 return deleteProperty(cx, obj, propname, succeeded);
1712 }
1714 JS_FRIEND_API(bool)
1715 JS_CopyPropertyFrom(JSContext *cx, HandleId id, HandleObject target,
1716 HandleObject obj)
1717 {
1718 // |obj| and |cx| are generally not same-compartment with |target| here.
1719 assertSameCompartment(cx, obj, id);
1720 Rooted<JSPropertyDescriptor> desc(cx);
1722 if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
1723 return false;
1724 MOZ_ASSERT(desc.object());
1726 // Silently skip JSPropertyOp-implemented accessors.
1727 if (desc.getter() && !desc.hasGetterObject())
1728 return true;
1729 if (desc.setter() && !desc.hasSetterObject())
1730 return true;
1732 JSAutoCompartment ac(cx, target);
1733 RootedId wrappedId(cx, id);
1734 if (!cx->compartment()->wrap(cx, &desc))
1735 return false;
1736 if (!cx->compartment()->wrapId(cx, wrappedId.address()))
1737 return false;
1739 bool ignored;
1740 return DefineOwnProperty(cx, target, wrappedId, desc, &ignored);
1741 }
1743 JS_FRIEND_API(bool)
1744 JS_CopyPropertiesFrom(JSContext *cx, HandleObject target, HandleObject obj)
1745 {
1746 JSAutoCompartment ac(cx, obj);
1748 AutoIdVector props(cx);
1749 if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &props))
1750 return false;
1752 for (size_t i = 0; i < props.length(); ++i) {
1753 if (!JS_CopyPropertyFrom(cx, props.handleAt(i), target, obj))
1754 return false;
1755 }
1757 return true;
1758 }
1760 static bool
1761 CopySlots(JSContext *cx, HandleObject from, HandleObject to)
1762 {
1763 JS_ASSERT(!from->isNative() && !to->isNative());
1764 JS_ASSERT(from->getClass() == to->getClass());
1766 size_t n = 0;
1767 if (from->is<WrapperObject>() &&
1768 (Wrapper::wrapperHandler(from)->flags() &
1769 Wrapper::CROSS_COMPARTMENT)) {
1770 to->setSlot(0, from->getSlot(0));
1771 to->setSlot(1, from->getSlot(1));
1772 n = 2;
1773 }
1775 size_t span = JSCLASS_RESERVED_SLOTS(from->getClass());
1776 RootedValue v(cx);
1777 for (; n < span; ++n) {
1778 v = from->getSlot(n);
1779 if (!cx->compartment()->wrap(cx, &v))
1780 return false;
1781 to->setSlot(n, v);
1782 }
1783 return true;
1784 }
1786 JSObject *
1787 js::CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto, HandleObject parent)
1788 {
1789 if (!obj->isNative() && !obj->is<ProxyObject>()) {
1790 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
1791 JSMSG_CANT_CLONE_OBJECT);
1792 return nullptr;
1793 }
1795 RootedObject clone(cx, NewObjectWithGivenProto(cx, obj->getClass(), proto, parent));
1796 if (!clone)
1797 return nullptr;
1798 if (obj->isNative()) {
1799 if (clone->is<JSFunction>() && (obj->compartment() != clone->compartment())) {
1800 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
1801 JSMSG_CANT_CLONE_OBJECT);
1802 return nullptr;
1803 }
1805 if (obj->hasPrivate())
1806 clone->setPrivate(obj->getPrivate());
1807 } else {
1808 JS_ASSERT(obj->is<ProxyObject>());
1809 if (!CopySlots(cx, obj, clone))
1810 return nullptr;
1811 }
1813 return clone;
1814 }
1816 JSObject *
1817 js::DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind)
1818 {
1819 /* NB: Keep this in sync with XDRObjectLiteral. */
1820 JS_ASSERT(JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
1821 JS_ASSERT(obj->is<JSObject>() || obj->is<ArrayObject>());
1823 // Result of the clone function.
1824 RootedObject clone(cx);
1826 // Temporary element/slot which would be stored in the cloned object.
1827 RootedValue v(cx);
1828 RootedObject deepObj(cx);
1830 if (obj->getClass() == &ArrayObject::class_) {
1831 clone = NewDenseUnallocatedArray(cx, obj->as<ArrayObject>().length(), nullptr, newKind);
1832 } else {
1833 // Object literals are tenured by default as holded by the JSScript.
1834 JS_ASSERT(obj->isTenured());
1835 AllocKind kind = obj->tenuredGetAllocKind();
1836 Rooted<TypeObject*> typeObj(cx, obj->getType(cx));
1837 if (!typeObj)
1838 return nullptr;
1839 RootedObject parent(cx, obj->getParent());
1840 clone = NewObjectWithGivenProto(cx, &JSObject::class_, typeObj->proto().toObject(),
1841 parent, kind, newKind);
1842 }
1844 // Allocate the same number of slots.
1845 if (!clone || !clone->ensureElements(cx, obj->getDenseCapacity()))
1846 return nullptr;
1848 // Copy the number of initialized elements.
1849 uint32_t initialized = obj->getDenseInitializedLength();
1850 if (initialized)
1851 clone->setDenseInitializedLength(initialized);
1853 // Recursive copy of dense element.
1854 for (uint32_t i = 0; i < initialized; ++i) {
1855 v = obj->getDenseElement(i);
1856 if (v.isObject()) {
1857 deepObj = &v.toObject();
1858 deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind);
1859 if (!deepObj) {
1860 JS_ReportOutOfMemory(cx);
1861 return nullptr;
1862 }
1863 v.setObject(*deepObj);
1864 }
1865 clone->initDenseElement(i, v);
1866 }
1868 JS_ASSERT(obj->compartment() == clone->compartment());
1869 JS_ASSERT(!obj->hasPrivate());
1870 RootedShape shape(cx, obj->lastProperty());
1871 size_t span = shape->slotSpan();
1872 clone->setLastProperty(cx, clone, shape);
1873 for (size_t i = 0; i < span; i++) {
1874 v = obj->getSlot(i);
1875 if (v.isObject()) {
1876 deepObj = &v.toObject();
1877 deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind);
1878 if (!deepObj)
1879 return nullptr;
1880 v.setObject(*deepObj);
1881 }
1882 clone->setSlot(i, v);
1883 }
1885 if (obj->getClass() == &ArrayObject::class_)
1886 FixArrayType(cx, clone);
1887 else
1888 FixObjectType(cx, clone);
1890 #ifdef DEBUG
1891 Rooted<TypeObject*> typeObj(cx, obj->getType(cx));
1892 Rooted<TypeObject*> cloneTypeObj(cx, clone->getType(cx));
1893 if (!typeObj || !cloneTypeObj)
1894 return nullptr;
1895 JS_ASSERT(typeObj == cloneTypeObj);
1896 #endif
1898 return clone;
1899 }
1901 template<XDRMode mode>
1902 bool
1903 js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj)
1904 {
1905 /* NB: Keep this in sync with DeepCloneObjectLiteral. */
1907 JSContext *cx = xdr->cx();
1908 JS_ASSERT_IF(mode == XDR_ENCODE, JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
1910 // Distinguish between objects and array classes.
1911 uint32_t isArray = 0;
1912 {
1913 if (mode == XDR_ENCODE) {
1914 JS_ASSERT(obj->is<JSObject>() || obj->is<ArrayObject>());
1915 isArray = obj->getClass() == &ArrayObject::class_ ? 1 : 0;
1916 }
1918 if (!xdr->codeUint32(&isArray))
1919 return false;
1920 }
1922 if (isArray) {
1923 uint32_t length;
1925 if (mode == XDR_ENCODE)
1926 length = obj->as<ArrayObject>().length();
1928 if (!xdr->codeUint32(&length))
1929 return false;
1931 if (mode == XDR_DECODE)
1932 obj.set(NewDenseUnallocatedArray(cx, length, NULL, js::MaybeSingletonObject));
1934 } else {
1935 // Code the alloc kind of the object.
1936 AllocKind kind;
1937 {
1938 if (mode == XDR_ENCODE) {
1939 JS_ASSERT(obj->getClass() == &JSObject::class_);
1940 JS_ASSERT(obj->isTenured());
1941 kind = obj->tenuredGetAllocKind();
1942 }
1944 if (!xdr->codeEnum32(&kind))
1945 return false;
1947 if (mode == XDR_DECODE)
1948 obj.set(NewBuiltinClassInstance(cx, &JSObject::class_, kind, js::MaybeSingletonObject));
1949 }
1950 }
1952 {
1953 uint32_t capacity;
1954 if (mode == XDR_ENCODE)
1955 capacity = obj->getDenseCapacity();
1956 if (!xdr->codeUint32(&capacity))
1957 return false;
1958 if (mode == XDR_DECODE) {
1959 if (!obj->ensureElements(cx, capacity)) {
1960 JS_ReportOutOfMemory(cx);
1961 return false;
1962 }
1963 }
1964 }
1966 uint32_t initialized;
1967 {
1968 if (mode == XDR_ENCODE)
1969 initialized = obj->getDenseInitializedLength();
1970 if (!xdr->codeUint32(&initialized))
1971 return false;
1972 if (mode == XDR_DECODE) {
1973 if (initialized)
1974 obj->setDenseInitializedLength(initialized);
1975 }
1976 }
1978 RootedValue tmpValue(cx);
1980 // Recursively copy dense elements.
1981 {
1982 for (unsigned i = 0; i < initialized; i++) {
1983 if (mode == XDR_ENCODE)
1984 tmpValue = obj->getDenseElement(i);
1986 if (!xdr->codeConstValue(&tmpValue))
1987 return false;
1989 if (mode == XDR_DECODE)
1990 obj->initDenseElement(i, tmpValue);
1991 }
1992 }
1994 JS_ASSERT(!obj->hasPrivate());
1995 RootedShape shape(cx, obj->lastProperty());
1997 // Code the number of slots in the vector.
1998 unsigned nslot = 0;
2000 // Code ids of the object in order. As opposed to DeepCloneObjectLiteral we
2001 // cannot just re-use the shape of the original bytecode value and we have
2002 // to write down the shape as well as the corresponding values. Ideally we
2003 // would have a mechanism to serialize the shape too.
2004 js::AutoIdVector ids(cx);
2005 {
2006 if (mode == XDR_ENCODE && !shape->isEmptyShape()) {
2007 nslot = shape->slotSpan();
2008 if (!ids.reserve(nslot))
2009 return false;
2011 for (unsigned i = 0; i < nslot; i++)
2012 ids.infallibleAppend(JSID_VOID);
2014 for (Shape::Range<NoGC> it(shape); !it.empty(); it.popFront()) {
2015 // If we have reached the native property of the array class, we
2016 // exit as the remaining would only be reserved slots.
2017 if (!it.front().hasSlot()) {
2018 JS_ASSERT(isArray);
2019 break;
2020 }
2022 JS_ASSERT(it.front().hasDefaultGetter());
2023 ids[it.front().slot()] = it.front().propid();
2024 }
2025 }
2027 if (!xdr->codeUint32(&nslot))
2028 return false;
2030 RootedAtom atom(cx);
2031 RootedId id(cx);
2032 uint32_t idType = 0;
2033 for (unsigned i = 0; i < nslot; i++) {
2034 if (mode == XDR_ENCODE) {
2035 id = ids[i];
2036 if (JSID_IS_INT(id))
2037 idType = JSID_TYPE_INT;
2038 else if (JSID_IS_ATOM(id))
2039 idType = JSID_TYPE_STRING;
2040 else
2041 MOZ_ASSUME_UNREACHABLE("Object property is not yet supported by XDR.");
2043 tmpValue = obj->getSlot(i);
2044 }
2046 if (!xdr->codeUint32(&idType))
2047 return false;
2049 if (idType == JSID_TYPE_STRING) {
2050 if (mode == XDR_ENCODE)
2051 atom = JSID_TO_ATOM(id);
2052 if (!XDRAtom(xdr, &atom))
2053 return false;
2054 if (mode == XDR_DECODE)
2055 id = AtomToId(atom);
2056 } else {
2057 JS_ASSERT(idType == JSID_TYPE_INT);
2058 uint32_t indexVal;
2059 if (mode == XDR_ENCODE)
2060 indexVal = uint32_t(JSID_TO_INT(id));
2061 if (!xdr->codeUint32(&indexVal))
2062 return false;
2063 if (mode == XDR_DECODE)
2064 id = INT_TO_JSID(int32_t(indexVal));
2065 }
2067 if (!xdr->codeConstValue(&tmpValue))
2068 return false;
2070 if (mode == XDR_DECODE) {
2071 if (!DefineNativeProperty(cx, obj, id, tmpValue, NULL, NULL, JSPROP_ENUMERATE))
2072 return false;
2073 }
2074 }
2076 JS_ASSERT_IF(mode == XDR_DECODE, !obj->inDictionaryMode());
2077 }
2079 if (mode == XDR_DECODE) {
2080 if (isArray)
2081 FixArrayType(cx, obj);
2082 else
2083 FixObjectType(cx, obj);
2084 }
2086 return true;
2087 }
2089 template bool
2090 js::XDRObjectLiteral(XDRState<XDR_ENCODE> *xdr, MutableHandleObject obj);
2092 template bool
2093 js::XDRObjectLiteral(XDRState<XDR_DECODE> *xdr, MutableHandleObject obj);
2095 JSObject *
2096 js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj)
2097 {
2098 Rooted<TypeObject*> typeObj(cx);
2099 typeObj = cx->getNewType(&JSObject::class_, cx->global()->getOrCreateObjectPrototype(cx));
2101 JS_ASSERT(srcObj->getClass() == &JSObject::class_);
2102 AllocKind kind = GetBackgroundAllocKind(GuessObjectGCKind(srcObj->numFixedSlots()));
2103 JS_ASSERT_IF(srcObj->isTenured(), kind == srcObj->tenuredGetAllocKind());
2105 RootedShape shape(cx, srcObj->lastProperty());
2106 return NewReshapedObject(cx, typeObj, parent, kind, shape);
2107 }
2109 struct JSObject::TradeGutsReserved {
2110 Vector<Value> avals;
2111 Vector<Value> bvals;
2112 int newafixed;
2113 int newbfixed;
2114 RootedShape newashape;
2115 RootedShape newbshape;
2116 HeapSlot *newaslots;
2117 HeapSlot *newbslots;
2119 TradeGutsReserved(JSContext *cx)
2120 : avals(cx), bvals(cx),
2121 newafixed(0), newbfixed(0),
2122 newashape(cx), newbshape(cx),
2123 newaslots(nullptr), newbslots(nullptr)
2124 {}
2126 ~TradeGutsReserved()
2127 {
2128 js_free(newaslots);
2129 js_free(newbslots);
2130 }
2131 };
2133 bool
2134 JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *aArg, JSObject *bArg,
2135 TradeGutsReserved &reserved)
2136 {
2137 /*
2138 * Avoid GC in here to avoid confusing the tracing code with our
2139 * intermediate state.
2140 */
2141 AutoSuppressGC suppress(cx);
2143 RootedObject a(cx, aArg);
2144 RootedObject b(cx, bArg);
2145 JS_ASSERT(a->compartment() == b->compartment());
2146 AutoCompartment ac(cx, a);
2148 /*
2149 * When performing multiple swaps between objects which may have different
2150 * numbers of fixed slots, we reserve all space ahead of time so that the
2151 * swaps can be performed infallibly.
2152 */
2154 /*
2155 * Swap prototypes and classes on the two objects, so that TradeGuts can
2156 * preserve the types of the two objects.
2157 */
2158 const Class *aClass = a->getClass();
2159 const Class *bClass = b->getClass();
2160 Rooted<TaggedProto> aProto(cx, a->getTaggedProto());
2161 Rooted<TaggedProto> bProto(cx, b->getTaggedProto());
2162 bool success;
2163 if (!SetClassAndProto(cx, a, bClass, bProto, &success) || !success)
2164 return false;
2165 if (!SetClassAndProto(cx, b, aClass, aProto, &success) || !success)
2166 return false;
2168 if (a->tenuredSizeOfThis() == b->tenuredSizeOfThis())
2169 return true;
2171 /*
2172 * If either object is native, it needs a new shape to preserve the
2173 * invariant that objects with the same shape have the same number of
2174 * inline slots. The fixed slots will be updated in place during TradeGuts.
2175 * Non-native objects need to be reshaped according to the new count.
2176 */
2177 if (a->isNative()) {
2178 if (!a->generateOwnShape(cx))
2179 return false;
2180 } else {
2181 reserved.newbshape = EmptyShape::getInitialShape(cx, aClass, aProto, a->getParent(), a->getMetadata(),
2182 b->tenuredGetAllocKind());
2183 if (!reserved.newbshape)
2184 return false;
2185 }
2186 if (b->isNative()) {
2187 if (!b->generateOwnShape(cx))
2188 return false;
2189 } else {
2190 reserved.newashape = EmptyShape::getInitialShape(cx, bClass, bProto, b->getParent(), b->getMetadata(),
2191 a->tenuredGetAllocKind());
2192 if (!reserved.newashape)
2193 return false;
2194 }
2196 /* The avals/bvals vectors hold all original values from the objects. */
2198 if (!reserved.avals.reserve(a->slotSpan()))
2199 return false;
2200 if (!reserved.bvals.reserve(b->slotSpan()))
2201 return false;
2203 /*
2204 * The newafixed/newbfixed hold the number of fixed slots in the objects
2205 * after the swap. Adjust these counts according to whether the objects
2206 * use their last fixed slot for storing private data.
2207 */
2209 reserved.newafixed = a->numFixedSlots();
2210 reserved.newbfixed = b->numFixedSlots();
2212 if (aClass->hasPrivate()) {
2213 reserved.newafixed++;
2214 reserved.newbfixed--;
2215 }
2216 if (bClass->hasPrivate()) {
2217 reserved.newbfixed++;
2218 reserved.newafixed--;
2219 }
2221 JS_ASSERT(reserved.newafixed >= 0);
2222 JS_ASSERT(reserved.newbfixed >= 0);
2224 /*
2225 * The newaslots/newbslots arrays hold any dynamic slots for the objects
2226 * if they do not have enough fixed slots to accomodate the slots in the
2227 * other object.
2228 */
2230 unsigned adynamic = dynamicSlotsCount(reserved.newafixed, b->slotSpan(), b->getClass());
2231 unsigned bdynamic = dynamicSlotsCount(reserved.newbfixed, a->slotSpan(), a->getClass());
2233 if (adynamic) {
2234 reserved.newaslots = cx->pod_malloc<HeapSlot>(adynamic);
2235 if (!reserved.newaslots)
2236 return false;
2237 Debug_SetSlotRangeToCrashOnTouch(reserved.newaslots, adynamic);
2238 }
2239 if (bdynamic) {
2240 reserved.newbslots = cx->pod_malloc<HeapSlot>(bdynamic);
2241 if (!reserved.newbslots)
2242 return false;
2243 Debug_SetSlotRangeToCrashOnTouch(reserved.newbslots, bdynamic);
2244 }
2246 return true;
2247 }
2249 void
2250 JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &reserved)
2251 {
2252 JS_ASSERT(a->compartment() == b->compartment());
2253 JS_ASSERT(a->is<JSFunction>() == b->is<JSFunction>());
2255 /*
2256 * Swap the object's types, to restore their initial type information.
2257 * The prototypes and classes of the objects were swapped in ReserveForTradeGuts.
2258 */
2259 TypeObject *tmp = a->type_;
2260 a->type_ = b->type_;
2261 b->type_ = tmp;
2263 /* Don't try to swap a JSFunction for a plain function JSObject. */
2264 JS_ASSERT_IF(a->is<JSFunction>(), a->tenuredSizeOfThis() == b->tenuredSizeOfThis());
2266 /*
2267 * Regexp guts are more complicated -- we would need to migrate the
2268 * refcounted JIT code blob for them across compartments instead of just
2269 * swapping guts.
2270 */
2271 JS_ASSERT(!a->is<RegExpObject>() && !b->is<RegExpObject>());
2273 /* Arrays can use their fixed storage for elements. */
2274 JS_ASSERT(!a->is<ArrayObject>() && !b->is<ArrayObject>());
2276 /*
2277 * Callers should not try to swap ArrayBuffer objects,
2278 * these use a different slot representation from other objects.
2279 */
2280 JS_ASSERT(!a->is<ArrayBufferObject>() && !b->is<ArrayBufferObject>());
2282 /* Trade the guts of the objects. */
2283 const size_t size = a->tenuredSizeOfThis();
2284 if (size == b->tenuredSizeOfThis()) {
2285 /*
2286 * If the objects are the same size, then we make no assumptions about
2287 * whether they have dynamically allocated slots and instead just copy
2288 * them over wholesale.
2289 */
2290 char tmp[mozilla::tl::Max<sizeof(JSFunction), sizeof(JSObject_Slots16)>::value];
2291 JS_ASSERT(size <= sizeof(tmp));
2293 js_memcpy(tmp, a, size);
2294 js_memcpy(a, b, size);
2295 js_memcpy(b, tmp, size);
2297 #ifdef JSGC_GENERATIONAL
2298 /*
2299 * Trigger post barriers for fixed slots. JSObject bits are barriered
2300 * below, in common with the other case.
2301 */
2302 for (size_t i = 0; i < a->numFixedSlots(); ++i) {
2303 HeapSlot::writeBarrierPost(cx->runtime(), a, HeapSlot::Slot, i, a->getSlot(i));
2304 HeapSlot::writeBarrierPost(cx->runtime(), b, HeapSlot::Slot, i, b->getSlot(i));
2305 }
2306 #endif
2307 } else {
2308 /*
2309 * If the objects are of differing sizes, use the space we reserved
2310 * earlier to save the slots from each object and then copy them into
2311 * the new layout for the other object.
2312 */
2314 uint32_t acap = a->slotSpan();
2315 uint32_t bcap = b->slotSpan();
2317 for (size_t i = 0; i < acap; i++)
2318 reserved.avals.infallibleAppend(a->getSlot(i));
2320 for (size_t i = 0; i < bcap; i++)
2321 reserved.bvals.infallibleAppend(b->getSlot(i));
2323 /* Done with the dynamic slots. */
2324 if (a->hasDynamicSlots())
2325 js_free(a->slots);
2326 if (b->hasDynamicSlots())
2327 js_free(b->slots);
2329 void *apriv = a->hasPrivate() ? a->getPrivate() : nullptr;
2330 void *bpriv = b->hasPrivate() ? b->getPrivate() : nullptr;
2332 char tmp[sizeof(JSObject)];
2333 js_memcpy(&tmp, a, sizeof tmp);
2334 js_memcpy(a, b, sizeof tmp);
2335 js_memcpy(b, &tmp, sizeof tmp);
2337 if (a->isNative())
2338 a->shape_->setNumFixedSlots(reserved.newafixed);
2339 else
2340 a->shape_ = reserved.newashape;
2342 a->slots = reserved.newaslots;
2343 a->initSlotRange(0, reserved.bvals.begin(), bcap);
2344 if (a->hasPrivate())
2345 a->initPrivate(bpriv);
2347 if (b->isNative())
2348 b->shape_->setNumFixedSlots(reserved.newbfixed);
2349 else
2350 b->shape_ = reserved.newbshape;
2352 b->slots = reserved.newbslots;
2353 b->initSlotRange(0, reserved.avals.begin(), acap);
2354 if (b->hasPrivate())
2355 b->initPrivate(apriv);
2357 /* Make sure the destructor for reserved doesn't free the slots. */
2358 reserved.newaslots = nullptr;
2359 reserved.newbslots = nullptr;
2360 }
2362 #ifdef JSGC_GENERATIONAL
2363 Shape::writeBarrierPost(a->shape_, &a->shape_);
2364 Shape::writeBarrierPost(b->shape_, &b->shape_);
2365 types::TypeObject::writeBarrierPost(a->type_, &a->type_);
2366 types::TypeObject::writeBarrierPost(b->type_, &b->type_);
2367 #endif
2369 if (a->inDictionaryMode())
2370 a->lastProperty()->listp = &a->shape_;
2371 if (b->inDictionaryMode())
2372 b->lastProperty()->listp = &b->shape_;
2374 #ifdef JSGC_INCREMENTAL
2375 /*
2376 * We need a write barrier here. If |a| was marked and |b| was not, then
2377 * after the swap, |b|'s guts would never be marked. The write barrier
2378 * solves this.
2379 *
2380 * Normally write barriers happen before the write. However, that's not
2381 * necessary here because nothing is being destroyed. We're just swapping.
2382 * We don't do the barrier before TradeGuts because ReserveForTradeGuts
2383 * makes changes to the objects that might confuse the tracing code.
2384 */
2385 JS::Zone *zone = a->zone();
2386 if (zone->needsBarrier()) {
2387 MarkChildren(zone->barrierTracer(), a);
2388 MarkChildren(zone->barrierTracer(), b);
2389 }
2390 #endif
2391 }
2393 /* Use this method with extreme caution. It trades the guts of two objects. */
2394 bool
2395 JSObject::swap(JSContext *cx, HandleObject a, HandleObject b)
2396 {
2397 AutoMarkInDeadZone adc1(a->zone());
2398 AutoMarkInDeadZone adc2(b->zone());
2400 // Ensure swap doesn't cause a finalizer to not be run.
2401 JS_ASSERT(IsBackgroundFinalized(a->tenuredGetAllocKind()) ==
2402 IsBackgroundFinalized(b->tenuredGetAllocKind()));
2403 JS_ASSERT(a->compartment() == b->compartment());
2405 unsigned r = NotifyGCPreSwap(a, b);
2407 TradeGutsReserved reserved(cx);
2408 if (!ReserveForTradeGuts(cx, a, b, reserved)) {
2409 NotifyGCPostSwap(b, a, r);
2410 return false;
2411 }
2412 TradeGuts(cx, a, b, reserved);
2414 NotifyGCPostSwap(a, b, r);
2415 return true;
2416 }
2418 static bool
2419 DefineStandardSlot(JSContext *cx, HandleObject obj, JSProtoKey key, JSAtom *atom,
2420 HandleValue v, uint32_t attrs, bool &named)
2421 {
2422 RootedId id(cx, AtomToId(atom));
2424 if (key != JSProto_Null) {
2425 /*
2426 * Initializing an actual standard class on a global object. If the
2427 * property is not yet present, force it into a new one bound to a
2428 * reserved slot. Otherwise, go through the normal property path.
2429 */
2430 JS_ASSERT(obj->is<GlobalObject>());
2431 JS_ASSERT(obj->isNative());
2433 if (!obj->nativeLookup(cx, id)) {
2434 obj->as<GlobalObject>().setConstructorPropertySlot(key, v);
2436 uint32_t slot = GlobalObject::constructorPropertySlot(key);
2437 if (!JSObject::addProperty(cx, obj, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0))
2438 return false;
2440 named = true;
2441 return true;
2442 }
2443 }
2445 named = JSObject::defineGeneric(cx, obj, id,
2446 v, JS_PropertyStub, JS_StrictPropertyStub, attrs);
2447 return named;
2448 }
2450 static void
2451 SetClassObject(JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto)
2452 {
2453 JS_ASSERT(!obj->getParent());
2454 if (!obj->is<GlobalObject>())
2455 return;
2457 obj->as<GlobalObject>().setConstructor(key, ObjectOrNullValue(cobj));
2458 obj->as<GlobalObject>().setPrototype(key, ObjectOrNullValue(proto));
2459 }
2461 static void
2462 ClearClassObject(JSObject *obj, JSProtoKey key)
2463 {
2464 JS_ASSERT(!obj->getParent());
2465 if (!obj->is<GlobalObject>())
2466 return;
2468 obj->as<GlobalObject>().setConstructor(key, UndefinedValue());
2469 obj->as<GlobalObject>().setPrototype(key, UndefinedValue());
2470 }
2472 static JSObject *
2473 DefineConstructorAndPrototype(JSContext *cx, HandleObject obj, JSProtoKey key, HandleAtom atom,
2474 JSObject *protoProto, const Class *clasp,
2475 Native constructor, unsigned nargs,
2476 const JSPropertySpec *ps, const JSFunctionSpec *fs,
2477 const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs,
2478 JSObject **ctorp, AllocKind ctorKind)
2479 {
2480 /*
2481 * Create a prototype object for this class.
2482 *
2483 * FIXME: lazy standard (built-in) class initialization and even older
2484 * eager boostrapping code rely on all of these properties:
2485 *
2486 * 1. NewObject attempting to compute a default prototype object when
2487 * passed null for proto; and
2488 *
2489 * 2. NewObject tolerating no default prototype (null proto slot value)
2490 * due to this js_InitClass call coming from js_InitFunctionClass on an
2491 * otherwise-uninitialized global.
2492 *
2493 * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
2494 * &JSFunction::class_, not a JSObject-sized (smaller) GC-thing.
2495 *
2496 * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
2497 * be &JSFunction::class_ (we could break compatibility easily). But
2498 * fixing (3) is not enough without addressing the bootstrapping dependency
2499 * on (1) and (2).
2500 */
2502 /*
2503 * Create the prototype object. (GlobalObject::createBlankPrototype isn't
2504 * used because it parents the prototype object to the global and because
2505 * it uses WithProto::Given. FIXME: Undo dependencies on this parentage
2506 * [which already needs to happen for bug 638316], figure out nicer
2507 * semantics for null-protoProto, and use createBlankPrototype.)
2508 */
2509 RootedObject proto(cx, NewObjectWithClassProto(cx, clasp, protoProto, obj, SingletonObject));
2510 if (!proto)
2511 return nullptr;
2513 /* After this point, control must exit via label bad or out. */
2514 RootedObject ctor(cx);
2515 bool named = false;
2516 bool cached = false;
2517 if (!constructor) {
2518 /*
2519 * Lacking a constructor, name the prototype (e.g., Math) unless this
2520 * class (a) is anonymous, i.e. for internal use only; (b) the class
2521 * of obj (the global object) is has a reserved slot indexed by key;
2522 * and (c) key is not the null key.
2523 */
2524 if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) || !obj->is<GlobalObject>() ||
2525 key == JSProto_Null)
2526 {
2527 uint32_t attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
2528 ? JSPROP_READONLY | JSPROP_PERMANENT
2529 : 0;
2530 RootedValue value(cx, ObjectValue(*proto));
2531 if (!DefineStandardSlot(cx, obj, key, atom, value, attrs, named))
2532 goto bad;
2533 }
2535 ctor = proto;
2536 } else {
2537 /*
2538 * Create the constructor, not using GlobalObject::createConstructor
2539 * because the constructor currently must have |obj| as its parent.
2540 * (FIXME: remove this dependency on the exact identity of the parent,
2541 * perhaps as part of bug 638316.)
2542 */
2543 RootedFunction fun(cx, NewFunction(cx, js::NullPtr(), constructor, nargs,
2544 JSFunction::NATIVE_CTOR, obj, atom, ctorKind));
2545 if (!fun)
2546 goto bad;
2548 /*
2549 * Set the class object early for standard class constructors. Type
2550 * inference may need to access these, and js::GetBuiltinPrototype will
2551 * fail if it tries to do a reentrant reconstruction of the class.
2552 */
2553 if (key != JSProto_Null) {
2554 SetClassObject(obj, key, fun, proto);
2555 cached = true;
2556 }
2558 RootedValue value(cx, ObjectValue(*fun));
2559 if (!DefineStandardSlot(cx, obj, key, atom, value, 0, named))
2560 goto bad;
2562 /*
2563 * Optionally construct the prototype object, before the class has
2564 * been fully initialized. Allow the ctor to replace proto with a
2565 * different object, as is done for operator new.
2566 */
2567 ctor = fun;
2568 if (!LinkConstructorAndPrototype(cx, ctor, proto))
2569 goto bad;
2571 /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
2572 Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
2573 if (ctor->getClass() == clasp && !ctor->splicePrototype(cx, clasp, tagged))
2574 goto bad;
2575 }
2577 if (!DefinePropertiesAndBrand(cx, proto, ps, fs) ||
2578 (ctor != proto && !DefinePropertiesAndBrand(cx, ctor, static_ps, static_fs)))
2579 {
2580 goto bad;
2581 }
2583 /* If this is a standard class, cache its prototype. */
2584 if (!cached && key != JSProto_Null)
2585 SetClassObject(obj, key, ctor, proto);
2587 if (ctorp)
2588 *ctorp = ctor;
2589 return proto;
2591 bad:
2592 if (named) {
2593 bool succeeded;
2594 JSObject::deleteByValue(cx, obj, StringValue(atom), &succeeded);
2595 }
2596 if (cached)
2597 ClearClassObject(obj, key);
2598 return nullptr;
2599 }
2601 JSObject *
2602 js_InitClass(JSContext *cx, HandleObject obj, JSObject *protoProto_,
2603 const Class *clasp, Native constructor, unsigned nargs,
2604 const JSPropertySpec *ps, const JSFunctionSpec *fs,
2605 const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs,
2606 JSObject **ctorp, AllocKind ctorKind)
2607 {
2608 RootedObject protoProto(cx, protoProto_);
2610 /* Assert mandatory function pointer members. */
2611 JS_ASSERT(clasp->addProperty);
2612 JS_ASSERT(clasp->delProperty);
2613 JS_ASSERT(clasp->getProperty);
2614 JS_ASSERT(clasp->setProperty);
2615 JS_ASSERT(clasp->enumerate);
2616 JS_ASSERT(clasp->resolve);
2617 JS_ASSERT(clasp->convert);
2619 RootedAtom atom(cx, Atomize(cx, clasp->name, strlen(clasp->name)));
2620 if (!atom)
2621 return nullptr;
2623 /*
2624 * All instances of the class will inherit properties from the prototype
2625 * object we are about to create (in DefineConstructorAndPrototype), which
2626 * in turn will inherit from protoProto.
2627 *
2628 * When initializing a standard class (other than Object), if protoProto is
2629 * null, default to Object.prototype. The engine's internal uses of
2630 * js_InitClass depend on this nicety.
2631 */
2632 JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
2633 if (key != JSProto_Null &&
2634 !protoProto &&
2635 !GetBuiltinPrototype(cx, JSProto_Object, &protoProto))
2636 {
2637 return nullptr;
2638 }
2640 return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
2641 ps, fs, static_ps, static_fs, ctorp, ctorKind);
2642 }
2644 /* static */ inline bool
2645 JSObject::updateSlotsForSpan(ThreadSafeContext *cx,
2646 HandleObject obj, size_t oldSpan, size_t newSpan)
2647 {
2648 JS_ASSERT(cx->isThreadLocal(obj));
2649 JS_ASSERT(oldSpan != newSpan);
2651 size_t oldCount = dynamicSlotsCount(obj->numFixedSlots(), oldSpan, obj->getClass());
2652 size_t newCount = dynamicSlotsCount(obj->numFixedSlots(), newSpan, obj->getClass());
2654 if (oldSpan < newSpan) {
2655 if (oldCount < newCount && !JSObject::growSlots(cx, obj, oldCount, newCount))
2656 return false;
2658 if (newSpan == oldSpan + 1)
2659 obj->initSlotUnchecked(oldSpan, UndefinedValue());
2660 else
2661 obj->initializeSlotRange(oldSpan, newSpan - oldSpan);
2662 } else {
2663 /* Trigger write barriers on the old slots before reallocating. */
2664 obj->prepareSlotRangeForOverwrite(newSpan, oldSpan);
2665 obj->invalidateSlotRange(newSpan, oldSpan - newSpan);
2667 if (oldCount > newCount)
2668 JSObject::shrinkSlots(cx, obj, oldCount, newCount);
2669 }
2671 return true;
2672 }
2674 /* static */ bool
2675 JSObject::setLastProperty(ThreadSafeContext *cx, HandleObject obj, HandleShape shape)
2676 {
2677 JS_ASSERT(cx->isThreadLocal(obj));
2678 JS_ASSERT(!obj->inDictionaryMode());
2679 JS_ASSERT(!shape->inDictionary());
2680 JS_ASSERT(shape->compartment() == obj->compartment());
2681 JS_ASSERT(shape->numFixedSlots() == obj->numFixedSlots());
2683 size_t oldSpan = obj->lastProperty()->slotSpan();
2684 size_t newSpan = shape->slotSpan();
2686 if (oldSpan == newSpan) {
2687 obj->shape_ = shape;
2688 return true;
2689 }
2691 if (!updateSlotsForSpan(cx, obj, oldSpan, newSpan))
2692 return false;
2694 obj->shape_ = shape;
2695 return true;
2696 }
2698 /* static */ bool
2699 JSObject::setSlotSpan(ThreadSafeContext *cx, HandleObject obj, uint32_t span)
2700 {
2701 JS_ASSERT(cx->isThreadLocal(obj));
2702 JS_ASSERT(obj->inDictionaryMode());
2704 size_t oldSpan = obj->lastProperty()->base()->slotSpan();
2705 if (oldSpan == span)
2706 return true;
2708 if (!JSObject::updateSlotsForSpan(cx, obj, oldSpan, span))
2709 return false;
2711 obj->lastProperty()->base()->setSlotSpan(span);
2712 return true;
2713 }
2715 static HeapSlot *
2716 AllocateSlots(ThreadSafeContext *cx, JSObject *obj, uint32_t nslots)
2717 {
2718 #ifdef JSGC_GENERATIONAL
2719 if (cx->isJSContext())
2720 return cx->asJSContext()->runtime()->gcNursery.allocateSlots(cx->asJSContext(), obj, nslots);
2721 #endif
2722 return cx->pod_malloc<HeapSlot>(nslots);
2723 }
2725 static HeapSlot *
2726 ReallocateSlots(ThreadSafeContext *cx, JSObject *obj, HeapSlot *oldSlots,
2727 uint32_t oldCount, uint32_t newCount)
2728 {
2729 #ifdef JSGC_GENERATIONAL
2730 if (cx->isJSContext()) {
2731 return cx->asJSContext()->runtime()->gcNursery.reallocateSlots(cx->asJSContext(),
2732 obj, oldSlots,
2733 oldCount, newCount);
2734 }
2735 #endif
2736 return (HeapSlot *)cx->realloc_(oldSlots, oldCount * sizeof(HeapSlot),
2737 newCount * sizeof(HeapSlot));
2738 }
2740 /* static */ bool
2741 JSObject::growSlots(ThreadSafeContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount)
2742 {
2743 JS_ASSERT(cx->isThreadLocal(obj));
2744 JS_ASSERT(newCount > oldCount);
2745 JS_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
2747 /*
2748 * Slot capacities are determined by the span of allocated objects. Due to
2749 * the limited number of bits to store shape slots, object growth is
2750 * throttled well before the slot capacity can overflow.
2751 */
2752 JS_ASSERT(newCount < NELEMENTS_LIMIT);
2754 /*
2755 * If we are allocating slots for an object whose type is always created
2756 * by calling 'new' on a particular script, bump the GC kind for that
2757 * type to give these objects a larger number of fixed slots when future
2758 * objects are constructed.
2759 */
2760 if (!obj->hasLazyType() && !oldCount && obj->type()->hasNewScript()) {
2761 JSObject *oldTemplate = obj->type()->newScript()->templateObject;
2762 gc::AllocKind kind = gc::GetGCObjectFixedSlotsKind(oldTemplate->numFixedSlots());
2763 uint32_t newScriptSlots = gc::GetGCKindSlots(kind);
2764 if (newScriptSlots == obj->numFixedSlots() &&
2765 gc::TryIncrementAllocKind(&kind) &&
2766 cx->isJSContext())
2767 {
2768 JSContext *ncx = cx->asJSContext();
2769 AutoEnterAnalysis enter(ncx);
2771 Rooted<TypeObject*> typeObj(cx, obj->type());
2772 RootedShape shape(cx, oldTemplate->lastProperty());
2773 JSObject *reshapedObj = NewReshapedObject(ncx, typeObj, obj->getParent(), kind, shape,
2774 MaybeSingletonObject);
2775 if (!reshapedObj)
2776 return false;
2778 typeObj->newScript()->templateObject = reshapedObj;
2779 typeObj->markStateChange(ncx);
2780 }
2781 }
2783 if (!oldCount) {
2784 obj->slots = AllocateSlots(cx, obj, newCount);
2785 if (!obj->slots)
2786 return false;
2787 Debug_SetSlotRangeToCrashOnTouch(obj->slots, newCount);
2788 return true;
2789 }
2791 HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount);
2792 if (!newslots)
2793 return false; /* Leave slots at its old size. */
2795 obj->slots = newslots;
2797 Debug_SetSlotRangeToCrashOnTouch(obj->slots + oldCount, newCount - oldCount);
2799 return true;
2800 }
2802 static void
2803 FreeSlots(ThreadSafeContext *cx, HeapSlot *slots)
2804 {
2805 // Note: threads without a JSContext do not have access to nursery allocated things.
2806 #ifdef JSGC_GENERATIONAL
2807 if (cx->isJSContext())
2808 return cx->asJSContext()->runtime()->gcNursery.freeSlots(cx->asJSContext(), slots);
2809 #endif
2810 js_free(slots);
2811 }
2813 /* static */ void
2814 JSObject::shrinkSlots(ThreadSafeContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount)
2815 {
2816 JS_ASSERT(cx->isThreadLocal(obj));
2817 JS_ASSERT(newCount < oldCount);
2819 if (newCount == 0) {
2820 FreeSlots(cx, obj->slots);
2821 obj->slots = nullptr;
2822 return;
2823 }
2825 JS_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
2827 HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount);
2828 if (!newslots)
2829 return; /* Leave slots at its old size. */
2831 obj->slots = newslots;
2832 }
2834 /* static */ bool
2835 JSObject::sparsifyDenseElement(ExclusiveContext *cx, HandleObject obj, uint32_t index)
2836 {
2837 RootedValue value(cx, obj->getDenseElement(index));
2838 JS_ASSERT(!value.isMagic(JS_ELEMENTS_HOLE));
2840 JSObject::removeDenseElementForSparseIndex(cx, obj, index);
2842 uint32_t slot = obj->slotSpan();
2843 if (!obj->addDataProperty(cx, INT_TO_JSID(index), slot, JSPROP_ENUMERATE)) {
2844 obj->setDenseElement(index, value);
2845 return false;
2846 }
2848 JS_ASSERT(slot == obj->slotSpan() - 1);
2849 obj->initSlot(slot, value);
2851 return true;
2852 }
2854 /* static */ bool
2855 JSObject::sparsifyDenseElements(js::ExclusiveContext *cx, HandleObject obj)
2856 {
2857 uint32_t initialized = obj->getDenseInitializedLength();
2859 /* Create new properties with the value of non-hole dense elements. */
2860 for (uint32_t i = 0; i < initialized; i++) {
2861 if (obj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE))
2862 continue;
2864 if (!sparsifyDenseElement(cx, obj, i))
2865 return false;
2866 }
2868 if (initialized)
2869 obj->setDenseInitializedLength(0);
2871 /*
2872 * Reduce storage for dense elements which are now holes. Explicitly mark
2873 * the elements capacity as zero, so that any attempts to add dense
2874 * elements will be caught in ensureDenseElements.
2875 */
2876 if (obj->getDenseCapacity()) {
2877 obj->shrinkElements(cx, 0);
2878 obj->getElementsHeader()->capacity = 0;
2879 }
2881 return true;
2882 }
2884 bool
2885 JSObject::willBeSparseElements(uint32_t requiredCapacity, uint32_t newElementsHint)
2886 {
2887 JS_ASSERT(isNative());
2888 JS_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
2890 uint32_t cap = getDenseCapacity();
2891 JS_ASSERT(requiredCapacity >= cap);
2893 if (requiredCapacity >= NELEMENTS_LIMIT)
2894 return true;
2896 uint32_t minimalDenseCount = requiredCapacity / SPARSE_DENSITY_RATIO;
2897 if (newElementsHint >= minimalDenseCount)
2898 return false;
2899 minimalDenseCount -= newElementsHint;
2901 if (minimalDenseCount > cap)
2902 return true;
2904 uint32_t len = getDenseInitializedLength();
2905 const Value *elems = getDenseElements();
2906 for (uint32_t i = 0; i < len; i++) {
2907 if (!elems[i].isMagic(JS_ELEMENTS_HOLE) && !--minimalDenseCount)
2908 return false;
2909 }
2910 return true;
2911 }
2913 /* static */ JSObject::EnsureDenseResult
2914 JSObject::maybeDensifySparseElements(js::ExclusiveContext *cx, HandleObject obj)
2915 {
2916 /*
2917 * Wait until after the object goes into dictionary mode, which must happen
2918 * when sparsely packing any array with more than MIN_SPARSE_INDEX elements
2919 * (see PropertyTree::MAX_HEIGHT).
2920 */
2921 if (!obj->inDictionaryMode())
2922 return ED_SPARSE;
2924 /*
2925 * Only measure the number of indexed properties every log(n) times when
2926 * populating the object.
2927 */
2928 uint32_t slotSpan = obj->slotSpan();
2929 if (slotSpan != RoundUpPow2(slotSpan))
2930 return ED_SPARSE;
2932 /* Watch for conditions under which an object's elements cannot be dense. */
2933 if (!obj->nonProxyIsExtensible() || obj->watched())
2934 return ED_SPARSE;
2936 /*
2937 * The indexes in the object need to be sufficiently dense before they can
2938 * be converted to dense mode.
2939 */
2940 uint32_t numDenseElements = 0;
2941 uint32_t newInitializedLength = 0;
2943 RootedShape shape(cx, obj->lastProperty());
2944 while (!shape->isEmptyShape()) {
2945 uint32_t index;
2946 if (js_IdIsIndex(shape->propid(), &index)) {
2947 if (shape->attributes() == JSPROP_ENUMERATE &&
2948 shape->hasDefaultGetter() &&
2949 shape->hasDefaultSetter())
2950 {
2951 numDenseElements++;
2952 newInitializedLength = Max(newInitializedLength, index + 1);
2953 } else {
2954 /*
2955 * For simplicity, only densify the object if all indexed
2956 * properties can be converted to dense elements.
2957 */
2958 return ED_SPARSE;
2959 }
2960 }
2961 shape = shape->previous();
2962 }
2964 if (numDenseElements * SPARSE_DENSITY_RATIO < newInitializedLength)
2965 return ED_SPARSE;
2967 if (newInitializedLength >= NELEMENTS_LIMIT)
2968 return ED_SPARSE;
2970 /*
2971 * This object meets all necessary restrictions, convert all indexed
2972 * properties into dense elements.
2973 */
2975 if (newInitializedLength > obj->getDenseCapacity()) {
2976 if (!obj->growElements(cx, newInitializedLength))
2977 return ED_FAILED;
2978 }
2980 obj->ensureDenseInitializedLength(cx, newInitializedLength, 0);
2982 RootedValue value(cx);
2984 shape = obj->lastProperty();
2985 while (!shape->isEmptyShape()) {
2986 jsid id = shape->propid();
2987 uint32_t index;
2988 if (js_IdIsIndex(id, &index)) {
2989 value = obj->getSlot(shape->slot());
2991 /*
2992 * When removing a property from a dictionary, the specified
2993 * property will be removed from the dictionary list and the
2994 * last property will then be changed due to reshaping the object.
2995 * Compute the next shape in the traverse, watching for such
2996 * removals from the list.
2997 */
2998 if (shape != obj->lastProperty()) {
2999 shape = shape->previous();
3000 if (!obj->removeProperty(cx, id))
3001 return ED_FAILED;
3002 } else {
3003 if (!obj->removeProperty(cx, id))
3004 return ED_FAILED;
3005 shape = obj->lastProperty();
3006 }
3008 obj->setDenseElement(index, value);
3009 } else {
3010 shape = shape->previous();
3011 }
3012 }
3014 /*
3015 * All indexed properties on the object are now dense, clear the indexed
3016 * flag so that we will not start using sparse indexes again if we need
3017 * to grow the object.
3018 */
3019 if (!obj->clearFlag(cx, BaseShape::INDEXED))
3020 return ED_FAILED;
3022 return ED_OK;
3023 }
3025 static ObjectElements *
3026 AllocateElements(ThreadSafeContext *cx, JSObject *obj, uint32_t nelems)
3027 {
3028 #ifdef JSGC_GENERATIONAL
3029 if (cx->isJSContext())
3030 return cx->asJSContext()->runtime()->gcNursery.allocateElements(cx->asJSContext(), obj, nelems);
3031 #endif
3033 return static_cast<js::ObjectElements *>(cx->malloc_(nelems * sizeof(HeapValue)));
3034 }
3036 static ObjectElements *
3037 ReallocateElements(ThreadSafeContext *cx, JSObject *obj, ObjectElements *oldHeader,
3038 uint32_t oldCount, uint32_t newCount)
3039 {
3040 #ifdef JSGC_GENERATIONAL
3041 if (cx->isJSContext()) {
3042 return cx->asJSContext()->runtime()->gcNursery.reallocateElements(cx->asJSContext(), obj,
3043 oldHeader, oldCount,
3044 newCount);
3045 }
3046 #endif
3048 return static_cast<js::ObjectElements *>(cx->realloc_(oldHeader, oldCount * sizeof(HeapSlot),
3049 newCount * sizeof(HeapSlot)));
3050 }
3052 bool
3053 JSObject::growElements(ThreadSafeContext *cx, uint32_t newcap)
3054 {
3055 JS_ASSERT(nonProxyIsExtensible());
3056 JS_ASSERT(canHaveNonEmptyElements());
3058 /*
3059 * When an object with CAPACITY_DOUBLING_MAX or fewer elements needs to
3060 * grow, double its capacity, to add N elements in amortized O(N) time.
3061 *
3062 * Above this limit, grow by 12.5% each time. Speed is still amortized
3063 * O(N), with a higher constant factor, and we waste less space.
3064 */
3065 static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024;
3066 static const size_t CAPACITY_CHUNK = CAPACITY_DOUBLING_MAX / sizeof(Value);
3068 uint32_t oldcap = getDenseCapacity();
3069 JS_ASSERT(oldcap <= newcap);
3071 uint32_t nextsize = (oldcap <= CAPACITY_DOUBLING_MAX)
3072 ? oldcap * 2
3073 : oldcap + (oldcap >> 3);
3075 uint32_t actualCapacity;
3076 if (is<ArrayObject>() && !as<ArrayObject>().lengthIsWritable()) {
3077 JS_ASSERT(newcap <= as<ArrayObject>().length());
3078 // Preserve the |capacity <= length| invariant for arrays with
3079 // non-writable length. See also js::ArraySetLength which initially
3080 // enforces this requirement.
3081 actualCapacity = newcap;
3082 } else {
3083 actualCapacity = Max(newcap, nextsize);
3084 if (actualCapacity >= CAPACITY_CHUNK)
3085 actualCapacity = JS_ROUNDUP(actualCapacity, CAPACITY_CHUNK);
3086 else if (actualCapacity < SLOT_CAPACITY_MIN)
3087 actualCapacity = SLOT_CAPACITY_MIN;
3089 /* Don't let nelements get close to wrapping around uint32_t. */
3090 if (actualCapacity >= NELEMENTS_LIMIT || actualCapacity < oldcap || actualCapacity < newcap)
3091 return false;
3092 }
3094 uint32_t initlen = getDenseInitializedLength();
3095 uint32_t oldAllocated = oldcap + ObjectElements::VALUES_PER_HEADER;
3096 uint32_t newAllocated = actualCapacity + ObjectElements::VALUES_PER_HEADER;
3098 ObjectElements *newheader;
3099 if (hasDynamicElements()) {
3100 newheader = ReallocateElements(cx, this, getElementsHeader(), oldAllocated, newAllocated);
3101 if (!newheader)
3102 return false; /* Leave elements as its old size. */
3103 } else {
3104 newheader = AllocateElements(cx, this, newAllocated);
3105 if (!newheader)
3106 return false; /* Leave elements as its old size. */
3107 js_memcpy(newheader, getElementsHeader(),
3108 (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
3109 }
3111 newheader->capacity = actualCapacity;
3112 elements = newheader->elements();
3114 Debug_SetSlotRangeToCrashOnTouch(elements + initlen, actualCapacity - initlen);
3116 return true;
3117 }
3119 void
3120 JSObject::shrinkElements(ThreadSafeContext *cx, uint32_t newcap)
3121 {
3122 JS_ASSERT(cx->isThreadLocal(this));
3123 JS_ASSERT(canHaveNonEmptyElements());
3125 uint32_t oldcap = getDenseCapacity();
3126 JS_ASSERT(newcap <= oldcap);
3128 // Don't shrink elements below the minimum capacity.
3129 if (oldcap <= SLOT_CAPACITY_MIN || !hasDynamicElements())
3130 return;
3132 newcap = Max(newcap, SLOT_CAPACITY_MIN);
3134 uint32_t oldAllocated = oldcap + ObjectElements::VALUES_PER_HEADER;
3135 uint32_t newAllocated = newcap + ObjectElements::VALUES_PER_HEADER;
3137 ObjectElements *newheader = ReallocateElements(cx, this, getElementsHeader(),
3138 oldAllocated, newAllocated);
3139 if (!newheader) {
3140 cx->recoverFromOutOfMemory();
3141 return; // Leave elements at its old size.
3142 }
3144 newheader->capacity = newcap;
3145 elements = newheader->elements();
3146 }
3148 bool
3149 js::SetClassAndProto(JSContext *cx, HandleObject obj,
3150 const Class *clasp, Handle<js::TaggedProto> proto,
3151 bool *succeeded)
3152 {
3153 /*
3154 * Regenerate shapes for all of the scopes along the old prototype chain,
3155 * in case any entries were filled by looking up through obj. Stop when a
3156 * non-native object is found, prototype lookups will not be cached across
3157 * these.
3158 *
3159 * How this shape change is done is very delicate; the change can be made
3160 * either by marking the object's prototype as uncacheable (such that the
3161 * property cache and JIT'ed ICs cannot assume the shape determines the
3162 * prototype) or by just generating a new shape for the object. Choosing
3163 * the former is bad if the object is on the prototype chain of other
3164 * objects, as the uncacheable prototype can inhibit iterator caches on
3165 * those objects and slow down prototype accesses. Choosing the latter is
3166 * bad if there are many similar objects to this one which will have their
3167 * prototype mutated, as the generateOwnShape forces the object into
3168 * dictionary mode and similar property lineages will be repeatedly cloned.
3169 *
3170 * :XXX: bug 707717 make this code less brittle.
3171 */
3172 *succeeded = false;
3173 RootedObject oldproto(cx, obj);
3174 while (oldproto && oldproto->isNative()) {
3175 if (oldproto->hasSingletonType()) {
3176 if (!oldproto->generateOwnShape(cx))
3177 return false;
3178 } else {
3179 if (!oldproto->setUncacheableProto(cx))
3180 return false;
3181 }
3182 oldproto = oldproto->getProto();
3183 }
3185 if (obj->hasSingletonType()) {
3186 /*
3187 * Just splice the prototype, but mark the properties as unknown for
3188 * consistent behavior.
3189 */
3190 if (!obj->splicePrototype(cx, clasp, proto))
3191 return false;
3192 MarkTypeObjectUnknownProperties(cx, obj->type());
3193 *succeeded = true;
3194 return true;
3195 }
3197 if (proto.isObject()) {
3198 RootedObject protoObj(cx, proto.toObject());
3199 if (!JSObject::setNewTypeUnknown(cx, clasp, protoObj))
3200 return false;
3201 }
3203 TypeObject *type = cx->getNewType(clasp, proto);
3204 if (!type)
3205 return false;
3207 /*
3208 * Setting __proto__ on an object that has escaped and may be referenced by
3209 * other heap objects can only be done if the properties of both objects
3210 * are unknown. Type sets containing this object will contain the original
3211 * type but not the new type of the object, so we need to go and scan the
3212 * entire compartment for type sets which have these objects and mark them
3213 * as containing generic objects.
3214 */
3215 MarkTypeObjectUnknownProperties(cx, obj->type(), true);
3216 MarkTypeObjectUnknownProperties(cx, type, true);
3218 obj->setType(type);
3220 *succeeded = true;
3221 return true;
3222 }
3224 static bool
3225 MaybeResolveConstructor(ExclusiveContext *cxArg, Handle<GlobalObject*> global, JSProtoKey key)
3226 {
3227 if (global->isStandardClassResolved(key))
3228 return true;
3229 if (!cxArg->shouldBeJSContext())
3230 return false;
3232 JSContext *cx = cxArg->asJSContext();
3233 return GlobalObject::resolveConstructor(cx, global, key);
3234 }
3236 bool
3237 js::GetBuiltinConstructor(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject objp)
3238 {
3239 MOZ_ASSERT(key != JSProto_Null);
3240 Rooted<GlobalObject*> global(cx, cx->global());
3241 if (!MaybeResolveConstructor(cx, global, key))
3242 return false;
3244 objp.set(&global->getConstructor(key).toObject());
3245 return true;
3246 }
3248 bool
3249 js::GetBuiltinPrototype(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject protop)
3250 {
3251 MOZ_ASSERT(key != JSProto_Null);
3252 Rooted<GlobalObject*> global(cx, cx->global());
3253 if (!MaybeResolveConstructor(cx, global, key))
3254 return false;
3256 protop.set(&global->getPrototype(key).toObject());
3257 return true;
3258 }
3260 static bool
3261 IsStandardPrototype(JSObject *obj, JSProtoKey key)
3262 {
3263 GlobalObject &global = obj->global();
3264 Value v = global.getPrototype(key);
3265 return v.isObject() && obj == &v.toObject();
3266 }
3268 JSProtoKey
3269 JS::IdentifyStandardInstance(JSObject *obj)
3270 {
3271 // Note: The prototype shares its JSClass with instances.
3272 JS_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
3273 JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
3274 if (key != JSProto_Null && !IsStandardPrototype(obj, key))
3275 return key;
3276 return JSProto_Null;
3277 }
3279 JSProtoKey
3280 JS::IdentifyStandardPrototype(JSObject *obj)
3281 {
3282 // Note: The prototype shares its JSClass with instances.
3283 JS_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
3284 JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
3285 if (key != JSProto_Null && IsStandardPrototype(obj, key))
3286 return key;
3287 return JSProto_Null;
3288 }
3290 JSProtoKey
3291 JS::IdentifyStandardInstanceOrPrototype(JSObject *obj)
3292 {
3293 return JSCLASS_CACHED_PROTO_KEY(obj->getClass());
3294 }
3296 bool
3297 js::FindClassObject(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp)
3298 {
3299 JSProtoKey protoKey = GetClassProtoKey(clasp);
3300 if (protoKey != JSProto_Null) {
3301 JS_ASSERT(JSProto_Null < protoKey);
3302 JS_ASSERT(protoKey < JSProto_LIMIT);
3303 return GetBuiltinConstructor(cx, protoKey, protop);
3304 }
3306 JSAtom *atom = Atomize(cx, clasp->name, strlen(clasp->name));
3307 if (!atom)
3308 return false;
3309 RootedId id(cx, AtomToId(atom));
3311 RootedObject pobj(cx);
3312 RootedShape shape(cx);
3313 if (!LookupNativeProperty(cx, cx->global(), id, &pobj, &shape))
3314 return false;
3315 RootedValue v(cx);
3316 if (shape && pobj->isNative()) {
3317 if (shape->hasSlot())
3318 v = pobj->nativeGetSlot(shape->slot());
3319 }
3320 if (v.isObject())
3321 protop.set(&v.toObject());
3322 return true;
3323 }
3325 /* static */ bool
3326 JSObject::allocSlot(ThreadSafeContext *cx, HandleObject obj, uint32_t *slotp)
3327 {
3328 JS_ASSERT(cx->isThreadLocal(obj));
3330 uint32_t slot = obj->slotSpan();
3331 JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
3333 /*
3334 * If this object is in dictionary mode, try to pull a free slot from the
3335 * shape table's slot-number freelist.
3336 */
3337 if (obj->inDictionaryMode()) {
3338 ShapeTable &table = obj->lastProperty()->table();
3339 uint32_t last = table.freelist;
3340 if (last != SHAPE_INVALID_SLOT) {
3341 #ifdef DEBUG
3342 JS_ASSERT(last < slot);
3343 uint32_t next = obj->getSlot(last).toPrivateUint32();
3344 JS_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot);
3345 #endif
3347 *slotp = last;
3349 const Value &vref = obj->getSlot(last);
3350 table.freelist = vref.toPrivateUint32();
3351 obj->setSlot(last, UndefinedValue());
3352 return true;
3353 }
3354 }
3356 if (slot >= SHAPE_MAXIMUM_SLOT) {
3357 js_ReportOutOfMemory(cx);
3358 return false;
3359 }
3361 *slotp = slot;
3363 if (obj->inDictionaryMode() && !setSlotSpan(cx, obj, slot + 1))
3364 return false;
3366 return true;
3367 }
3369 void
3370 JSObject::freeSlot(uint32_t slot)
3371 {
3372 JS_ASSERT(slot < slotSpan());
3374 if (inDictionaryMode()) {
3375 uint32_t &last = lastProperty()->table().freelist;
3377 /* Can't afford to check the whole freelist, but let's check the head. */
3378 JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan() && last != slot);
3380 /*
3381 * Place all freed slots other than reserved slots (bug 595230) on the
3382 * dictionary's free list.
3383 */
3384 if (JSSLOT_FREE(getClass()) <= slot) {
3385 JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan());
3386 setSlot(slot, PrivateUint32Value(last));
3387 last = slot;
3388 return;
3389 }
3390 }
3391 setSlot(slot, UndefinedValue());
3392 }
3394 static bool
3395 PurgeProtoChain(ExclusiveContext *cx, JSObject *objArg, HandleId id)
3396 {
3397 /* Root locally so we can re-assign. */
3398 RootedObject obj(cx, objArg);
3400 RootedShape shape(cx);
3401 while (obj) {
3402 /* Lookups will not be cached through non-native protos. */
3403 if (!obj->isNative())
3404 break;
3406 shape = obj->nativeLookup(cx, id);
3407 if (shape) {
3408 if (!obj->shadowingShapeChange(cx, *shape))
3409 return false;
3411 obj->shadowingShapeChange(cx, *shape);
3412 return true;
3413 }
3414 obj = obj->getProto();
3415 }
3417 return true;
3418 }
3420 static bool
3421 PurgeScopeChainHelper(ExclusiveContext *cx, HandleObject objArg, HandleId id)
3422 {
3423 /* Re-root locally so we can re-assign. */
3424 RootedObject obj(cx, objArg);
3426 JS_ASSERT(obj->isNative());
3427 JS_ASSERT(obj->isDelegate());
3429 /* Lookups on integer ids cannot be cached through prototypes. */
3430 if (JSID_IS_INT(id))
3431 return true;
3433 PurgeProtoChain(cx, obj->getProto(), id);
3435 /*
3436 * We must purge the scope chain only for Call objects as they are the only
3437 * kind of cacheable non-global object that can gain properties after outer
3438 * properties with the same names have been cached or traced. Call objects
3439 * may gain such properties via eval introducing new vars; see bug 490364.
3440 */
3441 if (obj->is<CallObject>()) {
3442 while ((obj = obj->enclosingScope()) != nullptr) {
3443 if (!PurgeProtoChain(cx, obj, id))
3444 return false;
3445 }
3446 }
3448 return true;
3449 }
3451 /*
3452 * PurgeScopeChain does nothing if obj is not itself a prototype or parent
3453 * scope, else it reshapes the scope and prototype chains it links. It calls
3454 * PurgeScopeChainHelper, which asserts that obj is flagged as a delegate
3455 * (i.e., obj has ever been on a prototype or parent chain).
3456 */
3457 static inline bool
3458 PurgeScopeChain(ExclusiveContext *cx, JS::HandleObject obj, JS::HandleId id)
3459 {
3460 if (obj->isDelegate())
3461 return PurgeScopeChainHelper(cx, obj, id);
3462 return true;
3463 }
3465 bool
3466 baseops::DefineGeneric(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value,
3467 PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
3468 {
3469 return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
3470 }
3472 /* static */ bool
3473 JSObject::defineGeneric(ExclusiveContext *cx, HandleObject obj,
3474 HandleId id, HandleValue value,
3475 JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
3476 {
3477 JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
3478 js::DefineGenericOp op = obj->getOps()->defineGeneric;
3479 if (op) {
3480 if (!cx->shouldBeJSContext())
3481 return false;
3482 return op(cx->asJSContext(), obj, id, value, getter, setter, attrs);
3483 }
3484 return baseops::DefineGeneric(cx, obj, id, value, getter, setter, attrs);
3485 }
3487 /* static */ bool
3488 JSObject::defineProperty(ExclusiveContext *cx, HandleObject obj,
3489 PropertyName *name, HandleValue value,
3490 JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
3491 {
3492 RootedId id(cx, NameToId(name));
3493 return defineGeneric(cx, obj, id, value, getter, setter, attrs);
3494 }
3496 bool
3497 baseops::DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, HandleValue value,
3498 PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
3499 {
3500 RootedId id(cx);
3501 if (index <= JSID_INT_MAX) {
3502 id = INT_TO_JSID(index);
3503 return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
3504 }
3506 AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
3508 if (!IndexToId(cx, index, &id))
3509 return false;
3511 return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
3512 }
3514 /* static */ bool
3515 JSObject::defineElement(ExclusiveContext *cx, HandleObject obj,
3516 uint32_t index, HandleValue value,
3517 JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
3518 {
3519 js::DefineElementOp op = obj->getOps()->defineElement;
3520 if (op) {
3521 if (!cx->shouldBeJSContext())
3522 return false;
3523 return op(cx->asJSContext(), obj, index, value, getter, setter, attrs);
3524 }
3525 return baseops::DefineElement(cx, obj, index, value, getter, setter, attrs);
3526 }
3528 Shape *
3529 JSObject::addDataProperty(ExclusiveContext *cx, jsid idArg, uint32_t slot, unsigned attrs)
3530 {
3531 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
3532 RootedObject self(cx, this);
3533 RootedId id(cx, idArg);
3534 return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
3535 }
3537 Shape *
3538 JSObject::addDataProperty(ExclusiveContext *cx, HandlePropertyName name,
3539 uint32_t slot, unsigned attrs)
3540 {
3541 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
3542 RootedObject self(cx, this);
3543 RootedId id(cx, NameToId(name));
3544 return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
3545 }
3547 /*
3548 * Backward compatibility requires allowing addProperty hooks to mutate the
3549 * nominal initial value of a slotful property, while GC safety wants that
3550 * value to be stored before the call-out through the hook. Optimize to do
3551 * both while saving cycles for classes that stub their addProperty hook.
3552 */
3553 template <ExecutionMode mode>
3554 static inline bool
3555 CallAddPropertyHook(typename ExecutionModeTraits<mode>::ExclusiveContextType cxArg,
3556 const Class *clasp, HandleObject obj, HandleShape shape,
3557 HandleValue nominal)
3558 {
3559 if (clasp->addProperty != JS_PropertyStub) {
3560 if (mode == ParallelExecution)
3561 return false;
3563 ExclusiveContext *cx = cxArg->asExclusiveContext();
3564 if (!cx->shouldBeJSContext())
3565 return false;
3567 /* Make a local copy of value so addProperty can mutate its inout parameter. */
3568 RootedValue value(cx, nominal);
3570 Rooted<jsid> id(cx, shape->propid());
3571 if (!CallJSPropertyOp(cx->asJSContext(), clasp->addProperty, obj, id, &value)) {
3572 obj->removeProperty(cx, shape->propid());
3573 return false;
3574 }
3575 if (value.get() != nominal) {
3576 if (shape->hasSlot())
3577 obj->nativeSetSlotWithType(cx, shape, value);
3578 }
3579 }
3580 return true;
3581 }
3583 template <ExecutionMode mode>
3584 static inline bool
3585 CallAddPropertyHookDense(typename ExecutionModeTraits<mode>::ExclusiveContextType cxArg,
3586 const Class *clasp, HandleObject obj, uint32_t index,
3587 HandleValue nominal)
3588 {
3589 /* Inline addProperty for array objects. */
3590 if (obj->is<ArrayObject>()) {
3591 ArrayObject *arr = &obj->as<ArrayObject>();
3592 uint32_t length = arr->length();
3593 if (index >= length) {
3594 if (mode == ParallelExecution) {
3595 /* We cannot deal with overflows in parallel. */
3596 if (length > INT32_MAX)
3597 return false;
3598 arr->setLengthInt32(index + 1);
3599 } else {
3600 arr->setLength(cxArg->asExclusiveContext(), index + 1);
3601 }
3602 }
3603 return true;
3604 }
3606 if (clasp->addProperty != JS_PropertyStub) {
3607 if (mode == ParallelExecution)
3608 return false;
3610 ExclusiveContext *cx = cxArg->asExclusiveContext();
3611 if (!cx->shouldBeJSContext())
3612 return false;
3614 /* Make a local copy of value so addProperty can mutate its inout parameter. */
3615 RootedValue value(cx, nominal);
3617 Rooted<jsid> id(cx, INT_TO_JSID(index));
3618 if (!CallJSPropertyOp(cx->asJSContext(), clasp->addProperty, obj, id, &value)) {
3619 obj->setDenseElementHole(cx, index);
3620 return false;
3621 }
3622 if (value.get() != nominal)
3623 obj->setDenseElementWithType(cx, index, value);
3624 }
3626 return true;
3627 }
3629 template <ExecutionMode mode>
3630 static bool
3631 UpdateShapeTypeAndValue(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
3632 JSObject *obj, Shape *shape, const Value &value)
3633 {
3634 jsid id = shape->propid();
3635 if (shape->hasSlot()) {
3636 if (mode == ParallelExecution) {
3637 if (!obj->nativeSetSlotIfHasType(shape, value))
3638 return false;
3639 } else {
3640 obj->nativeSetSlotWithType(cx->asExclusiveContext(), shape, value);
3641 }
3642 }
3643 if (!shape->hasSlot() || !shape->hasDefaultGetter() || !shape->hasDefaultSetter()) {
3644 if (mode == ParallelExecution) {
3645 if (!IsTypePropertyIdMarkedNonData(obj, id))
3646 return false;
3647 } else {
3648 MarkTypePropertyNonData(cx->asExclusiveContext(), obj, id);
3649 }
3650 }
3651 if (!shape->writable()) {
3652 if (mode == ParallelExecution) {
3653 if (!IsTypePropertyIdMarkedNonWritable(obj, id))
3654 return false;
3655 } else {
3656 MarkTypePropertyNonWritable(cx->asExclusiveContext(), obj, id);
3657 }
3658 }
3659 return true;
3660 }
3662 template <ExecutionMode mode>
3663 static inline bool
3664 DefinePropertyOrElement(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
3665 HandleObject obj, HandleId id,
3666 PropertyOp getter, StrictPropertyOp setter,
3667 unsigned attrs, HandleValue value,
3668 bool callSetterAfterwards, bool setterIsStrict)
3669 {
3670 /* Use dense storage for new indexed properties where possible. */
3671 if (JSID_IS_INT(id) &&
3672 getter == JS_PropertyStub &&
3673 setter == JS_StrictPropertyStub &&
3674 attrs == JSPROP_ENUMERATE &&
3675 (!obj->isIndexed() || !obj->nativeContainsPure(id)) &&
3676 !obj->is<TypedArrayObject>())
3677 {
3678 uint32_t index = JSID_TO_INT(id);
3679 bool definesPast;
3680 if (!WouldDefinePastNonwritableLength(cx, obj, index, setterIsStrict, &definesPast))
3681 return false;
3682 if (definesPast)
3683 return true;
3685 JSObject::EnsureDenseResult result;
3686 if (mode == ParallelExecution) {
3687 if (obj->writeToIndexWouldMarkNotPacked(index))
3688 return false;
3689 result = obj->ensureDenseElementsPreservePackedFlag(cx, index, 1);
3690 } else {
3691 result = obj->ensureDenseElements(cx->asExclusiveContext(), index, 1);
3692 }
3694 if (result == JSObject::ED_FAILED)
3695 return false;
3696 if (result == JSObject::ED_OK) {
3697 if (mode == ParallelExecution) {
3698 if (!obj->setDenseElementIfHasType(index, value))
3699 return false;
3700 } else {
3701 obj->setDenseElementWithType(cx->asExclusiveContext(), index, value);
3702 }
3703 return CallAddPropertyHookDense<mode>(cx, obj->getClass(), obj, index, value);
3704 }
3705 }
3707 if (obj->is<ArrayObject>()) {
3708 Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
3709 if (id == NameToId(cx->names().length)) {
3710 if (mode == SequentialExecution && !cx->shouldBeJSContext())
3711 return false;
3712 return ArraySetLength<mode>(ExecutionModeTraits<mode>::toContextType(cx), arr, id,
3713 attrs, value, setterIsStrict);
3714 }
3716 uint32_t index;
3717 if (js_IdIsIndex(id, &index)) {
3718 bool definesPast;
3719 if (!WouldDefinePastNonwritableLength(cx, arr, index, setterIsStrict, &definesPast))
3720 return false;
3721 if (definesPast)
3722 return true;
3723 }
3724 }
3726 // Don't define new indexed properties on typed arrays.
3727 if (obj->is<TypedArrayObject>()) {
3728 uint64_t index;
3729 if (IsTypedArrayIndex(id, &index))
3730 return true;
3731 }
3733 AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
3735 RootedShape shape(cx, JSObject::putProperty<mode>(cx, obj, id, getter, setter,
3736 SHAPE_INVALID_SLOT, attrs, 0));
3737 if (!shape)
3738 return false;
3740 if (!UpdateShapeTypeAndValue<mode>(cx, obj, shape, value))
3741 return false;
3743 /*
3744 * Clear any existing dense index after adding a sparse indexed property,
3745 * and investigate converting the object to dense indexes.
3746 */
3747 if (JSID_IS_INT(id)) {
3748 if (mode == ParallelExecution)
3749 return false;
3751 ExclusiveContext *ncx = cx->asExclusiveContext();
3752 uint32_t index = JSID_TO_INT(id);
3753 JSObject::removeDenseElementForSparseIndex(ncx, obj, index);
3754 JSObject::EnsureDenseResult result = JSObject::maybeDensifySparseElements(ncx, obj);
3755 if (result == JSObject::ED_FAILED)
3756 return false;
3757 if (result == JSObject::ED_OK) {
3758 JS_ASSERT(setter == JS_StrictPropertyStub);
3759 return CallAddPropertyHookDense<mode>(cx, obj->getClass(), obj, index, value);
3760 }
3761 }
3763 if (!CallAddPropertyHook<mode>(cx, obj->getClass(), obj, shape, value))
3764 return false;
3766 if (callSetterAfterwards && setter != JS_StrictPropertyStub) {
3767 if (!cx->shouldBeJSContext())
3768 return false;
3769 RootedValue nvalue(cx, value);
3770 return NativeSet<mode>(ExecutionModeTraits<mode>::toContextType(cx),
3771 obj, obj, shape, setterIsStrict, &nvalue);
3772 }
3773 return true;
3774 }
3776 static bool
3777 NativeLookupOwnProperty(ExclusiveContext *cx, HandleObject obj, HandleId id,
3778 MutableHandle<Shape*> shapep);
3780 bool
3781 js::DefineNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value,
3782 PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
3783 {
3784 JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
3786 AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
3788 /*
3789 * If defining a getter or setter, we must check for its counterpart and
3790 * update the attributes and property ops. A getter or setter is really
3791 * only half of a property.
3792 */
3793 RootedShape shape(cx);
3794 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
3795 /*
3796 * If we are defining a getter whose setter was already defined, or
3797 * vice versa, finish the job via obj->changeProperty.
3798 */
3799 if (!NativeLookupOwnProperty(cx, obj, id, &shape))
3800 return false;
3801 if (shape) {
3802 if (IsImplicitDenseOrTypedArrayElement(shape)) {
3803 if (obj->is<TypedArrayObject>()) {
3804 /* Ignore getter/setter properties added to typed arrays. */
3805 return true;
3806 }
3807 if (!JSObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
3808 return false;
3809 shape = obj->nativeLookup(cx, id);
3810 }
3811 if (shape->isAccessorDescriptor()) {
3812 shape = JSObject::changeProperty<SequentialExecution>(cx, obj, shape, attrs,
3813 JSPROP_GETTER | JSPROP_SETTER,
3814 (attrs & JSPROP_GETTER)
3815 ? getter
3816 : shape->getter(),
3817 (attrs & JSPROP_SETTER)
3818 ? setter
3819 : shape->setter());
3820 if (!shape)
3821 return false;
3822 } else {
3823 shape = nullptr;
3824 }
3825 }
3826 }
3828 /*
3829 * Purge the property cache of any properties named by id that are about
3830 * to be shadowed in obj's scope chain.
3831 */
3832 if (!PurgeScopeChain(cx, obj, id))
3833 return false;
3835 /* Use the object's class getter and setter by default. */
3836 const Class *clasp = obj->getClass();
3837 if (!getter && !(attrs & JSPROP_GETTER))
3838 getter = clasp->getProperty;
3839 if (!setter && !(attrs & JSPROP_SETTER))
3840 setter = clasp->setProperty;
3842 if (!shape) {
3843 return DefinePropertyOrElement<SequentialExecution>(cx, obj, id, getter, setter,
3844 attrs, value, false, false);
3845 }
3847 JS_ALWAYS_TRUE(UpdateShapeTypeAndValue<SequentialExecution>(cx, obj, shape, value));
3849 return CallAddPropertyHook<SequentialExecution>(cx, clasp, obj, shape, value);
3850 }
3852 /*
3853 * Call obj's resolve hook.
3854 *
3855 * cx, id, and flags are the parameters initially passed to the ongoing lookup;
3856 * objp and propp are its out parameters. obj is an object along the prototype
3857 * chain from where the lookup started.
3858 *
3859 * There are four possible outcomes:
3860 *
3861 * - On failure, report an error or exception and return false.
3862 *
3863 * - If we are already resolving a property of *curobjp, set *recursedp = true,
3864 * and return true.
3865 *
3866 * - If the resolve hook finds or defines the sought property, set *objp and
3867 * *propp appropriately, set *recursedp = false, and return true.
3868 *
3869 * - Otherwise no property was resolved. Set *propp = nullptr and
3870 * *recursedp = false and return true.
3871 */
3872 static MOZ_ALWAYS_INLINE bool
3873 CallResolveOp(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp,
3874 MutableHandleShape propp, bool *recursedp)
3875 {
3876 const Class *clasp = obj->getClass();
3877 JSResolveOp resolve = clasp->resolve;
3879 /*
3880 * Avoid recursion on (obj, id) already being resolved on cx.
3881 *
3882 * Once we have successfully added an entry for (obj, key) to
3883 * cx->resolvingTable, control must go through cleanup: before
3884 * returning. But note that JS_DHASH_ADD may find an existing
3885 * entry, in which case we bail to suppress runaway recursion.
3886 */
3887 AutoResolving resolving(cx, obj, id);
3888 if (resolving.alreadyStarted()) {
3889 /* Already resolving id in obj -- suppress recursion. */
3890 *recursedp = true;
3891 return true;
3892 }
3893 *recursedp = false;
3895 propp.set(nullptr);
3897 if (clasp->flags & JSCLASS_NEW_RESOLVE) {
3898 JSNewResolveOp newresolve = reinterpret_cast<JSNewResolveOp>(resolve);
3899 RootedObject obj2(cx, nullptr);
3900 if (!newresolve(cx, obj, id, &obj2))
3901 return false;
3903 /*
3904 * We trust the new style resolve hook to set obj2 to nullptr when
3905 * the id cannot be resolved. But, when obj2 is not null, we do
3906 * not assume that id must exist and do full nativeLookup for
3907 * compatibility.
3908 */
3909 if (!obj2)
3910 return true;
3912 if (!obj2->isNative()) {
3913 /* Whoops, newresolve handed back a foreign obj2. */
3914 JS_ASSERT(obj2 != obj);
3915 return JSObject::lookupGeneric(cx, obj2, id, objp, propp);
3916 }
3918 objp.set(obj2);
3919 } else {
3920 if (!resolve(cx, obj, id))
3921 return false;
3923 objp.set(obj);
3924 }
3926 if (JSID_IS_INT(id) && objp->containsDenseElement(JSID_TO_INT(id))) {
3927 MarkDenseOrTypedArrayElementFound<CanGC>(propp);
3928 return true;
3929 }
3931 Shape *shape;
3932 if (!objp->nativeEmpty() && (shape = objp->nativeLookup(cx, id)))
3933 propp.set(shape);
3934 else
3935 objp.set(nullptr);
3937 return true;
3938 }
3940 template <AllowGC allowGC>
3941 static MOZ_ALWAYS_INLINE bool
3942 LookupOwnPropertyInline(ExclusiveContext *cx,
3943 typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
3944 typename MaybeRooted<jsid, allowGC>::HandleType id,
3945 typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
3946 typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp,
3947 bool *donep)
3948 {
3949 // Check for a native dense element.
3950 if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
3951 objp.set(obj);
3952 MarkDenseOrTypedArrayElementFound<allowGC>(propp);
3953 *donep = true;
3954 return true;
3955 }
3957 // Check for a typed array element. Integer lookups always finish here
3958 // so that integer properties on the prototype are ignored even for out
3959 // of bounds accesses.
3960 if (obj->template is<TypedArrayObject>()) {
3961 uint64_t index;
3962 if (IsTypedArrayIndex(id, &index)) {
3963 if (index < obj->template as<TypedArrayObject>().length()) {
3964 objp.set(obj);
3965 MarkDenseOrTypedArrayElementFound<allowGC>(propp);
3966 } else {
3967 objp.set(nullptr);
3968 propp.set(nullptr);
3969 }
3970 *donep = true;
3971 return true;
3972 }
3973 }
3975 // Check for a native property.
3976 if (Shape *shape = obj->nativeLookup(cx, id)) {
3977 objp.set(obj);
3978 propp.set(shape);
3979 *donep = true;
3980 return true;
3981 }
3983 // id was not found in obj. Try obj's resolve hook, if any.
3984 if (obj->getClass()->resolve != JS_ResolveStub) {
3985 if (!cx->shouldBeJSContext() || !allowGC)
3986 return false;
3988 bool recursed;
3989 if (!CallResolveOp(cx->asJSContext(),
3990 MaybeRooted<JSObject*, allowGC>::toHandle(obj),
3991 MaybeRooted<jsid, allowGC>::toHandle(id),
3992 MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
3993 MaybeRooted<Shape*, allowGC>::toMutableHandle(propp),
3994 &recursed))
3995 {
3996 return false;
3997 }
3999 if (recursed) {
4000 objp.set(nullptr);
4001 propp.set(nullptr);
4002 *donep = true;
4003 return true;
4004 }
4006 if (propp) {
4007 *donep = true;
4008 return true;
4009 }
4010 }
4012 *donep = false;
4013 return true;
4014 }
4016 static bool
4017 NativeLookupOwnProperty(ExclusiveContext *cx, HandleObject obj, HandleId id,
4018 MutableHandle<Shape*> shapep)
4019 {
4020 RootedObject pobj(cx);
4021 bool done;
4023 if (!LookupOwnPropertyInline<CanGC>(cx, obj, id, &pobj, shapep, &done))
4024 return false;
4025 if (!done || pobj != obj)
4026 shapep.set(nullptr);
4027 return true;
4028 }
4030 template <AllowGC allowGC>
4031 static MOZ_ALWAYS_INLINE bool
4032 LookupPropertyInline(ExclusiveContext *cx,
4033 typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
4034 typename MaybeRooted<jsid, allowGC>::HandleType id,
4035 typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
4036 typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
4037 {
4038 /* NB: The logic of this procedure is implicitly reflected in IonBuilder.cpp's
4039 * |CanEffectlesslyCallLookupGenericOnObject| logic.
4040 * If this changes, please remember to update the logic there as well.
4041 */
4043 /* Search scopes starting with obj and following the prototype link. */
4044 typename MaybeRooted<JSObject*, allowGC>::RootType current(cx, obj);
4046 while (true) {
4047 bool done;
4048 if (!LookupOwnPropertyInline<allowGC>(cx, current, id, objp, propp, &done))
4049 return false;
4050 if (done)
4051 return true;
4053 typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->getProto());
4055 if (!proto)
4056 break;
4057 if (!proto->isNative()) {
4058 if (!cx->shouldBeJSContext() || !allowGC)
4059 return false;
4060 return JSObject::lookupGeneric(cx->asJSContext(),
4061 MaybeRooted<JSObject*, allowGC>::toHandle(proto),
4062 MaybeRooted<jsid, allowGC>::toHandle(id),
4063 MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
4064 MaybeRooted<Shape*, allowGC>::toMutableHandle(propp));
4065 }
4067 current = proto;
4068 }
4070 objp.set(nullptr);
4071 propp.set(nullptr);
4072 return true;
4073 }
4075 template <AllowGC allowGC>
4076 bool
4077 baseops::LookupProperty(ExclusiveContext *cx,
4078 typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
4079 typename MaybeRooted<jsid, allowGC>::HandleType id,
4080 typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
4081 typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
4082 {
4083 return LookupPropertyInline<allowGC>(cx, obj, id, objp, propp);
4084 }
4086 template bool
4087 baseops::LookupProperty<CanGC>(ExclusiveContext *cx, HandleObject obj, HandleId id,
4088 MutableHandleObject objp, MutableHandleShape propp);
4090 template bool
4091 baseops::LookupProperty<NoGC>(ExclusiveContext *cx, JSObject *obj, jsid id,
4092 FakeMutableHandle<JSObject*> objp,
4093 FakeMutableHandle<Shape*> propp);
4095 /* static */ bool
4096 JSObject::lookupGeneric(JSContext *cx, HandleObject obj, js::HandleId id,
4097 MutableHandleObject objp, MutableHandleShape propp)
4098 {
4099 /*
4100 * NB: The logic of lookupGeneric is implicitly reflected in IonBuilder.cpp's
4101 * |CanEffectlesslyCallLookupGenericOnObject| logic.
4102 * If this changes, please remember to update the logic there as well.
4103 */
4104 LookupGenericOp op = obj->getOps()->lookupGeneric;
4105 if (op)
4106 return op(cx, obj, id, objp, propp);
4107 return baseops::LookupProperty<js::CanGC>(cx, obj, id, objp, propp);
4108 }
4110 bool
4111 baseops::LookupElement(JSContext *cx, HandleObject obj, uint32_t index,
4112 MutableHandleObject objp, MutableHandleShape propp)
4113 {
4114 RootedId id(cx);
4115 if (!IndexToId(cx, index, &id))
4116 return false;
4118 return LookupPropertyInline<CanGC>(cx, obj, id, objp, propp);
4119 }
4121 bool
4122 js::LookupNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id,
4123 MutableHandleObject objp, MutableHandleShape propp)
4124 {
4125 return LookupPropertyInline<CanGC>(cx, obj, id, objp, propp);
4126 }
4128 bool
4129 js::LookupName(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
4130 MutableHandleObject objp, MutableHandleObject pobjp, MutableHandleShape propp)
4131 {
4132 RootedId id(cx, NameToId(name));
4134 for (RootedObject scope(cx, scopeChain); scope; scope = scope->enclosingScope()) {
4135 if (!JSObject::lookupGeneric(cx, scope, id, pobjp, propp))
4136 return false;
4137 if (propp) {
4138 objp.set(scope);
4139 return true;
4140 }
4141 }
4143 objp.set(nullptr);
4144 pobjp.set(nullptr);
4145 propp.set(nullptr);
4146 return true;
4147 }
4149 bool
4150 js::LookupNameNoGC(JSContext *cx, PropertyName *name, JSObject *scopeChain,
4151 JSObject **objp, JSObject **pobjp, Shape **propp)
4152 {
4153 AutoAssertNoException nogc(cx);
4155 JS_ASSERT(!*objp && !*pobjp && !*propp);
4157 for (JSObject *scope = scopeChain; scope; scope = scope->enclosingScope()) {
4158 if (scope->getOps()->lookupGeneric)
4159 return false;
4160 if (!LookupPropertyInline<NoGC>(cx, scope, NameToId(name), pobjp, propp))
4161 return false;
4162 if (*propp) {
4163 *objp = scope;
4164 return true;
4165 }
4166 }
4168 return true;
4169 }
4171 bool
4172 js::LookupNameWithGlobalDefault(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
4173 MutableHandleObject objp)
4174 {
4175 RootedId id(cx, NameToId(name));
4177 RootedObject pobj(cx);
4178 RootedShape prop(cx);
4180 RootedObject scope(cx, scopeChain);
4181 for (; !scope->is<GlobalObject>(); scope = scope->enclosingScope()) {
4182 if (!JSObject::lookupGeneric(cx, scope, id, &pobj, &prop))
4183 return false;
4184 if (prop)
4185 break;
4186 }
4188 objp.set(scope);
4189 return true;
4190 }
4192 template <AllowGC allowGC>
4193 bool
4194 js::HasOwnProperty(JSContext *cx, LookupGenericOp lookup,
4195 typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
4196 typename MaybeRooted<jsid, allowGC>::HandleType id,
4197 typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
4198 typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
4199 {
4200 if (lookup) {
4201 if (!allowGC)
4202 return false;
4203 if (!lookup(cx,
4204 MaybeRooted<JSObject*, allowGC>::toHandle(obj),
4205 MaybeRooted<jsid, allowGC>::toHandle(id),
4206 MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
4207 MaybeRooted<Shape*, allowGC>::toMutableHandle(propp)))
4208 {
4209 return false;
4210 }
4211 } else {
4212 bool done;
4213 if (!LookupOwnPropertyInline<allowGC>(cx, obj, id, objp, propp, &done))
4214 return false;
4215 if (!done) {
4216 objp.set(nullptr);
4217 propp.set(nullptr);
4218 return true;
4219 }
4220 }
4222 if (!propp)
4223 return true;
4225 if (objp == obj)
4226 return true;
4228 JSObject *outer = nullptr;
4229 if (JSObjectOp op = objp->getClass()->ext.outerObject) {
4230 if (!allowGC)
4231 return false;
4232 RootedObject inner(cx, objp);
4233 outer = op(cx, inner);
4234 if (!outer)
4235 return false;
4236 }
4238 if (outer != objp)
4239 propp.set(nullptr);
4240 return true;
4241 }
4243 template bool
4244 js::HasOwnProperty<CanGC>(JSContext *cx, LookupGenericOp lookup,
4245 HandleObject obj, HandleId id,
4246 MutableHandleObject objp, MutableHandleShape propp);
4248 template bool
4249 js::HasOwnProperty<NoGC>(JSContext *cx, LookupGenericOp lookup,
4250 JSObject *obj, jsid id,
4251 FakeMutableHandle<JSObject*> objp, FakeMutableHandle<Shape*> propp);
4253 bool
4254 js::HasOwnProperty(JSContext *cx, HandleObject obj, HandleId id, bool *resultp)
4255 {
4256 RootedObject pobj(cx);
4257 RootedShape shape(cx);
4258 if (!HasOwnProperty<CanGC>(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &shape))
4259 return false;
4260 *resultp = (shape != nullptr);
4261 return true;
4262 }
4264 template <AllowGC allowGC>
4265 static MOZ_ALWAYS_INLINE bool
4266 NativeGetInline(JSContext *cx,
4267 typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
4268 typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
4269 typename MaybeRooted<JSObject*, allowGC>::HandleType pobj,
4270 typename MaybeRooted<Shape*, allowGC>::HandleType shape,
4271 typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
4272 {
4273 JS_ASSERT(pobj->isNative());
4275 if (shape->hasSlot()) {
4276 vp.set(pobj->nativeGetSlot(shape->slot()));
4277 JS_ASSERT(!vp.isMagic());
4278 JS_ASSERT_IF(!pobj->hasSingletonType() &&
4279 !pobj->template is<ScopeObject>() &&
4280 shape->hasDefaultGetter(),
4281 js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), vp));
4282 } else {
4283 vp.setUndefined();
4284 }
4285 if (shape->hasDefaultGetter())
4286 return true;
4288 {
4289 jsbytecode *pc;
4290 JSScript *script = cx->currentScript(&pc);
4291 #ifdef JS_ION
4292 if (script && script->hasBaselineScript()) {
4293 switch (JSOp(*pc)) {
4294 case JSOP_GETPROP:
4295 case JSOP_CALLPROP:
4296 case JSOP_LENGTH:
4297 script->baselineScript()->noteAccessedGetter(script->pcToOffset(pc));
4298 break;
4299 default:
4300 break;
4301 }
4302 }
4303 #endif
4304 }
4306 if (!allowGC)
4307 return false;
4309 if (!shape->get(cx,
4310 MaybeRooted<JSObject*, allowGC>::toHandle(receiver),
4311 MaybeRooted<JSObject*, allowGC>::toHandle(obj),
4312 MaybeRooted<JSObject*, allowGC>::toHandle(pobj),
4313 MaybeRooted<Value, allowGC>::toMutableHandle(vp)))
4314 {
4315 return false;
4316 }
4318 /* Update slotful shapes according to the value produced by the getter. */
4319 if (shape->hasSlot() && pobj->nativeContains(cx, shape))
4320 pobj->nativeSetSlot(shape->slot(), vp);
4322 return true;
4323 }
4325 bool
4326 js::NativeGet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> pobj, Handle<Shape*> shape,
4327 MutableHandle<Value> vp)
4328 {
4329 return NativeGetInline<CanGC>(cx, obj, obj, pobj, shape, vp);
4330 }
4332 template <ExecutionMode mode>
4333 bool
4334 js::NativeSet(typename ExecutionModeTraits<mode>::ContextType cxArg,
4335 Handle<JSObject*> obj, Handle<JSObject*> receiver,
4336 HandleShape shape, bool strict, MutableHandleValue vp)
4337 {
4338 JS_ASSERT(cxArg->isThreadLocal(obj));
4339 JS_ASSERT(obj->isNative());
4341 if (shape->hasSlot()) {
4342 /* If shape has a stub setter, just store vp. */
4343 if (shape->hasDefaultSetter()) {
4344 if (mode == ParallelExecution) {
4345 if (!obj->nativeSetSlotIfHasType(shape, vp))
4346 return false;
4347 } else {
4348 obj->nativeSetSlotWithType(cxArg->asExclusiveContext(), shape, vp);
4349 }
4351 return true;
4352 }
4353 }
4355 if (mode == ParallelExecution)
4356 return false;
4357 JSContext *cx = cxArg->asJSContext();
4359 if (!shape->hasSlot()) {
4360 /*
4361 * Allow API consumers to create shared properties with stub setters.
4362 * Such properties effectively function as data descriptors which are
4363 * not writable, so attempting to set such a property should do nothing
4364 * or throw if we're in strict mode.
4365 */
4366 if (!shape->hasGetterValue() && shape->hasDefaultSetter())
4367 return js_ReportGetterOnlyAssignment(cx, strict);
4368 }
4370 RootedValue ovp(cx, vp);
4372 uint32_t sample = cx->runtime()->propertyRemovals;
4373 if (!shape->set(cx, obj, receiver, strict, vp))
4374 return false;
4376 /*
4377 * Update any slot for the shape with the value produced by the setter,
4378 * unless the setter deleted the shape.
4379 */
4380 if (shape->hasSlot() &&
4381 (MOZ_LIKELY(cx->runtime()->propertyRemovals == sample) ||
4382 obj->nativeContains(cx, shape)))
4383 {
4384 obj->setSlot(shape->slot(), vp);
4385 }
4387 return true;
4388 }
4390 template bool
4391 js::NativeSet<SequentialExecution>(JSContext *cx,
4392 Handle<JSObject*> obj, Handle<JSObject*> receiver,
4393 HandleShape shape, bool strict, MutableHandleValue vp);
4394 template bool
4395 js::NativeSet<ParallelExecution>(ForkJoinContext *cx,
4396 Handle<JSObject*> obj, Handle<JSObject*> receiver,
4397 HandleShape shape, bool strict, MutableHandleValue vp);
4399 template <AllowGC allowGC>
4400 static MOZ_ALWAYS_INLINE bool
4401 GetPropertyHelperInline(JSContext *cx,
4402 typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
4403 typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
4404 typename MaybeRooted<jsid, allowGC>::HandleType id,
4405 typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
4406 {
4407 /* This call site is hot -- use the always-inlined variant of LookupNativeProperty(). */
4408 typename MaybeRooted<JSObject*, allowGC>::RootType obj2(cx);
4409 typename MaybeRooted<Shape*, allowGC>::RootType shape(cx);
4410 if (!LookupPropertyInline<allowGC>(cx, obj, id, &obj2, &shape))
4411 return false;
4413 if (!shape) {
4414 if (!allowGC)
4415 return false;
4417 vp.setUndefined();
4419 if (!CallJSPropertyOp(cx, obj->getClass()->getProperty,
4420 MaybeRooted<JSObject*, allowGC>::toHandle(obj),
4421 MaybeRooted<jsid, allowGC>::toHandle(id),
4422 MaybeRooted<Value, allowGC>::toMutableHandle(vp)))
4423 {
4424 return false;
4425 }
4427 /*
4428 * Give a strict warning if foo.bar is evaluated by a script for an
4429 * object foo with no property named 'bar'.
4430 */
4431 if (vp.isUndefined()) {
4432 jsbytecode *pc = nullptr;
4433 RootedScript script(cx, cx->currentScript(&pc));
4434 if (!pc)
4435 return true;
4436 JSOp op = (JSOp) *pc;
4438 if (op == JSOP_GETXPROP) {
4439 /* Undefined property during a name lookup, report an error. */
4440 JSAutoByteString printable;
4441 if (js_ValueToPrintable(cx, IdToValue(id), &printable))
4442 js_ReportIsNotDefined(cx, printable.ptr());
4443 return false;
4444 }
4446 /* Don't warn if extra warnings not enabled or for random getprop operations. */
4447 if (!cx->options().extraWarnings() || (op != JSOP_GETPROP && op != JSOP_GETELEM))
4448 return true;
4450 /* Don't warn repeatedly for the same script. */
4451 if (!script || script->warnedAboutUndefinedProp())
4452 return true;
4454 /*
4455 * Don't warn in self-hosted code (where the further presence of
4456 * JS::ContextOptions::werror() would result in impossible-to-avoid
4457 * errors to entirely-innocent client code).
4458 */
4459 if (script->selfHosted())
4460 return true;
4462 /* We may just be checking if that object has an iterator. */
4463 if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic))
4464 return true;
4466 /* Do not warn about tests like (obj[prop] == undefined). */
4467 pc += js_CodeSpec[op].length;
4468 if (Detecting(cx, script, pc))
4469 return true;
4471 unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT;
4472 script->setWarnedAboutUndefinedProp();
4474 /* Ok, bad undefined property reference: whine about it. */
4475 RootedValue val(cx, IdToValue(id));
4476 if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP,
4477 JSDVG_IGNORE_STACK, val, js::NullPtr(),
4478 nullptr, nullptr))
4479 {
4480 return false;
4481 }
4482 }
4483 return true;
4484 }
4486 if (!obj2->isNative()) {
4487 if (!allowGC)
4488 return false;
4489 HandleObject obj2Handle = MaybeRooted<JSObject*, allowGC>::toHandle(obj2);
4490 HandleObject receiverHandle = MaybeRooted<JSObject*, allowGC>::toHandle(receiver);
4491 HandleId idHandle = MaybeRooted<jsid, allowGC>::toHandle(id);
4492 MutableHandleValue vpHandle = MaybeRooted<Value, allowGC>::toMutableHandle(vp);
4493 return obj2->template is<ProxyObject>()
4494 ? Proxy::get(cx, obj2Handle, receiverHandle, idHandle, vpHandle)
4495 : JSObject::getGeneric(cx, obj2Handle, obj2Handle, idHandle, vpHandle);
4496 }
4498 if (IsImplicitDenseOrTypedArrayElement(shape)) {
4499 vp.set(obj2->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
4500 return true;
4501 }
4503 /* This call site is hot -- use the always-inlined variant of NativeGet(). */
4504 if (!NativeGetInline<allowGC>(cx, obj, receiver, obj2, shape, vp))
4505 return false;
4507 return true;
4508 }
4510 bool
4511 baseops::GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, MutableHandleValue vp)
4512 {
4513 /* This call site is hot -- use the always-inlined variant of GetPropertyHelper(). */
4514 return GetPropertyHelperInline<CanGC>(cx, obj, receiver, id, vp);
4515 }
4517 bool
4518 baseops::GetPropertyNoGC(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
4519 {
4520 AutoAssertNoException nogc(cx);
4521 return GetPropertyHelperInline<NoGC>(cx, obj, receiver, id, vp);
4522 }
4524 static MOZ_ALWAYS_INLINE bool
4525 LookupPropertyPureInline(JSObject *obj, jsid id, JSObject **objp, Shape **propp)
4526 {
4527 if (!obj->isNative())
4528 return false;
4530 JSObject *current = obj;
4531 while (true) {
4532 /* Search for a native dense element, typed array element, or property. */
4534 if (JSID_IS_INT(id) && current->containsDenseElement(JSID_TO_INT(id))) {
4535 *objp = current;
4536 MarkDenseOrTypedArrayElementFound<NoGC>(propp);
4537 return true;
4538 }
4540 if (current->is<TypedArrayObject>()) {
4541 uint64_t index;
4542 if (IsTypedArrayIndex(id, &index)) {
4543 if (index < obj->as<TypedArrayObject>().length()) {
4544 *objp = current;
4545 MarkDenseOrTypedArrayElementFound<NoGC>(propp);
4546 } else {
4547 *objp = nullptr;
4548 *propp = nullptr;
4549 }
4550 return true;
4551 }
4552 }
4554 if (Shape *shape = current->nativeLookupPure(id)) {
4555 *objp = current;
4556 *propp = shape;
4557 return true;
4558 }
4560 /* Fail if there's a resolve hook. */
4561 if (current->getClass()->resolve != JS_ResolveStub)
4562 return false;
4564 JSObject *proto = current->getProto();
4566 if (!proto)
4567 break;
4568 if (!proto->isNative())
4569 return false;
4571 current = proto;
4572 }
4574 *objp = nullptr;
4575 *propp = nullptr;
4576 return true;
4577 }
4579 static MOZ_ALWAYS_INLINE bool
4580 NativeGetPureInline(JSObject *pobj, Shape *shape, Value *vp)
4581 {
4582 JS_ASSERT(pobj->isNative());
4584 if (shape->hasSlot()) {
4585 *vp = pobj->nativeGetSlot(shape->slot());
4586 JS_ASSERT(!vp->isMagic());
4587 } else {
4588 vp->setUndefined();
4589 }
4591 /* Fail if we have a custom getter. */
4592 return shape->hasDefaultGetter();
4593 }
4595 bool
4596 js::LookupPropertyPure(JSObject *obj, jsid id, JSObject **objp, Shape **propp)
4597 {
4598 return LookupPropertyPureInline(obj, id, objp, propp);
4599 }
4601 static inline bool
4602 IdIsLength(ThreadSafeContext *cx, jsid id)
4603 {
4604 return JSID_IS_ATOM(id) && cx->names().length == JSID_TO_ATOM(id);
4605 }
4607 /*
4608 * A pure version of GetPropertyHelper that can be called from parallel code
4609 * without locking. This code path cannot GC. This variant returns false
4610 * whenever a side-effect might have occured in the effectful version. This
4611 * includes, but is not limited to:
4612 *
4613 * - Any object in the lookup chain has a non-stub resolve hook.
4614 * - Any object in the lookup chain is non-native.
4615 * - The property has a getter.
4616 */
4617 bool
4618 js::GetPropertyPure(ThreadSafeContext *cx, JSObject *obj, jsid id, Value *vp)
4619 {
4620 /* Deal with native objects. */
4621 JSObject *obj2;
4622 Shape *shape;
4623 if (!LookupPropertyPureInline(obj, id, &obj2, &shape))
4624 return false;
4626 if (!shape) {
4627 /* Fail if we have a non-stub class op hooks. */
4628 if (obj->getClass()->getProperty && obj->getClass()->getProperty != JS_PropertyStub)
4629 return false;
4631 if (obj->getOps()->getElement)
4632 return false;
4634 /* Vanilla native object, return undefined. */
4635 vp->setUndefined();
4636 return true;
4637 }
4639 if (IsImplicitDenseOrTypedArrayElement(shape)) {
4640 *vp = obj2->getDenseOrTypedArrayElement(JSID_TO_INT(id));
4641 return true;
4642 }
4644 /* Special case 'length' on Array and TypedArray. */
4645 if (IdIsLength(cx, id)) {
4646 if (obj->is<ArrayObject>()) {
4647 vp->setNumber(obj->as<ArrayObject>().length());
4648 return true;
4649 }
4651 if (obj->is<TypedArrayObject>()) {
4652 vp->setNumber(obj->as<TypedArrayObject>().length());
4653 return true;
4654 }
4655 }
4657 return NativeGetPureInline(obj2, shape, vp);
4658 }
4660 static bool
4661 MOZ_ALWAYS_INLINE
4662 GetElementPure(ThreadSafeContext *cx, JSObject *obj, uint32_t index, Value *vp)
4663 {
4664 if (index <= JSID_INT_MAX)
4665 return GetPropertyPure(cx, obj, INT_TO_JSID(index), vp);
4666 return false;
4667 }
4669 /*
4670 * A pure version of GetObjectElementOperation that can be called from
4671 * parallel code without locking. This variant returns false whenever a
4672 * side-effect might have occurred.
4673 */
4674 bool
4675 js::GetObjectElementOperationPure(ThreadSafeContext *cx, JSObject *obj, const Value &prop,
4676 Value *vp)
4677 {
4678 uint32_t index;
4679 if (IsDefinitelyIndex(prop, &index))
4680 return GetElementPure(cx, obj, index, vp);
4682 /* Atomizing the property value is effectful and not threadsafe. */
4683 if (!prop.isString() || !prop.toString()->isAtom())
4684 return false;
4686 JSAtom *name = &prop.toString()->asAtom();
4687 if (name->isIndex(&index))
4688 return GetElementPure(cx, obj, index, vp);
4690 return GetPropertyPure(cx, obj, NameToId(name->asPropertyName()), vp);
4691 }
4693 bool
4694 baseops::GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
4695 MutableHandleValue vp)
4696 {
4697 RootedId id(cx);
4698 if (!IndexToId(cx, index, &id))
4699 return false;
4701 /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
4702 return GetPropertyHelperInline<CanGC>(cx, obj, receiver, id, vp);
4703 }
4705 static bool
4706 MaybeReportUndeclaredVarAssignment(JSContext *cx, JSString *propname)
4707 {
4708 {
4709 JSScript *script = cx->currentScript(nullptr, JSContext::ALLOW_CROSS_COMPARTMENT);
4710 if (!script)
4711 return true;
4713 // If the code is not strict and extra warnings aren't enabled, then no
4714 // check is needed.
4715 if (!script->strict() && !cx->options().extraWarnings())
4716 return true;
4717 }
4719 JSAutoByteString bytes(cx, propname);
4720 return !!bytes &&
4721 JS_ReportErrorFlagsAndNumber(cx,
4722 (JSREPORT_WARNING | JSREPORT_STRICT
4723 | JSREPORT_STRICT_MODE_ERROR),
4724 js_GetErrorMessage, nullptr,
4725 JSMSG_UNDECLARED_VAR, bytes.ptr());
4726 }
4728 bool
4729 js::ReportIfUndeclaredVarAssignment(JSContext *cx, HandleString propname)
4730 {
4731 {
4732 jsbytecode *pc;
4733 JSScript *script = cx->currentScript(&pc, JSContext::ALLOW_CROSS_COMPARTMENT);
4734 if (!script)
4735 return true;
4737 // If the code is not strict and extra warnings aren't enabled, then no
4738 // check is needed.
4739 if (!script->strict() && !cx->options().extraWarnings())
4740 return true;
4742 /*
4743 * We only need to check for bare name mutations: we shouldn't be
4744 * warning, or throwing, or whatever, if we're not doing a variable
4745 * access.
4746 *
4747 * TryConvertToGname in frontend/BytecodeEmitter.cpp checks for rather
4748 * more opcodes when it does, in the normal course of events, what this
4749 * method does in the abnormal course of events. Because we're called
4750 * in narrower circumstances, we only need check two. We don't need to
4751 * check for the increment/decrement opcodes because they're no-ops:
4752 * the actual semantics are implemented by desugaring. And we don't
4753 * need to check name-access because this method is only supposed to be
4754 * called in assignment contexts.
4755 */
4756 MOZ_ASSERT(*pc != JSOP_NAME);
4757 MOZ_ASSERT(*pc != JSOP_GETGNAME);
4758 if (*pc != JSOP_SETNAME && *pc != JSOP_SETGNAME)
4759 return true;
4760 }
4762 JSAutoByteString bytes(cx, propname);
4763 return !!bytes &&
4764 JS_ReportErrorFlagsAndNumber(cx,
4765 JSREPORT_WARNING | JSREPORT_STRICT |
4766 JSREPORT_STRICT_MODE_ERROR,
4767 js_GetErrorMessage, nullptr,
4768 JSMSG_UNDECLARED_VAR, bytes.ptr());
4769 }
4771 bool
4772 JSObject::reportReadOnly(ThreadSafeContext *cxArg, jsid id, unsigned report)
4773 {
4774 if (cxArg->isForkJoinContext())
4775 return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report);
4777 if (!cxArg->isJSContext())
4778 return true;
4780 JSContext *cx = cxArg->asJSContext();
4781 RootedValue val(cx, IdToValue(id));
4782 return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
4783 JSDVG_IGNORE_STACK, val, js::NullPtr(),
4784 nullptr, nullptr);
4785 }
4787 bool
4788 JSObject::reportNotConfigurable(ThreadSafeContext *cxArg, jsid id, unsigned report)
4789 {
4790 if (cxArg->isForkJoinContext())
4791 return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report);
4793 if (!cxArg->isJSContext())
4794 return true;
4796 JSContext *cx = cxArg->asJSContext();
4797 RootedValue val(cx, IdToValue(id));
4798 return js_ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE,
4799 JSDVG_IGNORE_STACK, val, js::NullPtr(),
4800 nullptr, nullptr);
4801 }
4803 bool
4804 JSObject::reportNotExtensible(ThreadSafeContext *cxArg, unsigned report)
4805 {
4806 if (cxArg->isForkJoinContext())
4807 return cxArg->asForkJoinContext()->reportError(ParallelBailoutUnsupportedVM, report);
4809 if (!cxArg->isJSContext())
4810 return true;
4812 JSContext *cx = cxArg->asJSContext();
4813 RootedValue val(cx, ObjectValue(*this));
4814 return js_ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE,
4815 JSDVG_IGNORE_STACK, val, js::NullPtr(),
4816 nullptr, nullptr);
4817 }
4819 bool
4820 JSObject::callMethod(JSContext *cx, HandleId id, unsigned argc, Value *argv, MutableHandleValue vp)
4821 {
4822 RootedValue fval(cx);
4823 RootedObject obj(cx, this);
4824 if (!JSObject::getGeneric(cx, obj, obj, id, &fval))
4825 return false;
4826 return Invoke(cx, ObjectValue(*obj), fval, argc, argv, vp);
4827 }
4829 template <ExecutionMode mode>
4830 bool
4831 baseops::SetPropertyHelper(typename ExecutionModeTraits<mode>::ContextType cxArg,
4832 HandleObject obj, HandleObject receiver, HandleId id,
4833 QualifiedBool qualified, MutableHandleValue vp, bool strict)
4834 {
4835 JS_ASSERT(cxArg->isThreadLocal(obj));
4837 if (MOZ_UNLIKELY(obj->watched())) {
4838 if (mode == ParallelExecution)
4839 return false;
4841 /* Fire watchpoints, if any. */
4842 JSContext *cx = cxArg->asJSContext();
4843 WatchpointMap *wpmap = cx->compartment()->watchpointMap;
4844 if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
4845 return false;
4846 }
4848 RootedObject pobj(cxArg);
4849 RootedShape shape(cxArg);
4850 if (mode == ParallelExecution) {
4851 if (!LookupPropertyPure(obj, id, pobj.address(), shape.address()))
4852 return false;
4853 } else {
4854 JSContext *cx = cxArg->asJSContext();
4855 if (!LookupNativeProperty(cx, obj, id, &pobj, &shape))
4856 return false;
4857 }
4858 if (shape) {
4859 if (!pobj->isNative()) {
4860 if (pobj->is<ProxyObject>()) {
4861 if (mode == ParallelExecution)
4862 return false;
4864 JSContext *cx = cxArg->asJSContext();
4865 Rooted<PropertyDescriptor> pd(cx);
4866 if (!Proxy::getPropertyDescriptor(cx, pobj, id, &pd))
4867 return false;
4869 if ((pd.attributes() & (JSPROP_SHARED | JSPROP_SHADOWABLE)) == JSPROP_SHARED) {
4870 return !pd.setter() ||
4871 CallSetter(cx, receiver, id, pd.setter(), pd.attributes(), strict, vp);
4872 }
4874 if (pd.isReadonly()) {
4875 if (strict)
4876 return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
4877 if (cx->options().extraWarnings())
4878 return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
4879 return true;
4880 }
4881 }
4883 shape = nullptr;
4884 }
4885 } else {
4886 /* We should never add properties to lexical blocks. */
4887 JS_ASSERT(!obj->is<BlockObject>());
4889 if (obj->is<GlobalObject>() && !qualified) {
4890 if (mode == ParallelExecution)
4891 return false;
4893 if (!MaybeReportUndeclaredVarAssignment(cxArg->asJSContext(), JSID_TO_STRING(id)))
4894 return false;
4895 }
4896 }
4898 /*
4899 * Now either shape is null, meaning id was not found in obj or one of its
4900 * prototypes; or shape is non-null, meaning id was found directly in pobj.
4901 */
4902 unsigned attrs = JSPROP_ENUMERATE;
4903 const Class *clasp = obj->getClass();
4904 PropertyOp getter = clasp->getProperty;
4905 StrictPropertyOp setter = clasp->setProperty;
4907 if (IsImplicitDenseOrTypedArrayElement(shape)) {
4908 /* ES5 8.12.4 [[Put]] step 2, for a dense data property on pobj. */
4909 if (pobj != obj)
4910 shape = nullptr;
4911 } else if (shape) {
4912 /* ES5 8.12.4 [[Put]] step 2. */
4913 if (shape->isAccessorDescriptor()) {
4914 if (shape->hasDefaultSetter()) {
4915 /* Bail out of parallel execution if we are strict to throw. */
4916 if (mode == ParallelExecution)
4917 return !strict;
4919 return js_ReportGetterOnlyAssignment(cxArg->asJSContext(), strict);
4920 }
4921 } else {
4922 JS_ASSERT(shape->isDataDescriptor());
4924 if (!shape->writable()) {
4925 /*
4926 * Error in strict mode code, warn with extra warnings
4927 * options, otherwise do nothing.
4928 *
4929 * Bail out of parallel execution if we are strict to throw.
4930 */
4931 if (mode == ParallelExecution)
4932 return !strict;
4934 JSContext *cx = cxArg->asJSContext();
4935 if (strict)
4936 return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
4937 if (cx->options().extraWarnings())
4938 return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
4939 return true;
4940 }
4941 }
4943 attrs = shape->attributes();
4944 if (pobj != obj) {
4945 /*
4946 * We found id in a prototype object: prepare to share or shadow.
4947 */
4948 if (!shape->shadowable()) {
4949 if (shape->hasDefaultSetter() && !shape->hasGetterValue())
4950 return true;
4952 if (mode == ParallelExecution)
4953 return false;
4955 return shape->set(cxArg->asJSContext(), obj, receiver, strict, vp);
4956 }
4958 /*
4959 * Preserve attrs except JSPROP_SHARED, getter, and setter when
4960 * shadowing any property that has no slot (is shared). We must
4961 * clear the shared attribute for the shadowing shape so that the
4962 * property in obj that it defines has a slot to retain the value
4963 * being set, in case the setter simply cannot operate on instances
4964 * of obj's class by storing the value in some class-specific
4965 * location.
4966 */
4967 if (!shape->hasSlot()) {
4968 attrs &= ~JSPROP_SHARED;
4969 getter = shape->getter();
4970 setter = shape->setter();
4971 } else {
4972 /* Restore attrs to the ECMA default for new properties. */
4973 attrs = JSPROP_ENUMERATE;
4974 }
4976 /*
4977 * Forget we found the proto-property now that we've copied any
4978 * needed member values.
4979 */
4980 shape = nullptr;
4981 }
4982 }
4984 if (IsImplicitDenseOrTypedArrayElement(shape)) {
4985 uint32_t index = JSID_TO_INT(id);
4987 if (obj->is<TypedArrayObject>()) {
4988 double d;
4989 if (mode == ParallelExecution) {
4990 // Bail if converting the value might invoke user-defined
4991 // conversions.
4992 if (vp.isObject())
4993 return false;
4994 if (!NonObjectToNumber(cxArg, vp, &d))
4995 return false;
4996 } else {
4997 if (!ToNumber(cxArg->asJSContext(), vp, &d))
4998 return false;
4999 }
5001 // Silently do nothing for out-of-bounds sets, for consistency with
5002 // current behavior. (ES6 currently says to throw for this in
5003 // strict mode code, so we may eventually need to change.)
5004 TypedArrayObject &tarray = obj->as<TypedArrayObject>();
5005 if (index < tarray.length())
5006 TypedArrayObject::setElement(tarray, index, d);
5007 return true;
5008 }
5010 bool definesPast;
5011 if (!WouldDefinePastNonwritableLength(cxArg, obj, index, strict, &definesPast))
5012 return false;
5013 if (definesPast) {
5014 /* Bail out of parallel execution if we are strict to throw. */
5015 if (mode == ParallelExecution)
5016 return !strict;
5017 return true;
5018 }
5020 if (mode == ParallelExecution)
5021 return obj->setDenseElementIfHasType(index, vp);
5023 obj->setDenseElementWithType(cxArg->asJSContext(), index, vp);
5024 return true;
5025 }
5027 if (obj->is<ArrayObject>() && id == NameToId(cxArg->names().length)) {
5028 Rooted<ArrayObject*> arr(cxArg, &obj->as<ArrayObject>());
5029 return ArraySetLength<mode>(cxArg, arr, id, attrs, vp, strict);
5030 }
5032 if (!shape) {
5033 bool extensible;
5034 if (mode == ParallelExecution) {
5035 if (obj->is<ProxyObject>())
5036 return false;
5037 extensible = obj->nonProxyIsExtensible();
5038 } else {
5039 if (!JSObject::isExtensible(cxArg->asJSContext(), obj, &extensible))
5040 return false;
5041 }
5043 if (!extensible) {
5044 /* Error in strict mode code, warn with extra warnings option, otherwise do nothing. */
5045 if (strict)
5046 return obj->reportNotExtensible(cxArg);
5047 if (mode == SequentialExecution && cxArg->asJSContext()->options().extraWarnings())
5048 return obj->reportNotExtensible(cxArg, JSREPORT_STRICT | JSREPORT_WARNING);
5049 return true;
5050 }
5052 if (mode == ParallelExecution) {
5053 if (obj->isDelegate())
5054 return false;
5056 if (getter != JS_PropertyStub || !HasTypePropertyId(obj, id, vp))
5057 return false;
5058 } else {
5059 JSContext *cx = cxArg->asJSContext();
5061 /* Purge the property cache of now-shadowed id in obj's scope chain. */
5062 if (!PurgeScopeChain(cx, obj, id))
5063 return false;
5064 }
5066 return DefinePropertyOrElement<mode>(cxArg, obj, id, getter, setter,
5067 attrs, vp, true, strict);
5068 }
5070 return NativeSet<mode>(cxArg, obj, receiver, shape, strict, vp);
5071 }
5073 template bool
5074 baseops::SetPropertyHelper<SequentialExecution>(JSContext *cx, HandleObject obj,
5075 HandleObject receiver, HandleId id,
5076 QualifiedBool qualified,
5077 MutableHandleValue vp, bool strict);
5078 template bool
5079 baseops::SetPropertyHelper<ParallelExecution>(ForkJoinContext *cx, HandleObject obj,
5080 HandleObject receiver, HandleId id,
5081 QualifiedBool qualified,
5082 MutableHandleValue vp, bool strict);
5084 bool
5085 baseops::SetElementHelper(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
5086 MutableHandleValue vp, bool strict)
5087 {
5088 RootedId id(cx);
5089 if (!IndexToId(cx, index, &id))
5090 return false;
5091 return baseops::SetPropertyHelper<SequentialExecution>(cx, obj, receiver, id, Qualified, vp,
5092 strict);
5093 }
5095 bool
5096 baseops::GetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
5097 {
5098 RootedObject nobj(cx);
5099 RootedShape shape(cx);
5100 if (!baseops::LookupProperty<CanGC>(cx, obj, id, &nobj, &shape))
5101 return false;
5102 if (!shape) {
5103 *attrsp = 0;
5104 return true;
5105 }
5106 if (!nobj->isNative())
5107 return JSObject::getGenericAttributes(cx, nobj, id, attrsp);
5109 *attrsp = GetShapeAttributes(nobj, shape);
5110 return true;
5111 }
5113 bool
5114 baseops::SetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
5115 {
5116 RootedObject nobj(cx);
5117 RootedShape shape(cx);
5118 if (!baseops::LookupProperty<CanGC>(cx, obj, id, &nobj, &shape))
5119 return false;
5120 if (!shape)
5121 return true;
5122 if (nobj->isNative() && IsImplicitDenseOrTypedArrayElement(shape)) {
5123 if (nobj->is<TypedArrayObject>()) {
5124 if (*attrsp == (JSPROP_ENUMERATE | JSPROP_PERMANENT))
5125 return true;
5126 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_ARRAY_ATTRS);
5127 return false;
5128 }
5129 if (!JSObject::sparsifyDenseElement(cx, nobj, JSID_TO_INT(id)))
5130 return false;
5131 shape = obj->nativeLookup(cx, id);
5132 }
5133 if (nobj->isNative()) {
5134 if (!JSObject::changePropertyAttributes(cx, nobj, shape, *attrsp))
5135 return false;
5136 if (*attrsp & JSPROP_READONLY)
5137 MarkTypePropertyNonWritable(cx, obj, id);
5138 return true;
5139 } else {
5140 return JSObject::setGenericAttributes(cx, nobj, id, attrsp);
5141 }
5142 }
5144 bool
5145 baseops::DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
5146 {
5147 RootedObject proto(cx);
5148 RootedShape shape(cx);
5149 if (!baseops::LookupProperty<CanGC>(cx, obj, id, &proto, &shape))
5150 return false;
5151 if (!shape || proto != obj) {
5152 /*
5153 * If no property, or the property comes from a prototype, call the
5154 * class's delProperty hook, passing succeeded as the result parameter.
5155 */
5156 return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded);
5157 }
5159 GCPoke(cx->runtime());
5161 if (IsImplicitDenseOrTypedArrayElement(shape)) {
5162 if (obj->is<TypedArrayObject>()) {
5163 // Don't delete elements from typed arrays.
5164 *succeeded = false;
5165 return true;
5166 }
5168 if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded))
5169 return false;
5170 if (!succeeded)
5171 return true;
5173 obj->setDenseElementHole(cx, JSID_TO_INT(id));
5174 return js_SuppressDeletedProperty(cx, obj, id);
5175 }
5177 if (!shape->configurable()) {
5178 *succeeded = false;
5179 return true;
5180 }
5182 RootedId propid(cx, shape->propid());
5183 if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, propid, succeeded))
5184 return false;
5185 if (!succeeded)
5186 return true;
5188 return obj->removeProperty(cx, id) && js_SuppressDeletedProperty(cx, obj, id);
5189 }
5191 bool
5192 baseops::DeleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
5193 bool *succeeded)
5194 {
5195 Rooted<jsid> id(cx, NameToId(name));
5196 return baseops::DeleteGeneric(cx, obj, id, succeeded);
5197 }
5199 bool
5200 baseops::DeleteElement(JSContext *cx, HandleObject obj, uint32_t index, bool *succeeded)
5201 {
5202 RootedId id(cx);
5203 if (!IndexToId(cx, index, &id))
5204 return false;
5205 return baseops::DeleteGeneric(cx, obj, id, succeeded);
5206 }
5208 bool
5209 js::WatchGuts(JSContext *cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable)
5210 {
5211 RootedObject obj(cx, GetInnerObject(cx, origObj));
5212 if (obj->isNative()) {
5213 // Use sparse indexes for watched objects, as dense elements can be
5214 // written to without checking the watchpoint map.
5215 if (!JSObject::sparsifyDenseElements(cx, obj))
5216 return false;
5218 types::MarkTypePropertyNonData(cx, obj, id);
5219 }
5221 WatchpointMap *wpmap = cx->compartment()->watchpointMap;
5222 if (!wpmap) {
5223 wpmap = cx->runtime()->new_<WatchpointMap>();
5224 if (!wpmap || !wpmap->init()) {
5225 js_ReportOutOfMemory(cx);
5226 return false;
5227 }
5228 cx->compartment()->watchpointMap = wpmap;
5229 }
5231 return wpmap->watch(cx, obj, id, js::WatchHandler, callable);
5232 }
5234 bool
5235 baseops::Watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable)
5236 {
5237 if (!obj->isNative() || obj->is<TypedArrayObject>()) {
5238 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
5239 obj->getClass()->name);
5240 return false;
5241 }
5243 return WatchGuts(cx, obj, id, callable);
5244 }
5246 bool
5247 js::UnwatchGuts(JSContext *cx, JS::HandleObject origObj, JS::HandleId id)
5248 {
5249 // Looking in the map for an unsupported object will never hit, so we don't
5250 // need to check for nativeness or watchable-ness here.
5251 RootedObject obj(cx, GetInnerObject(cx, origObj));
5252 if (WatchpointMap *wpmap = cx->compartment()->watchpointMap)
5253 wpmap->unwatch(obj, id, nullptr, nullptr);
5254 return true;
5255 }
5257 bool
5258 baseops::Unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id)
5259 {
5260 return UnwatchGuts(cx, obj, id);
5261 }
5263 bool
5264 js::HasDataProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
5265 {
5266 if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
5267 *vp = obj->getDenseElement(JSID_TO_INT(id));
5268 return true;
5269 }
5271 if (Shape *shape = obj->nativeLookup(cx, id)) {
5272 if (shape->hasDefaultGetter() && shape->hasSlot()) {
5273 *vp = obj->nativeGetSlot(shape->slot());
5274 return true;
5275 }
5276 }
5278 return false;
5279 }
5281 /*
5282 * Gets |obj[id]|. If that value's not callable, returns true and stores a
5283 * non-primitive value in *vp. If it's callable, calls it with no arguments
5284 * and |obj| as |this|, returning the result in *vp.
5285 *
5286 * This is a mini-abstraction for ES5 8.12.8 [[DefaultValue]], either steps 1-2
5287 * or steps 3-4.
5288 */
5289 static bool
5290 MaybeCallMethod(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
5291 {
5292 if (!JSObject::getGeneric(cx, obj, obj, id, vp))
5293 return false;
5294 if (!js_IsCallable(vp)) {
5295 vp.setObject(*obj);
5296 return true;
5297 }
5298 return Invoke(cx, ObjectValue(*obj), vp, 0, nullptr, vp);
5299 }
5301 JS_FRIEND_API(bool)
5302 js::DefaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp)
5303 {
5304 JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
5306 Rooted<jsid> id(cx);
5308 const Class *clasp = obj->getClass();
5309 if (hint == JSTYPE_STRING) {
5310 id = NameToId(cx->names().toString);
5312 /* Optimize (new String(...)).toString(). */
5313 if (clasp == &StringObject::class_) {
5314 if (ClassMethodIsNative(cx, obj, &StringObject::class_, id, js_str_toString)) {
5315 vp.setString(obj->as<StringObject>().unbox());
5316 return true;
5317 }
5318 }
5320 if (!MaybeCallMethod(cx, obj, id, vp))
5321 return false;
5322 if (vp.isPrimitive())
5323 return true;
5325 id = NameToId(cx->names().valueOf);
5326 if (!MaybeCallMethod(cx, obj, id, vp))
5327 return false;
5328 if (vp.isPrimitive())
5329 return true;
5330 } else {
5332 /* Optimize new String(...).valueOf(). */
5333 if (clasp == &StringObject::class_) {
5334 id = NameToId(cx->names().valueOf);
5335 if (ClassMethodIsNative(cx, obj, &StringObject::class_, id, js_str_toString)) {
5336 vp.setString(obj->as<StringObject>().unbox());
5337 return true;
5338 }
5339 }
5341 /* Optimize new Number(...).valueOf(). */
5342 if (clasp == &NumberObject::class_) {
5343 id = NameToId(cx->names().valueOf);
5344 if (ClassMethodIsNative(cx, obj, &NumberObject::class_, id, js_num_valueOf)) {
5345 vp.setNumber(obj->as<NumberObject>().unbox());
5346 return true;
5347 }
5348 }
5350 id = NameToId(cx->names().valueOf);
5351 if (!MaybeCallMethod(cx, obj, id, vp))
5352 return false;
5353 if (vp.isPrimitive())
5354 return true;
5356 id = NameToId(cx->names().toString);
5357 if (!MaybeCallMethod(cx, obj, id, vp))
5358 return false;
5359 if (vp.isPrimitive())
5360 return true;
5361 }
5363 /* Avoid recursive death when decompiling in js_ReportValueError. */
5364 RootedString str(cx);
5365 if (hint == JSTYPE_STRING) {
5366 str = JS_InternString(cx, clasp->name);
5367 if (!str)
5368 return false;
5369 } else {
5370 str = nullptr;
5371 }
5373 RootedValue val(cx, ObjectValue(*obj));
5374 js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, JSDVG_SEARCH_STACK, val, str,
5375 (hint == JSTYPE_VOID) ? "primitive type" : TypeStrings[hint]);
5376 return false;
5377 }
5379 JS_FRIEND_API(bool)
5380 JS_EnumerateState(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
5381 MutableHandleValue statep, JS::MutableHandleId idp)
5382 {
5383 /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
5384 const Class *clasp = obj->getClass();
5385 JSEnumerateOp enumerate = clasp->enumerate;
5386 if (clasp->flags & JSCLASS_NEW_ENUMERATE) {
5387 JS_ASSERT(enumerate != JS_EnumerateStub);
5388 return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
5389 }
5391 if (!enumerate(cx, obj))
5392 return false;
5394 /* Tell InitNativeIterator to treat us like a native object. */
5395 JS_ASSERT(enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL);
5396 statep.setMagic(JS_NATIVE_ENUMERATE);
5397 return true;
5398 }
5400 bool
5401 js::IsDelegate(JSContext *cx, HandleObject obj, const js::Value &v, bool *result)
5402 {
5403 if (v.isPrimitive()) {
5404 *result = false;
5405 return true;
5406 }
5407 return IsDelegateOfObject(cx, obj, &v.toObject(), result);
5408 }
5410 bool
5411 js::IsDelegateOfObject(JSContext *cx, HandleObject protoObj, JSObject* obj, bool *result)
5412 {
5413 RootedObject obj2(cx, obj);
5414 for (;;) {
5415 if (!JSObject::getProto(cx, obj2, &obj2))
5416 return false;
5417 if (!obj2) {
5418 *result = false;
5419 return true;
5420 }
5421 if (obj2 == protoObj) {
5422 *result = true;
5423 return true;
5424 }
5425 }
5426 }
5428 JSObject *
5429 js::GetBuiltinPrototypePure(GlobalObject *global, JSProtoKey protoKey)
5430 {
5431 JS_ASSERT(JSProto_Null <= protoKey);
5432 JS_ASSERT(protoKey < JSProto_LIMIT);
5434 if (protoKey != JSProto_Null) {
5435 const Value &v = global->getPrototype(protoKey);
5436 if (v.isObject())
5437 return &v.toObject();
5438 }
5440 return nullptr;
5441 }
5443 /*
5444 * The first part of this function has been hand-expanded and optimized into
5445 * NewBuiltinClassInstance in jsobjinlines.h.
5446 */
5447 bool
5448 js::FindClassPrototype(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp)
5449 {
5450 protop.set(nullptr);
5451 JSProtoKey protoKey = GetClassProtoKey(clasp);
5452 if (protoKey != JSProto_Null)
5453 return GetBuiltinPrototype(cx, protoKey, protop);
5455 RootedObject ctor(cx);
5456 if (!FindClassObject(cx, &ctor, clasp))
5457 return false;
5459 if (ctor && ctor->is<JSFunction>()) {
5460 RootedValue v(cx);
5461 if (cx->isJSContext()) {
5462 if (!JSObject::getProperty(cx->asJSContext(),
5463 ctor, ctor, cx->names().prototype, &v))
5464 {
5465 return false;
5466 }
5467 } else {
5468 Shape *shape = ctor->nativeLookup(cx, cx->names().prototype);
5469 if (!shape || !NativeGetPureInline(ctor, shape, v.address()))
5470 return false;
5471 }
5472 if (v.isObject())
5473 protop.set(&v.toObject());
5474 }
5475 return true;
5476 }
5478 JSObject *
5479 js::PrimitiveToObject(JSContext *cx, const Value &v)
5480 {
5481 if (v.isString()) {
5482 Rooted<JSString*> str(cx, v.toString());
5483 return StringObject::create(cx, str);
5484 }
5485 if (v.isNumber())
5486 return NumberObject::create(cx, v.toNumber());
5488 JS_ASSERT(v.isBoolean());
5489 return BooleanObject::create(cx, v.toBoolean());
5490 }
5492 /* Callers must handle the already-object case . */
5493 JSObject *
5494 js::ToObjectSlow(JSContext *cx, HandleValue val, bool reportScanStack)
5495 {
5496 JS_ASSERT(!val.isMagic());
5497 JS_ASSERT(!val.isObject());
5499 if (val.isNullOrUndefined()) {
5500 if (reportScanStack) {
5501 js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, val, NullPtr());
5502 } else {
5503 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
5504 val.isNull() ? "null" : "undefined", "object");
5505 }
5506 return nullptr;
5507 }
5509 return PrimitiveToObject(cx, val);
5510 }
5512 void
5513 js_GetObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
5514 {
5515 JS_ASSERT(trc->debugPrinter() == js_GetObjectSlotName);
5517 JSObject *obj = (JSObject *)trc->debugPrintArg();
5518 uint32_t slot = uint32_t(trc->debugPrintIndex());
5520 Shape *shape;
5521 if (obj->isNative()) {
5522 shape = obj->lastProperty();
5523 while (shape && (!shape->hasSlot() || shape->slot() != slot))
5524 shape = shape->previous();
5525 } else {
5526 shape = nullptr;
5527 }
5529 if (!shape) {
5530 const char *slotname = nullptr;
5531 if (obj->is<GlobalObject>()) {
5532 #define TEST_SLOT_MATCHES_PROTOTYPE(name,code,init,clasp) \
5533 if ((code) == slot) { slotname = js_##name##_str; goto found; }
5534 JS_FOR_EACH_PROTOTYPE(TEST_SLOT_MATCHES_PROTOTYPE)
5535 #undef TEST_SLOT_MATCHES_PROTOTYPE
5536 }
5537 found:
5538 if (slotname)
5539 JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname);
5540 else
5541 JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot);
5542 } else {
5543 jsid propid = shape->propid();
5544 if (JSID_IS_INT(propid)) {
5545 JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(propid));
5546 } else if (JSID_IS_ATOM(propid)) {
5547 PutEscapedString(buf, bufsize, JSID_TO_ATOM(propid), 0);
5548 } else {
5549 JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**");
5550 }
5551 }
5552 }
5554 bool
5555 js_ReportGetterOnlyAssignment(JSContext *cx, bool strict)
5556 {
5557 return JS_ReportErrorFlagsAndNumber(cx,
5558 strict
5559 ? JSREPORT_ERROR
5560 : JSREPORT_WARNING | JSREPORT_STRICT,
5561 js_GetErrorMessage, nullptr,
5562 JSMSG_GETTER_ONLY);
5563 }
5565 JS_FRIEND_API(bool)
5566 js_GetterOnlyPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict,
5567 MutableHandleValue vp)
5568 {
5569 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
5570 return false;
5571 }
5573 #ifdef DEBUG
5575 /*
5576 * Routines to print out values during debugging. These are FRIEND_API to help
5577 * the debugger find them and to support temporarily hacking js_Dump* calls
5578 * into other code.
5579 */
5581 static void
5582 dumpValue(const Value &v)
5583 {
5584 if (v.isNull())
5585 fprintf(stderr, "null");
5586 else if (v.isUndefined())
5587 fprintf(stderr, "undefined");
5588 else if (v.isInt32())
5589 fprintf(stderr, "%d", v.toInt32());
5590 else if (v.isDouble())
5591 fprintf(stderr, "%g", v.toDouble());
5592 else if (v.isString())
5593 v.toString()->dump();
5594 else if (v.isObject() && v.toObject().is<JSFunction>()) {
5595 JSFunction *fun = &v.toObject().as<JSFunction>();
5596 if (fun->displayAtom()) {
5597 fputs("<function ", stderr);
5598 FileEscapedString(stderr, fun->displayAtom(), 0);
5599 } else {
5600 fputs("<unnamed function", stderr);
5601 }
5602 if (fun->hasScript()) {
5603 JSScript *script = fun->nonLazyScript();
5604 fprintf(stderr, " (%s:%d)",
5605 script->filename() ? script->filename() : "", (int) script->lineno());
5606 }
5607 fprintf(stderr, " at %p>", (void *) fun);
5608 } else if (v.isObject()) {
5609 JSObject *obj = &v.toObject();
5610 const Class *clasp = obj->getClass();
5611 fprintf(stderr, "<%s%s at %p>",
5612 clasp->name,
5613 (clasp == &JSObject::class_) ? "" : " object",
5614 (void *) obj);
5615 } else if (v.isBoolean()) {
5616 if (v.toBoolean())
5617 fprintf(stderr, "true");
5618 else
5619 fprintf(stderr, "false");
5620 } else if (v.isMagic()) {
5621 fprintf(stderr, "<invalid");
5622 #ifdef DEBUG
5623 switch (v.whyMagic()) {
5624 case JS_ELEMENTS_HOLE: fprintf(stderr, " elements hole"); break;
5625 case JS_NATIVE_ENUMERATE: fprintf(stderr, " native enumeration"); break;
5626 case JS_NO_ITER_VALUE: fprintf(stderr, " no iter value"); break;
5627 case JS_GENERATOR_CLOSING: fprintf(stderr, " generator closing"); break;
5628 case JS_OPTIMIZED_OUT: fprintf(stderr, " optimized out"); break;
5629 default: fprintf(stderr, " ?!"); break;
5630 }
5631 #endif
5632 fprintf(stderr, ">");
5633 } else {
5634 fprintf(stderr, "unexpected value");
5635 }
5636 }
5638 JS_FRIEND_API(void)
5639 js_DumpValue(const Value &val)
5640 {
5641 dumpValue(val);
5642 fputc('\n', stderr);
5643 }
5645 JS_FRIEND_API(void)
5646 js_DumpId(jsid id)
5647 {
5648 fprintf(stderr, "jsid %p = ", (void *) JSID_BITS(id));
5649 dumpValue(IdToValue(id));
5650 fputc('\n', stderr);
5651 }
5653 static void
5654 DumpProperty(JSObject *obj, Shape &shape)
5655 {
5656 jsid id = shape.propid();
5657 uint8_t attrs = shape.attributes();
5659 fprintf(stderr, " ((JSShape *) %p) ", (void *) &shape);
5660 if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate ");
5661 if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly ");
5662 if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent ");
5663 if (attrs & JSPROP_SHARED) fprintf(stderr, "shared ");
5665 if (shape.hasGetterValue())
5666 fprintf(stderr, "getterValue=%p ", (void *) shape.getterObject());
5667 else if (!shape.hasDefaultGetter())
5668 fprintf(stderr, "getterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.getterOp()));
5670 if (shape.hasSetterValue())
5671 fprintf(stderr, "setterValue=%p ", (void *) shape.setterObject());
5672 else if (!shape.hasDefaultSetter())
5673 fprintf(stderr, "setterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.setterOp()));
5675 if (JSID_IS_ATOM(id))
5676 JSID_TO_STRING(id)->dump();
5677 else if (JSID_IS_INT(id))
5678 fprintf(stderr, "%d", (int) JSID_TO_INT(id));
5679 else
5680 fprintf(stderr, "unknown jsid %p", (void *) JSID_BITS(id));
5682 uint32_t slot = shape.hasSlot() ? shape.maybeSlot() : SHAPE_INVALID_SLOT;
5683 fprintf(stderr, ": slot %d", slot);
5684 if (shape.hasSlot()) {
5685 fprintf(stderr, " = ");
5686 dumpValue(obj->getSlot(slot));
5687 } else if (slot != SHAPE_INVALID_SLOT) {
5688 fprintf(stderr, " (INVALID!)");
5689 }
5690 fprintf(stderr, "\n");
5691 }
5693 bool
5694 JSObject::uninlinedIsProxy() const
5695 {
5696 return is<ProxyObject>();
5697 }
5699 void
5700 JSObject::dump()
5701 {
5702 JSObject *obj = this;
5703 fprintf(stderr, "object %p\n", (void *) obj);
5704 const Class *clasp = obj->getClass();
5705 fprintf(stderr, "class %p %s\n", (const void *)clasp, clasp->name);
5707 fprintf(stderr, "flags:");
5708 if (obj->isDelegate()) fprintf(stderr, " delegate");
5709 if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible()) fprintf(stderr, " not_extensible");
5710 if (obj->isIndexed()) fprintf(stderr, " indexed");
5711 if (obj->isBoundFunction()) fprintf(stderr, " bound_function");
5712 if (obj->isVarObj()) fprintf(stderr, " varobj");
5713 if (obj->watched()) fprintf(stderr, " watched");
5714 if (obj->isIteratedSingleton()) fprintf(stderr, " iterated_singleton");
5715 if (obj->isNewTypeUnknown()) fprintf(stderr, " new_type_unknown");
5716 if (obj->hasUncacheableProto()) fprintf(stderr, " has_uncacheable_proto");
5717 if (obj->hadElementsAccess()) fprintf(stderr, " had_elements_access");
5719 if (obj->isNative()) {
5720 if (obj->inDictionaryMode())
5721 fprintf(stderr, " inDictionaryMode");
5722 if (obj->hasShapeTable())
5723 fprintf(stderr, " hasShapeTable");
5724 }
5725 fprintf(stderr, "\n");
5727 if (obj->isNative()) {
5728 uint32_t slots = obj->getDenseInitializedLength();
5729 if (slots) {
5730 fprintf(stderr, "elements\n");
5731 for (uint32_t i = 0; i < slots; i++) {
5732 fprintf(stderr, " %3d: ", i);
5733 dumpValue(obj->getDenseElement(i));
5734 fprintf(stderr, "\n");
5735 fflush(stderr);
5736 }
5737 }
5738 }
5740 fprintf(stderr, "proto ");
5741 TaggedProto proto = obj->getTaggedProto();
5742 if (proto.isLazy())
5743 fprintf(stderr, "<lazy>");
5744 else
5745 dumpValue(ObjectOrNullValue(proto.toObjectOrNull()));
5746 fputc('\n', stderr);
5748 fprintf(stderr, "parent ");
5749 dumpValue(ObjectOrNullValue(obj->getParent()));
5750 fputc('\n', stderr);
5752 if (clasp->flags & JSCLASS_HAS_PRIVATE)
5753 fprintf(stderr, "private %p\n", obj->getPrivate());
5755 if (!obj->isNative())
5756 fprintf(stderr, "not native\n");
5758 uint32_t reservedEnd = JSCLASS_RESERVED_SLOTS(clasp);
5759 uint32_t slots = obj->slotSpan();
5760 uint32_t stop = obj->isNative() ? reservedEnd : slots;
5761 if (stop > 0)
5762 fprintf(stderr, obj->isNative() ? "reserved slots:\n" : "slots:\n");
5763 for (uint32_t i = 0; i < stop; i++) {
5764 fprintf(stderr, " %3d ", i);
5765 if (i < reservedEnd)
5766 fprintf(stderr, "(reserved) ");
5767 fprintf(stderr, "= ");
5768 dumpValue(obj->getSlot(i));
5769 fputc('\n', stderr);
5770 }
5772 if (obj->isNative()) {
5773 fprintf(stderr, "properties:\n");
5774 Vector<Shape *, 8, SystemAllocPolicy> props;
5775 for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront())
5776 props.append(&r.front());
5777 for (size_t i = props.length(); i-- != 0;)
5778 DumpProperty(obj, *props[i]);
5779 }
5780 fputc('\n', stderr);
5781 }
5783 static void
5784 MaybeDumpObject(const char *name, JSObject *obj)
5785 {
5786 if (obj) {
5787 fprintf(stderr, " %s: ", name);
5788 dumpValue(ObjectValue(*obj));
5789 fputc('\n', stderr);
5790 }
5791 }
5793 static void
5794 MaybeDumpValue(const char *name, const Value &v)
5795 {
5796 if (!v.isNull()) {
5797 fprintf(stderr, " %s: ", name);
5798 dumpValue(v);
5799 fputc('\n', stderr);
5800 }
5801 }
5803 JS_FRIEND_API(void)
5804 js_DumpInterpreterFrame(JSContext *cx, InterpreterFrame *start)
5805 {
5806 /* This should only called during live debugging. */
5807 ScriptFrameIter i(cx, ScriptFrameIter::GO_THROUGH_SAVED);
5808 if (!start) {
5809 if (i.done()) {
5810 fprintf(stderr, "no stack for cx = %p\n", (void*) cx);
5811 return;
5812 }
5813 } else {
5814 while (!i.done() && !i.isJit() && i.interpFrame() != start)
5815 ++i;
5817 if (i.done()) {
5818 fprintf(stderr, "fp = %p not found in cx = %p\n",
5819 (void *)start, (void *)cx);
5820 return;
5821 }
5822 }
5824 for (; !i.done(); ++i) {
5825 if (i.isJit())
5826 fprintf(stderr, "JIT frame\n");
5827 else
5828 fprintf(stderr, "InterpreterFrame at %p\n", (void *) i.interpFrame());
5830 if (i.isFunctionFrame()) {
5831 fprintf(stderr, "callee fun: ");
5832 dumpValue(i.calleev());
5833 } else {
5834 fprintf(stderr, "global frame, no callee");
5835 }
5836 fputc('\n', stderr);
5838 fprintf(stderr, "file %s line %u\n",
5839 i.script()->filename(), (unsigned) i.script()->lineno());
5841 if (jsbytecode *pc = i.pc()) {
5842 fprintf(stderr, " pc = %p\n", pc);
5843 fprintf(stderr, " current op: %s\n", js_CodeName[*pc]);
5844 MaybeDumpObject("staticScope", i.script()->getStaticScope(pc));
5845 }
5846 MaybeDumpValue("this", i.thisv());
5847 if (!i.isJit()) {
5848 fprintf(stderr, " rval: ");
5849 dumpValue(i.interpFrame()->returnValue());
5850 fputc('\n', stderr);
5851 }
5853 fprintf(stderr, " flags:");
5854 if (i.isConstructing())
5855 fprintf(stderr, " constructing");
5856 if (!i.isJit() && i.interpFrame()->isDebuggerFrame())
5857 fprintf(stderr, " debugger");
5858 if (i.isEvalFrame())
5859 fprintf(stderr, " eval");
5860 if (!i.isJit() && i.interpFrame()->isYielding())
5861 fprintf(stderr, " yielding");
5862 if (!i.isJit() && i.interpFrame()->isGeneratorFrame())
5863 fprintf(stderr, " generator");
5864 fputc('\n', stderr);
5866 fprintf(stderr, " scopeChain: (JSObject *) %p\n", (void *) i.scopeChain());
5868 fputc('\n', stderr);
5869 }
5870 }
5872 #endif /* DEBUG */
5874 JS_FRIEND_API(void)
5875 js_DumpBacktrace(JSContext *cx)
5876 {
5877 Sprinter sprinter(cx);
5878 sprinter.init();
5879 size_t depth = 0;
5880 for (ScriptFrameIter i(cx); !i.done(); ++i, ++depth) {
5881 const char *filename = JS_GetScriptFilename(i.script());
5882 unsigned line = JS_PCToLineNumber(cx, i.script(), i.pc());
5883 JSScript *script = i.script();
5884 sprinter.printf("#%d %14p %s:%d (%p @ %d)\n",
5885 depth, (i.isJit() ? 0 : i.interpFrame()), filename, line,
5886 script, script->pcToOffset(i.pc()));
5887 }
5888 fprintf(stdout, "%s", sprinter.string());
5889 }
5890 void
5891 JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes)
5892 {
5893 if (hasDynamicSlots())
5894 sizes->mallocHeapSlots += mallocSizeOf(slots);
5896 if (hasDynamicElements()) {
5897 js::ObjectElements *elements = getElementsHeader();
5898 sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements);
5899 }
5901 // Other things may be measured in the future if DMD indicates it is worthwhile.
5902 if (is<JSFunction>() ||
5903 is<JSObject>() ||
5904 is<ArrayObject>() ||
5905 is<CallObject>() ||
5906 is<RegExpObject>() ||
5907 is<ProxyObject>())
5908 {
5909 // Do nothing. But this function is hot, and we win by getting the
5910 // common cases out of the way early. Some stats on the most common
5911 // classes, as measured during a vanilla browser session:
5912 // - (53.7%, 53.7%): Function
5913 // - (18.0%, 71.7%): Object
5914 // - (16.9%, 88.6%): Array
5915 // - ( 3.9%, 92.5%): Call
5916 // - ( 2.8%, 95.3%): RegExp
5917 // - ( 1.0%, 96.4%): Proxy
5919 } else if (is<ArgumentsObject>()) {
5920 sizes->mallocHeapArgumentsData += as<ArgumentsObject>().sizeOfMisc(mallocSizeOf);
5921 } else if (is<RegExpStaticsObject>()) {
5922 sizes->mallocHeapRegExpStatics += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
5923 } else if (is<PropertyIteratorObject>()) {
5924 sizes->mallocHeapPropertyIteratorData += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
5925 } else if (is<ArrayBufferObject>() || is<SharedArrayBufferObject>()) {
5926 ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, sizes);
5927 #ifdef JS_ION
5928 } else if (is<AsmJSModuleObject>()) {
5929 as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &sizes->nonHeapCodeAsmJS,
5930 &sizes->mallocHeapAsmJSModuleData);
5931 #endif
5932 #ifdef JS_HAS_CTYPES
5933 } else {
5934 // This must be the last case.
5935 sizes->mallocHeapCtypesData +=
5936 js::SizeOfDataIfCDataObject(mallocSizeOf, const_cast<JSObject *>(this));
5937 #endif
5938 }
5939 }