1 : /* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
2 : /* vim: set ts=40 sw=4 et tw=99: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is the Mozilla SpiderMonkey property tree implementation
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Mozilla Foundation
20 : * Portions created by the Initial Developer are Copyright (C) 2002-2010
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Brendan Eich <brendan@mozilla.org>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include <new>
41 :
42 : #include "jstypes.h"
43 : #include "jsprf.h"
44 : #include "jsapi.h"
45 : #include "jscntxt.h"
46 : #include "jsgc.h"
47 : #include "jspropertytree.h"
48 : #include "jsscope.h"
49 :
50 : #include "jsgcinlines.h"
51 : #include "jsobjinlines.h"
52 : #include "jsscopeinlines.h"
53 :
54 : using namespace js;
55 :
56 : inline HashNumber
57 6236301 : ShapeHasher::hash(const Lookup &l)
58 : {
59 6236301 : return l.hash();
60 : }
61 :
62 : inline bool
63 4136813 : ShapeHasher::match(const Key k, const Lookup &l)
64 : {
65 4136813 : return k->matches(l);
66 : }
67 :
68 : Shape *
69 15359492 : PropertyTree::newShape(JSContext *cx)
70 : {
71 15359492 : Shape *shape = js_NewGCShape(cx);
72 15359492 : if (!shape) {
73 0 : JS_ReportOutOfMemory(cx);
74 0 : return NULL;
75 : }
76 15359492 : return shape;
77 : }
78 :
79 : static KidsHash *
80 708487 : HashChildren(Shape *kid1, Shape *kid2)
81 : {
82 708487 : KidsHash *hash = OffTheBooks::new_<KidsHash>();
83 708487 : if (!hash || !hash->init(2)) {
84 0 : Foreground::delete_(hash);
85 0 : return NULL;
86 : }
87 :
88 708487 : JS_ALWAYS_TRUE(hash->putNew(kid1, kid1));
89 708487 : JS_ALWAYS_TRUE(hash->putNew(kid2, kid2));
90 708487 : return hash;
91 : }
92 :
93 : bool
94 13499442 : PropertyTree::insertChild(JSContext *cx, Shape *parent, Shape *child)
95 : {
96 13499442 : JS_ASSERT(!parent->inDictionary());
97 13499442 : JS_ASSERT(!child->parent);
98 13499442 : JS_ASSERT(!child->inDictionary());
99 13499442 : JS_ASSERT(cx->compartment == compartment);
100 13499442 : JS_ASSERT(child->compartment() == parent->compartment());
101 :
102 13499442 : KidsPointer *kidp = &parent->kids;
103 :
104 13499442 : if (kidp->isNull()) {
105 12449698 : child->setParent(parent);
106 12449698 : kidp->setShape(child);
107 12449698 : return true;
108 : }
109 :
110 1049744 : if (kidp->isShape()) {
111 708487 : Shape *shape = kidp->toShape();
112 708487 : JS_ASSERT(shape != child);
113 708487 : JS_ASSERT(!shape->matches(child));
114 :
115 708487 : KidsHash *hash = HashChildren(shape, child);
116 708487 : if (!hash) {
117 0 : JS_ReportOutOfMemory(cx);
118 0 : return false;
119 : }
120 708487 : kidp->setHash(hash);
121 708487 : child->setParent(parent);
122 708487 : return true;
123 : }
124 :
125 341257 : if (!kidp->toHash()->putNew(child, child)) {
126 0 : JS_ReportOutOfMemory(cx);
127 0 : return false;
128 : }
129 :
130 341257 : child->setParent(parent);
131 341257 : return true;
132 : }
133 :
134 : void
135 44584 : Shape::removeChild(Shape *child)
136 : {
137 44584 : JS_ASSERT(!child->inDictionary());
138 :
139 44584 : KidsPointer *kidp = &kids;
140 :
141 44584 : if (kidp->isShape()) {
142 3372 : JS_ASSERT(kidp->toShape() == child);
143 3372 : kidp->setNull();
144 3372 : return;
145 : }
146 :
147 41212 : KidsHash *hash = kidp->toHash();
148 41212 : JS_ASSERT(hash->count() >= 2); /* otherwise kidp->isShape() should be true */
149 :
150 41212 : hash->remove(child);
151 :
152 41212 : if (hash->count() == 1) {
153 : /* Convert from HASH form back to SHAPE form. */
154 26111 : KidsHash::Range r = hash->all();
155 26111 : Shape *otherChild = r.front();
156 26111 : JS_ASSERT((r.popFront(), r.empty())); /* No more elements! */
157 26111 : kidp->setShape(otherChild);
158 26111 : js::UnwantedForeground::delete_(hash);
159 : }
160 : }
161 :
162 : /*
163 : * We need a read barrier for the shape tree, since these are weak pointers.
164 : */
165 : static Shape *
166 19719003 : ReadBarrier(Shape *shape)
167 : {
168 : #ifdef JSGC_INCREMENTAL
169 19719003 : JSCompartment *comp = shape->compartment();
170 19719003 : if (comp->needsBarrier()) {
171 1059 : Shape *tmp = shape;
172 1059 : MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
173 1059 : JS_ASSERT(tmp == shape);
174 : }
175 : #endif
176 19719003 : return shape;
177 : }
178 :
179 : Shape *
180 33218445 : PropertyTree::getChild(JSContext *cx, Shape *parent, uint32_t nfixed, const StackShape &child)
181 : {
182 : Shape *shape;
183 :
184 33218445 : JS_ASSERT(parent);
185 :
186 : /*
187 : * The property tree has extremely low fan-out below its root in
188 : * popular embeddings with real-world workloads. Patterns such as
189 : * defining closures that capture a constructor's environment as
190 : * getters or setters on the new object that is passed in as
191 : * |this| can significantly increase fan-out below the property
192 : * tree root -- see bug 335700 for details.
193 : */
194 33218445 : KidsPointer *kidp = &parent->kids;
195 33218445 : if (kidp->isShape()) {
196 16331889 : shape = kidp->toShape();
197 16331889 : if (shape->matches(child))
198 15623402 : return ReadBarrier(shape);
199 16886556 : } else if (kidp->isHash()) {
200 4436858 : shape = *kidp->toHash()->lookup(child);
201 4436858 : if (shape)
202 4095601 : return ReadBarrier(shape);
203 : } else {
204 : /* If kidp->isNull(), we always insert. */
205 : }
206 :
207 26998884 : RootStackShape childRoot(cx, &child);
208 26998884 : RootShape parentRoot(cx, &parent);
209 :
210 13499442 : shape = newShape(cx);
211 13499442 : if (!shape)
212 0 : return NULL;
213 :
214 13499442 : new (shape) Shape(child, nfixed);
215 :
216 13499442 : if (!insertChild(cx, parent, shape))
217 0 : return NULL;
218 :
219 13499442 : return shape;
220 : }
221 :
222 : void
223 20149251 : Shape::finalize(FreeOp *fop)
224 : {
225 20149251 : if (!inDictionary()) {
226 15359492 : if (parent && parent->isMarked())
227 44584 : parent->removeChild(this);
228 :
229 15359492 : if (kids.isHash())
230 682376 : fop->delete_(kids.toHash());
231 : }
232 20149251 : }
233 :
234 : #ifdef DEBUG
235 :
236 : void
237 0 : KidsPointer::checkConsistency(const Shape *aKid) const
238 : {
239 0 : if (isShape()) {
240 0 : JS_ASSERT(toShape() == aKid);
241 : } else {
242 0 : JS_ASSERT(isHash());
243 0 : KidsHash *hash = toHash();
244 0 : KidsHash::Ptr ptr = hash->lookup(aKid);
245 0 : JS_ASSERT(*ptr == aKid);
246 : }
247 0 : }
248 :
249 : void
250 0 : Shape::dump(JSContext *cx, FILE *fp) const
251 : {
252 0 : jsid propid = this->propid();
253 :
254 0 : JS_ASSERT(!JSID_IS_VOID(propid));
255 :
256 0 : if (JSID_IS_INT(propid)) {
257 0 : fprintf(fp, "[%ld]", (long) JSID_TO_INT(propid));
258 0 : } else if (JSID_IS_DEFAULT_XML_NAMESPACE(propid)) {
259 0 : fprintf(fp, "<default XML namespace>");
260 : } else {
261 : JSLinearString *str;
262 0 : if (JSID_IS_ATOM(propid)) {
263 0 : str = JSID_TO_ATOM(propid);
264 : } else {
265 0 : JS_ASSERT(JSID_IS_OBJECT(propid));
266 0 : JSString *s = ToStringSlow(cx, IdToValue(propid));
267 0 : fputs("object ", fp);
268 0 : str = s ? s->ensureLinear(cx) : NULL;
269 : }
270 0 : if (!str)
271 0 : fputs("<error>", fp);
272 : else
273 0 : FileEscapedString(fp, str, '"');
274 : }
275 :
276 : fprintf(fp, " g/s %p/%p slot %d attrs %x ",
277 0 : JS_FUNC_TO_DATA_PTR(void *, base()->rawGetter),
278 0 : JS_FUNC_TO_DATA_PTR(void *, base()->rawSetter),
279 0 : hasSlot() ? slot() : -1, attrs);
280 :
281 0 : if (attrs) {
282 0 : int first = 1;
283 0 : fputs("(", fp);
284 : #define DUMP_ATTR(name, display) if (attrs & JSPROP_##name) fputs(&(" " #display)[first], fp), first = 0
285 0 : DUMP_ATTR(ENUMERATE, enumerate);
286 0 : DUMP_ATTR(READONLY, readonly);
287 0 : DUMP_ATTR(PERMANENT, permanent);
288 0 : DUMP_ATTR(GETTER, getter);
289 0 : DUMP_ATTR(SETTER, setter);
290 0 : DUMP_ATTR(SHARED, shared);
291 : #undef DUMP_ATTR
292 0 : fputs(") ", fp);
293 : }
294 :
295 0 : fprintf(fp, "flags %x ", flags);
296 0 : if (flags) {
297 0 : int first = 1;
298 0 : fputs("(", fp);
299 : #define DUMP_FLAG(name, display) if (flags & name) fputs(&(" " #display)[first], fp), first = 0
300 0 : DUMP_FLAG(HAS_SHORTID, has_shortid);
301 0 : DUMP_FLAG(IN_DICTIONARY, in_dictionary);
302 : #undef DUMP_FLAG
303 0 : fputs(") ", fp);
304 : }
305 :
306 0 : fprintf(fp, "shortid %d\n", shortid());
307 0 : }
308 :
309 : void
310 0 : Shape::dumpSubtree(JSContext *cx, int level, FILE *fp) const
311 : {
312 0 : if (!parent) {
313 0 : JS_ASSERT(level == 0);
314 0 : JS_ASSERT(JSID_IS_EMPTY(propid_));
315 0 : fprintf(fp, "class %s emptyShape\n", getObjectClass()->name);
316 : } else {
317 0 : fprintf(fp, "%*sid ", level, "");
318 0 : dump(cx, fp);
319 : }
320 :
321 0 : if (!kids.isNull()) {
322 0 : ++level;
323 0 : if (kids.isShape()) {
324 0 : Shape *kid = kids.toShape();
325 0 : JS_ASSERT(kid->parent == this);
326 0 : kid->dumpSubtree(cx, level, fp);
327 : } else {
328 0 : const KidsHash &hash = *kids.toHash();
329 0 : for (KidsHash::Range range = hash.all(); !range.empty(); range.popFront()) {
330 0 : Shape *kid = range.front();
331 :
332 0 : JS_ASSERT(kid->parent == this);
333 0 : kid->dumpSubtree(cx, level, fp);
334 : }
335 : }
336 : }
337 0 : }
338 :
339 : void
340 38427 : js::PropertyTree::dumpShapes(JSRuntime *rt)
341 : {
342 : static bool init = false;
343 : static FILE *dumpfp = NULL;
344 38427 : if (!init) {
345 18667 : init = true;
346 18667 : const char *name = getenv("JS_DUMP_SHAPES_FILE");
347 18667 : if (!name)
348 18667 : return;
349 0 : dumpfp = fopen(name, "a");
350 : }
351 :
352 19760 : if (!dumpfp)
353 19760 : return;
354 :
355 0 : fprintf(dumpfp, "rt->gcNumber = %lu", (unsigned long)rt->gcNumber);
356 :
357 0 : for (gc::GCCompartmentsIter c(rt); !c.done(); c.next()) {
358 0 : fprintf(dumpfp, "*** Compartment %p ***\n", (void *)c.get());
359 :
360 : /*
361 : typedef JSCompartment::EmptyShapeSet HS;
362 : HS &h = c->emptyShapes;
363 : for (HS::Range r = h.all(); !r.empty(); r.popFront()) {
364 : Shape *empty = r.front();
365 : empty->dumpSubtree(rt, 0, dumpfp);
366 : putc('\n', dumpfp);
367 : }
368 : */
369 : }
370 : }
371 : #endif
|