Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / js / src / vm / PlainObject.cpp
blob8341636796c6cca885303e9444a86d4e53c77e4c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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 "vm/PlainObject-inl.h"
13 #include "mozilla/Assertions.h" // MOZ_ASSERT
15 #include "jspubtd.h" // JSProto_Object
17 #include "ds/IdValuePair.h" // js::IdValuePair
18 #include "gc/AllocKind.h" // js::gc::AllocKind
19 #include "vm/JSContext.h" // JSContext
20 #include "vm/JSFunction.h" // JSFunction
21 #include "vm/JSObject.h" // JSObject, js::GetPrototypeFromConstructor
22 #include "vm/TaggedProto.h" // js::TaggedProto
24 #include "vm/JSFunction-inl.h"
25 #include "vm/JSObject-inl.h" // js::NewObjectWithGroup, js::NewObjectGCKind
27 using namespace js;
29 using JS::Handle;
30 using JS::Rooted;
32 static MOZ_ALWAYS_INLINE SharedShape* GetPlainObjectShapeWithProto(
33 JSContext* cx, JSObject* proto, gc::AllocKind kind) {
34 MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(&PlainObject::class_) == 0,
35 "all slots can be used for properties");
36 uint32_t nfixed = GetGCKindSlots(kind);
37 return SharedShape::getInitialShape(cx, &PlainObject::class_, cx->realm(),
38 TaggedProto(proto), nfixed);
41 SharedShape* js::ThisShapeForFunction(JSContext* cx, Handle<JSFunction*> callee,
42 Handle<JSObject*> newTarget) {
43 MOZ_ASSERT(cx->realm() == callee->realm());
44 MOZ_ASSERT(!callee->constructorNeedsUninitializedThis());
46 Rooted<JSObject*> proto(cx);
47 if (!GetPrototypeFromConstructor(cx, newTarget, JSProto_Object, &proto)) {
48 return nullptr;
51 js::gc::AllocKind allocKind = NewObjectGCKind();
52 if (!JSFunction::getAllocKindForThis(cx, callee, allocKind)) {
53 return nullptr;
56 SharedShape* res;
57 if (proto && proto != cx->global()->maybeGetPrototype(JSProto_Object)) {
58 res = GetPlainObjectShapeWithProto(cx, proto, allocKind);
59 } else {
60 res = GlobalObject::getPlainObjectShapeWithDefaultProto(cx, allocKind);
63 MOZ_ASSERT_IF(res, res->realm() == callee->realm());
65 return res;
68 #ifdef DEBUG
69 void PlainObject::assertHasNoNonWritableOrAccessorPropExclProto() const {
70 // Check the most recent MaxCount properties to not slow down debug builds too
71 // much.
72 static constexpr size_t MaxCount = 8;
74 size_t count = 0;
75 PropertyName* protoName = runtimeFromMainThread()->commonNames->proto_;
77 for (ShapePropertyIter<NoGC> iter(shape()); !iter.done(); iter++) {
78 // __proto__ is always allowed.
79 if (iter->key().isAtom(protoName)) {
80 continue;
83 MOZ_ASSERT(iter->isDataProperty());
84 MOZ_ASSERT(iter->writable());
86 count++;
87 if (count > MaxCount) {
88 return;
92 #endif
94 // static
95 PlainObject* PlainObject::createWithTemplateFromDifferentRealm(
96 JSContext* cx, Handle<PlainObject*> templateObject) {
97 MOZ_ASSERT(cx->realm() != templateObject->realm(),
98 "Use createWithTemplate() for same-realm objects");
100 // Currently only implemented for null-proto.
101 MOZ_ASSERT(templateObject->staticPrototype() == nullptr);
103 // The object mustn't be in dictionary mode.
104 MOZ_ASSERT(!templateObject->shape()->isDictionary());
106 TaggedProto proto = TaggedProto(nullptr);
107 SharedShape* templateShape = templateObject->sharedShape();
108 Rooted<SharedPropMap*> map(cx, templateShape->propMap());
110 Rooted<SharedShape*> shape(
111 cx, SharedShape::getInitialOrPropMapShape(
112 cx, &PlainObject::class_, cx->realm(), proto,
113 templateShape->numFixedSlots(), map,
114 templateShape->propMapLength(), templateShape->objectFlags()));
115 if (!shape) {
116 return nullptr;
118 return createWithShape(cx, shape);
121 // static
122 SharedShape* GlobalObject::createPlainObjectShapeWithDefaultProto(
123 JSContext* cx, gc::AllocKind kind) {
124 PlainObjectSlotsKind slotsKind = PlainObjectSlotsKindFromAllocKind(kind);
125 HeapPtr<SharedShape*>& shapeRef =
126 cx->global()->data().plainObjectShapesWithDefaultProto[slotsKind];
127 MOZ_ASSERT(!shapeRef);
129 JSObject* proto = &cx->global()->getObjectPrototype();
130 SharedShape* shape = GetPlainObjectShapeWithProto(cx, proto, kind);
131 if (!shape) {
132 return nullptr;
135 shapeRef.init(shape);
136 return shape;
139 PlainObject* js::NewPlainObject(JSContext* cx, NewObjectKind newKind) {
140 constexpr gc::AllocKind allocKind = gc::AllocKind::OBJECT0;
141 MOZ_ASSERT(gc::GetGCObjectKind(&PlainObject::class_) == allocKind);
143 Rooted<SharedShape*> shape(
144 cx, GlobalObject::getPlainObjectShapeWithDefaultProto(cx, allocKind));
145 if (!shape) {
146 return nullptr;
149 return PlainObject::createWithShape(cx, shape, allocKind, newKind);
152 PlainObject* js::NewPlainObjectWithAllocKind(JSContext* cx,
153 gc::AllocKind allocKind,
154 NewObjectKind newKind) {
155 Rooted<SharedShape*> shape(
156 cx, GlobalObject::getPlainObjectShapeWithDefaultProto(cx, allocKind));
157 if (!shape) {
158 return nullptr;
161 return PlainObject::createWithShape(cx, shape, allocKind, newKind);
164 PlainObject* js::NewPlainObjectWithProto(JSContext* cx, HandleObject proto,
165 NewObjectKind newKind) {
166 // Use a faster path if |proto| is %Object.prototype% (the common case).
167 if (proto && proto == cx->global()->maybeGetPrototype(JSProto_Object)) {
168 return NewPlainObject(cx, newKind);
171 constexpr gc::AllocKind allocKind = gc::AllocKind::OBJECT0;
172 MOZ_ASSERT(gc::GetGCObjectKind(&PlainObject::class_) == allocKind);
174 Rooted<SharedShape*> shape(
175 cx, GetPlainObjectShapeWithProto(cx, proto, allocKind));
176 if (!shape) {
177 return nullptr;
180 return PlainObject::createWithShape(cx, shape, allocKind, newKind);
183 PlainObject* js::NewPlainObjectWithProtoAndAllocKind(JSContext* cx,
184 HandleObject proto,
185 gc::AllocKind allocKind,
186 NewObjectKind newKind) {
187 // Use a faster path if |proto| is %Object.prototype% (the common case).
188 if (proto && proto == cx->global()->maybeGetPrototype(JSProto_Object)) {
189 return NewPlainObjectWithAllocKind(cx, allocKind, newKind);
192 Rooted<SharedShape*> shape(
193 cx, GetPlainObjectShapeWithProto(cx, proto, allocKind));
194 if (!shape) {
195 return nullptr;
198 return PlainObject::createWithShape(cx, shape, allocKind, newKind);
201 void js::NewPlainObjectWithPropsCache::add(SharedShape* shape) {
202 MOZ_ASSERT(shape);
203 MOZ_ASSERT(shape->slotSpan() > 0);
204 for (size_t i = NumEntries - 1; i > 0; i--) {
205 entries_[i] = entries_[i - 1];
207 entries_[0] = shape;
210 static bool ShapeMatches(IdValuePair* properties, size_t nproperties,
211 SharedShape* shape) {
212 if (shape->slotSpan() != nproperties) {
213 return false;
215 SharedShapePropertyIter<NoGC> iter(shape);
216 for (size_t i = nproperties; i > 0; i--) {
217 MOZ_ASSERT(iter->isDataProperty());
218 MOZ_ASSERT(iter->flags() == PropertyFlags::defaultDataPropFlags);
219 if (properties[i - 1].id != iter->key()) {
220 return false;
222 iter++;
224 MOZ_ASSERT(iter.done());
225 return true;
228 SharedShape* js::NewPlainObjectWithPropsCache::lookup(
229 IdValuePair* properties, size_t nproperties) const {
230 for (size_t i = 0; i < NumEntries; i++) {
231 SharedShape* shape = entries_[i];
232 if (shape && ShapeMatches(properties, nproperties, shape)) {
233 return shape;
236 return nullptr;
239 enum class KeysKind { UniqueNames, Unknown };
241 template <KeysKind Kind>
242 static PlainObject* NewPlainObjectWithProperties(JSContext* cx,
243 IdValuePair* properties,
244 size_t nproperties,
245 NewObjectKind newKind) {
246 auto& cache = cx->realm()->newPlainObjectWithPropsCache;
248 // If we recently created an object with these properties, we can use that
249 // Shape directly.
250 if (SharedShape* shape = cache.lookup(properties, nproperties)) {
251 Rooted<SharedShape*> shapeRoot(cx, shape);
252 PlainObject* obj = PlainObject::createWithShape(cx, shapeRoot, newKind);
253 if (!obj) {
254 return nullptr;
256 MOZ_ASSERT(obj->slotSpan() == nproperties);
257 for (size_t i = 0; i < nproperties; i++) {
258 obj->initSlot(i, properties[i].value);
260 return obj;
263 gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
264 Rooted<PlainObject*> obj(cx,
265 NewPlainObjectWithAllocKind(cx, allocKind, newKind));
266 if (!obj) {
267 return nullptr;
270 if (nproperties == 0) {
271 return obj;
274 Rooted<PropertyKey> key(cx);
275 Rooted<Value> value(cx);
276 bool canCache = true;
278 for (size_t i = 0; i < nproperties; i++) {
279 key = properties[i].id;
280 value = properties[i].value;
282 // Integer keys may need to be stored in dense elements. This is uncommon so
283 // just fall back to NativeDefineDataProperty.
284 if constexpr (Kind == KeysKind::Unknown) {
285 if (MOZ_UNLIKELY(key.isInt())) {
286 canCache = false;
287 if (!NativeDefineDataProperty(cx, obj, key, value, JSPROP_ENUMERATE)) {
288 return nullptr;
290 continue;
294 MOZ_ASSERT(key.isAtom() || key.isSymbol());
296 // Check for duplicate keys. In this case we must overwrite the earlier
297 // property value.
298 if constexpr (Kind == KeysKind::UniqueNames) {
299 MOZ_ASSERT(!obj->containsPure(key));
300 } else {
301 mozilla::Maybe<PropertyInfo> prop = obj->lookup(cx, key);
302 if (MOZ_UNLIKELY(prop)) {
303 canCache = false;
304 MOZ_ASSERT(prop->isDataProperty());
305 obj->setSlot(prop->slot(), value);
306 continue;
310 if (!AddDataPropertyToPlainObject(cx, obj, key, value)) {
311 return nullptr;
315 if (canCache && !obj->inDictionaryMode()) {
316 MOZ_ASSERT(obj->getDenseInitializedLength() == 0);
317 MOZ_ASSERT(obj->slotSpan() == nproperties);
318 cache.add(obj->sharedShape());
321 return obj;
324 PlainObject* js::NewPlainObjectWithUniqueNames(JSContext* cx,
325 IdValuePair* properties,
326 size_t nproperties,
327 NewObjectKind newKind) {
328 return NewPlainObjectWithProperties<KeysKind::UniqueNames>(
329 cx, properties, nproperties, newKind);
332 PlainObject* js::NewPlainObjectWithMaybeDuplicateKeys(JSContext* cx,
333 IdValuePair* properties,
334 size_t nproperties,
335 NewObjectKind newKind) {
336 return NewPlainObjectWithProperties<KeysKind::Unknown>(cx, properties,
337 nproperties, newKind);