|
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/. */ |
|
6 |
|
7 #ifndef vm_Shape_inl_h |
|
8 #define vm_Shape_inl_h |
|
9 |
|
10 #include "vm/Shape.h" |
|
11 |
|
12 #include "mozilla/TypeTraits.h" |
|
13 |
|
14 #include "jsobj.h" |
|
15 |
|
16 #include "vm/Interpreter.h" |
|
17 #include "vm/ScopeObject.h" |
|
18 #include "vm/TypedArrayObject.h" |
|
19 |
|
20 #include "jsatominlines.h" |
|
21 #include "jscntxtinlines.h" |
|
22 #include "jsgcinlines.h" |
|
23 |
|
24 namespace js { |
|
25 |
|
26 inline |
|
27 StackBaseShape::StackBaseShape(ThreadSafeContext *cx, const Class *clasp, |
|
28 JSObject *parent, JSObject *metadata, uint32_t objectFlags) |
|
29 : flags(objectFlags), |
|
30 clasp(clasp), |
|
31 parent(parent), |
|
32 metadata(metadata), |
|
33 rawGetter(nullptr), |
|
34 rawSetter(nullptr), |
|
35 compartment(cx->compartment_) |
|
36 {} |
|
37 |
|
38 inline bool |
|
39 Shape::get(JSContext* cx, HandleObject receiver, JSObject* obj, JSObject *pobj, |
|
40 MutableHandleValue vp) |
|
41 { |
|
42 JS_ASSERT(!hasDefaultGetter()); |
|
43 |
|
44 if (hasGetterValue()) { |
|
45 Value fval = getterValue(); |
|
46 return InvokeGetterOrSetter(cx, receiver, fval, 0, 0, vp); |
|
47 } |
|
48 |
|
49 RootedId id(cx, propid()); |
|
50 return CallJSPropertyOp(cx, getterOp(), receiver, id, vp); |
|
51 } |
|
52 |
|
53 inline Shape * |
|
54 Shape::search(ExclusiveContext *cx, jsid id) |
|
55 { |
|
56 Shape **_; |
|
57 return search(cx, this, id, &_); |
|
58 } |
|
59 |
|
60 inline Shape * |
|
61 Shape::searchThreadLocal(ThreadSafeContext *cx, Shape *start, jsid id, |
|
62 Shape ***pspp, bool adding) |
|
63 { |
|
64 /* |
|
65 * Note that adding is a best-effort attempt to claim an entry in a shape |
|
66 * table. In the sequential case, this can be done either when the object |
|
67 * is in dictionary mode, or when it has been hashified. |
|
68 * |
|
69 * In parallel, an object that is in dictionary mode may be thread |
|
70 * local. That is, it was converted to a dictionary in the current thread, |
|
71 * with all its shapes cloned into the current thread, and its shape table |
|
72 * allocated thread locally. In that case, we may add to the |
|
73 * table. Otherwise it is not allowed. |
|
74 */ |
|
75 JS_ASSERT_IF(adding, cx->isThreadLocal(start) && start->inDictionary()); |
|
76 |
|
77 if (start->inDictionary()) { |
|
78 *pspp = start->table().search(id, adding); |
|
79 return SHAPE_FETCH(*pspp); |
|
80 } |
|
81 |
|
82 *pspp = nullptr; |
|
83 |
|
84 return searchNoHashify(start, id); |
|
85 } |
|
86 |
|
87 inline bool |
|
88 Shape::set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, |
|
89 MutableHandleValue vp) |
|
90 { |
|
91 JS_ASSERT_IF(hasDefaultSetter(), hasGetterValue()); |
|
92 |
|
93 if (attrs & JSPROP_SETTER) { |
|
94 Value fval = setterValue(); |
|
95 return InvokeGetterOrSetter(cx, receiver, fval, 1, vp.address(), vp); |
|
96 } |
|
97 |
|
98 if (attrs & JSPROP_GETTER) |
|
99 return js_ReportGetterOnlyAssignment(cx, strict); |
|
100 |
|
101 RootedId id(cx, propid()); |
|
102 |
|
103 /* |
|
104 * |with (it) color='red';| ends up here. |
|
105 * Avoid exposing the With object to native setters. |
|
106 */ |
|
107 if (obj->is<DynamicWithObject>()) { |
|
108 RootedObject nobj(cx, &obj->as<DynamicWithObject>().object()); |
|
109 return CallJSPropertyOpSetter(cx, setterOp(), nobj, id, strict, vp); |
|
110 } |
|
111 |
|
112 return CallJSPropertyOpSetter(cx, setterOp(), obj, id, strict, vp); |
|
113 } |
|
114 |
|
115 /* static */ inline Shape * |
|
116 Shape::search(ExclusiveContext *cx, Shape *start, jsid id, Shape ***pspp, bool adding) |
|
117 { |
|
118 if (start->inDictionary()) { |
|
119 *pspp = start->table().search(id, adding); |
|
120 return SHAPE_FETCH(*pspp); |
|
121 } |
|
122 |
|
123 *pspp = nullptr; |
|
124 |
|
125 if (start->hasTable()) { |
|
126 Shape **spp = start->table().search(id, adding); |
|
127 return SHAPE_FETCH(spp); |
|
128 } |
|
129 |
|
130 if (start->numLinearSearches() == LINEAR_SEARCHES_MAX) { |
|
131 if (start->isBigEnoughForAShapeTable()) { |
|
132 if (Shape::hashify(cx, start)) { |
|
133 Shape **spp = start->table().search(id, adding); |
|
134 return SHAPE_FETCH(spp); |
|
135 } else { |
|
136 cx->recoverFromOutOfMemory(); |
|
137 } |
|
138 } |
|
139 /* |
|
140 * No table built -- there weren't enough entries, or OOM occurred. |
|
141 * Don't increment numLinearSearches, to keep hasTable() false. |
|
142 */ |
|
143 JS_ASSERT(!start->hasTable()); |
|
144 } else { |
|
145 start->incrementNumLinearSearches(); |
|
146 } |
|
147 |
|
148 for (Shape *shape = start; shape; shape = shape->parent) { |
|
149 if (shape->propidRef() == id) |
|
150 return shape; |
|
151 } |
|
152 |
|
153 return nullptr; |
|
154 } |
|
155 |
|
156 template<class ObjectSubclass> |
|
157 /* static */ inline bool |
|
158 EmptyShape::ensureInitialCustomShape(ExclusiveContext *cx, Handle<ObjectSubclass*> obj) |
|
159 { |
|
160 static_assert(mozilla::IsBaseOf<JSObject, ObjectSubclass>::value, |
|
161 "ObjectSubclass must be a subclass of JSObject"); |
|
162 |
|
163 // If the provided object has a non-empty shape, it was given the cached |
|
164 // initial shape when created: nothing to do. |
|
165 if (!obj->nativeEmpty()) |
|
166 return true; |
|
167 |
|
168 // If no initial shape was assigned, do so. |
|
169 RootedShape shape(cx, ObjectSubclass::assignInitialShape(cx, obj)); |
|
170 if (!shape) |
|
171 return false; |
|
172 MOZ_ASSERT(!obj->nativeEmpty()); |
|
173 |
|
174 // If the object is a standard prototype -- |RegExp.prototype|, |
|
175 // |String.prototype|, |RangeError.prototype|, &c. -- GlobalObject.cpp's |
|
176 // |CreateBlankProto| marked it as a delegate. These are the only objects |
|
177 // of this class that won't use the standard prototype, and there's no |
|
178 // reason to pollute the initial shape cache with entries for them. |
|
179 if (obj->isDelegate()) |
|
180 return true; |
|
181 |
|
182 // Cache the initial shape for non-prototype objects, however, so that |
|
183 // future instances will begin life with that shape. |
|
184 RootedObject proto(cx, obj->getProto()); |
|
185 EmptyShape::insertInitialShape(cx, shape, proto); |
|
186 return true; |
|
187 } |
|
188 |
|
189 inline |
|
190 AutoRooterGetterSetter::Inner::Inner(ThreadSafeContext *cx, uint8_t attrs, |
|
191 PropertyOp *pgetter_, StrictPropertyOp *psetter_) |
|
192 : CustomAutoRooter(cx), attrs(attrs), |
|
193 pgetter(pgetter_), psetter(psetter_) |
|
194 { |
|
195 JS_ASSERT_IF(attrs & JSPROP_GETTER, !IsPoisonedPtr(*pgetter)); |
|
196 JS_ASSERT_IF(attrs & JSPROP_SETTER, !IsPoisonedPtr(*psetter)); |
|
197 } |
|
198 |
|
199 inline |
|
200 AutoRooterGetterSetter::AutoRooterGetterSetter(ThreadSafeContext *cx, uint8_t attrs, |
|
201 PropertyOp *pgetter, StrictPropertyOp *psetter |
|
202 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) |
|
203 { |
|
204 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) |
|
205 inner.construct(cx, attrs, pgetter, psetter); |
|
206 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
207 } |
|
208 |
|
209 static inline uint8_t |
|
210 GetShapeAttributes(JSObject *obj, Shape *shape) |
|
211 { |
|
212 JS_ASSERT(obj->isNative()); |
|
213 |
|
214 if (IsImplicitDenseOrTypedArrayElement(shape)) { |
|
215 if (obj->is<TypedArrayObject>()) |
|
216 return JSPROP_ENUMERATE | JSPROP_PERMANENT; |
|
217 return JSPROP_ENUMERATE; |
|
218 } |
|
219 |
|
220 return shape->attributes(); |
|
221 } |
|
222 |
|
223 } /* namespace js */ |
|
224 |
|
225 #endif /* vm_Shape_inl_h */ |