Line data Source code
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_Caches_h
8 : #define vm_Caches_h
9 :
10 : #include "jsatom.h"
11 : #include "jsbytecode.h"
12 : #include "jsmath.h"
13 : #include "jsobj.h"
14 : #include "jsscript.h"
15 :
16 : #include "ds/FixedSizeHash.h"
17 : #include "frontend/SourceNotes.h"
18 : #include "gc/Tracer.h"
19 : #include "js/RootingAPI.h"
20 : #include "js/UniquePtr.h"
21 : #include "vm/NativeObject.h"
22 :
23 : namespace js {
24 :
25 : /*
26 : * GetSrcNote cache to avoid O(n^2) growth in finding a source note for a
27 : * given pc in a script. We use the script->code pointer to tag the cache,
28 : * instead of the script address itself, so that source notes are always found
29 : * by offset from the bytecode with which they were generated.
30 : */
31 319 : struct GSNCache {
32 : typedef HashMap<jsbytecode*,
33 : jssrcnote*,
34 : PointerHasher<jsbytecode*, 0>,
35 : SystemAllocPolicy> Map;
36 :
37 : jsbytecode* code;
38 : Map map;
39 :
40 333 : GSNCache() : code(nullptr) { }
41 :
42 : void purge();
43 : };
44 :
45 : /*
46 : * EnvironmentCoordinateName cache to avoid O(n^2) growth in finding the name
47 : * associated with a given aliasedvar operation.
48 : */
49 169 : struct EnvironmentCoordinateNameCache {
50 : typedef HashMap<uint32_t,
51 : jsid,
52 : DefaultHasher<uint32_t>,
53 : SystemAllocPolicy> Map;
54 :
55 : Shape* shape;
56 : Map map;
57 :
58 183 : EnvironmentCoordinateNameCache() : shape(nullptr) {}
59 : void purge();
60 : };
61 :
62 : struct EvalCacheEntry
63 : {
64 : JSLinearString* str;
65 : JSScript* script;
66 : JSScript* callerScript;
67 : jsbytecode* pc;
68 : };
69 :
70 2 : struct EvalCacheLookup
71 : {
72 2 : explicit EvalCacheLookup(JSContext* cx) : str(cx), callerScript(cx) {}
73 : RootedLinearString str;
74 : RootedScript callerScript;
75 : JSVersion version;
76 : jsbytecode* pc;
77 : };
78 :
79 : struct EvalCacheHashPolicy
80 : {
81 : typedef EvalCacheLookup Lookup;
82 :
83 : static HashNumber hash(const Lookup& l);
84 : static bool match(const EvalCacheEntry& entry, const EvalCacheLookup& l);
85 : };
86 :
87 : typedef HashSet<EvalCacheEntry, EvalCacheHashPolicy, SystemAllocPolicy> EvalCache;
88 :
89 : struct LazyScriptHashPolicy
90 : {
91 : struct Lookup {
92 : JSContext* cx;
93 : LazyScript* lazy;
94 :
95 2423 : Lookup(JSContext* cx, LazyScript* lazy)
96 2423 : : cx(cx), lazy(lazy)
97 2423 : {}
98 : };
99 :
100 : static const size_t NumHashes = 3;
101 :
102 : static void hash(const Lookup& lookup, HashNumber hashes[NumHashes]);
103 : static bool match(JSScript* script, const Lookup& lookup);
104 :
105 : // Alternate methods for use when removing scripts from the hash without an
106 : // explicit LazyScript lookup.
107 : static void hash(JSScript* script, HashNumber hashes[NumHashes]);
108 0 : static bool match(JSScript* script, JSScript* lookup) { return script == lookup; }
109 :
110 0 : static void clear(JSScript** pscript) { *pscript = nullptr; }
111 4851 : static bool isCleared(JSScript* script) { return !script; }
112 : };
113 :
114 : typedef FixedSizeHashSet<JSScript*, LazyScriptHashPolicy, 769> LazyScriptCache;
115 :
116 : class PropertyIteratorObject;
117 :
118 : class NativeIterCache
119 : {
120 : static const size_t SIZE = size_t(1) << 8;
121 :
122 : /* Cached native iterators. */
123 : PropertyIteratorObject* data[SIZE];
124 :
125 143 : static size_t getIndex(uint32_t key) {
126 143 : return size_t(key) % SIZE;
127 : }
128 :
129 : public:
130 4 : NativeIterCache() {
131 4 : mozilla::PodArrayZero(data);
132 4 : }
133 :
134 1 : void purge() {
135 1 : mozilla::PodArrayZero(data);
136 1 : }
137 :
138 87 : PropertyIteratorObject* get(uint32_t key) const {
139 87 : return data[getIndex(key)];
140 : }
141 :
142 56 : void set(uint32_t key, PropertyIteratorObject* iterobj) {
143 56 : data[getIndex(key)] = iterobj;
144 56 : }
145 : };
146 :
147 : /*
148 : * Cache for speeding up repetitive creation of objects in the VM.
149 : * When an object is created which matches the criteria in the 'key' section
150 : * below, an entry is filled with the resulting object.
151 : */
152 : class NewObjectCache
153 : {
154 : /* Statically asserted to be equal to sizeof(JSObject_Slots16) */
155 : static const unsigned MAX_OBJ_SIZE = 4 * sizeof(void*) + 16 * sizeof(Value);
156 :
157 : static void staticAsserts() {
158 : JS_STATIC_ASSERT(NewObjectCache::MAX_OBJ_SIZE == sizeof(JSObject_Slots16));
159 : JS_STATIC_ASSERT(gc::AllocKind::OBJECT_LAST == gc::AllocKind::OBJECT16_BACKGROUND);
160 : }
161 :
162 : struct Entry
163 : {
164 : /* Class of the constructed object. */
165 : const Class* clasp;
166 :
167 : /*
168 : * Key with one of three possible values:
169 : *
170 : * - Global for the object. The object must have a standard class for
171 : * which the global's prototype can be determined, and the object's
172 : * parent will be the global.
173 : *
174 : * - Prototype for the object (cannot be global). The object's parent
175 : * will be the prototype's parent.
176 : *
177 : * - Type for the object. The object's parent will be the type's
178 : * prototype's parent.
179 : */
180 : gc::Cell* key;
181 :
182 : /* Allocation kind for the constructed object. */
183 : gc::AllocKind kind;
184 :
185 : /* Number of bytes to copy from the template object. */
186 : uint32_t nbytes;
187 :
188 : /*
189 : * Template object to copy from, with the initial values of fields,
190 : * fixed slots (undefined) and private data (nullptr).
191 : */
192 : char templateObject[MAX_OBJ_SIZE];
193 : };
194 :
195 : Entry entries[41]; // TODO: reconsider size
196 :
197 : public:
198 :
199 : typedef int EntryIndex;
200 :
201 4 : NewObjectCache() { mozilla::PodZero(this); }
202 1 : void purge() { mozilla::PodZero(this); }
203 :
204 : /* Remove any cached items keyed on moved objects. */
205 : void clearNurseryObjects(JSRuntime* rt);
206 :
207 : /*
208 : * Get the entry index for the given lookup, return whether there was a hit
209 : * on an existing entry.
210 : */
211 : inline bool lookupProto(const Class* clasp, JSObject* proto, gc::AllocKind kind, EntryIndex* pentry);
212 : inline bool lookupGlobal(const Class* clasp, js::GlobalObject* global, gc::AllocKind kind,
213 : EntryIndex* pentry);
214 :
215 322 : bool lookupGroup(js::ObjectGroup* group, gc::AllocKind kind, EntryIndex* pentry) {
216 322 : return lookup(group->clasp(), group, kind, pentry);
217 : }
218 :
219 : /*
220 : * Return a new object from a cache hit produced by a lookup method, or
221 : * nullptr if returning the object could possibly trigger GC (does not
222 : * indicate failure).
223 : */
224 : inline NativeObject* newObjectFromHit(JSContext* cx, EntryIndex entry, js::gc::InitialHeap heap);
225 :
226 : /* Fill an entry after a cache miss. */
227 : void fillProto(EntryIndex entry, const Class* clasp, js::TaggedProto proto,
228 : gc::AllocKind kind, NativeObject* obj);
229 :
230 : inline void fillGlobal(EntryIndex entry, const Class* clasp, js::GlobalObject* global,
231 : gc::AllocKind kind, NativeObject* obj);
232 :
233 57 : void fillGroup(EntryIndex entry, js::ObjectGroup* group, gc::AllocKind kind,
234 : NativeObject* obj)
235 : {
236 57 : MOZ_ASSERT(obj->group() == group);
237 57 : return fill(entry, group->clasp(), group, kind, obj);
238 : }
239 :
240 : /* Invalidate any entries which might produce an object with shape/proto. */
241 : void invalidateEntriesForShape(JSContext* cx, HandleShape shape, HandleObject proto);
242 :
243 : private:
244 46057 : EntryIndex makeIndex(const Class* clasp, gc::Cell* key, gc::AllocKind kind) {
245 46057 : uintptr_t hash = (uintptr_t(clasp) ^ uintptr_t(key)) + size_t(kind);
246 46057 : return hash % mozilla::ArrayLength(entries);
247 : }
248 :
249 38965 : bool lookup(const Class* clasp, gc::Cell* key, gc::AllocKind kind, EntryIndex* pentry) {
250 38965 : *pentry = makeIndex(clasp, key, kind);
251 38965 : Entry* entry = &entries[*pentry];
252 :
253 : /* N.B. Lookups with the same clasp/key but different kinds map to different entries. */
254 38965 : return entry->clasp == clasp && entry->key == key;
255 : }
256 :
257 7089 : void fill(EntryIndex entry_, const Class* clasp, gc::Cell* key, gc::AllocKind kind,
258 : NativeObject* obj) {
259 7089 : MOZ_ASSERT(unsigned(entry_) < mozilla::ArrayLength(entries));
260 7089 : MOZ_ASSERT(entry_ == makeIndex(clasp, key, kind));
261 7089 : Entry* entry = &entries[entry_];
262 :
263 7089 : entry->clasp = clasp;
264 7089 : entry->key = key;
265 7089 : entry->kind = kind;
266 :
267 7089 : entry->nbytes = gc::Arena::thingSize(kind);
268 7089 : js_memcpy(&entry->templateObject, obj, entry->nbytes);
269 7089 : }
270 :
271 24321 : static void copyCachedToObject(NativeObject* dst, NativeObject* src, gc::AllocKind kind) {
272 24321 : js_memcpy(dst, src, gc::Arena::thingSize(kind));
273 24321 : Shape::writeBarrierPost(&dst->shape_, nullptr, dst->shape_);
274 24321 : ObjectGroup::writeBarrierPost(&dst->group_, nullptr, dst->group_);
275 24321 : }
276 : };
277 :
278 4 : class RuntimeCaches
279 : {
280 : UniquePtr<js::MathCache> mathCache_;
281 :
282 : js::MathCache* createMathCache(JSContext* cx);
283 :
284 : public:
285 : js::GSNCache gsnCache;
286 : js::EnvironmentCoordinateNameCache envCoordinateNameCache;
287 : js::NewObjectCache newObjectCache;
288 : js::NativeIterCache nativeIterCache;
289 : js::UncompressedSourceCache uncompressedSourceCache;
290 : js::EvalCache evalCache;
291 : LazyScriptCache lazyScriptCache;
292 :
293 : bool init();
294 :
295 0 : js::MathCache* getMathCache(JSContext* cx) {
296 0 : return mathCache_ ? mathCache_.get() : createMathCache(cx);
297 : }
298 0 : js::MathCache* maybeGetMathCache() {
299 0 : return mathCache_.get();
300 : }
301 : };
302 :
303 : } // namespace js
304 :
305 : #endif /* vm_Caches_h */
|