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/. */
6
7 /*
8 * JS object implementation.
9 */
10
11 #include "vm/PlainObject-inl.h"
12
13 #include "mozilla/Assertions.h" // MOZ_ASSERT
14
15 #include "jspubtd.h" // JSProto_Object
16
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
23
24 #include "vm/JSObject-inl.h" // js::NewObjectWithGroup, js::NewObjectGCKind
25
26 using namespace js;
27
28 using JS::Handle;
29 using JS::Rooted;
30
GetPlainObjectShapeWithProto(JSContext * cx,JSObject * proto,gc::AllocKind kind)31 static MOZ_ALWAYS_INLINE Shape* GetPlainObjectShapeWithProto(
32 JSContext* cx, JSObject* proto, gc::AllocKind kind) {
33 MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(&PlainObject::class_) == 0,
34 "all slots can be used for properties");
35 uint32_t nfixed = GetGCKindSlots(kind);
36 return SharedShape::getInitialShape(cx, &PlainObject::class_, cx->realm(),
37 TaggedProto(proto), nfixed);
38 }
39
ThisShapeForFunction(JSContext * cx,Handle<JSFunction * > callee,Handle<JSObject * > newTarget)40 Shape* js::ThisShapeForFunction(JSContext* cx, Handle<JSFunction*> callee,
41 Handle<JSObject*> newTarget) {
42 MOZ_ASSERT(cx->realm() == callee->realm());
43 MOZ_ASSERT(!callee->constructorNeedsUninitializedThis());
44
45 Rooted<JSObject*> proto(cx);
46 if (!GetPrototypeFromConstructor(cx, newTarget, JSProto_Object, &proto)) {
47 return nullptr;
48 }
49
50 js::gc::AllocKind allocKind = NewObjectGCKind();
51
52 Shape* res;
53 if (proto && proto != cx->global()->maybeGetPrototype(JSProto_Object)) {
54 res = GetPlainObjectShapeWithProto(cx, proto, allocKind);
55 } else {
56 res = GlobalObject::getPlainObjectShapeWithDefaultProto(cx, allocKind);
57 }
58
59 MOZ_ASSERT_IF(res, res->realm() == callee->realm());
60
61 return res;
62 }
63
64 #ifdef DEBUG
assertHasNoNonWritableOrAccessorPropExclProto() const65 void PlainObject::assertHasNoNonWritableOrAccessorPropExclProto() const {
66 // Check the most recent MaxCount properties to not slow down debug builds too
67 // much.
68 static constexpr size_t MaxCount = 8;
69
70 size_t count = 0;
71 PropertyName* protoName = runtimeFromMainThread()->commonNames->proto;
72
73 for (ShapePropertyIter<NoGC> iter(shape()); !iter.done(); iter++) {
74 // __proto__ is always allowed.
75 if (iter->key().isAtom(protoName)) {
76 continue;
77 }
78
79 MOZ_ASSERT(iter->isDataProperty());
80 MOZ_ASSERT(iter->writable());
81
82 count++;
83 if (count > MaxCount) {
84 return;
85 }
86 }
87 }
88 #endif
89
90 // static
createWithTemplateFromDifferentRealm(JSContext * cx,HandlePlainObject templateObject)91 PlainObject* PlainObject::createWithTemplateFromDifferentRealm(
92 JSContext* cx, HandlePlainObject templateObject) {
93 MOZ_ASSERT(cx->realm() != templateObject->realm(),
94 "Use createWithTemplate() for same-realm objects");
95
96 // Currently only implemented for null-proto.
97 MOZ_ASSERT(templateObject->staticPrototype() == nullptr);
98
99 // The object mustn't be in dictionary mode.
100 MOZ_ASSERT(!templateObject->shape()->isDictionary());
101
102 TaggedProto proto = TaggedProto(nullptr);
103 Shape* templateShape = templateObject->shape();
104 Rooted<SharedPropMap*> map(cx, templateShape->propMap()->asShared());
105
106 RootedShape shape(
107 cx, SharedShape::getInitialOrPropMapShape(
108 cx, &PlainObject::class_, cx->realm(), proto,
109 templateShape->numFixedSlots(), map,
110 templateShape->propMapLength(), templateShape->objectFlags()));
111 if (!shape) {
112 return nullptr;
113 }
114 return createWithShape(cx, shape);
115 }
116
AddPlainObjectProperties(JSContext * cx,HandlePlainObject obj,IdValuePair * properties,size_t nproperties)117 static bool AddPlainObjectProperties(JSContext* cx, HandlePlainObject obj,
118 IdValuePair* properties,
119 size_t nproperties) {
120 RootedId propid(cx);
121 RootedValue value(cx);
122
123 for (size_t i = 0; i < nproperties; i++) {
124 propid = properties[i].id;
125 value = properties[i].value;
126 if (!NativeDefineDataProperty(cx, obj, propid, value, JSPROP_ENUMERATE)) {
127 return false;
128 }
129 }
130
131 return true;
132 }
133
134 // static
createPlainObjectShapeWithDefaultProto(JSContext * cx,gc::AllocKind kind)135 Shape* GlobalObject::createPlainObjectShapeWithDefaultProto(
136 JSContext* cx, gc::AllocKind kind) {
137 PlainObjectSlotsKind slotsKind = PlainObjectSlotsKindFromAllocKind(kind);
138 HeapPtr<Shape*>& shapeRef =
139 cx->global()->data().plainObjectShapesWithDefaultProto[slotsKind];
140 MOZ_ASSERT(!shapeRef);
141
142 JSObject* proto = GlobalObject::getOrCreatePrototype(cx, JSProto_Object);
143 if (!proto) {
144 return nullptr;
145 }
146
147 Shape* shape = GetPlainObjectShapeWithProto(cx, proto, kind);
148 if (!shape) {
149 return nullptr;
150 }
151
152 shapeRef.init(shape);
153 return shape;
154 }
155
NewPlainObject(JSContext * cx,NewObjectKind newKind)156 PlainObject* js::NewPlainObject(JSContext* cx, NewObjectKind newKind) {
157 constexpr gc::AllocKind allocKind = gc::AllocKind::OBJECT0;
158 MOZ_ASSERT(gc::GetGCObjectKind(&PlainObject::class_) == allocKind);
159
160 RootedShape shape(
161 cx, GlobalObject::getPlainObjectShapeWithDefaultProto(cx, allocKind));
162 if (!shape) {
163 return nullptr;
164 }
165
166 return PlainObject::createWithShape(cx, shape, allocKind, newKind);
167 }
168
NewPlainObjectWithAllocKind(JSContext * cx,gc::AllocKind allocKind,NewObjectKind newKind)169 PlainObject* js::NewPlainObjectWithAllocKind(JSContext* cx,
170 gc::AllocKind allocKind,
171 NewObjectKind newKind) {
172 RootedShape shape(
173 cx, GlobalObject::getPlainObjectShapeWithDefaultProto(cx, allocKind));
174 if (!shape) {
175 return nullptr;
176 }
177
178 return PlainObject::createWithShape(cx, shape, allocKind, newKind);
179 }
180
NewPlainObjectWithProto(JSContext * cx,HandleObject proto,NewObjectKind newKind)181 PlainObject* js::NewPlainObjectWithProto(JSContext* cx, HandleObject proto,
182 NewObjectKind newKind) {
183 // Use a faster path if |proto| is %Object.prototype% (the common case).
184 if (proto && proto == cx->global()->maybeGetPrototype(JSProto_Object)) {
185 return NewPlainObject(cx, newKind);
186 }
187
188 constexpr gc::AllocKind allocKind = gc::AllocKind::OBJECT0;
189 MOZ_ASSERT(gc::GetGCObjectKind(&PlainObject::class_) == allocKind);
190
191 RootedShape shape(cx, GetPlainObjectShapeWithProto(cx, proto, allocKind));
192 if (!shape) {
193 return nullptr;
194 }
195
196 return PlainObject::createWithShape(cx, shape, allocKind, newKind);
197 }
198
NewPlainObjectWithProtoAndAllocKind(JSContext * cx,HandleObject proto,gc::AllocKind allocKind,NewObjectKind newKind)199 PlainObject* js::NewPlainObjectWithProtoAndAllocKind(JSContext* cx,
200 HandleObject proto,
201 gc::AllocKind allocKind,
202 NewObjectKind newKind) {
203 // Use a faster path if |proto| is %Object.prototype% (the common case).
204 if (proto && proto == cx->global()->maybeGetPrototype(JSProto_Object)) {
205 return NewPlainObjectWithAllocKind(cx, allocKind, newKind);
206 }
207
208 RootedShape shape(cx, GetPlainObjectShapeWithProto(cx, proto, allocKind));
209 if (!shape) {
210 return nullptr;
211 }
212
213 return PlainObject::createWithShape(cx, shape, allocKind, newKind);
214 }
215
NewPlainObjectWithProperties(JSContext * cx,IdValuePair * properties,size_t nproperties,NewObjectKind newKind)216 PlainObject* js::NewPlainObjectWithProperties(JSContext* cx,
217 IdValuePair* properties,
218 size_t nproperties,
219 NewObjectKind newKind) {
220 gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
221 RootedPlainObject obj(cx,
222 NewPlainObjectWithAllocKind(cx, allocKind, newKind));
223 if (!obj || !AddPlainObjectProperties(cx, obj, properties, nproperties)) {
224 return nullptr;
225 }
226 return obj;
227 }
228