1 : /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99 ft=cpp:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is SpiderMonkey JavaScript engine.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Mozilla Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 2011
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Chris Leary <cdleary@mozilla.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #ifndef ParseMaps_h__
42 : #define ParseMaps_h__
43 :
44 : #include "mozilla/Attributes.h"
45 :
46 : #include "ds/InlineMap.h"
47 : #include "js/HashTable.h"
48 : #include "js/Vector.h"
49 :
50 : namespace js {
51 :
52 : struct Definition;
53 :
54 : /*
55 : * A pool that permits the reuse of the backing storage for the defn, index, or
56 : * defn-or-header (multi) maps.
57 : *
58 : * The pool owns all the maps that are given out, and is responsible for
59 : * relinquishing all resources when |purgeAll| is triggered.
60 : */
61 : class ParseMapPool
62 : {
63 : typedef Vector<void *, 32, SystemAllocPolicy> RecyclableMaps;
64 :
65 : RecyclableMaps all;
66 : RecyclableMaps recyclable;
67 : JSContext *cx;
68 :
69 : void checkInvariants();
70 :
71 3435852 : void recycle(void *map) {
72 3435852 : JS_ASSERT(map);
73 : #ifdef DEBUG
74 3435852 : bool ok = false;
75 : /* Make sure the map is in |all| but not already in |recyclable|. */
76 73271492 : for (void **it = all.begin(), **end = all.end(); it != end; ++it) {
77 73271492 : if (*it == map) {
78 3435852 : ok = true;
79 3435852 : break;
80 : }
81 : }
82 3435852 : JS_ASSERT(ok);
83 137514019 : for (void **it = recyclable.begin(), **end = recyclable.end(); it != end; ++it)
84 134078167 : JS_ASSERT(*it != map);
85 : #endif
86 3435852 : JS_ASSERT(recyclable.length() < all.length());
87 3435852 : recyclable.infallibleAppend(map); /* Reserved in allocateFresh. */
88 3435852 : }
89 :
90 : void *allocateFresh();
91 : void *allocate();
92 :
93 : /* Arbitrary atom map type, that has keys and values of the same kind. */
94 : typedef AtomIndexMap AtomMapT;
95 :
96 3436438 : static AtomMapT *asAtomMap(void *ptr) {
97 3436438 : return reinterpret_cast<AtomMapT *>(ptr);
98 : }
99 :
100 : public:
101 22498 : explicit ParseMapPool(JSContext *cx) : cx(cx) {}
102 :
103 44996 : ~ParseMapPool() {
104 22498 : purgeAll();
105 22498 : }
106 :
107 : void purgeAll();
108 :
109 : bool empty() const {
110 : return all.empty();
111 : }
112 :
113 : /* Fallibly aquire one of the supported map types from the pool. */
114 : template <typename T>
115 : T *acquire();
116 :
117 : /* Release one of the supported map types back to the pool. */
118 :
119 1206130 : void release(AtomIndexMap *map) {
120 1206130 : recycle((void *) map);
121 1206130 : }
122 :
123 1114568 : void release(AtomDefnMap *map) {
124 1114568 : recycle((void *) map);
125 1114568 : }
126 :
127 1115154 : void release(AtomDOHMap *map) {
128 1115154 : recycle((void *) map);
129 1115154 : }
130 : }; /* ParseMapPool */
131 :
132 : /*
133 : * N.B. This is a POD-type so that it can be included in the ParseNode union.
134 : * If possible, use the corresponding |OwnedAtomThingMapPtr| variant.
135 : */
136 : template <class Map>
137 : struct AtomThingMapPtr
138 5422640 : {
139 : Map *map_;
140 :
141 6532354 : void init() { clearMap(); }
142 :
143 2441778 : bool ensureMap(JSContext *cx);
144 6233903 : void releaseMap(JSContext *cx);
145 :
146 2799300 : bool hasMap() const { return map_; }
147 1109121 : Map *getMap() { return map_; }
148 : void setMap(Map *newMap) { JS_ASSERT(!map_); map_ = newMap; }
149 8155259 : void clearMap() { map_ = NULL; }
150 :
151 80491138 : Map *operator->() { return map_; }
152 83617 : const Map *operator->() const { return map_; }
153 : Map &operator*() const { return *map_; }
154 : };
155 :
156 : struct AtomDefnMapPtr : public AtomThingMapPtr<AtomDefnMap>
157 2093498 : {
158 : JS_ALWAYS_INLINE
159 3533094 : Definition *lookupDefn(JSAtom *atom) {
160 3533094 : AtomDefnMap::Ptr p = map_->lookup(atom);
161 3533094 : return p ? p.value() : NULL;
162 : }
163 : };
164 :
165 : typedef AtomThingMapPtr<AtomIndexMap> AtomIndexMapPtr;
166 :
167 : /*
168 : * Wrapper around an AtomThingMapPtr (or its derivatives) that automatically
169 : * releases a map on destruction, if one has been acquired.
170 : */
171 : template <typename AtomThingMapPtrT>
172 : class OwnedAtomThingMapPtr : public AtomThingMapPtrT
173 : {
174 : JSContext *cx;
175 :
176 : public:
177 5422640 : explicit OwnedAtomThingMapPtr(JSContext *cx) : cx(cx) {
178 5422640 : AtomThingMapPtrT::init();
179 5422640 : }
180 :
181 5422640 : ~OwnedAtomThingMapPtr() {
182 5422640 : AtomThingMapPtrT::releaseMap(cx);
183 5422640 : }
184 : };
185 :
186 : typedef OwnedAtomThingMapPtr<AtomDefnMapPtr> OwnedAtomDefnMapPtr;
187 : typedef OwnedAtomThingMapPtr<AtomIndexMapPtr> OwnedAtomIndexMapPtr;
188 :
189 : /* Node structure for chaining in AtomDecls. */
190 : struct AtomDeclNode
191 : {
192 : Definition *defn;
193 : AtomDeclNode *next;
194 :
195 754363 : explicit AtomDeclNode(Definition *defn)
196 754363 : : defn(defn), next(NULL)
197 754363 : {}
198 : };
199 :
200 : /*
201 : * Tagged union of a Definition and an AtomDeclNode, for use in AtomDecl's
202 : * internal map.
203 : */
204 : class DefnOrHeader
205 : {
206 : union {
207 : Definition *defn;
208 : AtomDeclNode *head;
209 : uintptr_t bits;
210 : } u;
211 :
212 : public:
213 2473773 : DefnOrHeader() {
214 2473773 : u.bits = 0;
215 2473773 : }
216 :
217 2561368 : explicit DefnOrHeader(Definition *defn) {
218 2561368 : u.defn = defn;
219 2561368 : JS_ASSERT(!isHeader());
220 2561368 : }
221 :
222 756768 : explicit DefnOrHeader(AtomDeclNode *node) {
223 756768 : u.head = node;
224 756768 : u.bits |= 0x1;
225 756768 : JS_ASSERT(isHeader());
226 756768 : }
227 :
228 19715360 : bool isHeader() const {
229 19715360 : return u.bits & 0x1;
230 : }
231 :
232 6519616 : Definition *defn() const {
233 6519616 : JS_ASSERT(!isHeader());
234 6519616 : return u.defn;
235 : }
236 :
237 1678962 : AtomDeclNode *header() const {
238 1678962 : JS_ASSERT(isHeader());
239 1678962 : return (AtomDeclNode *) (u.bits & ~0x1);
240 : }
241 :
242 : #ifdef DEBUG
243 : void dump();
244 : #endif
245 : };
246 :
247 : namespace tl {
248 :
249 : template <> struct IsPodType<DefnOrHeader> {
250 : static const bool result = true;
251 : };
252 :
253 : } /* namespace tl */
254 :
255 : /*
256 : * Multimap for function-scope atom declarations.
257 : *
258 : * Wraps an internal DeclOrHeader map with multi-map functionality.
259 : *
260 : * In the common case, no block scoping is used, and atoms have a single
261 : * associated definition. In the uncommon (block scoping) case, we map the atom
262 : * to a chain of definition nodes.
263 : */
264 : class AtomDecls
265 : {
266 : /* AtomDeclsIter needs to get at the DOHMap directly. */
267 : friend class AtomDeclsIter;
268 :
269 : JSContext *cx;
270 : AtomDOHMap *map;
271 :
272 : AtomDecls(const AtomDecls &other) MOZ_DELETE;
273 : void operator=(const AtomDecls &other) MOZ_DELETE;
274 :
275 : AtomDeclNode *allocNode(Definition *defn);
276 :
277 : /*
278 : * Fallibly return the value in |doh| as a node.
279 : * Update the defn currently occupying |doh| to a node if necessary.
280 : */
281 : AtomDeclNode *lastAsNode(DefnOrHeader *doh);
282 :
283 : public:
284 2093498 : explicit AtomDecls(JSContext *cx)
285 2093498 : : cx(cx), map(NULL)
286 2093498 : {}
287 :
288 : ~AtomDecls();
289 :
290 : bool init();
291 :
292 : void clear() {
293 : map->clear();
294 : }
295 :
296 : /* Return the definition at the head of the chain for |atom|. */
297 : inline Definition *lookupFirst(JSAtom *atom);
298 :
299 : /* Perform a lookup that can iterate over the definitions associated with |atom|. */
300 : inline MultiDeclRange lookupMulti(JSAtom *atom);
301 :
302 : /* Add-or-update a known-unique definition for |atom|. */
303 : inline bool addUnique(JSAtom *atom, Definition *defn);
304 : bool addShadow(JSAtom *atom, Definition *defn);
305 : bool addHoist(JSAtom *atom, Definition *defn);
306 :
307 : /* Updating the definition for an entry that is known to exist is infallible. */
308 68 : void updateFirst(JSAtom *atom, Definition *defn) {
309 68 : JS_ASSERT(map);
310 68 : AtomDOHMap::Ptr p = map->lookup(atom);
311 68 : JS_ASSERT(p);
312 68 : if (p.value().isHeader())
313 18 : p.value().header()->defn = defn;
314 : else
315 50 : p.value() = DefnOrHeader(defn);
316 68 : }
317 :
318 : /* Remove the node at the head of the chain for |atom|. */
319 750968 : void remove(JSAtom *atom) {
320 750968 : JS_ASSERT(map);
321 750968 : AtomDOHMap::Ptr p = map->lookup(atom);
322 750968 : if (!p)
323 0 : return;
324 :
325 750968 : DefnOrHeader &doh = p.value();
326 750968 : if (!doh.isHeader()) {
327 0 : map->remove(p);
328 0 : return;
329 : }
330 :
331 750968 : AtomDeclNode *node = doh.header();
332 750968 : AtomDeclNode *newHead = node->next;
333 750968 : if (newHead)
334 5764 : p.value() = DefnOrHeader(newHead);
335 : else
336 745204 : map->remove(p);
337 : }
338 :
339 265193 : AtomDOHMap::Range all() {
340 265193 : JS_ASSERT(map);
341 265193 : return map->all();
342 : }
343 :
344 : #ifdef DEBUG
345 : void dump();
346 : #endif
347 : };
348 :
349 : /*
350 : * Lookup state tracker for those situations where the caller wants to traverse
351 : * multiple definitions associated with a single atom. This occurs due to block
352 : * scoping.
353 : */
354 : class MultiDeclRange
355 : {
356 : friend class AtomDecls;
357 :
358 : AtomDeclNode *node;
359 : Definition *defn;
360 :
361 14064148 : explicit MultiDeclRange(Definition *defn) : node(NULL), defn(defn) {}
362 899969 : explicit MultiDeclRange(AtomDeclNode *node) : node(node), defn(node->defn) {}
363 :
364 : public:
365 9 : void popFront() {
366 9 : JS_ASSERT(!empty());
367 9 : if (!node) {
368 0 : defn = NULL;
369 0 : return;
370 : }
371 9 : node = node->next;
372 9 : defn = node ? node->defn : NULL;
373 : }
374 :
375 5678822 : Definition *front() {
376 5678822 : JS_ASSERT(!empty());
377 5678822 : return defn;
378 : }
379 :
380 21782021 : bool empty() const {
381 21782021 : JS_ASSERT_IF(!defn, !node);
382 21782021 : return !defn;
383 : }
384 : };
385 :
386 : /* Iterates over all the definitions in an AtomDecls. */
387 : class AtomDeclsIter
388 : {
389 : AtomDOHMap::Range r; /* Range over the map. */
390 : AtomDeclNode *link; /* Optional next node in the current atom's chain. */
391 :
392 : public:
393 265193 : explicit AtomDeclsIter(AtomDecls *decls) : r(decls->all()), link(NULL) {}
394 :
395 896765 : Definition *operator()() {
396 896765 : if (link) {
397 0 : JS_ASSERT(link != link->next);
398 0 : Definition *result = link->defn;
399 0 : link = link->next;
400 0 : JS_ASSERT(result);
401 0 : return result;
402 : }
403 :
404 896765 : if (r.empty())
405 258512 : return NULL;
406 :
407 638253 : const DefnOrHeader &doh = r.front().value();
408 638253 : r.popFront();
409 :
410 638253 : if (!doh.isHeader())
411 637558 : return doh.defn();
412 :
413 695 : JS_ASSERT(!link);
414 695 : AtomDeclNode *node = doh.header();
415 695 : link = node->next;
416 695 : return node->defn;
417 : }
418 : };
419 :
420 : typedef AtomDefnMap::Range AtomDefnRange;
421 : typedef AtomDefnMap::AddPtr AtomDefnAddPtr;
422 : typedef AtomDefnMap::Ptr AtomDefnPtr;
423 : typedef AtomIndexMap::AddPtr AtomIndexAddPtr;
424 : typedef AtomIndexMap::Ptr AtomIndexPtr;
425 : typedef AtomDOHMap::Ptr AtomDOHPtr;
426 : typedef AtomDOHMap::AddPtr AtomDOHAddPtr;
427 : typedef AtomDOHMap::Range AtomDOHRange;
428 :
429 : } /* namepsace js */
430 :
431 : #endif
|