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
CreateThisForFunction(JSContext * cx,Handle<JSFunction * > callee,Handle<JSObject * > newTarget,NewObjectKind newKind)31 PlainObject* js::CreateThisForFunction(JSContext* cx,
32 Handle<JSFunction*> callee,
33 Handle<JSObject*> newTarget,
34 NewObjectKind newKind) {
35 MOZ_ASSERT(cx->realm() == callee->realm());
36 MOZ_ASSERT(!callee->constructorNeedsUninitializedThis());
37
38 Rooted<JSObject*> proto(cx);
39 if (!GetPrototypeFromConstructor(cx, newTarget, JSProto_Object, &proto)) {
40 return nullptr;
41 }
42
43 PlainObject* res;
44 if (proto) {
45 js::gc::AllocKind allocKind = NewObjectGCKind();
46 res = NewObjectWithGivenProtoAndKinds<PlainObject>(cx, proto, allocKind,
47 newKind);
48 } else {
49 res = NewBuiltinClassInstanceWithKind<PlainObject>(cx, newKind);
50 }
51
52 MOZ_ASSERT_IF(res, res->nonCCWRealm() == callee->realm());
53
54 return res;
55 }
56
57 #ifdef DEBUG
assertHasNoNonWritableOrAccessorPropExclProto() const58 void PlainObject::assertHasNoNonWritableOrAccessorPropExclProto() const {
59 // Check the most recent MaxCount properties to not slow down debug builds too
60 // much.
61 static constexpr size_t MaxCount = 8;
62
63 size_t count = 0;
64 PropertyName* protoName = runtimeFromMainThread()->commonNames->proto;
65
66 for (ShapePropertyIter<NoGC> iter(shape()); !iter.done(); iter++) {
67 // __proto__ is always allowed.
68 if (iter->key().isAtom(protoName)) {
69 continue;
70 }
71
72 MOZ_ASSERT(iter->isDataProperty());
73 MOZ_ASSERT(iter->writable());
74
75 count++;
76 if (count > MaxCount) {
77 return;
78 }
79 }
80 }
81 #endif
82
83 JS::Result<PlainObject*, JS::OOM>
createWithTemplateFromDifferentRealm(JSContext * cx,HandlePlainObject templateObject)84 PlainObject::createWithTemplateFromDifferentRealm(
85 JSContext* cx, HandlePlainObject templateObject) {
86 MOZ_ASSERT(cx->realm() != templateObject->realm(),
87 "Use createWithTemplate() for same-realm objects");
88
89 // Currently only implemented for null-proto.
90 MOZ_ASSERT(templateObject->staticPrototype() == nullptr);
91
92 // The object mustn't be in dictionary mode.
93 MOZ_ASSERT(!templateObject->shape()->isDictionary());
94
95 TaggedProto proto = TaggedProto(nullptr);
96 Shape* templateShape = templateObject->shape();
97 Rooted<SharedPropMap*> map(cx, templateShape->propMap()->asShared());
98
99 RootedShape shape(
100 cx, SharedShape::getInitialOrPropMapShape(
101 cx, &PlainObject::class_, cx->realm(), proto,
102 templateShape->numFixedSlots(), map,
103 templateShape->propMapLength(), templateShape->objectFlags()));
104 if (!shape) {
105 return nullptr;
106 }
107 return createWithShape(cx, shape);
108 }
109
AddPlainObjectProperties(JSContext * cx,HandlePlainObject obj,IdValuePair * properties,size_t nproperties)110 static bool AddPlainObjectProperties(JSContext* cx, HandlePlainObject obj,
111 IdValuePair* properties,
112 size_t nproperties) {
113 RootedId propid(cx);
114 RootedValue value(cx);
115
116 for (size_t i = 0; i < nproperties; i++) {
117 propid = properties[i].id;
118 value = properties[i].value;
119 if (!NativeDefineDataProperty(cx, obj, propid, value, JSPROP_ENUMERATE)) {
120 return false;
121 }
122 }
123
124 return true;
125 }
126
NewPlainObjectWithProperties(JSContext * cx,IdValuePair * properties,size_t nproperties,NewObjectKind newKind)127 PlainObject* js::NewPlainObjectWithProperties(JSContext* cx,
128 IdValuePair* properties,
129 size_t nproperties,
130 NewObjectKind newKind) {
131 gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
132 RootedPlainObject obj(
133 cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind));
134 if (!obj || !AddPlainObjectProperties(cx, obj, properties, nproperties)) {
135 return nullptr;
136 }
137 return obj;
138 }
139