Bumping manifests a=b2g-bump
[gecko.git] / js / src / jsobj.cpp
blob0fdda0ebd8cae09458ffe49ab0f7b855db0a65ed
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/CheckedInt.h"
15 #include "mozilla/MathAlgorithms.h"
16 #include "mozilla/MemoryReporting.h"
17 #include "mozilla/TemplateLib.h"
19 #include <string.h>
21 #include "jsapi.h"
22 #include "jsarray.h"
23 #include "jsatom.h"
24 #include "jscntxt.h"
25 #include "jsfriendapi.h"
26 #include "jsfun.h"
27 #include "jsgc.h"
28 #include "jsiter.h"
29 #include "jsnum.h"
30 #include "jsopcode.h"
31 #include "jsprf.h"
32 #include "jsproxy.h"
33 #include "jsscript.h"
34 #include "jsstr.h"
35 #include "jstypes.h"
36 #include "jsutil.h"
37 #include "jswatchpoint.h"
38 #include "jswrapper.h"
40 #include "asmjs/AsmJSModule.h"
41 #include "builtin/Eval.h"
42 #include "builtin/Object.h"
43 #include "builtin/SymbolObject.h"
44 #include "frontend/BytecodeCompiler.h"
45 #include "gc/Marking.h"
46 #include "jit/BaselineJIT.h"
47 #include "js/MemoryMetrics.h"
48 #include "js/OldDebugAPI.h"
49 #include "vm/ArgumentsObject.h"
50 #include "vm/Interpreter.h"
51 #include "vm/ProxyObject.h"
52 #include "vm/RegExpStaticsObject.h"
53 #include "vm/Shape.h"
55 #include "jsatominlines.h"
56 #include "jsboolinlines.h"
57 #include "jscntxtinlines.h"
58 #include "jscompartmentinlines.h"
60 #include "vm/ArrayObject-inl.h"
61 #include "vm/BooleanObject-inl.h"
62 #include "vm/NumberObject-inl.h"
63 #include "vm/ObjectImpl-inl.h"
64 #include "vm/Runtime-inl.h"
65 #include "vm/Shape-inl.h"
66 #include "vm/StringObject-inl.h"
68 using namespace js;
69 using namespace js::gc;
70 using namespace js::types;
72 using mozilla::Maybe;
73 using mozilla::RoundUpPow2;
75 JS_STATIC_ASSERT(int32_t((JSObject::NELEMENTS_LIMIT - 1) * sizeof(Value)) == int64_t((JSObject::NELEMENTS_LIMIT - 1) * sizeof(Value)));
77 static JSObject *
78 CreateObjectConstructor(JSContext *cx, JSProtoKey key)
80 Rooted<GlobalObject*> self(cx, cx->global());
81 if (!GlobalObject::ensureConstructor(cx, self, JSProto_Function))
82 return nullptr;
84 RootedObject functionProto(cx, &self->getPrototype(JSProto_Function).toObject());
86 /* Create the Object function now that we have a [[Prototype]] for it. */
87 RootedObject ctor(cx, NewObjectWithGivenProto(cx, &JSFunction::class_, functionProto,
88 self, SingletonObject));
89 if (!ctor)
90 return nullptr;
91 return NewFunction(cx, ctor, obj_construct, 1, JSFunction::NATIVE_CTOR, self,
92 HandlePropertyName(cx->names().Object));
95 static JSObject *
96 CreateObjectPrototype(JSContext *cx, JSProtoKey key)
98 Rooted<GlobalObject*> self(cx, cx->global());
100 JS_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
101 JS_ASSERT(self->isNative());
104 * Create |Object.prototype| first, mirroring CreateBlankProto but for the
105 * prototype of the created object.
107 RootedObject objectProto(cx, NewObjectWithGivenProto(cx, &JSObject::class_, nullptr,
108 self, SingletonObject));
109 if (!objectProto)
110 return nullptr;
113 * The default 'new' type of Object.prototype is required by type inference
114 * to have unknown properties, to simplify handling of e.g. heterogenous
115 * objects in JSON and script literals.
117 if (!JSObject::setNewTypeUnknown(cx, &JSObject::class_, objectProto))
118 return nullptr;
120 return objectProto;
123 static bool
124 FinishObjectClassInit(JSContext *cx, JS::HandleObject ctor, JS::HandleObject proto)
126 Rooted<GlobalObject*> self(cx, cx->global());
128 /* ES5 15.1.2.1. */
129 RootedId evalId(cx, NameToId(cx->names().eval));
130 JSObject *evalobj = DefineFunction(cx, self, evalId, IndirectEval, 1, JSFUN_STUB_GSOPS);
131 if (!evalobj)
132 return false;
133 self->setOriginalEval(evalobj);
135 RootedObject intrinsicsHolder(cx);
136 if (cx->runtime()->isSelfHostingGlobal(self)) {
137 intrinsicsHolder = self;
138 } else {
139 intrinsicsHolder = NewObjectWithGivenProto(cx, &JSObject::class_, proto, self,
140 TenuredObject);
141 if (!intrinsicsHolder)
142 return false;
144 self->setIntrinsicsHolder(intrinsicsHolder);
145 /* Define a property 'global' with the current global as its value. */
146 RootedValue global(cx, ObjectValue(*self));
147 if (!JSObject::defineProperty(cx, intrinsicsHolder, cx->names().global,
148 global, JS_PropertyStub, JS_StrictPropertyStub,
149 JSPROP_PERMANENT | JSPROP_READONLY))
151 return false;
155 * Define self-hosted functions after setting the intrinsics holder
156 * (which is needed to define self-hosted functions)
158 if (!JS_DefineFunctions(cx, ctor, object_static_selfhosted_methods))
159 return false;
162 * The global object should have |Object.prototype| as its [[Prototype]].
163 * Eventually we'd like to have standard classes be there from the start,
164 * and thus we would know we were always setting what had previously been a
165 * null [[Prototype]], but right now some code assumes it can set the
166 * [[Prototype]] before standard classes have been initialized. For now,
167 * only set the [[Prototype]] if it hasn't already been set.
169 Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
170 if (self->shouldSplicePrototype(cx) && !self->splicePrototype(cx, self->getClass(), tagged))
171 return false;
172 return true;
176 const Class JSObject::class_ = {
177 js_Object_str,
178 JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
179 JS_PropertyStub, /* addProperty */
180 JS_DeletePropertyStub, /* delProperty */
181 JS_PropertyStub, /* getProperty */
182 JS_StrictPropertyStub, /* setProperty */
183 JS_EnumerateStub,
184 JS_ResolveStub,
185 JS_ConvertStub,
186 nullptr, /* finalize */
187 nullptr, /* call */
188 nullptr, /* hasInstance */
189 nullptr, /* construct */
190 nullptr, /* trace */
192 CreateObjectConstructor,
193 CreateObjectPrototype,
194 object_static_methods,
195 object_methods,
196 object_properties,
197 FinishObjectClassInit
201 const Class* const js::ObjectClassPtr = &JSObject::class_;
203 JS_FRIEND_API(JSObject *)
204 JS_ObjectToInnerObject(JSContext *cx, HandleObject obj)
206 if (!obj) {
207 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INACTIVE);
208 return nullptr;
210 return GetInnerObject(obj);
213 JS_FRIEND_API(JSObject *)
214 JS_ObjectToOuterObject(JSContext *cx, HandleObject obj)
216 assertSameCompartment(cx, obj);
217 return GetOuterObject(cx, obj);
220 JSObject *
221 js::NonNullObject(JSContext *cx, const Value &v)
223 if (v.isPrimitive()) {
224 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
225 return nullptr;
227 return &v.toObject();
230 const char *
231 js::InformalValueTypeName(const Value &v)
233 if (v.isObject())
234 return v.toObject().getClass()->name;
235 if (v.isString())
236 return "string";
237 if (v.isSymbol())
238 return "symbol";
239 if (v.isNumber())
240 return "number";
241 if (v.isBoolean())
242 return "boolean";
243 if (v.isNull())
244 return "null";
245 if (v.isUndefined())
246 return "undefined";
247 return "value";
250 bool
251 js::NewPropertyDescriptorObject(JSContext *cx, Handle<PropertyDescriptor> desc,
252 MutableHandleValue vp)
254 if (!desc.object()) {
255 vp.setUndefined();
256 return true;
259 Rooted<PropDesc> d(cx);
261 d.initFromPropertyDescriptor(desc);
262 RootedObject descObj(cx);
263 if (!d.makeObject(cx, &descObj))
264 return false;
265 vp.setObject(*descObj);
266 return true;
269 void
270 PropDesc::initFromPropertyDescriptor(Handle<PropertyDescriptor> desc)
272 MOZ_ASSERT(isUndefined());
274 if (!desc.object())
275 return;
277 isUndefined_ = false;
278 attrs = uint8_t(desc.attributes());
279 JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
280 if (desc.hasGetterOrSetterObject()) {
281 hasGet_ = true;
282 get_ = desc.hasGetterObject() && desc.getterObject()
283 ? ObjectValue(*desc.getterObject())
284 : UndefinedValue();
285 hasSet_ = true;
286 set_ = desc.hasSetterObject() && desc.setterObject()
287 ? ObjectValue(*desc.setterObject())
288 : UndefinedValue();
289 hasValue_ = false;
290 value_.setUndefined();
291 hasWritable_ = false;
292 } else {
293 hasGet_ = false;
294 get_.setUndefined();
295 hasSet_ = false;
296 set_.setUndefined();
297 hasValue_ = !(desc.attributes() & JSPROP_IGNORE_VALUE);
298 value_ = hasValue_ ? desc.value() : UndefinedValue();
299 hasWritable_ = !(desc.attributes() & JSPROP_IGNORE_READONLY);
301 hasEnumerable_ = !(desc.attributes() & JSPROP_IGNORE_ENUMERATE);
302 hasConfigurable_ = !(desc.attributes() & JSPROP_IGNORE_PERMANENT);
305 void
306 PropDesc::populatePropertyDescriptor(HandleObject obj, MutableHandle<PropertyDescriptor> desc) const
308 if (isUndefined()) {
309 desc.object().set(nullptr);
310 return;
313 desc.value().set(hasValue() ? value() : UndefinedValue());
314 desc.setGetter(getter());
315 desc.setSetter(setter());
317 // Make sure we honor the "has" notions in some way.
318 unsigned attrs = attributes();
319 if (!hasEnumerable())
320 attrs |= JSPROP_IGNORE_ENUMERATE;
321 if (!hasWritable())
322 attrs |= JSPROP_IGNORE_READONLY;
323 if (!hasConfigurable())
324 attrs |= JSPROP_IGNORE_PERMANENT;
325 if (!hasValue())
326 attrs |= JSPROP_IGNORE_VALUE;
327 desc.setAttributes(attrs);
329 desc.object().set(obj);
332 bool
333 PropDesc::makeObject(JSContext *cx, MutableHandleObject obj)
335 MOZ_ASSERT(!isUndefined());
337 obj.set(NewBuiltinClassInstance(cx, &JSObject::class_));
338 if (!obj)
339 return false;
341 const JSAtomState &names = cx->names();
342 RootedValue configurableVal(cx, BooleanValue((attrs & JSPROP_PERMANENT) == 0));
343 RootedValue enumerableVal(cx, BooleanValue((attrs & JSPROP_ENUMERATE) != 0));
344 RootedValue writableVal(cx, BooleanValue((attrs & JSPROP_READONLY) == 0));
345 if ((hasConfigurable() &&
346 !JSObject::defineProperty(cx, obj, names.configurable, configurableVal)) ||
347 (hasEnumerable() &&
348 !JSObject::defineProperty(cx, obj, names.enumerable, enumerableVal)) ||
349 (hasGet() &&
350 !JSObject::defineProperty(cx, obj, names.get, getterValue())) ||
351 (hasSet() &&
352 !JSObject::defineProperty(cx, obj, names.set, setterValue())) ||
353 (hasValue() &&
354 !JSObject::defineProperty(cx, obj, names.value, value())) ||
355 (hasWritable() &&
356 !JSObject::defineProperty(cx, obj, names.writable, writableVal)))
358 return false;
361 return true;
364 bool
365 js::GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
366 MutableHandle<PropertyDescriptor> desc)
368 if (obj->is<ProxyObject>())
369 return Proxy::getOwnPropertyDescriptor(cx, obj, id, desc);
371 RootedObject pobj(cx);
372 RootedShape shape(cx);
373 if (!HasOwnProperty<CanGC>(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &shape))
374 return false;
375 if (!shape) {
376 desc.object().set(nullptr);
377 return true;
380 bool doGet = true;
381 if (pobj->isNative()) {
382 desc.setAttributes(GetShapeAttributes(pobj, shape));
383 if (desc.hasGetterOrSetterObject()) {
384 MOZ_ASSERT(desc.isShared());
385 doGet = false;
386 if (desc.hasGetterObject())
387 desc.setGetterObject(shape->getterObject());
388 if (desc.hasSetterObject())
389 desc.setSetterObject(shape->setterObject());
390 } else {
391 // This is either a straight-up data property or (rarely) a
392 // property with a JSPropertyOp getter/setter. The latter must be
393 // reported to the caller as a plain data property, so don't
394 // populate desc.getter/setter, and mask away the SHARED bit.
395 desc.attributesRef() &= ~JSPROP_SHARED;
397 } else {
398 if (!JSObject::getGenericAttributes(cx, pobj, id, &desc.attributesRef()))
399 return false;
402 RootedValue value(cx);
403 if (doGet && !JSObject::getGeneric(cx, obj, obj, id, &value))
404 return false;
406 desc.value().set(value);
407 desc.object().set(obj);
408 return true;
411 bool
412 js::GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
414 Rooted<PropertyDescriptor> desc(cx);
415 return GetOwnPropertyDescriptor(cx, obj, id, &desc) &&
416 NewPropertyDescriptorObject(cx, desc, vp);
419 bool
420 js::GetFirstArgumentAsObject(JSContext *cx, const CallArgs &args, const char *method,
421 MutableHandleObject objp)
423 if (args.length() == 0) {
424 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
425 method, "0", "s");
426 return false;
429 HandleValue v = args[0];
430 if (!v.isObject()) {
431 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NullPtr());
432 if (!bytes)
433 return false;
434 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
435 bytes, "not an object");
436 js_free(bytes);
437 return false;
440 objp.set(&v.toObject());
441 return true;
444 static bool
445 HasProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp, bool *foundp)
447 if (!JSObject::hasProperty(cx, obj, id, foundp))
448 return false;
449 if (!*foundp) {
450 vp.setUndefined();
451 return true;
455 * We must go through the method read barrier in case id is 'get' or 'set'.
456 * There is no obvious way to defer cloning a joined function object whose
457 * identity will be used by DefinePropertyOnObject, e.g., or reflected via
458 * js::GetOwnPropertyDescriptor, as the getter or setter callable object.
460 return !!JSObject::getGeneric(cx, obj, obj, id, vp);
463 bool
464 PropDesc::initialize(JSContext *cx, const Value &origval, bool checkAccessors)
466 MOZ_ASSERT(isUndefined());
468 RootedValue v(cx, origval);
470 /* 8.10.5 step 1 */
471 if (v.isPrimitive()) {
472 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
473 return false;
475 RootedObject desc(cx, &v.toObject());
477 isUndefined_ = false;
480 * Start with the proper defaults. XXX shouldn't be necessary when we get
481 * rid of PropDesc::attributes()
483 attrs = JSPROP_PERMANENT | JSPROP_READONLY;
485 bool found = false;
486 RootedId id(cx);
488 /* 8.10.5 step 3 */
489 id = NameToId(cx->names().enumerable);
490 if (!HasProperty(cx, desc, id, &v, &found))
491 return false;
492 if (found) {
493 hasEnumerable_ = true;
494 if (ToBoolean(v))
495 attrs |= JSPROP_ENUMERATE;
498 /* 8.10.5 step 4 */
499 id = NameToId(cx->names().configurable);
500 if (!HasProperty(cx, desc, id, &v, &found))
501 return false;
502 if (found) {
503 hasConfigurable_ = true;
504 if (ToBoolean(v))
505 attrs &= ~JSPROP_PERMANENT;
508 /* 8.10.5 step 5 */
509 id = NameToId(cx->names().value);
510 if (!HasProperty(cx, desc, id, &v, &found))
511 return false;
512 if (found) {
513 hasValue_ = true;
514 value_ = v;
517 /* 8.10.6 step 6 */
518 id = NameToId(cx->names().writable);
519 if (!HasProperty(cx, desc, id, &v, &found))
520 return false;
521 if (found) {
522 hasWritable_ = true;
523 if (ToBoolean(v))
524 attrs &= ~JSPROP_READONLY;
527 /* 8.10.7 step 7 */
528 id = NameToId(cx->names().get);
529 if (!HasProperty(cx, desc, id, &v, &found))
530 return false;
531 if (found) {
532 hasGet_ = true;
533 get_ = v;
534 attrs |= JSPROP_GETTER | JSPROP_SHARED;
535 attrs &= ~JSPROP_READONLY;
536 if (checkAccessors && !checkGetter(cx))
537 return false;
540 /* 8.10.7 step 8 */
541 id = NameToId(cx->names().set);
542 if (!HasProperty(cx, desc, id, &v, &found))
543 return false;
544 if (found) {
545 hasSet_ = true;
546 set_ = v;
547 attrs |= JSPROP_SETTER | JSPROP_SHARED;
548 attrs &= ~JSPROP_READONLY;
549 if (checkAccessors && !checkSetter(cx))
550 return false;
553 /* 8.10.7 step 9 */
554 if ((hasGet() || hasSet()) && (hasValue() || hasWritable())) {
555 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INVALID_DESCRIPTOR);
556 return false;
559 JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
561 return true;
564 void
565 PropDesc::complete()
567 MOZ_ASSERT(!isUndefined());
569 if (isGenericDescriptor() || isDataDescriptor()) {
570 if (!hasValue_) {
571 hasValue_ = true;
572 value_.setUndefined();
574 if (!hasWritable_) {
575 hasWritable_ = true;
576 attrs |= JSPROP_READONLY;
578 } else {
579 if (!hasGet_) {
580 hasGet_ = true;
581 get_.setUndefined();
583 if (!hasSet_) {
584 hasSet_ = true;
585 set_.setUndefined();
588 if (!hasEnumerable_) {
589 hasEnumerable_ = true;
590 attrs &= ~JSPROP_ENUMERATE;
592 if (!hasConfigurable_) {
593 hasConfigurable_ = true;
594 attrs |= JSPROP_PERMANENT;
598 bool
599 js::Throw(JSContext *cx, jsid id, unsigned errorNumber)
601 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount == 1);
603 JSString *idstr = IdToString(cx, id);
604 if (!idstr)
605 return false;
606 JSAutoByteString bytes(cx, idstr);
607 if (!bytes)
608 return false;
609 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, errorNumber, bytes.ptr());
610 return false;
613 bool
614 js::Throw(JSContext *cx, JSObject *obj, unsigned errorNumber)
616 if (js_ErrorFormatString[errorNumber].argCount == 1) {
617 RootedValue val(cx, ObjectValue(*obj));
618 js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,
619 JSDVG_IGNORE_STACK, val, NullPtr(),
620 nullptr, nullptr);
621 } else {
622 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount == 0);
623 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, errorNumber);
625 return false;
628 static bool
629 Reject(JSContext *cx, unsigned errorNumber, bool throwError, jsid id, bool *rval)
631 if (throwError)
632 return Throw(cx, id, errorNumber);
634 *rval = false;
635 return true;
638 static bool
639 Reject(JSContext *cx, JSObject *obj, unsigned errorNumber, bool throwError, bool *rval)
641 if (throwError)
642 return Throw(cx, obj, errorNumber);
644 *rval = false;
645 return true;
648 static bool
649 Reject(JSContext *cx, HandleId id, unsigned errorNumber, bool throwError, bool *rval)
651 if (throwError)
652 return Throw(cx, id, errorNumber);
654 *rval = false;
655 return true;
658 static unsigned
659 ApplyAttributes(unsigned attrs, bool enumerable, bool writable, bool configurable)
662 * Respect the fact that some callers may want to preserve existing attributes as much as
663 * possible, or install defaults otherwise.
665 if (attrs & JSPROP_IGNORE_ENUMERATE) {
666 attrs &= ~JSPROP_IGNORE_ENUMERATE;
667 if (enumerable)
668 attrs |= JSPROP_ENUMERATE;
669 else
670 attrs &= ~JSPROP_ENUMERATE;
672 if (attrs & JSPROP_IGNORE_READONLY) {
673 attrs &= ~JSPROP_IGNORE_READONLY;
674 // Only update the writability if it's relevant
675 if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
676 if (!writable)
677 attrs |= JSPROP_READONLY;
678 else
679 attrs &= ~JSPROP_READONLY;
682 if (attrs & JSPROP_IGNORE_PERMANENT) {
683 attrs &= ~JSPROP_IGNORE_PERMANENT;
684 if (!configurable)
685 attrs |= JSPROP_PERMANENT;
686 else
687 attrs &= ~JSPROP_PERMANENT;
689 return attrs;
692 static unsigned
693 ApplyOrDefaultAttributes(unsigned attrs, const Shape *shape = nullptr)
695 bool enumerable = shape ? shape->enumerable() : false;
696 bool writable = shape ? shape->writable() : false;
697 bool configurable = shape ? shape->configurable() : false;
698 return ApplyAttributes(attrs, enumerable, writable, configurable);
701 static unsigned
702 ApplyOrDefaultAttributes(unsigned attrs, Handle<PropertyDescriptor> desc)
704 bool present = !!desc.object();
705 bool enumerable = present ? desc.isEnumerable() : false;
706 bool writable = present ? !desc.isReadonly() : false;
707 bool configurable = present ? !desc.isPermanent() : false;
708 return ApplyAttributes(attrs, enumerable, writable, configurable);
711 // See comments on CheckDefineProperty in jsfriendapi.h.
713 // DefinePropertyOnObject has its own implementation of these checks.
715 JS_FRIEND_API(bool)
716 js::CheckDefineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
717 unsigned attrs, PropertyOp getter, StrictPropertyOp setter)
719 if (!obj->isNative())
720 return true;
722 // ES5 8.12.9 Step 1. Even though we know obj is native, we use generic
723 // APIs for shorter, more readable code.
724 Rooted<PropertyDescriptor> desc(cx);
725 if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
726 return false;
728 // Appropriately handle the potential for ignored attributes. Since the proxy code calls us
729 // directly, these might flow in legitimately. Ensure that we compare against the values that
730 // are intended.
731 attrs = ApplyOrDefaultAttributes(attrs, desc) & ~JSPROP_IGNORE_VALUE;
733 // This does not have to check obj's extensibility when !desc.obj (steps
734 // 2-3) because the low-level methods JSObject::{add,put}Property check
735 // for that.
736 if (desc.object() && desc.isPermanent()) {
737 // Steps 6-11, skipping step 10.a.ii. Prohibit redefining a permanent
738 // property with different metadata, except to make a writable property
739 // non-writable.
740 if ((getter != desc.getter() && !(getter == JS_PropertyStub && !desc.getter())) ||
741 (setter != desc.setter() && !(setter == JS_StrictPropertyStub && !desc.setter())) ||
742 (attrs != desc.attributes() && attrs != (desc.attributes() | JSPROP_READONLY)))
744 return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
747 // Step 10.a.ii. Prohibit changing the value of a non-configurable,
748 // non-writable data property.
749 if ((desc.attributes() & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_READONLY)) == JSPROP_READONLY) {
750 bool same;
751 if (!SameValue(cx, value, desc.value(), &same))
752 return false;
753 if (!same)
754 return JSObject::reportReadOnly(cx, id);
757 return true;
760 static bool
761 DefinePropertyOnObject(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc,
762 bool throwError, bool *rval)
764 /* 8.12.9 step 1. */
765 RootedShape shape(cx);
766 RootedObject obj2(cx);
767 JS_ASSERT(!obj->getOps()->lookupGeneric);
768 if (!HasOwnProperty<CanGC>(cx, nullptr, obj, id, &obj2, &shape))
769 return false;
771 JS_ASSERT(!obj->getOps()->defineProperty);
773 /* 8.12.9 steps 2-4. */
774 if (!shape) {
775 bool extensible;
776 if (!JSObject::isExtensible(cx, obj, &extensible))
777 return false;
778 if (!extensible)
779 return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
781 *rval = true;
783 if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
784 JS_ASSERT(!obj->getOps()->defineProperty);
785 RootedValue v(cx, desc.hasValue() ? desc.value() : UndefinedValue());
786 return baseops::DefineGeneric(cx, obj, id, v,
787 JS_PropertyStub, JS_StrictPropertyStub,
788 desc.attributes());
791 JS_ASSERT(desc.isAccessorDescriptor());
793 return baseops::DefineGeneric(cx, obj, id, UndefinedHandleValue,
794 desc.getter(), desc.setter(), desc.attributes());
797 /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
798 RootedValue v(cx);
800 JS_ASSERT(obj == obj2);
802 bool shapeDataDescriptor = true,
803 shapeAccessorDescriptor = false,
804 shapeWritable = true,
805 shapeConfigurable = true,
806 shapeEnumerable = true,
807 shapeHasDefaultGetter = true,
808 shapeHasDefaultSetter = true,
809 shapeHasGetterValue = false,
810 shapeHasSetterValue = false;
811 uint8_t shapeAttributes = GetShapeAttributes(obj, shape);
812 if (!IsImplicitDenseOrTypedArrayElement(shape)) {
813 shapeDataDescriptor = shape->isDataDescriptor();
814 shapeAccessorDescriptor = shape->isAccessorDescriptor();
815 shapeWritable = shape->writable();
816 shapeConfigurable = shape->configurable();
817 shapeEnumerable = shape->enumerable();
818 shapeHasDefaultGetter = shape->hasDefaultGetter();
819 shapeHasDefaultSetter = shape->hasDefaultSetter();
820 shapeHasGetterValue = shape->hasGetterValue();
821 shapeHasSetterValue = shape->hasSetterValue();
822 shapeAttributes = shape->attributes();
825 do {
826 if (desc.isAccessorDescriptor()) {
827 if (!shapeAccessorDescriptor)
828 break;
830 if (desc.hasGet()) {
831 bool same;
832 if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same))
833 return false;
834 if (!same)
835 break;
838 if (desc.hasSet()) {
839 bool same;
840 if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
841 return false;
842 if (!same)
843 break;
845 } else {
847 * Determine the current value of the property once, if the current
848 * value might actually need to be used or preserved later. NB: we
849 * guard on whether the current property is a data descriptor to
850 * avoid calling a getter; we won't need the value if it's not a
851 * data descriptor.
853 if (IsImplicitDenseOrTypedArrayElement(shape)) {
854 v = obj->getDenseOrTypedArrayElement(JSID_TO_INT(id));
855 } else if (shape->isDataDescriptor()) {
857 * We must rule out a non-configurable js::PropertyOp-guarded
858 * property becoming a writable unguarded data property, since
859 * such a property can have its value changed to one the getter
860 * and setter preclude.
862 * A desc lacking writable but with value is a data descriptor
863 * and we must reject it as if it had writable: true if current
864 * is writable.
866 if (!shape->configurable() &&
867 (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()) &&
868 desc.isDataDescriptor() &&
869 (desc.hasWritable() ? desc.writable() : shape->writable()))
871 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
874 if (!NativeGet(cx, obj, obj2, shape, &v))
875 return false;
878 if (desc.isDataDescriptor()) {
879 if (!shapeDataDescriptor)
880 break;
882 bool same;
883 if (desc.hasValue()) {
884 if (!SameValue(cx, desc.value(), v, &same))
885 return false;
886 if (!same) {
888 * Insist that a non-configurable js::PropertyOp data
889 * property is frozen at exactly the last-got value.
891 * Duplicate the first part of the big conjunction that
892 * we tested above, rather than add a local bool flag.
893 * Likewise, don't try to keep shape->writable() in a
894 * flag we veto from true to false for non-configurable
895 * PropertyOp-based data properties and test before the
896 * SameValue check later on in order to re-use that "if
897 * (!SameValue) Reject" logic.
899 * This function is large and complex enough that it
900 * seems best to repeat a small bit of code and return
901 * Reject(...) ASAP, instead of being clever.
903 if (!shapeConfigurable &&
904 (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()))
906 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
908 break;
911 if (desc.hasWritable() && desc.writable() != shapeWritable)
912 break;
913 } else {
914 /* The only fields in desc will be handled below. */
915 JS_ASSERT(desc.isGenericDescriptor());
919 if (desc.hasConfigurable() && desc.configurable() != shapeConfigurable)
920 break;
921 if (desc.hasEnumerable() && desc.enumerable() != shapeEnumerable)
922 break;
924 /* The conditions imposed by step 5 or step 6 apply. */
925 *rval = true;
926 return true;
927 } while (0);
929 /* 8.12.9 step 7. */
930 if (!shapeConfigurable) {
931 if ((desc.hasConfigurable() && desc.configurable()) ||
932 (desc.hasEnumerable() && desc.enumerable() != shape->enumerable())) {
933 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
937 bool callDelProperty = false;
939 if (desc.isGenericDescriptor()) {
940 /* 8.12.9 step 8, no validation required */
941 } else if (desc.isDataDescriptor() != shapeDataDescriptor) {
942 /* 8.12.9 step 9. */
943 if (!shapeConfigurable)
944 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
945 } else if (desc.isDataDescriptor()) {
946 /* 8.12.9 step 10. */
947 JS_ASSERT(shapeDataDescriptor);
948 if (!shapeConfigurable && !shape->writable()) {
949 if (desc.hasWritable() && desc.writable())
950 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
951 if (desc.hasValue()) {
952 bool same;
953 if (!SameValue(cx, desc.value(), v, &same))
954 return false;
955 if (!same)
956 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
960 callDelProperty = !shapeHasDefaultGetter || !shapeHasDefaultSetter;
961 } else {
962 /* 8.12.9 step 11. */
963 JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor());
964 if (!shape->configurable()) {
965 if (desc.hasSet()) {
966 bool same;
967 if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
968 return false;
969 if (!same)
970 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
973 if (desc.hasGet()) {
974 bool same;
975 if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same))
976 return false;
977 if (!same)
978 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
983 /* 8.12.9 step 12. */
984 unsigned attrs;
985 PropertyOp getter;
986 StrictPropertyOp setter;
987 if (desc.isGenericDescriptor()) {
988 unsigned changed = 0;
989 if (desc.hasConfigurable())
990 changed |= JSPROP_PERMANENT;
991 if (desc.hasEnumerable())
992 changed |= JSPROP_ENUMERATE;
994 attrs = (shapeAttributes & ~changed) | (desc.attributes() & changed);
995 getter = IsImplicitDenseOrTypedArrayElement(shape) ? JS_PropertyStub : shape->getter();
996 setter = IsImplicitDenseOrTypedArrayElement(shape) ? JS_StrictPropertyStub : shape->setter();
997 } else if (desc.isDataDescriptor()) {
998 unsigned unchanged = 0;
999 if (!desc.hasConfigurable())
1000 unchanged |= JSPROP_PERMANENT;
1001 if (!desc.hasEnumerable())
1002 unchanged |= JSPROP_ENUMERATE;
1003 /* Watch out for accessor -> data transformations here. */
1004 if (!desc.hasWritable() && shapeDataDescriptor)
1005 unchanged |= JSPROP_READONLY;
1007 if (desc.hasValue())
1008 v = desc.value();
1009 attrs = (desc.attributes() & ~unchanged) | (shapeAttributes & unchanged);
1010 getter = JS_PropertyStub;
1011 setter = JS_StrictPropertyStub;
1012 } else {
1013 JS_ASSERT(desc.isAccessorDescriptor());
1015 /* 8.12.9 step 12. */
1016 unsigned changed = 0;
1017 if (desc.hasConfigurable())
1018 changed |= JSPROP_PERMANENT;
1019 if (desc.hasEnumerable())
1020 changed |= JSPROP_ENUMERATE;
1021 if (desc.hasGet())
1022 changed |= JSPROP_GETTER | JSPROP_SHARED | JSPROP_READONLY;
1023 if (desc.hasSet())
1024 changed |= JSPROP_SETTER | JSPROP_SHARED | JSPROP_READONLY;
1026 attrs = (desc.attributes() & changed) | (shapeAttributes & ~changed);
1027 if (desc.hasGet()) {
1028 getter = desc.getter();
1029 } else {
1030 getter = (shapeHasDefaultGetter && !shapeHasGetterValue)
1031 ? JS_PropertyStub
1032 : shape->getter();
1034 if (desc.hasSet()) {
1035 setter = desc.setter();
1036 } else {
1037 setter = (shapeHasDefaultSetter && !shapeHasSetterValue)
1038 ? JS_StrictPropertyStub
1039 : shape->setter();
1043 *rval = true;
1046 * Since "data" properties implemented using native C functions may rely on
1047 * side effects during setting, we must make them aware that they have been
1048 * "assigned"; deleting the property before redefining it does the trick.
1049 * See bug 539766, where we ran into problems when we redefined
1050 * arguments.length without making the property aware that its value had
1051 * been changed (which would have happened if we had deleted it before
1052 * redefining it or we had invoked its setter to change its value).
1054 if (callDelProperty) {
1055 bool succeeded;
1056 if (!CallJSDeletePropertyOp(cx, obj2->getClass()->delProperty, obj2, id, &succeeded))
1057 return false;
1060 return baseops::DefineGeneric(cx, obj, id, v, getter, setter, attrs);
1063 /* ES6 20130308 draft 8.4.2.1 [[DefineOwnProperty]] */
1064 static bool
1065 DefinePropertyOnArray(JSContext *cx, Handle<ArrayObject*> arr, HandleId id, const PropDesc &desc,
1066 bool throwError, bool *rval)
1068 /* Step 2. */
1069 if (id == NameToId(cx->names().length)) {
1070 // Canonicalize value, if necessary, before proceeding any further. It
1071 // would be better if this were always/only done by ArraySetLength.
1072 // But canonicalization may throw a RangeError (or other exception, if
1073 // the value is an object with user-defined conversion semantics)
1074 // before other attributes are checked. So as long as our internal
1075 // defineProperty hook doesn't match the ECMA one, this duplicate
1076 // checking can't be helped.
1077 RootedValue v(cx);
1078 if (desc.hasValue()) {
1079 uint32_t newLen;
1080 if (!CanonicalizeArrayLengthValue<SequentialExecution>(cx, desc.value(), &newLen))
1081 return false;
1082 v.setNumber(newLen);
1083 } else {
1084 v.setNumber(arr->length());
1087 if (desc.hasConfigurable() && desc.configurable())
1088 return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval);
1089 if (desc.hasEnumerable() && desc.enumerable())
1090 return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval);
1092 if (desc.isAccessorDescriptor())
1093 return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval);
1095 unsigned attrs = arr->nativeLookup(cx, id)->attributes();
1096 if (!arr->lengthIsWritable()) {
1097 if (desc.hasWritable() && desc.writable())
1098 return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval);
1099 } else {
1100 if (desc.hasWritable() && !desc.writable())
1101 attrs = attrs | JSPROP_READONLY;
1104 return ArraySetLength<SequentialExecution>(cx, arr, id, attrs, v, throwError);
1107 /* Step 3. */
1108 uint32_t index;
1109 if (js_IdIsIndex(id, &index)) {
1110 /* Step 3b. */
1111 uint32_t oldLen = arr->length();
1113 /* Steps 3a, 3e. */
1114 if (index >= oldLen && !arr->lengthIsWritable())
1115 return Reject(cx, arr, JSMSG_CANT_APPEND_TO_ARRAY, throwError, rval);
1117 /* Steps 3f-j. */
1118 return DefinePropertyOnObject(cx, arr, id, desc, throwError, rval);
1121 /* Step 4. */
1122 return DefinePropertyOnObject(cx, arr, id, desc, throwError, rval);
1125 bool
1126 js::DefineProperty(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc,
1127 bool throwError, bool *rval)
1129 if (obj->is<ArrayObject>()) {
1130 Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
1131 return DefinePropertyOnArray(cx, arr, id, desc, throwError, rval);
1134 if (obj->getOps()->lookupGeneric) {
1135 if (obj->is<ProxyObject>()) {
1136 Rooted<PropertyDescriptor> pd(cx);
1137 desc.populatePropertyDescriptor(obj, &pd);
1138 pd.object().set(obj);
1139 return Proxy::defineProperty(cx, obj, id, &pd);
1141 return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
1144 return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval);
1147 bool
1148 js::DefineOwnProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue descriptor,
1149 bool *bp)
1151 Rooted<PropDesc> desc(cx);
1152 if (!desc.initialize(cx, descriptor))
1153 return false;
1155 bool rval;
1156 if (!DefineProperty(cx, obj, id, desc, true, &rval))
1157 return false;
1158 *bp = !!rval;
1159 return true;
1162 bool
1163 js::DefineOwnProperty(JSContext *cx, HandleObject obj, HandleId id,
1164 Handle<PropertyDescriptor> descriptor, bool *bp)
1166 Rooted<PropDesc> desc(cx);
1168 desc.initFromPropertyDescriptor(descriptor);
1170 bool rval;
1171 if (!DefineProperty(cx, obj, id, desc, true, &rval))
1172 return false;
1173 *bp = !!rval;
1174 return true;
1178 bool
1179 js::ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccessors,
1180 AutoIdVector *ids, AutoPropDescVector *descs)
1182 if (!GetPropertyNames(cx, props, JSITER_OWNONLY | JSITER_SYMBOLS, ids))
1183 return false;
1185 RootedId id(cx);
1186 for (size_t i = 0, len = ids->length(); i < len; i++) {
1187 id = (*ids)[i];
1188 Rooted<PropDesc> desc(cx);
1189 RootedValue v(cx);
1190 if (!JSObject::getGeneric(cx, props, props, id, &v) ||
1191 !desc.initialize(cx, v, checkAccessors) ||
1192 !descs->append(desc))
1194 return false;
1197 return true;
1200 bool
1201 js::DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
1203 AutoIdVector ids(cx);
1204 AutoPropDescVector descs(cx);
1205 if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs))
1206 return false;
1208 if (obj->is<ArrayObject>()) {
1209 bool dummy;
1210 Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
1211 for (size_t i = 0, len = ids.length(); i < len; i++) {
1212 if (!DefinePropertyOnArray(cx, arr, ids[i], descs[i], true, &dummy))
1213 return false;
1215 return true;
1218 if (obj->getOps()->lookupGeneric) {
1219 if (obj->is<ProxyObject>()) {
1220 Rooted<PropertyDescriptor> pd(cx);
1221 for (size_t i = 0, len = ids.length(); i < len; i++) {
1222 descs[i].populatePropertyDescriptor(obj, &pd);
1223 if (!Proxy::defineProperty(cx, obj, ids[i], &pd))
1224 return false;
1226 return true;
1228 bool dummy;
1229 return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, true, &dummy);
1232 bool dummy;
1233 for (size_t i = 0, len = ids.length(); i < len; i++) {
1234 if (!DefinePropertyOnObject(cx, obj, ids[i], descs[i], true, &dummy))
1235 return false;
1238 return true;
1241 extern bool
1242 js_PopulateObject(JSContext *cx, HandleObject newborn, HandleObject props)
1244 return DefineProperties(cx, newborn, props);
1247 js::types::TypeObject*
1248 JSObject::uninlinedGetType(JSContext *cx)
1250 return getType(cx);
1253 void
1254 JSObject::uninlinedSetType(js::types::TypeObject *newType)
1256 setType(newType);
1259 /* static */ inline unsigned
1260 JSObject::getSealedOrFrozenAttributes(unsigned attrs, ImmutabilityType it)
1262 /* Make all attributes permanent; if freezing, make data attributes read-only. */
1263 if (it == FREEZE && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1264 return JSPROP_PERMANENT | JSPROP_READONLY;
1265 return JSPROP_PERMANENT;
1268 /* static */ bool
1269 JSObject::sealOrFreeze(JSContext *cx, HandleObject obj, ImmutabilityType it)
1271 assertSameCompartment(cx, obj);
1272 JS_ASSERT(it == SEAL || it == FREEZE);
1274 bool extensible;
1275 if (!JSObject::isExtensible(cx, obj, &extensible))
1276 return false;
1277 if (extensible && !JSObject::preventExtensions(cx, obj))
1278 return false;
1280 AutoIdVector props(cx);
1281 if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &props))
1282 return false;
1284 /* preventExtensions must sparsify dense objects, so we can assign to holes without checks. */
1285 JS_ASSERT_IF(obj->isNative(), obj->getDenseCapacity() == 0);
1287 if (obj->isNative() && !obj->inDictionaryMode() && !obj->is<TypedArrayObject>()) {
1289 * Seal/freeze non-dictionary objects by constructing a new shape
1290 * hierarchy mirroring the original one, which can be shared if many
1291 * objects with the same structure are sealed/frozen. If we use the
1292 * generic path below then any non-empty object will be converted to
1293 * dictionary mode.
1295 RootedShape last(cx, EmptyShape::getInitialShape(cx, obj->getClass(),
1296 obj->getTaggedProto(),
1297 obj->getParent(),
1298 obj->getMetadata(),
1299 obj->numFixedSlots(),
1300 obj->lastProperty()->getObjectFlags()));
1301 if (!last)
1302 return false;
1304 /* Get an in order list of the shapes in this object. */
1305 AutoShapeVector shapes(cx);
1306 for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) {
1307 if (!shapes.append(&r.front()))
1308 return false;
1310 Reverse(shapes.begin(), shapes.end());
1312 for (size_t i = 0; i < shapes.length(); i++) {
1313 StackShape unrootedChild(shapes[i]);
1314 RootedGeneric<StackShape*> child(cx, &unrootedChild);
1315 child->attrs |= getSealedOrFrozenAttributes(child->attrs, it);
1317 if (!JSID_IS_EMPTY(child->propid) && it == FREEZE)
1318 MarkTypePropertyNonWritable(cx, obj, child->propid);
1320 last = cx->compartment()->propertyTree.getChild(cx, last, *child);
1321 if (!last)
1322 return false;
1325 JS_ASSERT(obj->lastProperty()->slotSpan() == last->slotSpan());
1326 JS_ALWAYS_TRUE(setLastProperty(cx, obj, last));
1327 } else {
1328 RootedId id(cx);
1329 for (size_t i = 0; i < props.length(); i++) {
1330 id = props[i];
1332 unsigned attrs;
1333 if (!getGenericAttributes(cx, obj, id, &attrs))
1334 return false;
1336 unsigned new_attrs = getSealedOrFrozenAttributes(attrs, it);
1338 /* If we already have the attributes we need, skip the setAttributes call. */
1339 if ((attrs | new_attrs) == attrs)
1340 continue;
1342 attrs |= new_attrs;
1343 if (!setGenericAttributes(cx, obj, id, &attrs))
1344 return false;
1348 // Ordinarily ArraySetLength handles this, but we're going behind its back
1349 // right now, so we must do this manually. Neither the custom property
1350 // tree mutations nor the setGenericAttributes call in the above code will
1351 // do this for us.
1353 // ArraySetLength also implements the capacity <= length invariant for
1354 // arrays with non-writable length. We don't need to do anything special
1355 // for that, because capacity was zeroed out by preventExtensions. (See
1356 // the assertion before the if-else above.)
1357 if (it == FREEZE && obj->is<ArrayObject>()) {
1358 if (!obj->maybeCopyElementsForWrite(cx))
1359 return false;
1360 obj->getElementsHeader()->setNonwritableArrayLength();
1363 return true;
1366 /* static */ bool
1367 JSObject::isSealedOrFrozen(JSContext *cx, HandleObject obj, ImmutabilityType it, bool *resultp)
1369 bool extensible;
1370 if (!JSObject::isExtensible(cx, obj, &extensible))
1371 return false;
1372 if (extensible) {
1373 *resultp = false;
1374 return true;
1377 if (obj->is<TypedArrayObject>()) {
1378 if (it == SEAL) {
1379 // Typed arrays are always sealed.
1380 *resultp = true;
1381 } else {
1382 // Typed arrays cannot be frozen, but an empty typed array is
1383 // trivially frozen.
1384 *resultp = (obj->as<TypedArrayObject>().length() == 0);
1386 return true;
1389 AutoIdVector props(cx);
1390 if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &props))
1391 return false;
1393 RootedId id(cx);
1394 for (size_t i = 0, len = props.length(); i < len; i++) {
1395 id = props[i];
1397 unsigned attrs;
1398 if (!getGenericAttributes(cx, obj, id, &attrs))
1399 return false;
1402 * If the property is configurable, this object is neither sealed nor
1403 * frozen. If the property is a writable data property, this object is
1404 * not frozen.
1406 if (!(attrs & JSPROP_PERMANENT) ||
1407 (it == FREEZE && !(attrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER))))
1409 *resultp = false;
1410 return true;
1414 /* All properties checked out. This object is sealed/frozen. */
1415 *resultp = true;
1416 return true;
1419 /* static */
1420 const char *
1421 JSObject::className(JSContext *cx, HandleObject obj)
1423 assertSameCompartment(cx, obj);
1425 if (obj->is<ProxyObject>())
1426 return Proxy::className(cx, obj);
1428 return obj->getClass()->name;
1432 * Get the GC kind to use for scripted 'new' on the given class.
1433 * FIXME bug 547327: estimate the size from the allocation site.
1435 static inline gc::AllocKind
1436 NewObjectGCKind(const js::Class *clasp)
1438 if (clasp == &ArrayObject::class_)
1439 return gc::FINALIZE_OBJECT8;
1440 if (clasp == &JSFunction::class_)
1441 return gc::FINALIZE_OBJECT2;
1442 return gc::FINALIZE_OBJECT4;
1445 static inline JSObject *
1446 NewObject(ExclusiveContext *cx, types::TypeObject *type_, JSObject *parent, gc::AllocKind kind,
1447 NewObjectKind newKind)
1449 const Class *clasp = type_->clasp();
1451 JS_ASSERT(clasp != &ArrayObject::class_);
1452 JS_ASSERT_IF(clasp == &JSFunction::class_,
1453 kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
1454 JS_ASSERT_IF(parent, &parent->global() == cx->global());
1456 RootedTypeObject type(cx, type_);
1458 JSObject *metadata = nullptr;
1459 if (!NewObjectMetadata(cx, &metadata))
1460 return nullptr;
1462 // For objects which can have fixed data following the object, only use
1463 // enough fixed slots to cover the number of reserved slots in the object,
1464 // regardless of the allocation kind specified.
1465 size_t nfixed = ClassCanHaveFixedData(clasp)
1466 ? GetGCKindSlots(gc::GetGCObjectKind(clasp), clasp)
1467 : GetGCKindSlots(kind, clasp);
1469 RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, type->proto(),
1470 parent, metadata, nfixed));
1471 if (!shape)
1472 return nullptr;
1474 gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
1475 JSObject *obj = JSObject::create(cx, kind, heap, shape, type);
1476 if (!obj)
1477 return nullptr;
1479 if (newKind == SingletonObject) {
1480 RootedObject nobj(cx, obj);
1481 if (!JSObject::setSingletonType(cx, nobj))
1482 return nullptr;
1483 obj = nobj;
1487 * This will cancel an already-running incremental GC from doing any more
1488 * slices, and it will prevent any future incremental GCs.
1490 bool globalWithoutCustomTrace = clasp->trace == JS_GlobalObjectTraceHook &&
1491 !cx->compartment()->options().getTrace();
1492 if (clasp->trace &&
1493 !globalWithoutCustomTrace &&
1494 !(clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS))
1496 if (!cx->shouldBeJSContext())
1497 return nullptr;
1498 JSRuntime *rt = cx->asJSContext()->runtime();
1499 rt->gc.disallowIncrementalGC();
1501 #ifdef DEBUG
1502 if (rt->gc.gcMode() == JSGC_MODE_INCREMENTAL) {
1503 fprintf(stderr,
1504 "The class %s has a trace hook but does not declare the\n"
1505 "JSCLASS_IMPLEMENTS_BARRIERS flag. Please ensure that it correctly\n"
1506 "implements write barriers and then set the flag.\n",
1507 clasp->name);
1508 MOZ_CRASH();
1510 #endif
1513 probes::CreateObject(cx, obj);
1514 return obj;
1517 void
1518 NewObjectCache::fillProto(EntryIndex entry, const Class *clasp, js::TaggedProto proto,
1519 gc::AllocKind kind, JSObject *obj)
1521 JS_ASSERT_IF(proto.isObject(), !proto.toObject()->is<GlobalObject>());
1522 JS_ASSERT(obj->getTaggedProto() == proto);
1523 return fill(entry, clasp, proto.raw(), kind, obj);
1526 JSObject *
1527 js::NewObjectWithGivenProto(ExclusiveContext *cxArg, const js::Class *clasp,
1528 js::TaggedProto protoArg, JSObject *parentArg,
1529 gc::AllocKind allocKind, NewObjectKind newKind)
1531 if (CanBeFinalizedInBackground(allocKind, clasp))
1532 allocKind = GetBackgroundAllocKind(allocKind);
1534 NewObjectCache::EntryIndex entry = -1;
1535 uint64_t gcNumber = 0;
1536 if (JSContext *cx = cxArg->maybeJSContext()) {
1537 JSRuntime *rt = cx->runtime();
1538 NewObjectCache &cache = rt->newObjectCache;
1539 if (protoArg.isObject() &&
1540 newKind == GenericObject &&
1541 !cx->compartment()->hasObjectMetadataCallback() &&
1542 (!parentArg || parentArg == protoArg.toObject()->getParent()) &&
1543 !protoArg.toObject()->is<GlobalObject>())
1545 if (cache.lookupProto(clasp, protoArg.toObject(), allocKind, &entry)) {
1546 JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, clasp));
1547 if (obj) {
1548 return obj;
1549 } else {
1550 Rooted<TaggedProto> proto(cxArg, protoArg);
1551 RootedObject parent(cxArg, parentArg);
1552 obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, clasp));
1553 JS_ASSERT(!obj);
1554 parentArg = parent;
1555 protoArg = proto;
1557 } else {
1558 gcNumber = rt->gc.gcNumber();
1563 Rooted<TaggedProto> proto(cxArg, protoArg);
1564 RootedObject parent(cxArg, parentArg);
1566 types::TypeObject *type = cxArg->getNewType(clasp, proto, nullptr);
1567 if (!type)
1568 return nullptr;
1571 * Default parent to the parent of the prototype, which was set from
1572 * the parent of the prototype's constructor.
1574 if (!parent && proto.isObject())
1575 parent = proto.toObject()->getParent();
1577 RootedObject obj(cxArg, NewObject(cxArg, type, parent, allocKind, newKind));
1578 if (!obj)
1579 return nullptr;
1581 if (entry != -1 && !obj->hasDynamicSlots() &&
1582 cxArg->asJSContext()->runtime()->gc.gcNumber() == gcNumber)
1584 cxArg->asJSContext()->runtime()->newObjectCache.fillProto(entry, clasp,
1585 proto, allocKind, obj);
1588 return obj;
1591 static JSProtoKey
1592 ClassProtoKeyOrAnonymousOrNull(const js::Class *clasp)
1594 JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
1595 if (key != JSProto_Null)
1596 return key;
1597 if (clasp->flags & JSCLASS_IS_ANONYMOUS)
1598 return JSProto_Object;
1599 return JSProto_Null;
1602 JSObject *
1603 js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg,
1604 const js::Class *clasp, JSObject *protoArg, JSObject *parentArg,
1605 gc::AllocKind allocKind, NewObjectKind newKind)
1607 if (protoArg)
1608 return NewObjectWithGivenProto(cxArg, clasp, TaggedProto(protoArg), parentArg, allocKind, newKind);
1610 if (CanBeFinalizedInBackground(allocKind, clasp))
1611 allocKind = GetBackgroundAllocKind(allocKind);
1613 if (!parentArg)
1614 parentArg = cxArg->global();
1617 * Use the object cache, except for classes without a cached proto key.
1618 * On these objects, FindProto will do a dynamic property lookup to get
1619 * global[className].prototype, where changes to either the className or
1620 * prototype property would render the cached lookup incorrect. For classes
1621 * with a proto key, the prototype created during class initialization is
1622 * stored in an immutable slot on the global (except for ClearScope, which
1623 * will flush the new object cache).
1625 JSProtoKey protoKey = ClassProtoKeyOrAnonymousOrNull(clasp);
1627 NewObjectCache::EntryIndex entry = -1;
1628 if (JSContext *cx = cxArg->maybeJSContext()) {
1629 NewObjectCache &cache = cx->runtime()->newObjectCache;
1630 if (parentArg->is<GlobalObject>() &&
1631 protoKey != JSProto_Null &&
1632 newKind == GenericObject &&
1633 !cx->compartment()->hasObjectMetadataCallback())
1635 if (cache.lookupGlobal(clasp, &parentArg->as<GlobalObject>(), allocKind, &entry)) {
1636 JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, clasp));
1637 if (obj) {
1638 return obj;
1639 } else {
1640 RootedObject parent(cxArg, parentArg);
1641 RootedObject proto(cxArg, protoArg);
1642 obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, clasp));
1643 JS_ASSERT(!obj);
1644 protoArg = proto;
1645 parentArg = parent;
1651 RootedObject parent(cxArg, parentArg);
1652 RootedObject proto(cxArg, protoArg);
1654 if (!FindProto(cxArg, clasp, &proto))
1655 return nullptr;
1657 Rooted<TaggedProto> taggedProto(cxArg, TaggedProto(proto));
1658 types::TypeObject *type = cxArg->getNewType(clasp, taggedProto);
1659 if (!type)
1660 return nullptr;
1662 JSObject *obj = NewObject(cxArg, type, parent, allocKind, newKind);
1663 if (!obj)
1664 return nullptr;
1666 if (entry != -1 && !obj->hasDynamicSlots()) {
1667 cxArg->asJSContext()->runtime()->newObjectCache.fillGlobal(entry, clasp,
1668 &parent->as<GlobalObject>(),
1669 allocKind, obj);
1672 return obj;
1676 * Create a plain object with the specified type. This bypasses getNewType to
1677 * avoid losing creation site information for objects made by scripted 'new'.
1679 JSObject *
1680 js::NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc::AllocKind allocKind,
1681 NewObjectKind newKind)
1683 JS_ASSERT(parent);
1685 JS_ASSERT(allocKind <= gc::FINALIZE_OBJECT_LAST);
1686 if (CanBeFinalizedInBackground(allocKind, type->clasp()))
1687 allocKind = GetBackgroundAllocKind(allocKind);
1689 NewObjectCache &cache = cx->runtime()->newObjectCache;
1691 NewObjectCache::EntryIndex entry = -1;
1692 if (parent == type->proto().toObject()->getParent() &&
1693 newKind == GenericObject &&
1694 !cx->compartment()->hasObjectMetadataCallback())
1696 if (cache.lookupType(type, allocKind, &entry)) {
1697 JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, type->clasp()));
1698 if (obj) {
1699 return obj;
1700 } else {
1701 obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, type->clasp()));
1702 parent = type->proto().toObject()->getParent();
1707 JSObject *obj = NewObject(cx, type, parent, allocKind, newKind);
1708 if (!obj)
1709 return nullptr;
1711 if (entry != -1 && !obj->hasDynamicSlots())
1712 cache.fillType(entry, type, allocKind, obj);
1714 return obj;
1717 bool
1718 js::NewObjectScriptedCall(JSContext *cx, MutableHandleObject pobj)
1720 jsbytecode *pc;
1721 RootedScript script(cx, cx->currentScript(&pc));
1722 gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_);
1723 NewObjectKind newKind = script
1724 ? UseNewTypeForInitializer(script, pc, &JSObject::class_)
1725 : GenericObject;
1726 RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_, allocKind, newKind));
1727 if (!obj)
1728 return false;
1730 if (script) {
1731 /* Try to specialize the type of the object to the scripted call site. */
1732 if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind))
1733 return false;
1736 pobj.set(obj);
1737 return true;
1740 JSObject*
1741 js::CreateThis(JSContext *cx, const Class *newclasp, HandleObject callee)
1743 RootedValue protov(cx);
1744 if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov))
1745 return nullptr;
1747 JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : nullptr;
1748 JSObject *parent = callee->getParent();
1749 gc::AllocKind kind = NewObjectGCKind(newclasp);
1750 return NewObjectWithClassProto(cx, newclasp, proto, parent, kind);
1753 static inline JSObject *
1754 CreateThisForFunctionWithType(JSContext *cx, HandleTypeObject type, JSObject *parent,
1755 NewObjectKind newKind)
1757 if (type->newScript()) {
1759 * Make an object with the type's associated finalize kind and shape,
1760 * which reflects any properties that will definitely be added to the
1761 * object before it is read from.
1763 RootedObject templateObject(cx, type->newScript()->templateObject);
1764 JS_ASSERT(templateObject->type() == type);
1766 RootedObject res(cx, CopyInitializerObject(cx, templateObject, newKind));
1767 if (!res)
1768 return nullptr;
1769 if (newKind == SingletonObject) {
1770 Rooted<TaggedProto> proto(cx, TaggedProto(templateObject->getProto()));
1771 if (!res->splicePrototype(cx, &JSObject::class_, proto))
1772 return nullptr;
1773 } else {
1774 res->setType(type);
1776 return res;
1779 gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_);
1780 return NewObjectWithType(cx, type, parent, allocKind, newKind);
1783 JSObject *
1784 js::CreateThisForFunctionWithProto(JSContext *cx, HandleObject callee, JSObject *proto,
1785 NewObjectKind newKind /* = GenericObject */)
1787 RootedObject res(cx);
1789 if (proto) {
1790 RootedTypeObject type(cx, cx->getNewType(&JSObject::class_, TaggedProto(proto), &callee->as<JSFunction>()));
1791 if (!type)
1792 return nullptr;
1793 res = CreateThisForFunctionWithType(cx, type, callee->getParent(), newKind);
1794 } else {
1795 gc::AllocKind allocKind = NewObjectGCKind(&JSObject::class_);
1796 res = NewObjectWithClassProto(cx, &JSObject::class_, proto, callee->getParent(), allocKind, newKind);
1799 if (res) {
1800 JSScript *script = callee->as<JSFunction>().getOrCreateScript(cx);
1801 if (!script)
1802 return nullptr;
1803 TypeScript::SetThis(cx, script, types::Type::ObjectType(res));
1806 return res;
1809 JSObject *
1810 js::CreateThisForFunction(JSContext *cx, HandleObject callee, NewObjectKind newKind)
1812 RootedValue protov(cx);
1813 if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &protov))
1814 return nullptr;
1815 JSObject *proto;
1816 if (protov.isObject())
1817 proto = &protov.toObject();
1818 else
1819 proto = nullptr;
1820 JSObject *obj = CreateThisForFunctionWithProto(cx, callee, proto, newKind);
1822 if (obj && newKind == SingletonObject) {
1823 RootedObject nobj(cx, obj);
1825 /* Reshape the singleton before passing it as the 'this' value. */
1826 JSObject::clear(cx, nobj);
1828 JSScript *calleeScript = callee->as<JSFunction>().nonLazyScript();
1829 TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(nobj));
1831 return nobj;
1834 return obj;
1838 * Given pc pointing after a property accessing bytecode, return true if the
1839 * access is "object-detecting" in the sense used by web scripts, e.g., when
1840 * checking whether document.all is defined.
1842 static bool
1843 Detecting(JSContext *cx, JSScript *script, jsbytecode *pc)
1845 JS_ASSERT(script->containsPC(pc));
1847 /* General case: a branch or equality op follows the access. */
1848 JSOp op = JSOp(*pc);
1849 if (js_CodeSpec[op].format & JOF_DETECTING)
1850 return true;
1852 jsbytecode *endpc = script->codeEnd();
1854 if (op == JSOP_NULL) {
1856 * Special case #1: handle (document.all == null). Don't sweat
1857 * about JS1.2's revision of the equality operators here.
1859 if (++pc < endpc) {
1860 op = JSOp(*pc);
1861 return op == JSOP_EQ || op == JSOP_NE;
1863 return false;
1866 if (op == JSOP_GETGNAME || op == JSOP_NAME) {
1868 * Special case #2: handle (document.all == undefined). Don't worry
1869 * about a local variable named |undefined| shadowing the immutable
1870 * global binding...because, really?
1872 JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
1873 if (atom == cx->names().undefined &&
1874 (pc += js_CodeSpec[op].length) < endpc) {
1875 op = JSOp(*pc);
1876 return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
1880 return false;
1883 /* static */ bool
1884 JSObject::nonNativeSetProperty(JSContext *cx, HandleObject obj,
1885 HandleId id, MutableHandleValue vp, bool strict)
1887 if (MOZ_UNLIKELY(obj->watched())) {
1888 WatchpointMap *wpmap = cx->compartment()->watchpointMap;
1889 if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
1890 return false;
1892 return obj->getOps()->setGeneric(cx, obj, id, vp, strict);
1895 /* static */ bool
1896 JSObject::nonNativeSetElement(JSContext *cx, HandleObject obj,
1897 uint32_t index, MutableHandleValue vp, bool strict)
1899 if (MOZ_UNLIKELY(obj->watched())) {
1900 RootedId id(cx);
1901 if (!IndexToId(cx, index, &id))
1902 return false;
1904 WatchpointMap *wpmap = cx->compartment()->watchpointMap;
1905 if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
1906 return false;
1908 return obj->getOps()->setElement(cx, obj, index, vp, strict);
1911 JS_FRIEND_API(bool)
1912 JS_CopyPropertyFrom(JSContext *cx, HandleId id, HandleObject target,
1913 HandleObject obj)
1915 // |obj| and |cx| are generally not same-compartment with |target| here.
1916 assertSameCompartment(cx, obj, id);
1917 Rooted<JSPropertyDescriptor> desc(cx);
1919 if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
1920 return false;
1921 MOZ_ASSERT(desc.object());
1923 // Silently skip JSPropertyOp-implemented accessors.
1924 if (desc.getter() && !desc.hasGetterObject())
1925 return true;
1926 if (desc.setter() && !desc.hasSetterObject())
1927 return true;
1929 JSAutoCompartment ac(cx, target);
1930 RootedId wrappedId(cx, id);
1931 if (!cx->compartment()->wrap(cx, &desc))
1932 return false;
1934 bool ignored;
1935 return DefineOwnProperty(cx, target, wrappedId, desc, &ignored);
1938 JS_FRIEND_API(bool)
1939 JS_CopyPropertiesFrom(JSContext *cx, HandleObject target, HandleObject obj)
1941 JSAutoCompartment ac(cx, obj);
1943 AutoIdVector props(cx);
1944 if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &props))
1945 return false;
1947 for (size_t i = 0; i < props.length(); ++i) {
1948 if (!JS_CopyPropertyFrom(cx, props[i], target, obj))
1949 return false;
1952 return true;
1955 static bool
1956 CopySlots(JSContext *cx, HandleObject from, HandleObject to)
1958 JS_ASSERT(!from->isNative() && !to->isNative());
1959 JS_ASSERT(from->getClass() == to->getClass());
1961 size_t n = 0;
1962 if (from->is<WrapperObject>() &&
1963 (Wrapper::wrapperHandler(from)->flags() &
1964 Wrapper::CROSS_COMPARTMENT)) {
1965 to->setSlot(0, from->getSlot(0));
1966 to->setSlot(1, from->getSlot(1));
1967 n = 2;
1970 size_t span = JSCLASS_RESERVED_SLOTS(from->getClass());
1971 RootedValue v(cx);
1972 for (; n < span; ++n) {
1973 v = from->getSlot(n);
1974 if (!cx->compartment()->wrap(cx, &v))
1975 return false;
1976 to->setSlot(n, v);
1978 return true;
1981 JSObject *
1982 js::CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto, HandleObject parent)
1984 if (!obj->isNative() && !obj->is<ProxyObject>()) {
1985 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
1986 JSMSG_CANT_CLONE_OBJECT);
1987 return nullptr;
1990 RootedObject clone(cx, NewObjectWithGivenProto(cx, obj->getClass(), proto, parent));
1991 if (!clone)
1992 return nullptr;
1993 if (obj->isNative()) {
1994 if (clone->is<JSFunction>() && (obj->compartment() != clone->compartment())) {
1995 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
1996 JSMSG_CANT_CLONE_OBJECT);
1997 return nullptr;
2000 if (obj->hasPrivate())
2001 clone->setPrivate(obj->getPrivate());
2002 } else {
2003 JS_ASSERT(obj->is<ProxyObject>());
2004 if (!CopySlots(cx, obj, clone))
2005 return nullptr;
2008 return clone;
2011 JSObject *
2012 js::DeepCloneObjectLiteral(JSContext *cx, HandleObject obj, NewObjectKind newKind)
2014 /* NB: Keep this in sync with XDRObjectLiteral. */
2015 JS_ASSERT_IF(obj->hasSingletonType(),
2016 JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
2017 JS_ASSERT(obj->is<JSObject>() || obj->is<ArrayObject>());
2019 // Result of the clone function.
2020 RootedObject clone(cx);
2022 // Temporary element/slot which would be stored in the cloned object.
2023 RootedValue v(cx);
2024 RootedObject deepObj(cx);
2026 if (obj->is<ArrayObject>()) {
2027 clone = NewDenseUnallocatedArray(cx, obj->as<ArrayObject>().length(), nullptr, newKind);
2028 } else {
2029 // Object literals are tenured by default as holded by the JSScript.
2030 JS_ASSERT(obj->isTenured());
2031 AllocKind kind = obj->tenuredGetAllocKind();
2032 Rooted<TypeObject*> typeObj(cx, obj->getType(cx));
2033 if (!typeObj)
2034 return nullptr;
2035 RootedObject parent(cx, obj->getParent());
2036 clone = NewObjectWithGivenProto(cx, &JSObject::class_, TaggedProto(typeObj->proto().toObject()),
2037 parent, kind, newKind);
2040 // Allocate the same number of slots.
2041 if (!clone || !clone->ensureElements(cx, obj->getDenseCapacity()))
2042 return nullptr;
2044 // Copy the number of initialized elements.
2045 uint32_t initialized = obj->getDenseInitializedLength();
2046 if (initialized)
2047 clone->setDenseInitializedLength(initialized);
2049 // Recursive copy of dense element.
2050 for (uint32_t i = 0; i < initialized; ++i) {
2051 v = obj->getDenseElement(i);
2052 if (v.isObject()) {
2053 deepObj = &v.toObject();
2054 deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind);
2055 if (!deepObj) {
2056 JS_ReportOutOfMemory(cx);
2057 return nullptr;
2059 v.setObject(*deepObj);
2061 clone->initDenseElement(i, v);
2064 JS_ASSERT(obj->compartment() == clone->compartment());
2065 JS_ASSERT(!obj->hasPrivate());
2066 RootedShape shape(cx, obj->lastProperty());
2067 size_t span = shape->slotSpan();
2068 clone->setLastProperty(cx, clone, shape);
2069 for (size_t i = 0; i < span; i++) {
2070 v = obj->getSlot(i);
2071 if (v.isObject()) {
2072 deepObj = &v.toObject();
2073 deepObj = js::DeepCloneObjectLiteral(cx, deepObj, newKind);
2074 if (!deepObj)
2075 return nullptr;
2076 v.setObject(*deepObj);
2078 clone->setSlot(i, v);
2081 if (obj->hasSingletonType()) {
2082 if (!JSObject::setSingletonType(cx, clone))
2083 return nullptr;
2084 } else if (obj->getClass() == &ArrayObject::class_) {
2085 FixArrayType(cx, clone);
2086 } else {
2087 FixObjectType(cx, clone);
2090 if (obj->is<ArrayObject>() && obj->denseElementsAreCopyOnWrite()) {
2091 if (!ObjectElements::MakeElementsCopyOnWrite(cx, clone))
2092 return nullptr;
2095 return clone;
2098 template<XDRMode mode>
2099 bool
2100 js::XDRObjectLiteral(XDRState<mode> *xdr, MutableHandleObject obj)
2102 /* NB: Keep this in sync with DeepCloneObjectLiteral. */
2104 JSContext *cx = xdr->cx();
2105 JS_ASSERT_IF(mode == XDR_ENCODE && obj->hasSingletonType(),
2106 JS::CompartmentOptionsRef(cx).getSingletonsAsTemplates());
2108 // Distinguish between objects and array classes.
2109 uint32_t isArray = 0;
2111 if (mode == XDR_ENCODE) {
2112 JS_ASSERT(obj->is<JSObject>() || obj->is<ArrayObject>());
2113 isArray = obj->getClass() == &ArrayObject::class_ ? 1 : 0;
2116 if (!xdr->codeUint32(&isArray))
2117 return false;
2120 if (isArray) {
2121 uint32_t length;
2123 if (mode == XDR_ENCODE)
2124 length = obj->as<ArrayObject>().length();
2126 if (!xdr->codeUint32(&length))
2127 return false;
2129 if (mode == XDR_DECODE)
2130 obj.set(NewDenseUnallocatedArray(cx, length, NULL, js::MaybeSingletonObject));
2132 } else {
2133 // Code the alloc kind of the object.
2134 AllocKind kind;
2136 if (mode == XDR_ENCODE) {
2137 JS_ASSERT(obj->getClass() == &JSObject::class_);
2138 JS_ASSERT(obj->isTenured());
2139 kind = obj->tenuredGetAllocKind();
2142 if (!xdr->codeEnum32(&kind))
2143 return false;
2145 if (mode == XDR_DECODE)
2146 obj.set(NewBuiltinClassInstance(cx, &JSObject::class_, kind, js::MaybeSingletonObject));
2151 uint32_t capacity;
2152 if (mode == XDR_ENCODE)
2153 capacity = obj->getDenseCapacity();
2154 if (!xdr->codeUint32(&capacity))
2155 return false;
2156 if (mode == XDR_DECODE) {
2157 if (!obj->ensureElements(cx, capacity)) {
2158 JS_ReportOutOfMemory(cx);
2159 return false;
2164 uint32_t initialized;
2166 if (mode == XDR_ENCODE)
2167 initialized = obj->getDenseInitializedLength();
2168 if (!xdr->codeUint32(&initialized))
2169 return false;
2170 if (mode == XDR_DECODE) {
2171 if (initialized)
2172 obj->setDenseInitializedLength(initialized);
2176 RootedValue tmpValue(cx);
2178 // Recursively copy dense elements.
2180 for (unsigned i = 0; i < initialized; i++) {
2181 if (mode == XDR_ENCODE)
2182 tmpValue = obj->getDenseElement(i);
2184 if (!xdr->codeConstValue(&tmpValue))
2185 return false;
2187 if (mode == XDR_DECODE)
2188 obj->initDenseElement(i, tmpValue);
2192 JS_ASSERT(!obj->hasPrivate());
2193 RootedShape shape(cx, obj->lastProperty());
2195 // Code the number of slots in the vector.
2196 unsigned nslot = 0;
2198 // Code ids of the object in order. As opposed to DeepCloneObjectLiteral we
2199 // cannot just re-use the shape of the original bytecode value and we have
2200 // to write down the shape as well as the corresponding values. Ideally we
2201 // would have a mechanism to serialize the shape too.
2202 js::AutoIdVector ids(cx);
2204 if (mode == XDR_ENCODE && !shape->isEmptyShape()) {
2205 nslot = shape->slotSpan();
2206 if (!ids.reserve(nslot))
2207 return false;
2209 for (unsigned i = 0; i < nslot; i++)
2210 ids.infallibleAppend(JSID_VOID);
2212 for (Shape::Range<NoGC> it(shape); !it.empty(); it.popFront()) {
2213 // If we have reached the native property of the array class, we
2214 // exit as the remaining would only be reserved slots.
2215 if (!it.front().hasSlot()) {
2216 JS_ASSERT(isArray);
2217 break;
2220 JS_ASSERT(it.front().hasDefaultGetter());
2221 ids[it.front().slot()].set(it.front().propid());
2225 if (!xdr->codeUint32(&nslot))
2226 return false;
2228 RootedAtom atom(cx);
2229 RootedId id(cx);
2230 uint32_t idType = 0;
2231 for (unsigned i = 0; i < nslot; i++) {
2232 if (mode == XDR_ENCODE) {
2233 id = ids[i];
2234 if (JSID_IS_INT(id))
2235 idType = JSID_TYPE_INT;
2236 else if (JSID_IS_ATOM(id))
2237 idType = JSID_TYPE_STRING;
2238 else
2239 MOZ_CRASH("Symbol property is not yet supported by XDR.");
2241 tmpValue = obj->getSlot(i);
2244 if (!xdr->codeUint32(&idType))
2245 return false;
2247 if (idType == JSID_TYPE_STRING) {
2248 if (mode == XDR_ENCODE)
2249 atom = JSID_TO_ATOM(id);
2250 if (!XDRAtom(xdr, &atom))
2251 return false;
2252 if (mode == XDR_DECODE)
2253 id = AtomToId(atom);
2254 } else {
2255 JS_ASSERT(idType == JSID_TYPE_INT);
2256 uint32_t indexVal;
2257 if (mode == XDR_ENCODE)
2258 indexVal = uint32_t(JSID_TO_INT(id));
2259 if (!xdr->codeUint32(&indexVal))
2260 return false;
2261 if (mode == XDR_DECODE)
2262 id = INT_TO_JSID(int32_t(indexVal));
2265 if (!xdr->codeConstValue(&tmpValue))
2266 return false;
2268 if (mode == XDR_DECODE) {
2269 if (!DefineNativeProperty(cx, obj, id, tmpValue, NULL, NULL, JSPROP_ENUMERATE))
2270 return false;
2274 JS_ASSERT_IF(mode == XDR_DECODE, !obj->inDictionaryMode());
2277 // Fix up types, distinguishing singleton-typed objects.
2278 uint32_t isSingletonTyped;
2279 if (mode == XDR_ENCODE)
2280 isSingletonTyped = obj->hasSingletonType() ? 1 : 0;
2281 if (!xdr->codeUint32(&isSingletonTyped))
2282 return false;
2284 if (mode == XDR_DECODE) {
2285 if (isSingletonTyped) {
2286 if (!JSObject::setSingletonType(cx, obj))
2287 return false;
2288 } else if (isArray) {
2289 FixArrayType(cx, obj);
2290 } else {
2291 FixObjectType(cx, obj);
2296 uint32_t frozen;
2297 bool extensible;
2298 if (mode == XDR_ENCODE) {
2299 if (!JSObject::isExtensible(cx, obj, &extensible))
2300 return false;
2301 frozen = extensible ? 0 : 1;
2303 if (!xdr->codeUint32(&frozen))
2304 return false;
2305 if (mode == XDR_DECODE && frozen == 1) {
2306 if (!JSObject::freeze(cx, obj))
2307 return false;
2311 if (isArray) {
2312 uint32_t copyOnWrite;
2313 if (mode == XDR_ENCODE)
2314 copyOnWrite = obj->denseElementsAreCopyOnWrite();
2315 if (!xdr->codeUint32(&copyOnWrite))
2316 return false;
2317 if (mode == XDR_DECODE && copyOnWrite) {
2318 if (!ObjectElements::MakeElementsCopyOnWrite(cx, obj))
2319 return false;
2323 return true;
2326 template bool
2327 js::XDRObjectLiteral(XDRState<XDR_ENCODE> *xdr, MutableHandleObject obj);
2329 template bool
2330 js::XDRObjectLiteral(XDRState<XDR_DECODE> *xdr, MutableHandleObject obj);
2332 JSObject *
2333 js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj)
2335 if (srcObj->getClass() == &JSObject::class_) {
2336 AllocKind kind = GetBackgroundAllocKind(GuessObjectGCKind(srcObj->numFixedSlots()));
2337 JS_ASSERT_IF(srcObj->isTenured(), kind == srcObj->tenuredGetAllocKind());
2339 JSObject *proto = cx->global()->getOrCreateObjectPrototype(cx);
2340 if (!proto)
2341 return nullptr;
2342 Rooted<TypeObject*> typeObj(cx, cx->getNewType(&JSObject::class_, TaggedProto(proto)));
2343 if (!typeObj)
2344 return nullptr;
2346 RootedShape shape(cx, srcObj->lastProperty());
2347 return NewReshapedObject(cx, typeObj, parent, kind, shape);
2350 JS_ASSERT(srcObj->is<ArrayObject>());
2351 JS_ASSERT(srcObj->denseElementsAreCopyOnWrite());
2352 JS_ASSERT(srcObj->getElementsHeader()->ownerObject() == srcObj);
2354 size_t length = srcObj->as<ArrayObject>().length();
2355 RootedObject res(cx, NewDenseAllocatedArray(cx, length, nullptr, MaybeSingletonObject));
2356 if (!res)
2357 return nullptr;
2359 RootedId id(cx);
2360 RootedValue value(cx);
2361 for (size_t i = 0; i < length; i++) {
2362 // The only markable values in copy on write arrays are atoms, which
2363 // can be freely copied between compartments.
2364 value = srcObj->getDenseElement(i);
2365 JS_ASSERT_IF(value.isMarkable(),
2366 cx->runtime()->isAtomsZone(value.toGCThing()->tenuredZone()));
2368 id = INT_TO_JSID(i);
2369 if (!JSObject::defineGeneric(cx, res, id, value, nullptr, nullptr, JSPROP_ENUMERATE))
2370 return nullptr;
2373 if (!ObjectElements::MakeElementsCopyOnWrite(cx, res))
2374 return nullptr;
2376 return res;
2379 struct JSObject::TradeGutsReserved {
2380 Vector<Value> avals;
2381 Vector<Value> bvals;
2382 int newafixed;
2383 int newbfixed;
2384 RootedShape newashape;
2385 RootedShape newbshape;
2386 HeapSlot *newaslots;
2387 HeapSlot *newbslots;
2389 explicit TradeGutsReserved(JSContext *cx)
2390 : avals(cx), bvals(cx),
2391 newafixed(0), newbfixed(0),
2392 newashape(cx), newbshape(cx),
2393 newaslots(nullptr), newbslots(nullptr)
2396 ~TradeGutsReserved()
2398 js_free(newaslots);
2399 js_free(newbslots);
2403 bool
2404 JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *aArg, JSObject *bArg,
2405 TradeGutsReserved &reserved)
2408 * Avoid GC in here to avoid confusing the tracing code with our
2409 * intermediate state.
2411 AutoSuppressGC suppress(cx);
2413 RootedObject a(cx, aArg);
2414 RootedObject b(cx, bArg);
2415 JS_ASSERT(a->compartment() == b->compartment());
2416 AutoCompartment ac(cx, a);
2419 * When performing multiple swaps between objects which may have different
2420 * numbers of fixed slots, we reserve all space ahead of time so that the
2421 * swaps can be performed infallibly.
2425 * Swap prototypes and classes on the two objects, so that TradeGuts can
2426 * preserve the types of the two objects.
2428 const Class *aClass = a->getClass();
2429 const Class *bClass = b->getClass();
2430 Rooted<TaggedProto> aProto(cx, a->getTaggedProto());
2431 Rooted<TaggedProto> bProto(cx, b->getTaggedProto());
2432 bool success;
2433 if (!SetClassAndProto(cx, a, bClass, bProto, &success) || !success)
2434 return false;
2435 if (!SetClassAndProto(cx, b, aClass, aProto, &success) || !success)
2436 return false;
2438 if (a->tenuredSizeOfThis() == b->tenuredSizeOfThis())
2439 return true;
2442 * If either object is native, it needs a new shape to preserve the
2443 * invariant that objects with the same shape have the same number of
2444 * inline slots. The fixed slots will be updated in place during TradeGuts.
2445 * Non-native objects need to be reshaped according to the new count.
2447 if (a->isNative()) {
2448 if (!a->generateOwnShape(cx))
2449 return false;
2450 } else {
2451 reserved.newbshape = EmptyShape::getInitialShape(cx, aClass, aProto, a->getParent(), a->getMetadata(),
2452 b->tenuredGetAllocKind());
2453 if (!reserved.newbshape)
2454 return false;
2456 if (b->isNative()) {
2457 if (!b->generateOwnShape(cx))
2458 return false;
2459 } else {
2460 reserved.newashape = EmptyShape::getInitialShape(cx, bClass, bProto, b->getParent(), b->getMetadata(),
2461 a->tenuredGetAllocKind());
2462 if (!reserved.newashape)
2463 return false;
2466 /* The avals/bvals vectors hold all original values from the objects. */
2468 if (!reserved.avals.reserve(a->slotSpan()))
2469 return false;
2470 if (!reserved.bvals.reserve(b->slotSpan()))
2471 return false;
2474 * The newafixed/newbfixed hold the number of fixed slots in the objects
2475 * after the swap. Adjust these counts according to whether the objects
2476 * use their last fixed slot for storing private data.
2479 reserved.newafixed = a->numFixedSlots();
2480 reserved.newbfixed = b->numFixedSlots();
2482 if (aClass->hasPrivate()) {
2483 reserved.newafixed++;
2484 reserved.newbfixed--;
2486 if (bClass->hasPrivate()) {
2487 reserved.newbfixed++;
2488 reserved.newafixed--;
2491 JS_ASSERT(reserved.newafixed >= 0);
2492 JS_ASSERT(reserved.newbfixed >= 0);
2495 * The newaslots/newbslots arrays hold any dynamic slots for the objects
2496 * if they do not have enough fixed slots to accomodate the slots in the
2497 * other object.
2500 unsigned adynamic = dynamicSlotsCount(reserved.newafixed, b->slotSpan(), b->getClass());
2501 unsigned bdynamic = dynamicSlotsCount(reserved.newbfixed, a->slotSpan(), a->getClass());
2503 if (adynamic) {
2504 reserved.newaslots = a->pod_malloc<HeapSlot>(adynamic);
2505 if (!reserved.newaslots)
2506 return false;
2507 Debug_SetSlotRangeToCrashOnTouch(reserved.newaslots, adynamic);
2509 if (bdynamic) {
2510 reserved.newbslots = b->pod_malloc<HeapSlot>(bdynamic);
2511 if (!reserved.newbslots)
2512 return false;
2513 Debug_SetSlotRangeToCrashOnTouch(reserved.newbslots, bdynamic);
2516 return true;
2519 void
2520 JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &reserved)
2522 JS_ASSERT(a->compartment() == b->compartment());
2523 JS_ASSERT(a->is<JSFunction>() == b->is<JSFunction>());
2526 * Neither object may be in the nursery, but ensure we update any embedded
2527 * nursery pointers in either object.
2529 #ifdef JSGC_GENERATIONAL
2530 JS_ASSERT(!IsInsideNursery(a) && !IsInsideNursery(b));
2531 cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(a);
2532 cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(b);
2533 #endif
2534 JS::AutoCheckCannotGC nogc;
2537 * Swap the object's types, to restore their initial type information.
2538 * The prototypes and classes of the objects were swapped in ReserveForTradeGuts.
2540 TypeObject *tmp = a->type_;
2541 a->type_ = b->type_;
2542 b->type_ = tmp;
2544 /* Don't try to swap a JSFunction for a plain function JSObject. */
2545 JS_ASSERT_IF(a->is<JSFunction>(), a->tenuredSizeOfThis() == b->tenuredSizeOfThis());
2548 * Regexp guts are more complicated -- we would need to migrate the
2549 * refcounted JIT code blob for them across compartments instead of just
2550 * swapping guts.
2552 JS_ASSERT(!a->is<RegExpObject>() && !b->is<RegExpObject>());
2554 /* Arrays can use their fixed storage for elements. */
2555 JS_ASSERT(!a->is<ArrayObject>() && !b->is<ArrayObject>());
2558 * Callers should not try to swap ArrayBuffer objects,
2559 * these use a different slot representation from other objects.
2561 JS_ASSERT(!a->is<ArrayBufferObject>() && !b->is<ArrayBufferObject>());
2563 /* Trade the guts of the objects. */
2564 const size_t size = a->tenuredSizeOfThis();
2565 if (size == b->tenuredSizeOfThis()) {
2567 * If the objects are the same size, then we make no assumptions about
2568 * whether they have dynamically allocated slots and instead just copy
2569 * them over wholesale.
2571 char tmp[mozilla::tl::Max<sizeof(JSFunction), sizeof(JSObject_Slots16)>::value];
2572 JS_ASSERT(size <= sizeof(tmp));
2574 js_memcpy(tmp, a, size);
2575 js_memcpy(a, b, size);
2576 js_memcpy(b, tmp, size);
2577 } else {
2579 * If the objects are of differing sizes, use the space we reserved
2580 * earlier to save the slots from each object and then copy them into
2581 * the new layout for the other object.
2584 uint32_t acap = a->slotSpan();
2585 uint32_t bcap = b->slotSpan();
2587 for (size_t i = 0; i < acap; i++)
2588 reserved.avals.infallibleAppend(a->getSlot(i));
2590 for (size_t i = 0; i < bcap; i++)
2591 reserved.bvals.infallibleAppend(b->getSlot(i));
2593 /* Done with the dynamic slots. */
2594 if (a->hasDynamicSlots())
2595 js_free(a->slots);
2596 if (b->hasDynamicSlots())
2597 js_free(b->slots);
2599 void *apriv = a->hasPrivate() ? a->getPrivate() : nullptr;
2600 void *bpriv = b->hasPrivate() ? b->getPrivate() : nullptr;
2602 char tmp[sizeof(JSObject)];
2603 js_memcpy(&tmp, a, sizeof tmp);
2604 js_memcpy(a, b, sizeof tmp);
2605 js_memcpy(b, &tmp, sizeof tmp);
2607 if (a->isNative())
2608 a->shape_->setNumFixedSlots(reserved.newafixed);
2609 else
2610 a->shape_ = reserved.newashape;
2612 a->slots = reserved.newaslots;
2613 a->initSlotRange(0, reserved.bvals.begin(), bcap);
2614 if (a->hasPrivate())
2615 a->initPrivate(bpriv);
2617 if (b->isNative())
2618 b->shape_->setNumFixedSlots(reserved.newbfixed);
2619 else
2620 b->shape_ = reserved.newbshape;
2622 b->slots = reserved.newbslots;
2623 b->initSlotRange(0, reserved.avals.begin(), acap);
2624 if (b->hasPrivate())
2625 b->initPrivate(apriv);
2627 /* Make sure the destructor for reserved doesn't free the slots. */
2628 reserved.newaslots = nullptr;
2629 reserved.newbslots = nullptr;
2632 if (a->inDictionaryMode())
2633 a->lastProperty()->listp = &a->shape_;
2634 if (b->inDictionaryMode())
2635 b->lastProperty()->listp = &b->shape_;
2637 #ifdef JSGC_INCREMENTAL
2639 * We need a write barrier here. If |a| was marked and |b| was not, then
2640 * after the swap, |b|'s guts would never be marked. The write barrier
2641 * solves this.
2643 * Normally write barriers happen before the write. However, that's not
2644 * necessary here because nothing is being destroyed. We're just swapping.
2645 * We don't do the barrier before TradeGuts because ReserveForTradeGuts
2646 * makes changes to the objects that might confuse the tracing code.
2648 JS::Zone *zone = a->zone();
2649 if (zone->needsIncrementalBarrier()) {
2650 MarkChildren(zone->barrierTracer(), a);
2651 MarkChildren(zone->barrierTracer(), b);
2653 #endif
2656 /* Use this method with extreme caution. It trades the guts of two objects. */
2657 bool
2658 JSObject::swap(JSContext *cx, HandleObject a, HandleObject b)
2660 // Ensure swap doesn't cause a finalizer to not be run.
2661 JS_ASSERT(IsBackgroundFinalized(a->tenuredGetAllocKind()) ==
2662 IsBackgroundFinalized(b->tenuredGetAllocKind()));
2663 JS_ASSERT(a->compartment() == b->compartment());
2665 unsigned r = NotifyGCPreSwap(a, b);
2667 TradeGutsReserved reserved(cx);
2668 if (!ReserveForTradeGuts(cx, a, b, reserved)) {
2669 NotifyGCPostSwap(b, a, r);
2670 return false;
2672 TradeGuts(cx, a, b, reserved);
2674 NotifyGCPostSwap(a, b, r);
2675 return true;
2678 static bool
2679 DefineStandardSlot(JSContext *cx, HandleObject obj, JSProtoKey key, JSAtom *atom,
2680 HandleValue v, uint32_t attrs, bool &named)
2682 RootedId id(cx, AtomToId(atom));
2684 if (key != JSProto_Null) {
2686 * Initializing an actual standard class on a global object. If the
2687 * property is not yet present, force it into a new one bound to a
2688 * reserved slot. Otherwise, go through the normal property path.
2690 JS_ASSERT(obj->is<GlobalObject>());
2691 JS_ASSERT(obj->isNative());
2693 if (!obj->nativeLookup(cx, id)) {
2694 obj->as<GlobalObject>().setConstructorPropertySlot(key, v);
2696 uint32_t slot = GlobalObject::constructorPropertySlot(key);
2697 if (!JSObject::addProperty(cx, obj, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0))
2698 return false;
2700 named = true;
2701 return true;
2705 named = JSObject::defineGeneric(cx, obj, id,
2706 v, JS_PropertyStub, JS_StrictPropertyStub, attrs);
2707 return named;
2710 static void
2711 SetClassObject(JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto)
2713 JS_ASSERT(!obj->getParent());
2714 if (!obj->is<GlobalObject>())
2715 return;
2717 obj->as<GlobalObject>().setConstructor(key, ObjectOrNullValue(cobj));
2718 obj->as<GlobalObject>().setPrototype(key, ObjectOrNullValue(proto));
2721 static void
2722 ClearClassObject(JSObject *obj, JSProtoKey key)
2724 JS_ASSERT(!obj->getParent());
2725 if (!obj->is<GlobalObject>())
2726 return;
2728 obj->as<GlobalObject>().setConstructor(key, UndefinedValue());
2729 obj->as<GlobalObject>().setPrototype(key, UndefinedValue());
2732 static JSObject *
2733 DefineConstructorAndPrototype(JSContext *cx, HandleObject obj, JSProtoKey key, HandleAtom atom,
2734 JSObject *protoProto, const Class *clasp,
2735 Native constructor, unsigned nargs,
2736 const JSPropertySpec *ps, const JSFunctionSpec *fs,
2737 const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs,
2738 JSObject **ctorp, AllocKind ctorKind)
2741 * Create a prototype object for this class.
2743 * FIXME: lazy standard (built-in) class initialization and even older
2744 * eager boostrapping code rely on all of these properties:
2746 * 1. NewObject attempting to compute a default prototype object when
2747 * passed null for proto; and
2749 * 2. NewObject tolerating no default prototype (null proto slot value)
2750 * due to this js_InitClass call coming from js_InitFunctionClass on an
2751 * otherwise-uninitialized global.
2753 * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
2754 * &JSFunction::class_, not a JSObject-sized (smaller) GC-thing.
2756 * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
2757 * be &JSFunction::class_ (we could break compatibility easily). But
2758 * fixing (3) is not enough without addressing the bootstrapping dependency
2759 * on (1) and (2).
2763 * Create the prototype object. (GlobalObject::createBlankPrototype isn't
2764 * used because it parents the prototype object to the global and because
2765 * it uses WithProto::Given. FIXME: Undo dependencies on this parentage
2766 * [which already needs to happen for bug 638316], figure out nicer
2767 * semantics for null-protoProto, and use createBlankPrototype.)
2769 RootedObject proto(cx, NewObjectWithClassProto(cx, clasp, protoProto, obj, SingletonObject));
2770 if (!proto)
2771 return nullptr;
2773 /* After this point, control must exit via label bad or out. */
2774 RootedObject ctor(cx);
2775 bool named = false;
2776 bool cached = false;
2777 if (!constructor) {
2779 * Lacking a constructor, name the prototype (e.g., Math) unless this
2780 * class (a) is anonymous, i.e. for internal use only; (b) the class
2781 * of obj (the global object) is has a reserved slot indexed by key;
2782 * and (c) key is not the null key.
2784 if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) || !obj->is<GlobalObject>() ||
2785 key == JSProto_Null)
2787 uint32_t attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
2788 ? JSPROP_READONLY | JSPROP_PERMANENT
2789 : 0;
2790 RootedValue value(cx, ObjectValue(*proto));
2791 if (!DefineStandardSlot(cx, obj, key, atom, value, attrs, named))
2792 goto bad;
2795 ctor = proto;
2796 } else {
2798 * Create the constructor, not using GlobalObject::createConstructor
2799 * because the constructor currently must have |obj| as its parent.
2800 * (FIXME: remove this dependency on the exact identity of the parent,
2801 * perhaps as part of bug 638316.)
2803 RootedFunction fun(cx, NewFunction(cx, js::NullPtr(), constructor, nargs,
2804 JSFunction::NATIVE_CTOR, obj, atom, ctorKind));
2805 if (!fun)
2806 goto bad;
2809 * Set the class object early for standard class constructors. Type
2810 * inference may need to access these, and js::GetBuiltinPrototype will
2811 * fail if it tries to do a reentrant reconstruction of the class.
2813 if (key != JSProto_Null) {
2814 SetClassObject(obj, key, fun, proto);
2815 cached = true;
2818 RootedValue value(cx, ObjectValue(*fun));
2819 if (!DefineStandardSlot(cx, obj, key, atom, value, 0, named))
2820 goto bad;
2823 * Optionally construct the prototype object, before the class has
2824 * been fully initialized. Allow the ctor to replace proto with a
2825 * different object, as is done for operator new.
2827 ctor = fun;
2828 if (!LinkConstructorAndPrototype(cx, ctor, proto))
2829 goto bad;
2831 /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
2832 Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
2833 if (ctor->getClass() == clasp && !ctor->splicePrototype(cx, clasp, tagged))
2834 goto bad;
2837 if (!DefinePropertiesAndFunctions(cx, proto, ps, fs) ||
2838 (ctor != proto && !DefinePropertiesAndFunctions(cx, ctor, static_ps, static_fs)))
2840 goto bad;
2843 /* If this is a standard class, cache its prototype. */
2844 if (!cached && key != JSProto_Null)
2845 SetClassObject(obj, key, ctor, proto);
2847 if (ctorp)
2848 *ctorp = ctor;
2849 return proto;
2851 bad:
2852 if (named) {
2853 bool succeeded;
2854 RootedId id(cx, AtomToId(atom));
2855 JSObject::deleteGeneric(cx, obj, id, &succeeded);
2857 if (cached)
2858 ClearClassObject(obj, key);
2859 return nullptr;
2862 JSObject *
2863 js_InitClass(JSContext *cx, HandleObject obj, JSObject *protoProto_,
2864 const Class *clasp, Native constructor, unsigned nargs,
2865 const JSPropertySpec *ps, const JSFunctionSpec *fs,
2866 const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs,
2867 JSObject **ctorp, AllocKind ctorKind)
2869 RootedObject protoProto(cx, protoProto_);
2871 /* Assert mandatory function pointer members. */
2872 JS_ASSERT(clasp->addProperty);
2873 JS_ASSERT(clasp->delProperty);
2874 JS_ASSERT(clasp->getProperty);
2875 JS_ASSERT(clasp->setProperty);
2876 JS_ASSERT(clasp->enumerate);
2877 JS_ASSERT(clasp->resolve);
2878 JS_ASSERT(clasp->convert);
2880 RootedAtom atom(cx, Atomize(cx, clasp->name, strlen(clasp->name)));
2881 if (!atom)
2882 return nullptr;
2885 * All instances of the class will inherit properties from the prototype
2886 * object we are about to create (in DefineConstructorAndPrototype), which
2887 * in turn will inherit from protoProto.
2889 * When initializing a standard class (other than Object), if protoProto is
2890 * null, default to Object.prototype. The engine's internal uses of
2891 * js_InitClass depend on this nicety.
2893 JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
2894 if (key != JSProto_Null &&
2895 !protoProto &&
2896 !GetBuiltinPrototype(cx, JSProto_Object, &protoProto))
2898 return nullptr;
2901 return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
2902 ps, fs, static_ps, static_fs, ctorp, ctorKind);
2905 /* static */ inline bool
2906 JSObject::updateSlotsForSpan(ThreadSafeContext *cx,
2907 HandleObject obj, size_t oldSpan, size_t newSpan)
2909 JS_ASSERT(cx->isThreadLocal(obj));
2910 JS_ASSERT(oldSpan != newSpan);
2912 size_t oldCount = dynamicSlotsCount(obj->numFixedSlots(), oldSpan, obj->getClass());
2913 size_t newCount = dynamicSlotsCount(obj->numFixedSlots(), newSpan, obj->getClass());
2915 if (oldSpan < newSpan) {
2916 if (oldCount < newCount && !JSObject::growSlots(cx, obj, oldCount, newCount))
2917 return false;
2919 if (newSpan == oldSpan + 1)
2920 obj->initSlotUnchecked(oldSpan, UndefinedValue());
2921 else
2922 obj->initializeSlotRange(oldSpan, newSpan - oldSpan);
2923 } else {
2924 /* Trigger write barriers on the old slots before reallocating. */
2925 obj->prepareSlotRangeForOverwrite(newSpan, oldSpan);
2926 obj->invalidateSlotRange(newSpan, oldSpan - newSpan);
2928 if (oldCount > newCount)
2929 JSObject::shrinkSlots(cx, obj, oldCount, newCount);
2932 return true;
2935 /* static */ bool
2936 JSObject::setLastProperty(ThreadSafeContext *cx, HandleObject obj, HandleShape shape)
2938 JS_ASSERT(cx->isThreadLocal(obj));
2939 JS_ASSERT(!obj->inDictionaryMode());
2940 JS_ASSERT(!shape->inDictionary());
2941 JS_ASSERT(shape->compartment() == obj->compartment());
2942 JS_ASSERT(shape->numFixedSlots() == obj->numFixedSlots());
2944 size_t oldSpan = obj->lastProperty()->slotSpan();
2945 size_t newSpan = shape->slotSpan();
2947 if (oldSpan == newSpan) {
2948 obj->shape_ = shape;
2949 return true;
2952 if (!updateSlotsForSpan(cx, obj, oldSpan, newSpan))
2953 return false;
2955 obj->shape_ = shape;
2956 return true;
2959 /* static */ bool
2960 JSObject::setSlotSpan(ThreadSafeContext *cx, HandleObject obj, uint32_t span)
2962 JS_ASSERT(cx->isThreadLocal(obj));
2963 JS_ASSERT(obj->inDictionaryMode());
2965 size_t oldSpan = obj->lastProperty()->base()->slotSpan();
2966 if (oldSpan == span)
2967 return true;
2969 if (!JSObject::updateSlotsForSpan(cx, obj, oldSpan, span))
2970 return false;
2972 obj->lastProperty()->base()->setSlotSpan(span);
2973 return true;
2976 // This will not run the garbage collector. If a nursery cannot accomodate the slot array
2977 // an attempt will be made to place the array in the tenured area.
2978 static HeapSlot *
2979 AllocateSlots(ThreadSafeContext *cx, JSObject *obj, uint32_t nslots)
2981 #ifdef JSGC_GENERATIONAL
2982 if (cx->isJSContext())
2983 return cx->asJSContext()->runtime()->gc.nursery.allocateSlots(obj, nslots);
2984 #endif
2985 #ifdef JSGC_FJGENERATIONAL
2986 if (cx->isForkJoinContext())
2987 return cx->asForkJoinContext()->nursery().allocateSlots(obj, nslots);
2988 #endif
2989 return obj->pod_malloc<HeapSlot>(nslots);
2992 // This will not run the garbage collector. If a nursery cannot accomodate the slot array
2993 // an attempt will be made to place the array in the tenured area.
2995 // If this returns null then the old slots will be left alone.
2996 static HeapSlot *
2997 ReallocateSlots(ThreadSafeContext *cx, JSObject *obj, HeapSlot *oldSlots,
2998 uint32_t oldCount, uint32_t newCount)
3000 #ifdef JSGC_GENERATIONAL
3001 if (cx->isJSContext()) {
3002 return cx->asJSContext()->runtime()->gc.nursery.reallocateSlots(obj, oldSlots,
3003 oldCount, newCount);
3005 #endif
3006 #ifdef JSGC_FJGENERATIONAL
3007 if (cx->isForkJoinContext()) {
3008 return cx->asForkJoinContext()->nursery().reallocateSlots(obj, oldSlots,
3009 oldCount, newCount);
3011 #endif
3012 return obj->pod_realloc<HeapSlot>(oldSlots, oldCount, newCount);
3015 /* static */ bool
3016 JSObject::growSlots(ThreadSafeContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount)
3018 JS_ASSERT(cx->isThreadLocal(obj));
3019 JS_ASSERT(newCount > oldCount);
3020 JS_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
3023 * Slot capacities are determined by the span of allocated objects. Due to
3024 * the limited number of bits to store shape slots, object growth is
3025 * throttled well before the slot capacity can overflow.
3027 JS_ASSERT(newCount < NELEMENTS_LIMIT);
3030 * If we are allocating slots for an object whose type is always created
3031 * by calling 'new' on a particular script, bump the GC kind for that
3032 * type to give these objects a larger number of fixed slots when future
3033 * objects are constructed.
3035 if (!obj->hasLazyType() && !oldCount && obj->type()->newScript()) {
3036 JSObject *oldTemplate = obj->type()->newScript()->templateObject;
3037 gc::AllocKind kind = gc::GetGCObjectFixedSlotsKind(oldTemplate->numFixedSlots());
3038 uint32_t newScriptSlots = gc::GetGCKindSlots(kind);
3039 if (newScriptSlots == obj->numFixedSlots() &&
3040 gc::TryIncrementAllocKind(&kind) &&
3041 cx->isJSContext())
3043 JSContext *ncx = cx->asJSContext();
3044 AutoEnterAnalysis enter(ncx);
3046 Rooted<TypeObject*> typeObj(cx, obj->type());
3047 RootedShape shape(cx, oldTemplate->lastProperty());
3048 JSObject *reshapedObj = NewReshapedObject(ncx, typeObj, obj->getParent(), kind, shape,
3049 MaybeSingletonObject);
3050 if (!reshapedObj)
3051 return false;
3053 typeObj->newScript()->templateObject = reshapedObj;
3054 typeObj->markStateChange(ncx);
3058 if (!oldCount) {
3059 obj->slots = AllocateSlots(cx, obj, newCount);
3060 if (!obj->slots)
3061 return false;
3062 Debug_SetSlotRangeToCrashOnTouch(obj->slots, newCount);
3063 return true;
3066 HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount);
3067 if (!newslots)
3068 return false; /* Leave slots at its old size. */
3070 obj->slots = newslots;
3072 Debug_SetSlotRangeToCrashOnTouch(obj->slots + oldCount, newCount - oldCount);
3074 return true;
3077 static void
3078 FreeSlots(ThreadSafeContext *cx, HeapSlot *slots)
3080 #ifdef JSGC_GENERATIONAL
3081 // Note: threads without a JSContext do not have access to GGC nursery allocated things.
3082 if (cx->isJSContext())
3083 return cx->asJSContext()->runtime()->gc.nursery.freeSlots(slots);
3084 #endif
3085 #ifdef JSGC_FJGENERATIONAL
3086 if (cx->isForkJoinContext())
3087 return cx->asForkJoinContext()->nursery().freeSlots(slots);
3088 #endif
3089 js_free(slots);
3092 /* static */ void
3093 JSObject::shrinkSlots(ThreadSafeContext *cx, HandleObject obj, uint32_t oldCount, uint32_t newCount)
3095 JS_ASSERT(cx->isThreadLocal(obj));
3096 JS_ASSERT(newCount < oldCount);
3098 if (newCount == 0) {
3099 FreeSlots(cx, obj->slots);
3100 obj->slots = nullptr;
3101 return;
3104 JS_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
3106 HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount);
3107 if (!newslots)
3108 return; /* Leave slots at its old size. */
3110 obj->slots = newslots;
3113 /* static */ bool
3114 JSObject::sparsifyDenseElement(ExclusiveContext *cx, HandleObject obj, uint32_t index)
3116 if (!obj->maybeCopyElementsForWrite(cx))
3117 return false;
3119 RootedValue value(cx, obj->getDenseElement(index));
3120 JS_ASSERT(!value.isMagic(JS_ELEMENTS_HOLE));
3122 JSObject::removeDenseElementForSparseIndex(cx, obj, index);
3124 uint32_t slot = obj->slotSpan();
3125 if (!obj->addDataProperty(cx, INT_TO_JSID(index), slot, JSPROP_ENUMERATE)) {
3126 obj->setDenseElement(index, value);
3127 return false;
3130 JS_ASSERT(slot == obj->slotSpan() - 1);
3131 obj->initSlot(slot, value);
3133 return true;
3136 /* static */ bool
3137 JSObject::sparsifyDenseElements(js::ExclusiveContext *cx, HandleObject obj)
3139 if (!obj->maybeCopyElementsForWrite(cx))
3140 return false;
3142 uint32_t initialized = obj->getDenseInitializedLength();
3144 /* Create new properties with the value of non-hole dense elements. */
3145 for (uint32_t i = 0; i < initialized; i++) {
3146 if (obj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE))
3147 continue;
3149 if (!sparsifyDenseElement(cx, obj, i))
3150 return false;
3153 if (initialized)
3154 obj->setDenseInitializedLength(0);
3157 * Reduce storage for dense elements which are now holes. Explicitly mark
3158 * the elements capacity as zero, so that any attempts to add dense
3159 * elements will be caught in ensureDenseElements.
3161 if (obj->getDenseCapacity()) {
3162 obj->shrinkElements(cx, 0);
3163 obj->getElementsHeader()->capacity = 0;
3166 return true;
3169 bool
3170 JSObject::willBeSparseElements(uint32_t requiredCapacity, uint32_t newElementsHint)
3172 JS_ASSERT(isNative());
3173 JS_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
3175 uint32_t cap = getDenseCapacity();
3176 JS_ASSERT(requiredCapacity >= cap);
3178 if (requiredCapacity >= NELEMENTS_LIMIT)
3179 return true;
3181 uint32_t minimalDenseCount = requiredCapacity / SPARSE_DENSITY_RATIO;
3182 if (newElementsHint >= minimalDenseCount)
3183 return false;
3184 minimalDenseCount -= newElementsHint;
3186 if (minimalDenseCount > cap)
3187 return true;
3189 uint32_t len = getDenseInitializedLength();
3190 const Value *elems = getDenseElements();
3191 for (uint32_t i = 0; i < len; i++) {
3192 if (!elems[i].isMagic(JS_ELEMENTS_HOLE) && !--minimalDenseCount)
3193 return false;
3195 return true;
3198 /* static */ JSObject::EnsureDenseResult
3199 JSObject::maybeDensifySparseElements(js::ExclusiveContext *cx, HandleObject obj)
3202 * Wait until after the object goes into dictionary mode, which must happen
3203 * when sparsely packing any array with more than MIN_SPARSE_INDEX elements
3204 * (see PropertyTree::MAX_HEIGHT).
3206 if (!obj->inDictionaryMode())
3207 return ED_SPARSE;
3210 * Only measure the number of indexed properties every log(n) times when
3211 * populating the object.
3213 uint32_t slotSpan = obj->slotSpan();
3214 if (slotSpan != RoundUpPow2(slotSpan))
3215 return ED_SPARSE;
3217 /* Watch for conditions under which an object's elements cannot be dense. */
3218 if (!obj->nonProxyIsExtensible() || obj->watched())
3219 return ED_SPARSE;
3222 * The indexes in the object need to be sufficiently dense before they can
3223 * be converted to dense mode.
3225 uint32_t numDenseElements = 0;
3226 uint32_t newInitializedLength = 0;
3228 RootedShape shape(cx, obj->lastProperty());
3229 while (!shape->isEmptyShape()) {
3230 uint32_t index;
3231 if (js_IdIsIndex(shape->propid(), &index)) {
3232 if (shape->attributes() == JSPROP_ENUMERATE &&
3233 shape->hasDefaultGetter() &&
3234 shape->hasDefaultSetter())
3236 numDenseElements++;
3237 newInitializedLength = Max(newInitializedLength, index + 1);
3238 } else {
3240 * For simplicity, only densify the object if all indexed
3241 * properties can be converted to dense elements.
3243 return ED_SPARSE;
3246 shape = shape->previous();
3249 if (numDenseElements * SPARSE_DENSITY_RATIO < newInitializedLength)
3250 return ED_SPARSE;
3252 if (newInitializedLength >= NELEMENTS_LIMIT)
3253 return ED_SPARSE;
3256 * This object meets all necessary restrictions, convert all indexed
3257 * properties into dense elements.
3260 if (!obj->maybeCopyElementsForWrite(cx))
3261 return ED_FAILED;
3263 if (newInitializedLength > obj->getDenseCapacity()) {
3264 if (!obj->growElements(cx, newInitializedLength))
3265 return ED_FAILED;
3268 obj->ensureDenseInitializedLength(cx, newInitializedLength, 0);
3270 RootedValue value(cx);
3272 shape = obj->lastProperty();
3273 while (!shape->isEmptyShape()) {
3274 jsid id = shape->propid();
3275 uint32_t index;
3276 if (js_IdIsIndex(id, &index)) {
3277 value = obj->getSlot(shape->slot());
3280 * When removing a property from a dictionary, the specified
3281 * property will be removed from the dictionary list and the
3282 * last property will then be changed due to reshaping the object.
3283 * Compute the next shape in the traverse, watching for such
3284 * removals from the list.
3286 if (shape != obj->lastProperty()) {
3287 shape = shape->previous();
3288 if (!obj->removeProperty(cx, id))
3289 return ED_FAILED;
3290 } else {
3291 if (!obj->removeProperty(cx, id))
3292 return ED_FAILED;
3293 shape = obj->lastProperty();
3296 obj->setDenseElement(index, value);
3297 } else {
3298 shape = shape->previous();
3303 * All indexed properties on the object are now dense, clear the indexed
3304 * flag so that we will not start using sparse indexes again if we need
3305 * to grow the object.
3307 if (!obj->clearFlag(cx, BaseShape::INDEXED))
3308 return ED_FAILED;
3310 return ED_OK;
3313 // This will not run the garbage collector. If a nursery cannot accomodate the element array
3314 // an attempt will be made to place the array in the tenured area.
3315 static ObjectElements *
3316 AllocateElements(ThreadSafeContext *cx, JSObject *obj, uint32_t nelems)
3318 #ifdef JSGC_GENERATIONAL
3319 if (cx->isJSContext())
3320 return cx->asJSContext()->runtime()->gc.nursery.allocateElements(obj, nelems);
3321 #endif
3322 #ifdef JSGC_FJGENERATIONAL
3323 if (cx->isForkJoinContext())
3324 return cx->asForkJoinContext()->nursery().allocateElements(obj, nelems);
3325 #endif
3327 return reinterpret_cast<js::ObjectElements *>(obj->pod_malloc<HeapSlot>(nelems));
3330 // This will not run the garbage collector. If a nursery cannot accomodate the element array
3331 // an attempt will be made to place the array in the tenured area.
3332 static ObjectElements *
3333 ReallocateElements(ThreadSafeContext *cx, JSObject *obj, ObjectElements *oldHeader,
3334 uint32_t oldCount, uint32_t newCount)
3336 #ifdef JSGC_GENERATIONAL
3337 if (cx->isJSContext()) {
3338 return cx->asJSContext()->runtime()->gc.nursery.reallocateElements(obj, oldHeader,
3339 oldCount, newCount);
3341 #endif
3342 #ifdef JSGC_FJGENERATIONAL
3343 if (cx->isForkJoinContext()) {
3344 return cx->asForkJoinContext()->nursery().reallocateElements(obj, oldHeader,
3345 oldCount, newCount);
3347 #endif
3349 return reinterpret_cast<js::ObjectElements *>(
3350 obj->pod_realloc<HeapSlot>(reinterpret_cast<HeapSlot *>(oldHeader),
3351 oldCount, newCount));
3354 // Round up |reqAllocated| to a good size. Up to 1 Mebi (i.e. 1,048,576) the
3355 // slot count is usually a power-of-two:
3357 // 8, 16, 32, 64, ..., 256 Ki, 512 Ki, 1 Mi
3359 // Beyond that, we use this formula:
3361 // count(n+1) = Math.ceil(count(n) * 1.125)
3363 // where |count(n)| is the size of the nth bucket measured in MiSlots.
3365 // These counts lets us add N elements to an array in amortized O(N) time.
3366 // Having the second class means that for bigger arrays the constant factor is
3367 // higher, but we waste less space.
3369 // There is one exception to the above rule: for the power-of-two cases, if the
3370 // chosen capacity would be 2/3 or more of the array's length, the chosen
3371 // capacity is adjusted (up or down) to be equal to the array's length
3372 // (assuming length is at least as large as the required capacity). This avoids
3373 // the allocation of excess elements which are unlikely to be needed, either in
3374 // this resizing or a subsequent one. The 2/3 factor is chosen so that
3375 // exceptional resizings will at most triple the capacity, as opposed to the
3376 // usual doubling.
3378 /* static */ uint32_t
3379 JSObject::goodAllocated(uint32_t reqAllocated, uint32_t length = 0)
3381 static const uint32_t Mebi = 1024 * 1024;
3383 // This table was generated with this JavaScript code and a small amount
3384 // subsequent reformatting:
3386 // for (let n = 1, i = 0; i < 57; i++) {
3387 // print((n * 1024 * 1024) + ', ');
3388 // n = Math.ceil(n * 1.125);
3389 // }
3390 // print('0');
3392 // The final element is a sentinel value.
3393 static const uint32_t BigBuckets[] = {
3394 1048576, 2097152, 3145728, 4194304, 5242880, 6291456, 7340032, 8388608,
3395 9437184, 11534336, 13631488, 15728640, 17825792, 20971520, 24117248,
3396 27262976, 31457280, 35651584, 40894464, 46137344, 52428800, 59768832,
3397 68157440, 77594624, 88080384, 99614720, 112197632, 126877696,
3398 143654912, 162529280, 183500800, 206569472, 232783872, 262144000,
3399 295698432, 333447168, 375390208, 422576128, 476053504, 535822336,
3400 602931200, 678428672, 763363328, 858783744, 966787072, 1088421888,
3401 1224736768, 1377828864, 1550843904, 1744830464, 1962934272, 2208301056,
3402 2485125120, 2796552192, 3146776576, 3541041152, 3984588800, 0
3405 // This code relies very much on |goodAllocated| being a uint32_t.
3406 uint32_t goodAllocated = reqAllocated;
3407 if (goodAllocated < Mebi) {
3408 goodAllocated = RoundUpPow2(goodAllocated);
3410 // Look for the abovementioned exception.
3411 uint32_t goodCapacity = goodAllocated - ObjectElements::VALUES_PER_HEADER;
3412 uint32_t reqCapacity = reqAllocated - ObjectElements::VALUES_PER_HEADER;
3413 if (length >= reqCapacity && goodCapacity > (length / 3) * 2)
3414 goodAllocated = length + ObjectElements::VALUES_PER_HEADER;
3416 if (goodAllocated < JSObject::SLOT_CAPACITY_MIN)
3417 goodAllocated = JSObject::SLOT_CAPACITY_MIN;
3419 } else {
3420 uint32_t i = 0;
3421 while (true) {
3422 uint32_t b = BigBuckets[i++];
3423 if (b >= goodAllocated) {
3424 // Found the first bucket greater than or equal to
3425 // |goodAllocated|.
3426 goodAllocated = b;
3427 break;
3428 } else if (b == 0) {
3429 // Hit the end; return the maximum possible goodAllocated.
3430 goodAllocated = 0xffffffff;
3431 break;
3436 return goodAllocated;
3439 bool
3440 JSObject::growElements(ThreadSafeContext *cx, uint32_t reqCapacity)
3442 JS_ASSERT(nonProxyIsExtensible());
3443 JS_ASSERT(canHaveNonEmptyElements());
3444 if (denseElementsAreCopyOnWrite())
3445 MOZ_CRASH();
3447 uint32_t oldCapacity = getDenseCapacity();
3448 JS_ASSERT(oldCapacity < reqCapacity);
3450 using mozilla::CheckedInt;
3452 CheckedInt<uint32_t> checkedOldAllocated =
3453 CheckedInt<uint32_t>(oldCapacity) + ObjectElements::VALUES_PER_HEADER;
3454 CheckedInt<uint32_t> checkedReqAllocated =
3455 CheckedInt<uint32_t>(reqCapacity) + ObjectElements::VALUES_PER_HEADER;
3456 if (!checkedOldAllocated.isValid() || !checkedReqAllocated.isValid())
3457 return false;
3459 uint32_t reqAllocated = checkedReqAllocated.value();
3460 uint32_t oldAllocated = checkedOldAllocated.value();
3462 uint32_t newAllocated;
3463 if (is<ArrayObject>() && !as<ArrayObject>().lengthIsWritable()) {
3464 JS_ASSERT(reqCapacity <= as<ArrayObject>().length());
3465 // Preserve the |capacity <= length| invariant for arrays with
3466 // non-writable length. See also js::ArraySetLength which initially
3467 // enforces this requirement.
3468 newAllocated = reqAllocated;
3469 } else {
3470 newAllocated = goodAllocated(reqAllocated, getElementsHeader()->length);
3473 uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
3474 JS_ASSERT(newCapacity > oldCapacity && newCapacity >= reqCapacity);
3476 // Don't let nelements get close to wrapping around uint32_t.
3477 if (newCapacity >= NELEMENTS_LIMIT)
3478 return false;
3480 uint32_t initlen = getDenseInitializedLength();
3482 ObjectElements *newheader;
3483 if (hasDynamicElements()) {
3484 newheader = ReallocateElements(cx, this, getElementsHeader(), oldAllocated, newAllocated);
3485 if (!newheader)
3486 return false; // Leave elements at its old size.
3487 } else {
3488 newheader = AllocateElements(cx, this, newAllocated);
3489 if (!newheader)
3490 return false; // Leave elements at its old size.
3491 js_memcpy(newheader, getElementsHeader(),
3492 (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
3495 newheader->capacity = newCapacity;
3496 elements = newheader->elements();
3498 Debug_SetSlotRangeToCrashOnTouch(elements + initlen, newCapacity - initlen);
3500 return true;
3503 void
3504 JSObject::shrinkElements(ThreadSafeContext *cx, uint32_t reqCapacity)
3506 JS_ASSERT(cx->isThreadLocal(this));
3507 JS_ASSERT(canHaveNonEmptyElements());
3508 if (denseElementsAreCopyOnWrite())
3509 MOZ_CRASH();
3511 if (!hasDynamicElements())
3512 return;
3514 uint32_t oldCapacity = getDenseCapacity();
3515 JS_ASSERT(reqCapacity < oldCapacity);
3517 uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER;
3518 uint32_t reqAllocated = reqCapacity + ObjectElements::VALUES_PER_HEADER;
3519 uint32_t newAllocated = goodAllocated(reqAllocated);
3520 if (newAllocated == oldAllocated)
3521 return; // Leave elements at its old size.
3523 MOZ_ASSERT(newAllocated > ObjectElements::VALUES_PER_HEADER);
3524 uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
3526 ObjectElements *newheader = ReallocateElements(cx, this, getElementsHeader(),
3527 oldAllocated, newAllocated);
3528 if (!newheader) {
3529 cx->recoverFromOutOfMemory();
3530 return; // Leave elements at its old size.
3533 newheader->capacity = newCapacity;
3534 elements = newheader->elements();
3537 /* static */ bool
3538 JSObject::CopyElementsForWrite(ThreadSafeContext *cx, JSObject *obj)
3540 JS_ASSERT(obj->denseElementsAreCopyOnWrite());
3542 // The original owner of a COW elements array should never be modified.
3543 JS_ASSERT(obj->getElementsHeader()->ownerObject() != obj);
3545 uint32_t initlen = obj->getDenseInitializedLength();
3546 uint32_t allocated = initlen + ObjectElements::VALUES_PER_HEADER;
3547 uint32_t newAllocated = goodAllocated(allocated);
3549 uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
3551 if (newCapacity >= NELEMENTS_LIMIT)
3552 return false;
3554 JSObject::writeBarrierPre(obj->getElementsHeader()->ownerObject());
3556 ObjectElements *newheader = AllocateElements(cx, obj, newAllocated);
3557 if (!newheader)
3558 return false;
3559 js_memcpy(newheader, obj->getElementsHeader(),
3560 (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
3562 newheader->capacity = newCapacity;
3563 newheader->clearCopyOnWrite();
3564 obj->elements = newheader->elements();
3566 Debug_SetSlotRangeToCrashOnTouch(obj->elements + initlen, newCapacity - initlen);
3568 return true;
3571 bool
3572 js::SetClassAndProto(JSContext *cx, HandleObject obj,
3573 const Class *clasp, Handle<js::TaggedProto> proto,
3574 bool *succeeded)
3577 * Regenerate shapes for all of the scopes along the old prototype chain,
3578 * in case any entries were filled by looking up through obj. Stop when a
3579 * non-native object is found, prototype lookups will not be cached across
3580 * these.
3582 * How this shape change is done is very delicate; the change can be made
3583 * either by marking the object's prototype as uncacheable (such that the
3584 * property cache and JIT'ed ICs cannot assume the shape determines the
3585 * prototype) or by just generating a new shape for the object. Choosing
3586 * the former is bad if the object is on the prototype chain of other
3587 * objects, as the uncacheable prototype can inhibit iterator caches on
3588 * those objects and slow down prototype accesses. Choosing the latter is
3589 * bad if there are many similar objects to this one which will have their
3590 * prototype mutated, as the generateOwnShape forces the object into
3591 * dictionary mode and similar property lineages will be repeatedly cloned.
3593 * :XXX: bug 707717 make this code less brittle.
3595 *succeeded = false;
3596 RootedObject oldproto(cx, obj);
3597 while (oldproto && oldproto->isNative()) {
3598 if (oldproto->hasSingletonType()) {
3599 if (!oldproto->generateOwnShape(cx))
3600 return false;
3601 } else {
3602 if (!oldproto->setUncacheableProto(cx))
3603 return false;
3605 oldproto = oldproto->getProto();
3608 if (obj->hasSingletonType()) {
3610 * Just splice the prototype, but mark the properties as unknown for
3611 * consistent behavior.
3613 if (!obj->splicePrototype(cx, clasp, proto))
3614 return false;
3615 MarkTypeObjectUnknownProperties(cx, obj->type());
3616 *succeeded = true;
3617 return true;
3620 if (proto.isObject()) {
3621 RootedObject protoObj(cx, proto.toObject());
3622 if (!JSObject::setNewTypeUnknown(cx, clasp, protoObj))
3623 return false;
3626 TypeObject *type = cx->getNewType(clasp, proto);
3627 if (!type)
3628 return false;
3631 * Setting __proto__ on an object that has escaped and may be referenced by
3632 * other heap objects can only be done if the properties of both objects
3633 * are unknown. Type sets containing this object will contain the original
3634 * type but not the new type of the object, so we need to go and scan the
3635 * entire compartment for type sets which have these objects and mark them
3636 * as containing generic objects.
3638 MarkTypeObjectUnknownProperties(cx, obj->type(), true);
3639 MarkTypeObjectUnknownProperties(cx, type, true);
3641 obj->setType(type);
3643 *succeeded = true;
3644 return true;
3647 static bool
3648 MaybeResolveConstructor(ExclusiveContext *cxArg, Handle<GlobalObject*> global, JSProtoKey key)
3650 if (global->isStandardClassResolved(key))
3651 return true;
3652 if (!cxArg->shouldBeJSContext())
3653 return false;
3655 JSContext *cx = cxArg->asJSContext();
3656 return GlobalObject::resolveConstructor(cx, global, key);
3659 bool
3660 js::GetBuiltinConstructor(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject objp)
3662 MOZ_ASSERT(key != JSProto_Null);
3663 Rooted<GlobalObject*> global(cx, cx->global());
3664 if (!MaybeResolveConstructor(cx, global, key))
3665 return false;
3667 objp.set(&global->getConstructor(key).toObject());
3668 return true;
3671 bool
3672 js::GetBuiltinPrototype(ExclusiveContext *cx, JSProtoKey key, MutableHandleObject protop)
3674 MOZ_ASSERT(key != JSProto_Null);
3675 Rooted<GlobalObject*> global(cx, cx->global());
3676 if (!MaybeResolveConstructor(cx, global, key))
3677 return false;
3679 protop.set(&global->getPrototype(key).toObject());
3680 return true;
3683 static bool
3684 IsStandardPrototype(JSObject *obj, JSProtoKey key)
3686 GlobalObject &global = obj->global();
3687 Value v = global.getPrototype(key);
3688 return v.isObject() && obj == &v.toObject();
3691 JSProtoKey
3692 JS::IdentifyStandardInstance(JSObject *obj)
3694 // Note: The prototype shares its JSClass with instances.
3695 JS_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
3696 JSProtoKey key = StandardProtoKeyOrNull(obj);
3697 if (key != JSProto_Null && !IsStandardPrototype(obj, key))
3698 return key;
3699 return JSProto_Null;
3702 JSProtoKey
3703 JS::IdentifyStandardPrototype(JSObject *obj)
3705 // Note: The prototype shares its JSClass with instances.
3706 JS_ASSERT(!obj->is<CrossCompartmentWrapperObject>());
3707 JSProtoKey key = StandardProtoKeyOrNull(obj);
3708 if (key != JSProto_Null && IsStandardPrototype(obj, key))
3709 return key;
3710 return JSProto_Null;
3713 JSProtoKey
3714 JS::IdentifyStandardInstanceOrPrototype(JSObject *obj)
3716 return StandardProtoKeyOrNull(obj);
3719 JSProtoKey
3720 JS::IdentifyStandardConstructor(JSObject *obj)
3722 // Note that NATIVE_CTOR does not imply that we are a standard constructor,
3723 // but the converse is true (at least until we start having self-hosted
3724 // constructors for standard classes). This lets us avoid a costly loop for
3725 // many functions (which, depending on the call site, may be the common case).
3726 if (!obj->is<JSFunction>() || !(obj->as<JSFunction>().flags() & JSFunction::NATIVE_CTOR))
3727 return JSProto_Null;
3729 GlobalObject &global = obj->global();
3730 for (size_t k = 0; k < JSProto_LIMIT; ++k) {
3731 JSProtoKey key = static_cast<JSProtoKey>(k);
3732 if (global.getConstructor(key) == ObjectValue(*obj))
3733 return key;
3736 return JSProto_Null;
3739 bool
3740 js::FindClassObject(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp)
3742 JSProtoKey protoKey = ClassProtoKeyOrAnonymousOrNull(clasp);
3743 if (protoKey != JSProto_Null) {
3744 JS_ASSERT(JSProto_Null < protoKey);
3745 JS_ASSERT(protoKey < JSProto_LIMIT);
3746 return GetBuiltinConstructor(cx, protoKey, protop);
3749 JSAtom *atom = Atomize(cx, clasp->name, strlen(clasp->name));
3750 if (!atom)
3751 return false;
3752 RootedId id(cx, AtomToId(atom));
3754 RootedObject pobj(cx);
3755 RootedShape shape(cx);
3756 if (!LookupNativeProperty(cx, cx->global(), id, &pobj, &shape))
3757 return false;
3758 RootedValue v(cx);
3759 if (shape && pobj->isNative()) {
3760 if (shape->hasSlot())
3761 v = pobj->nativeGetSlot(shape->slot());
3763 if (v.isObject())
3764 protop.set(&v.toObject());
3765 return true;
3768 bool
3769 JSObject::isConstructor() const
3771 if (is<JSFunction>()) {
3772 const JSFunction &fun = as<JSFunction>();
3773 return fun.isNativeConstructor() || fun.isInterpretedConstructor();
3775 return getClass()->construct != nullptr;
3778 /* static */ bool
3779 JSObject::allocSlot(ThreadSafeContext *cx, HandleObject obj, uint32_t *slotp)
3781 JS_ASSERT(cx->isThreadLocal(obj));
3783 uint32_t slot = obj->slotSpan();
3784 JS_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
3787 * If this object is in dictionary mode, try to pull a free slot from the
3788 * shape table's slot-number freelist.
3790 if (obj->inDictionaryMode()) {
3791 ShapeTable &table = obj->lastProperty()->table();
3792 uint32_t last = table.freelist;
3793 if (last != SHAPE_INVALID_SLOT) {
3794 #ifdef DEBUG
3795 JS_ASSERT(last < slot);
3796 uint32_t next = obj->getSlot(last).toPrivateUint32();
3797 JS_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot);
3798 #endif
3800 *slotp = last;
3802 const Value &vref = obj->getSlot(last);
3803 table.freelist = vref.toPrivateUint32();
3804 obj->setSlot(last, UndefinedValue());
3805 return true;
3809 if (slot >= SHAPE_MAXIMUM_SLOT) {
3810 js_ReportOutOfMemory(cx);
3811 return false;
3814 *slotp = slot;
3816 if (obj->inDictionaryMode() && !setSlotSpan(cx, obj, slot + 1))
3817 return false;
3819 return true;
3822 void
3823 JSObject::freeSlot(uint32_t slot)
3825 JS_ASSERT(slot < slotSpan());
3827 if (inDictionaryMode()) {
3828 uint32_t &last = lastProperty()->table().freelist;
3830 /* Can't afford to check the whole freelist, but let's check the head. */
3831 JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan() && last != slot);
3834 * Place all freed slots other than reserved slots (bug 595230) on the
3835 * dictionary's free list.
3837 if (JSSLOT_FREE(getClass()) <= slot) {
3838 JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan());
3839 setSlot(slot, PrivateUint32Value(last));
3840 last = slot;
3841 return;
3844 setSlot(slot, UndefinedValue());
3847 static bool
3848 PurgeProtoChain(ExclusiveContext *cx, JSObject *objArg, HandleId id)
3850 /* Root locally so we can re-assign. */
3851 RootedObject obj(cx, objArg);
3853 RootedShape shape(cx);
3854 while (obj) {
3855 /* Lookups will not be cached through non-native protos. */
3856 if (!obj->isNative())
3857 break;
3859 shape = obj->nativeLookup(cx, id);
3860 if (shape) {
3861 if (!obj->shadowingShapeChange(cx, *shape))
3862 return false;
3864 obj->shadowingShapeChange(cx, *shape);
3865 return true;
3867 obj = obj->getProto();
3870 return true;
3873 static bool
3874 PurgeScopeChainHelper(ExclusiveContext *cx, HandleObject objArg, HandleId id)
3876 /* Re-root locally so we can re-assign. */
3877 RootedObject obj(cx, objArg);
3879 JS_ASSERT(obj->isNative());
3880 JS_ASSERT(obj->isDelegate());
3882 /* Lookups on integer ids cannot be cached through prototypes. */
3883 if (JSID_IS_INT(id))
3884 return true;
3886 PurgeProtoChain(cx, obj->getProto(), id);
3889 * We must purge the scope chain only for Call objects as they are the only
3890 * kind of cacheable non-global object that can gain properties after outer
3891 * properties with the same names have been cached or traced. Call objects
3892 * may gain such properties via eval introducing new vars; see bug 490364.
3894 if (obj->is<CallObject>()) {
3895 while ((obj = obj->enclosingScope()) != nullptr) {
3896 if (!PurgeProtoChain(cx, obj, id))
3897 return false;
3901 return true;
3905 * PurgeScopeChain does nothing if obj is not itself a prototype or parent
3906 * scope, else it reshapes the scope and prototype chains it links. It calls
3907 * PurgeScopeChainHelper, which asserts that obj is flagged as a delegate
3908 * (i.e., obj has ever been on a prototype or parent chain).
3910 static inline bool
3911 PurgeScopeChain(ExclusiveContext *cx, JS::HandleObject obj, JS::HandleId id)
3913 if (obj->isDelegate())
3914 return PurgeScopeChainHelper(cx, obj, id);
3915 return true;
3918 bool
3919 baseops::DefineGeneric(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value,
3920 PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
3922 return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
3925 /* static */ bool
3926 JSObject::defineGeneric(ExclusiveContext *cx, HandleObject obj,
3927 HandleId id, HandleValue value,
3928 JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
3930 JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
3931 js::DefineGenericOp op = obj->getOps()->defineGeneric;
3932 if (op) {
3933 if (!cx->shouldBeJSContext())
3934 return false;
3935 return op(cx->asJSContext(), obj, id, value, getter, setter, attrs);
3937 return baseops::DefineGeneric(cx, obj, id, value, getter, setter, attrs);
3940 /* static */ bool
3941 JSObject::defineProperty(ExclusiveContext *cx, HandleObject obj,
3942 PropertyName *name, HandleValue value,
3943 JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
3945 RootedId id(cx, NameToId(name));
3946 return defineGeneric(cx, obj, id, value, getter, setter, attrs);
3949 bool
3950 baseops::DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, HandleValue value,
3951 PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
3953 RootedId id(cx);
3954 if (index <= JSID_INT_MAX) {
3955 id = INT_TO_JSID(index);
3956 return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
3959 AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
3961 if (!IndexToId(cx, index, &id))
3962 return false;
3964 return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
3967 /* static */ bool
3968 JSObject::defineElement(ExclusiveContext *cx, HandleObject obj,
3969 uint32_t index, HandleValue value,
3970 JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
3972 js::DefineElementOp op = obj->getOps()->defineElement;
3973 if (op) {
3974 if (!cx->shouldBeJSContext())
3975 return false;
3976 return op(cx->asJSContext(), obj, index, value, getter, setter, attrs);
3978 return baseops::DefineElement(cx, obj, index, value, getter, setter, attrs);
3981 Shape *
3982 JSObject::addDataProperty(ExclusiveContext *cx, jsid idArg, uint32_t slot, unsigned attrs)
3984 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
3985 RootedObject self(cx, this);
3986 RootedId id(cx, idArg);
3987 return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
3990 Shape *
3991 JSObject::addDataProperty(ExclusiveContext *cx, HandlePropertyName name,
3992 uint32_t slot, unsigned attrs)
3994 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
3995 RootedObject self(cx, this);
3996 RootedId id(cx, NameToId(name));
3997 return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
4001 * Backward compatibility requires allowing addProperty hooks to mutate the
4002 * nominal initial value of a slotful property, while GC safety wants that
4003 * value to be stored before the call-out through the hook. Optimize to do
4004 * both while saving cycles for classes that stub their addProperty hook.
4006 template <ExecutionMode mode>
4007 static inline bool
4008 CallAddPropertyHook(typename ExecutionModeTraits<mode>::ExclusiveContextType cxArg,
4009 const Class *clasp, HandleObject obj, HandleShape shape,
4010 HandleValue nominal)
4012 if (clasp->addProperty != JS_PropertyStub) {
4013 if (mode == ParallelExecution)
4014 return false;
4016 ExclusiveContext *cx = cxArg->asExclusiveContext();
4017 if (!cx->shouldBeJSContext())
4018 return false;
4020 /* Make a local copy of value so addProperty can mutate its inout parameter. */
4021 RootedValue value(cx, nominal);
4023 Rooted<jsid> id(cx, shape->propid());
4024 if (!CallJSPropertyOp(cx->asJSContext(), clasp->addProperty, obj, id, &value)) {
4025 obj->removeProperty(cx, shape->propid());
4026 return false;
4028 if (value.get() != nominal) {
4029 if (shape->hasSlot())
4030 obj->nativeSetSlotWithType(cx, shape, value);
4033 return true;
4036 template <ExecutionMode mode>
4037 static inline bool
4038 CallAddPropertyHookDense(typename ExecutionModeTraits<mode>::ExclusiveContextType cxArg,
4039 const Class *clasp, HandleObject obj, uint32_t index,
4040 HandleValue nominal)
4042 /* Inline addProperty for array objects. */
4043 if (obj->is<ArrayObject>()) {
4044 ArrayObject *arr = &obj->as<ArrayObject>();
4045 uint32_t length = arr->length();
4046 if (index >= length) {
4047 if (mode == ParallelExecution) {
4048 /* We cannot deal with overflows in parallel. */
4049 if (length > INT32_MAX)
4050 return false;
4051 arr->setLengthInt32(index + 1);
4052 } else {
4053 arr->setLength(cxArg->asExclusiveContext(), index + 1);
4056 return true;
4059 if (clasp->addProperty != JS_PropertyStub) {
4060 if (mode == ParallelExecution)
4061 return false;
4063 ExclusiveContext *cx = cxArg->asExclusiveContext();
4064 if (!cx->shouldBeJSContext())
4065 return false;
4067 if (!obj->maybeCopyElementsForWrite(cx))
4068 return false;
4070 /* Make a local copy of value so addProperty can mutate its inout parameter. */
4071 RootedValue value(cx, nominal);
4073 Rooted<jsid> id(cx, INT_TO_JSID(index));
4074 if (!CallJSPropertyOp(cx->asJSContext(), clasp->addProperty, obj, id, &value)) {
4075 obj->setDenseElementHole(cx, index);
4076 return false;
4078 if (value.get() != nominal)
4079 obj->setDenseElementWithType(cx, index, value);
4082 return true;
4085 template <ExecutionMode mode>
4086 static bool
4087 UpdateShapeTypeAndValue(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
4088 JSObject *obj, Shape *shape, const Value &value)
4090 jsid id = shape->propid();
4091 if (shape->hasSlot()) {
4092 if (mode == ParallelExecution) {
4093 if (!obj->nativeSetSlotIfHasType(shape, value, /* overwriting = */ false))
4094 return false;
4095 } else {
4096 obj->nativeSetSlotWithType(cx->asExclusiveContext(), shape, value, /* overwriting = */ false);
4099 if (!shape->hasSlot() || !shape->hasDefaultGetter() || !shape->hasDefaultSetter()) {
4100 if (mode == ParallelExecution) {
4101 if (!IsTypePropertyIdMarkedNonData(obj, id))
4102 return false;
4103 } else {
4104 MarkTypePropertyNonData(cx->asExclusiveContext(), obj, id);
4107 if (!shape->writable()) {
4108 if (mode == ParallelExecution) {
4109 if (!IsTypePropertyIdMarkedNonWritable(obj, id))
4110 return false;
4111 } else {
4112 MarkTypePropertyNonWritable(cx->asExclusiveContext(), obj, id);
4115 return true;
4118 template <ExecutionMode mode>
4119 static inline bool
4120 DefinePropertyOrElement(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
4121 HandleObject obj, HandleId id,
4122 PropertyOp getter, StrictPropertyOp setter,
4123 unsigned attrs, HandleValue value,
4124 bool callSetterAfterwards, bool setterIsStrict)
4126 /* Use dense storage for new indexed properties where possible. */
4127 if (JSID_IS_INT(id) &&
4128 getter == JS_PropertyStub &&
4129 setter == JS_StrictPropertyStub &&
4130 attrs == JSPROP_ENUMERATE &&
4131 (!obj->isIndexed() || !obj->nativeContainsPure(id)) &&
4132 !obj->is<TypedArrayObject>())
4134 uint32_t index = JSID_TO_INT(id);
4135 bool definesPast;
4136 if (!WouldDefinePastNonwritableLength(cx, obj, index, setterIsStrict, &definesPast))
4137 return false;
4138 if (definesPast)
4139 return true;
4141 JSObject::EnsureDenseResult result;
4142 if (mode == ParallelExecution) {
4143 if (obj->writeToIndexWouldMarkNotPacked(index))
4144 return false;
4145 result = obj->ensureDenseElementsPreservePackedFlag(cx, index, 1);
4146 } else {
4147 result = obj->ensureDenseElements(cx->asExclusiveContext(), index, 1);
4150 if (result == JSObject::ED_FAILED)
4151 return false;
4152 if (result == JSObject::ED_OK) {
4153 if (mode == ParallelExecution) {
4154 if (!obj->setDenseElementIfHasType(index, value))
4155 return false;
4156 } else {
4157 obj->setDenseElementWithType(cx->asExclusiveContext(), index, value);
4159 return CallAddPropertyHookDense<mode>(cx, obj->getClass(), obj, index, value);
4163 if (obj->is<ArrayObject>()) {
4164 Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
4165 if (id == NameToId(cx->names().length)) {
4166 if (mode == SequentialExecution && !cx->shouldBeJSContext())
4167 return false;
4168 return ArraySetLength<mode>(ExecutionModeTraits<mode>::toContextType(cx), arr, id,
4169 attrs, value, setterIsStrict);
4172 uint32_t index;
4173 if (js_IdIsIndex(id, &index)) {
4174 bool definesPast;
4175 if (!WouldDefinePastNonwritableLength(cx, arr, index, setterIsStrict, &definesPast))
4176 return false;
4177 if (definesPast)
4178 return true;
4182 // Don't define new indexed properties on typed arrays.
4183 if (obj->is<TypedArrayObject>()) {
4184 uint64_t index;
4185 if (IsTypedArrayIndex(id, &index))
4186 return true;
4189 AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
4191 RootedShape shape(cx, JSObject::putProperty<mode>(cx, obj, id, getter, setter,
4192 SHAPE_INVALID_SLOT, attrs, 0));
4193 if (!shape)
4194 return false;
4196 if (!UpdateShapeTypeAndValue<mode>(cx, obj, shape, value))
4197 return false;
4200 * Clear any existing dense index after adding a sparse indexed property,
4201 * and investigate converting the object to dense indexes.
4203 if (JSID_IS_INT(id)) {
4204 if (mode == ParallelExecution)
4205 return false;
4207 if (!obj->maybeCopyElementsForWrite(cx))
4208 return false;
4210 ExclusiveContext *ncx = cx->asExclusiveContext();
4211 uint32_t index = JSID_TO_INT(id);
4212 JSObject::removeDenseElementForSparseIndex(ncx, obj, index);
4213 JSObject::EnsureDenseResult result = JSObject::maybeDensifySparseElements(ncx, obj);
4214 if (result == JSObject::ED_FAILED)
4215 return false;
4216 if (result == JSObject::ED_OK) {
4217 JS_ASSERT(setter == JS_StrictPropertyStub);
4218 return CallAddPropertyHookDense<mode>(cx, obj->getClass(), obj, index, value);
4222 if (!CallAddPropertyHook<mode>(cx, obj->getClass(), obj, shape, value))
4223 return false;
4225 if (callSetterAfterwards && setter != JS_StrictPropertyStub) {
4226 if (!cx->shouldBeJSContext())
4227 return false;
4228 RootedValue nvalue(cx, value);
4229 return NativeSet<mode>(ExecutionModeTraits<mode>::toContextType(cx),
4230 obj, obj, shape, setterIsStrict, &nvalue);
4232 return true;
4235 static bool
4236 NativeLookupOwnProperty(ExclusiveContext *cx, HandleObject obj, HandleId id,
4237 MutableHandle<Shape*> shapep);
4239 bool
4240 js::DefineNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value,
4241 PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
4243 JS_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
4245 AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
4247 RootedShape shape(cx);
4248 RootedValue updateValue(cx, value);
4249 bool shouldDefine = true;
4252 * If defining a getter or setter, we must check for its counterpart and
4253 * update the attributes and property ops. A getter or setter is really
4254 * only half of a property.
4256 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
4257 if (!NativeLookupOwnProperty(cx, obj, id, &shape))
4258 return false;
4259 if (shape) {
4261 * If we are defining a getter whose setter was already defined, or
4262 * vice versa, finish the job via obj->changeProperty.
4264 if (IsImplicitDenseOrTypedArrayElement(shape)) {
4265 if (obj->is<TypedArrayObject>()) {
4266 /* Ignore getter/setter properties added to typed arrays. */
4267 return true;
4269 if (!JSObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
4270 return false;
4271 shape = obj->nativeLookup(cx, id);
4273 if (shape->isAccessorDescriptor()) {
4274 attrs = ApplyOrDefaultAttributes(attrs, shape);
4275 shape = JSObject::changeProperty<SequentialExecution>(cx, obj, shape, attrs,
4276 JSPROP_GETTER | JSPROP_SETTER,
4277 (attrs & JSPROP_GETTER)
4278 ? getter
4279 : shape->getter(),
4280 (attrs & JSPROP_SETTER)
4281 ? setter
4282 : shape->setter());
4283 if (!shape)
4284 return false;
4285 shouldDefine = false;
4288 } else if (!(attrs & JSPROP_IGNORE_VALUE)) {
4290 * We might still want to ignore redefining some of our attributes, if the
4291 * request came through a proxy after Object.defineProperty(), but only if we're redefining
4292 * a data property.
4293 * FIXME: All this logic should be removed when Proxies use PropDesc, but we need to
4294 * remove JSPropertyOp getters and setters first.
4295 * FIXME: This is still wrong for various array types, and will set the wrong attributes
4296 * by accident, but we can't use NativeLookupOwnProperty in this case, because of resolve
4297 * loops.
4299 shape = obj->nativeLookup(cx, id);
4300 if (shape && shape->isDataDescriptor())
4301 attrs = ApplyOrDefaultAttributes(attrs, shape);
4302 } else {
4304 * We have been asked merely to update some attributes by a caller of
4305 * Object.defineProperty, laundered through the proxy system, and returning here. We can
4306 * get away with just using JSObject::changeProperty here.
4308 if (!NativeLookupOwnProperty(cx, obj, id, &shape))
4309 return false;
4311 if (shape) {
4312 // Don't forget about arrays.
4313 if (IsImplicitDenseOrTypedArrayElement(shape)) {
4314 if (obj->is<TypedArrayObject>()) {
4316 * Silently ignore attempts to change individial index attributes.
4317 * FIXME: Uses the same broken behavior as for accessors. This should
4318 * probably throw.
4320 return true;
4322 if (!JSObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
4323 return false;
4324 shape = obj->nativeLookup(cx, id);
4327 attrs = ApplyOrDefaultAttributes(attrs, shape);
4329 if (shape->isAccessorDescriptor() && !(attrs & JSPROP_IGNORE_READONLY)) {
4330 // ES6 draft 2014-10-14 9.1.6.3 step 7.c: Since [[Writable]]
4331 // is present, change the existing accessor property to a data
4332 // property.
4333 updateValue = UndefinedValue();
4334 } else {
4335 // We are at most changing some attributes, and cannot convert
4336 // from data descriptor to accessor, or vice versa. Take
4337 // everything from the shape that we aren't changing.
4338 uint32_t propMask = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
4339 attrs = (shape->attributes() & ~propMask) | (attrs & propMask);
4340 getter = shape->getter();
4341 setter = shape->setter();
4342 if (shape->hasSlot())
4343 updateValue = obj->getSlot(shape->slot());
4349 * Purge the property cache of any properties named by id that are about
4350 * to be shadowed in obj's scope chain.
4352 if (!PurgeScopeChain(cx, obj, id))
4353 return false;
4355 /* Use the object's class getter and setter by default. */
4356 const Class *clasp = obj->getClass();
4357 if (!getter && !(attrs & JSPROP_GETTER))
4358 getter = clasp->getProperty;
4359 if (!setter && !(attrs & JSPROP_SETTER))
4360 setter = clasp->setProperty;
4362 if (shouldDefine) {
4363 // Handle the default cases here. Anyone that wanted to set non-default attributes has
4364 // cleared the IGNORE flags by now. Since we can never get here with JSPROP_IGNORE_VALUE
4365 // relevant, just clear it.
4366 attrs = ApplyOrDefaultAttributes(attrs) & ~JSPROP_IGNORE_VALUE;
4367 return DefinePropertyOrElement<SequentialExecution>(cx, obj, id, getter, setter,
4368 attrs, updateValue, false, false);
4371 MOZ_ASSERT(shape);
4373 JS_ALWAYS_TRUE(UpdateShapeTypeAndValue<SequentialExecution>(cx, obj, shape, updateValue));
4375 return CallAddPropertyHook<SequentialExecution>(cx, clasp, obj, shape, updateValue);
4379 * Call obj's resolve hook.
4381 * cx, id, and flags are the parameters initially passed to the ongoing lookup;
4382 * objp and propp are its out parameters. obj is an object along the prototype
4383 * chain from where the lookup started.
4385 * There are four possible outcomes:
4387 * - On failure, report an error or exception and return false.
4389 * - If we are already resolving a property of *curobjp, set *recursedp = true,
4390 * and return true.
4392 * - If the resolve hook finds or defines the sought property, set *objp and
4393 * *propp appropriately, set *recursedp = false, and return true.
4395 * - Otherwise no property was resolved. Set *propp = nullptr and
4396 * *recursedp = false and return true.
4398 static MOZ_ALWAYS_INLINE bool
4399 CallResolveOp(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp,
4400 MutableHandleShape propp, bool *recursedp)
4402 const Class *clasp = obj->getClass();
4403 JSResolveOp resolve = clasp->resolve;
4406 * Avoid recursion on (obj, id) already being resolved on cx.
4408 * Once we have successfully added an entry for (obj, key) to
4409 * cx->resolvingTable, control must go through cleanup: before
4410 * returning. But note that JS_DHASH_ADD may find an existing
4411 * entry, in which case we bail to suppress runaway recursion.
4413 AutoResolving resolving(cx, obj, id);
4414 if (resolving.alreadyStarted()) {
4415 /* Already resolving id in obj -- suppress recursion. */
4416 *recursedp = true;
4417 return true;
4419 *recursedp = false;
4421 propp.set(nullptr);
4423 if (clasp->flags & JSCLASS_NEW_RESOLVE) {
4424 JSNewResolveOp newresolve = reinterpret_cast<JSNewResolveOp>(resolve);
4425 RootedObject obj2(cx, nullptr);
4426 if (!newresolve(cx, obj, id, &obj2))
4427 return false;
4430 * We trust the new style resolve hook to set obj2 to nullptr when
4431 * the id cannot be resolved. But, when obj2 is not null, we do
4432 * not assume that id must exist and do full nativeLookup for
4433 * compatibility.
4435 if (!obj2)
4436 return true;
4438 if (!obj2->isNative()) {
4439 /* Whoops, newresolve handed back a foreign obj2. */
4440 JS_ASSERT(obj2 != obj);
4441 return JSObject::lookupGeneric(cx, obj2, id, objp, propp);
4444 objp.set(obj2);
4445 } else {
4446 if (!resolve(cx, obj, id))
4447 return false;
4449 objp.set(obj);
4452 if (JSID_IS_INT(id) && objp->containsDenseElement(JSID_TO_INT(id))) {
4453 MarkDenseOrTypedArrayElementFound<CanGC>(propp);
4454 return true;
4457 Shape *shape;
4458 if (!objp->nativeEmpty() && (shape = objp->nativeLookup(cx, id)))
4459 propp.set(shape);
4460 else
4461 objp.set(nullptr);
4463 return true;
4466 template <AllowGC allowGC>
4467 static MOZ_ALWAYS_INLINE bool
4468 LookupOwnPropertyInline(ExclusiveContext *cx,
4469 typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
4470 typename MaybeRooted<jsid, allowGC>::HandleType id,
4471 typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
4472 typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp,
4473 bool *donep)
4475 // Check for a native dense element.
4476 if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
4477 objp.set(obj);
4478 MarkDenseOrTypedArrayElementFound<allowGC>(propp);
4479 *donep = true;
4480 return true;
4483 // Check for a typed array element. Integer lookups always finish here
4484 // so that integer properties on the prototype are ignored even for out
4485 // of bounds accesses.
4486 if (obj->template is<TypedArrayObject>()) {
4487 uint64_t index;
4488 if (IsTypedArrayIndex(id, &index)) {
4489 if (index < obj->template as<TypedArrayObject>().length()) {
4490 objp.set(obj);
4491 MarkDenseOrTypedArrayElementFound<allowGC>(propp);
4492 } else {
4493 objp.set(nullptr);
4494 propp.set(nullptr);
4496 *donep = true;
4497 return true;
4501 // Check for a native property.
4502 if (Shape *shape = obj->nativeLookup(cx, id)) {
4503 objp.set(obj);
4504 propp.set(shape);
4505 *donep = true;
4506 return true;
4509 // id was not found in obj. Try obj's resolve hook, if any.
4510 if (obj->getClass()->resolve != JS_ResolveStub) {
4511 if (!cx->shouldBeJSContext() || !allowGC)
4512 return false;
4514 bool recursed;
4515 if (!CallResolveOp(cx->asJSContext(),
4516 MaybeRooted<JSObject*, allowGC>::toHandle(obj),
4517 MaybeRooted<jsid, allowGC>::toHandle(id),
4518 MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
4519 MaybeRooted<Shape*, allowGC>::toMutableHandle(propp),
4520 &recursed))
4522 return false;
4525 if (recursed) {
4526 objp.set(nullptr);
4527 propp.set(nullptr);
4528 *donep = true;
4529 return true;
4532 if (propp) {
4533 *donep = true;
4534 return true;
4538 *donep = false;
4539 return true;
4542 static bool
4543 NativeLookupOwnProperty(ExclusiveContext *cx, HandleObject obj, HandleId id,
4544 MutableHandle<Shape*> shapep)
4546 RootedObject pobj(cx);
4547 bool done;
4549 if (!LookupOwnPropertyInline<CanGC>(cx, obj, id, &pobj, shapep, &done))
4550 return false;
4551 if (!done || pobj != obj)
4552 shapep.set(nullptr);
4553 return true;
4556 template <AllowGC allowGC>
4557 static MOZ_ALWAYS_INLINE bool
4558 LookupPropertyInline(ExclusiveContext *cx,
4559 typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
4560 typename MaybeRooted<jsid, allowGC>::HandleType id,
4561 typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
4562 typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
4564 /* NB: The logic of this procedure is implicitly reflected in
4565 * BaselineIC.cpp's |EffectlesslyLookupProperty| logic.
4566 * If this changes, please remember to update the logic there as well.
4569 /* Search scopes starting with obj and following the prototype link. */
4570 typename MaybeRooted<JSObject*, allowGC>::RootType current(cx, obj);
4572 while (true) {
4573 bool done;
4574 if (!LookupOwnPropertyInline<allowGC>(cx, current, id, objp, propp, &done))
4575 return false;
4576 if (done)
4577 return true;
4579 typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->getProto());
4581 if (!proto)
4582 break;
4583 if (!proto->isNative()) {
4584 if (!cx->shouldBeJSContext() || !allowGC)
4585 return false;
4586 return JSObject::lookupGeneric(cx->asJSContext(),
4587 MaybeRooted<JSObject*, allowGC>::toHandle(proto),
4588 MaybeRooted<jsid, allowGC>::toHandle(id),
4589 MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
4590 MaybeRooted<Shape*, allowGC>::toMutableHandle(propp));
4593 current = proto;
4596 objp.set(nullptr);
4597 propp.set(nullptr);
4598 return true;
4601 template <AllowGC allowGC>
4602 bool
4603 baseops::LookupProperty(ExclusiveContext *cx,
4604 typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
4605 typename MaybeRooted<jsid, allowGC>::HandleType id,
4606 typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
4607 typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
4609 return LookupPropertyInline<allowGC>(cx, obj, id, objp, propp);
4612 template bool
4613 baseops::LookupProperty<CanGC>(ExclusiveContext *cx, HandleObject obj, HandleId id,
4614 MutableHandleObject objp, MutableHandleShape propp);
4616 template bool
4617 baseops::LookupProperty<NoGC>(ExclusiveContext *cx, JSObject *obj, jsid id,
4618 FakeMutableHandle<JSObject*> objp,
4619 FakeMutableHandle<Shape*> propp);
4621 /* static */ bool
4622 JSObject::lookupGeneric(JSContext *cx, HandleObject obj, js::HandleId id,
4623 MutableHandleObject objp, MutableHandleShape propp)
4625 /* NB: The logic of lookupGeneric is implicitly reflected in
4626 * BaselineIC.cpp's |EffectlesslyLookupProperty| logic.
4627 * If this changes, please remember to update the logic there as well.
4629 LookupGenericOp op = obj->getOps()->lookupGeneric;
4630 if (op)
4631 return op(cx, obj, id, objp, propp);
4632 return baseops::LookupProperty<js::CanGC>(cx, obj, id, objp, propp);
4635 bool
4636 baseops::LookupElement(JSContext *cx, HandleObject obj, uint32_t index,
4637 MutableHandleObject objp, MutableHandleShape propp)
4639 RootedId id(cx);
4640 if (!IndexToId(cx, index, &id))
4641 return false;
4643 return LookupPropertyInline<CanGC>(cx, obj, id, objp, propp);
4646 bool
4647 js::LookupNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id,
4648 MutableHandleObject objp, MutableHandleShape propp)
4650 return LookupPropertyInline<CanGC>(cx, obj, id, objp, propp);
4653 bool
4654 js::LookupName(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
4655 MutableHandleObject objp, MutableHandleObject pobjp, MutableHandleShape propp)
4657 RootedId id(cx, NameToId(name));
4659 for (RootedObject scope(cx, scopeChain); scope; scope = scope->enclosingScope()) {
4660 if (!JSObject::lookupGeneric(cx, scope, id, pobjp, propp))
4661 return false;
4662 if (propp) {
4663 objp.set(scope);
4664 return true;
4668 objp.set(nullptr);
4669 pobjp.set(nullptr);
4670 propp.set(nullptr);
4671 return true;
4674 bool
4675 js::LookupNameNoGC(JSContext *cx, PropertyName *name, JSObject *scopeChain,
4676 JSObject **objp, JSObject **pobjp, Shape **propp)
4678 AutoAssertNoException nogc(cx);
4680 JS_ASSERT(!*objp && !*pobjp && !*propp);
4682 for (JSObject *scope = scopeChain; scope; scope = scope->enclosingScope()) {
4683 if (scope->getOps()->lookupGeneric)
4684 return false;
4685 if (!LookupPropertyInline<NoGC>(cx, scope, NameToId(name), pobjp, propp))
4686 return false;
4687 if (*propp) {
4688 *objp = scope;
4689 return true;
4693 return true;
4696 bool
4697 js::LookupNameWithGlobalDefault(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
4698 MutableHandleObject objp)
4700 RootedId id(cx, NameToId(name));
4702 RootedObject pobj(cx);
4703 RootedShape prop(cx);
4705 RootedObject scope(cx, scopeChain);
4706 for (; !scope->is<GlobalObject>(); scope = scope->enclosingScope()) {
4707 if (!JSObject::lookupGeneric(cx, scope, id, &pobj, &prop))
4708 return false;
4709 if (prop)
4710 break;
4713 objp.set(scope);
4714 return true;
4717 bool
4718 js::LookupNameUnqualified(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
4719 MutableHandleObject objp)
4721 RootedId id(cx, NameToId(name));
4723 RootedObject pobj(cx);
4724 RootedShape prop(cx);
4726 RootedObject scope(cx, scopeChain);
4727 for (; !scope->isUnqualifiedVarObj(); scope = scope->enclosingScope()) {
4728 if (!JSObject::lookupGeneric(cx, scope, id, &pobj, &prop))
4729 return false;
4730 if (prop)
4731 break;
4734 objp.set(scope);
4735 return true;
4738 template <AllowGC allowGC>
4739 bool
4740 js::HasOwnProperty(JSContext *cx, LookupGenericOp lookup,
4741 typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
4742 typename MaybeRooted<jsid, allowGC>::HandleType id,
4743 typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
4744 typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
4746 if (lookup) {
4747 if (!allowGC)
4748 return false;
4749 if (!lookup(cx,
4750 MaybeRooted<JSObject*, allowGC>::toHandle(obj),
4751 MaybeRooted<jsid, allowGC>::toHandle(id),
4752 MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
4753 MaybeRooted<Shape*, allowGC>::toMutableHandle(propp)))
4755 return false;
4757 } else {
4758 bool done;
4759 if (!LookupOwnPropertyInline<allowGC>(cx, obj, id, objp, propp, &done))
4760 return false;
4761 if (!done) {
4762 objp.set(nullptr);
4763 propp.set(nullptr);
4764 return true;
4768 if (!propp)
4769 return true;
4771 if (objp == obj)
4772 return true;
4774 JSObject *outer = nullptr;
4775 if (js::ObjectOp op = objp->getClass()->ext.outerObject) {
4776 if (!allowGC)
4777 return false;
4778 RootedObject inner(cx, objp);
4779 outer = op(cx, inner);
4780 if (!outer)
4781 return false;
4784 if (outer != objp)
4785 propp.set(nullptr);
4786 return true;
4789 template bool
4790 js::HasOwnProperty<CanGC>(JSContext *cx, LookupGenericOp lookup,
4791 HandleObject obj, HandleId id,
4792 MutableHandleObject objp, MutableHandleShape propp);
4794 template bool
4795 js::HasOwnProperty<NoGC>(JSContext *cx, LookupGenericOp lookup,
4796 JSObject *obj, jsid id,
4797 FakeMutableHandle<JSObject*> objp, FakeMutableHandle<Shape*> propp);
4799 bool
4800 js::HasOwnProperty(JSContext *cx, HandleObject obj, HandleId id, bool *resultp)
4802 RootedObject pobj(cx);
4803 RootedShape shape(cx);
4804 if (!HasOwnProperty<CanGC>(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &shape))
4805 return false;
4806 *resultp = (shape != nullptr);
4807 return true;
4810 template <AllowGC allowGC>
4811 static MOZ_ALWAYS_INLINE bool
4812 NativeGetInline(JSContext *cx,
4813 typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
4814 typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
4815 typename MaybeRooted<JSObject*, allowGC>::HandleType pobj,
4816 typename MaybeRooted<Shape*, allowGC>::HandleType shape,
4817 typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
4819 JS_ASSERT(pobj->isNative());
4821 if (shape->hasSlot()) {
4822 vp.set(pobj->nativeGetSlot(shape->slot()));
4823 JS_ASSERT(!vp.isMagic());
4824 JS_ASSERT_IF(!pobj->hasSingletonType() &&
4825 !pobj->template is<ScopeObject>() &&
4826 shape->hasDefaultGetter(),
4827 js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), vp));
4828 } else {
4829 vp.setUndefined();
4831 if (shape->hasDefaultGetter())
4832 return true;
4835 jsbytecode *pc;
4836 JSScript *script = cx->currentScript(&pc);
4837 if (script && script->hasBaselineScript()) {
4838 switch (JSOp(*pc)) {
4839 case JSOP_GETPROP:
4840 case JSOP_CALLPROP:
4841 case JSOP_LENGTH:
4842 script->baselineScript()->noteAccessedGetter(script->pcToOffset(pc));
4843 break;
4844 default:
4845 break;
4850 if (!allowGC)
4851 return false;
4853 if (!shape->get(cx,
4854 MaybeRooted<JSObject*, allowGC>::toHandle(receiver),
4855 MaybeRooted<JSObject*, allowGC>::toHandle(obj),
4856 MaybeRooted<JSObject*, allowGC>::toHandle(pobj),
4857 MaybeRooted<Value, allowGC>::toMutableHandle(vp)))
4859 return false;
4862 /* Update slotful shapes according to the value produced by the getter. */
4863 if (shape->hasSlot() && pobj->nativeContains(cx, shape))
4864 pobj->nativeSetSlot(shape->slot(), vp);
4866 return true;
4869 bool
4870 js::NativeGet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> pobj, Handle<Shape*> shape,
4871 MutableHandle<Value> vp)
4873 return NativeGetInline<CanGC>(cx, obj, obj, pobj, shape, vp);
4876 template <ExecutionMode mode>
4877 bool
4878 js::NativeSet(typename ExecutionModeTraits<mode>::ContextType cxArg,
4879 Handle<JSObject*> obj, Handle<JSObject*> receiver,
4880 HandleShape shape, bool strict, MutableHandleValue vp)
4882 JS_ASSERT(cxArg->isThreadLocal(obj));
4883 JS_ASSERT(obj->isNative());
4885 if (shape->hasSlot()) {
4886 /* If shape has a stub setter, just store vp. */
4887 if (shape->hasDefaultSetter()) {
4888 if (mode == ParallelExecution) {
4889 if (!obj->nativeSetSlotIfHasType(shape, vp))
4890 return false;
4891 } else {
4892 obj->nativeSetSlotWithType(cxArg->asExclusiveContext(), shape, vp);
4895 return true;
4899 if (mode == ParallelExecution)
4900 return false;
4901 JSContext *cx = cxArg->asJSContext();
4903 if (!shape->hasSlot()) {
4905 * Allow API consumers to create shared properties with stub setters.
4906 * Such properties effectively function as data descriptors which are
4907 * not writable, so attempting to set such a property should do nothing
4908 * or throw if we're in strict mode.
4910 if (!shape->hasGetterValue() && shape->hasDefaultSetter())
4911 return js_ReportGetterOnlyAssignment(cx, strict);
4914 RootedValue ovp(cx, vp);
4916 uint32_t sample = cx->runtime()->propertyRemovals;
4917 if (!shape->set(cx, obj, receiver, strict, vp))
4918 return false;
4921 * Update any slot for the shape with the value produced by the setter,
4922 * unless the setter deleted the shape.
4924 if (shape->hasSlot() &&
4925 (MOZ_LIKELY(cx->runtime()->propertyRemovals == sample) ||
4926 obj->nativeContains(cx, shape)))
4928 obj->setSlot(shape->slot(), vp);
4931 return true;
4934 template bool
4935 js::NativeSet<SequentialExecution>(JSContext *cx,
4936 Handle<JSObject*> obj, Handle<JSObject*> receiver,
4937 HandleShape shape, bool strict, MutableHandleValue vp);
4938 template bool
4939 js::NativeSet<ParallelExecution>(ForkJoinContext *cx,
4940 Handle<JSObject*> obj, Handle<JSObject*> receiver,
4941 HandleShape shape, bool strict, MutableHandleValue vp);
4943 template <AllowGC allowGC>
4944 static MOZ_ALWAYS_INLINE bool
4945 GetPropertyHelperInline(JSContext *cx,
4946 typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
4947 typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
4948 typename MaybeRooted<jsid, allowGC>::HandleType id,
4949 typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
4951 /* This call site is hot -- use the always-inlined variant of LookupNativeProperty(). */
4952 typename MaybeRooted<JSObject*, allowGC>::RootType obj2(cx);
4953 typename MaybeRooted<Shape*, allowGC>::RootType shape(cx);
4954 if (!LookupPropertyInline<allowGC>(cx, obj, id, &obj2, &shape))
4955 return false;
4957 if (!shape) {
4958 if (!allowGC)
4959 return false;
4961 vp.setUndefined();
4963 if (!CallJSPropertyOp(cx, obj->getClass()->getProperty,
4964 MaybeRooted<JSObject*, allowGC>::toHandle(obj),
4965 MaybeRooted<jsid, allowGC>::toHandle(id),
4966 MaybeRooted<Value, allowGC>::toMutableHandle(vp)))
4968 return false;
4972 * Give a strict warning if foo.bar is evaluated by a script for an
4973 * object foo with no property named 'bar'.
4975 if (vp.isUndefined()) {
4976 jsbytecode *pc = nullptr;
4977 RootedScript script(cx, cx->currentScript(&pc));
4978 if (!pc)
4979 return true;
4980 JSOp op = (JSOp) *pc;
4982 if (op == JSOP_GETXPROP) {
4983 /* Undefined property during a name lookup, report an error. */
4984 JSAutoByteString printable;
4985 if (js_ValueToPrintable(cx, IdToValue(id), &printable))
4986 js_ReportIsNotDefined(cx, printable.ptr());
4987 return false;
4990 /* Don't warn if extra warnings not enabled or for random getprop operations. */
4991 if (!cx->compartment()->options().extraWarnings(cx) || (op != JSOP_GETPROP && op != JSOP_GETELEM))
4992 return true;
4994 /* Don't warn repeatedly for the same script. */
4995 if (!script || script->warnedAboutUndefinedProp())
4996 return true;
4999 * Don't warn in self-hosted code (where the further presence of
5000 * JS::RuntimeOptions::werror() would result in impossible-to-avoid
5001 * errors to entirely-innocent client code).
5003 if (script->selfHosted())
5004 return true;
5006 /* We may just be checking if that object has an iterator. */
5007 if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic))
5008 return true;
5010 /* Do not warn about tests like (obj[prop] == undefined). */
5011 pc += js_CodeSpec[op].length;
5012 if (Detecting(cx, script, pc))
5013 return true;
5015 unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT;
5016 script->setWarnedAboutUndefinedProp();
5018 /* Ok, bad undefined property reference: whine about it. */
5019 RootedValue val(cx, IdToValue(id));
5020 if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP,
5021 JSDVG_IGNORE_STACK, val, js::NullPtr(),
5022 nullptr, nullptr))
5024 return false;
5027 return true;
5030 if (!obj2->isNative()) {
5031 if (!allowGC)
5032 return false;
5033 HandleObject obj2Handle = MaybeRooted<JSObject*, allowGC>::toHandle(obj2);
5034 HandleObject receiverHandle = MaybeRooted<JSObject*, allowGC>::toHandle(receiver);
5035 HandleId idHandle = MaybeRooted<jsid, allowGC>::toHandle(id);
5036 MutableHandleValue vpHandle = MaybeRooted<Value, allowGC>::toMutableHandle(vp);
5037 return obj2->template is<ProxyObject>()
5038 ? Proxy::get(cx, obj2Handle, receiverHandle, idHandle, vpHandle)
5039 : JSObject::getGeneric(cx, obj2Handle, obj2Handle, idHandle, vpHandle);
5042 if (IsImplicitDenseOrTypedArrayElement(shape)) {
5043 vp.set(obj2->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
5044 return true;
5047 /* This call site is hot -- use the always-inlined variant of NativeGet(). */
5048 if (!NativeGetInline<allowGC>(cx, obj, receiver, obj2, shape, vp))
5049 return false;
5051 return true;
5054 bool
5055 baseops::GetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, MutableHandleValue vp)
5057 /* This call site is hot -- use the always-inlined variant of GetPropertyHelper(). */
5058 return GetPropertyHelperInline<CanGC>(cx, obj, receiver, id, vp);
5061 bool
5062 baseops::GetPropertyNoGC(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
5064 AutoAssertNoException nogc(cx);
5065 return GetPropertyHelperInline<NoGC>(cx, obj, receiver, id, vp);
5068 static MOZ_ALWAYS_INLINE bool
5069 LookupPropertyPureInline(JSObject *obj, jsid id, JSObject **objp, Shape **propp)
5071 if (!obj->isNative())
5072 return false;
5074 JSObject *current = obj;
5075 while (true) {
5076 /* Search for a native dense element, typed array element, or property. */
5078 if (JSID_IS_INT(id) && current->containsDenseElement(JSID_TO_INT(id))) {
5079 *objp = current;
5080 MarkDenseOrTypedArrayElementFound<NoGC>(propp);
5081 return true;
5084 if (current->is<TypedArrayObject>()) {
5085 uint64_t index;
5086 if (IsTypedArrayIndex(id, &index)) {
5087 if (index < obj->as<TypedArrayObject>().length()) {
5088 *objp = current;
5089 MarkDenseOrTypedArrayElementFound<NoGC>(propp);
5090 } else {
5091 *objp = nullptr;
5092 *propp = nullptr;
5094 return true;
5098 if (Shape *shape = current->nativeLookupPure(id)) {
5099 *objp = current;
5100 *propp = shape;
5101 return true;
5104 /* Fail if there's a resolve hook. */
5105 if (current->getClass()->resolve != JS_ResolveStub)
5106 return false;
5108 JSObject *proto = current->getProto();
5110 if (!proto)
5111 break;
5112 if (!proto->isNative())
5113 return false;
5115 current = proto;
5118 *objp = nullptr;
5119 *propp = nullptr;
5120 return true;
5123 static MOZ_ALWAYS_INLINE bool
5124 NativeGetPureInline(JSObject *pobj, Shape *shape, Value *vp)
5126 JS_ASSERT(pobj->isNative());
5128 if (shape->hasSlot()) {
5129 *vp = pobj->nativeGetSlot(shape->slot());
5130 JS_ASSERT(!vp->isMagic());
5131 } else {
5132 vp->setUndefined();
5135 /* Fail if we have a custom getter. */
5136 return shape->hasDefaultGetter();
5139 bool
5140 js::LookupPropertyPure(JSObject *obj, jsid id, JSObject **objp, Shape **propp)
5142 return LookupPropertyPureInline(obj, id, objp, propp);
5146 * A pure version of GetPropertyHelper that can be called from parallel code
5147 * without locking. This code path cannot GC. This variant returns false
5148 * whenever a side-effect might have occured in the effectful version. This
5149 * includes, but is not limited to:
5151 * - Any object in the lookup chain has a non-stub resolve hook.
5152 * - Any object in the lookup chain is non-native.
5153 * - The property has a getter.
5155 bool
5156 js::GetPropertyPure(ThreadSafeContext *cx, JSObject *obj, jsid id, Value *vp)
5158 /* Deal with native objects. */
5159 JSObject *obj2;
5160 Shape *shape;
5161 if (!LookupPropertyPureInline(obj, id, &obj2, &shape))
5162 return false;
5164 if (!shape) {
5165 /* Fail if we have a non-stub class op hooks. */
5166 if (obj->getClass()->getProperty && obj->getClass()->getProperty != JS_PropertyStub)
5167 return false;
5169 if (obj->getOps()->getElement)
5170 return false;
5172 /* Vanilla native object, return undefined. */
5173 vp->setUndefined();
5174 return true;
5177 if (IsImplicitDenseOrTypedArrayElement(shape)) {
5178 *vp = obj2->getDenseOrTypedArrayElement(JSID_TO_INT(id));
5179 return true;
5182 /* Special case 'length' on Array and TypedArray. */
5183 if (JSID_IS_ATOM(id, cx->names().length)) {
5184 if (obj->is<ArrayObject>()) {
5185 vp->setNumber(obj->as<ArrayObject>().length());
5186 return true;
5189 if (obj->is<TypedArrayObject>()) {
5190 vp->setNumber(obj->as<TypedArrayObject>().length());
5191 return true;
5195 return NativeGetPureInline(obj2, shape, vp);
5198 static bool
5199 MOZ_ALWAYS_INLINE
5200 GetElementPure(ThreadSafeContext *cx, JSObject *obj, uint32_t index, Value *vp)
5202 if (index <= JSID_INT_MAX)
5203 return GetPropertyPure(cx, obj, INT_TO_JSID(index), vp);
5204 return false;
5208 * A pure version of GetObjectElementOperation that can be called from
5209 * parallel code without locking. This variant returns false whenever a
5210 * side-effect might have occurred.
5212 bool
5213 js::GetObjectElementOperationPure(ThreadSafeContext *cx, JSObject *obj, const Value &prop,
5214 Value *vp)
5216 uint32_t index;
5217 if (IsDefinitelyIndex(prop, &index))
5218 return GetElementPure(cx, obj, index, vp);
5220 /* Atomizing the property value is effectful and not threadsafe. */
5221 if (!prop.isString() || !prop.toString()->isAtom())
5222 return false;
5224 JSAtom *name = &prop.toString()->asAtom();
5225 if (name->isIndex(&index))
5226 return GetElementPure(cx, obj, index, vp);
5228 return GetPropertyPure(cx, obj, NameToId(name->asPropertyName()), vp);
5231 bool
5232 baseops::GetElement(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
5233 MutableHandleValue vp)
5235 RootedId id(cx);
5236 if (!IndexToId(cx, index, &id))
5237 return false;
5239 /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
5240 return GetPropertyHelperInline<CanGC>(cx, obj, receiver, id, vp);
5243 static bool
5244 MaybeReportUndeclaredVarAssignment(JSContext *cx, JSString *propname)
5247 JSScript *script = cx->currentScript(nullptr, JSContext::ALLOW_CROSS_COMPARTMENT);
5248 if (!script)
5249 return true;
5251 // If the code is not strict and extra warnings aren't enabled, then no
5252 // check is needed.
5253 if (!script->strict() && !cx->compartment()->options().extraWarnings(cx))
5254 return true;
5257 JSAutoByteString bytes(cx, propname);
5258 return !!bytes &&
5259 JS_ReportErrorFlagsAndNumber(cx,
5260 (JSREPORT_WARNING | JSREPORT_STRICT
5261 | JSREPORT_STRICT_MODE_ERROR),
5262 js_GetErrorMessage, nullptr,
5263 JSMSG_UNDECLARED_VAR, bytes.ptr());
5266 bool
5267 JSObject::reportReadOnly(ThreadSafeContext *cxArg, jsid id, unsigned report)
5269 if (cxArg->isForkJoinContext())
5270 return cxArg->asForkJoinContext()->reportError(report);
5272 if (!cxArg->isJSContext())
5273 return true;
5275 JSContext *cx = cxArg->asJSContext();
5276 RootedValue val(cx, IdToValue(id));
5277 return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
5278 JSDVG_IGNORE_STACK, val, js::NullPtr(),
5279 nullptr, nullptr);
5282 bool
5283 JSObject::reportNotConfigurable(ThreadSafeContext *cxArg, jsid id, unsigned report)
5285 if (cxArg->isForkJoinContext())
5286 return cxArg->asForkJoinContext()->reportError(report);
5288 if (!cxArg->isJSContext())
5289 return true;
5291 JSContext *cx = cxArg->asJSContext();
5292 RootedValue val(cx, IdToValue(id));
5293 return js_ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE,
5294 JSDVG_IGNORE_STACK, val, js::NullPtr(),
5295 nullptr, nullptr);
5298 bool
5299 JSObject::reportNotExtensible(ThreadSafeContext *cxArg, unsigned report)
5301 if (cxArg->isForkJoinContext())
5302 return cxArg->asForkJoinContext()->reportError(report);
5304 if (!cxArg->isJSContext())
5305 return true;
5307 JSContext *cx = cxArg->asJSContext();
5308 RootedValue val(cx, ObjectValue(*this));
5309 return js_ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE,
5310 JSDVG_IGNORE_STACK, val, js::NullPtr(),
5311 nullptr, nullptr);
5314 bool
5315 JSObject::callMethod(JSContext *cx, HandleId id, unsigned argc, Value *argv, MutableHandleValue vp)
5317 RootedValue fval(cx);
5318 RootedObject obj(cx, this);
5319 if (!JSObject::getGeneric(cx, obj, obj, id, &fval))
5320 return false;
5321 return Invoke(cx, ObjectValue(*obj), fval, argc, argv, vp);
5324 template <ExecutionMode mode>
5325 bool
5326 baseops::SetPropertyHelper(typename ExecutionModeTraits<mode>::ContextType cxArg,
5327 HandleObject obj, HandleObject receiver, HandleId id,
5328 QualifiedBool qualified, MutableHandleValue vp, bool strict)
5330 JS_ASSERT(cxArg->isThreadLocal(obj));
5332 if (MOZ_UNLIKELY(obj->watched())) {
5333 if (mode == ParallelExecution)
5334 return false;
5336 /* Fire watchpoints, if any. */
5337 JSContext *cx = cxArg->asJSContext();
5338 WatchpointMap *wpmap = cx->compartment()->watchpointMap;
5339 if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
5340 return false;
5343 RootedObject pobj(cxArg);
5344 RootedShape shape(cxArg);
5345 if (mode == ParallelExecution) {
5346 if (!LookupPropertyPure(obj, id, pobj.address(), shape.address()))
5347 return false;
5348 } else {
5349 JSContext *cx = cxArg->asJSContext();
5350 if (!LookupNativeProperty(cx, obj, id, &pobj, &shape))
5351 return false;
5353 if (shape) {
5354 if (!pobj->isNative()) {
5355 if (pobj->is<ProxyObject>()) {
5356 if (mode == ParallelExecution)
5357 return false;
5359 JSContext *cx = cxArg->asJSContext();
5360 Rooted<PropertyDescriptor> pd(cx);
5361 if (!Proxy::getPropertyDescriptor(cx, pobj, id, &pd))
5362 return false;
5364 if ((pd.attributes() & (JSPROP_SHARED | JSPROP_SHADOWABLE)) == JSPROP_SHARED) {
5365 return !pd.setter() ||
5366 CallSetter(cx, receiver, id, pd.setter(), pd.attributes(), strict, vp);
5369 if (pd.isReadonly()) {
5370 if (strict)
5371 return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
5372 if (cx->compartment()->options().extraWarnings(cx))
5373 return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
5374 return true;
5378 shape = nullptr;
5380 } else {
5381 /* We should never add properties to lexical blocks. */
5382 JS_ASSERT(!obj->is<BlockObject>());
5384 if (obj->isUnqualifiedVarObj() && !qualified) {
5385 if (mode == ParallelExecution)
5386 return false;
5388 if (!MaybeReportUndeclaredVarAssignment(cxArg->asJSContext(), JSID_TO_STRING(id)))
5389 return false;
5394 * Now either shape is null, meaning id was not found in obj or one of its
5395 * prototypes; or shape is non-null, meaning id was found directly in pobj.
5397 unsigned attrs = JSPROP_ENUMERATE;
5398 const Class *clasp = obj->getClass();
5399 PropertyOp getter = clasp->getProperty;
5400 StrictPropertyOp setter = clasp->setProperty;
5402 if (IsImplicitDenseOrTypedArrayElement(shape)) {
5403 /* ES5 8.12.4 [[Put]] step 2, for a dense data property on pobj. */
5404 if (pobj != obj)
5405 shape = nullptr;
5406 } else if (shape) {
5407 /* ES5 8.12.4 [[Put]] step 2. */
5408 if (shape->isAccessorDescriptor()) {
5409 if (shape->hasDefaultSetter()) {
5410 /* Bail out of parallel execution if we are strict to throw. */
5411 if (mode == ParallelExecution)
5412 return !strict;
5414 return js_ReportGetterOnlyAssignment(cxArg->asJSContext(), strict);
5416 } else {
5417 JS_ASSERT(shape->isDataDescriptor());
5419 if (!shape->writable()) {
5421 * Error in strict mode code, warn with extra warnings
5422 * options, otherwise do nothing.
5424 * Bail out of parallel execution if we are strict to throw.
5426 if (mode == ParallelExecution)
5427 return !strict;
5429 JSContext *cx = cxArg->asJSContext();
5430 if (strict)
5431 return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
5432 if (cx->compartment()->options().extraWarnings(cx))
5433 return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
5434 return true;
5438 attrs = shape->attributes();
5439 if (pobj != obj) {
5441 * We found id in a prototype object: prepare to share or shadow.
5443 if (!shape->shadowable()) {
5444 if (shape->hasDefaultSetter() && !shape->hasGetterValue())
5445 return true;
5447 if (mode == ParallelExecution)
5448 return false;
5450 return shape->set(cxArg->asJSContext(), obj, receiver, strict, vp);
5454 * Preserve attrs except JSPROP_SHARED, getter, and setter when
5455 * shadowing any property that has no slot (is shared). We must
5456 * clear the shared attribute for the shadowing shape so that the
5457 * property in obj that it defines has a slot to retain the value
5458 * being set, in case the setter simply cannot operate on instances
5459 * of obj's class by storing the value in some class-specific
5460 * location.
5462 if (!shape->hasSlot()) {
5463 attrs &= ~JSPROP_SHARED;
5464 getter = shape->getter();
5465 setter = shape->setter();
5466 } else {
5467 /* Restore attrs to the ECMA default for new properties. */
5468 attrs = JSPROP_ENUMERATE;
5472 * Forget we found the proto-property now that we've copied any
5473 * needed member values.
5475 shape = nullptr;
5479 if (IsImplicitDenseOrTypedArrayElement(shape)) {
5480 uint32_t index = JSID_TO_INT(id);
5482 if (obj->is<TypedArrayObject>()) {
5483 double d;
5484 if (mode == ParallelExecution) {
5485 // Bail if converting the value might invoke user-defined
5486 // conversions.
5487 if (vp.isObject())
5488 return false;
5489 if (!NonObjectToNumber(cxArg, vp, &d))
5490 return false;
5491 } else {
5492 if (!ToNumber(cxArg->asJSContext(), vp, &d))
5493 return false;
5496 // Silently do nothing for out-of-bounds sets, for consistency with
5497 // current behavior. (ES6 currently says to throw for this in
5498 // strict mode code, so we may eventually need to change.)
5499 TypedArrayObject &tarray = obj->as<TypedArrayObject>();
5500 if (index < tarray.length())
5501 TypedArrayObject::setElement(tarray, index, d);
5502 return true;
5505 bool definesPast;
5506 if (!WouldDefinePastNonwritableLength(cxArg, obj, index, strict, &definesPast))
5507 return false;
5508 if (definesPast) {
5509 /* Bail out of parallel execution if we are strict to throw. */
5510 if (mode == ParallelExecution)
5511 return !strict;
5512 return true;
5515 if (!obj->maybeCopyElementsForWrite(cxArg))
5516 return false;
5518 if (mode == ParallelExecution)
5519 return obj->setDenseElementIfHasType(index, vp);
5521 obj->setDenseElementWithType(cxArg->asJSContext(), index, vp);
5522 return true;
5525 if (obj->is<ArrayObject>() && id == NameToId(cxArg->names().length)) {
5526 Rooted<ArrayObject*> arr(cxArg, &obj->as<ArrayObject>());
5527 return ArraySetLength<mode>(cxArg, arr, id, attrs, vp, strict);
5530 if (!shape) {
5531 bool extensible;
5532 if (mode == ParallelExecution) {
5533 if (obj->is<ProxyObject>())
5534 return false;
5535 extensible = obj->nonProxyIsExtensible();
5536 } else {
5537 if (!JSObject::isExtensible(cxArg->asJSContext(), obj, &extensible))
5538 return false;
5541 if (!extensible) {
5542 /* Error in strict mode code, warn with extra warnings option, otherwise do nothing. */
5543 if (strict)
5544 return obj->reportNotExtensible(cxArg);
5545 if (mode == SequentialExecution &&
5546 cxArg->asJSContext()->compartment()->options().extraWarnings(cxArg->asJSContext()))
5548 return obj->reportNotExtensible(cxArg, JSREPORT_STRICT | JSREPORT_WARNING);
5550 return true;
5553 if (mode == ParallelExecution) {
5554 if (obj->isDelegate())
5555 return false;
5557 if (getter != JS_PropertyStub || !HasTypePropertyId(obj, id, vp))
5558 return false;
5559 } else {
5560 JSContext *cx = cxArg->asJSContext();
5562 /* Purge the property cache of now-shadowed id in obj's scope chain. */
5563 if (!PurgeScopeChain(cx, obj, id))
5564 return false;
5567 return DefinePropertyOrElement<mode>(cxArg, obj, id, getter, setter,
5568 attrs, vp, true, strict);
5571 return NativeSet<mode>(cxArg, obj, receiver, shape, strict, vp);
5574 template bool
5575 baseops::SetPropertyHelper<SequentialExecution>(JSContext *cx, HandleObject obj,
5576 HandleObject receiver, HandleId id,
5577 QualifiedBool qualified,
5578 MutableHandleValue vp, bool strict);
5579 template bool
5580 baseops::SetPropertyHelper<ParallelExecution>(ForkJoinContext *cx, HandleObject obj,
5581 HandleObject receiver, HandleId id,
5582 QualifiedBool qualified,
5583 MutableHandleValue vp, bool strict);
5585 bool
5586 baseops::SetElementHelper(JSContext *cx, HandleObject obj, HandleObject receiver, uint32_t index,
5587 MutableHandleValue vp, bool strict)
5589 RootedId id(cx);
5590 if (!IndexToId(cx, index, &id))
5591 return false;
5592 return baseops::SetPropertyHelper<SequentialExecution>(cx, obj, receiver, id, Qualified, vp,
5593 strict);
5596 bool
5597 baseops::GetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
5599 RootedObject nobj(cx);
5600 RootedShape shape(cx);
5601 if (!baseops::LookupProperty<CanGC>(cx, obj, id, &nobj, &shape))
5602 return false;
5603 if (!shape) {
5604 *attrsp = 0;
5605 return true;
5607 if (!nobj->isNative())
5608 return JSObject::getGenericAttributes(cx, nobj, id, attrsp);
5610 *attrsp = GetShapeAttributes(nobj, shape);
5611 return true;
5614 bool
5615 baseops::SetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *attrsp)
5617 RootedObject nobj(cx);
5618 RootedShape shape(cx);
5619 if (!baseops::LookupProperty<CanGC>(cx, obj, id, &nobj, &shape))
5620 return false;
5621 if (!shape)
5622 return true;
5623 if (nobj->isNative() && IsImplicitDenseOrTypedArrayElement(shape)) {
5624 if (nobj->is<TypedArrayObject>()) {
5625 if (*attrsp == (JSPROP_ENUMERATE | JSPROP_PERMANENT))
5626 return true;
5627 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_ARRAY_ATTRS);
5628 return false;
5630 if (!JSObject::sparsifyDenseElement(cx, nobj, JSID_TO_INT(id)))
5631 return false;
5632 shape = obj->nativeLookup(cx, id);
5634 if (nobj->isNative()) {
5635 if (!JSObject::changePropertyAttributes(cx, nobj, shape, *attrsp))
5636 return false;
5637 if (*attrsp & JSPROP_READONLY)
5638 MarkTypePropertyNonWritable(cx, obj, id);
5639 return true;
5640 } else {
5641 return JSObject::setGenericAttributes(cx, nobj, id, attrsp);
5645 bool
5646 baseops::DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
5648 RootedObject proto(cx);
5649 RootedShape shape(cx);
5650 if (!baseops::LookupProperty<CanGC>(cx, obj, id, &proto, &shape))
5651 return false;
5652 if (!shape || proto != obj) {
5654 * If no property, or the property comes from a prototype, call the
5655 * class's delProperty hook, passing succeeded as the result parameter.
5657 return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded);
5660 cx->runtime()->gc.poke();
5662 if (IsImplicitDenseOrTypedArrayElement(shape)) {
5663 if (obj->is<TypedArrayObject>()) {
5664 // Don't delete elements from typed arrays.
5665 *succeeded = false;
5666 return true;
5669 if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded))
5670 return false;
5671 if (!succeeded)
5672 return true;
5674 if (!obj->maybeCopyElementsForWrite(cx))
5675 return false;
5677 obj->setDenseElementHole(cx, JSID_TO_INT(id));
5678 return js_SuppressDeletedProperty(cx, obj, id);
5681 if (!shape->configurable()) {
5682 *succeeded = false;
5683 return true;
5686 RootedId propid(cx, shape->propid());
5687 if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, propid, succeeded))
5688 return false;
5689 if (!succeeded)
5690 return true;
5692 return obj->removeProperty(cx, id) && js_SuppressDeletedProperty(cx, obj, id);
5695 bool
5696 js::WatchGuts(JSContext *cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable)
5698 RootedObject obj(cx, GetInnerObject(origObj));
5699 if (obj->isNative()) {
5700 // Use sparse indexes for watched objects, as dense elements can be
5701 // written to without checking the watchpoint map.
5702 if (!JSObject::sparsifyDenseElements(cx, obj))
5703 return false;
5705 types::MarkTypePropertyNonData(cx, obj, id);
5708 WatchpointMap *wpmap = cx->compartment()->watchpointMap;
5709 if (!wpmap) {
5710 wpmap = cx->runtime()->new_<WatchpointMap>();
5711 if (!wpmap || !wpmap->init()) {
5712 js_ReportOutOfMemory(cx);
5713 return false;
5715 cx->compartment()->watchpointMap = wpmap;
5718 return wpmap->watch(cx, obj, id, js::WatchHandler, callable);
5721 bool
5722 baseops::Watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable)
5724 if (!obj->isNative() || obj->is<TypedArrayObject>()) {
5725 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
5726 obj->getClass()->name);
5727 return false;
5730 return WatchGuts(cx, obj, id, callable);
5733 bool
5734 js::UnwatchGuts(JSContext *cx, JS::HandleObject origObj, JS::HandleId id)
5736 // Looking in the map for an unsupported object will never hit, so we don't
5737 // need to check for nativeness or watchable-ness here.
5738 RootedObject obj(cx, GetInnerObject(origObj));
5739 if (WatchpointMap *wpmap = cx->compartment()->watchpointMap)
5740 wpmap->unwatch(obj, id, nullptr, nullptr);
5741 return true;
5744 bool
5745 baseops::Unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id)
5747 return UnwatchGuts(cx, obj, id);
5750 bool
5751 js::HasDataProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
5753 if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
5754 *vp = obj->getDenseElement(JSID_TO_INT(id));
5755 return true;
5758 if (Shape *shape = obj->nativeLookup(cx, id)) {
5759 if (shape->hasDefaultGetter() && shape->hasSlot()) {
5760 *vp = obj->nativeGetSlot(shape->slot());
5761 return true;
5765 return false;
5769 * Gets |obj[id]|. If that value's not callable, returns true and stores a
5770 * non-primitive value in *vp. If it's callable, calls it with no arguments
5771 * and |obj| as |this|, returning the result in *vp.
5773 * This is a mini-abstraction for ES5 8.12.8 [[DefaultValue]], either steps 1-2
5774 * or steps 3-4.
5776 static bool
5777 MaybeCallMethod(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
5779 if (!JSObject::getGeneric(cx, obj, obj, id, vp))
5780 return false;
5781 if (!IsCallable(vp)) {
5782 vp.setObject(*obj);
5783 return true;
5785 return Invoke(cx, ObjectValue(*obj), vp, 0, nullptr, vp);
5788 JS_FRIEND_API(bool)
5789 js::DefaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp)
5791 JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
5793 Rooted<jsid> id(cx);
5795 const Class *clasp = obj->getClass();
5796 if (hint == JSTYPE_STRING) {
5797 id = NameToId(cx->names().toString);
5799 /* Optimize (new String(...)).toString(). */
5800 if (clasp == &StringObject::class_) {
5801 if (ClassMethodIsNative(cx, obj, &StringObject::class_, id, js_str_toString)) {
5802 vp.setString(obj->as<StringObject>().unbox());
5803 return true;
5807 if (!MaybeCallMethod(cx, obj, id, vp))
5808 return false;
5809 if (vp.isPrimitive())
5810 return true;
5812 id = NameToId(cx->names().valueOf);
5813 if (!MaybeCallMethod(cx, obj, id, vp))
5814 return false;
5815 if (vp.isPrimitive())
5816 return true;
5817 } else {
5819 /* Optimize new String(...).valueOf(). */
5820 if (clasp == &StringObject::class_) {
5821 id = NameToId(cx->names().valueOf);
5822 if (ClassMethodIsNative(cx, obj, &StringObject::class_, id, js_str_toString)) {
5823 vp.setString(obj->as<StringObject>().unbox());
5824 return true;
5828 /* Optimize new Number(...).valueOf(). */
5829 if (clasp == &NumberObject::class_) {
5830 id = NameToId(cx->names().valueOf);
5831 if (ClassMethodIsNative(cx, obj, &NumberObject::class_, id, js_num_valueOf)) {
5832 vp.setNumber(obj->as<NumberObject>().unbox());
5833 return true;
5837 id = NameToId(cx->names().valueOf);
5838 if (!MaybeCallMethod(cx, obj, id, vp))
5839 return false;
5840 if (vp.isPrimitive())
5841 return true;
5843 id = NameToId(cx->names().toString);
5844 if (!MaybeCallMethod(cx, obj, id, vp))
5845 return false;
5846 if (vp.isPrimitive())
5847 return true;
5850 /* Avoid recursive death when decompiling in js_ReportValueError. */
5851 RootedString str(cx);
5852 if (hint == JSTYPE_STRING) {
5853 str = JS_InternString(cx, clasp->name);
5854 if (!str)
5855 return false;
5856 } else {
5857 str = nullptr;
5860 RootedValue val(cx, ObjectValue(*obj));
5861 js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, JSDVG_SEARCH_STACK, val, str,
5862 hint == JSTYPE_VOID
5863 ? "primitive type"
5864 : hint == JSTYPE_STRING ? "string" : "number");
5865 return false;
5868 JS_FRIEND_API(bool)
5869 JS_EnumerateState(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
5870 MutableHandleValue statep, JS::MutableHandleId idp)
5872 /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
5873 const Class *clasp = obj->getClass();
5874 JSEnumerateOp enumerate = clasp->enumerate;
5875 if (clasp->flags & JSCLASS_NEW_ENUMERATE) {
5876 JS_ASSERT(enumerate != JS_EnumerateStub);
5877 return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
5880 if (!enumerate(cx, obj))
5881 return false;
5883 /* Tell InitNativeIterator to treat us like a native object. */
5884 JS_ASSERT(enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL);
5885 statep.setMagic(JS_NATIVE_ENUMERATE);
5886 return true;
5889 bool
5890 js::IsDelegate(JSContext *cx, HandleObject obj, const js::Value &v, bool *result)
5892 if (v.isPrimitive()) {
5893 *result = false;
5894 return true;
5896 return IsDelegateOfObject(cx, obj, &v.toObject(), result);
5899 bool
5900 js::IsDelegateOfObject(JSContext *cx, HandleObject protoObj, JSObject* obj, bool *result)
5902 RootedObject obj2(cx, obj);
5903 for (;;) {
5904 if (!JSObject::getProto(cx, obj2, &obj2))
5905 return false;
5906 if (!obj2) {
5907 *result = false;
5908 return true;
5910 if (obj2 == protoObj) {
5911 *result = true;
5912 return true;
5917 JSObject *
5918 js::GetBuiltinPrototypePure(GlobalObject *global, JSProtoKey protoKey)
5920 JS_ASSERT(JSProto_Null <= protoKey);
5921 JS_ASSERT(protoKey < JSProto_LIMIT);
5923 if (protoKey != JSProto_Null) {
5924 const Value &v = global->getPrototype(protoKey);
5925 if (v.isObject())
5926 return &v.toObject();
5929 return nullptr;
5933 * The first part of this function has been hand-expanded and optimized into
5934 * NewBuiltinClassInstance in jsobjinlines.h.
5936 bool
5937 js::FindClassPrototype(ExclusiveContext *cx, MutableHandleObject protop, const Class *clasp)
5939 protop.set(nullptr);
5940 JSProtoKey protoKey = ClassProtoKeyOrAnonymousOrNull(clasp);
5941 if (protoKey != JSProto_Null)
5942 return GetBuiltinPrototype(cx, protoKey, protop);
5944 RootedObject ctor(cx);
5945 if (!FindClassObject(cx, &ctor, clasp))
5946 return false;
5948 if (ctor && ctor->is<JSFunction>()) {
5949 RootedValue v(cx);
5950 if (cx->isJSContext()) {
5951 if (!JSObject::getProperty(cx->asJSContext(),
5952 ctor, ctor, cx->names().prototype, &v))
5954 return false;
5956 } else {
5957 Shape *shape = ctor->nativeLookup(cx, cx->names().prototype);
5958 if (!shape || !NativeGetPureInline(ctor, shape, v.address()))
5959 return false;
5961 if (v.isObject())
5962 protop.set(&v.toObject());
5964 return true;
5967 JSObject *
5968 js::PrimitiveToObject(JSContext *cx, const Value &v)
5970 if (v.isString()) {
5971 Rooted<JSString*> str(cx, v.toString());
5972 return StringObject::create(cx, str);
5974 if (v.isNumber())
5975 return NumberObject::create(cx, v.toNumber());
5976 if (v.isBoolean())
5977 return BooleanObject::create(cx, v.toBoolean());
5978 JS_ASSERT(v.isSymbol());
5979 RootedSymbol symbol(cx, v.toSymbol());
5980 return SymbolObject::create(cx, symbol);
5983 /* Callers must handle the already-object case. */
5984 JSObject *
5985 js::ToObjectSlow(JSContext *cx, HandleValue val, bool reportScanStack)
5987 JS_ASSERT(!val.isMagic());
5988 JS_ASSERT(!val.isObject());
5990 if (val.isNullOrUndefined()) {
5991 if (reportScanStack) {
5992 js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, val, NullPtr());
5993 } else {
5994 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
5995 val.isNull() ? "null" : "undefined", "object");
5997 return nullptr;
6000 return PrimitiveToObject(cx, val);
6003 void
6004 js_GetObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
6006 JS_ASSERT(trc->debugPrinter() == js_GetObjectSlotName);
6008 JSObject *obj = (JSObject *)trc->debugPrintArg();
6009 uint32_t slot = uint32_t(trc->debugPrintIndex());
6011 Shape *shape;
6012 if (obj->isNative()) {
6013 shape = obj->lastProperty();
6014 while (shape && (!shape->hasSlot() || shape->slot() != slot))
6015 shape = shape->previous();
6016 } else {
6017 shape = nullptr;
6020 if (!shape) {
6021 do {
6022 const char *slotname = nullptr;
6023 const char *pattern = nullptr;
6024 if (obj->is<GlobalObject>()) {
6025 pattern = "CLASS_OBJECT(%s)";
6026 if (false)
6028 #define TEST_SLOT_MATCHES_PROTOTYPE(name,code,init,clasp) \
6029 else if ((code) == slot) { slotname = js_##name##_str; }
6030 JS_FOR_EACH_PROTOTYPE(TEST_SLOT_MATCHES_PROTOTYPE)
6031 #undef TEST_SLOT_MATCHES_PROTOTYPE
6032 } else {
6033 pattern = "%s";
6034 if (obj->is<ScopeObject>()) {
6035 if (slot == ScopeObject::enclosingScopeSlot()) {
6036 slotname = "enclosing_environment";
6037 } else if (obj->is<CallObject>()) {
6038 if (slot == CallObject::calleeSlot())
6039 slotname = "callee_slot";
6040 } else if (obj->is<DeclEnvObject>()) {
6041 if (slot == DeclEnvObject::lambdaSlot())
6042 slotname = "named_lambda";
6043 } else if (obj->is<DynamicWithObject>()) {
6044 if (slot == DynamicWithObject::objectSlot())
6045 slotname = "with_object";
6046 else if (slot == DynamicWithObject::thisSlot())
6047 slotname = "with_this";
6052 if (slotname)
6053 JS_snprintf(buf, bufsize, pattern, slotname);
6054 else
6055 JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot);
6056 } while (false);
6057 } else {
6058 jsid propid = shape->propid();
6059 if (JSID_IS_INT(propid)) {
6060 JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(propid));
6061 } else if (JSID_IS_ATOM(propid)) {
6062 PutEscapedString(buf, bufsize, JSID_TO_ATOM(propid), 0);
6063 } else if (JSID_IS_SYMBOL(propid)) {
6064 JS_snprintf(buf, bufsize, "**SYMBOL KEY**");
6065 } else {
6066 JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**");
6071 bool
6072 js_ReportGetterOnlyAssignment(JSContext *cx, bool strict)
6074 return JS_ReportErrorFlagsAndNumber(cx,
6075 strict
6076 ? JSREPORT_ERROR
6077 : JSREPORT_WARNING | JSREPORT_STRICT,
6078 js_GetErrorMessage, nullptr,
6079 JSMSG_GETTER_ONLY);
6082 JS_FRIEND_API(bool)
6083 js_GetterOnlyPropertyStub(JSContext *cx, HandleObject obj, HandleId id, bool strict,
6084 MutableHandleValue vp)
6086 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
6087 return false;
6090 #ifdef DEBUG
6093 * Routines to print out values during debugging. These are FRIEND_API to help
6094 * the debugger find them and to support temporarily hacking js_Dump* calls
6095 * into other code.
6098 static void
6099 dumpValue(const Value &v)
6101 if (v.isNull())
6102 fprintf(stderr, "null");
6103 else if (v.isUndefined())
6104 fprintf(stderr, "undefined");
6105 else if (v.isInt32())
6106 fprintf(stderr, "%d", v.toInt32());
6107 else if (v.isDouble())
6108 fprintf(stderr, "%g", v.toDouble());
6109 else if (v.isString())
6110 v.toString()->dump();
6111 else if (v.isObject() && v.toObject().is<JSFunction>()) {
6112 JSFunction *fun = &v.toObject().as<JSFunction>();
6113 if (fun->displayAtom()) {
6114 fputs("<function ", stderr);
6115 FileEscapedString(stderr, fun->displayAtom(), 0);
6116 } else {
6117 fputs("<unnamed function", stderr);
6119 if (fun->hasScript()) {
6120 JSScript *script = fun->nonLazyScript();
6121 fprintf(stderr, " (%s:%d)",
6122 script->filename() ? script->filename() : "", (int) script->lineno());
6124 fprintf(stderr, " at %p>", (void *) fun);
6125 } else if (v.isObject()) {
6126 JSObject *obj = &v.toObject();
6127 const Class *clasp = obj->getClass();
6128 fprintf(stderr, "<%s%s at %p>",
6129 clasp->name,
6130 (clasp == &JSObject::class_) ? "" : " object",
6131 (void *) obj);
6132 } else if (v.isBoolean()) {
6133 if (v.toBoolean())
6134 fprintf(stderr, "true");
6135 else
6136 fprintf(stderr, "false");
6137 } else if (v.isMagic()) {
6138 fprintf(stderr, "<invalid");
6139 #ifdef DEBUG
6140 switch (v.whyMagic()) {
6141 case JS_ELEMENTS_HOLE: fprintf(stderr, " elements hole"); break;
6142 case JS_NATIVE_ENUMERATE: fprintf(stderr, " native enumeration"); break;
6143 case JS_NO_ITER_VALUE: fprintf(stderr, " no iter value"); break;
6144 case JS_GENERATOR_CLOSING: fprintf(stderr, " generator closing"); break;
6145 case JS_OPTIMIZED_OUT: fprintf(stderr, " optimized out"); break;
6146 default: fprintf(stderr, " ?!"); break;
6148 #endif
6149 fprintf(stderr, ">");
6150 } else {
6151 fprintf(stderr, "unexpected value");
6155 JS_FRIEND_API(void)
6156 js_DumpValue(const Value &val)
6158 dumpValue(val);
6159 fputc('\n', stderr);
6162 JS_FRIEND_API(void)
6163 js_DumpId(jsid id)
6165 fprintf(stderr, "jsid %p = ", (void *) JSID_BITS(id));
6166 dumpValue(IdToValue(id));
6167 fputc('\n', stderr);
6170 static void
6171 DumpProperty(JSObject *obj, Shape &shape)
6173 jsid id = shape.propid();
6174 uint8_t attrs = shape.attributes();
6176 fprintf(stderr, " ((js::Shape *) %p) ", (void *) &shape);
6177 if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate ");
6178 if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly ");
6179 if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent ");
6180 if (attrs & JSPROP_SHARED) fprintf(stderr, "shared ");
6182 if (shape.hasGetterValue())
6183 fprintf(stderr, "getterValue=%p ", (void *) shape.getterObject());
6184 else if (!shape.hasDefaultGetter())
6185 fprintf(stderr, "getterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.getterOp()));
6187 if (shape.hasSetterValue())
6188 fprintf(stderr, "setterValue=%p ", (void *) shape.setterObject());
6189 else if (!shape.hasDefaultSetter())
6190 fprintf(stderr, "setterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.setterOp()));
6192 if (JSID_IS_ATOM(id) || JSID_IS_INT(id) || JSID_IS_SYMBOL(id))
6193 dumpValue(js::IdToValue(id));
6194 else
6195 fprintf(stderr, "unknown jsid %p", (void *) JSID_BITS(id));
6197 uint32_t slot = shape.hasSlot() ? shape.maybeSlot() : SHAPE_INVALID_SLOT;
6198 fprintf(stderr, ": slot %d", slot);
6199 if (shape.hasSlot()) {
6200 fprintf(stderr, " = ");
6201 dumpValue(obj->getSlot(slot));
6202 } else if (slot != SHAPE_INVALID_SLOT) {
6203 fprintf(stderr, " (INVALID!)");
6205 fprintf(stderr, "\n");
6208 bool
6209 JSObject::uninlinedIsProxy() const
6211 return is<ProxyObject>();
6214 void
6215 JSObject::dump()
6217 JSObject *obj = this;
6218 fprintf(stderr, "object %p\n", (void *) obj);
6219 const Class *clasp = obj->getClass();
6220 fprintf(stderr, "class %p %s\n", (const void *)clasp, clasp->name);
6222 fprintf(stderr, "flags:");
6223 if (obj->isDelegate()) fprintf(stderr, " delegate");
6224 if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible()) fprintf(stderr, " not_extensible");
6225 if (obj->isIndexed()) fprintf(stderr, " indexed");
6226 if (obj->isBoundFunction()) fprintf(stderr, " bound_function");
6227 if (obj->isQualifiedVarObj()) fprintf(stderr, " varobj");
6228 if (obj->isUnqualifiedVarObj()) fprintf(stderr, " unqualified_varobj");
6229 if (obj->watched()) fprintf(stderr, " watched");
6230 if (obj->isIteratedSingleton()) fprintf(stderr, " iterated_singleton");
6231 if (obj->isNewTypeUnknown()) fprintf(stderr, " new_type_unknown");
6232 if (obj->hasUncacheableProto()) fprintf(stderr, " has_uncacheable_proto");
6233 if (obj->hadElementsAccess()) fprintf(stderr, " had_elements_access");
6235 if (obj->isNative()) {
6236 if (obj->inDictionaryMode())
6237 fprintf(stderr, " inDictionaryMode");
6238 if (obj->hasShapeTable())
6239 fprintf(stderr, " hasShapeTable");
6241 fprintf(stderr, "\n");
6243 if (obj->isNative()) {
6244 uint32_t slots = obj->getDenseInitializedLength();
6245 if (slots) {
6246 fprintf(stderr, "elements\n");
6247 for (uint32_t i = 0; i < slots; i++) {
6248 fprintf(stderr, " %3d: ", i);
6249 dumpValue(obj->getDenseElement(i));
6250 fprintf(stderr, "\n");
6251 fflush(stderr);
6256 fprintf(stderr, "proto ");
6257 TaggedProto proto = obj->getTaggedProto();
6258 if (proto.isLazy())
6259 fprintf(stderr, "<lazy>");
6260 else
6261 dumpValue(ObjectOrNullValue(proto.toObjectOrNull()));
6262 fputc('\n', stderr);
6264 fprintf(stderr, "parent ");
6265 dumpValue(ObjectOrNullValue(obj->getParent()));
6266 fputc('\n', stderr);
6268 if (clasp->flags & JSCLASS_HAS_PRIVATE)
6269 fprintf(stderr, "private %p\n", obj->getPrivate());
6271 if (!obj->isNative())
6272 fprintf(stderr, "not native\n");
6274 uint32_t reservedEnd = JSCLASS_RESERVED_SLOTS(clasp);
6275 uint32_t slots = obj->slotSpan();
6276 uint32_t stop = obj->isNative() ? reservedEnd : slots;
6277 if (stop > 0)
6278 fprintf(stderr, obj->isNative() ? "reserved slots:\n" : "slots:\n");
6279 for (uint32_t i = 0; i < stop; i++) {
6280 fprintf(stderr, " %3d ", i);
6281 if (i < reservedEnd)
6282 fprintf(stderr, "(reserved) ");
6283 fprintf(stderr, "= ");
6284 dumpValue(obj->getSlot(i));
6285 fputc('\n', stderr);
6288 if (obj->isNative()) {
6289 fprintf(stderr, "properties:\n");
6290 Vector<Shape *, 8, SystemAllocPolicy> props;
6291 for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront())
6292 props.append(&r.front());
6293 for (size_t i = props.length(); i-- != 0;)
6294 DumpProperty(obj, *props[i]);
6296 fputc('\n', stderr);
6299 static void
6300 MaybeDumpObject(const char *name, JSObject *obj)
6302 if (obj) {
6303 fprintf(stderr, " %s: ", name);
6304 dumpValue(ObjectValue(*obj));
6305 fputc('\n', stderr);
6309 static void
6310 MaybeDumpValue(const char *name, const Value &v)
6312 if (!v.isNull()) {
6313 fprintf(stderr, " %s: ", name);
6314 dumpValue(v);
6315 fputc('\n', stderr);
6319 JS_FRIEND_API(void)
6320 js_DumpInterpreterFrame(JSContext *cx, InterpreterFrame *start)
6322 /* This should only called during live debugging. */
6323 ScriptFrameIter i(cx, ScriptFrameIter::GO_THROUGH_SAVED);
6324 if (!start) {
6325 if (i.done()) {
6326 fprintf(stderr, "no stack for cx = %p\n", (void*) cx);
6327 return;
6329 } else {
6330 while (!i.done() && !i.isJit() && i.interpFrame() != start)
6331 ++i;
6333 if (i.done()) {
6334 fprintf(stderr, "fp = %p not found in cx = %p\n",
6335 (void *)start, (void *)cx);
6336 return;
6340 for (; !i.done(); ++i) {
6341 if (i.isJit())
6342 fprintf(stderr, "JIT frame\n");
6343 else
6344 fprintf(stderr, "InterpreterFrame at %p\n", (void *) i.interpFrame());
6346 if (i.isFunctionFrame()) {
6347 fprintf(stderr, "callee fun: ");
6348 dumpValue(i.calleev());
6349 } else {
6350 fprintf(stderr, "global frame, no callee");
6352 fputc('\n', stderr);
6354 fprintf(stderr, "file %s line %u\n",
6355 i.script()->filename(), (unsigned) i.script()->lineno());
6357 if (jsbytecode *pc = i.pc()) {
6358 fprintf(stderr, " pc = %p\n", pc);
6359 fprintf(stderr, " current op: %s\n", js_CodeName[*pc]);
6360 MaybeDumpObject("staticScope", i.script()->getStaticScope(pc));
6362 MaybeDumpValue("this", i.thisv());
6363 if (!i.isJit()) {
6364 fprintf(stderr, " rval: ");
6365 dumpValue(i.interpFrame()->returnValue());
6366 fputc('\n', stderr);
6369 fprintf(stderr, " flags:");
6370 if (i.isConstructing())
6371 fprintf(stderr, " constructing");
6372 if (!i.isJit() && i.interpFrame()->isDebuggerFrame())
6373 fprintf(stderr, " debugger");
6374 if (i.isEvalFrame())
6375 fprintf(stderr, " eval");
6376 if (!i.isJit() && i.interpFrame()->isYielding())
6377 fprintf(stderr, " yielding");
6378 if (!i.isJit() && i.interpFrame()->isGeneratorFrame())
6379 fprintf(stderr, " generator");
6380 fputc('\n', stderr);
6382 fprintf(stderr, " scopeChain: (JSObject *) %p\n", (void *) i.scopeChain());
6384 fputc('\n', stderr);
6388 #endif /* DEBUG */
6390 JS_FRIEND_API(void)
6391 js_DumpBacktrace(JSContext *cx)
6393 Sprinter sprinter(cx);
6394 sprinter.init();
6395 size_t depth = 0;
6396 for (ScriptFrameIter i(cx); !i.done(); ++i, ++depth) {
6397 const char *filename = JS_GetScriptFilename(i.script());
6398 unsigned line = JS_PCToLineNumber(cx, i.script(), i.pc());
6399 JSScript *script = i.script();
6400 sprinter.printf("#%d %14p %s:%d (%p @ %d)\n",
6401 depth, (i.isJit() ? 0 : i.interpFrame()), filename, line,
6402 script, script->pcToOffset(i.pc()));
6404 fprintf(stdout, "%s", sprinter.string());
6407 void
6408 JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo *info)
6410 if (hasDynamicSlots())
6411 info->objectsMallocHeapSlots += mallocSizeOf(slots);
6413 if (hasDynamicElements()) {
6414 js::ObjectElements *elements = getElementsHeader();
6415 if (!elements->isCopyOnWrite() || elements->ownerObject() == this)
6416 info->objectsMallocHeapElementsNonAsmJS += mallocSizeOf(elements);
6419 // Other things may be measured in the future if DMD indicates it is worthwhile.
6420 if (is<JSFunction>() ||
6421 is<JSObject>() ||
6422 is<ArrayObject>() ||
6423 is<CallObject>() ||
6424 is<RegExpObject>() ||
6425 is<ProxyObject>())
6427 // Do nothing. But this function is hot, and we win by getting the
6428 // common cases out of the way early. Some stats on the most common
6429 // classes, as measured during a vanilla browser session:
6430 // - (53.7%, 53.7%): Function
6431 // - (18.0%, 71.7%): Object
6432 // - (16.9%, 88.6%): Array
6433 // - ( 3.9%, 92.5%): Call
6434 // - ( 2.8%, 95.3%): RegExp
6435 // - ( 1.0%, 96.4%): Proxy
6437 } else if (is<ArgumentsObject>()) {
6438 info->objectsMallocHeapMisc += as<ArgumentsObject>().sizeOfMisc(mallocSizeOf);
6439 } else if (is<RegExpStaticsObject>()) {
6440 info->objectsMallocHeapMisc += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
6441 } else if (is<PropertyIteratorObject>()) {
6442 info->objectsMallocHeapMisc += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
6443 } else if (is<ArrayBufferObject>() || is<SharedArrayBufferObject>()) {
6444 ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info);
6445 } else if (is<AsmJSModuleObject>()) {
6446 as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &info->objectsNonHeapCodeAsmJS,
6447 &info->objectsMallocHeapMisc);
6448 #ifdef JS_HAS_CTYPES
6449 } else {
6450 // This must be the last case.
6451 info->objectsMallocHeapMisc +=
6452 js::SizeOfDataIfCDataObject(mallocSizeOf, const_cast<JSObject *>(this));
6453 #endif