1 : /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=98:
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 Mozilla Communicator client code, released
18 : * March 31, 1998.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Netscape Communications Corporation.
22 : * Portions created by the Initial Developer are Copyright (C) 1998
23 : * the Initial Developer. All Rights Reserved.
24 : *
25 : * Contributor(s):
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 : #include "jspropertycache.h"
42 : #include "jscntxt.h"
43 : #include "jsnum.h"
44 : #include "jsobjinlines.h"
45 : #include "jsopcodeinlines.h"
46 : #include "jspropertycacheinlines.h"
47 :
48 : using namespace js;
49 :
50 : PropertyCacheEntry *
51 18783779 : PropertyCache::fill(JSContext *cx, JSObject *obj, unsigned scopeIndex, JSObject *pobj,
52 : const Shape *shape)
53 : {
54 18783779 : JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
55 18783779 : JS_ASSERT(!cx->runtime->gcRunning);
56 :
57 : /*
58 : * Check for fill from js_SetPropertyHelper where the setter removed shape
59 : * from pobj (via unwatch or delete, e.g.).
60 : */
61 18783779 : if (!pobj->nativeContains(cx, *shape)) {
62 : PCMETER(oddfills++);
63 0 : return JS_NO_PROP_CACHE_FILL;
64 : }
65 :
66 : /*
67 : * Check for overdeep scope and prototype chain. Because resolve, getter,
68 : * and setter hooks can change the prototype chain using JS_SetPrototype
69 : * after LookupPropertyWithFlags has returned, we calculate the protoIndex
70 : * here and not in LookupPropertyWithFlags.
71 : *
72 : * The scopeIndex can't be wrong. We require JS_SetParent calls to happen
73 : * before any running script might consult a parent-linked scope chain. If
74 : * this requirement is not satisfied, the fill in progress will never hit,
75 : * but scope shape tests ensure nothing malfunctions.
76 : */
77 18783779 : JS_ASSERT_IF(obj == pobj, scopeIndex == 0);
78 :
79 18783779 : JSObject *tmp = obj;
80 20502726 : for (unsigned i = 0; i < scopeIndex; i++)
81 1718947 : tmp = &tmp->asScope().enclosingScope();
82 :
83 18783779 : unsigned protoIndex = 0;
84 39646913 : while (tmp != pobj) {
85 : /*
86 : * Don't cache entries across prototype lookups which can mutate in
87 : * arbitrary ways without a shape change.
88 : */
89 2288364 : if (tmp->hasUncacheableProto()) {
90 : PCMETER(noprotos++);
91 208960 : return JS_NO_PROP_CACHE_FILL;
92 : }
93 :
94 2079404 : tmp = tmp->getProto();
95 :
96 : /*
97 : * We cannot cache properties coming from native objects behind
98 : * non-native ones on the prototype chain. The non-natives can
99 : * mutate in arbitrary way without changing any shapes.
100 : */
101 2079404 : if (!tmp || !tmp->isNative()) {
102 : PCMETER(noprotos++);
103 49 : return JS_NO_PROP_CACHE_FILL;
104 : }
105 2079355 : ++protoIndex;
106 : }
107 :
108 : typedef PropertyCacheEntry Entry;
109 18574770 : if (scopeIndex > Entry::MaxScopeIndex || protoIndex > Entry::MaxProtoIndex) {
110 : PCMETER(longchains++);
111 352 : return JS_NO_PROP_CACHE_FILL;
112 : }
113 :
114 : /*
115 : * Optimize the cached vword based on our parameters and the current pc's
116 : * opcode format flags.
117 : */
118 : jsbytecode *pc;
119 18574418 : (void) cx->stack.currentScript(&pc);
120 18574418 : JSOp op = JSOp(*pc);
121 18574418 : const JSCodeSpec *cs = &js_CodeSpec[op];
122 :
123 18574418 : if ((cs->format & JOF_SET) && obj->watched())
124 1253 : return JS_NO_PROP_CACHE_FILL;
125 :
126 18573165 : if (obj == pobj) {
127 15901152 : JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
128 : } else {
129 : #ifdef DEBUG
130 2672013 : if (scopeIndex == 0) {
131 1572611 : JS_ASSERT(protoIndex != 0);
132 1572611 : JS_ASSERT((protoIndex == 1) == (obj->getProto() == pobj));
133 : }
134 : #endif
135 :
136 2672013 : if (scopeIndex != 0 || protoIndex != 1) {
137 : /*
138 : * Make sure that a later shadowing assignment will enter
139 : * PurgeProtoChain and invalidate this entry, bug 479198.
140 : */
141 1429170 : if (!obj->isDelegate())
142 1373935 : return JS_NO_PROP_CACHE_FILL;
143 : }
144 : }
145 :
146 17199230 : PropertyCacheEntry *entry = &table[hash(pc, obj->lastProperty())];
147 : PCMETER(entry->vword.isNull() || recycles++);
148 17199230 : entry->assign(pc, obj->lastProperty(), pobj->lastProperty(), shape, scopeIndex, protoIndex);
149 :
150 17199230 : empty = false;
151 : PCMETER(fills++);
152 :
153 : /*
154 : * The modfills counter is not exact. It increases if a getter or setter
155 : * recurse into the interpreter.
156 : */
157 : PCMETER(entry == pctestentry || modfills++);
158 : PCMETER(pctestentry = NULL);
159 17199230 : return entry;
160 : }
161 :
162 : PropertyName *
163 23861499 : PropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp,
164 : PropertyCacheEntry *entry)
165 : {
166 : JSObject *obj, *pobj, *tmp;
167 : #ifdef DEBUG
168 23861499 : JSScript *script = cx->stack.currentScript();
169 : #endif
170 :
171 23861499 : JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
172 23861499 : JS_ASSERT(uint32_t(pc - script->code) < script->length);
173 :
174 23861499 : JSOp op = JSOp(*pc);
175 23861499 : const JSCodeSpec &cs = js_CodeSpec[op];
176 :
177 23861499 : obj = *objp;
178 :
179 23861499 : if (entry->kpc != pc) {
180 : PCMETER(kpcmisses++);
181 :
182 23143772 : PropertyName *name = GetNameFromBytecode(cx, pc, op, cs);
183 : #ifdef DEBUG_notme
184 : JSAutoByteString printable;
185 : fprintf(stderr,
186 : "id miss for %s from %s:%u"
187 : " (pc %u, kpc %u, kshape %p, shape %p)\n",
188 : js_AtomToPrintableString(cx, name, &printable),
189 : script->filename,
190 : js_PCToLineNumber(cx, script, pc),
191 : pc - script->code,
192 : entry->kpc - script->code,
193 : entry->kshape,
194 : obj->lastProperty());
195 : js_Disassemble1(cx, script, pc,
196 : pc - script->code,
197 : JS_FALSE, stderr);
198 : #endif
199 :
200 23143772 : return name;
201 : }
202 :
203 717727 : if (entry->kshape != obj->lastProperty()) {
204 : PCMETER(kshapemisses++);
205 553406 : return GetNameFromBytecode(cx, pc, op, cs);
206 : }
207 :
208 : /*
209 : * PropertyCache::test handles only the direct and immediate-prototype hit
210 : * cases. All others go here.
211 : */
212 164321 : pobj = obj;
213 :
214 164321 : if (JOF_MODE(cs.format) == JOF_NAME) {
215 151458 : uint8_t scopeIndex = entry->scopeIndex;
216 526853 : while (scopeIndex > 0) {
217 223937 : tmp = pobj->enclosingScope();
218 223937 : if (!tmp || !tmp->isNative())
219 0 : break;
220 223937 : pobj = tmp;
221 223937 : scopeIndex--;
222 : }
223 :
224 151458 : *objp = pobj;
225 : }
226 :
227 164321 : uint8_t protoIndex = entry->protoIndex;
228 341597 : while (protoIndex > 0) {
229 12955 : tmp = pobj->getProto();
230 12955 : if (!tmp || !tmp->isNative())
231 0 : break;
232 12955 : pobj = tmp;
233 12955 : protoIndex--;
234 : }
235 :
236 164321 : if (pobj->lastProperty() == entry->pshape) {
237 : #ifdef DEBUG
238 120733 : PropertyName *name = GetNameFromBytecode(cx, pc, op, cs);
239 120733 : JS_ASSERT(pobj->nativeContains(cx, js_CheckForStringIndex(ATOM_TO_JSID(name))));
240 : #endif
241 120733 : *pobjp = pobj;
242 120733 : return NULL;
243 : }
244 :
245 : PCMETER(vcapmisses++);
246 43588 : return GetNameFromBytecode(cx, pc, op, cs);
247 : }
248 :
249 : #ifdef DEBUG
250 : void
251 18726 : PropertyCache::assertEmpty()
252 : {
253 18726 : JS_ASSERT(empty);
254 76720422 : for (unsigned i = 0; i < SIZE; i++) {
255 76701696 : JS_ASSERT(!table[i].kpc);
256 76701696 : JS_ASSERT(!table[i].kshape);
257 76701696 : JS_ASSERT(!table[i].pshape);
258 76701696 : JS_ASSERT(!table[i].prop);
259 76701696 : JS_ASSERT(!table[i].scopeIndex);
260 76701696 : JS_ASSERT(!table[i].protoIndex);
261 : }
262 18726 : }
263 : #endif
264 :
265 : void
266 52591 : PropertyCache::purge(JSRuntime *rt)
267 : {
268 52591 : if (empty) {
269 18726 : assertEmpty();
270 18726 : return;
271 : }
272 :
273 33865 : PodArrayZero(table);
274 33865 : empty = true;
275 :
276 : #ifdef JS_PROPERTY_CACHE_METERING
277 : { static FILE *fp;
278 : if (!fp)
279 : fp = fopen("/tmp/propcache.stats", "w");
280 : if (fp) {
281 : fputs("Property cache stats for ", fp);
282 : fprintf(fp, "GC %lu\n", (unsigned long)rt->gcNumber);
283 :
284 : # define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)mem)
285 : P(fills);
286 : P(nofills);
287 : P(rofills);
288 : P(disfills);
289 : P(oddfills);
290 : P(add2dictfills);
291 : P(modfills);
292 : P(brandfills);
293 : P(noprotos);
294 : P(longchains);
295 : P(recycles);
296 : P(tests);
297 : P(pchits);
298 : P(protopchits);
299 : P(initests);
300 : P(inipchits);
301 : P(inipcmisses);
302 : P(settests);
303 : P(addpchits);
304 : P(setpchits);
305 : P(setpcmisses);
306 : P(setmisses);
307 : P(kpcmisses);
308 : P(kshapemisses);
309 : P(vcapmisses);
310 : P(misses);
311 : P(flushes);
312 : P(pcpurges);
313 : # undef P
314 :
315 : fprintf(fp, "hit rates: pc %g%% (proto %g%%), set %g%%, ini %g%%, full %g%%\n",
316 : (100. * pchits) / tests,
317 : (100. * protopchits) / tests,
318 : (100. * (addpchits + setpchits))
319 : / settests,
320 : (100. * inipchits) / initests,
321 : (100. * (tests - misses)) / tests);
322 : fflush(fp);
323 : }
324 : }
325 : #endif
326 :
327 : PCMETER(flushes++);
328 : }
329 :
330 : void
331 0 : PropertyCache::restore(PropertyCacheEntry *entry)
332 : {
333 : PropertyCacheEntry *entry2;
334 :
335 0 : empty = false;
336 :
337 0 : entry2 = &table[hash(entry->kpc, entry->kshape)];
338 0 : *entry2 = *entry;
339 0 : }
|