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