Parent Directory
|
Revision Log
|
Patch
revision 459 by siliconforks, Tue Dec 9 03:37:47 2008 UTC | revision 460 by siliconforks, Sat Sep 26 23:15:22 2009 UTC | |
---|---|---|
# | Line 1 | Line 1 |
1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- | /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 | * vim: set ts=8 sw=4 et tw=79: | * vim: set ts=8 sw=4 et tw=79: |
3 | * | * |
4 | * ***** BEGIN LICENSE BLOCK ***** | * ***** BEGIN LICENSE BLOCK ***** |
# | Line 54 | Line 54 |
54 | #include "jsatom.h" | #include "jsatom.h" |
55 | #include "jsbool.h" | #include "jsbool.h" |
56 | #include "jscntxt.h" | #include "jscntxt.h" |
57 | #include "jsdate.h" | |
58 | #include "jsversion.h" | #include "jsversion.h" |
59 | #include "jsdbgapi.h" | #include "jsdbgapi.h" |
60 | #include "jsfun.h" | #include "jsfun.h" |
# | Line 85 | Line 86 |
86 | #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ | #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ |
87 | ||
88 | uint32 | uint32 |
89 | js_GenerateShape(JSContext *cx, JSBool gcLocked, JSScopeProperty *sprop) | js_GenerateShape(JSContext *cx, JSBool gcLocked) |
90 | { | { |
91 | JSRuntime *rt; | JSRuntime *rt; |
92 | uint32 shape; | uint32 shape; |
JSTempValueRooter tvr; | ||
93 | ||
94 | rt = cx->runtime; | rt = cx->runtime; |
95 | shape = JS_ATOMIC_INCREMENT(&rt->shapeGen); | shape = JS_ATOMIC_INCREMENT(&rt->shapeGen); |
96 | JS_ASSERT(shape != 0); | JS_ASSERT(shape != 0); |
97 | if (shape & SHAPE_OVERFLOW_BIT) { | if (shape >= SHAPE_OVERFLOW_BIT) { |
98 | rt->gcPoke = JS_TRUE; | /* |
99 | if (sprop) | * FIXME bug 440834: The shape id space has overflowed. Currently we |
100 | JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); | * cope badly with this and schedule the GC on the every call. But |
101 | js_GC(cx, gcLocked ? GC_LOCK_HELD : GC_NORMAL); | * first we make sure that increments from other threads would not |
102 | if (sprop) | * have a chance to wrap around shapeGen to zero. |
103 | JS_POP_TEMP_ROOT(cx, &tvr); | */ |
104 | shape = JS_ATOMIC_INCREMENT(&rt->shapeGen); | rt->shapeGen = SHAPE_OVERFLOW_BIT; |
105 | JS_ASSERT(shape != 0); | js_TriggerGC(cx, gcLocked); |
JS_ASSERT_IF(shape & SHAPE_OVERFLOW_BIT, | ||
JS_PROPERTY_CACHE(cx).disabled); | ||
106 | } | } |
107 | return shape; | return shape; |
108 | } | } |
109 | ||
110 | void | JS_REQUIRES_STACK JSPropCacheEntry * |
111 | js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape, | js_FillPropertyCache(JSContext *cx, JSObject *obj, |
112 | uintN scopeIndex, uintN protoIndex, | uintN scopeIndex, uintN protoIndex, JSObject *pobj, |
113 | JSObject *pobj, JSScopeProperty *sprop, | JSScopeProperty *sprop, JSBool adding) |
JSPropCacheEntry **entryp) | ||
114 | { | { |
115 | JSPropertyCache *cache; | JSPropertyCache *cache; |
116 | jsbytecode *pc; | jsbytecode *pc; |
117 | JSScope *scope; | JSScope *scope; |
118 | jsuword kshape, vshape, khash; | |
119 | JSOp op; | JSOp op; |
120 | const JSCodeSpec *cs; | const JSCodeSpec *cs; |
121 | jsuword vword; | jsuword vword; |
122 | ptrdiff_t pcoff; | ptrdiff_t pcoff; |
jsuword khash; | ||
123 | JSAtom *atom; | JSAtom *atom; |
124 | JSPropCacheEntry *entry; | JSPropCacheEntry *entry; |
125 | ||
126 | JS_ASSERT(!cx->runtime->gcRunning); | JS_ASSERT(!cx->runtime->gcRunning); |
127 | cache = &JS_PROPERTY_CACHE(cx); | cache = &JS_PROPERTY_CACHE(cx); |
128 | pc = cx->fp->regs->pc; | |
129 | if (cache->disabled || (cx->fp->flags & JSFRAME_EVAL)) { | /* FIXME bug 489098: consider enabling the property cache for eval. */ |
130 | if (js_IsPropertyCacheDisabled(cx) || (cx->fp->flags & JSFRAME_EVAL)) { | |
131 | PCMETER(cache->disfills++); | PCMETER(cache->disfills++); |
132 | *entryp = NULL; | return JS_NO_PROP_CACHE_FILL; |
return; | ||
133 | } | } |
134 | ||
135 | /* | /* |
# | Line 143 | Line 140 |
140 | JS_ASSERT(scope->object == pobj); | JS_ASSERT(scope->object == pobj); |
141 | if (!SCOPE_HAS_PROPERTY(scope, sprop)) { | if (!SCOPE_HAS_PROPERTY(scope, sprop)) { |
142 | PCMETER(cache->oddfills++); | PCMETER(cache->oddfills++); |
143 | *entryp = NULL; | return JS_NO_PROP_CACHE_FILL; |
return; | ||
144 | } | } |
145 | ||
146 | /* | /* |
# | Line 161 | Line 157 |
157 | * but vcap vs. scope shape tests ensure nothing malfunctions. | * but vcap vs. scope shape tests ensure nothing malfunctions. |
158 | */ | */ |
159 | JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj); | JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj); |
160 | ||
161 | if (protoIndex != 0) { | if (protoIndex != 0) { |
162 | JSObject *tmp; | JSObject *tmp = obj; |
163 | ||
164 | for (uintN i = 0; i != scopeIndex; i++) | |
165 | tmp = OBJ_GET_PARENT(cx, tmp); | |
166 | JS_ASSERT(tmp != pobj); | |
167 | ||
JS_ASSERT(pobj != obj); | ||
168 | protoIndex = 1; | protoIndex = 1; |
tmp = obj; | ||
169 | for (;;) { | for (;;) { |
170 | tmp = OBJ_GET_PROTO(cx, tmp); | tmp = OBJ_GET_PROTO(cx, tmp); |
171 | if (!tmp) { | |
172 | /* | |
173 | * We cannot cache properties coming from native objects behind | |
174 | * non-native ones on the prototype chain. The non-natives can | |
175 | * mutate in arbitrary way without changing any shapes. | |
176 | */ | |
177 | if (!tmp || !OBJ_IS_NATIVE(tmp)) { | |
178 | PCMETER(cache->noprotos++); | PCMETER(cache->noprotos++); |
179 | *entryp = NULL; | return JS_NO_PROP_CACHE_FILL; |
return; | ||
180 | } | } |
181 | if (tmp == pobj) | if (tmp == pobj) |
182 | break; | break; |
183 | ++protoIndex; | ++protoIndex; |
184 | } | } |
185 | } | } |
186 | ||
187 | if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) { | if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) { |
188 | PCMETER(cache->longchains++); | PCMETER(cache->longchains++); |
189 | *entryp = NULL; | return JS_NO_PROP_CACHE_FILL; |
return; | ||
190 | } | } |
191 | ||
192 | /* | /* |
193 | * Optimize the cached vword based on our parameters and the current pc's | * Optimize the cached vword based on our parameters and the current pc's |
194 | * opcode format flags. | * opcode format flags. |
195 | */ | */ |
196 | op = (JSOp) *pc; | pc = cx->fp->regs->pc; |
197 | op = js_GetOpcode(cx, cx->fp->script, pc); | |
198 | cs = &js_CodeSpec[op]; | cs = &js_CodeSpec[op]; |
199 | kshape = 0; | |
200 | ||
201 | do { | do { |
202 | /* | /* |
# | Line 214 | Line 220 |
220 | * | * |
221 | * So here, on first cache fill for this method, we brand | * So here, on first cache fill for this method, we brand |
222 | * the scope with a new shape and set the SCOPE_BRANDED | * the scope with a new shape and set the SCOPE_BRANDED |
223 | * flag. Once this scope flag is set, any write that adds | * flag. Once this scope flag is set, any write that adds |
224 | * or deletes a function-valued plain old property in | * or deletes a function-valued plain old property in |
225 | * scope->object will result in shape being regenerated. | * scope->object will result in shape being regenerated. |
226 | */ | */ |
# | Line 222 | Line 228 |
228 | PCMETER(cache->brandfills++); | PCMETER(cache->brandfills++); |
229 | #ifdef DEBUG_notme | #ifdef DEBUG_notme |
230 | fprintf(stderr, | fprintf(stderr, |
231 | "branding %p (%s) for funobj %p (%s), kshape %lu\n", | "branding %p (%s) for funobj %p (%s), shape %lu\n", |
232 | pobj, LOCKED_OBJ_GET_CLASS(pobj)->name, | pobj, LOCKED_OBJ_GET_CLASS(pobj)->name, |
233 | JSVAL_TO_OBJECT(v), | JSVAL_TO_OBJECT(v), |
234 | JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx, | JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx, |
235 | JSVAL_TO_OBJECT(v))), | JSVAL_TO_OBJECT(v))), |
236 | kshape); | OBJ_SHAPE(obj)); |
237 | #endif | #endif |
238 | SCOPE_MAKE_UNIQUE_SHAPE(cx, scope); | js_MakeScopeShapeUnique(cx, scope); |
239 | if (js_IsPropertyCacheDisabled(cx)) { | |
240 | /* | |
241 | * js_GenerateShape could not recover from | |
242 | * rt->shapeGen's overflow. | |
243 | */ | |
244 | return JS_NO_PROP_CACHE_FILL; | |
245 | } | |
246 | SCOPE_SET_BRANDED(scope); | SCOPE_SET_BRANDED(scope); |
kshape = scope->shape; | ||
247 | } | } |
248 | vword = JSVAL_OBJECT_TO_PCVAL(v); | vword = JSVAL_OBJECT_TO_PCVAL(v); |
249 | break; | break; |
# | Line 240 | Line 252 |
252 | } | } |
253 | ||
254 | /* If getting a value via a stub getter, we can cache the slot. */ | /* If getting a value via a stub getter, we can cache the slot. */ |
255 | if (!(cs->format & JOF_SET) && | if (!(cs->format & (JOF_SET | JOF_INCDEC | JOF_FOR)) && |
256 | SPROP_HAS_STUB_GETTER(sprop) && | SPROP_HAS_STUB_GETTER(sprop) && |
257 | SPROP_HAS_VALID_SLOT(sprop, scope)) { | SPROP_HAS_VALID_SLOT(sprop, scope)) { |
258 | /* Great, let's cache sprop's slot and use it on cache hit. */ | /* Great, let's cache sprop's slot and use it on cache hit. */ |
# | Line 248 | Line 260 |
260 | } else { | } else { |
261 | /* Best we can do is to cache sprop (still a nice speedup). */ | /* Best we can do is to cache sprop (still a nice speedup). */ |
262 | vword = SPROP_TO_PCVAL(sprop); | vword = SPROP_TO_PCVAL(sprop); |
263 | if (adding && | |
264 | sprop == scope->lastProp && | |
265 | scope->shape == sprop->shape) { | |
266 | /* | |
267 | * Our caller added a new property. We also know that a setter | |
268 | * that js_NativeSet could have run has not mutated the scope | |
269 | * so the added property is still the last one added and the | |
270 | * scope is not branded. | |
271 | * | |
272 | * We want to cache under scope's shape before the property | |
273 | * addition to bias for the case when the mutator opcode | |
274 | * always adds the same property. It allows to optimize | |
275 | * periodic execution of object initializers or explicit | |
276 | * initialization sequences like | |
277 | * | |
278 | * obj = {}; obj.x = 1; obj.y = 2; | |
279 | * | |
280 | * We assume that on average the win from this optimization is | |
281 | * bigger that the cost of an extra mismatch per loop due to | |
282 | * the bias for the following case: | |
283 | * | |
284 | * obj = {}; ... for (...) { ... obj.x = ... } | |
285 | * | |
286 | * On the first iteration JSOP_SETPROP fills the cache with | |
287 | * the shape of newly created object, not the shape after | |
288 | * obj.x is assigned. That mismatches obj's shape on the | |
289 | * second iteration. Note that on third and the following | |
290 | * iterations the cache will be hit since the shape no longer | |
291 | * mutates. | |
292 | */ | |
293 | JS_ASSERT(scope->object == obj); | |
294 | if (sprop->parent) { | |
295 | kshape = sprop->parent->shape; | |
296 | } else { | |
297 | JSObject *proto = STOBJ_GET_PROTO(obj); | |
298 | if (proto && OBJ_IS_NATIVE(proto)) | |
299 | kshape = OBJ_SHAPE(proto); | |
300 | } | |
301 | ||
302 | /* | |
303 | * When adding we predict no prototype object will later gain a | |
304 | * readonly property or setter. | |
305 | */ | |
306 | vshape = cx->runtime->protoHazardShape; | |
307 | } | |
308 | } | } |
309 | } while (0); | } while (0); |
310 | ||
311 | /* | if (kshape == 0) { |
312 | * Our caller preserved the scope shape prior to the js_GetPropertyHelper | kshape = OBJ_SHAPE(obj); |
313 | * or similar call out of the interpreter. We want to cache under that | vshape = scope->shape; |
314 | * shape if op is overtly mutating, to bias for the case where the mutator | } |
* udpates shape predictably. | ||
* | ||
* Note that an apparently non-mutating op such as JSOP_NAME may still | ||
* mutate the base object via, e.g., lazy standard class initialization, | ||
* but that is a one-time event and we'll have to miss the old shape and | ||
* re-fill under the new one. | ||
*/ | ||
if (!(cs->format & (JOF_SET | JOF_INCDEC)) && obj == pobj) | ||
kshape = scope->shape; | ||
315 | ||
316 | khash = PROPERTY_CACHE_HASH_PC(pc, kshape); | khash = PROPERTY_CACHE_HASH_PC(pc, kshape); |
317 | if (obj == pobj) { | if (obj == pobj) { |
JS_ASSERT(kshape != 0 || scope->shape != 0); | ||
318 | JS_ASSERT(scopeIndex == 0 && protoIndex == 0); | JS_ASSERT(scopeIndex == 0 && protoIndex == 0); |
319 | JS_ASSERT(OBJ_SCOPE(obj)->object == obj); | JS_ASSERT(OBJ_SCOPE(obj)->object == obj); |
320 | JS_ASSERT(kshape != 0); | |
321 | } else { | } else { |
322 | if (op == JSOP_LENGTH) { | if (op == JSOP_LENGTH) { |
323 | atom = cx->runtime->atomState.lengthAtom; | atom = cx->runtime->atomState.lengthAtom; |
324 | } else { | } else { |
325 | pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0; | pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0; |
326 | GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom); | GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom); |
327 | } | } |
328 | JS_ASSERT_IF(scopeIndex == 0, | |
329 | protoIndex != 1 || OBJ_GET_PROTO(cx, obj) == pobj); | #ifdef DEBUG |
330 | if (scopeIndex == 0) { | |
331 | JS_ASSERT(protoIndex != 0); | |
332 | JS_ASSERT((protoIndex == 1) == (OBJ_GET_PROTO(cx, obj) == pobj)); | |
333 | } | |
334 | #endif | |
335 | ||
336 | if (scopeIndex != 0 || protoIndex != 1) { | if (scopeIndex != 0 || protoIndex != 1) { |
337 | khash = PROPERTY_CACHE_HASH_ATOM(atom, obj, pobj); | khash = PROPERTY_CACHE_HASH_ATOM(atom, obj, pobj); |
338 | PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1) | PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1) |
339 | cache->pcrecycles++); | cache->pcrecycles++); |
340 | pc = (jsbytecode *) atom; | pc = (jsbytecode *) atom; |
341 | kshape = (jsuword) obj; | kshape = (jsuword) obj; |
342 | ||
343 | /* | |
344 | * Make sure that a later shadowing assignment will enter | |
345 | * PurgeProtoChain and invalidate this entry, bug 479198. | |
346 | * | |
347 | * This is thread-safe even though obj is not locked. Only the | |
348 | * DELEGATE bit of obj->classword can change at runtime, given that | |
349 | * obj is native; and the bit is only set, never cleared. And on | |
350 | * platforms where another CPU can fail to see this write, it's OK | |
351 | * because the property cache and JIT cache are thread-local. | |
352 | */ | |
353 | OBJ_SET_DELEGATE(cx, obj); | |
354 | } | } |
355 | } | } |
356 | ||
357 | entry = &cache->table[khash]; | entry = &cache->table[khash]; |
358 | PCMETER(if (entry != *entryp) cache->modfills++); | PCMETER(PCVAL_IS_NULL(entry->vword) || cache->recycles++); |
PCMETER(if (!PCVAL_IS_NULL(entry->vword)) cache->recycles++); | ||
359 | entry->kpc = pc; | entry->kpc = pc; |
360 | entry->kshape = kshape; | entry->kshape = kshape; |
361 | entry->vcap = PCVCAP_MAKE(scope->shape, scopeIndex, protoIndex); | entry->vcap = PCVCAP_MAKE(vshape, scopeIndex, protoIndex); |
362 | entry->vword = vword; | entry->vword = vword; |
*entryp = entry; | ||
363 | ||
364 | cache->empty = JS_FALSE; | cache->empty = JS_FALSE; |
365 | PCMETER(cache->fills++); | PCMETER(cache->fills++); |
366 | ||
367 | /* | |
368 | * The modfills counter is not exact. It increases if a getter or setter | |
369 | * recurse into the interpreter. | |
370 | */ | |
371 | PCMETER(entry == cache->pctestentry || cache->modfills++); | |
372 | PCMETER(cache->pctestentry = NULL); | |
373 | return entry; | |
374 | } | } |
375 | ||
376 | JSAtom * | JS_REQUIRES_STACK JSAtom * |
377 | js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc, | js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc, |
378 | JSObject **objp, JSObject **pobjp, | JSObject **objp, JSObject **pobjp, |
379 | JSPropCacheEntry **entryp) | JSPropCacheEntry **entryp) |
# | Line 317 | Line 389 |
389 | JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code) | JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code) |
390 | < cx->fp->script->length); | < cx->fp->script->length); |
391 | ||
392 | op = (JSOp) *pc; | op = js_GetOpcode(cx, cx->fp->script, pc); |
393 | cs = &js_CodeSpec[op]; | cs = &js_CodeSpec[op]; |
394 | if (op == JSOP_LENGTH) { | if (op == JSOP_LENGTH) { |
395 | atom = cx->runtime->atomState.lengthAtom; | atom = cx->runtime->atomState.lengthAtom; |
396 | } else { | } else { |
397 | pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0; | pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0; |
398 | GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom); | GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom); |
399 | } | } |
400 | ||
# | Line 424 | Line 496 |
496 | JS_STATIC_ASSERT(PCVAL_NULL == 0); | JS_STATIC_ASSERT(PCVAL_NULL == 0); |
497 | ||
498 | void | void |
499 | js_FlushPropertyCache(JSContext *cx) | js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache) |
500 | { | { |
JSPropertyCache *cache; | ||
cache = &JS_PROPERTY_CACHE(cx); | ||
501 | if (cache->empty) { | if (cache->empty) { |
502 | ASSERT_CACHE_IS_EMPTY(cache); | ASSERT_CACHE_IS_EMPTY(cache); |
503 | return; | return; |
# | Line 496 | Line 565 |
565 | } | } |
566 | ||
567 | void | void |
568 | js_FlushPropertyCacheForScript(JSContext *cx, JSScript *script) | js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script) |
569 | { | { |
570 | JSPropertyCache *cache; | JSPropertyCache *cache; |
571 | JSPropCacheEntry *entry; | JSPropCacheEntry *entry; |
# | Line 514 | Line 583 |
583 | } | } |
584 | } | } |
585 | ||
void | ||
js_DisablePropertyCache(JSContext *cx) | ||
{ | ||
JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0); | ||
++JS_PROPERTY_CACHE(cx).disabled; | ||
} | ||
void | ||
js_EnablePropertyCache(JSContext *cx) | ||
{ | ||
--JS_PROPERTY_CACHE(cx).disabled; | ||
JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0); | ||
} | ||
586 | /* | /* |
587 | * Check if the current arena has enough space to fit nslots after sp and, if | * Check if the current arena has enough space to fit nslots after sp and, if |
588 | * so, reserve the necessary space. | * so, reserve the necessary space. |
589 | */ | */ |
590 | static JSBool | static JS_REQUIRES_STACK JSBool |
591 | AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots) | AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots) |
592 | { | { |
593 | uintN surplus; | uintN surplus; |
# | Line 557 | Line 612 |
612 | return JS_TRUE; | return JS_TRUE; |
613 | } | } |
614 | ||
615 | JS_STATIC_INTERPRET jsval * | JS_STATIC_INTERPRET JS_REQUIRES_STACK jsval * |
616 | js_AllocRawStack(JSContext *cx, uintN nslots, void **markp) | js_AllocRawStack(JSContext *cx, uintN nslots, void **markp) |
617 | { | { |
618 | jsval *sp; | jsval *sp; |
619 | ||
620 | JS_ASSERT(nslots != 0); | |
621 | js_LeaveTrace(cx); | |
622 | ||
623 | if (!cx->stackPool.first.next) { | if (!cx->stackPool.first.next) { |
624 | int64 *timestamp; | int64 *timestamp; |
625 | ||
# | Line 582 | Line 640 |
640 | return sp; | return sp; |
641 | } | } |
642 | ||
643 | JS_STATIC_INTERPRET void | JS_STATIC_INTERPRET JS_REQUIRES_STACK void |
644 | js_FreeRawStack(JSContext *cx, void *mark) | js_FreeRawStack(JSContext *cx, void *mark) |
645 | { | { |
646 | JS_ARENA_RELEASE(&cx->stackPool, mark); | JS_ARENA_RELEASE(&cx->stackPool, mark); |
647 | } | } |
648 | ||
649 | JS_FRIEND_API(jsval *) | JS_REQUIRES_STACK JS_FRIEND_API(jsval *) |
650 | js_AllocStack(JSContext *cx, uintN nslots, void **markp) | js_AllocStack(JSContext *cx, uintN nslots, void **markp) |
651 | { | { |
652 | jsval *sp; | jsval *sp; |
# | Line 634 | Line 692 |
692 | return sp; | return sp; |
693 | } | } |
694 | ||
695 | JS_FRIEND_API(void) | JS_REQUIRES_STACK JS_FRIEND_API(void) |
696 | js_FreeStack(JSContext *cx, void *mark) | js_FreeStack(JSContext *cx, void *mark) |
697 | { | { |
698 | JSStackHeader *sh; | JSStackHeader *sh; |
# | Line 662 | Line 720 |
720 | JSObject * | JSObject * |
721 | js_GetScopeChain(JSContext *cx, JSStackFrame *fp) | js_GetScopeChain(JSContext *cx, JSStackFrame *fp) |
722 | { | { |
723 | JSObject *obj, *cursor, *clonedChild, *parent; | JSObject *sharedBlock = fp->blockChain; |
JSTempValueRooter tvr; | ||
724 | ||
725 | obj = fp->blockChain; | if (!sharedBlock) { |
if (!obj) { | ||
726 | /* | /* |
727 | * Don't force a call object for a lightweight function call, but do | * Don't force a call object for a lightweight function call, but do |
728 | * insist that there is a call object for a heavyweight function call. | * insist that there is a call object for a heavyweight function call. |
# | Line 678 | Line 734 |
734 | return fp->scopeChain; | return fp->scopeChain; |
735 | } | } |
736 | ||
737 | /* We don't handle cloning blocks on trace. */ | |
738 | js_LeaveTrace(cx); | |
739 | ||
740 | /* | /* |
741 | * We have one or more lexical scopes to reflect into fp->scopeChain, so | * We have one or more lexical scopes to reflect into fp->scopeChain, so |
742 | * make sure there's a call object at the current head of the scope chain, | * make sure there's a call object at the current head of the scope chain, |
743 | * if this frame is a call frame. | * if this frame is a call frame. |
744 | * | |
745 | * Also, identify the innermost compiler-allocated block we needn't clone. | |
746 | */ | */ |
747 | JSObject *limitBlock, *limitClone; | |
748 | if (fp->fun && !fp->callobj) { | if (fp->fun && !fp->callobj) { |
749 | JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass || | JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass || |
750 | OBJ_GET_PRIVATE(cx, fp->scopeChain) != fp); | OBJ_GET_PRIVATE(cx, fp->scopeChain) != fp); |
751 | if (!js_GetCallObject(cx, fp, fp->scopeChain)) | if (!js_GetCallObject(cx, fp)) |
752 | return NULL; | return NULL; |
753 | ||
754 | /* We know we must clone everything on blockChain. */ | |
755 | limitBlock = limitClone = NULL; | |
756 | } else { | |
757 | /* | |
758 | * scopeChain includes all blocks whose static scope we're within that | |
759 | * have already been cloned. Find the innermost such block. Its | |
760 | * prototype should appear on blockChain; we'll clone blockChain up | |
761 | * to, but not including, that prototype. | |
762 | */ | |
763 | limitClone = fp->scopeChain; | |
764 | while (OBJ_GET_CLASS(cx, limitClone) == &js_WithClass) | |
765 | limitClone = OBJ_GET_PARENT(cx, limitClone); | |
766 | JS_ASSERT(limitClone); | |
767 | ||
768 | /* | |
769 | * It may seem like we don't know enough about limitClone to be able | |
770 | * to just grab its prototype as we do here, but it's actually okay. | |
771 | * | |
772 | * If limitClone is a block object belonging to this frame, then its | |
773 | * prototype is the innermost entry in blockChain that we have already | |
774 | * cloned, and is thus the place to stop when we clone below. | |
775 | * | |
776 | * Otherwise, there are no blocks for this frame on scopeChain, and we | |
777 | * need to clone the whole blockChain. In this case, limitBlock can | |
778 | * point to any object known not to be on blockChain, since we simply | |
779 | * loop until we hit limitBlock or NULL. If limitClone is a block, it | |
780 | * isn't a block from this function, since blocks can't be nested | |
781 | * within themselves on scopeChain (recursion is dynamic nesting, not | |
782 | * static nesting). If limitClone isn't a block, its prototype won't | |
783 | * be a block either. So we can just grab limitClone's prototype here | |
784 | * regardless of its type or which frame it belongs to. | |
785 | */ | |
786 | limitBlock = OBJ_GET_PROTO(cx, limitClone); | |
787 | ||
788 | /* If the innermost block has already been cloned, we are done. */ | |
789 | if (limitBlock == sharedBlock) | |
790 | return fp->scopeChain; | |
791 | } | } |
792 | ||
793 | /* | /* |
794 | * Clone the block chain. To avoid recursive cloning we set the parent of | * Special-case cloning the innermost block; this doesn't have enough in |
795 | * the cloned child after we clone the parent. In the following loop when | * common with subsequent steps to include in the loop. |
796 | * clonedChild is null it indicates the first iteration when no special GC | * |
797 | * rooting is necessary. On the second and the following iterations we | * We pass fp->scopeChain and not null even if we override the parent slot |
798 | * have to protect cloned so far chain against the GC during cloning of | * later as null triggers useless calculations of slot's value in |
799 | * the cursor object. | * js_NewObject that js_CloneBlockObject calls. |
800 | */ | */ |
801 | cursor = obj; | JSObject *innermostNewChild |
802 | clonedChild = NULL; | = js_CloneBlockObject(cx, sharedBlock, fp->scopeChain, fp); |
803 | if (!innermostNewChild) | |
804 | return NULL; | |
805 | JSAutoTempValueRooter tvr(cx, innermostNewChild); | |
806 | ||
807 | /* | |
808 | * Clone our way towards outer scopes until we reach the innermost | |
809 | * enclosing function, or the innermost block we've already cloned. | |
810 | */ | |
811 | JSObject *newChild = innermostNewChild; | |
812 | for (;;) { | for (;;) { |
813 | parent = OBJ_GET_PARENT(cx, cursor); | JS_ASSERT(OBJ_GET_PROTO(cx, newChild) == sharedBlock); |
814 | sharedBlock = OBJ_GET_PARENT(cx, sharedBlock); | |
815 | ||
816 | /* Sometimes limitBlock will be NULL, so check that first. */ | |
817 | if (sharedBlock == limitBlock || !sharedBlock) | |
818 | break; | |
819 | ||
820 | /* As in the call above, we don't know the real parent yet. */ | |
821 | JSObject *clone | |
822 | = js_CloneBlockObject(cx, sharedBlock, fp->scopeChain, fp); | |
823 | if (!clone) | |
824 | return NULL; | |
825 | ||
826 | /* | /* |
827 | * We pass fp->scopeChain and not null even if we override the parent | * Avoid OBJ_SET_PARENT overhead as newChild cannot escape to |
828 | * slot later as null triggers useless calculations of slot's value in | * other threads. |
* js_NewObject that js_CloneBlockObject calls. | ||
829 | */ | */ |
830 | cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp); | STOBJ_SET_PARENT(newChild, clone); |
831 | if (!cursor) { | newChild = clone; |
if (clonedChild) | ||
JS_POP_TEMP_ROOT(cx, &tvr); | ||
return NULL; | ||
} | ||
if (!clonedChild) { | ||
/* | ||
* The first iteration. Check if other follow and root obj if so | ||
* to protect the whole cloned chain against GC. | ||
*/ | ||
obj = cursor; | ||
if (!parent) | ||
break; | ||
JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); | ||
} else { | ||
/* | ||
* Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to | ||
* other threads. | ||
*/ | ||
STOBJ_SET_PARENT(clonedChild, cursor); | ||
if (!parent) { | ||
JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(obj)); | ||
JS_POP_TEMP_ROOT(cx, &tvr); | ||
break; | ||
} | ||
} | ||
clonedChild = cursor; | ||
cursor = parent; | ||
832 | } | } |
833 | fp->flags |= JSFRAME_POP_BLOCKS; | |
834 | fp->scopeChain = obj; | /* |
835 | fp->blockChain = NULL; | * If we found a limit block belonging to this frame, then we should have |
836 | return obj; | * found it in blockChain. |
837 | */ | |
838 | JS_ASSERT_IF(limitBlock && | |
839 | OBJ_GET_CLASS(cx, limitBlock) == &js_BlockClass && | |
840 | OBJ_GET_PRIVATE(cx, limitClone) == fp, | |
841 | sharedBlock); | |
842 | ||
843 | /* Place our newly cloned blocks at the head of the scope chain. */ | |
844 | fp->scopeChain = innermostNewChild; | |
845 | return fp->scopeChain; | |
846 | } | } |
847 | ||
848 | JSBool | JSBool |
# | Line 802 | Line 903 |
903 | * imposes a performance penalty on all js_ComputeGlobalThis calls, | * imposes a performance penalty on all js_ComputeGlobalThis calls, |
904 | * and it represents a maintenance hazard. | * and it represents a maintenance hazard. |
905 | */ | */ |
906 | fp = cx->fp; /* quell GCC overwarning */ | fp = js_GetTopStackFrame(cx); /* quell GCC overwarning */ |
907 | if (lazy) { | if (lazy) { |
908 | JS_ASSERT(fp->argv == argv); | JS_ASSERT(fp->argv == argv); |
909 | fp->dormantNext = cx->dormantFrameChain; | fp->dormantNext = cx->dormantFrameChain; |
# | Line 830 | Line 931 |
931 | thisp = parent; | thisp = parent; |
932 | } | } |
933 | ||
934 | OBJ_TO_OUTER_OBJECT(cx, thisp); | /* Some objects (e.g., With) delegate 'this' to another object. */ |
935 | thisp = OBJ_THIS_OBJECT(cx, thisp); | |
936 | if (!thisp) | if (!thisp) |
937 | return NULL; | return NULL; |
938 | argv[-1] = OBJECT_TO_JSVAL(thisp); | argv[-1] = OBJECT_TO_JSVAL(thisp); |
# | Line 849 | Line 951 |
951 | thisp = JSVAL_TO_OBJECT(argv[-1]); | thisp = JSVAL_TO_OBJECT(argv[-1]); |
952 | } else { | } else { |
953 | thisp = JSVAL_TO_OBJECT(argv[-1]); | thisp = JSVAL_TO_OBJECT(argv[-1]); |
954 | if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass) | if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass || |
955 | OBJ_GET_CLASS(cx, thisp) == &js_BlockClass) { | |
956 | return js_ComputeGlobalThis(cx, lazy, argv); | return js_ComputeGlobalThis(cx, lazy, argv); |
if (thisp->map->ops->thisObject) { | ||
/* Some objects (e.g., With) delegate 'this' to another object. */ | ||
thisp = thisp->map->ops->thisObject(cx, thisp); | ||
if (!thisp) | ||
return NULL; | ||
957 | } | } |
958 | OBJ_TO_OUTER_OBJECT(cx, thisp); | |
959 | /* Some objects (e.g., With) delegate 'this' to another object. */ | |
960 | thisp = OBJ_THIS_OBJECT(cx, thisp); | |
961 | if (!thisp) | if (!thisp) |
962 | return NULL; | return NULL; |
963 | argv[-1] = OBJECT_TO_JSVAL(thisp); | argv[-1] = OBJECT_TO_JSVAL(thisp); |
# | Line 881 | Line 980 |
980 | ||
981 | JSClass js_NoSuchMethodClass = { | JSClass js_NoSuchMethodClass = { |
982 | "NoSuchMethod", | "NoSuchMethod", |
983 | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS | | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS, |
JSCLASS_HAS_CACHED_PROTO(JSProto_NoSuchMethod), | ||
984 | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, |
985 | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, |
986 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL |
987 | }; | }; |
988 | ||
JS_BEGIN_EXTERN_C | ||
JSObject* | ||
js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj); | ||
JS_END_EXTERN_C | ||
JSObject* | ||
js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj) | ||
{ | ||
JSObject *proto; | ||
proto = JS_InitClass(cx, obj, NULL, &js_NoSuchMethodClass, NULL, 0, NULL, | ||
NULL, NULL, NULL); | ||
if (!proto) | ||
return NULL; | ||
OBJ_CLEAR_PROTO(cx, proto); | ||
return proto; | ||
} | ||
989 | /* | /* |
990 | * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of | * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of |
991 | * the base object, we search for the __noSuchMethod__ method in the base. | * the base object, we search for the __noSuchMethod__ method in the base. |
# | Line 937 | Line 1014 |
1014 | ||
1015 | MUST_FLOW_THROUGH("out"); | MUST_FLOW_THROUGH("out"); |
1016 | id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); | id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); |
1017 | #if JS_HAS_XML_SUPPORT | ok = js_GetMethod(cx, obj, id, false, &tvr.u.value); |
1018 | if (OBJECT_IS_XML(cx, obj)) { | if (!ok) |
1019 | JSXMLObjectOps *ops; | goto out; |
ops = (JSXMLObjectOps *) obj->map->ops; | ||
obj = ops->getMethod(cx, obj, id, &tvr.u.value); | ||
if (!obj) { | ||
ok = JS_FALSE; | ||
goto out; | ||
} | ||
vp[1] = OBJECT_TO_JSVAL(obj); | ||
} else | ||
#endif | ||
{ | ||
ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value); | ||
if (!ok) | ||
goto out; | ||
} | ||
1020 | if (JSVAL_IS_PRIMITIVE(tvr.u.value)) { | if (JSVAL_IS_PRIMITIVE(tvr.u.value)) { |
1021 | vp[0] = tvr.u.value; | vp[0] = tvr.u.value; |
1022 | } else { | } else { |
# | Line 969 | Line 1031 |
1031 | vp[0] = ID_TO_VALUE(id); | vp[0] = ID_TO_VALUE(id); |
1032 | } | } |
1033 | #endif | #endif |
1034 | obj = js_NewObject(cx, &js_NoSuchMethodClass, NULL, NULL, 0); | obj = js_NewObjectWithGivenProto(cx, &js_NoSuchMethodClass, |
1035 | NULL, NULL, 0); | |
1036 | if (!obj) { | if (!obj) { |
1037 | ok = JS_FALSE; | ok = JS_FALSE; |
1038 | goto out; | goto out; |
# | Line 985 | Line 1048 |
1048 | return ok; | return ok; |
1049 | } | } |
1050 | ||
1051 | static JSBool | static JS_REQUIRES_STACK JSBool |
1052 | NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags) | NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags) |
1053 | { | { |
1054 | jsval *invokevp; | jsval *invokevp; |
# | Line 1046 | Line 1109 |
1109 | * required arguments, allocate declared local variables, and pop everything | * required arguments, allocate declared local variables, and pop everything |
1110 | * when done. Then push the return value. | * when done. Then push the return value. |
1111 | */ | */ |
1112 | JS_FRIEND_API(JSBool) | JS_REQUIRES_STACK JS_FRIEND_API(JSBool) |
1113 | js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags) | js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags) |
1114 | { | { |
1115 | void *mark; | void *mark; |
# | Line 1069 | Line 1132 |
1132 | JS_ASSERT((jsval *) cx->stackPool.current->base <= vp); | JS_ASSERT((jsval *) cx->stackPool.current->base <= vp); |
1133 | JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail); | JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail); |
1134 | ||
1135 | /* | /* Mark the top of stack and load frequently-used registers. */ |
* Mark the top of stack and load frequently-used registers. After this | ||
* point the control should flow through label out2: to return. | ||
*/ | ||
1136 | mark = JS_ARENA_MARK(&cx->stackPool); | mark = JS_ARENA_MARK(&cx->stackPool); |
1137 | MUST_FLOW_THROUGH("out2"); | |
1138 | v = *vp; | v = *vp; |
1139 | ||
1140 | if (JSVAL_IS_PRIMITIVE(v)) | if (JSVAL_IS_PRIMITIVE(v)) |
# | Line 1098 | Line 1159 |
1159 | * XXX better to call that hook without converting | * XXX better to call that hook without converting |
1160 | * XXX the only thing that needs fixing is liveconnect | * XXX the only thing that needs fixing is liveconnect |
1161 | * | * |
1162 | * Try converting to function, for closure and API compatibility. | * FIXME bug 408416: try converting to function, for API compatibility |
1163 | * We attempt the conversion under all circumstances for 1.2, but | * if there is a call op defined. |
* only if there is a call op defined otherwise. | ||
1164 | */ | */ |
1165 | if ((ops == &js_ObjectOps) ? clasp->call : ops->call) { | if ((ops == &js_ObjectOps) ? clasp->call : ops->call) { |
1166 | ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v); | ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v); |
# | Line 1141 | Line 1201 |
1201 | if (FUN_INTERPRETED(fun)) { | if (FUN_INTERPRETED(fun)) { |
1202 | native = NULL; | native = NULL; |
1203 | script = fun->u.i.script; | script = fun->u.i.script; |
1204 | JS_ASSERT(script); | |
1205 | } else { | } else { |
1206 | native = fun->u.n.native; | native = fun->u.n.native; |
1207 | script = NULL; | script = NULL; |
# | Line 1265 | Line 1326 |
1326 | frame.down = cx->fp; | frame.down = cx->fp; |
1327 | frame.annotation = NULL; | frame.annotation = NULL; |
1328 | frame.scopeChain = NULL; /* set below for real, after cx->fp is set */ | frame.scopeChain = NULL; /* set below for real, after cx->fp is set */ |
1329 | frame.blockChain = NULL; | |
1330 | frame.regs = NULL; | frame.regs = NULL; |
1331 | frame.imacpc = NULL; | frame.imacpc = NULL; |
1332 | frame.slots = NULL; | frame.slots = NULL; |
# | Line 1273 | Line 1335 |
1335 | frame.flags = flags | rootedArgsFlag; | frame.flags = flags | rootedArgsFlag; |
1336 | frame.dormantNext = NULL; | frame.dormantNext = NULL; |
1337 | frame.xmlNamespace = NULL; | frame.xmlNamespace = NULL; |
1338 | frame.blockChain = NULL; | frame.displaySave = NULL; |
1339 | ||
1340 | MUST_FLOW_THROUGH("out"); | MUST_FLOW_THROUGH("out"); |
1341 | cx->fp = &frame; | cx->fp = &frame; |
# | Line 1282 | Line 1344 |
1344 | hook = cx->debugHooks->callHook; | hook = cx->debugHooks->callHook; |
1345 | hookData = NULL; | hookData = NULL; |
1346 | ||
/* call the hook if present */ | ||
if (hook && (native || script)) | ||
hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData); | ||
/* Call the function, either a native method or an interpreted script. */ | ||
1347 | if (native) { | if (native) { |
#ifdef DEBUG_NOT_THROWING | ||
JSBool alreadyThrowing = cx->throwing; | ||
#endif | ||
#if JS_HAS_LVALUE_RETURN | ||
/* Set by JS_SetCallReturnValue2, used to return reference types. */ | ||
cx->rval2set = JS_FALSE; | ||
#endif | ||
1348 | /* If native, use caller varobj and scopeChain for eval. */ | /* If native, use caller varobj and scopeChain for eval. */ |
1349 | JS_ASSERT(!frame.varobj); | JS_ASSERT(!frame.varobj); |
1350 | JS_ASSERT(!frame.scopeChain); | JS_ASSERT(!frame.scopeChain); |
# | Line 1308 | Line 1356 |
1356 | /* But ensure that we have a scope chain. */ | /* But ensure that we have a scope chain. */ |
1357 | if (!frame.scopeChain) | if (!frame.scopeChain) |
1358 | frame.scopeChain = parent; | frame.scopeChain = parent; |
1359 | } else { | |
frame.displaySave = NULL; | ||
ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval); | ||
JS_RUNTIME_METER(cx->runtime, nativeCalls); | ||
#ifdef DEBUG_NOT_THROWING | ||
if (ok && !alreadyThrowing) | ||
ASSERT_NOT_THROWING(cx); | ||
#endif | ||
} else if (script) { | ||
1360 | /* Use parent scope so js_GetCallObject can find the right "Call". */ | /* Use parent scope so js_GetCallObject can find the right "Call". */ |
1361 | frame.scopeChain = parent; | frame.scopeChain = parent; |
1362 | if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) { | if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) { |
1363 | /* Scope with a call object parented by the callee's parent. */ | /* Scope with a call object parented by the callee's parent. */ |
1364 | if (!js_GetCallObject(cx, &frame, parent)) { | if (!js_GetCallObject(cx, &frame)) { |
1365 | ok = JS_FALSE; | ok = JS_FALSE; |
1366 | goto out; | goto out; |
1367 | } | } |
1368 | } | } |
1369 | frame.slots = sp - fun->u.i.nvars; | frame.slots = sp - fun->u.i.nvars; |
1370 | } | |
1371 | ||
1372 | ok = js_Interpret(cx); | /* Call the hook if present after we fully initialized the frame. */ |
1373 | if (hook) | |
1374 | hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData); | |
1375 | ||
1376 | /* Call the function, either a native method or an interpreted script. */ | |
1377 | if (native) { | |
1378 | #ifdef DEBUG_NOT_THROWING | |
1379 | JSBool alreadyThrowing = cx->throwing; | |
1380 | #endif | |
1381 | ||
1382 | #if JS_HAS_LVALUE_RETURN | |
1383 | /* Set by JS_SetCallReturnValue2, used to return reference types. */ | |
1384 | cx->rval2set = JS_FALSE; | |
1385 | #endif | |
1386 | ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval); | |
1387 | JS_RUNTIME_METER(cx->runtime, nativeCalls); | |
1388 | #ifdef DEBUG_NOT_THROWING | |
1389 | if (ok && !alreadyThrowing) | |
1390 | ASSERT_NOT_THROWING(cx); | |
1391 | #endif | |
1392 | } else { | } else { |
1393 | /* fun might be onerror trying to report a syntax error in itself. */ | JS_ASSERT(script); |
1394 | frame.scopeChain = NULL; | ok = js_Interpret(cx); |
frame.displaySave = NULL; | ||
ok = JS_TRUE; | ||
1395 | } | } |
1396 | ||
1397 | out: | out: |
# | Line 1377 | Line 1435 |
1435 | void *mark; | void *mark; |
1436 | JSBool ok; | JSBool ok; |
1437 | ||
1438 | js_LeaveTrace(cx); | |
1439 | invokevp = js_AllocStack(cx, 2 + argc, &mark); | invokevp = js_AllocStack(cx, 2 + argc, &mark); |
1440 | if (!invokevp) | if (!invokevp) |
1441 | return JS_FALSE; | return JS_FALSE; |
# | Line 1415 | Line 1474 |
1474 | { | { |
1475 | JSSecurityCallbacks *callbacks; | JSSecurityCallbacks *callbacks; |
1476 | ||
1477 | js_LeaveTrace(cx); | |
1478 | ||
1479 | /* | /* |
1480 | * js_InternalInvoke could result in another try to get or set the same id | * js_InternalInvoke could result in another try to get or set the same id |
1481 | * again, see bug 355497. | * again, see bug 355497. |
# | Line 1459 | Line 1520 |
1520 | JSObject *obj, *tmp; | JSObject *obj, *tmp; |
1521 | JSBool ok; | JSBool ok; |
1522 | ||
1523 | js_LeaveTrace(cx); | |
1524 | ||
1525 | #ifdef INCLUDE_MOZILLA_DTRACE | #ifdef INCLUDE_MOZILLA_DTRACE |
1526 | if (JAVASCRIPT_EXECUTE_START_ENABLED()) | if (JAVASCRIPT_EXECUTE_START_ENABLED()) |
1527 | jsdtrace_execute_start(script); | jsdtrace_execute_start(script); |
# | Line 1466 | Line 1529 |
1529 | ||
1530 | hook = cx->debugHooks->executeHook; | hook = cx->debugHooks->executeHook; |
1531 | hookData = mark = NULL; | hookData = mark = NULL; |
1532 | oldfp = cx->fp; | oldfp = js_GetTopStackFrame(cx); |
1533 | frame.script = script; | frame.script = script; |
1534 | if (down) { | if (down) { |
1535 | /* Propagate arg state for eval and the debugger API. */ | /* Propagate arg state for eval and the debugger API. */ |
# | Line 1543 | Line 1606 |
1606 | ||
1607 | cx->fp = &frame; | cx->fp = &frame; |
1608 | if (!down) { | if (!down) { |
1609 | OBJ_TO_OUTER_OBJECT(cx, frame.thisp); | frame.thisp = OBJ_THIS_OBJECT(cx, frame.thisp); |
1610 | if (!frame.thisp) { | if (!frame.thisp) { |
1611 | ok = JS_FALSE; | ok = JS_FALSE; |
1612 | goto out2; | goto out2; |
# | Line 1596 | Line 1659 |
1659 | jsval value; | jsval value; |
1660 | const char *type, *name; | const char *type, *name; |
1661 | ||
1662 | /* | |
1663 | * Both objp and propp must be either null or given. When given, *propp | |
1664 | * must be null. This way we avoid an extra "if (propp) *propp = NULL" for | |
1665 | * the common case of a non-existing property. | |
1666 | */ | |
1667 | JS_ASSERT(!objp == !propp); | |
1668 | JS_ASSERT_IF(propp, !*propp); | |
1669 | ||
1670 | /* The JSPROP_INITIALIZER case below may generate a warning. Since we must | |
1671 | * drop the property before reporting it, we insists on !propp to avoid | |
1672 | * looking up the property again after the reporting is done. | |
1673 | */ | |
1674 | JS_ASSERT_IF(attrs & JSPROP_INITIALIZER, attrs == JSPROP_INITIALIZER); | |
1675 | JS_ASSERT_IF(attrs == JSPROP_INITIALIZER, !propp); | |
1676 | ||
1677 | if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) | if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) |
1678 | return JS_FALSE; | return JS_FALSE; |
if (propp) { | ||
*objp = obj2; | ||
*propp = prop; | ||
} | ||
1679 | if (!prop) | if (!prop) |
1680 | return JS_TRUE; | return JS_TRUE; |
1681 | ||
1682 | /* | /* Use prop as a speedup hint to OBJ_GET_ATTRIBUTES. */ |
* Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error. | ||
* An assertion at label bad: will insist that it is null. | ||
*/ | ||
1683 | if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) { | if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) { |
1684 | OBJ_DROP_PROPERTY(cx, obj2, prop); | OBJ_DROP_PROPERTY(cx, obj2, prop); |
1685 | #ifdef DEBUG | return JS_FALSE; |
prop = NULL; | ||
#endif | ||
goto bad; | ||
1686 | } | } |
1687 | ||
1688 | /* | /* |
* From here, return true, or else goto bad on failure to null out params. | ||
1689 | * If our caller doesn't want prop, drop it (we don't need it any longer). | * If our caller doesn't want prop, drop it (we don't need it any longer). |
1690 | */ | */ |
1691 | if (!propp) { | if (!propp) { |
1692 | OBJ_DROP_PROPERTY(cx, obj2, prop); | OBJ_DROP_PROPERTY(cx, obj2, prop); |
1693 | prop = NULL; | prop = NULL; |
1694 | } else { | |
1695 | *objp = obj2; | |
1696 | *propp = prop; | |
1697 | } | } |
1698 | ||
1699 | if (attrs == JSPROP_INITIALIZER) { | if (attrs == JSPROP_INITIALIZER) { |
1700 | /* Allow the new object to override properties. */ | /* Allow the new object to override properties. */ |
1701 | if (obj2 != obj) | if (obj2 != obj) |
1702 | return JS_TRUE; | return JS_TRUE; |
1703 | ||
1704 | /* The property must be dropped already. */ | |
1705 | JS_ASSERT(!prop); | |
1706 | report = JSREPORT_WARNING | JSREPORT_STRICT; | report = JSREPORT_WARNING | JSREPORT_STRICT; |
1707 | } else { | } else { |
1708 | /* We allow redeclaring some non-readonly properties. */ | /* We allow redeclaring some non-readonly properties. */ |
1709 | if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) { | if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) { |
1710 | /* | /* Allow redeclaration of variables and functions. */ |
* Allow redeclaration of variables and functions, but insist that | ||
* the new value is not a getter if the old value was, ditto for | ||
* setters -- unless prop is impermanent (in which case anyone | ||
* could delete it and redefine it, willy-nilly). | ||
*/ | ||
1711 | if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) | if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) |
1712 | return JS_TRUE; | return JS_TRUE; |
1713 | ||
1714 | /* | |
1715 | * Allow adding a getter only if a property already has a setter | |
1716 | * but no getter and similarly for adding a setter. That is, we | |
1717 | * allow only the following transitions: | |
1718 | * | |
1719 | * no-property --> getter --> getter + setter | |
1720 | * no-property --> setter --> getter + setter | |
1721 | */ | |
1722 | if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0) | if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0) |
1723 | return JS_TRUE; | return JS_TRUE; |
1724 | ||
1725 | /* | |
1726 | * Allow redeclaration of an impermanent property (in which case | |
1727 | * anyone could delete it and redefine it, willy-nilly). | |
1728 | */ | |
1729 | if (!(oldAttrs & JSPROP_PERMANENT)) | if (!(oldAttrs & JSPROP_PERMANENT)) |
1730 | return JS_TRUE; | return JS_TRUE; |
1731 | } | } |
1732 | if (prop) | |
1733 | OBJ_DROP_PROPERTY(cx, obj2, prop); | |
1734 | ||
1735 | report = JSREPORT_ERROR; | report = JSREPORT_ERROR; |
1736 | isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0; | isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0; |
1737 | if (!isFunction) { | if (!isFunction) { |
1738 | if (!OBJ_GET_PROPERTY(cx, obj, id, &value)) | if (!OBJ_GET_PROPERTY(cx, obj, id, &value)) |
1739 | goto bad; | return JS_FALSE; |
1740 | isFunction = VALUE_IS_FUNCTION(cx, value); | isFunction = VALUE_IS_FUNCTION(cx, value); |
1741 | } | } |
1742 | } | } |
# | Line 1670 | Line 1754 |
1754 | : js_var_str; | : js_var_str; |
1755 | name = js_ValueToPrintableString(cx, ID_TO_VALUE(id)); | name = js_ValueToPrintableString(cx, ID_TO_VALUE(id)); |
1756 | if (!name) | if (!name) |
1757 | goto bad; | return JS_FALSE; |
1758 | return JS_ReportErrorFlagsAndNumber(cx, report, | return JS_ReportErrorFlagsAndNumber(cx, report, |
1759 | js_GetErrorMessage, NULL, | js_GetErrorMessage, NULL, |
1760 | JSMSG_REDECLARED_VAR, | JSMSG_REDECLARED_VAR, |
1761 | type, name); | type, name); |
bad: | ||
if (propp) { | ||
*objp = NULL; | ||
*propp = NULL; | ||
} | ||
JS_ASSERT(!prop); | ||
return JS_FALSE; | ||
1762 | } | } |
1763 | ||
1764 | JSBool | JSBool |
# | Line 1728 | Line 1804 |
1804 | return lval == rval; | return lval == rval; |
1805 | } | } |
1806 | ||
1807 | JSBool | JS_REQUIRES_STACK JSBool |
1808 | js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp) | js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp) |
1809 | { | { |
1810 | JSFunction *fun, *fun2; | JSFunction *fun, *fun2; |
# | Line 1773 | Line 1849 |
1849 | ||
1850 | if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) { | if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) { |
1851 | fun2 = GET_FUNCTION_PRIVATE(cx, obj2); | fun2 = GET_FUNCTION_PRIVATE(cx, obj2); |
1852 | if (!FUN_INTERPRETED(fun2) && | if (!FUN_INTERPRETED(fun2) && fun2->u.n.clasp) |
1853 | !(fun2->flags & JSFUN_TRACEABLE) && | clasp = fun2->u.n.clasp; |
fun2->u.n.u.clasp) { | ||
clasp = fun2->u.n.u.clasp; | ||
} | ||
1854 | } | } |
1855 | } | } |
1856 | obj = js_NewObject(cx, clasp, proto, parent, 0); | obj = js_NewObject(cx, clasp, proto, parent, 0); |
# | Line 1833 | Line 1906 |
1906 | * Enter the new with scope using an object at sp[-1] and associate the depth | * Enter the new with scope using an object at sp[-1] and associate the depth |
1907 | * of the with block with sp + stackIndex. | * of the with block with sp + stackIndex. |
1908 | */ | */ |
1909 | JS_STATIC_INTERPRET JSBool | JS_STATIC_INTERPRET JS_REQUIRES_STACK JSBool |
1910 | js_EnterWith(JSContext *cx, jsint stackIndex) | js_EnterWith(JSContext *cx, jsint stackIndex) |
1911 | { | { |
1912 | JSStackFrame *fp; | JSStackFrame *fp; |
# | Line 1868 | Line 1941 |
1941 | return JS_FALSE; | return JS_FALSE; |
1942 | ||
1943 | fp->scopeChain = withobj; | fp->scopeChain = withobj; |
js_DisablePropertyCache(cx); | ||
1944 | return JS_TRUE; | return JS_TRUE; |
1945 | } | } |
1946 | ||
1947 | JS_STATIC_INTERPRET void | JS_STATIC_INTERPRET JS_REQUIRES_STACK void |
1948 | js_LeaveWith(JSContext *cx) | js_LeaveWith(JSContext *cx) |
1949 | { | { |
1950 | JSObject *withobj; | JSObject *withobj; |
# | Line 1883 | Line 1955 |
1955 | JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0); | JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0); |
1956 | cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj); | cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj); |
1957 | JS_SetPrivate(cx, withobj, NULL); | JS_SetPrivate(cx, withobj, NULL); |
js_EnablePropertyCache(cx); | ||
1958 | } | } |
1959 | ||
1960 | JSClass * | JS_REQUIRES_STACK JSClass * |
1961 | js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth) | js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth) |
1962 | { | { |
1963 | JSClass *clasp; | JSClass *clasp; |
# | Line 1900 | Line 1971 |
1971 | return NULL; | return NULL; |
1972 | } | } |
1973 | ||
JS_STATIC_INTERPRET jsint | ||
js_CountWithBlocks(JSContext *cx, JSStackFrame *fp) | ||
{ | ||
jsint n; | ||
JSObject *obj; | ||
JSClass *clasp; | ||
n = 0; | ||
for (obj = fp->scopeChain; | ||
(clasp = js_IsActiveWithOrBlock(cx, obj, 0)) != NULL; | ||
obj = OBJ_GET_PARENT(cx, obj)) { | ||
if (clasp == &js_WithClass) | ||
++n; | ||
} | ||
return n; | ||
} | ||
1974 | /* | /* |
1975 | * Unwind block and scope chains to match the given depth. The function sets | * Unwind block and scope chains to match the given depth. The function sets |
1976 | * fp->sp on return to stackDepth. | * fp->sp on return to stackDepth. |
1977 | */ | */ |
1978 | JSBool | JS_REQUIRES_STACK JSBool |
1979 | js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth, | js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth, |
1980 | JSBool normalUnwind) | JSBool normalUnwind) |
1981 | { | { |
# | Line 1989 | Line 2043 |
2043 | return JS_TRUE; | return JS_TRUE; |
2044 | } | } |
2045 | ||
2046 | jsval& | |
2047 | js_GetUpvar(JSContext *cx, uintN level, uintN cookie) | |
2048 | { | |
2049 | level -= UPVAR_FRAME_SKIP(cookie); | |
2050 | JS_ASSERT(level < JS_DISPLAY_SIZE); | |
2051 | ||
2052 | JSStackFrame *fp = cx->display[level]; | |
2053 | JS_ASSERT(fp->script); | |
2054 | ||
2055 | uintN slot = UPVAR_FRAME_SLOT(cookie); | |
2056 | jsval *vp; | |
2057 | ||
2058 | if (!fp->fun) { | |
2059 | vp = fp->slots + fp->script->nfixed; | |
2060 | } else if (slot < fp->fun->nargs) { | |
2061 | vp = fp->argv; | |
2062 | } else if (slot == CALLEE_UPVAR_SLOT) { | |
2063 | vp = &fp->argv[-2]; | |
2064 | slot = 0; | |
2065 | } else { | |
2066 | slot -= fp->fun->nargs; | |
2067 | JS_ASSERT(slot < fp->script->nslots); | |
2068 | vp = fp->slots; | |
2069 | } | |
2070 | ||
2071 | return vp[slot]; | |
2072 | } | |
2073 | ||
2074 | #ifdef DEBUG | #ifdef DEBUG |
2075 | ||
2076 | JS_STATIC_INTERPRET void | JS_STATIC_INTERPRET JS_REQUIRES_STACK void |
2077 | js_TraceOpcode(JSContext *cx, jsint len) | js_TraceOpcode(JSContext *cx) |
2078 | { | { |
2079 | FILE *tracefp; | FILE *tracefp; |
2080 | JSStackFrame *fp; | JSStackFrame *fp; |
2081 | JSFrameRegs *regs; | JSFrameRegs *regs; |
JSOp prevop; | ||
2082 | intN ndefs, n, nuses; | intN ndefs, n, nuses; |
2083 | jsval *siter; | jsval *siter; |
2084 | JSString *str; | JSString *str; |
# | Line 2007 | Line 2088 |
2088 | JS_ASSERT(tracefp); | JS_ASSERT(tracefp); |
2089 | fp = cx->fp; | fp = cx->fp; |
2090 | regs = fp->regs; | regs = fp->regs; |
2091 | if (len != 0) { | |
2092 | prevop = (JSOp) regs->pc[-len]; | /* |
2093 | ndefs = js_CodeSpec[prevop].ndefs; | * Operations in prologues don't produce interesting values, and |
2094 | if (ndefs != 0) { | * js_DecompileValueGenerator isn't set up to handle them anyway. |
2095 | */ | |
2096 | if (cx->tracePrevPc && regs->pc >= fp->script->main) { | |
2097 | JSOp tracePrevOp = JSOp(*cx->tracePrevPc); | |
2098 | ndefs = js_GetStackDefs(cx, &js_CodeSpec[tracePrevOp], tracePrevOp, | |
2099 | fp->script, cx->tracePrevPc); | |
2100 | ||
2101 | /* | |
2102 | * If there aren't that many elements on the stack, then | |
2103 | * we have probably entered a new frame, and printing output | |
2104 | * would just be misleading. | |
2105 | */ | |
2106 | if (ndefs != 0 && | |
2107 | ndefs < regs->sp - fp->slots) { | |
2108 | for (n = -ndefs; n < 0; n++) { | for (n = -ndefs; n < 0; n++) { |
2109 | char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n], | char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n], |
2110 | NULL); | NULL); |
# | Line 2041 | Line 2135 |
2135 | PTRDIFF(regs->pc, fp->script->code, jsbytecode), | PTRDIFF(regs->pc, fp->script->code, jsbytecode), |
2136 | JS_FALSE, tracefp); | JS_FALSE, tracefp); |
2137 | op = (JSOp) *regs->pc; | op = (JSOp) *regs->pc; |
2138 | nuses = js_CodeSpec[op].nuses; | nuses = js_GetStackUses(&js_CodeSpec[op], op, regs->pc); |
2139 | if (nuses != 0) { | if (nuses != 0) { |
2140 | for (n = -nuses; n < 0; n++) { | for (n = -nuses; n < 0; n++) { |
2141 | char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n], | char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n], |
# | Line 2055 | Line 2149 |
2149 | } | } |
2150 | fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp))); | fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp))); |
2151 | } | } |
2152 | cx->tracePrevPc = regs->pc; | |
2153 | ||
2154 | /* It's nice to have complete traces when debugging a crash. */ | |
2155 | fflush(tracefp); | |
2156 | } | } |
2157 | ||
2158 | #endif /* DEBUG */ | #endif /* DEBUG */ |
# | Line 2411 | Line 2509 |
2509 | #endif | #endif |
2510 | ||
2511 | /* | /* |
2512 | * Interpreter assumes the following to implement condition-free interrupt | * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on |
2513 | * implementation when !JS_THREADED_INTERP. | * single-thread DEBUG js shell testing to verify property cache hits. |
2514 | */ | */ |
2515 | JS_STATIC_ASSERT(JSOP_INTERRUPT == 0); | #if defined DEBUG && !defined JS_THREADSAFE |
2516 | ||
2517 | # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) \ | |
2518 | JS_BEGIN_MACRO \ | |
2519 | if (!AssertValidPropertyCacheHit(cx, script, regs, pcoff, obj, pobj, \ | |
2520 | entry)) { \ | |
2521 | goto error; \ | |
2522 | } \ | |
2523 | JS_END_MACRO | |
2524 | ||
2525 | static bool | |
2526 | AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs, | |
2527 | ptrdiff_t pcoff, JSObject *start, JSObject *found, | |
2528 | JSPropCacheEntry *entry) | |
2529 | { | |
2530 | uint32 sample = cx->runtime->gcNumber; | |
2531 | ||
2532 | JSAtom *atom; | |
2533 | if (pcoff >= 0) | |
2534 | GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom); | |
2535 | else | |
2536 | atom = cx->runtime->atomState.lengthAtom; | |
2537 | ||
2538 | JSObject *obj, *pobj; | |
2539 | JSProperty *prop; | |
2540 | bool ok; | |
2541 | ||
2542 | if (JOF_OPMODE(*regs.pc) == JOF_NAME) { | |
2543 | ok = js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &pobj, &prop); | |
2544 | } else { | |
2545 | obj = start; | |
2546 | ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); | |
2547 | } | |
2548 | if (!ok) | |
2549 | return false; | |
2550 | if (!prop) | |
2551 | return true; | |
2552 | if (cx->runtime->gcNumber != sample || | |
2553 | PCVCAP_SHAPE(entry->vcap) != OBJ_SHAPE(pobj)) { | |
2554 | OBJ_DROP_PROPERTY(cx, pobj, prop); | |
2555 | return true; | |
2556 | } | |
2557 | JS_ASSERT(prop); | |
2558 | JS_ASSERT(pobj == found); | |
2559 | ||
2560 | JSScopeProperty *sprop = (JSScopeProperty *) prop; | |
2561 | if (PCVAL_IS_SLOT(entry->vword)) { | |
2562 | JS_ASSERT(PCVAL_TO_SLOT(entry->vword) == sprop->slot); | |
2563 | } else if (PCVAL_IS_SPROP(entry->vword)) { | |
2564 | JS_ASSERT(PCVAL_TO_SPROP(entry->vword) == sprop); | |
2565 | } else { | |
2566 | jsval v; | |
2567 | JS_ASSERT(PCVAL_IS_OBJECT(entry->vword)); | |
2568 | JS_ASSERT(entry->vword != PCVAL_NULL); | |
2569 | JS_ASSERT(SCOPE_IS_BRANDED(OBJ_SCOPE(pobj))); | |
2570 | JS_ASSERT(SPROP_HAS_STUB_GETTER(sprop)); | |
2571 | JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); | |
2572 | v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); | |
2573 | JS_ASSERT(VALUE_IS_FUNCTION(cx, v)); | |
2574 | JS_ASSERT(PCVAL_TO_OBJECT(entry->vword) == JSVAL_TO_OBJECT(v)); | |
2575 | } | |
2576 | ||
2577 | OBJ_DROP_PROPERTY(cx, pobj, prop); | |
2578 | return true; | |
2579 | } | |
2580 | ||
2581 | #else | |
2582 | # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0) | |
2583 | #endif | |
2584 | ||
2585 | /* | /* |
2586 | * Ensure that the intrepreter switch can close call-bytecode cases in the | * Ensure that the intrepreter switch can close call-bytecode cases in the |
# | Line 2423 | Line 2589 |
2589 | JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH); | JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH); |
2590 | JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH); | JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH); |
2591 | JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH); | JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH); |
2592 | JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_CALLUPVAR_DBG_LENGTH); | |
2593 | JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETUPVAR_LENGTH); | |
2594 | JS_STATIC_ASSERT(JSOP_GETDSLOT_LENGTH == JSOP_CALLDSLOT_LENGTH); | |
2595 | JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH); | JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH); |
2596 | JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH); | JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH); |
2597 | JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH); | JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH); |
2598 | ||
2599 | /* | /* |
2600 | * Same for debuggable flat closures defined at top level in another function | |
2601 | * or program fragment. | |
2602 | */ | |
2603 | JS_STATIC_ASSERT(JSOP_DEFFUN_FC_LENGTH == JSOP_DEFFUN_DBGFC_LENGTH); | |
2604 | ||
2605 | /* | |
2606 | * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but | * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but |
2607 | * remain distinct for the decompiler. Ditto for JSOP_NULL{,THIS}. | * remain distinct for the decompiler. |
2608 | */ | */ |
2609 | JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH); | JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH); |
JS_STATIC_ASSERT(JSOP_NULL_LENGTH == JSOP_NULLTHIS_LENGTH); | ||
2610 | ||
2611 | /* See TRY_BRANCH_AFTER_COND. */ | /* See TRY_BRANCH_AFTER_COND. */ |
2612 | JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH); | JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH); |
# | Line 2443 | Line 2617 |
2617 | JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH); | JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH); |
2618 | JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH); | JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH); |
2619 | ||
2620 | JSBool | #ifdef JS_TRACER |
2621 | # define ABORT_RECORDING(cx, reason) \ | |
2622 | JS_BEGIN_MACRO \ | |
2623 | if (TRACE_RECORDER(cx)) \ | |
2624 | js_AbortRecording(cx, reason); \ | |
2625 | JS_END_MACRO | |
2626 | #else | |
2627 | # define ABORT_RECORDING(cx, reason) ((void) 0) | |
2628 | #endif | |
2629 | ||
2630 | JS_REQUIRES_STACK JSBool | |
2631 | js_Interpret(JSContext *cx) | js_Interpret(JSContext *cx) |
2632 | { | { |
2633 | JSRuntime *rt; | JSRuntime *rt; |
# | Line 2472 | Line 2656 |
2656 | JSClass *clasp; | JSClass *clasp; |
2657 | JSFunction *fun; | JSFunction *fun; |
2658 | JSType type; | JSType type; |
#if JS_THREADED_INTERP | ||
register void * const *jumpTable; | ||
#else | ||
register uint32 switchMask; | ||
uintN switchOp; | ||
#endif | ||
2659 | jsint low, high, off, npairs; | jsint low, high, off, npairs; |
2660 | JSBool match; | JSBool match; |
2661 | #if JS_HAS_GETTER_SETTER | #if JS_HAS_GETTER_SETTER |
# | Line 2493 | Line 2671 |
2671 | # define JS_EXTENSION_(s) s | # define JS_EXTENSION_(s) s |
2672 | #endif | #endif |
2673 | ||
2674 | # ifdef DEBUG | |
2675 | /* | |
2676 | * We call this macro from BEGIN_CASE in threaded interpreters, | |
2677 | * and before entering the switch in non-threaded interpreters. | |
2678 | * However, reaching such points doesn't mean we've actually | |
2679 | * fetched an OP from the instruction stream: some opcodes use | |
2680 | * 'op=x; DO_OP()' to let another opcode's implementation finish | |
2681 | * their work, and many opcodes share entry points with a run of | |
2682 | * consecutive BEGIN_CASEs. | |
2683 | * | |
2684 | * Take care to trace OP only when it is the opcode fetched from | |
2685 | * the instruction stream, so the trace matches what one would | |
2686 | * expect from looking at the code. (We do omit POPs after SETs; | |
2687 | * unfortunate, but not worth fixing.) | |
2688 | */ | |
2689 | # define TRACE_OPCODE(OP) JS_BEGIN_MACRO \ | |
2690 | if (JS_UNLIKELY(cx->tracefp != NULL) && \ | |
2691 | (OP) == *regs.pc) \ | |
2692 | js_TraceOpcode(cx); \ | |
2693 | JS_END_MACRO | |
2694 | # else | |
2695 | # define TRACE_OPCODE(OP) ((void) 0) | |
2696 | # endif | |
2697 | ||
2698 | #if JS_THREADED_INTERP | #if JS_THREADED_INTERP |
2699 | static void *const normalJumpTable[] = { | static void *const normalJumpTable[] = { |
2700 | # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ | # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ |
# | Line 2501 | Line 2703 |
2703 | # undef OPDEF | # undef OPDEF |
2704 | }; | }; |
2705 | ||
#ifdef JS_TRACER | ||
static void *const recordingJumpTable[] = { | ||
# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ | ||
JS_EXTENSION &&R_##op, | ||
# include "jsopcode.tbl" | ||
# undef OPDEF | ||
}; | ||
#endif /* JS_TRACER */ | ||
2706 | static void *const interruptJumpTable[] = { | static void *const interruptJumpTable[] = { |
2707 | # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ | # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ |
2708 | JS_EXTENSION &&L_JSOP_INTERRUPT, | JS_EXTENSION &&interrupt, |
2709 | # include "jsopcode.tbl" | # include "jsopcode.tbl" |
2710 | # undef OPDEF | # undef OPDEF |
2711 | }; | }; |
2712 | ||
2713 | register void * const *jumpTable = normalJumpTable; | |
2714 | ||
2715 | METER_OP_INIT(op); /* to nullify first METER_OP_PAIR */ | METER_OP_INIT(op); /* to nullify first METER_OP_PAIR */ |
2716 | ||
2717 | # define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable)) | |
2718 | ||
2719 | # ifdef JS_TRACER | # ifdef JS_TRACER |
2720 | # define CHECK_RECORDER() JS_BEGIN_MACRO \ | # define CHECK_RECORDER() \ |
2721 | JS_ASSERT(!TRACE_RECORDER(cx) ^ \ | JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable) |
(jumpTable == recordingJumpTable)); \ | ||
JS_END_MACRO | ||
2722 | # else | # else |
2723 | # define CHECK_RECORDER() ((void)0) | # define CHECK_RECORDER() ((void)0) |
2724 | # endif | # endif |
# | Line 2538 | Line 2733 |
2733 | DO_OP(); \ | DO_OP(); \ |
2734 | JS_END_MACRO | JS_END_MACRO |
2735 | ||
2736 | # define BEGIN_CASE(OP) L_##OP: \ | # define BEGIN_CASE(OP) L_##OP: TRACE_OPCODE(OP); CHECK_RECORDER(); |
CHECK_RECORDER(); | ||
2737 | # define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH); | # define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH); |
2738 | # define END_VARLEN_CASE DO_NEXT_OP(len); | # define END_VARLEN_CASE DO_NEXT_OP(len); |
2739 | # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) \ | # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) \ |
# | Line 2551 | Line 2745 |
2745 | ||
2746 | #else /* !JS_THREADED_INTERP */ | #else /* !JS_THREADED_INTERP */ |
2747 | ||
2748 | register intN switchMask = 0; | |
2749 | intN switchOp; | |
2750 | ||
2751 | # define ENABLE_INTERRUPTS() ((void) (switchMask = -1)) | |
2752 | ||
2753 | # ifdef JS_TRACER | |
2754 | # define CHECK_RECORDER() \ | |
2755 | JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1) | |
2756 | # else | |
2757 | # define CHECK_RECORDER() ((void)0) | |
2758 | # endif | |
2759 | ||
2760 | # define DO_OP() goto do_op | # define DO_OP() goto do_op |
2761 | # define DO_NEXT_OP(n) JS_BEGIN_MACRO \ | # define DO_NEXT_OP(n) JS_BEGIN_MACRO \ |
2762 | JS_ASSERT((n) == len); \ | JS_ASSERT((n) == len); \ |
2763 | goto advance_pc; \ | goto advance_pc; \ |
2764 | JS_END_MACRO | JS_END_MACRO |
2765 | ||
2766 | # define BEGIN_CASE(OP) case OP: | # define BEGIN_CASE(OP) case OP: CHECK_RECORDER(); |
2767 | # define END_CASE(OP) END_CASE_LEN(OP##_LENGTH) | # define END_CASE(OP) END_CASE_LEN(OP##_LENGTH) |
2768 | # define END_CASE_LEN(n) END_CASE_LENX(n) | # define END_CASE_LEN(n) END_CASE_LENX(n) |
2769 | # define END_CASE_LENX(n) END_CASE_LEN##n | # define END_CASE_LENX(n) END_CASE_LEN##n |
# | Line 2579 | Line 2785 |
2785 | ||
2786 | #ifdef JS_TRACER | #ifdef JS_TRACER |
2787 | /* We had better not be entering the interpreter from JIT-compiled code. */ | /* We had better not be entering the interpreter from JIT-compiled code. */ |
2788 | TraceRecorder *tr = NULL; | TraceRecorder *tr = TRACE_RECORDER(cx); |
2789 | if (JS_ON_TRACE(cx)) { | SET_TRACE_RECORDER(cx, NULL); |
2790 | tr = TRACE_RECORDER(cx); | |
2791 | SET_TRACE_RECORDER(cx, NULL); | /* If a recorder is pending and we try to re-enter the interpreter, flag |
2792 | JS_TRACE_MONITOR(cx).onTrace = JS_FALSE; | the recorder to be destroyed when we return. */ |
2793 | /* | if (tr) { |
2794 | * ON_TRACE means either recording or coming from traced code. | if (tr->wasDeepAborted()) |
2795 | * If there's no recorder (the latter case), don't care. | tr->removeFragmentoReferences(); |
2796 | */ | else |
2797 | if (tr) { | tr->pushAbortStack(); |
if (tr->wasDeepAborted()) | ||
tr->removeFragmentoReferences(); | ||
else | ||
tr->pushAbortStack(); | ||
} | ||
2798 | } | } |
2799 | #endif | #endif |
2800 | ||
# | Line 2644 | Line 2845 |
2845 | #define MONITOR_BRANCH() \ | #define MONITOR_BRANCH() \ |
2846 | JS_BEGIN_MACRO \ | JS_BEGIN_MACRO \ |
2847 | if (TRACING_ENABLED(cx)) { \ | if (TRACING_ENABLED(cx)) { \ |
2848 | ENABLE_TRACER(js_MonitorLoopEdge(cx, inlineCallCount)); \ | if (js_MonitorLoopEdge(cx, inlineCallCount)) { \ |
2849 | JS_ASSERT(TRACE_RECORDER(cx)); \ | |
2850 | ENABLE_INTERRUPTS(); \ | |
2851 | } \ | |
2852 | fp = cx->fp; \ | fp = cx->fp; \ |
2853 | script = fp->script; \ | script = fp->script; \ |
2854 | atoms = script->atomMap.vector; \ | atoms = FrameAtomBase(cx, fp); \ |
2855 | currentVersion = (JSVersion) script->version; \ | currentVersion = (JSVersion) script->version; \ |
2856 | JS_ASSERT(fp->regs == ®s); \ | JS_ASSERT(fp->regs == ®s); \ |
2857 | if (cx->throwing) \ | if (cx->throwing) \ |
# | Line 2667 | Line 2871 |
2871 | */ | */ |
2872 | #define CHECK_BRANCH() \ | #define CHECK_BRANCH() \ |
2873 | JS_BEGIN_MACRO \ | JS_BEGIN_MACRO \ |
2874 | if ((cx->operationCount -= JSOW_SCRIPT_JUMP) <= 0) { \ | if (!JS_CHECK_OPERATION_LIMIT(cx)) \ |
2875 | if (!js_ResetOperationCount(cx)) \ | goto error; \ |
goto error; \ | ||
} \ | ||
2876 | JS_END_MACRO | JS_END_MACRO |
2877 | ||
2878 | #ifndef TRACE_RECORDER | |
2879 | #define TRACE_RECORDER(cx) (false) | |
2880 | #endif | |
2881 | ||
2882 | #define BRANCH(n) \ | #define BRANCH(n) \ |
2883 | JS_BEGIN_MACRO \ | JS_BEGIN_MACRO \ |
2884 | regs.pc += n; \ | regs.pc += (n); \ |
2885 | if (n <= 0) { \ | op = (JSOp) *regs.pc; \ |
2886 | if ((n) <= 0) { \ | |
2887 | CHECK_BRANCH(); \ | CHECK_BRANCH(); \ |
2888 | MONITOR_BRANCH(); \ | if (op == JSOP_NOP) { \ |
2889 | if (TRACE_RECORDER(cx)) { \ | |
2890 | MONITOR_BRANCH(); \ | |
2891 | op = (JSOp) *regs.pc; \ | |
2892 | } else { \ | |
2893 | op = (JSOp) *++regs.pc; \ | |
2894 | } \ | |
2895 | } else if (op == JSOP_LOOP) { \ | |
2896 | MONITOR_BRANCH(); \ | |
2897 | op = (JSOp) *regs.pc; \ | |
2898 | } \ | |
2899 | } \ | } \ |
op = (JSOp) *regs.pc; \ | ||
2900 | DO_OP(); \ | DO_OP(); \ |
2901 | JS_END_MACRO | JS_END_MACRO |
2902 | ||
# | Line 2702 | Line 2918 |
2918 | js_SetVersion(cx, currentVersion); | js_SetVersion(cx, currentVersion); |
2919 | ||
2920 | /* Update the static-link display. */ | /* Update the static-link display. */ |
2921 | if (script->staticDepth < JS_DISPLAY_SIZE) { | if (script->staticLevel < JS_DISPLAY_SIZE) { |
2922 | JSStackFrame **disp = &cx->display[script->staticDepth]; | JSStackFrame **disp = &cx->display[script->staticLevel]; |
2923 | fp->displaySave = *disp; | fp->displaySave = *disp; |
2924 | *disp = fp; | *disp = fp; |
2925 | } | } |
2926 | #ifdef DEBUG | |
2927 | fp->pcDisabledSave = JS_PROPERTY_CACHE(cx).disabled; | # define CHECK_INTERRUPT_HANDLER() \ |
2928 | #endif | JS_BEGIN_MACRO \ |
2929 | if (cx->debugHooks->interruptHandler) \ | |
2930 | ENABLE_INTERRUPTS(); \ | |
2931 | JS_END_MACRO | |
2932 | ||
2933 | /* | /* |
2934 | * Load the debugger's interrupt hook here and after calling out to native | * Load the debugger's interrupt hook here and after calling out to native |
# | Line 2717 | Line 2936 |
2936 | * not have to reload it each time through the interpreter loop -- we hope | * not have to reload it each time through the interpreter loop -- we hope |
2937 | * the compiler can keep it in a register when it is non-null. | * the compiler can keep it in a register when it is non-null. |
2938 | */ | */ |
2939 | #if JS_THREADED_INTERP | CHECK_INTERRUPT_HANDLER(); |
#ifdef JS_TRACER | ||
# define LOAD_INTERRUPT_HANDLER(cx) \ | ||
((void) (jumpTable = (cx)->debugHooks->interruptHandler \ | ||
? interruptJumpTable \ | ||
: TRACE_RECORDER(cx) \ | ||
? recordingJumpTable \ | ||
: normalJumpTable)) | ||
# define ENABLE_TRACER(flag) \ | ||
JS_BEGIN_MACRO \ | ||
bool flag_ = (flag); \ | ||
JS_ASSERT(flag_ == !!TRACE_RECORDER(cx)); \ | ||
jumpTable = flag_ ? recordingJumpTable : normalJumpTable; \ | ||
JS_END_MACRO | ||
#else /* !JS_TRACER */ | ||
# define LOAD_INTERRUPT_HANDLER(cx) \ | ||
((void) (jumpTable = (cx)->debugHooks->interruptHandler \ | ||
? interruptJumpTable \ | ||
: normalJumpTable)) | ||
# define ENABLE_TRACER(flag) ((void)0) | ||
#endif /* !JS_TRACER */ | ||
#else /* !JS_THREADED_INTERP */ | ||
#ifdef JS_TRACER | ||
# define LOAD_INTERRUPT_HANDLER(cx) \ | ||
((void) (switchMask = ((cx)->debugHooks->interruptHandler || \ | ||
TRACE_RECORDER(cx)) ? 0 : 255)) | ||
# define ENABLE_TRACER(flag) \ | ||
JS_BEGIN_MACRO \ | ||
bool flag_ = (flag); \ | ||
JS_ASSERT(flag_ == !!TRACE_RECORDER(cx)); \ | ||
switchMask = flag_ ? 0 : 255; \ | ||
JS_END_MACRO | ||
#else /* !JS_TRACER */ | ||
# define LOAD_INTERRUPT_HANDLER(cx) \ | ||
((void) (switchMask = ((cx)->debugHooks->interruptHandler \ | ||
? 0 : 255))) | ||
# define ENABLE_TRACER(flag) ((void)0) | ||
#endif /* !JS_TRACER */ | ||
#endif /* !JS_THREADED_INTERP */ | ||
LOAD_INTERRUPT_HANDLER(cx); | ||
2940 | ||
2941 | #if !JS_HAS_GENERATORS | #if !JS_HAS_GENERATORS |
2942 | JS_ASSERT(!fp->regs); | JS_ASSERT(!fp->regs); |
# | Line 2780 | Line 2959 |
2959 | fp->regs = ®s; | fp->regs = ®s; |
2960 | JS_ASSERT((size_t) (regs.pc - script->code) <= script->length); | JS_ASSERT((size_t) (regs.pc - script->code) <= script->length); |
2961 | JS_ASSERT((size_t) (regs.sp - StackBase(fp)) <= StackDepth(script)); | JS_ASSERT((size_t) (regs.sp - StackBase(fp)) <= StackDepth(script)); |
JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0); | ||
JS_PROPERTY_CACHE(cx).disabled += js_CountWithBlocks(cx, fp); | ||
2962 | ||
2963 | /* | /* |
2964 | * To support generator_throw and to catch ignored exceptions, | * To support generator_throw and to catch ignored exceptions, |
# | Line 2813 | Line 2990 |
2990 | * This is a loop, but it does not look like a loop. The loop-closing | * This is a loop, but it does not look like a loop. The loop-closing |
2991 | * jump is distributed throughout goto *jumpTable[op] inside of DO_OP. | * jump is distributed throughout goto *jumpTable[op] inside of DO_OP. |
2992 | * When interrupts are enabled, jumpTable is set to interruptJumpTable | * When interrupts are enabled, jumpTable is set to interruptJumpTable |
2993 | * where all jumps point to the JSOP_INTERRUPT case. The latter, after | * where all jumps point to the interrupt label. The latter, after |
2994 | * calling the interrupt handler, dispatches through normalJumpTable to | * calling the interrupt handler, dispatches through normalJumpTable to |
2995 | * continue the normal bytecode processing. | * continue the normal bytecode processing. |
2996 | */ | */ |
2997 | #else | interrupt: |
2998 | #else /* !JS_THREADED_INTERP */ | |
2999 | for (;;) { | for (;;) { |
3000 | advance_pc_by_one: | advance_pc_by_one: |
3001 | JS_ASSERT(js_CodeSpec[op].length == 1); | JS_ASSERT(js_CodeSpec[op].length == 1); |
# | Line 2825 | Line 3003 |
3003 | advance_pc: | advance_pc: |
3004 | regs.pc += len; | regs.pc += len; |
3005 | op = (JSOp) *regs.pc; | op = (JSOp) *regs.pc; |
#ifdef DEBUG | ||
if (cx->tracefp) | ||
js_TraceOpcode(cx, len); | ||
#endif | ||
3006 | ||
3007 | do_op: | do_op: |
3008 | switchOp = op & switchMask; | CHECK_RECORDER(); |
3009 | TRACE_OPCODE(op); | |
3010 | switchOp = intN(op) | switchMask; | |
3011 | do_switch: | do_switch: |
3012 | switch (switchOp) { | switch (switchOp) { |
3013 | case -1: | |
3014 | JS_ASSERT(switchMask == -1); | |
3015 | #endif /* !JS_THREADED_INTERP */ | #endif /* !JS_THREADED_INTERP */ |
BEGIN_CASE(JSOP_INTERRUPT) | ||
3016 | { | { |
3017 | JSTrapHandler handler; | bool moreInterrupts = false; |
3018 | JSTrapHandler handler = cx->debugHooks->interruptHandler; | |
handler = cx->debugHooks->interruptHandler; | ||
3019 | if (handler) { | if (handler) { |
3020 | #ifdef JS_TRACER | |
3021 | if (TRACE_RECORDER(cx)) | |
3022 | js_AbortRecording(cx, "interrupt handler"); | |
3023 | #endif | |
3024 | switch (handler(cx, script, regs.pc, &rval, | switch (handler(cx, script, regs.pc, &rval, |
3025 | cx->debugHooks->interruptHandlerData)) { | cx->debugHooks->interruptHandlerData)) { |
3026 | case JSTRAP_ERROR: | case JSTRAP_ERROR: |
# | Line 2858 | Line 3037 |
3037 | goto error; | goto error; |
3038 | default:; | default:; |
3039 | } | } |
3040 | #if !JS_THREADED_INTERP | moreInterrupts = true; |
} else { | ||
/* this was not a real interrupt, the tracer is trying to | ||
record a trace */ | ||
switchOp = op + 256; | ||
goto do_switch; | ||
#endif | ||
3041 | } | } |
3042 | LOAD_INTERRUPT_HANDLER(cx); | |
3043 | #ifdef JS_TRACER | |
3044 | TraceRecorder* tr = TRACE_RECORDER(cx); | |
3045 | if (tr) { | |
3046 | JSRecordingStatus status = TraceRecorder::monitorRecording(cx, tr, op); | |
3047 | switch (status) { | |
3048 | case JSRS_CONTINUE: | |
3049 | moreInterrupts = true; | |
3050 | break; | |
3051 | case JSRS_IMACRO: | |
3052 | atoms = COMMON_ATOMS_START(&rt->atomState); | |
3053 | op = JSOp(*regs.pc); | |
3054 | DO_OP(); /* keep interrupting for op. */ | |
3055 | break; | |
3056 | case JSRS_ERROR: | |
3057 | // The code at 'error:' aborts the recording. | |
3058 | goto error; | |
3059 | case JSRS_STOP: | |
3060 | break; | |
3061 | default: | |
3062 | JS_NOT_REACHED("Bad recording status"); | |
3063 | } | |
3064 | } | |
3065 | #endif /* !JS_TRACER */ | |
3066 | ||
3067 | #if JS_THREADED_INTERP | #if JS_THREADED_INTERP |
3068 | jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable; | |
3069 | JS_EXTENSION_(goto *normalJumpTable[op]); | JS_EXTENSION_(goto *normalJumpTable[op]); |
3070 | #else | #else |
3071 | switchOp = op; | switchMask = moreInterrupts ? -1 : 0; |
3072 | switchOp = intN(op); | |
3073 | goto do_switch; | goto do_switch; |
3074 | #endif | #endif |
3075 | } | } |
# | Line 2880 | Line 3078 |
3078 | ADD_EMPTY_CASE(JSOP_NOP) | ADD_EMPTY_CASE(JSOP_NOP) |
3079 | ADD_EMPTY_CASE(JSOP_CONDSWITCH) | ADD_EMPTY_CASE(JSOP_CONDSWITCH) |
3080 | ADD_EMPTY_CASE(JSOP_TRY) | ADD_EMPTY_CASE(JSOP_TRY) |
ADD_EMPTY_CASE(JSOP_FINALLY) | ||
3081 | #if JS_HAS_XML_SUPPORT | #if JS_HAS_XML_SUPPORT |
3082 | ADD_EMPTY_CASE(JSOP_STARTXML) | ADD_EMPTY_CASE(JSOP_STARTXML) |
3083 | ADD_EMPTY_CASE(JSOP_STARTXMLEXPR) | ADD_EMPTY_CASE(JSOP_STARTXMLEXPR) |
# | Line 2951 | Line 3148 |
3148 | END_CASE(JSOP_LEAVEWITH) | END_CASE(JSOP_LEAVEWITH) |
3149 | ||
3150 | BEGIN_CASE(JSOP_RETURN) | BEGIN_CASE(JSOP_RETURN) |
CHECK_BRANCH(); | ||
3151 | fp->rval = POP_OPND(); | fp->rval = POP_OPND(); |
3152 | /* FALL THROUGH */ | /* FALL THROUGH */ |
3153 | ||
# | Line 2962 | Line 3158 |
3158 | * will be false after the inline_return label. | * will be false after the inline_return label. |
3159 | */ | */ |
3160 | ASSERT_NOT_THROWING(cx); | ASSERT_NOT_THROWING(cx); |
3161 | CHECK_BRANCH(); | |
3162 | ||
3163 | if (fp->imacpc) { | if (fp->imacpc) { |
3164 | /* | /* |
# | Line 2997 | Line 3194 |
3194 | JSInlineFrame *ifp = (JSInlineFrame *) fp; | JSInlineFrame *ifp = (JSInlineFrame *) fp; |
3195 | void *hookData = ifp->hookData; | void *hookData = ifp->hookData; |
3196 | ||
JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave); | ||
3197 | JS_ASSERT(!fp->blockChain); | JS_ASSERT(!fp->blockChain); |
3198 | JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0)); | JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0)); |
3199 | ||
3200 | if (script->staticDepth < JS_DISPLAY_SIZE) | if (script->staticLevel < JS_DISPLAY_SIZE) |
3201 | cx->display[script->staticDepth] = fp->displaySave; | cx->display[script->staticLevel] = fp->displaySave; |
3202 | ||
3203 | if (hookData) { | if (hookData) { |
3204 | JSInterpreterHook hook; | JSInterpreterHook hook; |
# | Line 3017 | Line 3213 |
3213 | status = ok; | status = ok; |
3214 | hook(cx, fp, JS_FALSE, &status, hookData); | hook(cx, fp, JS_FALSE, &status, hookData); |
3215 | ok = status; | ok = status; |
3216 | LOAD_INTERRUPT_HANDLER(cx); | CHECK_INTERRUPT_HANDLER(); |
3217 | } | } |
3218 | } | } |
3219 | ||
# | Line 3074 | Line 3270 |
3270 | ||
3271 | /* Restore the calling script's interpreter registers. */ | /* Restore the calling script's interpreter registers. */ |
3272 | script = fp->script; | script = fp->script; |
3273 | atoms = script->atomMap.vector; | atoms = FrameAtomBase(cx, fp); |
3274 | ||
3275 | /* Resume execution in the calling frame. */ | /* Resume execution in the calling frame. */ |
3276 | inlineCallCount--; | inlineCallCount--; |
3277 | if (JS_LIKELY(ok)) { | if (JS_LIKELY(ok)) { |
3278 | TRACE_0(LeaveFrame); | TRACE_0(LeaveFrame); |
3279 | JS_ASSERT(js_CodeSpec[*regs.pc].length == JSOP_CALL_LENGTH); | JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, script, regs.pc)].length |
3280 | == JSOP_CALL_LENGTH); | |
3281 | len = JSOP_CALL_LENGTH; | len = JSOP_CALL_LENGTH; |
3282 | DO_NEXT_OP(len); | DO_NEXT_OP(len); |
3283 | } | } |
# | Line 3230 | Line 3427 |
3427 | flags = regs.pc[1]; | flags = regs.pc[1]; |
3428 | if (!js_ValueToIterator(cx, flags, ®s.sp[-1])) | if (!js_ValueToIterator(cx, flags, ®s.sp[-1])) |
3429 | goto error; | goto error; |
3430 | LOAD_INTERRUPT_HANDLER(cx); | CHECK_INTERRUPT_HANDLER(); |
3431 | JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1])); | JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1])); |
3432 | PUSH(JSVAL_VOID); | PUSH(JSVAL_VOID); |
3433 | END_CASE(JSOP_ITER) | END_CASE(JSOP_ITER) |
# | Line 3240 | Line 3437 |
3437 | JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2])); | JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2])); |
3438 | if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), ®s.sp[-1])) | if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), ®s.sp[-1])) |
3439 | goto error; | goto error; |
3440 | LOAD_INTERRUPT_HANDLER(cx); | CHECK_INTERRUPT_HANDLER(); |
3441 | rval = BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE); | rval = BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE); |
3442 | PUSH(rval); | PUSH(rval); |
TRACE_0(IteratorNextComplete); | ||
3443 | END_CASE(JSOP_NEXTITER) | END_CASE(JSOP_NEXTITER) |
3444 | ||
3445 | BEGIN_CASE(JSOP_ENDITER) | BEGIN_CASE(JSOP_ENDITER) |
# | Line 3270 | Line 3466 |
3466 | slot = GET_SLOTNO(regs.pc); | slot = GET_SLOTNO(regs.pc); |
3467 | JS_ASSERT(slot < fp->script->nslots); | JS_ASSERT(slot < fp->script->nslots); |
3468 | vp = &fp->slots[slot]; | vp = &fp->slots[slot]; |
GC_POKE(cx, *vp); | ||
3469 | *vp = regs.sp[-1]; | *vp = regs.sp[-1]; |
3470 | END_CASE(JSOP_FORLOCAL) | END_CASE(JSOP_FORLOCAL) |
3471 | ||
# | Line 3332 | Line 3527 |
3527 | STORE_OPND(-2, rval); | STORE_OPND(-2, rval); |
3528 | END_CASE(JSOP_SWAP) | END_CASE(JSOP_SWAP) |
3529 | ||
3530 | BEGIN_CASE(JSOP_PICK) | |
3531 | i = regs.pc[1]; | |
3532 | JS_ASSERT(regs.sp - (i+1) >= StackBase(fp)); | |
3533 | lval = regs.sp[-(i+1)]; | |
3534 | memmove(regs.sp - (i+1), regs.sp - i, sizeof(jsval)*i); | |
3535 | regs.sp[-1] = lval; | |
3536 | END_CASE(JSOP_PICK) | |
3537 | ||
3538 | #define PROPERTY_OP(n, call) \ | #define PROPERTY_OP(n, call) \ |
3539 | JS_BEGIN_MACRO \ | JS_BEGIN_MACRO \ |
3540 | /* Fetch the left part and resolve it to a non-null object. */ \ | /* Fetch the left part and resolve it to a non-null object. */ \ |
# | Line 3370 | Line 3573 |
3573 | } \ | } \ |
3574 | JS_END_MACRO | JS_END_MACRO |
3575 | ||
3576 | #define NATIVE_SET(cx,obj,sprop,vp) \ | #define NATIVE_SET(cx,obj,sprop,entry,vp) \ |
3577 | JS_BEGIN_MACRO \ | JS_BEGIN_MACRO \ |
3578 | TRACE_2(SetPropHit, entry, sprop); \ | |
3579 | if (SPROP_HAS_STUB_SETTER(sprop) && \ | if (SPROP_HAS_STUB_SETTER(sprop) && \ |
3580 | (sprop)->slot != SPROP_INVALID_SLOT) { \ | (sprop)->slot != SPROP_INVALID_SLOT) { \ |
3581 | /* Fast path for, e.g., Object instance properties. */ \ | /* Fast path for, e.g., Object instance properties. */ \ |
# | Line 3383 | Line 3587 |
3587 | JS_END_MACRO | JS_END_MACRO |
3588 | ||
3589 | /* | /* |
* Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on | ||
* single-thread DEBUG js shell testing to verify property cache hits. | ||
*/ | ||
#if defined DEBUG && !defined JS_THREADSAFE | ||
# define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) \ | ||
do { \ | ||
JSAtom *atom_; \ | ||
JSObject *obj_, *pobj_; \ | ||
JSProperty *prop_; \ | ||
JSScopeProperty *sprop_; \ | ||
uint32 sample_ = rt->gcNumber; \ | ||
if (pcoff >= 0) \ | ||
GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom_); \ | ||
else \ | ||
atom_ = rt->atomState.lengthAtom; \ | ||
if (JOF_OPMODE(*regs.pc) == JOF_NAME) { \ | ||
ok = js_FindProperty(cx, ATOM_TO_JSID(atom_), &obj_, &pobj_, \ | ||
&prop_); \ | ||
} else { \ | ||
obj_ = obj; \ | ||
ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom_), &pobj_, \ | ||
&prop_); \ | ||
} \ | ||
if (!ok) \ | ||
goto error; \ | ||
if (rt->gcNumber != sample_) \ | ||
break; \ | ||
JS_ASSERT(prop_); \ | ||
JS_ASSERT(pobj_ == pobj); \ | ||
sprop_ = (JSScopeProperty *) prop_; \ | ||
if (PCVAL_IS_SLOT(entry->vword)) { \ | ||
JS_ASSERT(PCVAL_TO_SLOT(entry->vword) == sprop_->slot); \ | ||
} else if (PCVAL_IS_SPROP(entry->vword)) { \ | ||
JS_ASSERT(PCVAL_TO_SPROP(entry->vword) == sprop_); \ | ||
} else { \ | ||
jsval v_; \ | ||
JS_ASSERT(PCVAL_IS_OBJECT(entry->vword)); \ | ||
JS_ASSERT(entry->vword != PCVAL_NULL); \ | ||
JS_ASSERT(SCOPE_IS_BRANDED(OBJ_SCOPE(pobj))); \ | ||
JS_ASSERT(SPROP_HAS_STUB_GETTER(sprop_)); \ | ||
JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop_, OBJ_SCOPE(pobj_))); \ | ||
v_ = LOCKED_OBJ_GET_SLOT(pobj_, sprop_->slot); \ | ||
JS_ASSERT(VALUE_IS_FUNCTION(cx, v_)); \ | ||
JS_ASSERT(PCVAL_TO_OBJECT(entry->vword) == JSVAL_TO_OBJECT(v_)); \ | ||
} \ | ||
OBJ_DROP_PROPERTY(cx, pobj_, prop_); \ | ||
} while (0) | ||
#else | ||
# define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0) | ||
#endif | ||
/* | ||
3590 | * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is | * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is |
3591 | * the constant length of the SET opcode sequence, and spdec is the constant | * the constant length of the SET opcode sequence, and spdec is the constant |
3592 | * by which to decrease the stack pointer to pop all of the SET op's operands. | * by which to decrease the stack pointer to pop all of the SET op's operands. |
# | Line 3497 | Line 3649 |
3649 | do { | do { |
3650 | JSPropCacheEntry *entry; | JSPropCacheEntry *entry; |
3651 | ||
3652 | /* | |
3653 | * We can skip the property lookup for the global object. If | |
3654 | * the property does not exist anywhere on the scope chain, | |
3655 | * JSOP_SETNAME adds the property to the global. | |
3656 | * | |
3657 | * As a consequence of this optimization for the global object | |
3658 | * we run its JSRESOLVE_ASSIGNING-tolerant resolve hooks only | |
3659 | * in JSOP_SETNAME, after the interpreter evaluates the right- | |
3660 | * hand-side of the assignment, and not here. | |
3661 | * | |
3662 | * This should be transparent to the hooks because the script, | |
3663 | * instead of name = rhs, could have used global.name = rhs | |
3664 | * given a global object reference, which also calls the hooks | |
3665 | * only after evaluating the rhs. We desire such resolve hook | |
3666 | * equivalence between the two forms. | |
3667 | */ | |
3668 | obj = fp->scopeChain; | obj = fp->scopeChain; |
3669 | if (!OBJ_GET_PARENT(cx, obj)) | |
3670 | break; | |
3671 | if (JS_LIKELY(OBJ_IS_NATIVE(obj))) { | if (JS_LIKELY(OBJ_IS_NATIVE(obj))) { |
3672 | PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom); | PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom); |
3673 | if (!atom) { | if (!atom) { |
# | Line 3510 | Line 3680 |
3680 | LOAD_ATOM(0); | LOAD_ATOM(0); |
3681 | } | } |
3682 | id = ATOM_TO_JSID(atom); | id = ATOM_TO_JSID(atom); |
3683 | obj = js_FindIdentifierBase(cx, id, entry); | obj = js_FindIdentifierBase(cx, fp->scopeChain, id); |
3684 | if (!obj) | if (!obj) |
3685 | goto error; | goto error; |
3686 | } while (0); | } while (0); |
# | Line 3583 | Line 3753 |
3753 | (rtmp == JSVAL_OBJECT && \ | (rtmp == JSVAL_OBJECT && \ |
3754 | (obj2 = JSVAL_TO_OBJECT(rval)) && \ | (obj2 = JSVAL_TO_OBJECT(rval)) && \ |
3755 | OBJECT_IS_XML(cx, obj2))) { \ | OBJECT_IS_XML(cx, obj2))) { \ |
3756 | JSXMLObjectOps *ops; \ | if (JSVAL_IS_OBJECT(rval) && obj2 == JSVAL_TO_OBJECT(rval)) \ |
\ | ||
ops = (JSXMLObjectOps *) obj2->map->ops; \ | ||
if (obj2 == JSVAL_TO_OBJECT(rval)) \ | ||
3757 | rval = lval; \ | rval = lval; \ |
3758 | if (!ops->equality(cx, obj2, rval, &cond)) \ | if (!js_TestXMLEquality(cx, obj2, rval, &cond)) \ |
3759 | goto error; \ | goto error; \ |
3760 | cond = cond OP JS_TRUE; \ | cond = cond OP JS_TRUE; \ |
3761 | } else | } else |
# | Line 3762 | Line 3929 |
3929 | if (!JSVAL_IS_PRIMITIVE(lval) && | if (!JSVAL_IS_PRIMITIVE(lval) && |
3930 | (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) && | (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) && |
3931 | VALUE_IS_XML(cx, rval)) { | VALUE_IS_XML(cx, rval)) { |
3932 | JSXMLObjectOps *ops; | if (!js_ConcatenateXML(cx, obj2, rval, &rval)) |
ops = (JSXMLObjectOps *) obj2->map->ops; | ||
if (!ops->concatenate(cx, obj2, rval, &rval)) | ||
3933 | goto error; | goto error; |
3934 | regs.sp--; | regs.sp--; |
3935 | STORE_OPND(-1, rval); | STORE_OPND(-1, rval); |
# | Line 4008 | Line 4172 |
4172 | ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); | ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); |
4173 | if (obj == obj2 && PCVAL_IS_SLOT(entry->vword)) { | if (obj == obj2 && PCVAL_IS_SLOT(entry->vword)) { |
4174 | slot = PCVAL_TO_SLOT(entry->vword); | slot = PCVAL_TO_SLOT(entry->vword); |
4175 | JS_ASSERT(slot < obj->map->freeslot); | JS_ASSERT(slot < OBJ_SCOPE(obj)->freeslot); |
4176 | rval = LOCKED_OBJ_GET_SLOT(obj, slot); | rval = LOCKED_OBJ_GET_SLOT(obj, slot); |
4177 | if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) { | if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) { |
4178 | rtmp = rval; | rtmp = rval; |
# | Line 4026 | Line 4190 |
4190 | LOAD_ATOM(0); | LOAD_ATOM(0); |
4191 | } | } |
4192 | } else { | } else { |
entry = NULL; | ||
4193 | LOAD_ATOM(0); | LOAD_ATOM(0); |
4194 | } | } |
4195 | id = ATOM_TO_JSID(atom); | id = ATOM_TO_JSID(atom); |
4196 | if (js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry) < 0) | if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop)) |
4197 | goto error; | goto error; |
4198 | if (!prop) | if (!prop) |
4199 | goto atom_not_defined; | goto atom_not_defined; |
# | Line 4210 | Line 4373 |
4373 | ||
4374 | #define COMPUTE_THIS(cx, fp, obj) \ | #define COMPUTE_THIS(cx, fp, obj) \ |
4375 | JS_BEGIN_MACRO \ | JS_BEGIN_MACRO \ |
4376 | if (fp->flags & JSFRAME_COMPUTED_THIS) { \ | if (!(obj = js_ComputeThisForFrame(cx, fp))) \ |
4377 | obj = fp->thisp; \ | goto error; \ |
} else { \ | ||
obj = js_ComputeThis(cx, JS_TRUE, fp->argv); \ | ||
if (!obj) \ | ||
goto error; \ | ||
fp->thisp = obj; \ | ||
fp->flags |= JSFRAME_COMPUTED_THIS; \ | ||
} \ | ||
4378 | JS_END_MACRO | JS_END_MACRO |
4379 | ||
4380 | BEGIN_CASE(JSOP_THIS) | BEGIN_CASE(JSOP_THIS) |
# | Line 4263 | Line 4419 |
4419 | JSObject *aobj; | JSObject *aobj; |
4420 | JSPropCacheEntry *entry; | JSPropCacheEntry *entry; |
4421 | ||
4422 | aobj = OBJ_IS_DENSE_ARRAY(cx, obj) ? OBJ_GET_PROTO(cx, obj) : obj; | aobj = js_GetProtoIfDenseArray(cx, obj); |
4423 | if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) { | if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) { |
4424 | PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom); | PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom); |
4425 | if (!atom) { | if (!atom) { |
# | Line 4272 | Line 4428 |
4428 | rval = PCVAL_OBJECT_TO_JSVAL(entry->vword); | rval = PCVAL_OBJECT_TO_JSVAL(entry->vword); |
4429 | } else if (PCVAL_IS_SLOT(entry->vword)) { | } else if (PCVAL_IS_SLOT(entry->vword)) { |
4430 | slot = PCVAL_TO_SLOT(entry->vword); | slot = PCVAL_TO_SLOT(entry->vword); |
4431 | JS_ASSERT(slot < obj2->map->freeslot); | JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); |
4432 | rval = LOCKED_OBJ_GET_SLOT(obj2, slot); | rval = LOCKED_OBJ_GET_SLOT(obj2, slot); |
4433 | } else { | } else { |
4434 | JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); | JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); |
# | Line 4291 | Line 4447 |
4447 | } | } |
4448 | id = ATOM_TO_JSID(atom); | id = ATOM_TO_JSID(atom); |
4449 | if (entry | if (entry |
4450 | ? !js_GetPropertyHelper(cx, aobj, id, &rval, &entry) | ? !js_GetPropertyHelper(cx, obj, id, true, &rval) |
4451 | : !OBJ_GET_PROPERTY(cx, obj, id, &rval)) { | : !OBJ_GET_PROPERTY(cx, obj, id, &rval)) { |
4452 | goto error; | goto error; |
4453 | } | } |
# | Line 4354 | Line 4510 |
4510 | goto error; | goto error; |
4511 | } | } |
4512 | ||
4513 | aobj = OBJ_IS_DENSE_ARRAY(cx, obj) ? OBJ_GET_PROTO(cx, obj) : obj; | aobj = js_GetProtoIfDenseArray(cx, obj); |
4514 | if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) { | if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) { |
4515 | PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom); | PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom); |
4516 | if (!atom) { | if (!atom) { |
# | Line 4363 | Line 4519 |
4519 | rval = PCVAL_OBJECT_TO_JSVAL(entry->vword); | rval = PCVAL_OBJECT_TO_JSVAL(entry->vword); |
4520 | } else if (PCVAL_IS_SLOT(entry->vword)) { | } else if (PCVAL_IS_SLOT(entry->vword)) { |
4521 | slot = PCVAL_TO_SLOT(entry->vword); | slot = PCVAL_TO_SLOT(entry->vword); |
4522 | JS_ASSERT(slot < obj2->map->freeslot); | JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); |
4523 | rval = LOCKED_OBJ_GET_SLOT(obj2, slot); | rval = LOCKED_OBJ_GET_SLOT(obj2, slot); |
4524 | } else { | } else { |
4525 | JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); | JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); |
# | Line 4387 | Line 4543 |
4543 | id = ATOM_TO_JSID(atom); | id = ATOM_TO_JSID(atom); |
4544 | PUSH(JSVAL_NULL); | PUSH(JSVAL_NULL); |
4545 | if (!JSVAL_IS_PRIMITIVE(lval)) { | if (!JSVAL_IS_PRIMITIVE(lval)) { |
4546 | #if JS_HAS_XML_SUPPORT | if (!js_GetMethod(cx, obj, id, !!entry, &rval)) |
/* Special-case XML object method lookup, per ECMA-357. */ | ||
if (OBJECT_IS_XML(cx, obj)) { | ||
JSXMLObjectOps *ops; | ||
ops = (JSXMLObjectOps *) obj->map->ops; | ||
obj = ops->getMethod(cx, obj, id, &rval); | ||
if (!obj) | ||
goto error; | ||
} else | ||
#endif | ||
if (entry | ||
? !js_GetPropertyHelper(cx, aobj, id, &rval, &entry) | ||
: !OBJ_GET_PROPERTY(cx, obj, id, &rval)) { | ||
4547 | goto error; | goto error; |
} | ||
4548 | STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); | STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); |
4549 | STORE_OPND(-2, rval); | STORE_OPND(-2, rval); |
4550 | } else { | } else { |
4551 | JS_ASSERT(obj->map->ops->getProperty == js_GetProperty); | JS_ASSERT(obj->map->ops->getProperty == js_GetProperty); |
4552 | if (!js_GetPropertyHelper(cx, obj, id, &rval, &entry)) | if (!js_GetPropertyHelper(cx, obj, id, true, &rval)) |
4553 | goto error; | goto error; |
4554 | STORE_OPND(-1, lval); | STORE_OPND(-1, lval); |
4555 | STORE_OPND(-2, rval); | STORE_OPND(-2, rval); |
# | Line 4472 | Line 4614 |
4614 | * in native object o. | * in native object o. |
4615 | */ | */ |
4616 | entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)]; | entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)]; |
4617 | PCMETER(cache->pctestentry = entry); | |
4618 | PCMETER(cache->tests++); | PCMETER(cache->tests++); |
4619 | PCMETER(cache->settests++); | PCMETER(cache->settests++); |
4620 | if (entry->kpc == regs.pc && entry->kshape == kshape) { | if (entry->kpc == regs.pc && |
4621 | JSScope *scope; | entry->kshape == kshape && |
4622 | PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape) { | |
4623 | JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0); | |
4624 | ||
4625 | JS_LOCK_OBJ(cx, obj); | JS_LOCK_OBJ(cx, obj); |
4626 | scope = OBJ_SCOPE(obj); | JSScope *scope = OBJ_SCOPE(obj); |
4627 | if (scope->shape == kshape) { | if (scope->shape == kshape) { |
4628 | JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); | JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); |
4629 | sprop = PCVAL_TO_SPROP(entry->vword); | sprop = PCVAL_TO_SPROP(entry->vword); |
4630 | JS_ASSERT(!(sprop->attrs & JSPROP_READONLY)); | JS_ASSERT(!(sprop->attrs & JSPROP_READONLY)); |
4631 | JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj))); | JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj))); |
4632 | ||
4633 | if (scope->object == obj) { | /* |
4634 | /* | * Fastest path: check whether the cached sprop is |
4635 | * Fastest path: the cached sprop is already | * already in scope and call NATIVE_GET and break |
4636 | * in scope. Just NATIVE_SET and break to get | * to get out of the do-while(0). But we can call |
4637 | * out of the do-while(0). | * NATIVE_GET only if obj owns scope or sprop is |
4638 | */ | * shared. |
4639 | */ | |
4640 | bool checkForAdd; | |
4641 | if (scope->object == obj || | |
4642 | (sprop->attrs & JSPROP_SHARED)) { | |
4643 | if (sprop == scope->lastProp || | if (sprop == scope->lastProp || |
4644 | SCOPE_HAS_PROPERTY(scope, sprop)) { | SCOPE_HAS_PROPERTY(scope, sprop)) { |
4645 | PCMETER(cache->pchits++); | PCMETER(cache->pchits++); |
4646 | PCMETER(cache->setpchits++); | PCMETER(cache->setpchits++); |
4647 | NATIVE_SET(cx, obj, sprop, &rval); | NATIVE_SET(cx, obj, sprop, entry, &rval); |
4648 | JS_UNLOCK_SCOPE(cx, scope); | JS_UNLOCK_SCOPE(cx, scope); |
TRACE_2(SetPropHit, entry, sprop); | ||
4649 | break; | break; |
4650 | } | } |
4651 | checkForAdd = | |
4652 | !(sprop->attrs & JSPROP_SHARED) && | |
4653 | sprop->parent == scope->lastProp && | |
4654 | !SCOPE_HAD_MIDDLE_DELETE(scope); | |
4655 | ||
4656 | } else { | } else { |
4657 | scope = js_GetMutableScope(cx, obj); | scope = js_GetMutableScope(cx, obj); |
4658 | if (!scope) { | if (!scope) { |
4659 | JS_UNLOCK_OBJ(cx, obj); | JS_UNLOCK_OBJ(cx, obj); |
4660 | goto error; | goto error; |
4661 | } | } |
4662 | checkForAdd = !sprop->parent; | |
4663 | } | } |
4664 | ||
4665 | if (sprop->parent == scope->lastProp && | if (checkForAdd && |
!SCOPE_HAD_MIDDLE_DELETE(scope) && | ||
4666 | SPROP_HAS_STUB_SETTER(sprop) && | SPROP_HAS_STUB_SETTER(sprop) && |
4667 | (slot = sprop->slot) == scope->map.freeslot) { | (slot = sprop->slot) == scope->freeslot) { |
4668 | /* | /* |
4669 | * Fast path: adding a plain old property that | * Fast path: adding a plain old property that |
4670 | * was once at the frontier of the property | * was once at the frontier of the property |
# | Line 4536 | Line 4689 |
4689 | */ | */ |
4690 | if (slot < STOBJ_NSLOTS(obj) && | if (slot < STOBJ_NSLOTS(obj) && |
4691 | !OBJ_GET_CLASS(cx, obj)->reserveSlots) { | !OBJ_GET_CLASS(cx, obj)->reserveSlots) { |
4692 | ++scope->map.freeslot; | ++scope->freeslot; |
4693 | } else { | } else { |
4694 | if (!js_AllocSlot(cx, obj, &slot)) { | if (!js_AllocSlot(cx, obj, &slot)) { |
4695 | JS_UNLOCK_SCOPE(cx, scope); | JS_UNLOCK_SCOPE(cx, scope); |
# | Line 4579 | Line 4732 |
4732 | } | } |
4733 | sprop = sprop2; | sprop = sprop2; |
4734 | } else { | } else { |
4735 | SCOPE_EXTEND_SHAPE(cx, scope, sprop); | js_ExtendScopeShape(cx, scope, sprop); |
4736 | ++scope->entryCount; | ++scope->entryCount; |
4737 | scope->lastProp = sprop; | scope->lastProp = sprop; |
4738 | } | } |
# | Line 4587 | Line 4740 |
4740 | GC_WRITE_BARRIER(cx, scope, | GC_WRITE_BARRIER(cx, scope, |
4741 | LOCKED_OBJ_GET_SLOT(obj, slot), | LOCKED_OBJ_GET_SLOT(obj, slot), |
4742 | rval); | rval); |
4743 | TRACE_2(SetPropHit, entry, sprop); | |
4744 | LOCKED_OBJ_SET_SLOT(obj, slot, rval); | LOCKED_OBJ_SET_SLOT(obj, slot, rval); |
4745 | JS_UNLOCK_SCOPE(cx, scope); | JS_UNLOCK_SCOPE(cx, scope); |
4746 | TRACE_2(SetPropHit, entry, sprop); | |
4747 | /* | |
4748 | * Purge the property cache of the id we may | |
4749 | * have just shadowed in obj's scope and proto | |
4750 | * chains. We do this after unlocking obj's | |
4751 | * scope to avoid lock nesting. | |
4752 | */ | |
4753 | js_PurgeScopeChain(cx, obj, sprop->id); | |
4754 | break; | break; |
4755 | } | } |
4756 | ||
# | Line 4613 | Line 4774 |
4774 | sprop = PCVAL_TO_SPROP(entry->vword); | sprop = PCVAL_TO_SPROP(entry->vword); |
4775 | JS_ASSERT(!(sprop->attrs & JSPROP_READONLY)); | JS_ASSERT(!(sprop->attrs & JSPROP_READONLY)); |
4776 | JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj2))); | JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj2))); |
4777 | NATIVE_SET(cx, obj, sprop, &rval); | NATIVE_SET(cx, obj, sprop, entry, &rval); |
4778 | } | } |
4779 | JS_UNLOCK_OBJ(cx, obj2); | JS_UNLOCK_OBJ(cx, obj2); |
4780 | if (sprop) { | if (sprop) |
TRACE_2(SetPropHit, entry, sprop); | ||
4781 | break; | break; |
} | ||
4782 | } | } |
4783 | } | } |
4784 | ||
# | Line 4627 | Line 4786 |
4786 | LOAD_ATOM(0); | LOAD_ATOM(0); |
4787 | id = ATOM_TO_JSID(atom); | id = ATOM_TO_JSID(atom); |
4788 | if (entry) { | if (entry) { |
4789 | if (!js_SetPropertyHelper(cx, obj, id, &rval, &entry)) | if (!js_SetPropertyHelper(cx, obj, id, true, &rval)) |
4790 | goto error; | goto error; |
#ifdef JS_TRACER | ||
if (entry) | ||
TRACE_1(SetPropMiss, entry); | ||
#endif | ||
4791 | } else { | } else { |
4792 | if (!OBJ_SET_PROPERTY(cx, obj, id, &rval)) | if (!OBJ_SET_PROPERTY(cx, obj, id, &rval)) |
4793 | goto error; | goto error; |
4794 | } | ABORT_RECORDING(cx, "Non-native set"); |
4795 | #ifdef JS_TRACER | } |
if (!entry && TRACE_RECORDER(cx)) { | ||
js_AbortRecording(cx, "SetPropUncached"); | ||
ENABLE_TRACER(0); | ||
} | ||
#endif | ||
4796 | } while (0); | } while (0); |
4797 | END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2); | END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2); |
4798 | ||
# | Line 4667 | Line 4817 |
4817 | if (OBJ_IS_DENSE_ARRAY(cx, obj)) { | if (OBJ_IS_DENSE_ARRAY(cx, obj)) { |
4818 | jsuint length; | jsuint length; |
4819 | ||
4820 | length = ARRAY_DENSE_LENGTH(obj); | length = js_DenseArrayCapacity(obj); |
4821 | i = JSVAL_TO_INT(rval); | i = JSVAL_TO_INT(rval); |
4822 | if ((jsuint)i < length && | if ((jsuint)i < length && |
4823 | i < obj->fslots[JSSLOT_ARRAY_LENGTH]) { | i < obj->fslots[JSSLOT_ARRAY_LENGTH]) { |
# | Line 4693 | Line 4843 |
4843 | END_CASE(JSOP_GETELEM) | END_CASE(JSOP_GETELEM) |
4844 | ||
4845 | BEGIN_CASE(JSOP_CALLELEM) | BEGIN_CASE(JSOP_CALLELEM) |
4846 | /* | ELEMENT_OP(-1, js_GetMethod(cx, obj, id, false, &rval)); |
* FIXME: JSOP_CALLELEM should call getMethod on XML objects as | ||
* CALLPROP does. See bug 362910. | ||
*/ | ||
ELEMENT_OP(-1, OBJ_GET_PROPERTY(cx, obj, id, &rval)); | ||
4847 | #if JS_HAS_NO_SUCH_METHOD | #if JS_HAS_NO_SUCH_METHOD |
4848 | if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) { | if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) { |
4849 | regs.sp[-2] = regs.sp[-1]; | regs.sp[-2] = regs.sp[-1]; |
# | Line 4720 | Line 4866 |
4866 | if (OBJ_IS_DENSE_ARRAY(cx, obj) && JSID_IS_INT(id)) { | if (OBJ_IS_DENSE_ARRAY(cx, obj) && JSID_IS_INT(id)) { |
4867 | jsuint length; | jsuint length; |
4868 | ||
4869 | length = ARRAY_DENSE_LENGTH(obj); | length = js_DenseArrayCapacity(obj); |
4870 | i = JSID_TO_INT(id); | i = JSID_TO_INT(id); |
4871 | if ((jsuint)i < length) { | if ((jsuint)i < length) { |
4872 | if (obj->dslots[i] == JSVAL_HOLE) { | if (obj->dslots[i] == JSVAL_HOLE) { |
4873 | if (rt->anyArrayProtoHasElement) | if (js_PrototypeHasIndexedProperties(cx, obj)) |
4874 | break; | break; |
4875 | if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH]) | if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH]) |
4876 | obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1; | obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1; |
# | Line 4791 | Line 4937 |
4937 | if (!js_InvokeConstructor(cx, argc, JS_FALSE, vp)) | if (!js_InvokeConstructor(cx, argc, JS_FALSE, vp)) |
4938 | goto error; | goto error; |
4939 | regs.sp = vp + 1; | regs.sp = vp + 1; |
4940 | LOAD_INTERRUPT_HANDLER(cx); | CHECK_INTERRUPT_HANDLER(); |
4941 | TRACE_0(NativeCallComplete); | |
4942 | END_CASE(JSOP_NEW) | END_CASE(JSOP_NEW) |
4943 | ||
BEGIN_CASE(JSOP_APPLY) | ||
{ | ||
argc = GET_ARGC(regs.pc); | ||
vp = regs.sp - (argc + 2); | ||
lval = *vp; | ||
if (!VALUE_IS_FUNCTION(cx, lval)) | ||
goto do_call; | ||
obj = JSVAL_TO_OBJECT(lval); | ||
fun = GET_FUNCTION_PRIVATE(cx, obj); | ||
if (FUN_INTERPRETED(fun)) | ||
goto do_call; | ||
bool apply = (JSFastNative)fun->u.n.native == js_fun_apply; | ||
if (!apply && (JSFastNative)fun->u.n.native != js_fun_call) | ||
goto do_call; | ||
/* | ||
* If the second arg to apply is null or void, treat it as an empty | ||
* array. | ||
*/ | ||
jsuint applylen = 0; | ||
if (apply && argc >= 2 && | ||
!JSVAL_IS_VOID(vp[3]) && !JSVAL_IS_NULL(vp[3])) { | ||
/* | ||
* Fall back on js_Invoke when the array argument has a wrong | ||
* type or when it has too many elements to fit into the | ||
* current stack chunk. | ||
*/ | ||
if (!JSVAL_IS_OBJECT(vp[3])) | ||
goto do_call; | ||
JSBool arraylike; | ||
JSObject* aobj = JSVAL_TO_OBJECT(vp[3]); | ||
if (!js_IsArrayLike(cx, aobj, &arraylike, &applylen)) | ||
goto error; | ||
if (!arraylike || applylen > ARGC_LIMIT) | ||
goto do_call; | ||
JSArena *a = cx->stackPool.current; | ||
JS_ASSERT(jsuword(vp + 2) <= a->limit); | ||
/* | ||
* We need space for applylen elements plus an extra slot to | ||
* temporary root the array object when we unpack its elements | ||
* using OBJ_GET_PROPERTY below. | ||
*/ | ||
if (a->limit - jsuword(vp + 2) < (applylen + 1) * sizeof(jsval)) | ||
goto do_call; | ||
} | ||
if (!VALUE_IS_FUNCTION(cx, vp[1])) | ||
goto do_call; | ||
vp[0] = vp[1]; | ||
if (argc == 0) { | ||
/* | ||
* Call fun with its global object as the 'this' param if | ||
* no args. | ||
*/ | ||
obj = NULL; | ||
} else { | ||
/* Convert the first arg to 'this'. */ | ||
if (!JSVAL_IS_PRIMITIVE(vp[2])) | ||
obj = JSVAL_TO_OBJECT(vp[2]); | ||
else if (!js_ValueToObject(cx, vp[2], &obj)) | ||
goto error; | ||
} | ||
vp[1] = OBJECT_TO_JSVAL(obj); | ||
if (!apply) { | ||
if (argc != 0) { | ||
--argc; | ||
memmove(vp + 2, vp + 3, argc * sizeof *vp); | ||
} | ||
} else if (applylen == 0) { | ||
argc = 0; | ||
} else { | ||
/* | ||
* Make room for missing arguments to the right including the | ||
* temporary root nulling any extra stack slots for GC safety. | ||
*/ | ||
jsval* newsp = vp + 2 + applylen + 1; | ||
if (newsp > regs.sp) { | ||
JSArena *a = cx->stackPool.current; | ||
JS_ASSERT(jsuword(newsp) <= a->limit); /* see above */ | ||
if ((jsuword) newsp > a->avail) | ||
a->avail = (jsuword) newsp; | ||
memset(vp + 2 + argc, 0, (applylen - argc) * sizeof(jsval)); | ||
} | ||
JSObject *aobj = JSVAL_TO_OBJECT(vp[3]); | ||
newsp[-1] = vp[3]; | ||
regs.sp = newsp; | ||
/* Expand array content onto the stack. */ | ||
for (i = 0; i < jsint(applylen); i++) { | ||
id = INT_TO_JSID(i); | ||
if (!OBJ_GET_PROPERTY(cx, aobj, id, &vp[2 + i])) { | ||
/* | ||
* There is no good way to restore the original stack | ||
* state here, but it is in a reasonable state with | ||
* either original elements or nulls for all arguments | ||
* we didn't unpack yet, so we leave it at that. | ||
*/ | ||
goto error; | ||
} | ||
} | ||
argc = applylen; | ||
} | ||
regs.sp = vp + 2 + argc; | ||
goto do_call_with_specified_vp_and_argc; | ||
} | ||
4944 | BEGIN_CASE(JSOP_CALL) | BEGIN_CASE(JSOP_CALL) |
4945 | BEGIN_CASE(JSOP_EVAL) | BEGIN_CASE(JSOP_EVAL) |
4946 | do_call: | BEGIN_CASE(JSOP_APPLY) |
4947 | argc = GET_ARGC(regs.pc); | argc = GET_ARGC(regs.pc); |
4948 | vp = regs.sp - (argc + 2); | vp = regs.sp - (argc + 2); |
4949 | ||
do_call_with_specified_vp_and_argc: | ||
4950 | lval = *vp; | lval = *vp; |
4951 | if (VALUE_IS_FUNCTION(cx, lval)) { | if (VALUE_IS_FUNCTION(cx, lval)) { |
4952 | obj = JSVAL_TO_OBJECT(lval); | obj = JSVAL_TO_OBJECT(lval); |
# | Line 5015 | Line 5048 |
5048 | newifp->frame.dormantNext = NULL; | newifp->frame.dormantNext = NULL; |
5049 | newifp->frame.xmlNamespace = NULL; | newifp->frame.xmlNamespace = NULL; |
5050 | newifp->frame.blockChain = NULL; | newifp->frame.blockChain = NULL; |
5051 | if (script->staticDepth < JS_DISPLAY_SIZE) { | if (script->staticLevel < JS_DISPLAY_SIZE) { |
5052 | JSStackFrame **disp = &cx->display[script->staticDepth]; | JSStackFrame **disp = &cx->display[script->staticLevel]; |
5053 | newifp->frame.displaySave = *disp; | newifp->frame.displaySave = *disp; |
5054 | *disp = &newifp->frame; | *disp = &newifp->frame; |
5055 | } | } |
#ifdef DEBUG | ||
newifp->frame.pcDisabledSave = | ||
JS_PROPERTY_CACHE(cx).disabled; | ||
#endif | ||
5056 | newifp->mark = newmark; | newifp->mark = newmark; |
5057 | ||
5058 | /* Compute the 'this' parameter now that argv is set. */ | /* Compute the 'this' parameter now that argv is set. */ |
# | Line 5040 | Line 5069 |
5069 | while (nvars--) | while (nvars--) |
5070 | *newsp++ = JSVAL_VOID; | *newsp++ = JSVAL_VOID; |
5071 | ||
/* Call the debugger hook if present. */ | ||
hook = cx->debugHooks->callHook; | ||
if (hook) { | ||
newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0, | ||
cx->debugHooks->callHookData); | ||
LOAD_INTERRUPT_HANDLER(cx); | ||
} else { | ||
newifp->hookData = NULL; | ||
} | ||
5072 | /* Scope with a call object parented by callee's parent. */ | /* Scope with a call object parented by callee's parent. */ |
5073 | if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) && | if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) && |
5074 | !js_GetCallObject(cx, &newifp->frame, parent)) { | !js_GetCallObject(cx, &newifp->frame)) { |
5075 | goto bad_inline_call; | goto bad_inline_call; |
5076 | } | } |
5077 | ||
# | Line 5072 | Line 5091 |
5091 | newifp->frame.regs = ®s; | newifp->frame.regs = ®s; |
5092 | cx->fp = fp = &newifp->frame; | cx->fp = fp = &newifp->frame; |
5093 | ||
5094 | /* Call the debugger hook if present. */ | |
5095 | hook = cx->debugHooks->callHook; | |
5096 | if (hook) { | |
5097 | newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0, | |
5098 | cx->debugHooks->callHookData); | |
5099 | CHECK_INTERRUPT_HANDLER(); | |
5100 | } else { | |
5101 | newifp->hookData = NULL; | |
5102 | } | |
5103 | ||
5104 | TRACE_0(EnterFrame); | TRACE_0(EnterFrame); |
5105 | ||
5106 | inlineCallCount++; | inlineCallCount++; |
# | Line 5125 | Line 5154 |
5154 | } | } |
5155 | #endif | #endif |
5156 | regs.sp = vp + 1; | regs.sp = vp + 1; |
5157 | if (!ok) | if (!ok) { |
5158 | goto error; | /* |
5159 | TRACE_0(FastNativeCallComplete); | * If we are executing the JSOP_NEXTITER imacro and a Stopiteration |
5160 | * exception is raised, transform it into a JSVAL_HOLE return value. | |
5161 | * The tracer generates equivalent code by calling CatchStopIteration_tn. | |
5162 | */ | |
5163 | if (fp->imacpc && *fp->imacpc == JSOP_NEXTITER && | |
5164 | cx->throwing && js_ValueIsStopIteration(cx->exception)) { | |
5165 | // pc may point to JSOP_DUP here due to bug 474854. | |
5166 | JS_ASSERT(*regs.pc == JSOP_CALL || *regs.pc == JSOP_DUP); | |
5167 | cx->throwing = JS_FALSE; | |
5168 | cx->exception = JSVAL_VOID; | |
5169 | regs.sp[-1] = JSVAL_HOLE; | |
5170 | } else { | |
5171 | goto error; | |
5172 | } | |
5173 | } | |
5174 | TRACE_0(NativeCallComplete); | |
5175 | goto end_call; | goto end_call; |
5176 | } | } |
5177 | } | } |
# | Line 5143 | Line 5187 |
5187 | } | } |
5188 | #endif | #endif |
5189 | regs.sp = vp + 1; | regs.sp = vp + 1; |
5190 | LOAD_INTERRUPT_HANDLER(cx); | CHECK_INTERRUPT_HANDLER(); |
5191 | if (!ok) | if (!ok) |
5192 | goto error; | goto error; |
5193 | JS_RUNTIME_METER(rt, nonInlineCalls); | JS_RUNTIME_METER(rt, nonInlineCalls); |
5194 | TRACE_0(NativeCallComplete); | |
5195 | ||
5196 | end_call: | end_call: |
5197 | #if JS_HAS_LVALUE_RETURN | #if JS_HAS_LVALUE_RETURN |
# | Line 5181 | Line 5226 |
5226 | vp = regs.sp - argc - 2; | vp = regs.sp - argc - 2; |
5227 | ok = js_Invoke(cx, argc, vp, 0); | ok = js_Invoke(cx, argc, vp, 0); |
5228 | regs.sp = vp + 1; | regs.sp = vp + 1; |
5229 | LOAD_INTERRUPT_HANDLER(cx); | CHECK_INTERRUPT_HANDLER(); |
5230 | if (!ok) | if (!ok) |
5231 | goto error; | goto error; |
5232 | if (!cx->rval2set) { | if (!cx->rval2set) { |
5233 | op2 = (JSOp) regs.pc[JSOP_SETCALL_LENGTH]; | op2 = js_GetOpcode(cx, script, regs.pc + JSOP_SETCALL_LENGTH); |
5234 | if (op2 != JSOP_DELELEM) { | if (op2 != JSOP_DELELEM) { |
JS_ASSERT(!(js_CodeSpec[op2].format & JOF_DEL)); | ||
5235 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
5236 | JSMSG_BAD_LEFTSIDE_OF_ASS); | JSMSG_BAD_LEFTSIDE_OF_ASS); |
5237 | goto error; | goto error; |
# | Line 5226 | Line 5270 |
5270 | ||
5271 | if (PCVAL_IS_SLOT(entry->vword)) { | if (PCVAL_IS_SLOT(entry->vword)) { |
5272 | slot = PCVAL_TO_SLOT(entry->vword); | slot = PCVAL_TO_SLOT(entry->vword); |
5273 | JS_ASSERT(slot < obj2->map->freeslot); | JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); |
5274 | rval = LOCKED_OBJ_GET_SLOT(obj2, slot); | rval = LOCKED_OBJ_GET_SLOT(obj2, slot); |
5275 | JS_UNLOCK_OBJ(cx, obj2); | JS_UNLOCK_OBJ(cx, obj2); |
5276 | goto do_push_rval; | goto do_push_rval; |
# | Line 5237 | Line 5281 |
5281 | goto do_native_get; | goto do_native_get; |
5282 | } | } |
5283 | } else { | } else { |
entry = NULL; | ||
5284 | LOAD_ATOM(0); | LOAD_ATOM(0); |
5285 | } | } |
5286 | ||
5287 | id = ATOM_TO_JSID(atom); | id = ATOM_TO_JSID(atom); |
5288 | if (js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry) < 0) | if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop)) |
5289 | goto error; | goto error; |
5290 | if (!prop) { | if (!prop) { |
5291 | /* Kludge to allow (typeof foo == "undefined") tests. */ | /* Kludge to allow (typeof foo == "undefined") tests. */ |
5292 | endpc = script->code + script->length; | endpc = script->code + script->length; |
5293 | op2 = (JSOp) regs.pc[JSOP_NAME_LENGTH]; | op2 = js_GetOpcode(cx, script, regs.pc + JSOP_NAME_LENGTH); |
5294 | if (op2 == JSOP_TYPEOF) { | if (op2 == JSOP_TYPEOF) { |
5295 | PUSH_OPND(JSVAL_VOID); | PUSH_OPND(JSVAL_VOID); |
5296 | len = JSOP_NAME_LENGTH; | len = JSOP_NAME_LENGTH; |
# | Line 5261 | Line 5304 |
5304 | OBJ_DROP_PROPERTY(cx, obj2, prop); | OBJ_DROP_PROPERTY(cx, obj2, prop); |
5305 | if (!OBJ_GET_PROPERTY(cx, obj, id, &rval)) | if (!OBJ_GET_PROPERTY(cx, obj, id, &rval)) |
5306 | goto error; | goto error; |
entry = NULL; | ||
5307 | } else { | } else { |
5308 | sprop = (JSScopeProperty *)prop; | sprop = (JSScopeProperty *)prop; |
5309 | do_native_get: | do_native_get: |
# | Line 5453 | Line 5495 |
5495 | END_CASE(JSOP_ONE) | END_CASE(JSOP_ONE) |
5496 | ||
5497 | BEGIN_CASE(JSOP_NULL) | BEGIN_CASE(JSOP_NULL) |
BEGIN_CASE(JSOP_NULLTHIS) | ||
5498 | PUSH_OPND(JSVAL_NULL); | PUSH_OPND(JSVAL_NULL); |
5499 | END_CASE(JSOP_NULL) | END_CASE(JSOP_NULL) |
5500 | ||
# | Line 5619 | Line 5660 |
5660 | break; | break; |
5661 | } | } |
5662 | JS_ASSERT(status == JSTRAP_CONTINUE); | JS_ASSERT(status == JSTRAP_CONTINUE); |
5663 | LOAD_INTERRUPT_HANDLER(cx); | CHECK_INTERRUPT_HANDLER(); |
5664 | JS_ASSERT(JSVAL_IS_INT(rval)); | JS_ASSERT(JSVAL_IS_INT(rval)); |
5665 | op = (JSOp) JSVAL_TO_INT(rval); | op = (JSOp) JSVAL_TO_INT(rval); |
5666 | JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); | JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); |
# | Line 5661 | Line 5702 |
5702 | JS_ASSERT(slot < fp->fun->nargs); | JS_ASSERT(slot < fp->fun->nargs); |
5703 | METER_SLOT_OP(op, slot); | METER_SLOT_OP(op, slot); |
5704 | vp = &fp->argv[slot]; | vp = &fp->argv[slot]; |
GC_POKE(cx, *vp); | ||
5705 | *vp = FETCH_OPND(-1); | *vp = FETCH_OPND(-1); |
5706 | END_SET_CASE(JSOP_SETARG) | END_SET_CASE(JSOP_SETARG) |
5707 | ||
# | Line 5682 | Line 5722 |
5722 | slot = GET_SLOTNO(regs.pc); | slot = GET_SLOTNO(regs.pc); |
5723 | JS_ASSERT(slot < script->nslots); | JS_ASSERT(slot < script->nslots); |
5724 | vp = &fp->slots[slot]; | vp = &fp->slots[slot]; |
GC_POKE(cx, *vp); | ||
5725 | *vp = FETCH_OPND(-1); | *vp = FETCH_OPND(-1); |
5726 | END_SET_CASE(JSOP_SETLOCAL) | END_SET_CASE(JSOP_SETLOCAL) |
5727 | ||
5728 | BEGIN_CASE(JSOP_GETUPVAR) | BEGIN_CASE(JSOP_GETUPVAR) |
5729 | BEGIN_CASE(JSOP_CALLUPVAR) | BEGIN_CASE(JSOP_CALLUPVAR) |
5730 | { | { |
5731 | JSUpvarArray *uva; | JSUpvarArray *uva = JS_SCRIPT_UPVARS(script); |
uint32 skip; | ||
JSStackFrame *fp2; | ||
5732 | ||
5733 | index = GET_UINT16(regs.pc); | index = GET_UINT16(regs.pc); |
uva = JS_SCRIPT_UPVARS(script); | ||
5734 | JS_ASSERT(index < uva->length); | JS_ASSERT(index < uva->length); |
skip = UPVAR_FRAME_SKIP(uva->vector[index]); | ||
fp2 = cx->display[script->staticDepth - skip]; | ||
JS_ASSERT(fp2->fun && fp2->script); | ||
slot = UPVAR_FRAME_SLOT(uva->vector[index]); | ||
if (slot < fp2->fun->nargs) { | ||
vp = fp2->argv; | ||
} else { | ||
slot -= fp2->fun->nargs; | ||
JS_ASSERT(slot < fp2->script->nslots); | ||
vp = fp2->slots; | ||
} | ||
5735 | ||
5736 | PUSH_OPND(vp[slot]); | rval = js_GetUpvar(cx, script->staticLevel, uva->vector[index]); |
5737 | PUSH_OPND(rval); | |
5738 | ||
5739 | if (op == JSOP_CALLUPVAR) | if (op == JSOP_CALLUPVAR) |
5740 | PUSH_OPND(JSVAL_NULL); | PUSH_OPND(JSVAL_NULL); |
5741 | } | } |
5742 | END_CASE(JSOP_GETUPVAR) | END_CASE(JSOP_GETUPVAR) |
5743 | ||
5744 | BEGIN_CASE(JSOP_GETUPVAR_DBG) | |
5745 | BEGIN_CASE(JSOP_CALLUPVAR_DBG) | |
5746 | fun = fp->fun; | |
5747 | JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED); | |
5748 | JS_ASSERT(fun->u.i.wrapper); | |
5749 | ||
5750 | /* Scope for tempPool mark and local names allocation in it. */ | |
5751 | { | |
5752 | void *mark = JS_ARENA_MARK(&cx->tempPool); | |
5753 | jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool); | |
5754 | if (!names) | |
5755 | goto error; | |
5756 | ||
5757 | index = fun->countArgsAndVars() + GET_UINT16(regs.pc); | |
5758 | atom = JS_LOCAL_NAME_TO_ATOM(names[index]); | |
5759 | id = ATOM_TO_JSID(atom); | |
5760 | ||
5761 | ok = js_FindProperty(cx, id, &obj, &obj2, &prop); | |
5762 | JS_ARENA_RELEASE(&cx->tempPool, mark); | |
5763 | if (!ok) | |
5764 | goto error; | |
5765 | } | |
5766 | ||
5767 | if (!prop) | |
5768 | goto atom_not_defined; | |
5769 | ||
5770 | /* Minimize footprint with generic code instead of NATIVE_GET. */ | |
5771 | OBJ_DROP_PROPERTY(cx, obj2, prop); | |
5772 | vp = regs.sp; | |
5773 | PUSH_OPND(JSVAL_NULL); | |
5774 | if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) | |
5775 | goto error; | |
5776 | ||
5777 | if (op == JSOP_CALLUPVAR_DBG) | |
5778 | PUSH_OPND(JSVAL_NULL); | |
5779 | END_CASE(JSOP_GETUPVAR_DBG) | |
5780 | ||
5781 | BEGIN_CASE(JSOP_GETDSLOT) | |
5782 | BEGIN_CASE(JSOP_CALLDSLOT) | |
5783 | obj = fp->callee; | |
5784 | JS_ASSERT(obj); | |
5785 | JS_ASSERT(obj->dslots); | |
5786 | ||
5787 | index = GET_UINT16(regs.pc); | |
5788 | JS_ASSERT(JS_INITIAL_NSLOTS + index < jsatomid(obj->dslots[-1])); | |
5789 | JS_ASSERT_IF(OBJ_SCOPE(obj)->object == obj, | |
5790 | JS_INITIAL_NSLOTS + index < OBJ_SCOPE(obj)->freeslot); | |
5791 | ||
5792 | PUSH_OPND(obj->dslots[index]); | |
5793 | if (op == JSOP_CALLDSLOT) | |
5794 | PUSH_OPND(JSVAL_NULL); | |
5795 | END_CASE(JSOP_GETDSLOT) | |
5796 | ||
5797 | BEGIN_CASE(JSOP_GETGVAR) | BEGIN_CASE(JSOP_GETGVAR) |
5798 | BEGIN_CASE(JSOP_CALLGVAR) | BEGIN_CASE(JSOP_CALLGVAR) |
5799 | slot = GET_SLOTNO(regs.pc); | slot = GET_SLOTNO(regs.pc); |
# | Line 5746 | Line 5825 |
5825 | * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval] | * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval] |
5826 | * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME. | * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME. |
5827 | */ | */ |
5828 | #ifdef JS_TRACER | |
5829 | if (TRACE_RECORDER(cx)) | |
5830 | js_AbortRecording(cx, "SETGVAR with NULL slot"); | |
5831 | #endif | |
5832 | LOAD_ATOM(0); | LOAD_ATOM(0); |
5833 | id = ATOM_TO_JSID(atom); | id = ATOM_TO_JSID(atom); |
5834 | if (!OBJ_SET_PROPERTY(cx, obj, id, &rval)) | if (!OBJ_SET_PROPERTY(cx, obj, id, &rval)) |
# | Line 5777 | Line 5860 |
5860 | ||
5861 | /* Lookup id in order to check for redeclaration problems. */ | /* Lookup id in order to check for redeclaration problems. */ |
5862 | id = ATOM_TO_JSID(atom); | id = ATOM_TO_JSID(atom); |
5863 | prop = NULL; | |
5864 | if (!js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop)) | if (!js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop)) |
5865 | goto error; | goto error; |
5866 | ||
# | Line 5799 | Line 5883 |
5883 | */ | */ |
5884 | if (!fp->fun && | if (!fp->fun && |
5885 | index < GlobalVarCount(fp) && | index < GlobalVarCount(fp) && |
(attrs & JSPROP_PERMANENT) && | ||
5886 | obj2 == obj && | obj2 == obj && |
5887 | OBJ_IS_NATIVE(obj)) { | OBJ_IS_NATIVE(obj)) { |
5888 | sprop = (JSScopeProperty *) prop; | sprop = (JSScopeProperty *) prop; |
5889 | if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) && | if ((sprop->attrs & JSPROP_PERMANENT) && |
5890 | SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) && | |
5891 | SPROP_HAS_STUB_GETTER(sprop) && | SPROP_HAS_STUB_GETTER(sprop) && |
5892 | SPROP_HAS_STUB_SETTER(sprop)) { | SPROP_HAS_STUB_SETTER(sprop)) { |
5893 | /* | /* |
# | Line 5821 | Line 5905 |
5905 | END_CASE(JSOP_DEFVAR) | END_CASE(JSOP_DEFVAR) |
5906 | ||
5907 | BEGIN_CASE(JSOP_DEFFUN) | BEGIN_CASE(JSOP_DEFFUN) |
5908 | { | |
5909 | JSPropertyOp getter, setter; | |
5910 | bool doSet; | |
5911 | JSObject *pobj; | |
5912 | JSProperty *prop; | |
5913 | uint32 old; | |
5914 | ||
5915 | /* | /* |
5916 | * A top-level function defined in Global or Eval code (see | * A top-level function defined in Global or Eval code (see |
5917 | * ECMA-262 Ed. 3), or else a SpiderMonkey extension: a named | * ECMA-262 Ed. 3), or else a SpiderMonkey extension: a named |
# | Line 5829 | Line 5920 |
5920 | * function body). | * function body). |
5921 | */ | */ |
5922 | LOAD_FUNCTION(0); | LOAD_FUNCTION(0); |
5923 | obj = FUN_OBJECT(fun); | |
5924 | ||
5925 | if (!fp->blockChain) { | if (FUN_NULL_CLOSURE(fun)) { |
5926 | /* | |
5927 | * Even a null closure needs a parent for principals finding. | |
5928 | * FIXME: bug 476950, although debugger users may also demand | |
5929 | * some kind of scope link for debugger-assisted eval-in-frame. | |
5930 | */ | |
5931 | obj2 = fp->scopeChain; | obj2 = fp->scopeChain; |
5932 | } else { | } else { |
5933 | obj2 = js_GetScopeChain(cx, fp); | JS_ASSERT(!FUN_FLAT_CLOSURE(fun)); |
5934 | if (!obj2) | |
5935 | goto error; | /* |
5936 | * Inline js_GetScopeChain a bit to optimize for the case of a | |
5937 | * top-level function. | |
5938 | */ | |
5939 | if (!fp->blockChain) { | |
5940 | obj2 = fp->scopeChain; | |
5941 | } else { | |
5942 | obj2 = js_GetScopeChain(cx, fp); | |
5943 | if (!obj2) | |
5944 | goto error; | |
5945 | } | |
5946 | } | } |
5947 | ||
5948 | /* | /* |
5949 | * If static link is not current scope, clone fun's object to link | * If static link is not current scope, clone fun's object to link |
5950 | * to the current scope via parent. This clause exists to enable | * to the current scope via parent. We do this to enable sharing of |
5951 | * sharing of compiled functions among multiple equivalent scopes, | * compiled functions among multiple equivalent scopes, amortizing |
5952 | * splitting the cost of compilation evenly among the scopes and | * the cost of compilation over a number of executions. Examples |
5953 | * amortizing it over a number of executions. Examples include XUL | * include XUL scripts and event handlers shared among Firefox or |
5954 | * scripts and event handlers shared among Mozilla chrome windows, | * other Mozilla app chrome windows, and user-defined JS functions |
5955 | * and server-side JS user-defined functions shared among requests. | * precompiled and then shared among requests in server-side JS. |
5956 | */ | */ |
obj = FUN_OBJECT(fun); | ||
5957 | if (OBJ_GET_PARENT(cx, obj) != obj2) { | if (OBJ_GET_PARENT(cx, obj) != obj2) { |
5958 | obj = js_CloneFunctionObject(cx, fun, obj2); | obj = js_CloneFunctionObject(cx, fun, obj2); |
5959 | if (!obj) | if (!obj) |
# | Line 5859 | Line 5965 |
5965 | * paths from here must flow through the "Restore fp->scopeChain" | * paths from here must flow through the "Restore fp->scopeChain" |
5966 | * code below the OBJ_DEFINE_PROPERTY call. | * code below the OBJ_DEFINE_PROPERTY call. |
5967 | */ | */ |
5968 | MUST_FLOW_THROUGH("restore"); | MUST_FLOW_THROUGH("restore_scope"); |
5969 | fp->scopeChain = obj; | fp->scopeChain = obj; |
5970 | ||
5971 | rval = OBJECT_TO_JSVAL(obj); | rval = OBJECT_TO_JSVAL(obj); |
5972 | ||
5973 | /* | /* |
# | Line 5876 | Line 5983 |
5983 | * and setters do not need a slot, their value is stored elsewhere | * and setters do not need a slot, their value is stored elsewhere |
5984 | * in the property itself, not in obj slots. | * in the property itself, not in obj slots. |
5985 | */ | */ |
5986 | setter = getter = JS_PropertyStub; | |
5987 | flags = JSFUN_GSFLAG2ATTR(fun->flags); | flags = JSFUN_GSFLAG2ATTR(fun->flags); |
5988 | if (flags) { | if (flags) { |
5989 | /* Function cannot be both getter a setter. */ | |
5990 | JS_ASSERT(flags == JSPROP_GETTER || flags == JSPROP_SETTER); | |
5991 | attrs |= flags | JSPROP_SHARED; | attrs |= flags | JSPROP_SHARED; |
5992 | rval = JSVAL_VOID; | rval = JSVAL_VOID; |
5993 | if (flags == JSPROP_GETTER) | |
5994 | getter = js_CastAsPropertyOp(obj); | |
5995 | else | |
5996 | setter = js_CastAsPropertyOp(obj); | |
5997 | } | } |
5998 | ||
5999 | /* | /* |
# | Line 5898 | Line 6012 |
6012 | * as well as multiple HTML script tags. | * as well as multiple HTML script tags. |
6013 | */ | */ |
6014 | id = ATOM_TO_JSID(fun->atom); | id = ATOM_TO_JSID(fun->atom); |
6015 | prop = NULL; | |
6016 | ok = js_CheckRedeclaration(cx, parent, id, attrs, &pobj, &prop); | |
6017 | if (!ok) | |
6018 | goto restore_scope; | |
6019 | ||
6020 | /* | |
6021 | * We deviate from 10.1.2 in ECMA 262 v3 and under eval use for | |
6022 | * function declarations OBJ_SET_PROPERTY, not OBJ_DEFINE_PROPERTY, | |
6023 | * to preserve the JSOP_PERMANENT attribute of existing properties | |
6024 | * and make sure that such properties cannot be deleted. | |
6025 | * | |
6026 | * We also use OBJ_SET_PROPERTY for the existing properties of | |
6027 | * Call objects with matching attributes to preserve the native | |
6028 | * getters and setters that store the value of the property in the | |
6029 | * interpreter frame, see bug 467495. | |
6030 | */ | |
6031 | doSet = (attrs == JSPROP_ENUMERATE); | |
6032 | JS_ASSERT_IF(doSet, fp->flags & JSFRAME_EVAL); | |
6033 | if (prop) { | |
6034 | if (parent == pobj && | |
6035 | OBJ_GET_CLASS(cx, parent) == &js_CallClass && | |
6036 | (old = ((JSScopeProperty *) prop)->attrs, | |
6037 | !(old & (JSPROP_GETTER|JSPROP_SETTER)) && | |
6038 | (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs)) { | |
6039 | /* | |
6040 | * js_CheckRedeclaration must reject attempts to add a | |
6041 | * getter or setter to an existing property without a | |
6042 | * getter or setter. | |
6043 | */ | |
6044 | JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); | |
6045 | JS_ASSERT(!(old & JSPROP_READONLY)); | |
6046 | doSet = JS_TRUE; | |
6047 | } | |
6048 | OBJ_DROP_PROPERTY(cx, pobj, prop); | |
6049 | } | |
6050 | ok = doSet | |
6051 | ? OBJ_SET_PROPERTY(cx, parent, id, &rval) | |
6052 | : OBJ_DEFINE_PROPERTY(cx, parent, id, rval, getter, setter, | |
6053 | attrs, NULL); | |
6054 | ||
6055 | restore_scope: | |
6056 | /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ | |
6057 | fp->scopeChain = obj2; | |
6058 | if (!ok) { | |
6059 | cx->weakRoots.newborn[GCX_OBJECT] = NULL; | |
6060 | goto error; | |
6061 | } | |
6062 | } | |
6063 | END_CASE(JSOP_DEFFUN) | |
6064 | ||
6065 | BEGIN_CASE(JSOP_DEFFUN_FC) | |
6066 | BEGIN_CASE(JSOP_DEFFUN_DBGFC) | |
6067 | LOAD_FUNCTION(0); | |
6068 | ||
6069 | obj = (op == JSOP_DEFFUN_FC) | |
6070 | ? js_NewFlatClosure(cx, fun) | |
6071 | : js_NewDebuggableFlatClosure(cx, fun); | |
6072 | if (!obj) | |
6073 | goto error; | |
6074 | rval = OBJECT_TO_JSVAL(obj); | |
6075 | ||
6076 | attrs = (fp->flags & JSFRAME_EVAL) | |
6077 | ? JSPROP_ENUMERATE | |
6078 | : JSPROP_ENUMERATE | JSPROP_PERMANENT; | |
6079 | ||
6080 | flags = JSFUN_GSFLAG2ATTR(fun->flags); | |
6081 | if (flags) { | |
6082 | attrs |= flags | JSPROP_SHARED; | |
6083 | rval = JSVAL_VOID; | |
6084 | } | |
6085 | ||
6086 | parent = fp->varobj; | |
6087 | JS_ASSERT(parent); | |
6088 | ||
6089 | id = ATOM_TO_JSID(fun->atom); | |
6090 | ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL); | ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL); |
6091 | if (ok) { | if (ok) { |
6092 | if (attrs == JSPROP_ENUMERATE) { | if (attrs == JSPROP_ENUMERATE) { |
# | Line 5918 | Line 6107 |
6107 | } | } |
6108 | } | } |
6109 | ||
/* Restore fp->scopeChain now that obj is defined in fp->varobj. */ | ||
MUST_FLOW_LABEL(restore) | ||
fp->scopeChain = obj2; | ||
6110 | if (!ok) { | if (!ok) { |
6111 | cx->weakRoots.newborn[GCX_OBJECT] = NULL; | cx->weakRoots.newborn[GCX_OBJECT] = NULL; |
6112 | goto error; | goto error; |
6113 | } | } |
6114 | END_CASE(JSOP_DEFFUN) | END_CASE(JSOP_DEFFUN_FC) |
6115 | ||
6116 | BEGIN_CASE(JSOP_DEFLOCALFUN) | BEGIN_CASE(JSOP_DEFLOCALFUN) |
LOAD_FUNCTION(SLOTNO_LEN); | ||
6117 | /* | /* |
6118 | * Define a local function (i.e., one nested at the top level of | * Define a local function (i.e., one nested at the top level of |
6119 | * another function), parented by the current scope chain, and | * another function), parented by the current scope chain, stored |
6120 | * stored in a local variable slot that the compiler allocated. | * in a local variable slot that the compiler allocated. This is |
6121 | * This is an optimization over JSOP_DEFFUN that avoids requiring | * an optimization over JSOP_DEFFUN that avoids requiring a call |
6122 | * a call object for the outer function's activation. | * object for the outer function's activation. |
6123 | */ | */ |
6124 | slot = GET_SLOTNO(regs.pc); | LOAD_FUNCTION(SLOTNO_LEN); |
6125 | JS_ASSERT(FUN_INTERPRETED(fun)); | |
6126 | parent = js_GetScopeChain(cx, fp); | JS_ASSERT(!FUN_FLAT_CLOSURE(fun)); |
if (!parent) | ||
goto error; | ||
6127 | obj = FUN_OBJECT(fun); | obj = FUN_OBJECT(fun); |
6128 | if (OBJ_GET_PARENT(cx, obj) != parent) { | |
6129 | obj = js_CloneFunctionObject(cx, fun, parent); | if (FUN_NULL_CLOSURE(fun)) { |
6130 | obj = js_CloneFunctionObject(cx, fun, fp->scopeChain); | |
6131 | if (!obj) | if (!obj) |
6132 | goto error; | goto error; |
6133 | } else { | |
6134 | parent = js_GetScopeChain(cx, fp); | |
6135 | if (!parent) | |
6136 | goto error; | |
6137 | ||
6138 | if (OBJ_GET_PARENT(cx, obj) != parent) { | |
6139 | #ifdef JS_TRACER | |
6140 | if (TRACE_RECORDER(cx)) | |
6141 | js_AbortRecording(cx, "DEFLOCALFUN for closure"); | |
6142 | #endif | |
6143 | obj = js_CloneFunctionObject(cx, fun, parent); | |
6144 | if (!obj) | |
6145 | goto error; | |
6146 | } | |
6147 | } | } |
6148 | ||
6149 | slot = GET_SLOTNO(regs.pc); | |
6150 | TRACE_2(DefLocalFunSetSlot, slot, obj); | TRACE_2(DefLocalFunSetSlot, slot, obj); |
6151 | ||
6152 | fp->slots[slot] = OBJECT_TO_JSVAL(obj); | fp->slots[slot] = OBJECT_TO_JSVAL(obj); |
6153 | END_CASE(JSOP_DEFLOCALFUN) | END_CASE(JSOP_DEFLOCALFUN) |
6154 | ||
6155 | BEGIN_CASE(JSOP_ANONFUNOBJ) | BEGIN_CASE(JSOP_DEFLOCALFUN_FC) |
6156 | /* Load the specified function object literal. */ | LOAD_FUNCTION(SLOTNO_LEN); |
LOAD_FUNCTION(0); | ||
6157 | ||
6158 | /* If re-parenting, push a clone of the function object. */ | obj = js_NewFlatClosure(cx, fun); |
6159 | parent = js_GetScopeChain(cx, fp); | if (!obj) |
if (!parent) | ||
6160 | goto error; | goto error; |
6161 | ||
6162 | slot = GET_SLOTNO(regs.pc); | |
6163 | TRACE_2(DefLocalFunSetSlot, slot, obj); | |
6164 | ||
6165 | fp->slots[slot] = OBJECT_TO_JSVAL(obj); | |
6166 | END_CASE(JSOP_DEFLOCALFUN_FC) | |
6167 | ||
6168 | BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC) | |
6169 | LOAD_FUNCTION(SLOTNO_LEN); | |
6170 | ||
6171 | obj = js_NewDebuggableFlatClosure(cx, fun); | |
6172 | if (!obj) | |
6173 | goto error; | |
6174 | ||
6175 | slot = GET_SLOTNO(regs.pc); | |
6176 | fp->slots[slot] = OBJECT_TO_JSVAL(obj); | |
6177 | END_CASE(JSOP_DEFLOCALFUN_DBGFC) | |
6178 | ||
6179 | BEGIN_CASE(JSOP_LAMBDA) | |
6180 | /* Load the specified function object literal. */ | |
6181 | LOAD_FUNCTION(0); | |
6182 | obj = FUN_OBJECT(fun); | obj = FUN_OBJECT(fun); |
6183 | if (OBJ_GET_PARENT(cx, obj) != parent) { | |
6184 | obj = js_CloneFunctionObject(cx, fun, parent); | if (FUN_NULL_CLOSURE(fun)) { |
6185 | obj = js_CloneFunctionObject(cx, fun, fp->scopeChain); | |
6186 | if (!obj) | if (!obj) |
6187 | goto error; | goto error; |
6188 | } else { | |
6189 | parent = js_GetScopeChain(cx, fp); | |
6190 | if (!parent) | |
6191 | goto error; | |
6192 | ||
6193 | /* If re-parenting, push a clone of the function object. */ | |
6194 | if (OBJ_GET_PARENT(cx, obj) != parent) { | |
6195 | obj = js_CloneFunctionObject(cx, fun, parent); | |
6196 | if (!obj) | |
6197 | goto error; | |
6198 | } | |
6199 | } | } |
6200 | ||
6201 | PUSH_OPND(OBJECT_TO_JSVAL(obj)); | PUSH_OPND(OBJECT_TO_JSVAL(obj)); |
6202 | END_CASE(JSOP_ANONFUNOBJ) | END_CASE(JSOP_LAMBDA) |
6203 | ||
6204 | BEGIN_CASE(JSOP_NAMEDFUNOBJ) | BEGIN_CASE(JSOP_LAMBDA_FC) |
6205 | LOAD_FUNCTION(0); | LOAD_FUNCTION(0); |
6206 | ||
6207 | /* | obj = js_NewFlatClosure(cx, fun); |
* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. | ||
* | ||
* 1. Create a new object as if by the expression new Object(). | ||
* 2. Add Result(1) to the front of the scope chain. | ||
* | ||
* Step 2 is achieved by making the new object's parent be the | ||
* current scope chain, and then making the new object the parent | ||
* of the Function object clone. | ||
*/ | ||
obj2 = js_GetScopeChain(cx, fp); | ||
if (!obj2) | ||
goto error; | ||
parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2, 0); | ||
if (!parent) | ||
goto error; | ||
/* | ||
* 3. Create a new Function object as specified in section 13.2 | ||
* with [parameters and body specified by the function expression | ||
* that was parsed by the compiler into a Function object, and | ||
* saved in the script's atom map]. | ||
* | ||
* Protect parent from the GC. | ||
*/ | ||
fp->scopeChain = parent; | ||
obj = js_CloneFunctionObject(cx, fun, parent); | ||
6208 | if (!obj) | if (!obj) |
6209 | goto error; | goto error; |
6210 | ||
6211 | /* | PUSH_OPND(OBJECT_TO_JSVAL(obj)); |
6212 | * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All | END_CASE(JSOP_LAMBDA_FC) |
* paths from here must flow through the "Restore fp->scopeChain" | ||
* code below the OBJ_DEFINE_PROPERTY call. | ||
*/ | ||
MUST_FLOW_THROUGH("restore2"); | ||
fp->scopeChain = obj; | ||
rval = OBJECT_TO_JSVAL(obj); | ||
6213 | ||
6214 | /* | BEGIN_CASE(JSOP_LAMBDA_DBGFC) |
6215 | * 4. Create a property in the object Result(1). The property's | LOAD_FUNCTION(0); |
* name is [fun->atom, the identifier parsed by the compiler], | ||
* value is Result(3), and attributes are { DontDelete, ReadOnly }. | ||
*/ | ||
attrs = JSFUN_GSFLAG2ATTR(fun->flags); | ||
if (attrs) { | ||
attrs |= JSPROP_SHARED; | ||
rval = JSVAL_VOID; | ||
} | ||
ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, | ||
(attrs & JSPROP_GETTER) | ||
? JS_EXTENSION (JSPropertyOp) obj | ||
: JS_PropertyStub, | ||
(attrs & JSPROP_SETTER) | ||
? JS_EXTENSION (JSPropertyOp) obj | ||
: JS_PropertyStub, | ||
attrs | | ||
JSPROP_ENUMERATE | JSPROP_PERMANENT | | ||
JSPROP_READONLY, | ||
NULL); | ||
6216 | ||
6217 | /* Restore fp->scopeChain now that obj is defined in parent. */ | obj = js_NewDebuggableFlatClosure(cx, fun); |
6218 | MUST_FLOW_LABEL(restore2) | if (!obj) |
fp->scopeChain = obj2; | ||
if (!ok) { | ||
cx->weakRoots.newborn[GCX_OBJECT] = NULL; | ||
6219 | goto error; | goto error; |
} | ||
6220 | ||
/* | ||
* 5. Remove Result(1) from the front of the scope chain [no-op]. | ||
* 6. Return Result(3). | ||
*/ | ||
6221 | PUSH_OPND(OBJECT_TO_JSVAL(obj)); | PUSH_OPND(OBJECT_TO_JSVAL(obj)); |
6222 | END_CASE(JSOP_NAMEDFUNOBJ) | END_CASE(JSOP_LAMBDA_DBGFC) |
6223 | ||
6224 | BEGIN_CASE(JSOP_CALLEE) | |
6225 | PUSH_OPND(OBJECT_TO_JSVAL(fp->callee)); | |
6226 | END_CASE(JSOP_CALLEE) | |
6227 | ||
6228 | #if JS_HAS_GETTER_SETTER | #if JS_HAS_GETTER_SETTER |
6229 | BEGIN_CASE(JSOP_GETTER) | BEGIN_CASE(JSOP_GETTER) |
# | Line 6157 | Line 6331 |
6331 | END_CASE(JSOP_HOLE) | END_CASE(JSOP_HOLE) |
6332 | ||
6333 | BEGIN_CASE(JSOP_NEWARRAY) | BEGIN_CASE(JSOP_NEWARRAY) |
6334 | len = GET_UINT24(regs.pc); | len = GET_UINT16(regs.pc); |
6335 | JS_ASSERT(len <= regs.sp - StackBase(fp)); | cx->fp->assertValidStackDepth(len); |
6336 | obj = js_NewArrayObject(cx, len, regs.sp - len, JS_TRUE); | obj = js_NewArrayObject(cx, len, regs.sp - len, JS_TRUE); |
6337 | if (!obj) | if (!obj) |
6338 | goto error; | goto error; |
# | Line 6176 | Line 6350 |
6350 | goto error; | goto error; |
6351 | PUSH_OPND(OBJECT_TO_JSVAL(obj)); | PUSH_OPND(OBJECT_TO_JSVAL(obj)); |
6352 | fp->sharpDepth++; | fp->sharpDepth++; |
6353 | LOAD_INTERRUPT_HANDLER(cx); | CHECK_INTERRUPT_HANDLER(); |
6354 | END_CASE(JSOP_NEWINIT) | END_CASE(JSOP_NEWINIT) |
6355 | ||
6356 | BEGIN_CASE(JSOP_ENDINIT) | BEGIN_CASE(JSOP_ENDINIT) |
# | Line 6215 | Line 6389 |
6389 | kshape = scope->shape; | kshape = scope->shape; |
6390 | cache = &JS_PROPERTY_CACHE(cx); | cache = &JS_PROPERTY_CACHE(cx); |
6391 | entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)]; | entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)]; |
6392 | PCMETER(cache->pctestentry = entry); | |
6393 | PCMETER(cache->tests++); | PCMETER(cache->tests++); |
6394 | PCMETER(cache->initests++); | PCMETER(cache->initests++); |
6395 | ||
6396 | if (entry->kpc == regs.pc && entry->kshape == kshape) { | if (entry->kpc == regs.pc && |
6397 | entry->kshape == kshape && | |
6398 | PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape) { | |
6399 | JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0); | |
6400 | ||
6401 | PCMETER(cache->pchits++); | PCMETER(cache->pchits++); |
6402 | PCMETER(cache->inipchits++); | PCMETER(cache->inipchits++); |
6403 | ||
# | Line 6251 | Line 6430 |
6430 | if (sprop->parent != scope->lastProp) | if (sprop->parent != scope->lastProp) |
6431 | goto do_initprop_miss; | goto do_initprop_miss; |
6432 | ||
TRACE_2(SetPropHit, entry, sprop); | ||
6433 | /* | /* |
6434 | * Otherwise this entry must be for a direct property of | * Otherwise this entry must be for a direct property of |
6435 | * obj, not a proto-property, and there cannot have been | * obj, not a proto-property, and there cannot have been |
6436 | * any deletions of prior properties. | * any deletions of prior properties. |
6437 | */ | */ |
JS_ASSERT(PCVCAP_MAKE(sprop->shape, 0, 0) == entry->vcap); | ||
6438 | JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope)); | JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope)); |
6439 | JS_ASSERT(!scope->table || | JS_ASSERT(!scope->table || |
6440 | !SCOPE_HAS_PROPERTY(scope, sprop)); | !SCOPE_HAS_PROPERTY(scope, sprop)); |
6441 | ||
6442 | slot = sprop->slot; | slot = sprop->slot; |
6443 | JS_ASSERT(slot == scope->map.freeslot); | JS_ASSERT(slot == scope->freeslot); |
6444 | if (slot < STOBJ_NSLOTS(obj)) { | if (slot < STOBJ_NSLOTS(obj)) { |
6445 | ++scope->map.freeslot; | ++scope->freeslot; |
6446 | } else { | } else { |
6447 | if (!js_AllocSlot(cx, obj, &slot)) { | if (!js_AllocSlot(cx, obj, &slot)) { |
6448 | JS_UNLOCK_SCOPE(cx, scope); | JS_UNLOCK_SCOPE(cx, scope); |
# | Line 6290 | Line 6466 |
6466 | } | } |
6467 | JS_ASSERT(sprop2 == sprop); | JS_ASSERT(sprop2 == sprop); |
6468 | } else { | } else { |
6469 | js_LeaveTraceIfGlobalObject(cx, scope->object); | |
6470 | scope->shape = sprop->shape; | scope->shape = sprop->shape; |
6471 | ++scope->entryCount; | ++scope->entryCount; |
6472 | scope->lastProp = sprop; | scope->lastProp = sprop; |
# | Line 6298 | Line 6475 |
6475 | GC_WRITE_BARRIER(cx, scope, | GC_WRITE_BARRIER(cx, scope, |
6476 | LOCKED_OBJ_GET_SLOT(obj, slot), | LOCKED_OBJ_GET_SLOT(obj, slot), |
6477 | rval); | rval); |
6478 | TRACE_2(SetPropHit, entry, sprop); | |
6479 | LOCKED_OBJ_SET_SLOT(obj, slot, rval); | LOCKED_OBJ_SET_SLOT(obj, slot, rval); |
6480 | JS_UNLOCK_SCOPE(cx, scope); | JS_UNLOCK_SCOPE(cx, scope); |
6481 | break; | break; |
# | Line 6316 | Line 6494 |
6494 | NULL, NULL)) { | NULL, NULL)) { |
6495 | goto error; | goto error; |
6496 | } | } |
6497 | if (!js_SetPropertyHelper(cx, obj, id, &rval, &entry)) | |
6498 | if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom) | |
6499 | ? js_SetPropertyHelper(cx, obj, id, true, &rval) | |
6500 | : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL, | |
6501 | JSPROP_ENUMERATE, 0, 0, NULL, | |
6502 | JSDNP_CACHE_RESULT))) | |
6503 | goto error; | goto error; |
#ifdef JS_TRACER | ||
if (entry) | ||
TRACE_1(SetPropMiss, entry); | ||
#endif | ||
6504 | } while (0); | } while (0); |
6505 | ||
6506 | /* Common tail for property cache hit and miss cases. */ | /* Common tail for property cache hit and miss cases. */ |
# | Line 6345 | Line 6524 |
6524 | * Check for property redeclaration strict warning (we may be in | * Check for property redeclaration strict warning (we may be in |
6525 | * an object initialiser, not an array initialiser). | * an object initialiser, not an array initialiser). |
6526 | */ | */ |
6527 | if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, NULL, | if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, NULL, NULL)) |
NULL)) { | ||
6528 | goto error; | goto error; |
} | ||
6529 | ||
6530 | /* | /* |
6531 | * If rval is a hole, do not call OBJ_SET_PROPERTY. In this case, | * If rval is a hole, do not call OBJ_DEFINE_PROPERTY. In this case, |
6532 | * obj must be an array, so if the current op is the last element | * obj must be an array, so if the current op is the last element |
6533 | * initialiser, set the array length to one greater than id. | * initialiser, set the array length to one greater than id. |
6534 | */ | */ |
# | Line 6359 | Line 6536 |
6536 | JS_ASSERT(OBJ_IS_ARRAY(cx, obj)); | JS_ASSERT(OBJ_IS_ARRAY(cx, obj)); |
6537 | JS_ASSERT(JSID_IS_INT(id)); | JS_ASSERT(JSID_IS_INT(id)); |
6538 | JS_ASSERT((jsuint) JSID_TO_INT(id) < ARRAY_INIT_LIMIT); | JS_ASSERT((jsuint) JSID_TO_INT(id) < ARRAY_INIT_LIMIT); |
6539 | if ((JSOp) regs.pc[JSOP_INITELEM_LENGTH] == JSOP_ENDINIT && | if (js_GetOpcode(cx, script, regs.pc + JSOP_INITELEM_LENGTH) == JSOP_ENDINIT && |
6540 | !js_SetLengthProperty(cx, obj, | !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) { |
(jsuint) (JSID_TO_INT(id) + 1))) { | ||
6541 | goto error; | goto error; |
6542 | } | } |
6543 | } else { | } else { |
6544 | if (!OBJ_SET_PROPERTY(cx, obj, id, &rval)) | if (!OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL, JSPROP_ENUMERATE, NULL)) |
6545 | goto error; | goto error; |
6546 | } | } |
6547 | regs.sp -= 2; | regs.sp -= 2; |
# | Line 6455 | Line 6631 |
6631 | JS_ASSERT(cx->throwing); | JS_ASSERT(cx->throwing); |
6632 | PUSH(cx->exception); | PUSH(cx->exception); |
6633 | cx->throwing = JS_FALSE; | cx->throwing = JS_FALSE; |
6634 | CHECK_BRANCH(); | |
6635 | END_CASE(JSOP_EXCEPTION) | END_CASE(JSOP_EXCEPTION) |
6636 | ||
6637 | BEGIN_CASE(JSOP_FINALLY) | |
6638 | CHECK_BRANCH(); | |
6639 | END_CASE(JSOP_FINALLY) | |
6640 | ||
6641 | BEGIN_CASE(JSOP_THROWING) | BEGIN_CASE(JSOP_THROWING) |
6642 | JS_ASSERT(!cx->throwing); | JS_ASSERT(!cx->throwing); |
6643 | cx->throwing = JS_TRUE; | cx->throwing = JS_TRUE; |
# | Line 6465 | Line 6646 |
6646 | ||
6647 | BEGIN_CASE(JSOP_THROW) | BEGIN_CASE(JSOP_THROW) |
6648 | JS_ASSERT(!cx->throwing); | JS_ASSERT(!cx->throwing); |
6649 | CHECK_BRANCH(); | |
6650 | cx->throwing = JS_TRUE; | cx->throwing = JS_TRUE; |
6651 | cx->exception = POP_OPND(); | cx->exception = POP_OPND(); |
6652 | /* let the code at error try to catch the exception. */ | /* let the code at error try to catch the exception. */ |
# | Line 6481 | Line 6663 |
6663 | fp->slots[slot] = POP_OPND(); | fp->slots[slot] = POP_OPND(); |
6664 | END_CASE(JSOP_SETLOCALPOP) | END_CASE(JSOP_SETLOCALPOP) |
6665 | ||
6666 | BEGIN_CASE(JSOP_IFPRIMTOP) | |
6667 | /* | |
6668 | * If the top of stack is of primitive type, jump to our target. | |
6669 | * Otherwise advance to the next opcode. | |
6670 | */ | |
6671 | JS_ASSERT(regs.sp > StackBase(fp)); | |
6672 | rval = FETCH_OPND(-1); | |
6673 | if (JSVAL_IS_PRIMITIVE(rval)) { | |
6674 | len = GET_JUMP_OFFSET(regs.pc); | |
6675 | BRANCH(len); | |
6676 | } | |
6677 | END_CASE(JSOP_IFPRIMTOP) | |
6678 | ||
6679 | BEGIN_CASE(JSOP_PRIMTOP) | |
6680 | JS_ASSERT(regs.sp > StackBase(fp)); | |
6681 | lval = FETCH_OPND(-1); | |
6682 | i = GET_INT8(regs.pc); | |
6683 | if (!JSVAL_IS_PRIMITIVE(lval)) { | |
6684 | lval = FETCH_OPND(-2); | |
6685 | js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, | |
6686 | -2, lval, NULL, | |
6687 | (i == JSTYPE_VOID) | |
6688 | ? "primitive type" | |
6689 | : JS_TYPE_STR(i)); | |
6690 | goto error; | |
6691 | } | |
6692 | END_CASE(JSOP_PRIMTOP) | |
6693 | ||
6694 | BEGIN_CASE(JSOP_OBJTOP) | |
6695 | lval = FETCH_OPND(-1); | |
6696 | if (JSVAL_IS_PRIMITIVE(lval)) { | |
6697 | js_ReportValueError(cx, GET_UINT16(regs.pc), -1, lval, NULL); | |
6698 | goto error; | |
6699 | } | |
6700 | END_CASE(JSOP_OBJTOP) | |
6701 | ||
6702 | BEGIN_CASE(JSOP_INSTANCEOF) | BEGIN_CASE(JSOP_INSTANCEOF) |
6703 | rval = FETCH_OPND(-1); | rval = FETCH_OPND(-1); |
6704 | if (JSVAL_IS_PRIMITIVE(rval) || | if (JSVAL_IS_PRIMITIVE(rval) || |
# | Line 6518 | Line 6736 |
6736 | goto error; | goto error; |
6737 | default:; | default:; |
6738 | } | } |
6739 | LOAD_INTERRUPT_HANDLER(cx); | CHECK_INTERRUPT_HANDLER(); |
6740 | } | } |
6741 | } | } |
6742 | END_CASE(JSOP_DEBUGGER) | END_CASE(JSOP_DEBUGGER) |
# | Line 6771 | Line 6989 |
6989 | regs.sp++; | regs.sp++; |
6990 | } | } |
6991 | ||
6992 | #ifdef DEBUG | |
6993 | JS_ASSERT(fp->blockChain == OBJ_GET_PARENT(cx, obj)); | |
6994 | ||
6995 | /* | /* |
6996 | * If this frame had to reflect the compile-time block chain into | * The young end of fp->scopeChain may omit blocks if we |
6997 | * the runtime scope chain, we can't optimize block scopes out of | * haven't closed over them, but if there are any closure |
6998 | * runtime any longer, because an outer block that parents obj has | * blocks on fp->scopeChain, they'd better be (clones of) |
6999 | * been cloned onto the scope chain. To avoid re-cloning such a | * ancestors of the block we're entering now; anything |
7000 | * parent and accumulating redundant clones via js_GetScopeChain, | * else we should have popped off fp->scopeChain when we |
7001 | * we must clone each block eagerly on entry, and push it on the | * left its static scope. |
7002 | * scope chain, until this frame pops. | */ |
7003 | */ | obj2 = fp->scopeChain; |
7004 | if (fp->flags & JSFRAME_POP_BLOCKS) { | while ((clasp = OBJ_GET_CLASS(cx, obj2)) == &js_WithClass) |
7005 | JS_ASSERT(!fp->blockChain); | obj2 = OBJ_GET_PARENT(cx, obj2); |
7006 | obj = js_CloneBlockObject(cx, obj, fp->scopeChain, fp); | if (clasp == &js_BlockClass && |
7007 | if (!obj) | OBJ_GET_PRIVATE(cx, obj2) == fp) { |
7008 | goto error; | JSObject *youngestProto = OBJ_GET_PROTO(cx, obj2); |
7009 | fp->scopeChain = obj; | JS_ASSERT(!OBJ_IS_CLONED_BLOCK(youngestProto)); |
7010 | } else { | parent = obj; |
7011 | JS_ASSERT(!fp->blockChain || | while ((parent = OBJ_GET_PARENT(cx, parent)) != youngestProto) |
7012 | OBJ_GET_PARENT(cx, obj) == fp->blockChain); | JS_ASSERT(parent); |
fp->blockChain = obj; | ||
7013 | } | } |
7014 | #endif | |
7015 | ||
7016 | fp->blockChain = obj; | |
7017 | END_CASE(JSOP_ENTERBLOCK) | END_CASE(JSOP_ENTERBLOCK) |
7018 |