Parent Directory
|
Revision Log
|
Patch
revision 506 by siliconforks, Sat Sep 26 23:15:22 2009 UTC | revision 507 by siliconforks, Sun Jan 10 07:23:34 2010 UTC | |
---|---|---|
# | Line 1 | Line 1 |
1 | /* -*- Mode: C; tab-width: 4; 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 41 | Line 41 |
41 | /* | /* |
42 | * JavaScript bytecode interpreter. | * JavaScript bytecode interpreter. |
43 | */ | */ |
#include "jsstddef.h" | ||
44 | #include <stdio.h> | #include <stdio.h> |
45 | #include <string.h> | #include <string.h> |
46 | #include <math.h> | #include <math.h> |
47 | #include "jstypes.h" | #include "jstypes.h" |
48 | #include "jsstdint.h" | |
49 | #include "jsarena.h" /* Added by JSIFY */ | #include "jsarena.h" /* Added by JSIFY */ |
50 | #include "jsutil.h" /* Added by JSIFY */ | #include "jsutil.h" /* Added by JSIFY */ |
51 | #include "jsprf.h" | #include "jsprf.h" |
# | Line 71 | Line 71 |
71 | #include "jsstr.h" | #include "jsstr.h" |
72 | #include "jsstaticcheck.h" | #include "jsstaticcheck.h" |
73 | #include "jstracer.h" | #include "jstracer.h" |
74 | #include "jslibmath.h" | |
75 | #include "jsvector.h" | |
76 | #include "jsstrinlines.h" | |
77 | ||
78 | #ifdef INCLUDE_MOZILLA_DTRACE | #ifdef INCLUDE_MOZILLA_DTRACE |
79 | #include "jsdtracef.h" | #include "jsdtracef.h" |
# | Line 80 | Line 83 |
83 | #include "jsxml.h" | #include "jsxml.h" |
84 | #endif | #endif |
85 | ||
86 | #include "jsatominlines.h" | |
87 | #include "jsscriptinlines.h" | |
88 | ||
89 | #include "jsautooplen.h" | #include "jsautooplen.h" |
90 | ||
91 | /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */ | /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */ |
92 | #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ | #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___ |
93 | ||
uint32 | ||
js_GenerateShape(JSContext *cx, JSBool gcLocked) | ||
{ | ||
JSRuntime *rt; | ||
uint32 shape; | ||
rt = cx->runtime; | ||
shape = JS_ATOMIC_INCREMENT(&rt->shapeGen); | ||
JS_ASSERT(shape != 0); | ||
if (shape >= SHAPE_OVERFLOW_BIT) { | ||
/* | ||
* FIXME bug 440834: The shape id space has overflowed. Currently we | ||
* cope badly with this and schedule the GC on the every call. But | ||
* first we make sure that increments from other threads would not | ||
* have a chance to wrap around shapeGen to zero. | ||
*/ | ||
rt->shapeGen = SHAPE_OVERFLOW_BIT; | ||
js_TriggerGC(cx, gcLocked); | ||
} | ||
return shape; | ||
} | ||
94 | JS_REQUIRES_STACK JSPropCacheEntry * | JS_REQUIRES_STACK JSPropCacheEntry * |
95 | js_FillPropertyCache(JSContext *cx, JSObject *obj, | js_FillPropertyCache(JSContext *cx, JSObject *obj, |
96 | uintN scopeIndex, uintN protoIndex, JSObject *pobj, | uintN scopeIndex, uintN protoIndex, JSObject *pobj, |
# | Line 137 | Line 121 |
121 | * from pobj's scope (via unwatch or delete, e.g.). | * from pobj's scope (via unwatch or delete, e.g.). |
122 | */ | */ |
123 | scope = OBJ_SCOPE(pobj); | scope = OBJ_SCOPE(pobj); |
124 | JS_ASSERT(scope->object == pobj); | if (!scope->has(sprop)) { |
if (!SCOPE_HAS_PROPERTY(scope, sprop)) { | ||
125 | PCMETER(cache->oddfills++); | PCMETER(cache->oddfills++); |
126 | return JS_NO_PROP_CACHE_FILL; | return JS_NO_PROP_CACHE_FILL; |
127 | } | } |
# | Line 148 | Line 131 |
131 | * and setter hooks can change the prototype chain using JS_SetPrototype | * and setter hooks can change the prototype chain using JS_SetPrototype |
132 | * after js_LookupPropertyWithFlags has returned the nominal protoIndex, | * after js_LookupPropertyWithFlags has returned the nominal protoIndex, |
133 | * we have to validate protoIndex if it is non-zero. If it is zero, then | * we have to validate protoIndex if it is non-zero. If it is zero, then |
134 | * we know thanks to the SCOPE_HAS_PROPERTY test above, and from the fact | * we know thanks to the scope->has test above, combined with the fact that |
135 | * that obj == pobj, that protoIndex is invariant. | * obj == pobj, that protoIndex is invariant. |
136 | * | * |
137 | * The scopeIndex can't be wrong. We require JS_SetParent calls to happen | * The scopeIndex can't be wrong. We require JS_SetParent calls to happen |
138 | * before any running script might consult a parent-linked scope chain. If | * before any running script might consult a parent-linked scope chain. If |
# | Line 202 | Line 185 |
185 | /* | /* |
186 | * Check for a prototype "plain old method" callee computation. What | * Check for a prototype "plain old method" callee computation. What |
187 | * is a plain old method? It's a function-valued property with stub | * is a plain old method? It's a function-valued property with stub |
188 | * getter and setter, so get of a function is idempotent and set is | * getter, so get of a function is idempotent. |
* transparent. | ||
189 | */ | */ |
190 | if (cs->format & JOF_CALLOP) { | if ((cs->format & JOF_CALLOP) && |
191 | if (SPROP_HAS_STUB_GETTER(sprop) && | SPROP_HAS_STUB_GETTER(sprop) && |
192 | SPROP_HAS_VALID_SLOT(sprop, scope)) { | SPROP_HAS_VALID_SLOT(sprop, scope)) { |
193 | jsval v; | jsval v; |
194 | ||
195 | v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); | v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); |
196 | if (VALUE_IS_FUNCTION(cx, v)) { | if (VALUE_IS_FUNCTION(cx, v)) { |
197 | /* | /* |
198 | * Great, we have a function-valued prototype property | * Great, we have a function-valued prototype property where |
199 | * where the getter is JS_PropertyStub. The type id in | * the getter is JS_PropertyStub. The type id in pobj's scope |
200 | * pobj's scope does not evolve with changes to property | * does not evolve with changes to property values, however. |
201 | * values, however. | * |
202 | * | * So here, on first cache fill for this method, we brand the |
203 | * So here, on first cache fill for this method, we brand | * scope with a new shape and set the SCOPE_BRANDED flag. Once |
204 | * the scope with a new shape and set the SCOPE_BRANDED | * this scope flag is set, any write to a function-valued plain |
205 | * flag. Once this scope flag is set, any write that adds | * old property in pobj will result in shape being regenerated. |
206 | * or deletes a function-valued plain old property in | */ |
207 | * scope->object will result in shape being regenerated. | if (!scope->branded()) { |
208 | */ | PCMETER(cache->brandfills++); |
if (!SCOPE_IS_BRANDED(scope)) { | ||
PCMETER(cache->brandfills++); | ||
209 | #ifdef DEBUG_notme | #ifdef DEBUG_notme |
210 | fprintf(stderr, | fprintf(stderr, |
211 | "branding %p (%s) for funobj %p (%s), shape %lu\n", | "branding %p (%s) for funobj %p (%s), shape %lu\n", |
212 | pobj, LOCKED_OBJ_GET_CLASS(pobj)->name, | pobj, pobj->getClass()->name, |
213 | JSVAL_TO_OBJECT(v), | JSVAL_TO_OBJECT(v), |
214 | JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx, | JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v))), |
JSVAL_TO_OBJECT(v))), | ||
215 | OBJ_SHAPE(obj)); | OBJ_SHAPE(obj)); |
216 | #endif | #endif |
217 | js_MakeScopeShapeUnique(cx, scope); | scope->brandingShapeChange(cx, sprop->slot, v); |
218 | if (js_IsPropertyCacheDisabled(cx)) { | if (js_IsPropertyCacheDisabled(cx)) /* check for rt->shapeGen overflow */ |
219 | /* | return JS_NO_PROP_CACHE_FILL; |
220 | * js_GenerateShape could not recover from | scope->setBranded(); |
* rt->shapeGen's overflow. | ||
*/ | ||
return JS_NO_PROP_CACHE_FILL; | ||
} | ||
SCOPE_SET_BRANDED(scope); | ||
} | ||
vword = JSVAL_OBJECT_TO_PCVAL(v); | ||
break; | ||
221 | } | } |
222 | vword = JSVAL_OBJECT_TO_PCVAL(v); | |
223 | break; | |
224 | } | } |
225 | } | } |
226 | ||
# | Line 290 | Line 263 |
263 | * iterations the cache will be hit since the shape no longer | * iterations the cache will be hit since the shape no longer |
264 | * mutates. | * mutates. |
265 | */ | */ |
266 | JS_ASSERT(scope->object == obj); | JS_ASSERT(scope->owned()); |
267 | if (sprop->parent) { | if (sprop->parent) { |
268 | kshape = sprop->parent->shape; | kshape = sprop->parent->shape; |
269 | } else { | } else { |
270 | /* | |
271 | * If obj had its own empty scope before, with a unique | |
272 | * shape, that is lost. Here we only attempt to find a | |
273 | * matching empty scope. In unusual cases involving | |
274 | * __proto__ assignment we may not find one. | |
275 | */ | |
276 | JSObject *proto = STOBJ_GET_PROTO(obj); | JSObject *proto = STOBJ_GET_PROTO(obj); |
277 | if (proto && OBJ_IS_NATIVE(proto)) | if (!proto || !OBJ_IS_NATIVE(proto)) |
278 | kshape = OBJ_SHAPE(proto); | return JS_NO_PROP_CACHE_FILL; |
279 | JSScope *protoscope = OBJ_SCOPE(proto); | |
280 | if (!protoscope->emptyScope || | |
281 | protoscope->emptyScope->clasp != obj->getClass()) { | |
282 | return JS_NO_PROP_CACHE_FILL; | |
283 | } | |
284 | kshape = protoscope->emptyScope->shape; | |
285 | } | } |
286 | ||
287 | /* | /* |
# | Line 316 | Line 301 |
301 | khash = PROPERTY_CACHE_HASH_PC(pc, kshape); | khash = PROPERTY_CACHE_HASH_PC(pc, kshape); |
302 | if (obj == pobj) { | if (obj == pobj) { |
303 | JS_ASSERT(scopeIndex == 0 && protoIndex == 0); | JS_ASSERT(scopeIndex == 0 && protoIndex == 0); |
JS_ASSERT(OBJ_SCOPE(obj)->object == obj); | ||
JS_ASSERT(kshape != 0); | ||
304 | } else { | } else { |
305 | if (op == JSOP_LENGTH) { | if (op == JSOP_LENGTH) { |
306 | atom = cx->runtime->atomState.lengthAtom; | atom = cx->runtime->atomState.lengthAtom; |
# | Line 334 | Line 317 |
317 | #endif | #endif |
318 | ||
319 | if (scopeIndex != 0 || protoIndex != 1) { | if (scopeIndex != 0 || protoIndex != 1) { |
320 | khash = PROPERTY_CACHE_HASH_ATOM(atom, obj, pobj); | khash = PROPERTY_CACHE_HASH_ATOM(atom, obj); |
321 | PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1) | PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1) |
322 | cache->pcrecycles++); | cache->pcrecycles++); |
323 | pc = (jsbytecode *) atom; | pc = (jsbytecode *) atom; |
# | Line 350 | Line 333 |
333 | * platforms where another CPU can fail to see this write, it's OK | * platforms where another CPU can fail to see this write, it's OK |
334 | * because the property cache and JIT cache are thread-local. | * because the property cache and JIT cache are thread-local. |
335 | */ | */ |
336 | OBJ_SET_DELEGATE(cx, obj); | obj->setDelegate(); |
337 | } | } |
338 | } | } |
339 | ||
# | Line 400 | Line 383 |
383 | ||
384 | obj = *objp; | obj = *objp; |
385 | JS_ASSERT(OBJ_IS_NATIVE(obj)); | JS_ASSERT(OBJ_IS_NATIVE(obj)); |
386 | entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj, NULL)]; | entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj)]; |
387 | *entryp = entry; | *entryp = entry; |
388 | vcap = entry->vcap; | vcap = entry->vcap; |
389 | ||
# | Line 420 | Line 403 |
403 | entry->kshape, | entry->kshape, |
404 | OBJ_SHAPE(obj)); | OBJ_SHAPE(obj)); |
405 | js_Disassemble1(cx, cx->fp->script, pc, | js_Disassemble1(cx, cx->fp->script, pc, |
406 | PTRDIFF(pc, cx->fp->script->code, jsbytecode), | pc - cx->fp->script->code, |
407 | JS_FALSE, stderr); | JS_FALSE, stderr); |
408 | #endif | #endif |
409 | ||
# | Line 433 | Line 416 |
416 | } | } |
417 | ||
418 | pobj = obj; | pobj = obj; |
JS_LOCK_OBJ(cx, pobj); | ||
419 | ||
420 | if (JOF_MODE(cs->format) == JOF_NAME) { | if (JOF_MODE(cs->format) == JOF_NAME) { |
421 | while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) { | while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) { |
422 | tmp = LOCKED_OBJ_GET_PARENT(pobj); | tmp = OBJ_GET_PARENT(cx, pobj); |
423 | if (!tmp || !OBJ_IS_NATIVE(tmp)) | if (!tmp || !OBJ_IS_NATIVE(tmp)) |
424 | break; | break; |
JS_UNLOCK_OBJ(cx, pobj); | ||
425 | pobj = tmp; | pobj = tmp; |
JS_LOCK_OBJ(cx, pobj); | ||
426 | vcap -= PCVCAP_PROTOSIZE; | vcap -= PCVCAP_PROTOSIZE; |
427 | } | } |
428 | ||
# | Line 450 | Line 430 |
430 | } | } |
431 | ||
432 | while (vcap & PCVCAP_PROTOMASK) { | while (vcap & PCVCAP_PROTOMASK) { |
433 | tmp = LOCKED_OBJ_GET_PROTO(pobj); | tmp = OBJ_GET_PROTO(cx, pobj); |
434 | if (!tmp || !OBJ_IS_NATIVE(tmp)) | if (!tmp || !OBJ_IS_NATIVE(tmp)) |
435 | break; | break; |
JS_UNLOCK_OBJ(cx, pobj); | ||
436 | pobj = tmp; | pobj = tmp; |
JS_LOCK_OBJ(cx, pobj); | ||
437 | --vcap; | --vcap; |
438 | } | } |
439 | ||
440 | if (PCVCAP_SHAPE(vcap) == OBJ_SHAPE(pobj)) { | if (JS_LOCK_OBJ_IF_SHAPE(cx, pobj, PCVCAP_SHAPE(vcap))) { |
441 | #ifdef DEBUG | #ifdef DEBUG |
442 | jsid id = ATOM_TO_JSID(atom); | jsid id = ATOM_TO_JSID(atom); |
443 | ||
444 | CHECK_FOR_STRING_INDEX(id); | id = js_CheckForStringIndex(id); |
445 | JS_ASSERT(SCOPE_GET_PROPERTY(OBJ_SCOPE(pobj), id)); | JS_ASSERT(OBJ_SCOPE(pobj)->lookup(id)); |
446 | JS_ASSERT(OBJ_SCOPE(pobj)->object == pobj); | JS_ASSERT_IF(OBJ_SCOPE(pobj)->object, OBJ_SCOPE(pobj)->object == pobj); |
447 | #endif | #endif |
448 | *pobjp = pobj; | *pobjp = pobj; |
449 | return NULL; | return NULL; |
450 | } | } |
451 | ||
452 | PCMETER(JS_PROPERTY_CACHE(cx).vcmisses++); | PCMETER(JS_PROPERTY_CACHE(cx).vcmisses++); |
JS_UNLOCK_OBJ(cx, pobj); | ||
453 | return atom; | return atom; |
454 | } | } |
455 | ||
# | Line 618 | Line 595 |
595 | jsval *sp; | jsval *sp; |
596 | ||
597 | JS_ASSERT(nslots != 0); | JS_ASSERT(nslots != 0); |
598 | js_LeaveTrace(cx); | JS_ASSERT_NOT_ON_TRACE(cx); |
599 | ||
600 | if (!cx->stackPool.first.next) { | if (!cx->stackPool.first.next) { |
601 | int64 *timestamp; | int64 *timestamp; |
# | Line 747 | Line 724 |
724 | JSObject *limitBlock, *limitClone; | JSObject *limitBlock, *limitClone; |
725 | if (fp->fun && !fp->callobj) { | if (fp->fun && !fp->callobj) { |
726 | JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass || | JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass || |
727 | OBJ_GET_PRIVATE(cx, fp->scopeChain) != fp); | fp->scopeChain->getPrivate() != fp); |
728 | if (!js_GetCallObject(cx, fp)) | if (!js_GetCallObject(cx, fp)) |
729 | return NULL; | return NULL; |
730 | ||
# | Line 794 | Line 771 |
771 | * Special-case cloning the innermost block; this doesn't have enough in | * Special-case cloning the innermost block; this doesn't have enough in |
772 | * common with subsequent steps to include in the loop. | * common with subsequent steps to include in the loop. |
773 | * | * |
774 | * We pass fp->scopeChain and not null even if we override the parent slot | * js_CloneBlockObject leaves the clone's parent slot uninitialized. We |
775 | * later as null triggers useless calculations of slot's value in | * populate it below. |
* js_NewObject that js_CloneBlockObject calls. | ||
776 | */ | */ |
777 | JSObject *innermostNewChild | JSObject *innermostNewChild = js_CloneBlockObject(cx, sharedBlock, fp); |
= js_CloneBlockObject(cx, sharedBlock, fp->scopeChain, fp); | ||
778 | if (!innermostNewChild) | if (!innermostNewChild) |
779 | return NULL; | return NULL; |
780 | JSAutoTempValueRooter tvr(cx, innermostNewChild); | JSAutoTempValueRooter tvr(cx, innermostNewChild); |
# | Line 819 | Line 794 |
794 | ||
795 | /* As in the call above, we don't know the real parent yet. */ | /* As in the call above, we don't know the real parent yet. */ |
796 | JSObject *clone | JSObject *clone |
797 | = js_CloneBlockObject(cx, sharedBlock, fp->scopeChain, fp); | = js_CloneBlockObject(cx, sharedBlock, fp); |
798 | if (!clone) | if (!clone) |
799 | return NULL; | return NULL; |
800 | ||
# | Line 830 | Line 805 |
805 | STOBJ_SET_PARENT(newChild, clone); | STOBJ_SET_PARENT(newChild, clone); |
806 | newChild = clone; | newChild = clone; |
807 | } | } |
808 | STOBJ_SET_PARENT(newChild, fp->scopeChain); | |
809 | ||
810 | ||
811 | /* | /* |
812 | * If we found a limit block belonging to this frame, then we should have | * If we found a limit block belonging to this frame, then we should have |
# | Line 837 | Line 814 |
814 | */ | */ |
815 | JS_ASSERT_IF(limitBlock && | JS_ASSERT_IF(limitBlock && |
816 | OBJ_GET_CLASS(cx, limitBlock) == &js_BlockClass && | OBJ_GET_CLASS(cx, limitBlock) == &js_BlockClass && |
817 | OBJ_GET_PRIVATE(cx, limitClone) == fp, | limitClone->getPrivate() == fp, |
818 | sharedBlock); | sharedBlock); |
819 | ||
820 | /* Place our newly cloned blocks at the head of the scope chain. */ | /* Place our newly cloned blocks at the head of the scope chain. */ |
# | Line 856 | Line 833 |
833 | obj = JS_THIS_OBJECT(cx, vp); | obj = JS_THIS_OBJECT(cx, vp); |
834 | if (!JS_InstanceOf(cx, obj, clasp, vp + 2)) | if (!JS_InstanceOf(cx, obj, clasp, vp + 2)) |
835 | return JS_FALSE; | return JS_FALSE; |
836 | v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); | v = obj->fslots[JSSLOT_PRIMITIVE_THIS]; |
837 | } | } |
838 | *thisvp = v; | *thisvp = v; |
839 | return JS_TRUE; | return JS_TRUE; |
840 | } | } |
841 | ||
842 | /* Some objects (e.g., With) delegate 'this' to another object. */ | |
843 | static inline JSObject * | |
844 | CallThisObjectHook(JSContext *cx, JSObject *obj, jsval *argv) | |
845 | { | |
846 | JSObject *thisp = obj->thisObject(cx); | |
847 | if (!thisp) | |
848 | return NULL; | |
849 | argv[-1] = OBJECT_TO_JSVAL(thisp); | |
850 | return thisp; | |
851 | } | |
852 | ||
853 | /* | /* |
854 | * ECMA requires "the global object", but in embeddings such as the browser, | * ECMA requires "the global object", but in embeddings such as the browser, |
855 | * which have multiple top-level objects (windows, frames, etc. in the DOM), | * which have multiple top-level objects (windows, frames, etc. in the DOM), |
# | Line 914 | Line 902 |
902 | thisp = JSVAL_TO_OBJECT(argv[-2]); | thisp = JSVAL_TO_OBJECT(argv[-2]); |
903 | id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); | id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); |
904 | ||
905 | ok = OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs); | ok = thisp->checkAccess(cx, id, JSACC_PARENT, &v, &attrs); |
906 | if (lazy) { | if (lazy) { |
907 | cx->dormantFrameChain = fp->dormantNext; | cx->dormantFrameChain = fp->dormantNext; |
908 | fp->dormantNext = NULL; | fp->dormantNext = NULL; |
# | Line 931 | Line 919 |
919 | thisp = parent; | thisp = parent; |
920 | } | } |
921 | ||
922 | /* Some objects (e.g., With) delegate 'this' to another object. */ | return CallThisObjectHook(cx, thisp, argv); |
thisp = OBJ_THIS_OBJECT(cx, thisp); | ||
if (!thisp) | ||
return NULL; | ||
argv[-1] = OBJECT_TO_JSVAL(thisp); | ||
return thisp; | ||
923 | } | } |
924 | ||
925 | static JSObject * | static JSObject * |
# | Line 949 | Line 932 |
932 | if (!js_PrimitiveToObject(cx, &argv[-1])) | if (!js_PrimitiveToObject(cx, &argv[-1])) |
933 | return NULL; | return NULL; |
934 | thisp = JSVAL_TO_OBJECT(argv[-1]); | thisp = JSVAL_TO_OBJECT(argv[-1]); |
935 | } else { | return thisp; |
936 | thisp = JSVAL_TO_OBJECT(argv[-1]); | } |
if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass || | ||
OBJ_GET_CLASS(cx, thisp) == &js_BlockClass) { | ||
return js_ComputeGlobalThis(cx, lazy, argv); | ||
} | ||
937 | ||
938 | /* Some objects (e.g., With) delegate 'this' to another object. */ | thisp = JSVAL_TO_OBJECT(argv[-1]); |
939 | thisp = OBJ_THIS_OBJECT(cx, thisp); | if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass || OBJ_GET_CLASS(cx, thisp) == &js_BlockClass) |
940 | if (!thisp) | return js_ComputeGlobalThis(cx, lazy, argv); |
941 | return NULL; | |
942 | argv[-1] = OBJECT_TO_JSVAL(thisp); | return CallThisObjectHook(cx, thisp, argv); |
} | ||
return thisp; | ||
943 | } | } |
944 | ||
945 | JSObject * | JSObject * |
946 | js_ComputeThis(JSContext *cx, JSBool lazy, jsval *argv) | js_ComputeThis(JSContext *cx, JSBool lazy, jsval *argv) |
947 | { | { |
948 | JS_ASSERT(argv[-1] != JSVAL_HOLE); // check for SynthesizeFrame poisoning | |
949 | if (JSVAL_IS_NULL(argv[-1])) | if (JSVAL_IS_NULL(argv[-1])) |
950 | return js_ComputeGlobalThis(cx, lazy, argv); | return js_ComputeGlobalThis(cx, lazy, argv); |
951 | return ComputeThis(cx, lazy, argv); | return ComputeThis(cx, lazy, argv); |
# | Line 975 | Line 953 |
953 | ||
954 | #if JS_HAS_NO_SUCH_METHOD | #if JS_HAS_NO_SUCH_METHOD |
955 | ||
956 | #define JSSLOT_FOUND_FUNCTION JSSLOT_PRIVATE | const uint32 JSSLOT_FOUND_FUNCTION = JSSLOT_PRIVATE; |
957 | #define JSSLOT_SAVED_ID (JSSLOT_PRIVATE + 1) | const uint32 JSSLOT_SAVED_ID = JSSLOT_PRIVATE + 1; |
958 | ||
959 | JSClass js_NoSuchMethodClass = { | JSClass js_NoSuchMethodClass = { |
960 | "NoSuchMethod", | "NoSuchMethod", |
961 | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS, | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS, |
962 | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, |
963 | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, |
964 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL |
965 | }; | }; |
966 | ||
# | Line 1003 | Line 981 |
981 | JS_STATIC_INTERPRET JSBool | JS_STATIC_INTERPRET JSBool |
982 | js_OnUnknownMethod(JSContext *cx, jsval *vp) | js_OnUnknownMethod(JSContext *cx, jsval *vp) |
983 | { | { |
JSObject *obj; | ||
jsid id; | ||
JSTempValueRooter tvr; | ||
JSBool ok; | ||
984 | JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1])); | JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1])); |
obj = JSVAL_TO_OBJECT(vp[1]); | ||
JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); | ||
985 | ||
986 | MUST_FLOW_THROUGH("out"); | JSObject *obj = JSVAL_TO_OBJECT(vp[1]); |
987 | id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); | jsid id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); |
988 | ok = js_GetMethod(cx, obj, id, false, &tvr.u.value); | JSAutoTempValueRooter tvr(cx, JSVAL_NULL); |
989 | if (!ok) | if (!js_GetMethod(cx, obj, id, false, tvr.addr())) |
990 | goto out; | return false; |
991 | if (JSVAL_IS_PRIMITIVE(tvr.u.value)) { | if (JSVAL_IS_PRIMITIVE(tvr.value())) { |
992 | vp[0] = tvr.u.value; | vp[0] = tvr.value(); |
993 | } else { | } else { |
994 | #if JS_HAS_XML_SUPPORT | #if JS_HAS_XML_SUPPORT |
995 | /* Extract the function name from function::name qname. */ | /* Extract the function name from function::name qname. */ |
996 | if (!JSVAL_IS_PRIMITIVE(vp[0])) { | if (!JSVAL_IS_PRIMITIVE(vp[0])) { |
997 | obj = JSVAL_TO_OBJECT(vp[0]); | obj = JSVAL_TO_OBJECT(vp[0]); |
998 | ok = js_IsFunctionQName(cx, obj, &id); | if (!js_IsFunctionQName(cx, obj, &id)) |
999 | if (!ok) | return false; |
goto out; | ||
1000 | if (id != 0) | if (id != 0) |
1001 | vp[0] = ID_TO_VALUE(id); | vp[0] = ID_TO_VALUE(id); |
1002 | } | } |
1003 | #endif | #endif |
1004 | obj = js_NewObjectWithGivenProto(cx, &js_NoSuchMethodClass, | obj = js_NewObjectWithGivenProto(cx, &js_NoSuchMethodClass, |
1005 | NULL, NULL, 0); | NULL, NULL); |
1006 | if (!obj) { | if (!obj) |
1007 | ok = JS_FALSE; | return false; |
1008 | goto out; | obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.value(); |
} | ||
obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.u.value; | ||
1009 | obj->fslots[JSSLOT_SAVED_ID] = vp[0]; | obj->fslots[JSSLOT_SAVED_ID] = vp[0]; |
1010 | vp[0] = OBJECT_TO_JSVAL(obj); | vp[0] = OBJECT_TO_JSVAL(obj); |
1011 | } | } |
1012 | ok = JS_TRUE; | return true; |
out: | ||
JS_POP_TEMP_ROOT(cx, &tvr); | ||
return ok; | ||
1013 | } | } |
1014 | ||
1015 | static JS_REQUIRES_STACK JSBool | static JS_REQUIRES_STACK JSBool |
# | Line 1091 | Line 1055 |
1055 | JS_STATIC_ASSERT(JSVAL_INT == 1); | JS_STATIC_ASSERT(JSVAL_INT == 1); |
1056 | JS_STATIC_ASSERT(JSVAL_DOUBLE == 2); | JS_STATIC_ASSERT(JSVAL_DOUBLE == 2); |
1057 | JS_STATIC_ASSERT(JSVAL_STRING == 4); | JS_STATIC_ASSERT(JSVAL_STRING == 4); |
1058 | JS_STATIC_ASSERT(JSVAL_BOOLEAN == 6); | JS_STATIC_ASSERT(JSVAL_SPECIAL == 6); |
1059 | ||
1060 | const uint16 js_PrimitiveTestFlags[] = { | const uint16 js_PrimitiveTestFlags[] = { |
1061 | JSFUN_THISP_NUMBER, /* INT */ | JSFUN_THISP_NUMBER, /* INT */ |
# | Line 1119 | Line 1083 |
1083 | JSObject *funobj, *parent; | JSObject *funobj, *parent; |
1084 | JSBool ok; | JSBool ok; |
1085 | JSClass *clasp; | JSClass *clasp; |
1086 | JSObjectOps *ops; | const JSObjectOps *ops; |
1087 | JSNative native; | JSNative native; |
1088 | JSFunction *fun; | JSFunction *fun; |
1089 | JSScript *script; | JSScript *script; |
# | Line 1128 | Line 1092 |
1092 | JSInterpreterHook hook; | JSInterpreterHook hook; |
1093 | void *hookData; | void *hookData; |
1094 | ||
1095 | JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); | |
1096 | ||
1097 | /* [vp .. vp + 2 + argc) must belong to the last JS stack arena. */ | /* [vp .. vp + 2 + argc) must belong to the last JS stack arena. */ |
1098 | JS_ASSERT((jsval *) cx->stackPool.current->base <= vp); | JS_ASSERT((jsval *) cx->stackPool.current->base <= vp); |
1099 | JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail); | JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail); |
# | Line 1157 | Line 1123 |
1123 | /* | /* |
1124 | * XXX this makes no sense -- why convert to function if clasp->call? | * XXX this makes no sense -- why convert to function if clasp->call? |
1125 | * XXX better to call that hook without converting | * XXX better to call that hook without converting |
* XXX the only thing that needs fixing is liveconnect | ||
1126 | * | * |
1127 | * FIXME bug 408416: try converting to function, for API compatibility | * FIXME bug 408416: try converting to function, for API compatibility |
1128 | * if there is a call op defined. | * if there is a call op defined. |
# | Line 1314 | Line 1279 |
1279 | */ | */ |
1280 | frame.thisp = (JSObject *)vp[1]; | frame.thisp = (JSObject *)vp[1]; |
1281 | frame.varobj = NULL; | frame.varobj = NULL; |
1282 | frame.callobj = frame.argsobj = NULL; | frame.callobj = NULL; |
1283 | frame.argsobj = NULL; | |
1284 | frame.script = script; | frame.script = script; |
frame.callee = funobj; | ||
1285 | frame.fun = fun; | frame.fun = fun; |
1286 | frame.argc = argc; | frame.argc = argc; |
1287 | frame.argv = argv; | frame.argv = argv; |
# | Line 1334 | Line 1299 |
1299 | frame.sharpArray = NULL; | frame.sharpArray = NULL; |
1300 | frame.flags = flags | rootedArgsFlag; | frame.flags = flags | rootedArgsFlag; |
1301 | frame.dormantNext = NULL; | frame.dormantNext = NULL; |
frame.xmlNamespace = NULL; | ||
1302 | frame.displaySave = NULL; | frame.displaySave = NULL; |
1303 | ||
1304 | MUST_FLOW_THROUGH("out"); | MUST_FLOW_THROUGH("out"); |
# | Line 1373 | Line 1337 |
1337 | if (hook) | if (hook) |
1338 | hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData); | hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData); |
1339 | ||
1340 | #ifdef INCLUDE_MOZILLA_DTRACE | |
1341 | /* DTrace function entry, non-inlines */ | |
1342 | if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED()) | |
1343 | jsdtrace_function_entry(cx, &frame, fun); | |
1344 | if (JAVASCRIPT_FUNCTION_INFO_ENABLED()) | |
1345 | jsdtrace_function_info(cx, &frame, frame.down, fun); | |
1346 | if (JAVASCRIPT_FUNCTION_ARGS_ENABLED()) | |
1347 | jsdtrace_function_args(cx, &frame, fun, frame.argc, frame.argv); | |
1348 | #endif | |
1349 | ||
1350 | /* Call the function, either a native method or an interpreted script. */ | /* Call the function, either a native method or an interpreted script. */ |
1351 | if (native) { | if (native) { |
1352 | #ifdef DEBUG_NOT_THROWING | #ifdef DEBUG_NOT_THROWING |
# | Line 1394 | Line 1368 |
1368 | ok = js_Interpret(cx); | ok = js_Interpret(cx); |
1369 | } | } |
1370 | ||
1371 | #ifdef INCLUDE_MOZILLA_DTRACE | |
1372 | /* DTrace function return, non-inlines */ | |
1373 | if (JAVASCRIPT_FUNCTION_RVAL_ENABLED()) | |
1374 | jsdtrace_function_rval(cx, &frame, fun, &frame.rval); | |
1375 | if (JAVASCRIPT_FUNCTION_RETURN_ENABLED()) | |
1376 | jsdtrace_function_return(cx, &frame, fun); | |
1377 | #endif | |
1378 | ||
1379 | out: | out: |
1380 | if (hookData) { | if (hookData) { |
1381 | hook = cx->debugHooks->callHook; | hook = cx->debugHooks->callHook; |
# | Line 1401 | Line 1383 |
1383 | hook(cx, &frame, JS_FALSE, &ok, hookData); | hook(cx, &frame, JS_FALSE, &ok, hookData); |
1384 | } | } |
1385 | ||
1386 | /* If frame has a call object, sync values and clear back-pointer. */ | frame.putActivationObjects(cx); |
if (frame.callobj) | ||
ok &= js_PutCallObject(cx, &frame); | ||
/* If frame has an arguments object, sync values and clear back-pointer. */ | ||
if (frame.argsobj) | ||
ok &= js_PutArgsObject(cx, &frame); | ||
1387 | ||
1388 | *vp = frame.rval; | *vp = frame.rval; |
1389 | ||
# | Line 1536 | Line 1512 |
1512 | frame.callobj = down->callobj; | frame.callobj = down->callobj; |
1513 | frame.argsobj = down->argsobj; | frame.argsobj = down->argsobj; |
1514 | frame.varobj = down->varobj; | frame.varobj = down->varobj; |
frame.callee = down->callee; | ||
1515 | frame.fun = down->fun; | frame.fun = down->fun; |
1516 | frame.thisp = down->thisp; | frame.thisp = down->thisp; |
1517 | if (down->flags & JSFRAME_COMPUTED_THIS) | if (down->flags & JSFRAME_COMPUTED_THIS) |
# | Line 1547 | Line 1522 |
1522 | frame.sharpArray = down->sharpArray; | frame.sharpArray = down->sharpArray; |
1523 | JS_ASSERT(script->nfixed == 0); | JS_ASSERT(script->nfixed == 0); |
1524 | } else { | } else { |
1525 | frame.callobj = frame.argsobj = NULL; | frame.callobj = NULL; |
1526 | frame.argsobj = NULL; | |
1527 | obj = chain; | obj = chain; |
1528 | if (cx->options & JSOPTION_VAROBJFIX) { | if (cx->options & JSOPTION_VAROBJFIX) { |
1529 | while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) | while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) |
1530 | obj = tmp; | obj = tmp; |
1531 | } | } |
1532 | frame.varobj = obj; | frame.varobj = obj; |
frame.callee = NULL; | ||
1533 | frame.fun = NULL; | frame.fun = NULL; |
1534 | frame.thisp = chain; | frame.thisp = chain; |
1535 | frame.argc = 0; | frame.argc = 0; |
# | Line 1582 | Line 1557 |
1557 | frame.sharpDepth = 0; | frame.sharpDepth = 0; |
1558 | frame.flags = flags; | frame.flags = flags; |
1559 | frame.dormantNext = NULL; | frame.dormantNext = NULL; |
frame.xmlNamespace = NULL; | ||
1560 | frame.blockChain = NULL; | frame.blockChain = NULL; |
1561 | ||
1562 | /* | /* |
# | Line 1606 | Line 1580 |
1580 | ||
1581 | cx->fp = &frame; | cx->fp = &frame; |
1582 | if (!down) { | if (!down) { |
1583 | frame.thisp = OBJ_THIS_OBJECT(cx, frame.thisp); | OBJ_TO_INNER_OBJECT(cx, chain); |
1584 | if (!chain) | |
1585 | return JS_FALSE; | |
1586 | frame.scopeChain = chain; | |
1587 | ||
1588 | frame.thisp = frame.thisp->thisObject(cx); | |
1589 | if (!frame.thisp) { | if (!frame.thisp) { |
1590 | ok = JS_FALSE; | ok = JS_FALSE; |
1591 | goto out2; | goto out2; |
# | Line 1655 | Line 1634 |
1634 | JSObject *obj2; | JSObject *obj2; |
1635 | JSProperty *prop; | JSProperty *prop; |
1636 | uintN oldAttrs, report; | uintN oldAttrs, report; |
1637 | JSBool isFunction; | bool isFunction; |
1638 | jsval value; | jsval value; |
1639 | const char *type, *name; | const char *type, *name; |
1640 | ||
# | Line 1674 | Line 1653 |
1653 | JS_ASSERT_IF(attrs & JSPROP_INITIALIZER, attrs == JSPROP_INITIALIZER); | JS_ASSERT_IF(attrs & JSPROP_INITIALIZER, attrs == JSPROP_INITIALIZER); |
1654 | JS_ASSERT_IF(attrs == JSPROP_INITIALIZER, !propp); | JS_ASSERT_IF(attrs == JSPROP_INITIALIZER, !propp); |
1655 | ||
1656 | if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) | if (!obj->lookupProperty(cx, id, &obj2, &prop)) |
1657 | return JS_FALSE; | return JS_FALSE; |
1658 | if (!prop) | if (!prop) |
1659 | return JS_TRUE; | return JS_TRUE; |
1660 | ||
1661 | /* Use prop as a speedup hint to OBJ_GET_ATTRIBUTES. */ | /* Use prop as a speedup hint to obj->getAttributes. */ |
1662 | if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) { | if (!obj2->getAttributes(cx, id, prop, &oldAttrs)) { |
1663 | OBJ_DROP_PROPERTY(cx, obj2, prop); | obj2->dropProperty(cx, prop); |
1664 | return JS_FALSE; | return JS_FALSE; |
1665 | } | } |
1666 | ||
# | Line 1689 | Line 1668 |
1668 | * 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). |
1669 | */ | */ |
1670 | if (!propp) { | if (!propp) { |
1671 | OBJ_DROP_PROPERTY(cx, obj2, prop); | obj2->dropProperty(cx, prop); |
1672 | prop = NULL; | prop = NULL; |
1673 | } else { | } else { |
1674 | *objp = obj2; | *objp = obj2; |
# | Line 1704 | Line 1683 |
1683 | /* The property must be dropped already. */ | /* The property must be dropped already. */ |
1684 | JS_ASSERT(!prop); | JS_ASSERT(!prop); |
1685 | report = JSREPORT_WARNING | JSREPORT_STRICT; | report = JSREPORT_WARNING | JSREPORT_STRICT; |
1686 | ||
1687 | #ifdef __GNUC__ | |
1688 | isFunction = false; /* suppress bogus gcc warnings */ | |
1689 | #endif | |
1690 | } else { | } else { |
1691 | /* We allow redeclaring some non-readonly properties. */ | /* We allow redeclaring some non-readonly properties. */ |
1692 | if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) { | if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) { |
# | Line 1730 | Line 1713 |
1713 | return JS_TRUE; | return JS_TRUE; |
1714 | } | } |
1715 | if (prop) | if (prop) |
1716 | OBJ_DROP_PROPERTY(cx, obj2, prop); | obj2->dropProperty(cx, prop); |
1717 | ||
1718 | report = JSREPORT_ERROR; | report = JSREPORT_ERROR; |
1719 | isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0; | isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0; |
1720 | if (!isFunction) { | if (!isFunction) { |
1721 | if (!OBJ_GET_PROPERTY(cx, obj, id, &value)) | if (!obj->getProperty(cx, id, &value)) |
1722 | return JS_FALSE; | return JS_FALSE; |
1723 | isFunction = VALUE_IS_FUNCTION(cx, value); | isFunction = VALUE_IS_FUNCTION(cx, value); |
1724 | } | } |
# | Line 1804 | Line 1787 |
1787 | return lval == rval; | return lval == rval; |
1788 | } | } |
1789 | ||
1790 | static inline bool | |
1791 | IsNegativeZero(jsval v) | |
1792 | { | |
1793 | return JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_NEGZERO(*JSVAL_TO_DOUBLE(v)); | |
1794 | } | |
1795 | ||
1796 | static inline bool | |
1797 | IsNaN(jsval v) | |
1798 | { | |
1799 | return JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_NaN(*JSVAL_TO_DOUBLE(v)); | |
1800 | } | |
1801 | ||
1802 | JSBool | |
1803 | js_SameValue(jsval v1, jsval v2, JSContext *cx) | |
1804 | { | |
1805 | if (IsNegativeZero(v1)) | |
1806 | return IsNegativeZero(v2); | |
1807 | if (IsNegativeZero(v2)) | |
1808 | return JS_FALSE; | |
1809 | if (IsNaN(v1) && IsNaN(v2)) | |
1810 | return JS_TRUE; | |
1811 | return js_StrictlyEqual(cx, v1, v2); | |
1812 | } | |
1813 | ||
1814 | JS_REQUIRES_STACK JSBool | JS_REQUIRES_STACK JSBool |
1815 | js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp) | js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp) |
1816 | { | { |
# | Line 1837 | Line 1844 |
1844 | * root to protect this prototype, in case it has no other | * root to protect this prototype, in case it has no other |
1845 | * strong refs. | * strong refs. |
1846 | */ | */ |
1847 | if (!OBJ_GET_PROPERTY(cx, obj2, | if (!obj2->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), |
1848 | ATOM_TO_JSID(cx->runtime->atomState | &vp[1])) { |
.classPrototypeAtom), | ||
&vp[1])) { | ||
1849 | return JS_FALSE; | return JS_FALSE; |
1850 | } | } |
1851 | rval = vp[1]; | rval = vp[1]; |
# | Line 1853 | Line 1858 |
1858 | clasp = fun2->u.n.clasp; | clasp = fun2->u.n.clasp; |
1859 | } | } |
1860 | } | } |
1861 | obj = js_NewObject(cx, clasp, proto, parent, 0); | obj = js_NewObject(cx, clasp, proto, parent); |
1862 | if (!obj) | if (!obj) |
1863 | return JS_FALSE; | return JS_FALSE; |
1864 | ||
1865 | /* Now we have an object with a constructor method; call it. */ | /* Now we have an object with a constructor method; call it. */ |
1866 | vp[1] = OBJECT_TO_JSVAL(obj); | vp[1] = OBJECT_TO_JSVAL(obj); |
1867 | if (!js_Invoke(cx, argc, vp, JSINVOKE_CONSTRUCT)) { | if (!js_Invoke(cx, argc, vp, JSINVOKE_CONSTRUCT)) |
cx->weakRoots.newborn[GCX_OBJECT] = NULL; | ||
1868 | return JS_FALSE; | return JS_FALSE; |
} | ||
1869 | ||
1870 | /* Check the return value and if it's primitive, force it to be obj. */ | /* Check the return value and if it's primitive, force it to be obj. */ |
1871 | rval = *vp; | rval = *vp; |
# | Line 1951 | Line 1954 |
1954 | ||
1955 | withobj = cx->fp->scopeChain; | withobj = cx->fp->scopeChain; |
1956 | JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass); | JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass); |
1957 | JS_ASSERT(OBJ_GET_PRIVATE(cx, withobj) == cx->fp); | JS_ASSERT(withobj->getPrivate() == cx->fp); |
1958 | JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0); | JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0); |
1959 | cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj); | cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj); |
1960 | JS_SetPrivate(cx, withobj, NULL); | withobj->setPrivate(NULL); |
1961 | } | } |
1962 | ||
1963 | JS_REQUIRES_STACK JSClass * | JS_REQUIRES_STACK JSClass * |
# | Line 1964 | Line 1967 |
1967 | ||
1968 | clasp = OBJ_GET_CLASS(cx, obj); | clasp = OBJ_GET_CLASS(cx, obj); |
1969 | if ((clasp == &js_WithClass || clasp == &js_BlockClass) && | if ((clasp == &js_WithClass || clasp == &js_BlockClass) && |
1970 | OBJ_GET_PRIVATE(cx, obj) == cx->fp && | obj->getPrivate() == cx->fp && |
1971 | OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) { | OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) { |
1972 | return clasp; | return clasp; |
1973 | } | } |
# | Line 2062 | Line 2065 |
2065 | } else if (slot == CALLEE_UPVAR_SLOT) { | } else if (slot == CALLEE_UPVAR_SLOT) { |
2066 | vp = &fp->argv[-2]; | vp = &fp->argv[-2]; |
2067 | slot = 0; | slot = 0; |
2068 | } else { | } else { |
2069 | slot -= fp->fun->nargs; | slot -= fp->fun->nargs; |
2070 | JS_ASSERT(slot < fp->script->nslots); | JS_ASSERT(slot < fp->script->nslots); |
2071 | vp = fp->slots; | vp = fp->slots; |
# | Line 2099 | Line 2102 |
2102 | fp->script, cx->tracePrevPc); | fp->script, cx->tracePrevPc); |
2103 | ||
2104 | /* | /* |
2105 | * If there aren't that many elements on the stack, then | * If there aren't that many elements on the stack, then |
2106 | * we have probably entered a new frame, and printing output | * we have probably entered a new frame, and printing output |
2107 | * would just be misleading. | * would just be misleading. |
2108 | */ | */ |
# | Line 2112 | Line 2115 |
2115 | fprintf(tracefp, "%s %s", | fprintf(tracefp, "%s %s", |
2116 | (n == -ndefs) ? " output:" : ",", | (n == -ndefs) ? " output:" : ",", |
2117 | bytes); | bytes); |
2118 | JS_free(cx, bytes); | cx->free(bytes); |
2119 | } | } |
2120 | } | } |
2121 | fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp))); | fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp))); |
# | Line 2132 | Line 2135 |
2135 | fprintf(tracefp, "%4u: ", | fprintf(tracefp, "%4u: ", |
2136 | js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : regs->pc)); | js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : regs->pc)); |
2137 | js_Disassemble1(cx, fp->script, regs->pc, | js_Disassemble1(cx, fp->script, regs->pc, |
2138 | PTRDIFF(regs->pc, fp->script->code, jsbytecode), | regs->pc - fp->script->code, |
2139 | JS_FALSE, tracefp); | JS_FALSE, tracefp); |
2140 | op = (JSOp) *regs->pc; | op = (JSOp) *regs->pc; |
2141 | nuses = js_GetStackUses(&js_CodeSpec[op], op, regs->pc); | nuses = js_GetStackUses(&js_CodeSpec[op], op, regs->pc); |
# | Line 2144 | Line 2147 |
2147 | fprintf(tracefp, "%s %s", | fprintf(tracefp, "%s %s", |
2148 | (n == -nuses) ? " inputs:" : ",", | (n == -nuses) ? " inputs:" : ",", |
2149 | bytes); | bytes); |
2150 | JS_free(cx, bytes); | cx->free(bytes); |
2151 | } | } |
2152 | } | } |
2153 | fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp))); | fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp))); |
# | Line 2231 | Line 2234 |
2234 | ||
2235 | # define SIGNIFICANT(count,total) (200. * (count) >= (total)) | # define SIGNIFICANT(count,total) (200. * (count) >= (total)) |
2236 | ||
2237 | graph = (Edge *) calloc(nedges, sizeof graph[0]); | graph = (Edge *) js_calloc(nedges * sizeof graph[0]); |
2238 | for (i = nedges = 0; i < JSOP_LIMIT; i++) { | for (i = nedges = 0; i < JSOP_LIMIT; i++) { |
2239 | from = js_CodeName[i]; | from = js_CodeName[i]; |
2240 | for (j = 0; j < JSOP_LIMIT; j++) { | for (j = 0; j < JSOP_LIMIT; j++) { |
# | Line 2260 | Line 2263 |
2263 | graph[i].from, graph[i].to, | graph[i].from, graph[i].to, |
2264 | (unsigned long)graph[i].count, style); | (unsigned long)graph[i].count, style); |
2265 | } | } |
2266 | free(graph); | js_free(graph); |
2267 | fputs("}\n", fp); | fputs("}\n", fp); |
2268 | fclose(fp); | fclose(fp); |
2269 | ||
# | Line 2430 | Line 2433 |
2433 | JS_BEGIN_MACRO \ | JS_BEGIN_MACRO \ |
2434 | JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); \ | JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); \ |
2435 | JS_ASSERT(v == regs.sp[n]); \ | JS_ASSERT(v == regs.sp[n]); \ |
2436 | if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, ®s.sp[n])) \ | if (!JSVAL_TO_OBJECT(v)->defaultValue(cx, hint, ®s.sp[n])) \ |
2437 | goto error; \ | goto error; \ |
2438 | v = regs.sp[n]; \ | v = regs.sp[n]; \ |
2439 | JS_END_MACRO | JS_END_MACRO |
# | Line 2444 | Line 2447 |
2447 | #define CAN_DO_FAST_INC_DEC(v) (((((v) << 1) ^ v) & 0x80000001) == 1) | #define CAN_DO_FAST_INC_DEC(v) (((((v) << 1) ^ v) & 0x80000001) == 1) |
2448 | ||
2449 | JS_STATIC_ASSERT(JSVAL_INT == 1); | JS_STATIC_ASSERT(JSVAL_INT == 1); |
2450 | JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL(JSVAL_INT_MIN))); | JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL_CONSTEXPR(JSVAL_INT_MIN))); |
2451 | JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL(JSVAL_INT_MAX))); | JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL_CONSTEXPR(JSVAL_INT_MAX))); |
2452 | ||
2453 | /* | /* |
2454 | * Conditional assert to detect failure to clear a pending exception that is | * Conditional assert to detect failure to clear a pending exception that is |
# | Line 2551 | Line 2554 |
2554 | return true; | return true; |
2555 | if (cx->runtime->gcNumber != sample || | if (cx->runtime->gcNumber != sample || |
2556 | PCVCAP_SHAPE(entry->vcap) != OBJ_SHAPE(pobj)) { | PCVCAP_SHAPE(entry->vcap) != OBJ_SHAPE(pobj)) { |
2557 | OBJ_DROP_PROPERTY(cx, pobj, prop); | pobj->dropProperty(cx, prop); |
2558 | return true; | return true; |
2559 | } | } |
2560 | JS_ASSERT(prop); | JS_ASSERT(prop); |
# | Line 2566 | Line 2569 |
2569 | jsval v; | jsval v; |
2570 | JS_ASSERT(PCVAL_IS_OBJECT(entry->vword)); | JS_ASSERT(PCVAL_IS_OBJECT(entry->vword)); |
2571 | JS_ASSERT(entry->vword != PCVAL_NULL); | JS_ASSERT(entry->vword != PCVAL_NULL); |
2572 | JS_ASSERT(SCOPE_IS_BRANDED(OBJ_SCOPE(pobj))); | JS_ASSERT(OBJ_SCOPE(pobj)->branded()); |
2573 | JS_ASSERT(SPROP_HAS_STUB_GETTER(sprop)); | JS_ASSERT(SPROP_HAS_STUB_GETTER(sprop)); |
2574 | JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); | JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); |
2575 | v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); | v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot); |
# | Line 2574 | Line 2577 |
2577 | JS_ASSERT(PCVAL_TO_OBJECT(entry->vword) == JSVAL_TO_OBJECT(v)); | JS_ASSERT(PCVAL_TO_OBJECT(entry->vword) == JSVAL_TO_OBJECT(v)); |
2578 | } | } |
2579 | ||
2580 | OBJ_DROP_PROPERTY(cx, pobj, prop); | pobj->dropProperty(cx, prop); |
2581 | return true; | return true; |
2582 | } | } |
2583 | ||
# | Line 2630 | Line 2633 |
2633 | JS_REQUIRES_STACK JSBool | JS_REQUIRES_STACK JSBool |
2634 | js_Interpret(JSContext *cx) | js_Interpret(JSContext *cx) |
2635 | { | { |
2636 | #ifdef MOZ_TRACEVIS | |
2637 | TraceVisStateObj tvso(cx, S_INTERP); | |
2638 | #endif | |
2639 | ||
2640 | JSRuntime *rt; | JSRuntime *rt; |
2641 | JSStackFrame *fp; | JSStackFrame *fp; |
2642 | JSScript *script; | JSScript *script; |
# | Line 2680 | Line 2687 |
2687 | * 'op=x; DO_OP()' to let another opcode's implementation finish | * 'op=x; DO_OP()' to let another opcode's implementation finish |
2688 | * their work, and many opcodes share entry points with a run of | * their work, and many opcodes share entry points with a run of |
2689 | * consecutive BEGIN_CASEs. | * consecutive BEGIN_CASEs. |
2690 | * | * |
2691 | * Take care to trace OP only when it is the opcode fetched from | * Take care to trace OP only when it is the opcode fetched from |
2692 | * the instruction stream, so the trace matches what one would | * the instruction stream, so the trace matches what one would |
2693 | * expect from looking at the code. (We do omit POPs after SETs; | * expect from looking at the code. (We do omit POPs after SETs; |
# | Line 2784 | Line 2791 |
2791 | #endif /* !JS_THREADED_INTERP */ | #endif /* !JS_THREADED_INTERP */ |
2792 | ||
2793 | #ifdef JS_TRACER | #ifdef JS_TRACER |
2794 | /* We had better not be entering the interpreter from JIT-compiled code. */ | /* We cannot reenter the interpreter while recording. */ |
2795 | TraceRecorder *tr = TRACE_RECORDER(cx); | if (TRACE_RECORDER(cx)) |
2796 | SET_TRACE_RECORDER(cx, NULL); | js_AbortRecording(cx, "attempt to reenter interpreter while recording"); |
/* If a recorder is pending and we try to re-enter the interpreter, flag | ||
the recorder to be destroyed when we return. */ | ||
if (tr) { | ||
if (tr->wasDeepAborted()) | ||
tr->removeFragmentoReferences(); | ||
else | ||
tr->pushAbortStack(); | ||
} | ||
2797 | #endif | #endif |
2798 | ||
2799 | /* Check for too deep of a native thread stack. */ | /* Check for too deep of a native thread stack. */ |
# | Line 2835 | Line 2833 |
2833 | (atoms - script->atomMap.vector + GET_INDEX(regs.pc + PCOFF)) | (atoms - script->atomMap.vector + GET_INDEX(regs.pc + PCOFF)) |
2834 | ||
2835 | #define LOAD_OBJECT(PCOFF) \ | #define LOAD_OBJECT(PCOFF) \ |
2836 | JS_GET_SCRIPT_OBJECT(script, GET_FULL_INDEX(PCOFF), obj) | (obj = script->getObject(GET_FULL_INDEX(PCOFF))) |
2837 | ||
2838 | #define LOAD_FUNCTION(PCOFF) \ | #define LOAD_FUNCTION(PCOFF) \ |
2839 | JS_GET_SCRIPT_FUNCTION(script, GET_FULL_INDEX(PCOFF), fun) | (fun = script->getFunction(GET_FULL_INDEX(PCOFF))) |
2840 | ||
2841 | #ifdef JS_TRACER | #ifdef JS_TRACER |
2842 | ||
2843 | #ifdef MOZ_TRACEVIS | |
2844 | #if JS_THREADED_INTERP | |
2845 | #define MONITOR_BRANCH_TRACEVIS \ | |
2846 | JS_BEGIN_MACRO \ | |
2847 | if (jumpTable != interruptJumpTable) \ | |
2848 | js_EnterTraceVisState(cx, S_RECORD, R_NONE); \ | |
2849 | JS_END_MACRO | |
2850 | #else /* !JS_THREADED_INTERP */ | |
2851 | #define MONITOR_BRANCH_TRACEVIS \ | |
2852 | JS_BEGIN_MACRO \ | |
2853 | js_EnterTraceVisState(cx, S_RECORD, R_NONE); \ | |
2854 | JS_END_MACRO | |
2855 | #endif | |
2856 | #else | |
2857 | #define MONITOR_BRANCH_TRACEVIS | |
2858 | #endif | |
2859 | ||
2860 | #define MONITOR_BRANCH() \ | #define MONITOR_BRANCH() \ |
2861 | JS_BEGIN_MACRO \ | JS_BEGIN_MACRO \ |
2862 | if (TRACING_ENABLED(cx)) { \ | if (TRACING_ENABLED(cx)) { \ |
2863 | if (js_MonitorLoopEdge(cx, inlineCallCount)) { \ | if (js_MonitorLoopEdge(cx, inlineCallCount)) { \ |
2864 | JS_ASSERT(TRACE_RECORDER(cx)); \ | JS_ASSERT(TRACE_RECORDER(cx)); \ |
2865 | MONITOR_BRANCH_TRACEVIS; \ | |
2866 | ENABLE_INTERRUPTS(); \ | ENABLE_INTERRUPTS(); \ |
2867 | } \ | } \ |
2868 | fp = cx->fp; \ | fp = cx->fp; \ |
# | Line 2892 | Line 2908 |
2908 | } else { \ | } else { \ |
2909 | op = (JSOp) *++regs.pc; \ | op = (JSOp) *++regs.pc; \ |
2910 | } \ | } \ |
2911 | } else if (op == JSOP_LOOP) { \ | } else if (op == JSOP_TRACE) { \ |
2912 | MONITOR_BRANCH(); \ | MONITOR_BRANCH(); \ |
2913 | op = (JSOp) *regs.pc; \ | op = (JSOp) *regs.pc; \ |
2914 | } \ | } \ |
# | Line 2994 | Line 3010 |
3010 | * calling the interrupt handler, dispatches through normalJumpTable to | * calling the interrupt handler, dispatches through normalJumpTable to |
3011 | * continue the normal bytecode processing. | * continue the normal bytecode processing. |
3012 | */ | */ |
3013 | interrupt: | |
3014 | #else /* !JS_THREADED_INTERP */ | #else /* !JS_THREADED_INTERP */ |
3015 | for (;;) { | for (;;) { |
3016 | advance_pc_by_one: | advance_pc_by_one: |
# | Line 3010 | Line 3026 |
3026 | switchOp = intN(op) | switchMask; | switchOp = intN(op) | switchMask; |
3027 | do_switch: | do_switch: |
3028 | switch (switchOp) { | switch (switchOp) { |
case -1: | ||
JS_ASSERT(switchMask == -1); | ||
#endif /* !JS_THREADED_INTERP */ | ||
{ | ||
bool moreInterrupts = false; | ||
JSTrapHandler handler = cx->debugHooks->interruptHandler; | ||
if (handler) { | ||
#ifdef JS_TRACER | ||
if (TRACE_RECORDER(cx)) | ||
js_AbortRecording(cx, "interrupt handler"); | ||
#endif | ||
switch (handler(cx, script, regs.pc, &rval, | ||
cx->debugHooks->interruptHandlerData)) { | ||
case JSTRAP_ERROR: | ||
goto error; | ||
case JSTRAP_CONTINUE: | ||
break; | ||
case JSTRAP_RETURN: | ||
fp->rval = rval; | ||
ok = JS_TRUE; | ||
goto forced_return; | ||
case JSTRAP_THROW: | ||
cx->throwing = JS_TRUE; | ||
cx->exception = rval; | ||
goto error; | ||
default:; | ||
} | ||
moreInterrupts = true; | ||
} | ||
#ifdef JS_TRACER | ||
TraceRecorder* tr = TRACE_RECORDER(cx); | ||
if (tr) { | ||
JSRecordingStatus status = TraceRecorder::monitorRecording(cx, tr, op); | ||
switch (status) { | ||
case JSRS_CONTINUE: | ||
moreInterrupts = true; | ||
break; | ||
case JSRS_IMACRO: | ||
atoms = COMMON_ATOMS_START(&rt->atomState); | ||
op = JSOp(*regs.pc); | ||
DO_OP(); /* keep interrupting for op. */ | ||
break; | ||
case JSRS_ERROR: | ||
// The code at 'error:' aborts the recording. | ||
goto error; | ||
case JSRS_STOP: | ||
break; | ||
default: | ||
JS_NOT_REACHED("Bad recording status"); | ||
} | ||
} | ||
#endif /* !JS_TRACER */ | ||
#if JS_THREADED_INTERP | ||
jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable; | ||
JS_EXTENSION_(goto *normalJumpTable[op]); | ||
#else | ||
switchMask = moreInterrupts ? -1 : 0; | ||
switchOp = intN(op); | ||
goto do_switch; | ||
#endif | ||
} | ||
/* No-ops for ease of decompilation. */ | ||
ADD_EMPTY_CASE(JSOP_NOP) | ||
ADD_EMPTY_CASE(JSOP_CONDSWITCH) | ||
ADD_EMPTY_CASE(JSOP_TRY) | ||
#if JS_HAS_XML_SUPPORT | ||
ADD_EMPTY_CASE(JSOP_STARTXML) | ||
ADD_EMPTY_CASE(JSOP_STARTXMLEXPR) | ||
#endif | ||
END_EMPTY_CASES | ||
/* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */ | ||
BEGIN_CASE(JSOP_LINENO) | ||
END_CASE(JSOP_LINENO) | ||
BEGIN_CASE(JSOP_PUSH) | ||
PUSH_OPND(JSVAL_VOID); | ||
END_CASE(JSOP_PUSH) | ||
BEGIN_CASE(JSOP_POP) | ||
regs.sp--; | ||
END_CASE(JSOP_POP) | ||
BEGIN_CASE(JSOP_POPN) | ||
regs.sp -= GET_UINT16(regs.pc); | ||
#ifdef DEBUG | ||
JS_ASSERT(StackBase(fp) <= regs.sp); | ||
obj = fp->blockChain; | ||
JS_ASSERT_IF(obj, | ||
OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj) | ||
<= (size_t) (regs.sp - StackBase(fp))); | ||
for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) { | ||
clasp = OBJ_GET_CLASS(cx, obj); | ||
if (clasp != &js_BlockClass && clasp != &js_WithClass) | ||
continue; | ||
if (OBJ_GET_PRIVATE(cx, obj) != fp) | ||
break; | ||
JS_ASSERT(StackBase(fp) + OBJ_BLOCK_DEPTH(cx, obj) | ||
+ ((clasp == &js_BlockClass) | ||
? OBJ_BLOCK_COUNT(cx, obj) | ||
: 1) | ||
<= regs.sp); | ||
} | ||
#endif | ||
END_CASE(JSOP_POPN) | ||
BEGIN_CASE(JSOP_SETRVAL) | ||
BEGIN_CASE(JSOP_POPV) | ||
ASSERT_NOT_THROWING(cx); | ||
fp->rval = POP_OPND(); | ||
END_CASE(JSOP_POPV) | ||
BEGIN_CASE(JSOP_ENTERWITH) | ||
if (!js_EnterWith(cx, -1)) | ||
goto error; | ||
/* | ||
* We must ensure that different "with" blocks have different | ||
* stack depth associated with them. This allows the try handler | ||
* search to properly recover the scope chain. Thus we must keep | ||
* the stack at least at the current level. | ||
* | ||
* We set sp[-1] to the current "with" object to help asserting | ||
* the enter/leave balance in [leavewith]. | ||
*/ | ||
regs.sp[-1] = OBJECT_TO_JSVAL(fp->scopeChain); | ||
END_CASE(JSOP_ENTERWITH) | ||
BEGIN_CASE(JSOP_LEAVEWITH) | ||
JS_ASSERT(regs.sp[-1] == OBJECT_TO_JSVAL(fp->scopeChain)); | ||
regs.sp--; | ||
js_LeaveWith(cx); | ||
END_CASE(JSOP_LEAVEWITH) | ||
BEGIN_CASE(JSOP_RETURN) | ||
fp->rval = POP_OPND(); | ||
/* FALL THROUGH */ | ||
BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */ | ||
BEGIN_CASE(JSOP_STOP) | ||
/* | ||
* When the inlined frame exits with an exception or an error, ok | ||
* will be false after the inline_return label. | ||
*/ | ||
ASSERT_NOT_THROWING(cx); | ||
CHECK_BRANCH(); | ||
if (fp->imacpc) { | ||
/* | ||
* If we are at the end of an imacro, return to its caller in | ||
* the current frame. | ||
*/ | ||
JS_ASSERT(op == JSOP_STOP); | ||
end_imacro: | ||
JS_ASSERT((uintN)(regs.sp - fp->slots) <= script->nslots); | ||
regs.pc = fp->imacpc + js_CodeSpec[*fp->imacpc].length; | ||
fp->imacpc = NULL; | ||
atoms = script->atomMap.vector; | ||
op = JSOp(*regs.pc); | ||
DO_OP(); | ||
} | ||
JS_ASSERT(regs.sp == StackBase(fp)); | ||
if ((fp->flags & JSFRAME_CONSTRUCTING) && | ||
JSVAL_IS_PRIMITIVE(fp->rval)) { | ||
if (!fp->fun) { | ||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, | ||
JSMSG_BAD_NEW_RESULT, | ||
js_ValueToPrintableString(cx, rval)); | ||
goto error; | ||
} | ||
fp->rval = OBJECT_TO_JSVAL(fp->thisp); | ||
} | ||
ok = JS_TRUE; | ||
if (inlineCallCount) | ||
inline_return: | ||
{ | ||
JSInlineFrame *ifp = (JSInlineFrame *) fp; | ||
void *hookData = ifp->hookData; | ||
JS_ASSERT(!fp->blockChain); | ||
JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0)); | ||
if (script->staticLevel < JS_DISPLAY_SIZE) | ||
cx->display[script->staticLevel] = fp->displaySave; | ||
if (hookData) { | ||
JSInterpreterHook hook; | ||
JSBool status; | ||
hook = cx->debugHooks->callHook; | ||
if (hook) { | ||
/* | ||
* Do not pass &ok directly as exposing the address | ||
* inhibits optimizations and uninitialised warnings. | ||
*/ | ||
status = ok; | ||
hook(cx, fp, JS_FALSE, &status, hookData); | ||
ok = status; | ||
CHECK_INTERRUPT_HANDLER(); | ||
} | ||
} | ||
/* | ||
* If fp has a call object, sync values and clear the back- | ||
* pointer. This can happen for a lightweight function if it | ||
* calls eval unexpectedly (in a way that is hidden from the | ||
* compiler). See bug 325540. | ||
*/ | ||
if (fp->callobj) | ||
ok &= js_PutCallObject(cx, fp); | ||
if (fp->argsobj) | ||
ok &= js_PutArgsObject(cx, fp); | ||
#ifdef INCLUDE_MOZILLA_DTRACE | ||
/* DTrace function return, inlines */ | ||
if (JAVASCRIPT_FUNCTION_RVAL_ENABLED()) | ||
jsdtrace_function_rval(cx, fp, fp->fun); | ||
if (JAVASCRIPT_FUNCTION_RETURN_ENABLED()) | ||
jsdtrace_function_return(cx, fp, fp->fun); | ||
#endif | ||
/* Restore context version only if callee hasn't set version. */ | ||
if (JS_LIKELY(cx->version == currentVersion)) { | ||
currentVersion = ifp->callerVersion; | ||
if (currentVersion != cx->version) | ||
js_SetVersion(cx, currentVersion); | ||
} | ||
/* | ||
* If inline-constructing, replace primitive rval with the new | ||
* object passed in via |this|, and instrument this constructor | ||
* invocation | ||
*/ | ||
if (fp->flags & JSFRAME_CONSTRUCTING) { | ||
if (JSVAL_IS_PRIMITIVE(fp->rval)) | ||
fp->rval = OBJECT_TO_JSVAL(fp->thisp); | ||
JS_RUNTIME_METER(cx->runtime, constructs); | ||
} | ||
/* Restore caller's registers. */ | ||
regs = ifp->callerRegs; | ||
/* Store the return value in the caller's operand frame. */ | ||
regs.sp -= 1 + (size_t) ifp->frame.argc; | ||
regs.sp[-1] = fp->rval; | ||
/* Restore cx->fp and release the inline frame's space. */ | ||
cx->fp = fp = fp->down; | ||
JS_ASSERT(fp->regs == &ifp->callerRegs); | ||
fp->regs = ®s; | ||
JS_ARENA_RELEASE(&cx->stackPool, ifp->mark); | ||
/* Restore the calling script's interpreter registers. */ | ||
script = fp->script; | ||
atoms = FrameAtomBase(cx, fp); | ||
/* Resume execution in the calling frame. */ | ||
inlineCallCount--; | ||
if (JS_LIKELY(ok)) { | ||
TRACE_0(LeaveFrame); | ||
JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, script, regs.pc)].length | ||
== JSOP_CALL_LENGTH); | ||
len = JSOP_CALL_LENGTH; | ||
DO_NEXT_OP(len); | ||
} | ||
goto error; | ||
} | ||
goto exit; | ||
BEGIN_CASE(JSOP_DEFAULT) | ||
(void) POP(); | ||
/* FALL THROUGH */ | ||
BEGIN_CASE(JSOP_GOTO) | ||
len = GET_JUMP_OFFSET(regs.pc); | ||
BRANCH(len); | ||
END_CASE(JSOP_GOTO) | ||
BEGIN_CASE(JSOP_IFEQ) | ||
POP_BOOLEAN(cx, rval, cond); | ||
if (cond == JS_FALSE) { | ||
len = GET_JUMP_OFFSET(regs.pc); | ||
BRANCH(len); | ||
} | ||
END_CASE(JSOP_IFEQ) | ||
BEGIN_CASE(JSOP_IFNE) | ||
POP_BOOLEAN(cx, rval, cond); | ||
if (cond != JS_FALSE) { | ||
len = GET_JUMP_OFFSET(regs.pc); | ||
BRANCH(len); | ||
} | ||
END_CASE(JSOP_IFNE) | ||
BEGIN_CASE(JSOP_OR) | ||
POP_BOOLEAN(cx, rval, cond); | ||
if (cond == JS_TRUE) { | ||
len = GET_JUMP_OFFSET(regs.pc); | ||
PUSH_OPND(rval); | ||
DO_NEXT_OP(len); | ||
} | ||
END_CASE(JSOP_OR) | ||
BEGIN_CASE(JSOP_AND) | ||
POP_BOOLEAN(cx, rval, cond); | ||
if (cond == JS_FALSE) { | ||
len = GET_JUMP_OFFSET(regs.pc); | ||
PUSH_OPND(rval); | ||
DO_NEXT_OP(len); | ||
} | ||
END_CASE(JSOP_AND) | ||
BEGIN_CASE(JSOP_DEFAULTX) | ||
(void) POP(); | ||
/* FALL THROUGH */ | ||
BEGIN_CASE(JSOP_GOTOX) | ||
len = GET_JUMPX_OFFSET(regs.pc); | ||
BRANCH(len); | ||
END_CASE(JSOP_GOTOX); | ||
BEGIN_CASE(JSOP_IFEQX) | ||
POP_BOOLEAN(cx, rval, cond); | ||
if (cond == JS_FALSE) { | ||
len = GET_JUMPX_OFFSET(regs.pc); | ||
BRANCH(len); | ||
} | ||
END_CASE(JSOP_IFEQX) | ||
BEGIN_CASE(JSOP_IFNEX) | ||
POP_BOOLEAN(cx, rval, cond); | ||
if (cond != JS_FALSE) { | ||
len = GET_JUMPX_OFFSET(regs.pc); | ||
BRANCH(len); | ||
} | ||
END_CASE(JSOP_IFNEX) | ||
BEGIN_CASE(JSOP_ORX) | ||
POP_BOOLEAN(cx, rval, cond); | ||
if (cond == JS_TRUE) { | ||
len = GET_JUMPX_OFFSET(regs.pc); | ||
PUSH_OPND(rval); | ||
DO_NEXT_OP(len); | ||
} | ||
END_CASE(JSOP_ORX) | ||
BEGIN_CASE(JSOP_ANDX) | ||
POP_BOOLEAN(cx, rval, cond); | ||
if (cond == JS_FALSE) { | ||
len = GET_JUMPX_OFFSET(regs.pc); | ||
PUSH_OPND(rval); | ||
DO_NEXT_OP(len); | ||
} | ||
END_CASE(JSOP_ANDX) | ||
/* | ||
* If the index value at sp[n] is not an int that fits in a jsval, it could | ||
* be an object (an XML QName, AttributeName, or AnyName), but only if we are | ||
* compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a | ||
* string atom id. | ||
*/ | ||
#define FETCH_ELEMENT_ID(obj, n, id) \ | ||
JS_BEGIN_MACRO \ | ||
jsval idval_ = FETCH_OPND(n); \ | ||
if (JSVAL_IS_INT(idval_)) { \ | ||
id = INT_JSVAL_TO_JSID(idval_); \ | ||
} else { \ | ||
if (!js_InternNonIntElementId(cx, obj, idval_, &id)) \ | ||
goto error; \ | ||
regs.sp[n] = ID_TO_VALUE(id); \ | ||
} \ | ||
JS_END_MACRO | ||
#define TRY_BRANCH_AFTER_COND(cond,spdec) \ | ||
JS_BEGIN_MACRO \ | ||
uintN diff_; \ | ||
JS_ASSERT(js_CodeSpec[op].length == 1); \ | ||
diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \ | ||
if (diff_ <= 1) { \ | ||
regs.sp -= spdec; \ | ||
if (cond == (diff_ != 0)) { \ | ||
++regs.pc; \ | ||
len = GET_JUMP_OFFSET(regs.pc); \ | ||
BRANCH(len); \ | ||
} \ | ||
len = 1 + JSOP_IFEQ_LENGTH; \ | ||
DO_NEXT_OP(len); \ | ||
} \ | ||
JS_END_MACRO | ||
BEGIN_CASE(JSOP_IN) | ||
rval = FETCH_OPND(-1); | ||
if (JSVAL_IS_PRIMITIVE(rval)) { | ||
js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rval, NULL); | ||
goto error; | ||
} | ||
obj = JSVAL_TO_OBJECT(rval); | ||
FETCH_ELEMENT_ID(obj, -2, id); | ||
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) | ||
goto error; | ||
cond = prop != NULL; | ||
if (prop) | ||
OBJ_DROP_PROPERTY(cx, obj2, prop); | ||
TRY_BRANCH_AFTER_COND(cond, 2); | ||
regs.sp--; | ||
STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); | ||
END_CASE(JSOP_IN) | ||
BEGIN_CASE(JSOP_ITER) | ||
JS_ASSERT(regs.sp > StackBase(fp)); | ||
flags = regs.pc[1]; | ||
if (!js_ValueToIterator(cx, flags, ®s.sp[-1])) | ||
goto error; | ||
CHECK_INTERRUPT_HANDLER(); | ||
JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1])); | ||
PUSH(JSVAL_VOID); | ||
END_CASE(JSOP_ITER) | ||
BEGIN_CASE(JSOP_NEXTITER) | ||
JS_ASSERT(regs.sp - 2 >= StackBase(fp)); | ||
JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2])); | ||
if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), ®s.sp[-1])) | ||
goto error; | ||
CHECK_INTERRUPT_HANDLER(); | ||
rval = BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE); | ||
PUSH(rval); | ||
END_CASE(JSOP_NEXTITER) | ||
BEGIN_CASE(JSOP_ENDITER) | ||
/* | ||
* Decrease the stack pointer even when !ok -- see comments in the | ||
* exception capturing code for details. | ||
*/ | ||
JS_ASSERT(regs.sp - 2 >= StackBase(fp)); | ||
ok = js_CloseIterator(cx, regs.sp[-2]); | ||
regs.sp -= 2; | ||
if (!ok) | ||
goto error; | ||
END_CASE(JSOP_ENDITER) | ||
BEGIN_CASE(JSOP_FORARG) | ||
JS_ASSERT(regs.sp - 2 >= StackBase(fp)); | ||
slot = GET_ARGNO(regs.pc); | ||
JS_ASSERT(slot < fp->fun->nargs); | ||
fp->argv[slot] = regs.sp[-1]; | ||
END_CASE(JSOP_FORARG) | ||
BEGIN_CASE(JSOP_FORLOCAL) | ||
JS_ASSERT(regs.sp - 2 >= StackBase(fp)); | ||
slot = GET_SLOTNO(regs.pc); | ||
JS_ASSERT(slot < fp->script->nslots); | ||
vp = &fp->slots[slot]; | ||
*vp = regs.sp[-1]; | ||
END_CASE(JSOP_FORLOCAL) | ||
BEGIN_CASE(JSOP_FORNAME) | ||
JS_ASSERT(regs.sp - 2 >= StackBase(fp)); | ||
LOAD_ATOM(0); | ||
id = ATOM_TO_JSID(atom); | ||
if (!js_FindProperty(cx, id, &obj, &obj2, &prop)) | ||
goto error; | ||
if (prop) | ||
OBJ_DROP_PROPERTY(cx, obj2, prop); | ||
ok = OBJ_SET_PROPERTY(cx, obj, id, ®s.sp[-1]); | ||
if (!ok) | ||
goto error; | ||
END_CASE(JSOP_FORNAME) | ||
BEGIN_CASE(JSOP_FORPROP) | ||
JS_ASSERT(regs.sp - 2 >= StackBase(fp)); | ||
LOAD_ATOM(0); | ||
id = ATOM_TO_JSID(atom); | ||
FETCH_OBJECT(cx, -1, lval, obj); | ||
ok = OBJ_SET_PROPERTY(cx, obj, id, ®s.sp[-2]); | ||
if (!ok) | ||
goto error; | ||
regs.sp--; | ||
END_CASE(JSOP_FORPROP) | ||
BEGIN_CASE(JSOP_FORELEM) | ||
/* | ||
* JSOP_FORELEM simply dups the property identifier at top of stack | ||
* and lets the subsequent JSOP_ENUMELEM opcode sequence handle the | ||
* left-hand side expression evaluation and assignment. This opcode | ||
* exists solely to help the decompiler. | ||
*/ | ||
JS_ASSERT(regs.sp - 2 >= StackBase(fp)); | ||
rval = FETCH_OPND(-1); | ||
PUSH(rval); | ||
END_CASE(JSOP_FORELEM) | ||
BEGIN_CASE(JSOP_DUP) | ||
JS_ASSERT(regs.sp > StackBase(fp)); | ||
rval = FETCH_OPND(-1); | ||
PUSH(rval); | ||
END_CASE(JSOP_DUP) | ||
BEGIN_CASE(JSOP_DUP2) | ||
JS_ASSERT(regs.sp - 2 >= StackBase(fp)); | ||
lval = FETCH_OPND(-2); | ||
rval = FETCH_OPND(-1); | ||
PUSH(lval); | ||
PUSH(rval); | ||
END_CASE(JSOP_DUP2) | ||
BEGIN_CASE(JSOP_SWAP) | ||
JS_ASSERT(regs.sp - 2 >= StackBase(fp)); | ||
lval = FETCH_OPND(-2); | ||
rval = FETCH_OPND(-1); | ||
STORE_OPND(-1, lval); | ||
STORE_OPND(-2, rval); | ||
END_CASE(JSOP_SWAP) | ||
BEGIN_CASE(JSOP_PICK) | ||
i = regs.pc[1]; | ||
JS_ASSERT(regs.sp - (i+1) >= StackBase(fp)); | ||
lval = regs.sp[-(i+1)]; | ||
memmove(regs.sp - (i+1), regs.sp - i, sizeof(jsval)*i); | ||
regs.sp[-1] = lval; | ||
END_CASE(JSOP_PICK) | ||
#define PROPERTY_OP(n, call) \ | ||
JS_BEGIN_MACRO \ | ||
/* Fetch the left part and resolve it to a non-null object. */ \ | ||
FETCH_OBJECT(cx, n, lval, obj); \ | ||
\ | ||
/* Get or set the property. */ \ | ||
if (!call) \ | ||
goto error; \ | ||
JS_END_MACRO | ||
#define ELEMENT_OP(n, call) \ | ||
JS_BEGIN_MACRO \ | ||
/* Fetch the left part and resolve it to a non-null object. */ \ | ||
FETCH_OBJECT(cx, n - 1, lval, obj); \ | ||
\ | ||
/* Fetch index and convert it to id suitable for use with obj. */ \ | ||
FETCH_ELEMENT_ID(obj, n, id); \ | ||
\ | ||
/* Get or set the element. */ \ | ||
if (!call) \ | ||
goto error; \ | ||
JS_END_MACRO | ||
#define NATIVE_GET(cx,obj,pobj,sprop,vp) \ | ||
JS_BEGIN_MACRO \ | ||
if (SPROP_HAS_STUB_GETTER(sprop)) { \ | ||
/* Fast path for Object instance properties. */ \ | ||
JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \ | ||
!SPROP_HAS_STUB_SETTER(sprop)); \ | ||
*vp = ((sprop)->slot != SPROP_INVALID_SLOT) \ | ||
? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \ | ||
: JSVAL_VOID; \ | ||
} else { \ | ||
if (!js_NativeGet(cx, obj, pobj, sprop, vp)) \ | ||
goto error; \ | ||
} \ | ||
JS_END_MACRO | ||
#define NATIVE_SET(cx,obj,sprop,entry,vp) \ | ||
JS_BEGIN_MACRO \ | ||
TRACE_2(SetPropHit, entry, sprop); \ | ||
if (SPROP_HAS_STUB_SETTER(sprop) && \ | ||
(sprop)->slot != SPROP_INVALID_SLOT) { \ | ||
/* Fast path for, e.g., Object instance properties. */ \ | ||
LOCKED_OBJ_WRITE_BARRIER(cx, obj, (sprop)->slot, *vp); \ | ||
} else { \ | ||
if (!js_NativeSet(cx, obj, sprop, vp)) \ | ||
goto error; \ | ||
} \ | ||
JS_END_MACRO | ||
/* | ||
* Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is | ||
* the constant length of the SET opcode sequence, and spdec is the constant | ||
* by which to decrease the stack pointer to pop all of the SET op's operands. | ||
* | ||
* NB: unlike macros that could conceivably be replaced by functions (ignoring | ||
* goto error), where a call should not have to be braced in order to expand | ||
* correctly (e.g., in if (cond) FOO(); else BAR()), these three macros lack | ||
* JS_{BEGIN,END}_MACRO brackets. They are also indented so as to align with | ||
* nearby opcode code. | ||
*/ | ||
#define SKIP_POP_AFTER_SET(oplen,spdec) \ | ||
if (regs.pc[oplen] == JSOP_POP) { \ | ||
regs.sp -= spdec; \ | ||
regs.pc += oplen + JSOP_POP_LENGTH; \ | ||
op = (JSOp) *regs.pc; \ | ||
DO_OP(); \ | ||
} | ||
#define END_SET_CASE(OP) \ | ||
SKIP_POP_AFTER_SET(OP##_LENGTH, 1); \ | ||
END_CASE(OP) | ||
#define END_SET_CASE_STORE_RVAL(OP,spdec) \ | ||
SKIP_POP_AFTER_SET(OP##_LENGTH, spdec); \ | ||
rval = FETCH_OPND(-1); \ | ||
regs.sp -= (spdec) - 1; \ | ||
STORE_OPND(-1, rval); \ | ||
END_CASE(OP) | ||
BEGIN_CASE(JSOP_SETCONST) | ||
LOAD_ATOM(0); | ||
obj = fp->varobj; | ||
rval = FETCH_OPND(-1); | ||
if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval, | ||
JS_PropertyStub, JS_PropertyStub, | ||
JSPROP_ENUMERATE | JSPROP_PERMANENT | | ||
JSPROP_READONLY, | ||
NULL)) { | ||
goto error; | ||
} | ||
END_SET_CASE(JSOP_SETCONST); | ||
#if JS_HAS_DESTRUCTURING | ||
BEGIN_CASE(JSOP_ENUMCONSTELEM) | ||
rval = FETCH_OPND(-3); | ||
FETCH_OBJECT(cx, -2, lval, obj); | ||
FETCH_ELEMENT_ID(obj, -1, id); | ||
if (!OBJ_DEFINE_PROPERTY(cx, obj, id, rval, | ||
JS_PropertyStub, JS_PropertyStub, | ||
JSPROP_ENUMERATE | JSPROP_PERMANENT | | ||
JSPROP_READONLY, | ||
NULL)) { | ||
goto error; | ||
} | ||
regs.sp -= 3; | ||
END_CASE(JSOP_ENUMCONSTELEM) | ||
#endif | ||
BEGIN_CASE(JSOP_BINDNAME) | ||
do { | ||
JSPropCacheEntry *entry; | ||
/* | ||
* We can skip the property lookup for the global object. If | ||
* the property does not exist anywhere on the scope chain, | ||
* JSOP_SETNAME adds the property to the global. | ||
* | ||
* As a consequence of this optimization for the global object | ||
* we run its JSRESOLVE_ASSIGNING-tolerant resolve hooks only | ||
* in JSOP_SETNAME, after the interpreter evaluates the right- | ||
* hand-side of the assignment, and not here. | ||
* | ||
* This should be transparent to the hooks because the script, | ||
* instead of name = rhs, could have used global.name = rhs | ||
* given a global object reference, which also calls the hooks | ||
* only after evaluating the rhs. We desire such resolve hook | ||
* equivalence between the two forms. | ||
*/ | ||
obj = fp->scopeChain; | ||
if (!OBJ_GET_PARENT(cx, obj)) | ||
break; | ||
if (JS_LIKELY(OBJ_IS_NATIVE(obj))) { | ||
PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom); | ||
if (!atom) { | ||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); | ||
JS_UNLOCK_OBJ(cx, obj2); | ||
break; | ||
} | ||
} else { | ||
entry = NULL; | ||
LOAD_ATOM(0); | ||
} | ||
id = ATOM_TO_JSID(atom); | ||
obj = js_FindIdentifierBase(cx, fp->scopeChain, id); | ||
if (!obj) | ||
goto error; | ||
} while (0); | ||
PUSH_OPND(OBJECT_TO_JSVAL(obj)); | ||
END_CASE(JSOP_BINDNAME) | ||
BEGIN_CASE(JSOP_IMACOP) | ||
JS_ASSERT(JS_UPTRDIFF(fp->imacpc, script->code) < script->length); | ||
op = JSOp(*fp->imacpc); | ||
DO_OP(); | ||
#define BITWISE_OP(OP) \ | ||
JS_BEGIN_MACRO \ | ||
FETCH_INT(cx, -2, i); \ | ||
FETCH_INT(cx, -1, j); \ | ||
i = i OP j; \ | ||
regs.sp--; \ | ||
STORE_INT(cx, -1, i); \ | ||
JS_END_MACRO | ||
BEGIN_CASE(JSOP_BITOR) | ||
BITWISE_OP(|); | ||
END_CASE(JSOP_BITOR) | ||
BEGIN_CASE(JSOP_BITXOR) | ||
BITWISE_OP(^); | ||
END_CASE(JSOP_BITXOR) | ||
BEGIN_CASE(JSOP_BITAND) | ||
BITWISE_OP(&); | ||
END_CASE(JSOP_BITAND) | ||
#define RELATIONAL_OP(OP) \ | ||
JS_BEGIN_MACRO \ | ||
rval = FETCH_OPND(-1); \ | ||
lval = FETCH_OPND(-2); \ | ||
/* Optimize for two int-tagged operands (typical loop control). */ \ | ||
if ((lval & rval) & JSVAL_INT) { \ | ||
cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \ | ||
} else { \ | ||
if (!JSVAL_IS_PRIMITIVE(lval)) \ | ||
DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval); \ | ||
if (!JSVAL_IS_PRIMITIVE(rval)) \ | ||
DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval); \ | ||
if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \ | ||
str = JSVAL_TO_STRING(lval); \ | ||
str2 = JSVAL_TO_STRING(rval); \ | ||
cond = js_CompareStrings(str, str2) OP 0; \ | ||
} else { \ | ||
VALUE_TO_NUMBER(cx, -2, lval, d); \ | ||
VALUE_TO_NUMBER(cx, -1, rval, d2); \ | ||
cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \ | ||
} \ | ||
} \ | ||
TRY_BRANCH_AFTER_COND(cond, 2); \ | ||
regs.sp--; \ | ||
STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ | ||
JS_END_MACRO | ||
/* | ||
* NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies | ||
* because they begin if/else chains, so callers must not put semicolons after | ||
* the call expressions! | ||
*/ | ||
#if JS_HAS_XML_SUPPORT | ||
#define XML_EQUALITY_OP(OP) \ | ||
if ((ltmp == JSVAL_OBJECT && \ | ||
(obj2 = JSVAL_TO_OBJECT(lval)) && \ | ||
OBJECT_IS_XML(cx, obj2)) || \ | ||
(rtmp == JSVAL_OBJECT && \ | ||
(obj2 = JSVAL_TO_OBJECT(rval)) && \ | ||
OBJECT_IS_XML(cx, obj2))) { \ | ||
if (JSVAL_IS_OBJECT(rval) && obj2 == JSVAL_TO_OBJECT(rval)) \ | ||
rval = lval; \ | ||
if (!js_TestXMLEquality(cx, obj2, rval, &cond)) \ | ||
goto error; \ | ||
cond = cond OP JS_TRUE; \ | ||
} else | ||
#define EXTENDED_EQUALITY_OP(OP) \ | ||
if (ltmp == JSVAL_OBJECT && \ | ||
(obj2 = JSVAL_TO_OBJECT(lval)) && \ | ||
((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) { \ | ||
JSExtendedClass *xclasp; \ | ||
\ | ||
xclasp = (JSExtendedClass *) clasp; \ | ||
if (!xclasp->equality(cx, obj2, rval, &cond)) \ | ||
goto error; \ | ||
cond = cond OP JS_TRUE; \ | ||
} else | ||
#else | ||
#define XML_EQUALITY_OP(OP) /* nothing */ | ||
#define EXTENDED_EQUALITY_OP(OP) /* nothing */ | ||
#endif | ||
#define EQUALITY_OP(OP, IFNAN) \ | ||
JS_BEGIN_MACRO \ | ||
rval = FETCH_OPND(-1); \ | ||
lval = FETCH_OPND(-2); \ | ||
ltmp = JSVAL_TAG(lval); \ | ||
rtmp = JSVAL_TAG(rval); \ | ||
XML_EQUALITY_OP(OP) \ | ||
if (ltmp == rtmp) { \ | ||
if (ltmp == JSVAL_STRING) { \ | ||
str = JSVAL_TO_STRING(lval); \ | ||
str2 = JSVAL_TO_STRING(rval); \ | ||
cond = js_EqualStrings(str, str2) OP JS_TRUE; \ | ||
} else if (ltmp == JSVAL_DOUBLE) { \ | ||
d = *JSVAL_TO_DOUBLE(lval); \ | ||
d2 = *JSVAL_TO_DOUBLE(rval); \ | ||
cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ | ||
} else { \ | ||
EXTENDED_EQUALITY_OP(OP) \ | ||
/* Handle all undefined (=>NaN) and int combinations. */ \ | ||
cond = lval OP rval; \ | ||
} \ | ||
} else { \ | ||
if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \ | ||
cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \ | ||
} else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \ | ||
cond = 1 OP 0; \ | ||
} else { \ | ||
if (ltmp == JSVAL_OBJECT) { \ | ||
DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); \ | ||
ltmp = JSVAL_TAG(lval); \ | ||
} else if (rtmp == JSVAL_OBJECT) { \ | ||
DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); \ | ||
rtmp = JSVAL_TAG(rval); \ | ||
} \ | ||
if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \ | ||
str = JSVAL_TO_STRING(lval); \ | ||
str2 = JSVAL_TO_STRING(rval); \ | ||
cond = js_EqualStrings(str, str2) OP JS_TRUE; \ | ||
} else { \ | ||
VALUE_TO_NUMBER(cx, -2, lval, d); \ | ||
VALUE_TO_NUMBER(cx, -1, rval, d2); \ | ||
cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ | ||
} \ | ||
} \ | ||
} \ | ||
TRY_BRANCH_AFTER_COND(cond, 2); \ | ||
regs.sp--; \ | ||
STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ | ||
JS_END_MACRO | ||
BEGIN_CASE(JSOP_EQ) | ||
EQUALITY_OP(==, JS_FALSE); | ||
END_CASE(JSOP_EQ) | ||
BEGIN_CASE(JSOP_NE) | ||
EQUALITY_OP(!=, JS_TRUE); | ||
END_CASE(JSOP_NE) | ||
#define STRICT_EQUALITY_OP(OP) \ | ||
JS_BEGIN_MACRO \ | ||
rval = FETCH_OPND(-1); \ | ||
lval = FETCH_OPND(-2); \ | ||
cond = js_StrictlyEqual(cx, lval, rval) OP JS_TRUE; \ | ||
regs.sp--; \ | ||
STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ | ||
JS_END_MACRO | ||
BEGIN_CASE(JSOP_STRICTEQ) | ||
STRICT_EQUALITY_OP(==); | ||
END_CASE(JSOP_STRICTEQ) | ||
BEGIN_CASE(JSOP_STRICTNE) | ||
STRICT_EQUALITY_OP(!=); | ||
END_CASE(JSOP_STRICTNE) | ||
BEGIN_CASE(JSOP_CASE) | ||
STRICT_EQUALITY_OP(==); | ||
(void) POP(); | ||
if (cond) { | ||
len = GET_JUMP_OFFSET(regs.pc); | ||
BRANCH(len); | ||
} | ||
PUSH(lval); | ||
END_CASE(JSOP_CASE) | ||
BEGIN_CASE(JSOP_CASEX) | ||
STRICT_EQUALITY_OP(==); | ||
(void) POP(); | ||
if (cond) { | ||
len = GET_JUMPX_OFFSET(regs.pc); | ||
BRANCH(len); | ||
} | ||
PUSH(lval); | ||
END_CASE(JSOP_CASEX) | ||
BEGIN_CASE(JSOP_LT) | ||
RELATIONAL_OP(<); | ||
END_CASE(JSOP_LT) | ||
BEGIN_CASE(JSOP_LE) | ||
RELATIONAL_OP(<=); | ||
END_CASE(JSOP_LE) | ||
BEGIN_CASE(JSOP_GT) | ||
RELATIONAL_OP(>); | ||
END_CASE(JSOP_GT) | ||
BEGIN_CASE(JSOP_GE) | ||
RELATIONAL_OP(>=); | ||
END_CASE(JSOP_GE) | ||
#undef EQUALITY_OP | ||
#undef RELATIONAL_OP | ||
#define SIGNED_SHIFT_OP(OP) \ | ||
JS_BEGIN_MACRO \ | ||
FETCH_INT(cx, -2, i); \ | ||
FETCH_INT(cx, -1, j); \ | ||
i = i OP (j & 31); \ | ||
regs.sp--; \ | ||
STORE_INT(cx, -1, i); \ | ||
JS_END_MACRO | ||
BEGIN_CASE(JSOP_LSH) | ||
SIGNED_SHIFT_OP(<<); | ||
END_CASE(JSOP_LSH) | ||
BEGIN_CASE(JSOP_RSH) | ||
SIGNED_SHIFT_OP(>>); | ||
END_CASE(JSOP_RSH) | ||
BEGIN_CASE(JSOP_URSH) | ||
{ | ||
uint32 u; | ||
FETCH_UINT(cx, -2, u); | ||
FETCH_INT(cx, -1, j); | ||
u >>= (j & 31); | ||
regs.sp--; | ||
STORE_UINT(cx, -1, u); | ||
} | ||
END_CASE(JSOP_URSH) | ||
#undef BITWISE_OP | ||
#undef SIGNED_SHIFT_OP | ||
BEGIN_CASE(JSOP_ADD) | ||
rval = FETCH_OPND(-1); | ||
lval = FETCH_OPND(-2); | ||
#if JS_HAS_XML_SUPPORT | ||
if (!JSVAL_IS_PRIMITIVE(lval) && | ||
(obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) && | ||
VALUE_IS_XML(cx, rval)) { | ||
if (!js_ConcatenateXML(cx, obj2, rval, &rval)) | ||
goto error; | ||
regs.sp--; | ||
STORE_OPND(-1, rval); | ||
} else | ||
#endif | ||
{ | ||
if (!JSVAL_IS_PRIMITIVE(lval)) | ||
DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval); | ||
if (!JSVAL_IS_PRIMITIVE(rval)) | ||
DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval); | ||
if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) { | ||
if (cond) { | ||
str = JSVAL_TO_STRING(lval); | ||
str2 = js_ValueToString(cx, rval); | ||
if (!str2) | ||
goto error; | ||
regs.sp[-1] = STRING_TO_JSVAL(str2); | ||
} else { | ||
str2 = JSVAL_TO_STRING(rval); | ||
str = js_ValueToString(cx, lval); | ||
if (!str) | ||
goto error; | ||
regs.sp[-2] = STRING_TO_JSVAL(str); | ||
} | ||
str = js_ConcatStrings(cx, str, str2); | ||
if (!str) | ||
goto error; | ||
regs.sp--; | ||
STORE_OPND(-1, STRING_TO_JSVAL(str)); | ||
} else { | ||
VALUE_TO_NUMBER(cx, -2, lval, d); | ||
VALUE_TO_NUMBER(cx, -1, rval, d2); | ||
d += d2; | ||
regs.sp--; | ||
STORE_NUMBER(cx, -1, d); | ||
} | ||
} | ||
END_CASE(JSOP_ADD) | ||
#define BINARY_OP(OP) \ | ||
JS_BEGIN_MACRO \ | ||
FETCH_NUMBER(cx, -2, d); \ | ||
FETCH_NUMBER(cx, -1, d2); \ | ||
d = d OP d2; \ | ||
regs.sp--; \ | ||
STORE_NUMBER(cx, -1, d); \ | ||
JS_END_MACRO | ||
BEGIN_CASE(JSOP_SUB) | ||
BINARY_OP(-); | ||
END_CASE(JSOP_SUB) | ||
BEGIN_CASE(JSOP_MUL) | ||
BINARY_OP(*); | ||
END_CASE(JSOP_MUL) | ||
BEGIN_CASE(JSOP_DIV) | ||
FETCH_NUMBER(cx, -1, d2); | ||
FETCH_NUMBER(cx, -2, d); | ||
regs.sp--; | ||
if (d2 == 0) { | ||
#ifdef XP_WIN | ||
/* XXX MSVC miscompiles such that (NaN == 0) */ | ||
if (JSDOUBLE_IS_NaN(d2)) | ||
rval = DOUBLE_TO_JSVAL(rt->jsNaN); | ||
else | ||
#endif | ||
if (d == 0 || JSDOUBLE_IS_NaN(d)) | ||
rval = DOUBLE_TO_JSVAL(rt->jsNaN); | ||
else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31) | ||
rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity); | ||
else | ||
rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity); | ||
STORE_OPND(-1, rval); | ||
} else { | ||
d /= d2; | ||
STORE_NUMBER(cx, -1, d); | ||
} | ||
END_CASE(JSOP_DIV) | ||
BEGIN_CASE(JSOP_MOD) | ||
FETCH_NUMBER(cx, -1, d2); | ||
FETCH_NUMBER(cx, -2, d); | ||
regs.sp--; | ||
if (d2 == 0) { | ||
STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN)); | ||
} else { | ||
#ifdef XP_WIN | ||
/* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ | ||
if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2))) | ||
#endif | ||
d = fmod(d, d2); | ||
STORE_NUMBER(cx, -1, d); | ||
} | ||
END_CASE(JSOP_MOD) | ||
BEGIN_CASE(JSOP_NOT) | ||
POP_BOOLEAN(cx, rval, cond); | ||
PUSH_OPND(BOOLEAN_TO_JSVAL(!cond)); | ||
END_CASE(JSOP_NOT) | ||
BEGIN_CASE(JSOP_BITNOT) | ||
FETCH_INT(cx, -1, i); | ||
i = ~i; | ||
STORE_INT(cx, -1, i); | ||
END_CASE(JSOP_BITNOT) | ||
BEGIN_CASE(JSOP_NEG) | ||
/* | ||
* When the operand is int jsval, INT_FITS_IN_JSVAL(i) implies | ||
* INT_FITS_IN_JSVAL(-i) unless i is 0 or JSVAL_INT_MIN when the | ||
* results, -0.0 or JSVAL_INT_MAX + 1, are jsdouble values. | ||
*/ | ||
rval = FETCH_OPND(-1); | ||
if (JSVAL_IS_INT(rval) && | ||
rval != INT_TO_JSVAL(JSVAL_INT_MIN) && | ||
(i = JSVAL_TO_INT(rval)) != 0) { | ||
JS_STATIC_ASSERT(!INT_FITS_IN_JSVAL(-JSVAL_INT_MIN)); | ||
i = -i; | ||
JS_ASSERT(INT_FITS_IN_JSVAL(i)); | ||
regs.sp[-1] = INT_TO_JSVAL(i); | ||
} else { | ||
if (JSVAL_IS_DOUBLE(rval)) { | ||
d = *JSVAL_TO_DOUBLE(rval); | ||
} else { | ||
d = js_ValueToNumber(cx, ®s.sp[-1]); | ||
if (JSVAL_IS_NULL(regs.sp[-1])) | ||
goto error; | ||
JS_ASSERT(JSVAL_IS_NUMBER(regs.sp[-1]) || | ||
regs.sp[-1] == JSVAL_TRUE); | ||
} | ||
#ifdef HPUX | ||
/* | ||
* Negation of a zero doesn't produce a negative | ||
* zero on HPUX. Perform the operation by bit | ||
* twiddling. | ||
*/ | ||
JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT; | ||
#else | ||
d = -d; | ||
#endif | ||
if (!js_NewNumberInRootedValue(cx, d, ®s.sp[-1])) | ||
goto error; | ||
} | ||
END_CASE(JSOP_NEG) | ||
BEGIN_CASE(JSOP_POS) | ||
rval = FETCH_OPND(-1); | ||
if (!JSVAL_IS_NUMBER(rval)) { | ||
d = js_ValueToNumber(cx, ®s.sp[-1]); | ||
rval = regs.sp[-1]; | ||
if (JSVAL_IS_NULL(rval)) | ||
goto error; | ||
if (rval == JSVAL_TRUE) { | ||
if (!js_NewNumberInRootedValue(cx, d, ®s.sp[-1])) | ||
goto error; | ||
} else { | ||
JS_ASSERT(JSVAL_IS_NUMBER(rval)); | ||
} | ||
} | ||
END_CASE(JSOP_POS) | ||
BEGIN_CASE(JSOP_DELNAME) | ||
LOAD_ATOM(0); | ||
id = ATOM_TO_JSID(atom); | ||
if (!js_FindProperty(cx, id, &obj, &obj2, &prop)) | ||
goto error; | ||
/* ECMA says to return true if name is undefined or inherited. */ | ||
PUSH_OPND(JSVAL_TRUE); | ||
if (prop) { | ||
OBJ_DROP_PROPERTY(cx, obj2, prop); | ||
if (!OBJ_DELETE_PROPERTY(cx, obj, id, ®s.sp[-1])) | ||
goto error; | ||
} | ||
END_CASE(JSOP_DELNAME) | ||
BEGIN_CASE(JSOP_DELPROP) | ||
LOAD_ATOM(0); | ||
id = ATOM_TO_JSID(atom); | ||
PROPERTY_OP(-1, OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); | ||
STORE_OPND(-1, rval); | ||
END_CASE(JSOP_DELPROP) | ||
BEGIN_CASE(JSOP_DELELEM) | ||
ELEMENT_OP(-1, OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); | ||
regs.sp--; | ||
STORE_OPND(-1, rval); | ||
END_CASE(JSOP_DELELEM) | ||
BEGIN_CASE(JSOP_TYPEOFEXPR) | ||
BEGIN_CASE(JSOP_TYPEOF) | ||
rval = FETCH_OPND(-1); | ||
type = JS_TypeOfValue(cx, rval); | ||
atom = rt->atomState.typeAtoms[type]; | ||
STORE_OPND(-1, ATOM_KEY(atom)); | ||
END_CASE(JSOP_TYPEOF) | ||
BEGIN_CASE(JSOP_VOID) | ||
STORE_OPND(-1, JSVAL_VOID); | ||
END_CASE(JSOP_VOID) | ||
BEGIN_CASE(JSOP_INCELEM) | ||
BEGIN_CASE(JSOP_DECELEM) | ||
BEGIN_CASE(JSOP_ELEMINC) | ||
BEGIN_CASE(JSOP_ELEMDEC) | ||
/* | ||
* Delay fetching of id until we have the object to ensure | ||
* the proper evaluation order. See bug 372331. | ||
*/ | ||
id = 0; | ||
i = -2; | ||
goto fetch_incop_obj; | ||
BEGIN_CASE(JSOP_INCPROP) | ||
BEGIN_CASE(JSOP_DECPROP) | ||
BEGIN_CASE(JSOP_PROPINC) | ||
BEGIN_CASE(JSOP_PROPDEC) | ||
LOAD_ATOM(0); | ||
id = ATOM_TO_JSID(atom); | ||
i = -1; | ||
fetch_incop_obj: | ||
FETCH_OBJECT(cx, i, lval, obj); | ||
if (id == 0) | ||
FETCH_ELEMENT_ID(obj, -1, id); | ||
goto do_incop; | ||
BEGIN_CASE(JSOP_INCNAME) | ||
BEGIN_CASE(JSOP_DECNAME) | ||
BEGIN_CASE(JSOP_NAMEINC) | ||
BEGIN_CASE(JSOP_NAMEDEC) | ||
{ | ||
JSPropCacheEntry *entry; | ||
obj = fp->scopeChain; | ||
if (JS_LIKELY(OBJ_IS_NATIVE(obj))) { | ||
PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom); | ||
if (!atom) { | ||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); | ||
if (obj == obj2 && PCVAL_IS_SLOT(entry->vword)) { | ||
slot = PCVAL_TO_SLOT(entry->vword); | ||
JS_ASSERT(slot < OBJ_SCOPE(obj)->freeslot); | ||
rval = LOCKED_OBJ_GET_SLOT(obj, slot); | ||
if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) { | ||
rtmp = rval; | ||
rval += (js_CodeSpec[op].format & JOF_INC) ? 2 : -2; | ||
if (!(js_CodeSpec[op].format & JOF_POST)) | ||
rtmp = rval; | ||
LOCKED_OBJ_SET_SLOT(obj, slot, rval); | ||
JS_UNLOCK_OBJ(cx, obj); | ||
PUSH_OPND(rtmp); | ||
len = JSOP_INCNAME_LENGTH; | ||
DO_NEXT_OP(len); | ||
} | ||
} | ||
JS_UNLOCK_OBJ(cx, obj2); | ||
LOAD_ATOM(0); | ||
} | ||
} else { | ||
LOAD_ATOM(0); | ||
} | ||
id = ATOM_TO_JSID(atom); | ||
if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop)) | ||
goto error; | ||
if (!prop) | ||
goto atom_not_defined; | ||
OBJ_DROP_PROPERTY(cx, obj2, prop); | ||
} | ||
do_incop: | ||
{ | ||
const JSCodeSpec *cs; | ||
jsval v; | ||
/* | ||
* We need a root to store the value to leave on the stack until | ||
* we have done with OBJ_SET_PROPERTY. | ||
*/ | ||
PUSH_OPND(JSVAL_NULL); | ||
if (!OBJ_GET_PROPERTY(cx, obj, id, ®s.sp[-1])) | ||
goto error; | ||
cs = &js_CodeSpec[op]; | ||
JS_ASSERT(cs->ndefs == 1); | ||
JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) == JOF_TMPSLOT2); | ||
v = regs.sp[-1]; | ||
if (JS_LIKELY(CAN_DO_FAST_INC_DEC(v))) { | ||
jsval incr; | ||
incr = (cs->format & JOF_INC) ? 2 : -2; | ||
if (cs->format & JOF_POST) { | ||
regs.sp[-1] = v + incr; | ||
} else { | ||
v += incr; | ||
regs.sp[-1] = v; | ||
} | ||
fp->flags |= JSFRAME_ASSIGNING; | ||
ok = OBJ_SET_PROPERTY(cx, obj, id, ®s.sp[-1]); | ||
fp->flags &= ~JSFRAME_ASSIGNING; | ||
if (!ok) | ||
goto error; | ||
/* | ||
* We must set regs.sp[-1] to v for both post and pre increments | ||
* as the setter overwrites regs.sp[-1]. | ||
*/ | ||
regs.sp[-1] = v; | ||
} else { | ||
/* We need an extra root for the result. */ | ||
PUSH_OPND(JSVAL_NULL); | ||
if (!js_DoIncDec(cx, cs, ®s.sp[-2], ®s.sp[-1])) | ||
goto error; | ||
fp->flags |= JSFRAME_ASSIGNING; | ||
ok = OBJ_SET_PROPERTY(cx, obj, id, ®s.sp[-1]); | ||
fp->flags &= ~JSFRAME_ASSIGNING; | ||
if (!ok) | ||
goto error; | ||
regs.sp--; | ||
} | ||
if (cs->nuses == 0) { | ||
/* regs.sp[-1] already contains the result of name increment. */ | ||
} else { | ||
rtmp = regs.sp[-1]; | ||
regs.sp -= cs->nuses; | ||
regs.sp[-1] = rtmp; | ||
} | ||
len = cs->length; | ||
DO_NEXT_OP(len); | ||
} | ||
{ | ||
jsval incr, incr2; | ||
/* Position cases so the most frequent i++ does not need a jump. */ | ||
BEGIN_CASE(JSOP_DECARG) | ||
incr = -2; incr2 = -2; goto do_arg_incop; | ||
BEGIN_CASE(JSOP_ARGDEC) | ||
incr = -2; incr2 = 0; goto do_arg_incop; | ||
BEGIN_CASE(JSOP_INCARG) | ||
incr = 2; incr2 = 2; goto do_arg_incop; | ||
BEGIN_CASE(JSOP_ARGINC) | ||
incr = 2; incr2 = 0; | ||
do_arg_incop: | ||
slot = GET_ARGNO(regs.pc); | ||
JS_ASSERT(slot < fp->fun->nargs); | ||
METER_SLOT_OP(op, slot); | ||
vp = fp->argv + slot; | ||
goto do_int_fast_incop; | ||
BEGIN_CASE(JSOP_DECLOCAL) | ||
incr = -2; incr2 = -2; goto do_local_incop; | ||
BEGIN_CASE(JSOP_LOCALDEC) | ||
incr = -2; incr2 = 0; goto do_local_incop; | ||
BEGIN_CASE(JSOP_INCLOCAL) | ||
incr = 2; incr2 = 2; goto do_local_incop; | ||
BEGIN_CASE(JSOP_LOCALINC) | ||
incr = 2; incr2 = 0; | ||
/* | ||
* do_local_incop comes right before do_int_fast_incop as we want to | ||
* avoid an extra jump for variable cases as local++ is more frequent | ||
* than arg++. | ||
*/ | ||
do_local_incop: | ||
slot = GET_SLOTNO(regs.pc); | ||
JS_ASSERT(slot < fp->script->nslots); | ||
vp = fp->slots + slot; | ||
METER_SLOT_OP(op, slot); | ||
vp = fp->slots + slot; | ||
do_int_fast_incop: | ||
rval = *vp; | ||
if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) { | ||
*vp = rval + incr; | ||
JS_ASSERT(JSOP_INCARG_LENGTH == js_CodeSpec[op].length); | ||
SKIP_POP_AFTER_SET(JSOP_INCARG_LENGTH, 0); | ||
PUSH_OPND(rval + incr2); | ||
} else { | ||
PUSH_OPND(rval); | ||
if (!js_DoIncDec(cx, &js_CodeSpec[op], ®s.sp[-1], vp)) | ||
goto error; | ||
} | ||
len = JSOP_INCARG_LENGTH; | ||
JS_ASSERT(len == js_CodeSpec[op].length); | ||
DO_NEXT_OP(len); | ||
} | ||
/* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ | ||
#define FAST_GLOBAL_INCREMENT_OP(SLOWOP,INCR,INCR2) \ | ||
op2 = SLOWOP; \ | ||
incr = INCR; \ | ||
incr2 = INCR2; \ | ||
goto do_global_incop | ||
{ | ||
jsval incr, incr2; | ||
BEGIN_CASE(JSOP_DECGVAR) | ||
FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, -2, -2); | ||
BEGIN_CASE(JSOP_GVARDEC) | ||
FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, -2, 0); | ||
BEGIN_CASE(JSOP_INCGVAR) | ||
FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, 2, 2); | ||
BEGIN_CASE(JSOP_GVARINC) | ||
FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, 2, 0); | ||
#undef FAST_GLOBAL_INCREMENT_OP | ||
do_global_incop: | ||
JS_ASSERT((js_CodeSpec[op].format & JOF_TMPSLOT_MASK) == | ||
JOF_TMPSLOT2); | ||
slot = GET_SLOTNO(regs.pc); | ||
JS_ASSERT(slot < GlobalVarCount(fp)); | ||
METER_SLOT_OP(op, slot); | ||
lval = fp->slots[slot]; | ||
if (JSVAL_IS_NULL(lval)) { | ||
op = op2; | ||
DO_OP(); | ||
} | ||
slot = JSVAL_TO_INT(lval); | ||
rval = OBJ_GET_SLOT(cx, fp->varobj, slot); | ||
if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) { | ||
PUSH_OPND(rval + incr2); | ||
rval += incr; | ||
} else { | ||
PUSH_OPND(rval); | ||
PUSH_OPND(JSVAL_NULL); /* Extra root */ | ||
if (!js_DoIncDec(cx, &js_CodeSpec[op], ®s.sp[-2], ®s.sp[-1])) | ||
goto error; | ||
rval = regs.sp[-1]; | ||
--regs.sp; | ||
} | ||
OBJ_SET_SLOT(cx, fp->varobj, slot, rval); | ||
len = JSOP_INCGVAR_LENGTH; /* all gvar incops are same length */ | ||
JS_ASSERT(len == js_CodeSpec[op].length); | ||
DO_NEXT_OP(len); | ||
} | ||
#define COMPUTE_THIS(cx, fp, obj) \ | ||
JS_BEGIN_MACRO \ | ||
if (!(obj = js_ComputeThisForFrame(cx, fp))) \ | ||
goto error; \ | ||
JS_END_MACRO | ||
BEGIN_CASE(JSOP_THIS) | ||
COMPUTE_THIS(cx, fp, obj); | ||
PUSH_OPND(OBJECT_TO_JSVAL(obj)); | ||
END_CASE(JSOP_THIS) | ||
BEGIN_CASE(JSOP_GETTHISPROP) | ||
i = 0; | ||
COMPUTE_THIS(cx, fp, obj); | ||
PUSH(JSVAL_NULL); | ||
goto do_getprop_with_obj; | ||
#undef COMPUTE_THIS | ||
BEGIN_CASE(JSOP_GETARGPROP) | ||
i = ARGNO_LEN; | ||
slot = GET_ARGNO(regs.pc); | ||
JS_ASSERT(slot < fp->fun->nargs); | ||
PUSH_OPND(fp->argv[slot]); | ||
goto do_getprop_body; | ||
BEGIN_CASE(JSOP_GETLOCALPROP) | ||
i = SLOTNO_LEN; | ||
slot = GET_SLOTNO(regs.pc); | ||
JS_ASSERT(slot < script->nslots); | ||
PUSH_OPND(fp->slots[slot]); | ||
goto do_getprop_body; | ||
BEGIN_CASE(JSOP_GETPROP) | ||
BEGIN_CASE(JSOP_GETXPROP) | ||
i = 0; | ||
do_getprop_body: | ||
lval = FETCH_OPND(-1); | ||
do_getprop_with_lval: | ||
VALUE_TO_OBJECT(cx, -1, lval, obj); | ||
do_getprop_with_obj: | ||
do { | ||
JSObject *aobj; | ||
JSPropCacheEntry *entry; | ||
aobj = js_GetProtoIfDenseArray(cx, obj); | ||
if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) { | ||
PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom); | ||
if (!atom) { | ||
ASSERT_VALID_PROPERTY_CACHE_HIT(i, aobj, obj2, entry); | ||
if (PCVAL_IS_OBJECT(entry->vword)) { | ||
rval = PCVAL_OBJECT_TO_JSVAL(entry->vword); | ||
} else if (PCVAL_IS_SLOT(entry->vword)) { | ||
slot = PCVAL_TO_SLOT(entry->vword); | ||
JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); | ||
rval = LOCKED_OBJ_GET_SLOT(obj2, slot); | ||
} else { | ||
JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); | ||
sprop = PCVAL_TO_SPROP(entry->vword); | ||
NATIVE_GET(cx, obj, obj2, sprop, &rval); | ||
} | ||
JS_UNLOCK_OBJ(cx, obj2); | ||
break; | ||
} | ||
} else { | ||
entry = NULL; | ||
if (i < 0) | ||
atom = rt->atomState.lengthAtom; | ||
else | ||
LOAD_ATOM(i); | ||
} | ||
id = ATOM_TO_JSID(atom); | ||
if (entry | ||
? !js_GetPropertyHelper(cx, obj, id, true, &rval) | ||
: !OBJ_GET_PROPERTY(cx, obj, id, &rval)) { | ||
goto error; | ||
} | ||
} while (0); | ||
STORE_OPND(-1, rval); | ||
JS_ASSERT(JSOP_GETPROP_LENGTH + i == js_CodeSpec[op].length); | ||
len = JSOP_GETPROP_LENGTH + i; | ||
END_VARLEN_CASE | ||
BEGIN_CASE(JSOP_LENGTH) | ||
lval = FETCH_OPND(-1); | ||
if (JSVAL_IS_STRING(lval)) { | ||
str = JSVAL_TO_STRING(lval); | ||
regs.sp[-1] = INT_TO_JSVAL(JSSTRING_LENGTH(str)); | ||
} else if (!JSVAL_IS_PRIMITIVE(lval) && | ||
(obj = JSVAL_TO_OBJECT(lval), OBJ_IS_ARRAY(cx, obj))) { | ||
jsuint length; | ||
/* | ||
* We know that the array is created with only its 'length' | ||
* private data in a fixed slot at JSSLOT_ARRAY_LENGTH. See | ||
* also JSOP_ARRAYPUSH, far below. | ||
*/ | ||
length = obj->fslots[JSSLOT_ARRAY_LENGTH]; | ||
if (length <= JSVAL_INT_MAX) { | ||
regs.sp[-1] = INT_TO_JSVAL(length); | ||
} else if (!js_NewDoubleInRootedValue(cx, (jsdouble) length, | ||
®s.sp[-1])) { | ||
goto error; | ||
} | ||
} else { | ||
i = -2; | ||
goto do_getprop_with_lval; | ||
} | ||
END_CASE(JSOP_LENGTH) | ||
BEGIN_CASE(JSOP_CALLPROP) | ||
{ | ||
JSObject *aobj; | ||
JSPropCacheEntry *entry; | ||
lval = FETCH_OPND(-1); | ||
if (!JSVAL_IS_PRIMITIVE(lval)) { | ||
obj = JSVAL_TO_OBJECT(lval); | ||
} else { | ||
if (JSVAL_IS_STRING(lval)) { | ||
i = JSProto_String; | ||
} else if (JSVAL_IS_NUMBER(lval)) { | ||
i = JSProto_Number; | ||
} else if (JSVAL_IS_BOOLEAN(lval)) { | ||
i = JSProto_Boolean; | ||
} else { | ||
JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)); | ||
js_ReportIsNullOrUndefined(cx, -1, lval, NULL); | ||
goto error; | ||
} | ||
if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj)) | ||
goto error; | ||
} | ||
aobj = js_GetProtoIfDenseArray(cx, obj); | ||
if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) { | ||
PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom); | ||
if (!atom) { | ||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry); | ||
if (PCVAL_IS_OBJECT(entry->vword)) { | ||
rval = PCVAL_OBJECT_TO_JSVAL(entry->vword); | ||
} else if (PCVAL_IS_SLOT(entry->vword)) { | ||
slot = PCVAL_TO_SLOT(entry->vword); | ||
JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); | ||
rval = LOCKED_OBJ_GET_SLOT(obj2, slot); | ||
} else { | ||
JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); | ||
sprop = PCVAL_TO_SPROP(entry->vword); | ||
NATIVE_GET(cx, obj, obj2, sprop, &rval); | ||
} | ||
JS_UNLOCK_OBJ(cx, obj2); | ||
STORE_OPND(-1, rval); | ||
PUSH_OPND(lval); | ||
goto end_callprop; | ||
} | ||
} else { | ||
entry = NULL; | ||
LOAD_ATOM(0); | ||
} | ||
/* | ||
* Cache miss: use the immediate atom that was loaded for us under | ||
* PROPERTY_CACHE_TEST. | ||
*/ | ||
id = ATOM_TO_JSID(atom); | ||
PUSH(JSVAL_NULL); | ||
if (!JSVAL_IS_PRIMITIVE(lval)) { | ||
if (!js_GetMethod(cx, obj, id, !!entry, &rval)) | ||
goto error; | ||
STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); | ||
STORE_OPND(-2, rval); | ||
} else { | ||
JS_ASSERT(obj->map->ops->getProperty == js_GetProperty); | ||
if (!js_GetPropertyHelper(cx, obj, id, true, &rval)) | ||
goto error; | ||
STORE_OPND(-1, lval); | ||
STORE_OPND(-2, rval); | ||
} | ||
end_callprop: | ||
/* Wrap primitive lval in object clothing if necessary. */ | ||
if (JSVAL_IS_PRIMITIVE(lval)) { | ||
/* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */ | ||
if (!VALUE_IS_FUNCTION(cx, rval) || | ||
(obj = JSVAL_TO_OBJECT(rval), | ||
fun = GET_FUNCTION_PRIVATE(cx, obj), | ||
!PRIMITIVE_THIS_TEST(fun, lval))) { | ||
if (!js_PrimitiveToObject(cx, ®s.sp[-1])) | ||
goto error; | ||
} | ||
} | ||
#if JS_HAS_NO_SUCH_METHOD | ||
if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) { | ||
LOAD_ATOM(0); | ||
regs.sp[-2] = ATOM_KEY(atom); | ||
if (!js_OnUnknownMethod(cx, regs.sp - 2)) | ||
goto error; | ||
} | ||
#endif | ||
} | ||
END_CASE(JSOP_CALLPROP) | ||
BEGIN_CASE(JSOP_SETNAME) | ||
BEGIN_CASE(JSOP_SETPROP) | ||
rval = FETCH_OPND(-1); | ||
lval = FETCH_OPND(-2); | ||
JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval) || op == JSOP_SETPROP); | ||
VALUE_TO_OBJECT(cx, -2, lval, obj); | ||
do { | ||
JSPropCacheEntry *entry; | ||
entry = NULL; | ||
atom = NULL; | ||
if (JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) { | ||
JSPropertyCache *cache = &JS_PROPERTY_CACHE(cx); | ||
uint32 kshape = OBJ_SHAPE(obj); | ||
/* | ||
* Open-code JS_PROPERTY_CACHE_TEST, specializing for two | ||
* important set-property cases. First: | ||
* | ||
* function f(a, b, c) { | ||
* var o = {p:a, q:b, r:c}; | ||
* return o; | ||
* } | ||
* | ||
* or similar real-world cases, which evolve a newborn | ||
* native object predicatably through some bounded number | ||
* of property additions. And second: | ||
* | ||
* o.p = x; | ||
* | ||
* in a frequently executed method or loop body, where p | ||
* will (possibly after the first iteration) always exist | ||
* in native object o. | ||
*/ | ||
entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)]; | ||
PCMETER(cache->pctestentry = entry); | ||
PCMETER(cache->tests++); | ||
PCMETER(cache->settests++); | ||
if (entry->kpc == regs.pc && | ||
entry->kshape == kshape && | ||
PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape) { | ||
JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0); | ||
JS_LOCK_OBJ(cx, obj); | ||
JSScope *scope = OBJ_SCOPE(obj); | ||
if (scope->shape == kshape) { | ||
JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); | ||
sprop = PCVAL_TO_SPROP(entry->vword); | ||
JS_ASSERT(!(sprop->attrs & JSPROP_READONLY)); | ||
JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj))); | ||
/* | ||
* Fastest path: check whether the cached sprop is | ||
* already in scope and call NATIVE_GET and break | ||
* to get out of the do-while(0). But we can call | ||
* NATIVE_GET only if obj owns scope or sprop is | ||
* shared. | ||
*/ | ||
bool checkForAdd; | ||
if (scope->object == obj || | ||
(sprop->attrs & JSPROP_SHARED)) { | ||
if (sprop == scope->lastProp || | ||
SCOPE_HAS_PROPERTY(scope, sprop)) { | ||
PCMETER(cache->pchits++); | ||
PCMETER(cache->setpchits++); | ||
NATIVE_SET(cx, obj, sprop, entry, &rval); | ||
JS_UNLOCK_SCOPE(cx, scope); | ||
break; | ||
} | ||
checkForAdd = | ||
!(sprop->attrs & JSPROP_SHARED) && | ||
sprop->parent == scope->lastProp && | ||
!SCOPE_HAD_MIDDLE_DELETE(scope); | ||
} else { | ||
scope = js_GetMutableScope(cx, obj); | ||
if (!scope) { | ||
JS_UNLOCK_OBJ(cx, obj); | ||
goto error; | ||
} | ||
checkForAdd = !sprop->parent; | ||
} | ||
if (checkForAdd && | ||
SPROP_HAS_STUB_SETTER(sprop) && | ||
(slot = sprop->slot) == scope->freeslot) { | ||
/* | ||
* Fast path: adding a plain old property that | ||
* was once at the frontier of the property | ||
* tree, whose slot is next to claim among the | ||
* allocated slots in obj, where scope->table | ||
* has not been created yet. | ||
* | ||
* We may want to remove hazard conditions | ||
* above and inline compensation code here, | ||
* depending on real-world workloads. | ||
*/ | ||
JS_ASSERT(!(LOCKED_OBJ_GET_CLASS(obj)->flags & | ||
JSCLASS_SHARE_ALL_PROPERTIES)); | ||
PCMETER(cache->pchits++); | ||
PCMETER(cache->addpchits++); | ||
/* | ||
* Beware classes such as Function that use | ||
* the reserveSlots hook to allocate a number | ||
* of reserved slots that may vary with obj. | ||
*/ | ||
if (slot < STOBJ_NSLOTS(obj) && | ||
!OBJ_GET_CLASS(cx, obj)->reserveSlots) { | ||
++scope->freeslot; | ||
} else { | ||
if (!js_AllocSlot(cx, obj, &slot)) { | ||
JS_UNLOCK_SCOPE(cx, scope); | ||
goto error; | ||
} | ||
} | ||
/* | ||
* If this obj's number of reserved slots | ||
* differed, or if something created a hash | ||
* table for scope, we must pay the price of | ||
* js_AddScopeProperty. | ||
* | ||
* If slot does not match the cached sprop's | ||
* slot, update the cache entry in the hope | ||
* that obj and other instances with the same | ||
* number of reserved slots are now "hot". | ||
*/ | ||
if (slot != sprop->slot || scope->table) { | ||
JSScopeProperty *sprop2 = | ||
js_AddScopeProperty(cx, scope, | ||
sprop->id, | ||
sprop->getter, | ||
sprop->setter, | ||
slot, | ||
sprop->attrs, | ||
sprop->flags, | ||
sprop->shortid); | ||
if (!sprop2) { | ||
js_FreeSlot(cx, obj, slot); | ||
JS_UNLOCK_SCOPE(cx, scope); | ||
goto error; | ||
} | ||
if (sprop2 != sprop) { | ||
PCMETER(cache->slotchanges++); | ||
JS_ASSERT(slot != sprop->slot && | ||
slot == sprop2->slot && | ||
sprop2->id == sprop->id); | ||
entry->vword = SPROP_TO_PCVAL(sprop2); | ||
} | ||
sprop = sprop2; | ||
} else { | ||
js_ExtendScopeShape(cx, scope, sprop); | ||
++scope->entryCount; | ||
scope->lastProp = sprop; | ||
} | ||
GC_WRITE_BARRIER(cx, scope, | ||
LOCKED_OBJ_GET_SLOT(obj, slot), | ||
rval); | ||
TRACE_2(SetPropHit, entry, sprop); | ||
LOCKED_OBJ_SET_SLOT(obj, slot, rval); | ||
JS_UNLOCK_SCOPE(cx, scope); | ||
/* | ||
* Purge the property cache of the id we may | ||
* have just shadowed in obj's scope and proto | ||
* chains. We do this after unlocking obj's | ||
* scope to avoid lock nesting. | ||
*/ | ||
js_PurgeScopeChain(cx, obj, sprop->id); | ||
break; | ||
} | ||
PCMETER(cache->setpcmisses++); | ||
atom = NULL; | ||
} | ||
JS_UNLOCK_OBJ(cx, obj); | ||
} | ||
atom = js_FullTestPropertyCache(cx, regs.pc, &obj, &obj2, | ||
&entry); | ||
if (atom) { | ||
PCMETER(cache->misses++); | ||
PCMETER(cache->setmisses++); | ||
} else { | ||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); | ||
sprop = NULL; | ||
if (obj == obj2) { | ||
JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); | ||
sprop = PCVAL_TO_SPROP(entry->vword); | ||
JS_ASSERT(!(sprop->attrs & JSPROP_READONLY)); | ||
JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj2))); | ||
NATIVE_SET(cx, obj, sprop, entry, &rval); | ||
} | ||
JS_UNLOCK_OBJ(cx, obj2); | ||
if (sprop) | ||
break; | ||
} | ||
} | ||
if (!atom) | ||
LOAD_ATOM(0); | ||
id = ATOM_TO_JSID(atom); | ||
if (entry) { | ||
if (!js_SetPropertyHelper(cx, obj, id, true, &rval)) | ||
goto error; | ||
} else { | ||
if (!OBJ_SET_PROPERTY(cx, obj, id, &rval)) | ||
goto error; | ||
ABORT_RECORDING(cx, "Non-native set"); | ||
} | ||
} while (0); | ||
END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2); | ||
BEGIN_CASE(JSOP_GETELEM) | ||
/* Open-coded ELEMENT_OP optimized for strings and dense arrays. */ | ||
lval = FETCH_OPND(-2); | ||
rval = FETCH_OPND(-1); | ||
if (JSVAL_IS_STRING(lval) && JSVAL_IS_INT(rval)) { | ||
str = JSVAL_TO_STRING(lval); | ||
i = JSVAL_TO_INT(rval); | ||
if ((size_t)i < JSSTRING_LENGTH(str)) { | ||
str = js_GetUnitString(cx, str, (size_t)i); | ||
if (!str) | ||
goto error; | ||
rval = STRING_TO_JSVAL(str); | ||
goto end_getelem; | ||
} | ||
} | ||
VALUE_TO_OBJECT(cx, -2, lval, obj); | ||
if (JSVAL_IS_INT(rval)) { | ||
if (OBJ_IS_DENSE_ARRAY(cx, obj)) { | ||
jsuint length; | ||
length = js_DenseArrayCapacity(obj); | ||
i = JSVAL_TO_INT(rval); | ||
if ((jsuint)i < length && | ||
i < obj->fslots[JSSLOT_ARRAY_LENGTH]) { | ||
rval = obj->dslots[i]; | ||
if (rval != JSVAL_HOLE) | ||
goto end_getelem; | ||
/* Reload rval from the stack in the rare hole case. */ | ||
rval = FETCH_OPND(-1); | ||
} | ||
} | ||
id = INT_JSVAL_TO_JSID(rval); | ||
} else { | ||
if (!js_InternNonIntElementId(cx, obj, rval, &id)) | ||
goto error; | ||
} | ||
if (!OBJ_GET_PROPERTY(cx, obj, id, &rval)) | ||
goto error; | ||
end_getelem: | ||
regs.sp--; | ||
STORE_OPND(-1, rval); | ||
END_CASE(JSOP_GETELEM) | ||
BEGIN_CASE(JSOP_CALLELEM) | ||
ELEMENT_OP(-1, js_GetMethod(cx, obj, id, false, &rval)); | ||
#if JS_HAS_NO_SUCH_METHOD | ||
if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) { | ||
regs.sp[-2] = regs.sp[-1]; | ||
regs.sp[-1] = OBJECT_TO_JSVAL(obj); | ||
if (!js_OnUnknownMethod(cx, regs.sp - 2)) | ||
goto error; | ||
} else | ||
#endif | ||
{ | ||
STORE_OPND(-2, rval); | ||
STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); | ||
} | ||
END_CASE(JSOP_CALLELEM) | ||
BEGIN_CASE(JSOP_SETELEM) | ||
rval = FETCH_OPND(-1); | ||
FETCH_OBJECT(cx, -3, lval, obj); | ||
FETCH_ELEMENT_ID(obj, -2, id); | ||
do { | ||
if (OBJ_IS_DENSE_ARRAY(cx, obj) && JSID_IS_INT(id)) { | ||
jsuint length; | ||
length = js_DenseArrayCapacity(obj); | ||
i = JSID_TO_INT(id); | ||
if ((jsuint)i < length) { | ||
if (obj->dslots[i] == JSVAL_HOLE) { | ||
if (js_PrototypeHasIndexedProperties(cx, obj)) | ||
break; | ||
if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH]) | ||
obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1; | ||
obj->fslots[JSSLOT_ARRAY_COUNT]++; | ||
} | ||
obj->dslots[i] = rval; | ||
goto end_setelem; | ||
} | ||
} | ||
} while (0); | ||
if (!OBJ_SET_PROPERTY(cx, obj, id, &rval)) | ||
goto error; | ||
end_setelem: | ||
END_SET_CASE_STORE_RVAL(JSOP_SETELEM, 3) | ||
BEGIN_CASE(JSOP_ENUMELEM) | ||
/* Funky: the value to set is under the [obj, id] pair. */ | ||
rval = FETCH_OPND(-3); | ||
FETCH_OBJECT(cx, -2, lval, obj); | ||
FETCH_ELEMENT_ID(obj, -1, id); | ||
if (!OBJ_SET_PROPERTY(cx, obj, id, &rval)) | ||
goto error; | ||
regs.sp -= 3; | ||
END_CASE(JSOP_ENUMELEM) | ||
BEGIN_CASE(JSOP_NEW) | ||
/* Get immediate argc and find the constructor function. */ | ||
argc = GET_ARGC(regs.pc); | ||
vp = regs.sp - (2 + argc); | ||
JS_ASSERT(vp >= StackBase(fp)); | ||
/* | ||
* Assign lval, obj, and fun exactly as the code at inline_call: | ||
* expects to find them, to avoid nesting a js_Interpret call via | ||
* js_InvokeConstructor. | ||
*/ | ||
lval = *vp; | ||
if (VALUE_IS_FUNCTION(cx, lval)) { | ||
obj = JSVAL_TO_OBJECT(lval); | ||
fun = GET_FUNCTION_PRIVATE(cx, obj); | ||
if (FUN_INTERPRETED(fun)) { | ||
/* Root as we go using vp[1]. */ | ||
if (!OBJ_GET_PROPERTY(cx, obj, | ||
ATOM_TO_JSID(cx->runtime->atomState | ||
.classPrototypeAtom), | ||
&vp[1])) { | ||
goto error; | ||
} | ||
rval = vp[1]; | ||
obj2 = js_NewObject(cx, &js_ObjectClass, | ||
JSVAL_IS_OBJECT(rval) | ||
? JSVAL_TO_OBJECT(rval) | ||
: NULL, | ||
OBJ_GET_PARENT(cx, obj), | ||
0); | ||
if (!obj2) | ||
goto error; | ||
vp[1] = OBJECT_TO_JSVAL(obj2); | ||
flags = JSFRAME_CONSTRUCTING; | ||
goto inline_call; | ||
} | ||
} | ||
if (!js_InvokeConstructor(cx, argc, JS_FALSE, vp)) | ||
goto error; | ||
regs.sp = vp + 1; | ||
CHECK_INTERRUPT_HANDLER(); | ||
TRACE_0(NativeCallComplete); | ||
END_CASE(JSOP_NEW) | ||
BEGIN_CASE(JSOP_CALL) | ||
BEGIN_CASE(JSOP_EVAL) | ||
BEGIN_CASE(JSOP_APPLY) | ||
argc = GET_ARGC(regs.pc); | ||
vp = regs.sp - (argc + 2); | ||
lval = *vp; | ||
if (VALUE_IS_FUNCTION(cx, lval)) { | ||
obj = JSVAL_TO_OBJECT(lval); | ||
fun = GET_FUNCTION_PRIVATE(cx, obj); | ||
/* Clear frame flags since this is not a constructor call. */ | ||
flags = 0; | ||
if (FUN_INTERPRETED(fun)) | ||
inline_call: | ||
{ | ||
uintN nframeslots, nvars, missing; | ||
JSArena *a; | ||
jsuword nbytes; | ||
void *newmark; | ||
jsval *newsp; | ||
JSInlineFrame *newifp; | ||
JSInterpreterHook hook; | ||
/* Restrict recursion of lightweight functions. */ | ||
if (inlineCallCount == MAX_INLINE_CALL_COUNT) { | ||
js_ReportOverRecursed(cx); | ||
goto error; | ||
} | ||
/* Compute the total number of stack slots needed by fun. */ | ||
nframeslots = JS_HOWMANY(sizeof(JSInlineFrame), | ||
sizeof(jsval)); | ||
script = fun->u.i.script; | ||
atoms = script->atomMap.vector; | ||
nbytes = (nframeslots + script->nslots) * sizeof(jsval); | ||
/* Allocate missing expected args adjacent to actuals. */ | ||
a = cx->stackPool.current; | ||
newmark = (void *) a->avail; | ||
if (fun->nargs <= argc) { | ||
missing = 0; | ||
} else { | ||
newsp = vp + 2 + fun->nargs; | ||
JS_ASSERT(newsp > regs.sp); | ||
if ((jsuword) newsp <= a->limit) { | ||
if ((jsuword) newsp > a->avail) | ||
a->avail = (jsuword) newsp; | ||
jsval *argsp = newsp; | ||
do { | ||
*--argsp = JSVAL_VOID; | ||
} while (argsp != regs.sp); | ||
missing = 0; | ||
} else { | ||
missing = fun->nargs - argc; | ||
nbytes += (2 + fun->nargs) * sizeof(jsval); | ||
} | ||
} | ||
/* Allocate the inline frame with its slots and operands. */ | ||
if (a->avail + nbytes <= a->limit) { | ||
newsp = (jsval *) a->avail; | ||
a->avail += nbytes; | ||
JS_ASSERT(missing == 0); | ||
} else { | ||
JS_ARENA_ALLOCATE_CAST(newsp, jsval *, &cx->stackPool, | ||
nbytes); | ||
if (!newsp) { | ||
js_ReportOutOfScriptQuota(cx); | ||
goto bad_inline_call; | ||
} | ||
/* | ||
* Move args if the missing ones overflow arena a, then | ||
* push undefined for the missing args. | ||
*/ | ||
if (missing) { | ||
memcpy(newsp, vp, (2 + argc) * sizeof(jsval)); | ||
vp = newsp; | ||
newsp = vp + 2 + argc; | ||
do { | ||
*newsp++ = JSVAL_VOID; | ||
} while (--missing != 0); | ||
} | ||
} | ||
/* Claim space for the stack frame and initialize it. */ | ||
newifp = (JSInlineFrame *) newsp; | ||
newsp += nframeslots; | ||
newifp->frame.callobj = NULL; | ||
newifp->frame.argsobj = NULL; | ||
newifp->frame.varobj = NULL; | ||
newifp->frame.script = script; | ||
newifp->frame.callee = obj; | ||
newifp->frame.fun = fun; | ||
newifp->frame.argc = argc; | ||
newifp->frame.argv = vp + 2; | ||
newifp->frame.rval = JSVAL_VOID; | ||
newifp->frame.down = fp; | ||
newifp->frame.annotation = NULL; | ||
newifp->frame.scopeChain = parent = OBJ_GET_PARENT(cx, obj); | ||
newifp->frame.sharpDepth = 0; | ||
newifp->frame.sharpArray = NULL; | ||
newifp->frame.flags = flags; | ||
newifp->frame.dormantNext = NULL; | ||
newifp->frame.xmlNamespace = NULL; | ||
newifp->frame.blockChain = NULL; | ||
if (script->staticLevel < JS_DISPLAY_SIZE) { | ||
JSStackFrame **disp = &cx->display[script->staticLevel]; | ||
newifp->frame.displaySave = *disp; | ||
*disp = &newifp->frame; | ||
} | ||
newifp->mark = newmark; | ||
/* Compute the 'this' parameter now that argv is set. */ | ||
JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags)); | ||
JS_ASSERT(JSVAL_IS_OBJECT(vp[1])); | ||
newifp->frame.thisp = (JSObject *)vp[1]; | ||
newifp->frame.regs = NULL; | ||
newifp->frame.imacpc = NULL; | ||
newifp->frame.slots = newsp; | ||
/* Push void to initialize local variables. */ | ||
nvars = fun->u.i.nvars; | ||
while (nvars--) | ||
*newsp++ = JSVAL_VOID; | ||
/* Scope with a call object parented by callee's parent. */ | ||
if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) && | ||
!js_GetCallObject(cx, &newifp->frame)) { | ||
goto bad_inline_call; | ||
} | ||
/* Switch version if currentVersion wasn't overridden. */ | ||
newifp->callerVersion = (JSVersion) cx->version; | ||
if (JS_LIKELY(cx->version == currentVersion)) { | ||
currentVersion = (JSVersion) script->version; | ||
if (currentVersion != cx->version) | ||
js_SetVersion(cx, currentVersion); | ||
} | ||
/* Push the frame and set interpreter registers. */ | ||
newifp->callerRegs = regs; | ||
fp->regs = &newifp->callerRegs; | ||
regs.sp = newsp; | ||
regs.pc = script->code; | ||
newifp->frame.regs = ®s; | ||
cx->fp = fp = &newifp->frame; | ||
/* Call the debugger hook if present. */ | ||
hook = cx->debugHooks->callHook; | ||
if (hook) { | ||
newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0, | ||
cx->debugHooks->callHookData); | ||
CHECK_INTERRUPT_HANDLER(); | ||
} else { | ||
newifp->hookData = NULL; | ||
} | ||
TRACE_0(EnterFrame); | ||
inlineCallCount++; | ||
JS_RUNTIME_METER(rt, inlineCalls); | ||
#ifdef INCLUDE_MOZILLA_DTRACE | ||
/* DTrace function entry, inlines */ | ||
if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED()) | ||
jsdtrace_function_entry(cx, fp, fun); | ||
if (JAVASCRIPT_FUNCTION_INFO_ENABLED()) | ||
jsdtrace_function_info(cx, fp, fp->down, fun); | ||
if (JAVASCRIPT_FUNCTION_ARGS_ENABLED()) | ||
jsdtrace_function_args(cx, fp, fun); | ||
#endif | ||
/* Load first op and dispatch it (safe since JSOP_STOP). */ | ||
op = (JSOp) *regs.pc; | ||
DO_OP(); | ||
bad_inline_call: | ||
JS_ASSERT(fp->regs == ®s); | ||
script = fp->script; | ||
atoms = script->atomMap.vector; | ||
js_FreeRawStack(cx, newmark); | ||
goto error; | ||
} | ||
#ifdef INCLUDE_MOZILLA_DTRACE | ||
/* DTrace function entry, non-inlines */ | ||
if (VALUE_IS_FUNCTION(cx, lval)) { | ||
if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED()) | ||
jsdtrace_function_entry(cx, fp, fun); | ||
if (JAVASCRIPT_FUNCTION_INFO_ENABLED()) | ||
jsdtrace_function_info(cx, fp, fp, fun); | ||
if (JAVASCRIPT_FUNCTION_ARGS_ENABLED()) | ||
jsdtrace_function_args(cx, fp, fun); | ||
} | ||
#endif | ||
if (fun->flags & JSFUN_FAST_NATIVE) { | ||
JS_ASSERT(fun->u.n.extra == 0); | ||
JS_ASSERT(JSVAL_IS_OBJECT(vp[1]) || | ||
PRIMITIVE_THIS_TEST(fun, vp[1])); | ||
ok = ((JSFastNative) fun->u.n.native)(cx, argc, vp); | ||
#ifdef INCLUDE_MOZILLA_DTRACE | ||
if (VALUE_IS_FUNCTION(cx, lval)) { | ||
if (JAVASCRIPT_FUNCTION_RVAL_ENABLED()) | ||
jsdtrace_function_rval(cx, fp, fun); | ||
if (JAVASCRIPT_FUNCTION_RETURN_ENABLED()) | ||
jsdtrace_function_return(cx, fp, fun); | ||
} | ||
#endif | ||
regs.sp = vp + 1; | ||
if (!ok) { | ||
/* | ||
* If we are executing the JSOP_NEXTITER imacro and a Stopiteration | ||
* exception is raised, transform it into a JSVAL_HOLE return value. | ||
* The tracer generates equivalent code by calling CatchStopIteration_tn. | ||
*/ | ||
if (fp->imacpc && *fp->imacpc == JSOP_NEXTITER && | ||
cx->throwing && js_ValueIsStopIteration(cx->exception)) { | ||
// pc may point to JSOP_DUP here due to bug 474854. | ||
JS_ASSERT(*regs.pc == JSOP_CALL || *regs.pc == JSOP_DUP); | ||
cx->throwing = JS_FALSE; | ||
cx->exception = JSVAL_VOID; | ||
regs.sp[-1] = JSVAL_HOLE; | ||
} else { | ||
goto error; | ||
} | ||
} | ||
TRACE_0(NativeCallComplete); | ||
goto end_call; | ||
} | ||
} | ||
ok = js_Invoke(cx, argc, vp, 0); | ||
#ifdef INCLUDE_MOZILLA_DTRACE | ||
/* DTrace function return, non-inlines */ | ||
if (VALUE_IS_FUNCTION(cx, lval)) { | ||
if (JAVASCRIPT_FUNCTION_RVAL_ENABLED()) | ||
jsdtrace_function_rval(cx, fp, fun); | ||
if (JAVASCRIPT_FUNCTION_RETURN_ENABLED()) | ||
jsdtrace_function_return(cx, fp, fun); | ||
} | ||
#endif | ||
regs.sp = vp + 1; | ||
CHECK_INTERRUPT_HANDLER(); | ||
if (!ok) | ||
goto error; | ||
JS_RUNTIME_METER(rt, nonInlineCalls); | ||
TRACE_0(NativeCallComplete); | ||
end_call: | ||
#if JS_HAS_LVALUE_RETURN | ||
if (cx->rval2set) { | ||
/* | ||
* Use the stack depth we didn't claim in our budget, but that | ||
* we know is there on account of [fun, this] already having | ||
* been pushed, at a minimum (if no args). Those two slots | ||
* have been popped and [rval] has been pushed, which leaves | ||
* one more slot for rval2 before we might overflow. | ||
* | ||
* NB: rval2 must be the property identifier, and rval the | ||
* object from which to get the property. The pair form an | ||
* ECMA "reference type", which can be used on the right- or | ||
* left-hand side of assignment ops. Note well: only native | ||
* methods can return reference types. See JSOP_SETCALL just | ||
* below for the left-hand-side case. | ||
*/ | ||
PUSH_OPND(cx->rval2); | ||
ELEMENT_OP(-1, OBJ_GET_PROPERTY(cx, obj, id, &rval)); | ||
regs.sp--; | ||
STORE_OPND(-1, rval); | ||
cx->rval2set = JS_FALSE; | ||
} | ||
#endif /* JS_HAS_LVALUE_RETURN */ | ||
END_CASE(JSOP_CALL) | ||
#if JS_HAS_LVALUE_RETURN | ||
BEGIN_CASE(JSOP_SETCALL) | ||
argc = GET_ARGC(regs.pc); | ||
vp = regs.sp - argc - 2; | ||
ok = js_Invoke(cx, argc, vp, 0); | ||
regs.sp = vp + 1; | ||
CHECK_INTERRUPT_HANDLER(); | ||
if (!ok) | ||
goto error; | ||
if (!cx->rval2set) { | ||
op2 = js_GetOpcode(cx, script, regs.pc + JSOP_SETCALL_LENGTH); | ||
if (op2 != JSOP_DELELEM) { | ||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, | ||
JSMSG_BAD_LEFTSIDE_OF_ASS); | ||
goto error; | ||
} | ||
/* | ||
* Store true as the result of the emulated delete of a | ||
* non-existent property. NB: We don't METER_OP_PAIR here; | ||
* it doesn't seem worth the code for this obscure case. | ||
*/ | ||
*vp = JSVAL_TRUE; | ||
regs.pc += JSOP_SETCALL_LENGTH + JSOP_DELELEM_LENGTH; | ||
op = (JSOp) *regs.pc; | ||
DO_OP(); | ||
} | ||
PUSH_OPND(cx->rval2); | ||
cx->rval2set = JS_FALSE; | ||
END_CASE(JSOP_SETCALL) | ||
#endif | ||
BEGIN_CASE(JSOP_NAME) | ||
BEGIN_CASE(JSOP_CALLNAME) | ||
{ | ||
JSPropCacheEntry *entry; | ||
obj = fp->scopeChain; | ||
if (JS_LIKELY(OBJ_IS_NATIVE(obj))) { | ||
PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom); | ||
if (!atom) { | ||
ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); | ||
if (PCVAL_IS_OBJECT(entry->vword)) { | ||
rval = PCVAL_OBJECT_TO_JSVAL(entry->vword); | ||
JS_UNLOCK_OBJ(cx, obj2); | ||
goto do_push_rval; | ||
} | ||
if (PCVAL_IS_SLOT(entry->vword)) { | ||
slot = PCVAL_TO_SLOT(entry->vword); | ||
JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); | ||
rval = LOCKED_OBJ_GET_SLOT(obj2, slot); | ||
JS_UNLOCK_OBJ(cx, obj2); | ||
goto do_push_rval; | ||
} | ||
JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); | ||
sprop = PCVAL_TO_SPROP(entry->vword); | ||
goto do_native_get; | ||
} | ||
} else { | ||
LOAD_ATOM(0); | ||
} | ||
id = ATOM_TO_JSID(atom); | ||
if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop)) | ||
goto error; | ||
if (!prop) { | ||
/* Kludge to allow (typeof foo == "undefined") tests. */ | ||
endpc = script->code + script->length; | ||
op2 = js_GetOpcode(cx, script, regs.pc + JSOP_NAME_LENGTH); | ||
if (op2 == JSOP_TYPEOF) { | ||
PUSH_OPND(JSVAL_VOID); | ||
len = JSOP_NAME_LENGTH; | ||
DO_NEXT_OP(len); | ||
} | ||
goto atom_not_defined; | ||
} | ||
/* Take the slow path if prop was not found in a native object. */ | ||
if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) { | ||
OBJ_DROP_PROPERTY(cx, obj2, prop); | ||
if (!OBJ_GET_PROPERTY(cx, obj, id, &rval)) | ||
goto error; | ||
} else { | ||
sprop = (JSScopeProperty *)prop; | ||
do_native_get: | ||
NATIVE_GET(cx, obj, obj2, sprop, &rval); | ||
OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *) sprop); | ||
} | ||
do_push_rval: | ||
PUSH_OPND(rval); | ||
if (op == JSOP_CALLNAME) | ||
PUSH_OPND(OBJECT_TO_JSVAL(obj)); | ||
} | ||
END_CASE(JSOP_NAME) | ||
BEGIN_CASE(JSOP_UINT16) | ||
i = (jsint) GET_UINT16(regs.pc); | ||
rval = INT_TO_JSVAL(i); | ||
PUSH_OPND(rval); | ||
END_CASE(JSOP_UINT16) | ||
BEGIN_CASE(JSOP_UINT24) | ||
i = (jsint) GET_UINT24(regs.pc); | ||
rval = INT_TO_JSVAL(i); | ||
PUSH_OPND(rval); | ||
END_CASE(JSOP_UINT24) | ||
BEGIN_CASE(JSOP_INT8) | ||
i = GET_INT8(regs.pc); | ||
rval = INT_TO_JSVAL(i); | ||
PUSH_OPND(rval); | ||
END_CASE(JSOP_INT8) | ||
BEGIN_CASE(JSOP_INT32) | ||
i = GET_INT32(regs.pc); | ||
rval = INT_TO_JSVAL(i); | ||
PUSH_OPND(rval); | ||
END_CASE(JSOP_INT32) | ||
BEGIN_CASE(JSOP_INDEXBASE) | ||
/* | ||
* Here atoms can exceed script->atomMap.length as we use atoms | ||
* as a segment register for object literals as well. | ||
*/ | ||
atoms += GET_INDEXBASE(regs.pc); | ||
END_CASE(JSOP_INDEXBASE) | ||
BEGIN_CASE(JSOP_INDEXBASE1) | ||
BEGIN_CASE(JSOP_INDEXBASE2) | ||
BEGIN_CASE(JSOP_INDEXBASE3) | ||
atoms += (op - JSOP_INDEXBASE1 + 1) << 16; | ||
END_CASE(JSOP_INDEXBASE3) | ||
BEGIN_CASE(JSOP_RESETBASE0) | ||
BEGIN_CASE(JSOP_RESETBASE) | ||
atoms = script->atomMap.vector; | ||
END_CASE(JSOP_RESETBASE) | ||
BEGIN_CASE(JSOP_DOUBLE) | ||
BEGIN_CASE(JSOP_STRING) | ||
LOAD_ATOM(0); | ||
PUSH_OPND(ATOM_KEY(atom)); | ||
END_CASE(JSOP_DOUBLE) | ||
BEGIN_CASE(JSOP_OBJECT) | ||
LOAD_OBJECT(0); | ||
PUSH_OPND(OBJECT_TO_JSVAL(obj)); | ||
END_CASE(JSOP_OBJECT) | ||
BEGIN_CASE(JSOP_REGEXP) | ||
{ | ||
JSObject *funobj; | ||
/* | ||
* Push a regexp object for the atom mapped by the bytecode at pc, | ||
* cloning the literal's regexp object if necessary, to simulate in | ||
* the pre-compile/execute-later case what ECMA specifies for the | ||
* compile-and-go case: that scanning each regexp literal creates | ||
* a single corresponding RegExp object. | ||
* | ||
* To support pre-compilation transparently, we must handle the | ||
* case where a regexp object literal is used in a different global | ||
* at execution time from the global with which it was scanned at | ||
* compile time. We do this by re-wrapping the JSRegExp private | ||
* data struct with a cloned object having the right prototype and | ||
* parent, and having its own lastIndex property value storage. | ||
* | ||
* Unlike JSOP_DEFFUN and other prolog bytecodes that may clone | ||
* literal objects, we don't want to pay a script prolog execution | ||
* price for all regexp literals in a script (many may not be used | ||
* by a particular execution of that script, depending on control | ||
* flow), so we initialize lazily here. | ||
* | ||
* XXX This code is specific to regular expression objects. If we | ||
* need a similar op for other kinds of object literals, we should | ||
* push cloning down under JSObjectOps and reuse code here. | ||
*/ | ||
index = GET_FULL_INDEX(0); | ||
JS_ASSERT(index < JS_SCRIPT_REGEXPS(script)->length); | ||
slot = index; | ||
if (fp->fun) { | ||
/* | ||
* We're in function code, not global or eval code (in eval | ||
* code, JSOP_REGEXP is never emitted). The cloned funobj | ||
* contains JS_SCRIPT_REGEXPS(script)->length reserved slots | ||
* for the cloned regexps; see fun_reserveSlots, jsfun.c. | ||
*/ | ||
funobj = fp->callee; | ||
slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass); | ||
if (script->upvarsOffset != 0) | ||
slot += JS_SCRIPT_UPVARS(script)->length; | ||
if (!JS_GetReservedSlot(cx, funobj, slot, &rval)) | ||
goto error; | ||
if (JSVAL_IS_VOID(rval)) | ||
rval = JSVAL_NULL; | ||
} else { | ||
/* | ||
* We're in global code. The code generator reserved a slot | ||
* for the regexp among script->nfixed slots. All such slots | ||
* are initialized to null, not void, for faster testing in | ||
* JSOP_*GVAR cases. To simplify index calculations we count | ||
* regexps in the reverse order down from script->nslots - 1. | ||
*/ | ||
JS_ASSERT(slot < script->nfixed); | ||
slot = script->nfixed - slot - 1; | ||
rval = fp->slots[slot]; | ||
#ifdef __GNUC__ | ||
funobj = NULL; /* suppress bogus gcc warnings */ | ||
#endif | ||
} | ||
if (JSVAL_IS_NULL(rval)) { | ||
/* Compute the current global object in obj2. */ | ||
obj2 = fp->scopeChain; | ||
while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL) | ||
obj2 = parent; | ||
/* | ||
* If obj's parent is not obj2, we must clone obj so that it | ||
* has the right parent, and therefore, the right prototype. | ||
* | ||
* Yes, this means we assume that the correct RegExp.prototype | ||
* to which regexp instances (including literals) delegate can | ||
* be distinguished solely by the instance's parent, which was | ||
* set to the parent of the RegExp constructor function object | ||
* when the instance was created. In other words, | ||
* | ||
* (/x/.__parent__ == RegExp.__parent__) implies | ||
* (/x/.__proto__ == RegExp.prototype) | ||
* | ||
* (unless you assign a different object to RegExp.prototype | ||
* at runtime, in which case, ECMA doesn't specify operation, | ||
* and you get what you deserve). | ||
* | ||
* This same coupling between instance parent and constructor | ||
* parent turns up everywhere (see jsobj.c's FindClassObject, | ||
* js_ConstructObject, and js_NewObject). It's fundamental to | ||
* the design of the language when you consider multiple global | ||
* objects and separate compilation and execution, even though | ||
* it is not specified fully in ECMA. | ||
*/ | ||
JS_GET_SCRIPT_REGEXP(script, index, obj); | ||
if (OBJ_GET_PARENT(cx, obj) != obj2) { | ||
obj = js_CloneRegExpObject(cx, obj, obj2); | ||
if (!obj) | ||
goto error; | ||
} | ||
rval = OBJECT_TO_JSVAL(obj); | ||
/* Store the regexp object value in its cloneIndex slot. */ | ||
if (fp->fun) { | ||
if (!JS_SetReservedSlot(cx, funobj, slot, rval)) | ||
goto error; | ||
} else { | ||
fp->slots[slot] = rval; | ||
} | ||
} | ||
PUSH_OPND(rval); | ||
} | ||
END_CASE(JSOP_REGEXP) | ||
BEGIN_CASE(JSOP_ZERO) | ||
PUSH_OPND(JSVAL_ZERO); | ||
END_CASE(JSOP_ZERO) | ||
BEGIN_CASE(JSOP_ONE) | ||
PUSH_OPND(JSVAL_ONE); | ||
END_CASE(JSOP_ONE) | ||
BEGIN_CASE(JSOP_NULL) | ||
PUSH_OPND(JSVAL_NULL); | ||
END_CASE(JSOP_NULL) | ||
BEGIN_CASE(JSOP_FALSE) | ||
PUSH_OPND(JSVAL_FALSE); | ||
END_CASE(JSOP_FALSE) | ||
BEGIN_CASE(JSOP_TRUE) | ||
PUSH_OPND(JSVAL_TRUE); | ||
END_CASE(JSOP_TRUE) | ||
BEGIN_CASE(JSOP_TABLESWITCH) | ||
pc2 = regs.pc; | ||
len = GET_JUMP_OFFSET(pc2); | ||
/* | ||
* ECMAv2+ forbids conversion of discriminant, so we will skip to | ||
* the default case if the discriminant isn't already an int jsval. | ||
* (This opcode is emitted only for dense jsint-domain switches.) | ||
*/ | ||
rval = POP_OPND(); | ||
if (JSVAL_IS_INT(rval)) { | ||
i = JSVAL_TO_INT(rval); | ||
} else if (JSVAL_IS_DOUBLE(rval) && *JSVAL_TO_DOUBLE(rval) == 0) { | ||
/* Treat -0 (double) as 0. */ | ||
i = 0; | ||
} else { | ||
DO_NEXT_OP(len); | ||
} | ||
pc2 += JUMP_OFFSET_LEN; | ||
low = GET_JUMP_OFFSET(pc2); | ||
pc2 += JUMP_OFFSET_LEN; | ||
high = GET_JUMP_OFFSET(pc2); | ||
i -= low; | ||
if ((jsuint)i < (jsuint)(high - low + 1)) { | ||
pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i; | ||
off = (jsint) GET_JUMP_OFFSET(pc2); | ||
if (off) | ||
len = off; | ||
} | ||
END_VARLEN_CASE | ||
BEGIN_CASE(JSOP_TABLESWITCHX) | ||
pc2 = regs.pc; | ||
len = GET_JUMPX_OFFSET(pc2); | ||
/* | ||
* ECMAv2+ forbids conversion of discriminant, so we will skip to | ||
* the default case if the discriminant isn't already an int jsval. | ||
* (This opcode is emitted only for dense jsint-domain switches.) | ||
*/ | ||
rval = POP_OPND(); | ||
if (JSVAL_IS_INT(rval)) { | ||
i = JSVAL_TO_INT(rval); | ||
} else if (JSVAL_IS_DOUBLE(rval) && *JSVAL_TO_DOUBLE(rval) == 0) { | ||
/* Treat -0 (double) as 0. */ | ||
i = 0; | ||
} else { | ||
DO_NEXT_OP(len); | ||
} | ||
pc2 += JUMPX_OFFSET_LEN; | ||
low = GET_JUMP_OFFSET(pc2); | ||
pc2 += JUMP_OFFSET_LEN; | ||
high = GET_JUMP_OFFSET(pc2); | ||
i -= low; | ||
if ((jsuint)i < (jsuint)(high - low + 1)) { | ||
pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i; | ||
off = (jsint) GET_JUMPX_OFFSET(pc2); | ||
if (off) | ||
len = off; | ||
} | ||
END_VARLEN_CASE | ||
BEGIN_CASE(JSOP_LOOKUPSWITCHX) | ||
off = JUMPX_OFFSET_LEN; | ||
goto do_lookup_switch; | ||
BEGIN_CASE(JSOP_LOOKUPSWITCH) | ||
off = JUMP_OFFSET_LEN; | ||
do_lookup_switch: | ||
/* | ||
* JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if | ||
* any atom index in it would exceed 64K limit. | ||
*/ | ||
JS_ASSERT(atoms == script->atomMap.vector); | ||
pc2 = regs.pc; | ||
lval = POP_OPND(); | ||
if (!JSVAL_IS_NUMBER(lval) && | ||
!JSVAL_IS_STRING(lval) && | ||
!JSVAL_IS_BOOLEAN(lval)) { | ||
goto end_lookup_switch; | ||
} | ||
pc2 += off; | ||
npairs = (jsint) GET_UINT16(pc2); | ||
pc2 += UINT16_LEN; | ||
JS_ASSERT(npairs); /* empty switch uses JSOP_TABLESWITCH */ | ||
#define SEARCH_PAIRS(MATCH_CODE) \ | ||
for (;;) { \ | ||
JS_ASSERT(GET_INDEX(pc2) < script->atomMap.length); \ | ||
atom = atoms[GET_INDEX(pc2)]; \ | ||
rval = ATOM_KEY(atom); \ | ||
MATCH_CODE \ | ||
pc2 += INDEX_LEN; \ | ||
if (match) \ | ||
break; \ | ||
pc2 += off; \ | ||
if (--npairs == 0) { \ | ||
pc2 = regs.pc; \ | ||
break; \ | ||
} \ | ||
} | ||
if (JSVAL_IS_STRING(lval)) { | ||
str = JSVAL_TO_STRING(lval); | ||
SEARCH_PAIRS( | ||
match = (JSVAL_IS_STRING(rval) && | ||
((str2 = JSVAL_TO_STRING(rval)) == str || | ||
js_EqualStrings(str2, str))); | ||
) | ||
} else if (JSVAL_IS_DOUBLE(lval)) { | ||
d = *JSVAL_TO_DOUBLE(lval); | ||
SEARCH_PAIRS( | ||
match = (JSVAL_IS_DOUBLE(rval) && | ||
*JSVAL_TO_DOUBLE(rval) == d); | ||
) | ||
} else { | ||
SEARCH_PAIRS( | ||
match = (lval == rval); | ||
) | ||
} | ||
#undef SEARCH_PAIRS | ||
end_lookup_switch: | ||
len = (op == JSOP_LOOKUPSWITCH) | ||
? GET_JUMP_OFFSET(pc2) | ||
: GET_JUMPX_OFFSET(pc2); | ||
END_VARLEN_CASE | ||
BEGIN_CASE(JSOP_TRAP) | ||
{ | ||
JSTrapStatus status; | ||
status = JS_HandleTrap(cx, script, regs.pc, &rval); | ||
switch (status) { | ||
case JSTRAP_ERROR: | ||
goto error; | ||
case JSTRAP_RETURN: | ||
fp->rval = rval; | ||
ok = JS_TRUE; | ||
goto forced_return; | ||
case JSTRAP_THROW: | ||
cx->throwing = JS_TRUE; | ||
cx->exception = rval; | ||
goto error; | ||
default:; | ||
break; | ||
} | ||
JS_ASSERT(status == JSTRAP_CONTINUE); | ||
CHECK_INTERRUPT_HANDLER(); | ||
JS_ASSERT(JSVAL_IS_INT(rval)); | ||
op = (JSOp) JSVAL_TO_INT(rval); | ||
JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); | ||
DO_OP(); | ||
} | ||
BEGIN_CASE(JSOP_ARGUMENTS) | ||
if (!js_GetArgsValue(cx, fp, &rval)) | ||
goto error; | ||
PUSH_OPND(rval); | ||
END_CASE(JSOP_ARGUMENTS) | ||
BEGIN_CASE(JSOP_ARGSUB) | ||
id = INT_TO_JSID(GET_ARGNO(regs.pc)); | ||
if (!js_GetArgsProperty(cx, fp, id, &rval)) | ||
goto error; | ||
PUSH_OPND(rval); | ||
END_CASE(JSOP_ARGSUB) | ||
BEGIN_CASE(JSOP_ARGCNT) | ||
id = ATOM_TO_JSID(rt->atomState.lengthAtom); | ||
if (!js_GetArgsProperty(cx, fp, id, &rval)) | ||
goto error; | ||
PUSH_OPND(rval); | ||
END_CASE(JSOP_ARGCNT) | ||
BEGIN_CASE(JSOP_GETARG) | ||
BEGIN_CASE(JSOP_CALLARG) | ||
slot = GET_ARGNO(regs.pc); | ||
JS_ASSERT(slot < fp->fun->nargs); | ||
METER_SLOT_OP(op, slot); | ||
PUSH_OPND(fp->argv[slot]); | ||
if (op == JSOP_CALLARG) | ||
PUSH_OPND(JSVAL_NULL); | ||
END_CASE(JSOP_GETARG) | ||
BEGIN_CASE(JSOP_SETARG) | ||
slot = GET_ARGNO(regs.pc); | ||
JS_ASSERT(slot < fp->fun->nargs); | ||
METER_SLOT_OP(op, slot); | ||
vp = &fp->argv[slot]; | ||
*vp = FETCH_OPND(-1); | ||
END_SET_CASE(JSOP_SETARG) | ||
BEGIN_CASE(JSOP_GETLOCAL) | ||
slot = GET_SLOTNO(regs.pc); | ||
JS_ASSERT(slot < script->nslots); | ||
PUSH_OPND(fp->slots[slot]); | ||
END_CASE(JSOP_GETLOCAL) | ||
BEGIN_CASE(JSOP_CALLLOCAL) | ||
slot = GET_SLOTNO(regs.pc); | ||
JS_ASSERT(slot < script->nslots); | ||
PUSH_OPND(fp->slots[slot]); | ||
PUSH_OPND(JSVAL_NULL); | ||
END_CASE(JSOP_CALLLOCAL) | ||
BEGIN_CASE(JSOP_SETLOCAL) | ||
slot = GET_SLOTNO(regs.pc); | ||
JS_ASSERT(slot < script->nslots); | ||
vp = &fp->slots[slot]; | ||
*vp = FETCH_OPND(-1); | ||
END_SET_CASE(JSOP_SETLOCAL) | ||
BEGIN_CASE(JSOP_GETUPVAR) | ||
BEGIN_CASE(JSOP_CALLUPVAR) | ||
{ | ||
JSUpvarArray *uva = JS_SCRIPT_UPVARS(script); | ||
index = GET_UINT16(regs.pc); | ||
JS_ASSERT(index < uva->length); | ||
rval = js_GetUpvar(cx, script->staticLevel, uva->vector[index]); | ||
PUSH_OPND(rval); | ||
if (op == JSOP_CALLUPVAR) | ||
PUSH_OPND(JSVAL_NULL); | ||
} | ||
END_CASE(JSOP_GETUPVAR) | ||
BEGIN_CASE(JSOP_GETUPVAR_DBG) | ||
BEGIN_CASE(JSOP_CALLUPVAR_DBG) | ||
fun = fp->fun; | ||
JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED); | ||
JS_ASSERT(fun->u.i.wrapper); | ||
/* Scope for tempPool mark and local names allocation in it. */ | ||
{ | ||
void *mark = JS_ARENA_MARK(&cx->tempPool); | ||
jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool); | ||
if (!names) | ||
goto error; | ||
index = fun->countArgsAndVars() + GET_UINT16(regs.pc); | ||
atom = JS_LOCAL_NAME_TO_ATOM(names[index]); | ||
id = ATOM_TO_JSID(atom); | ||
ok = js_FindProperty(cx, id, &obj, &obj2, &prop); | ||
JS_ARENA_RELEASE(&cx->tempPool, mark); | ||
if (!ok) | ||
goto error; | ||
} | ||
if (!prop) | ||
goto atom_not_defined; | ||
/* Minimize footprint with generic code instead of NATIVE_GET. */ | ||
OBJ_DROP_PROPERTY(cx, obj2, prop); | ||
vp = regs.sp; | ||
PUSH_OPND(JSVAL_NULL); | ||
if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) | ||
goto error; | ||
if (op == JSOP_CALLUPVAR_DBG) | ||
PUSH_OPND(JSVAL_NULL); | ||
END_CASE(JSOP_GETUPVAR_DBG) | ||
BEGIN_CASE(JSOP_GETDSLOT) | ||
BEGIN_CASE(JSOP_CALLDSLOT) | ||
obj = fp->callee; | ||
JS_ASSERT(obj); | ||
JS_ASSERT(obj->dslots); | ||
index = GET_UINT16(regs.pc); | ||
JS_ASSERT(JS_INITIAL_NSLOTS + index < jsatomid(obj->dslots[-1])); | ||
JS_ASSERT_IF(OBJ_SCOPE(obj)->object == obj, | ||
JS_INITIAL_NSLOTS + index < OBJ_SCOPE(obj)->freeslot); | ||
PUSH_OPND(obj->dslots[index]); | ||
if (op == JSOP_CALLDSLOT) | ||
PUSH_OPND(JSVAL_NULL); | ||
END_CASE(JSOP_GETDSLOT) | ||
BEGIN_CASE(JSOP_GETGVAR) | ||
BEGIN_CASE(JSOP_CALLGVAR) | ||
slot = GET_SLOTNO(regs.pc); | ||
JS_ASSERT(slot < GlobalVarCount(fp)); | ||
METER_SLOT_OP(op, slot); | ||
lval = fp->slots[slot]; | ||
if (JSVAL_IS_NULL(lval)) { | ||
op = (op == JSOP_GETGVAR) ? JSOP_NAME : JSOP_CALLNAME; | ||
DO_OP(); | ||
} | ||
obj = fp->varobj; | ||
slot = JSVAL_TO_INT(lval); | ||
rval = OBJ_GET_SLOT(cx, obj, slot); | ||
PUSH_OPND(rval); | ||
if (op == JSOP_CALLGVAR) | ||
PUSH_OPND(OBJECT_TO_JSVAL(obj)); | ||
END_CASE(JSOP_GETGVAR) | ||
BEGIN_CASE(JSOP_SETGVAR) | ||
slot = GET_SLOTNO(regs.pc); | ||
JS_ASSERT(slot < GlobalVarCount(fp)); | ||
METER_SLOT_OP(op, slot); | ||
rval = FETCH_OPND(-1); | ||
obj = fp->varobj; | ||
lval = fp->slots[slot]; | ||
if (JSVAL_IS_NULL(lval)) { | ||
/* | ||
* Inline-clone and deoptimize JSOP_SETNAME code here because | ||
* JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval] | ||
* as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME. | ||
*/ | ||
#ifdef JS_TRACER | ||
if (TRACE_RECORDER(cx)) | ||
js_AbortRecording(cx, "SETGVAR with NULL slot"); | ||
#endif | ||
LOAD_ATOM(0); | ||
id = ATOM_TO_JSID(atom); | ||
if (!OBJ_SET_PROPERTY(cx, obj, id, &rval)) | ||
goto error; | ||
} else { | ||
slot = JSVAL_TO_INT(lval); | ||
JS_LOCK_OBJ(cx, obj); | ||
LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, rval); | ||
JS_UNLOCK_OBJ(cx, obj); | ||
} | ||
END_SET_CASE(JSOP_SETGVAR) | ||
BEGIN_CASE(JSOP_DEFCONST) | ||
BEGIN_CASE(JSOP_DEFVAR) | ||
index = GET_INDEX(regs.pc); | ||
atom = atoms[index]; | ||
/* | ||
* index is relative to atoms at this point but for global var | ||
* code below we need the absolute value. | ||
*/ | ||
index += atoms - script->atomMap.vector; | ||
obj = fp->varobj; | ||
attrs = JSPROP_ENUMERATE; | ||
if (!(fp->flags & JSFRAME_EVAL)) | ||
attrs |= JSPROP_PERMANENT; | ||
if (op == JSOP_DEFCONST) | ||
attrs |= JSPROP_READONLY; | ||
/* Lookup id in order to check for redeclaration problems. */ | ||
id = ATOM_TO_JSID(atom); | ||
prop = NULL; | ||
if (!js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop)) | ||
goto error; | ||
/* Bind a variable only if it's not yet defined. */ | ||
if (!prop) { | ||
if (!OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, | ||
JS_PropertyStub, JS_PropertyStub, | ||
attrs, &prop)) { | ||
goto error; | ||
} | ||
JS_ASSERT(prop); | ||
obj2 = obj; | ||
} | ||
/* | ||
* Try to optimize a property we either just created, or found | ||
* directly in the global object, that is permanent, has a slot, | ||
* and has stub getter and setter, into a "fast global" accessed | ||
* by the JSOP_*GVAR opcodes. | ||
*/ | ||
if (!fp->fun && | ||
index < GlobalVarCount(fp) && | ||
obj2 == obj && | ||
OBJ_IS_NATIVE(obj)) { | ||
sprop = (JSScopeProperty *) prop; | ||
if ((sprop->attrs & JSPROP_PERMANENT) && | ||
SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) && | ||
SPROP_HAS_STUB_GETTER(sprop) && | ||
SPROP_HAS_STUB_SETTER(sprop)) { | ||
/* | ||
* Fast globals use frame variables to map the global | ||
* name's atom index to the permanent fp->varobj slot | ||
* number, tagged as a jsval. The atom index for the | ||
* global's name literal is identical to its variable | ||
* index. | ||
*/ | ||
fp->slots[index] = INT_TO_JSVAL(sprop->slot); | ||
} | ||
} | ||
OBJ_DROP_PROPERTY(cx, obj2, prop); | ||
END_CASE(JSOP_DEFVAR) | ||
BEGIN_CASE(JSOP_DEFFUN) | ||
{ | ||
JSPropertyOp getter, setter; | ||
bool doSet; | ||
JSObject *pobj; | ||
JSProperty *prop; | ||
uint32 old; | ||
/* | ||
* A top-level function defined in Global or Eval code (see | ||
* ECMA-262 Ed. 3), or else a SpiderMonkey extension: a named | ||
* function statement in a compound statement (not at the top | ||
* statement level of global code, or at the top level of a | ||
* function body). | ||
*/ | ||
LOAD_FUNCTION(0); | ||
obj = FUN_OBJECT(fun); | ||
if (FUN_NULL_CLOSURE(fun)) { | ||
/* | ||
* Even a null closure needs a parent for principals finding. | ||
* FIXME: bug 476950, although debugger users may also demand | ||
* some kind of scope link for debugger-assisted eval-in-frame. | ||
*/ | ||
obj2 = fp->scopeChain; | ||
} else { | ||
JS_ASSERT(!FUN_FLAT_CLOSURE(fun)); | ||
/* | ||
* Inline js_GetScopeChain a bit to optimize for the case of a | ||
* top-level function. | ||
*/ | ||
if (!fp->blockChain) { | ||
obj2 = fp->scopeChain; | ||
} else { | ||
obj2 = js_GetScopeChain(cx, fp); | ||
if (!obj2) | ||
goto error; | ||
} | ||
} | ||
/* | ||
* If static link is not current scope, clone fun's object to link | ||
* to the current scope via parent. We do this to enable sharing of | ||
* compiled functions among multiple equivalent scopes, amortizing | ||
* the cost of compilation over a number of executions. Examples | ||
* include XUL scripts and event handlers shared among Firefox or | ||
* other Mozilla app chrome windows, and user-defined JS functions | ||
* precompiled and then shared among requests in server-side JS. | ||
*/ | ||
if (OBJ_GET_PARENT(cx, obj) != obj2) { | ||
obj = js_CloneFunctionObject(cx, fun, obj2); | ||
if (!obj) | ||
goto error; | ||
} | ||
/* | ||
* Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All | ||
* paths from here must flow through the "Restore fp->scopeChain" | ||
* code below the OBJ_DEFINE_PROPERTY call. | ||
*/ | ||
MUST_FLOW_THROUGH("restore_scope"); | ||
fp->scopeChain = obj; | ||
rval = OBJECT_TO_JSVAL(obj); | ||
/* | ||
* ECMA requires functions defined when entering Eval code to be | ||
* impermanent. | ||
*/ | ||
attrs = (fp->flags & JSFRAME_EVAL) | ||
? JSPROP_ENUMERATE | ||
: JSPROP_ENUMERATE | JSPROP_PERMANENT; | ||
/* | ||
* Load function flags that are also property attributes. Getters | ||
* and setters do not need a slot, their value is stored elsewhere | ||
* in the property itself, not in obj slots. | ||
*/ | ||
setter = getter = JS_PropertyStub; | ||
flags = JSFUN_GSFLAG2ATTR(fun->flags); | ||
if (flags) { | ||
/* Function cannot be both getter a setter. */ | ||
JS_ASSERT(flags == JSPROP_GETTER || flags == JSPROP_SETTER); | ||
attrs |= flags | JSPROP_SHARED; | ||
rval = JSVAL_VOID; | ||
if (flags == JSPROP_GETTER) | ||
getter = js_CastAsPropertyOp(obj); | ||
else | ||
setter = js_CastAsPropertyOp(obj); | ||
} | ||
/* | ||
* We define the function as a property of the variable object and | ||
* not the current scope chain even for the case of function | ||
* expression statements and functions defined by eval inside let | ||
* or with blocks. | ||
*/ | ||
parent = fp->varobj; | ||
JS_ASSERT(parent); | ||
/* | ||
* Check for a const property of the same name -- or any kind | ||
* of property if executing with the strict option. We check | ||
* here at runtime as well as at compile-time, to handle eval | ||
* as well as multiple HTML script tags. | ||
*/ | ||
id = ATOM_TO_JSID(fun->atom); | ||
prop = NULL; | ||
ok = js_CheckRedeclaration(cx, parent, id, attrs, &pobj, &prop); | ||
if (!ok) | ||
goto restore_scope; | ||
/* | ||
* We deviate from 10.1.2 in ECMA 262 v3 and under eval use for | ||
* function declarations OBJ_SET_PROPERTY, not OBJ_DEFINE_PROPERTY, | ||
* to preserve the JSOP_PERMANENT attribute of existing properties | ||
* and make sure that such properties cannot be deleted. | ||
* | ||
* We also use OBJ_SET_PROPERTY for the existing properties of | ||
* Call objects with matching attributes to preserve the native | ||
* getters and setters that store the value of the property in the | ||
* interpreter frame, see bug 467495. | ||
*/ | ||
doSet = (attrs == JSPROP_ENUMERATE); | ||
JS_ASSERT_IF(doSet, fp->flags & JSFRAME_EVAL); | ||
if (prop) { | ||
if (parent == pobj && | ||
OBJ_GET_CLASS(cx, parent) == &js_CallClass && | ||
(old = ((JSScopeProperty *) prop)->attrs, | ||
!(old & (JSPROP_GETTER|JSPROP_SETTER)) && | ||
(old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs)) { | ||
/* | ||
* js_CheckRedeclaration must reject attempts to add a | ||
* getter or setter to an existing property without a | ||
* getter or setter. | ||
*/ | ||
JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT))); | ||
JS_ASSERT(!(old & JSPROP_READONLY)); | ||
doSet = JS_TRUE; | ||
} | ||
OBJ_DROP_PROPERTY(cx, pobj, prop); | ||
} | ||
ok = doSet | ||
? OBJ_SET_PROPERTY(cx, parent, id, &rval) | ||
: OBJ_DEFINE_PROPERTY(cx, parent, id, rval, getter, setter, | ||
attrs, NULL); | ||
restore_scope: | ||
/* Restore fp->scopeChain now that obj is defined in fp->varobj. */ | ||
fp->scopeChain = obj2; | ||
if (!ok) { | ||
cx->weakRoots.newborn[GCX_OBJECT] = NULL; | ||
goto error; | ||
} | ||
} | ||
END_CASE(JSOP_DEFFUN) | ||
BEGIN_CASE(JSOP_DEFFUN_FC) | ||
BEGIN_CASE(JSOP_DEFFUN_DBGFC) | ||
LOAD_FUNCTION(0); | ||
obj = (op == JSOP_DEFFUN_FC) | ||
? js_NewFlatClosure(cx, fun) | ||
: js_NewDebuggableFlatClosure(cx, fun); | ||
if (!obj) | ||
goto error; | ||
rval = OBJECT_TO_JSVAL(obj); | ||
attrs = (fp->flags & JSFRAME_EVAL) | ||
? JSPROP_ENUMERATE | ||
: JSPROP_ENUMERATE | JSPROP_PERMANENT; | ||
flags = JSFUN_GSFLAG2ATTR(fun->flags); | ||
if (flags) { | ||
attrs |= flags | JSPROP_SHARED; | ||
rval = JSVAL_VOID; | ||
} | ||
parent = fp->varobj; | ||
JS_ASSERT(parent); | ||
id = ATOM_TO_JSID(fun->atom); | ||
ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL); | ||
if (ok) { | ||
if (attrs == JSPROP_ENUMERATE) { | ||
JS_ASSERT(fp->flags & JSFRAME_EVAL); | ||
ok = OBJ_SET_PROPERTY(cx, parent, id, &rval); | ||
} else { | ||
JS_ASSERT(attrs & JSPROP_PERMANENT); | ||
ok = OBJ_DEFINE_PROPERTY(cx, parent, id, rval, | ||
(flags & JSPROP_GETTER) | ||
? JS_EXTENSION (JSPropertyOp) obj | ||
: JS_PropertyStub, | ||
(flags & JSPROP_SETTER) | ||
? JS_EXTENSION (JSPropertyOp) obj | ||
: JS_PropertyStub, | ||
attrs, | ||
NULL); | ||
} | ||
} | ||
if (!ok) { | ||
cx->weakRoots.newborn[GCX_OBJECT] = NULL; | ||
goto error; | ||
} | ||
END_CASE(JSOP_DEFFUN_FC) | ||
BEGIN_CASE(JSOP_DEFLOCALFUN) | ||
/* | ||
* Define a local function (i.e., one nested at the top level of | ||
* another function), parented by the current scope chain, stored | ||
* in a local variable slot that the compiler allocated. This is | ||
* an optimization over JSOP_DEFFUN that avoids requiring a call | ||
* object for the outer function's activation. | ||
*/ | ||
LOAD_FUNCTION(SLOTNO_LEN); | ||
JS_ASSERT(FUN_INTERPRETED(fun)); | ||
JS_ASSERT(!FUN_FLAT_CLOSURE(fun)); | ||
obj = FUN_OBJECT(fun); | ||
if (FUN_NULL_CLOSURE(fun)) { | ||
obj = js_CloneFunctionObject(cx, fun, fp->scopeChain); | ||
if (!obj) | ||
goto error; | ||
} else { | ||
parent = js_GetScopeChain(cx, fp); | ||
if (!parent) | ||
goto error; | ||
if (OBJ_GET_PARENT(cx, obj) != parent) { | ||
#ifdef JS_TRACER | ||
if (TRACE_RECORDER(cx)) | ||
js_AbortRecording(cx, "DEFLOCALFUN for closure"); | ||
3029 | #endif | #endif |
obj = js_CloneFunctionObject(cx, fun, parent); | ||
if (!obj) | ||
goto error; | ||
} | ||
} | ||
slot = GET_SLOTNO(regs.pc); | ||
TRACE_2(DefLocalFunSetSlot, slot, obj); | ||
fp->slots[slot] = OBJECT_TO_JSVAL(obj); | ||
END_CASE(JSOP_DEFLOCALFUN) | ||
BEGIN_CASE(JSOP_DEFLOCALFUN_FC) | ||
LOAD_FUNCTION(SLOTNO_LEN); | ||
obj = js_NewFlatClosure(cx, fun); | ||
if (!obj) | ||
goto error; | ||
slot = GET_SLOTNO(regs.pc); | ||
TRACE_2(DefLocalFunSetSlot, slot, obj); | ||
fp->slots[slot] = OBJECT_TO_JSVAL(obj); | ||
END_CASE(JSOP_DEFLOCALFUN_FC) | ||
BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC) | ||
LOAD_FUNCTION(SLOTNO_LEN); | ||
obj = js_NewDebuggableFlatClosure(cx, fun); | ||
if (!obj) | ||
goto error; | ||
slot = GET_SLOTNO(regs.pc); | ||
fp->slots[slot] = OBJECT_TO_JSVAL(obj); | ||
END_CASE(JSOP_DEFLOCALFUN_DBGFC) | ||
BEGIN_CASE(JSOP_LAMBDA) | ||
/* Load the specified function object literal. */ | ||
LOAD_FUNCTION(0); | ||
obj = FUN_OBJECT(fun); | ||
if (FUN_NULL_CLOSURE(fun)) { | ||
obj = js_CloneFunctionObject(cx, fun, fp->scopeChain); | ||
if (!obj) | ||
goto error; | ||
} else { | ||
parent = js_GetScopeChain(cx, fp); | ||
if (!parent) | ||
goto error; | ||
/* If re-parenting, push a clone of the function object. */ | ||
if (OBJ_GET_PARENT(cx, obj) != parent) { | ||
obj = js_CloneFunctionObject(cx, fun, parent); | ||
if (!obj) | ||
goto error; | ||
} | ||
} | ||
PUSH_OPND(OBJECT_TO_JSVAL(obj)); | ||
END_CASE(JSOP_LAMBDA) | ||
BEGIN_CASE(JSOP_LAMBDA_FC) | ||
LOAD_FUNCTION(0); | ||
obj = js_NewFlatClosure(cx, fun); | ||
if (!obj) | ||
goto error; | ||
PUSH_OPND(OBJECT_TO_JSVAL(obj)); | ||
END_CASE(JSOP_LAMBDA_FC) | ||
BEGIN_CASE(JSOP_LAMBDA_DBGFC) | ||
LOAD_FUNCTION(0); | ||
obj = js_NewDebuggableFlatClosure(cx, fun); | ||
if (!obj) | ||
goto error; | ||
PUSH_OPND(OBJECT_TO_JSVAL(obj)); | ||
END_CASE(JSOP_LAMBDA_DBGFC) | ||
BEGIN_CASE(JSOP_CALLEE) | ||
PUSH_OPND(OBJECT_TO_JSVAL(fp->callee)); | ||
END_CASE(JSOP_CALLEE) | ||
#if JS_HAS_GETTER_SETTER | ||
BEGIN_CASE(JSOP_GETTER) | ||
BEGIN_CASE(JSOP_SETTER) | ||
do_getter_setter: | ||
op2 = (JSOp) *++regs.pc; | ||
switch (op2) { | ||
case JSOP_INDEXBASE: | ||
atoms += GET_INDEXBASE(regs.pc); | ||
regs.pc += JSOP_INDEXBASE_LENGTH - 1; | ||
goto do_getter_setter; | ||
case JSOP_INDEXBASE1: | ||
case JSOP_INDEXBASE2: | ||
case JSOP_INDEXBASE3: | ||
atoms += (op2 - JSOP_INDEXBASE1 + 1) << 16; | ||
goto do_getter_setter; | ||
case JSOP_SETNAME: | ||
case JSOP_SETPROP: | ||
LOAD_ATOM(0); | ||
id = ATOM_TO_JSID(atom); | ||
rval = FETCH_OPND(-1); | ||
i = -1; | ||
goto gs_pop_lval; | ||
case JSOP_SETELEM: | ||
rval = FETCH_OPND(-1); | ||
id = 0; | ||
i = -2; | ||
gs_pop_lval: | ||
FETCH_OBJECT(cx, i - 1, lval, obj); | ||
break; | ||
case JSOP_INITPROP: | ||
JS_ASSERT(regs.sp - StackBase(fp) >= 2); | ||
rval = FETCH_OPND(-1); | ||
i = -1; | ||
LOAD_ATOM(0); | ||
id = ATOM_TO_JSID(atom); | ||
goto gs_get_lval; | ||
default: | ||
JS_ASSERT(op2 == JSOP_INITELEM); | ||
JS_ASSERT(regs.sp - StackBase(fp) >= 3); | ||
rval = FETCH_OPND(-1); | ||
id = 0; | ||
i = -2; | ||
gs_get_lval: | ||
lval = FETCH_OPND(i-1); | ||
JS_ASSERT(JSVAL_IS_OBJECT(lval)); | ||
obj = JSVAL_TO_OBJECT(lval); | ||
break; | ||
} | ||
/* Ensure that id has a type suitable for use with obj. */ | ||
if (id == 0) | ||
FETCH_ELEMENT_ID(obj, i, id); | ||
if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) { | ||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, | ||
JSMSG_BAD_GETTER_OR_SETTER, | ||
(op == JSOP_GETTER) | ||
? js_getter_str | ||
: js_setter_str); | ||
goto error; | ||
} | ||
/* | ||
* Getters and setters are just like watchpoints from an access | ||
* control point of view. | ||
*/ | ||
if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs)) | ||
goto error; | ||
if (op == JSOP_GETTER) { | ||
getter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval); | ||
setter = JS_PropertyStub; | ||
attrs = JSPROP_GETTER; | ||
} else { | ||
getter = JS_PropertyStub; | ||
setter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval); | ||
attrs = JSPROP_SETTER; | ||
} | ||
attrs |= JSPROP_ENUMERATE | JSPROP_SHARED; | ||
/* Check for a readonly or permanent property of the same name. */ | ||
if (!js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL)) | ||
goto error; | ||
if (!OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter, | ||
attrs, NULL)) { | ||
goto error; | ||
} | ||
regs.sp += i; | ||
if (js_CodeSpec[op2].ndefs) | ||
STORE_OPND(-1, rval); | ||
len = js_CodeSpec[op2].length; | ||
DO_NEXT_OP(len); | ||
#endif /* JS_HAS_GETTER_SETTER */ | ||
BEGIN_CASE(JSOP_HOLE) | ||
PUSH_OPND(JSVAL_HOLE); | ||
END_CASE(JSOP_HOLE) | ||
BEGIN_CASE(JSOP_NEWARRAY) | ||
len = GET_UINT16(regs.pc); | ||
cx->fp->assertValidStackDepth(len); | ||
obj = js_NewArrayObject(cx, len, regs.sp - len, JS_TRUE); | ||
if (!obj) | ||
goto error; | ||
regs.sp -= len - 1; | ||
STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); | ||
END_CASE(JSOP_NEWARRAY) | ||
BEGIN_CASE(JSOP_NEWINIT) | ||
i = GET_INT8(regs.pc); | ||
JS_ASSERT(i == JSProto_Array || i == JSProto_Object); | ||
obj = (i == JSProto_Array) | ||
? js_NewArrayObject(cx, 0, NULL) | ||
: js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0); | ||
if (!obj) | ||
goto error; | ||
PUSH_OPND(OBJECT_TO_JSVAL(obj)); | ||
fp->sharpDepth++; | ||
CHECK_INTERRUPT_HANDLER(); | ||
END_CASE(JSOP_NEWINIT) | ||
BEGIN_CASE(JSOP_ENDINIT) | ||
if (--fp->sharpDepth == 0) | ||
fp->sharpArray = NULL; | ||
/* Re-set the newborn root to the top of this object tree. */ | ||
JS_ASSERT(regs.sp - StackBase(fp) >= 1); | ||
lval = FETCH_OPND(-1); | ||
JS_ASSERT(JSVAL_IS_OBJECT(lval)); | ||
cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval); | ||
END_CASE(JSOP_ENDINIT) | ||
BEGIN_CASE(JSOP_INITPROP) | ||
/* Load the property's initial value into rval. */ | ||
JS_ASSERT(regs.sp - StackBase(fp) >= 2); | ||
rval = FETCH_OPND(-1); | ||
/* Load the object being initialized into lval/obj. */ | ||
lval = FETCH_OPND(-2); | ||
obj = JSVAL_TO_OBJECT(lval); | ||
JS_ASSERT(OBJ_IS_NATIVE(obj)); | ||
JS_ASSERT(!OBJ_GET_CLASS(cx, obj)->reserveSlots); | ||
JS_ASSERT(!(LOCKED_OBJ_GET_CLASS(obj)->flags & | ||
JSCLASS_SHARE_ALL_PROPERTIES)); | ||
do { | ||
JSScope *scope; | ||
uint32 kshape; | ||
JSPropertyCache *cache; | ||
JSPropCacheEntry *entry; | ||
JS_LOCK_OBJ(cx, obj); | ||
scope = OBJ_SCOPE(obj); | ||
JS_ASSERT(!SCOPE_IS_SEALED(scope)); | ||
kshape = scope->shape; | ||
cache = &JS_PROPERTY_CACHE(cx); | ||
entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)]; | ||
PCMETER(cache->pctestentry = entry); | ||
PCMETER(cache->tests++); | ||
PCMETER(cache->initests++); | ||
if (entry->kpc == regs.pc && | ||
entry->kshape == kshape && | ||
PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape) { | ||
JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0); | ||
PCMETER(cache->pchits++); | ||
PCMETER(cache->inipchits++); | ||
JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); | ||
sprop = PCVAL_TO_SPROP(entry->vword); | ||
JS_ASSERT(!(sprop->attrs & JSPROP_READONLY)); | ||
/* | ||
* If this property has a non-stub setter, it must be | ||
* __proto__, __parent__, or another "shared prototype" | ||
* built-in. Force a miss to save code size here and let | ||
* the standard code path take care of business. | ||
*/ | ||
if (!SPROP_HAS_STUB_SETTER(sprop)) | ||
goto do_initprop_miss; | ||
if (scope->object != obj) { | ||
scope = js_GetMutableScope(cx, obj); | ||
if (!scope) { | ||
JS_UNLOCK_OBJ(cx, obj); | ||
goto error; | ||
} | ||
} | ||
/* | ||
* Detect a repeated property name and force a miss to | ||
* share the strict warning code and cope with complexity | ||
* managed by js_AddScopeProperty. | ||
*/ | ||
if (sprop->parent != scope->lastProp) | ||
goto do_initprop_miss; | ||
/* | ||
* Otherwise this entry must be for a direct property of | ||
* obj, not a proto-property, and there cannot have been | ||
* any deletions of prior properties. | ||
*/ | ||
JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope)); | ||
JS_ASSERT(!scope->table || | ||
!SCOPE_HAS_PROPERTY(scope, sprop)); | ||
slot = sprop->slot; | ||
JS_ASSERT(slot == scope->freeslot); | ||
if (slot < STOBJ_NSLOTS(obj)) { | ||
++scope->freeslot; | ||
} else { | ||
if (!js_AllocSlot(cx, obj, &slot)) { | ||
JS_UNLOCK_SCOPE(cx, scope); | ||
goto error; | ||
} | ||
JS_ASSERT(slot == sprop->slot); | ||
} | ||
JS_ASSERT(!scope->lastProp || | ||
scope->shape == scope->lastProp->shape); | ||
if (scope->table) { | ||
JSScopeProperty *sprop2 = | ||
js_AddScopeProperty(cx, scope, sprop->id, | ||
sprop->getter, sprop->setter, | ||
slot, sprop->attrs, | ||
sprop->flags, sprop->shortid); | ||
if (!sprop2) { | ||
js_FreeSlot(cx, obj, slot); | ||
JS_UNLOCK_SCOPE(cx, scope); | ||
goto error; | ||
} | ||
JS_ASSERT(sprop2 == sprop); | ||
} else { | ||
js_LeaveTraceIfGlobalObject(cx, scope->object); | ||
scope->shape = sprop->shape; | ||
++scope->entryCount; | ||
scope->lastProp = sprop; | ||
} | ||
GC_WRITE_BARRIER(cx, scope, | ||
LOCKED_OBJ_GET_SLOT(obj, slot), | ||
rval); | ||
TRACE_2(SetPropHit, entry, sprop); | ||
LOCKED_OBJ_SET_SLOT(obj, slot, rval); | ||
JS_UNLOCK_SCOPE(cx, scope); | ||
break; | ||
} | ||
do_initprop_miss: | ||
PCMETER(cache->inipcmisses++); | ||
JS_UNLOCK_SCOPE(cx, scope); | ||
/* Get the immediate property name into id. */ | ||
LOAD_ATOM(0); | ||
id = ATOM_TO_JSID(atom); | ||
/* Set the property named by obj[id] to rval. */ | ||
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, | ||
NULL, NULL)) { | ||
goto error; | ||
} | ||
if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom) | ||
? js_SetPropertyHelper(cx, obj, id, true, &rval) | ||
: js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL, | ||
JSPROP_ENUMERATE, 0, 0, NULL, | ||
JSDNP_CACHE_RESULT))) | ||
goto error; | ||
} while (0); | ||
/* Common tail for property cache hit and miss cases. */ | ||
regs.sp--; | ||
END_CASE(JSOP_INITPROP); | ||
BEGIN_CASE(JSOP_INITELEM) | ||
/* Pop the element's value into rval. */ | ||
JS_ASSERT(regs.sp - StackBase(fp) >= 3); | ||
rval = FETCH_OPND(-1); | ||
/* Find the object being initialized at top of stack. */ | ||
lval = FETCH_OPND(-3); | ||
JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval)); | ||
obj = JSVAL_TO_OBJECT(lval); | ||
3030 | ||
3031 | /* Fetch id now that we have obj. */ | /********************** Here we include the operations ***********************/ |
3032 | FETCH_ELEMENT_ID(obj, -2, id); | #include "jsops.cpp" |
3033 | /*****************************************************************************/ | |
/* | ||
* Check for property redeclaration strict warning (we may be in | ||
* an object initialiser, not an array initialiser). | ||
*/ | ||
if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, NULL, NULL)) | ||
goto error; | ||
/* | ||
* If rval is a hole, do not call OBJ_DEFINE_PROPERTY. In this case, | ||
* obj must be an array, so if the current op is the last element | ||
* initialiser, set the array length to one greater than id. | ||
*/ | ||
if (rval == JSVAL_HOLE) { | ||
JS_ASSERT(OBJ_IS_ARRAY(cx, obj)); | ||
JS_ASSERT(JSID_IS_INT(id)); | ||
JS_ASSERT((jsuint) JSID_TO_INT(id) < ARRAY_INIT_LIMIT); | ||
if (js_GetOpcode(cx, script, regs.pc + JSOP_INITELEM_LENGTH) == JSOP_ENDINIT && | ||
!js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) { | ||
goto error; | ||
} | ||
} else { | ||
if (!OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL, JSPROP_ENUMERATE, NULL)) | ||
goto error; | ||
} | ||
regs.sp -= 2; | ||
END_CASE(JSOP_INITELEM) | ||
#if JS_HAS_SHARP_VARS | ||
BEGIN_CASE(JSOP_DEFSHARP) | ||
obj = fp->sharpArray; | ||
if (!obj) { | ||
obj = js_NewArrayObject(cx, 0, NULL); | ||
if (!obj) | ||
goto error; | ||
fp->sharpArray = obj; | ||
} | ||
i = (jsint) GET_UINT16(regs.pc); | ||
id = INT_TO_JSID(i); | ||
rval = FETCH_OPND(-1); | ||
if (JSVAL_IS_PRIMITIVE(rval)) { | ||
char numBuf[12]; | ||
JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); | ||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, | ||
JSMSG_BAD_SHARP_DEF, numBuf); | ||
goto error; | ||
} | ||
if (!OBJ_SET_PROPERTY(cx, obj, id, &rval)) | ||
goto error; | ||
END_CASE(JSOP_DEFSHARP) | ||
BEGIN_CASE(JSOP_USESHARP) | ||
i = (jsint) GET_UINT16(regs.pc); | ||
id = INT_TO_JSID(i); | ||
obj = fp->sharpArray; | ||
if (!obj) { | ||
rval = JSVAL_VOID; | ||
} else { | ||
if (!OBJ_GET_PROPERTY(cx, obj, id, &rval)) | ||
goto error; | ||
} | ||
if (!JSVAL_IS_OBJECT(rval)) { | ||
char numBuf[12]; | ||
JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); | ||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, | ||
JSMSG_BAD_SHARP_USE, numBuf); | ||
goto error; | ||
} | ||
PUSH_OPND(rval); | ||
END_CASE(JSOP_USESHARP) | ||
#endif /* JS_HAS_SHARP_VARS */ | ||
BEGIN_CASE(JSOP_GOSUB) | ||
PUSH(JSVAL_FALSE); | ||
i = PTRDIFF(regs.pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH; | ||
PUSH(INT_TO_JSVAL(i)); | ||
len = GET_JUMP_OFFSET(regs.pc); | ||
END_VARLEN_CASE | ||
BEGIN_CASE(JSOP_GOSUBX) | ||
PUSH(JSVAL_FALSE); | ||
i = PTRDIFF(regs.pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH; | ||
len = GET_JUMPX_OFFSET(regs.pc); | ||
PUSH(INT_TO_JSVAL(i)); | ||
END_VARLEN_CASE | ||
BEGIN_CASE(JSOP_RETSUB) | ||
/* Pop [exception or hole, retsub pc-index]. */ | ||
rval = POP(); | ||
lval = POP(); | ||
JS_ASSERT(JSVAL_IS_BOOLEAN(lval)); | ||
if (JSVAL_TO_BOOLEAN(lval)) { | ||
/* | ||
* Exception was pending during finally, throw it *before* we | ||
* adjust pc, because pc indexes into script->trynotes. This | ||
* turns out not to be necessary, but it seems clearer. And | ||
* it points out a FIXME: 350509, due to Igor Bukanov. | ||
*/ | ||
cx->throwing = JS_TRUE; | ||
cx->exception = rval; | ||
goto error; | ||
} | ||
JS_ASSERT(JSVAL_IS_INT(rval)); | ||
len = JSVAL_TO_INT(rval); | ||
regs.pc = script->main; | ||
END_VARLEN_CASE | ||
BEGIN_CASE(JSOP_EXCEPTION) | ||
JS_ASSERT(cx->throwing); | ||
PUSH(cx->exception); | ||
cx->throwing = JS_FALSE; | ||
CHECK_BRANCH(); | ||
END_CASE(JSOP_EXCEPTION) | ||
BEGIN_CASE(JSOP_FINALLY) | ||
CHECK_BRANCH(); | ||
END_CASE(JSOP_FINALLY) | ||
BEGIN_CASE(JSOP_THROWING) | ||
JS_ASSERT(!cx->throwing); | ||
cx->throwing = JS_TRUE; | ||
cx->exception = POP_OPND(); | ||
END_CASE(JSOP_THROWING) | ||
BEGIN_CASE(JSOP_THROW) | ||
JS_ASSERT(!cx->throwing); | ||
CHECK_BRANCH(); | ||
cx->throwing = JS_TRUE; | ||
cx->exception = POP_OPND(); | ||
/* let the code at error try to catch the exception. */ | ||
goto error; | ||
BEGIN_CASE(JSOP_SETLOCALPOP) | ||
/* | ||
* The stack must have a block with at least one local slot below | ||
* the exception object. | ||
*/ | ||
JS_ASSERT((size_t) (regs.sp - StackBase(fp)) >= 2); | ||
slot = GET_UINT16(regs.pc); | ||
JS_ASSERT(slot + 1 < script->nslots); | ||
fp->slots[slot] = POP_OPND(); | ||
END_CASE(JSOP_SETLOCALPOP) | ||
BEGIN_CASE(JSOP_IFPRIMTOP) | ||
/* | ||
* If the top of stack is of primitive type, jump to our target. | ||
* Otherwise advance to the next opcode. | ||
*/ | ||
JS_ASSERT(regs.sp > StackBase(fp)); | ||
rval = FETCH_OPND(-1); | ||
if (JSVAL_IS_PRIMITIVE(rval)) { | ||
len = GET_JUMP_OFFSET(regs.pc); | ||
BRANCH(len); | ||
} | ||
END_CASE(JSOP_IFPRIMTOP) | ||
3034 | ||
3035 | BEGIN_CASE(JSOP_PRIMTOP) | #if !JS_THREADED_INTERP |
JS_ASSERT(regs.sp > StackBase(fp)); | ||
lval = FETCH_OPND(-1); | ||
i = GET_INT8(regs.pc); | ||
if (!JSVAL_IS_PRIMITIVE(lval)) { | ||
lval = FETCH_OPND(-2); | ||
js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, | ||
-2, lval, NULL, | ||
(i == JSTYPE_VOID) | ||
? "primitive type" | ||
: JS_TYPE_STR(i)); | ||
goto error; | ||
} | ||
END_CASE(JSOP_PRIMTOP) | ||
BEGIN_CASE(JSOP_OBJTOP) | ||
lval = FETCH_OPND(-1); | ||
if (JSVAL_IS_PRIMITIVE(lval)) { | ||
js_ReportValueError(cx, GET_UINT16(regs.pc), -1, lval, NULL); | ||
goto error; | ||
} | ||
END_CASE(JSOP_OBJTOP) | ||
BEGIN_CASE(JSOP_INSTANCEOF) | ||
rval = FETCH_OPND(-1); | ||
if (JSVAL_IS_PRIMITIVE(rval) || | ||
!(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) { | ||
js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, | ||
-1, rval, NULL); | ||
goto error; | ||
} | ||
lval = FETCH_OPND(-2); | ||
cond = JS_FALSE; | ||
if (!obj->map->ops->hasInstance(cx, obj, lval, &cond)) | ||
goto error; | ||
regs.sp--; | ||
STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); | ||
END_CASE(JSOP_INSTANCEOF) | ||
#if JS_HAS_DEBUGGER_KEYWORD | ||
BEGIN_CASE(JSOP_DEBUGGER) | ||
{ | ||
JSTrapHandler handler = cx->debugHooks->debuggerHandler; | ||
if (handler) { | ||
switch (handler(cx, script, regs.pc, &rval, | ||
cx->debugHooks->debuggerHandlerData)) { | ||
case JSTRAP_ERROR: | ||
goto error; | ||
case JSTRAP_CONTINUE: | ||
break; | ||
case JSTRAP_RETURN: | ||
fp->rval = rval; | ||
ok = JS_TRUE; | ||
goto forced_return; | ||
case JSTRAP_THROW: | ||
cx->throwing = JS_TRUE; | ||
cx->exception = rval; | ||
goto error; | ||
default:; | ||
} | ||
CHECK_INTERRUPT_HANDLER(); | ||
} | ||
} | ||
END_CASE(JSOP_DEBUGGER) | ||
#endif /* JS_HAS_DEBUGGER_KEYWORD */ | ||
#if JS_HAS_XML_SUPPORT | ||
BEGIN_CASE(JSOP_DEFXMLNS) | ||
rval = POP(); | ||
if (!js_SetDefaultXMLNamespace(cx, rval)) | ||
goto error; | ||
END_CASE(JSOP_DEFXMLNS) | ||
BEGIN_CASE(JSOP_ANYNAME) | ||
if (!js_GetAnyName(cx, &rval)) | ||
goto error; | ||
PUSH_OPND(rval); | ||
END_CASE(JSOP_ANYNAME) | ||
BEGIN_CASE(JSOP_QNAMEPART) | ||
LOAD_ATOM(0); | ||
PUSH_OPND(ATOM_KEY(atom)); | ||
END_CASE(JSOP_QNAMEPART) | ||
BEGIN_CASE(JSOP_QNAMECONST) | ||
LOAD_ATOM(0); | ||
rval = ATOM_KEY(atom); | ||
lval = FETCH_OPND(-1); | ||
obj = js_ConstructXMLQNameObject(cx, lval, rval); | ||
if (!obj) | ||
goto error; | ||
STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); | ||
END_CASE(JSOP_QNAMECONST) | ||
BEGIN_CASE(JSOP_QNAME) | ||
rval = FETCH_OPND(-1); | ||
lval = FETCH_OPND(-2); | ||
obj = js_ConstructXMLQNameObject(cx, lval, rval); | ||
if (!obj) | ||
goto error; | ||
regs.sp--; | ||
STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); | ||
END_CASE(JSOP_QNAME) | ||
BEGIN_CASE(JSOP_TOATTRNAME) | ||
rval = FETCH_OPND(-1); | ||
if (!js_ToAttributeName(cx, &rval)) | ||
goto error; | ||
STORE_OPND(-1, rval); | ||
END_CASE(JSOP_TOATTRNAME) | ||
BEGIN_CASE(JSOP_TOATTRVAL) | ||
rval = FETCH_OPND(-1); | ||
JS_ASSERT(JSVAL_IS_STRING(rval)); | ||
str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval), JS_FALSE); | ||
if (!str) | ||
goto error; | ||
STORE_OPND(-1, STRING_TO_JSVAL(str)); | ||
END_CASE(JSOP_TOATTRVAL) | ||
BEGIN_CASE(JSOP_ADDATTRNAME) | ||
BEGIN_CASE(JSOP_ADDATTRVAL) | ||
rval = FETCH_OPND(-1); | ||
lval = FETCH_OPND(-2); | ||
str = JSVAL_TO_STRING(lval); | ||
str2 = JSVAL_TO_STRING(rval); | ||
str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2); | ||
if (!str) | ||
goto error; | ||
regs.sp--; | ||
STORE_OPND(-1, STRING_TO_JSVAL(str)); | ||
END_CASE(JSOP_ADDATTRNAME) | ||
BEGIN_CASE(JSOP_BINDXMLNAME) | ||
lval = FETCH_OPND(-1); | ||
if (!js_FindXMLProperty(cx, lval, &obj, &id)) | ||
goto error; | ||
STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); | ||
PUSH_OPND(ID_TO_VALUE(id)); | ||
END_CASE(JSOP_BINDXMLNAME) | ||
BEGIN_CASE(JSOP_SETXMLNAME) | ||
obj = JSVAL_TO_OBJECT(FETCH_OPND(-3)); | ||
rval = FETCH_OPND(-1); | ||
FETCH_ELEMENT_ID(obj, -2, id); | ||
if (!OBJ_SET_PROPERTY(cx, obj, id, &rval)) | ||
goto error; | ||
rval = FETCH_OPND(-1); | ||
regs.sp -= 2; | ||
STORE_OPND(-1, rval); | ||
END_CASE(JSOP_SETXMLNAME) | ||
BEGIN_CASE(JSOP_CALLXMLNAME) | ||
BEGIN_CASE(JSOP_XMLNAME) | ||
lval = FETCH_OPND(-1); | ||
if (!js_FindXMLProperty(cx, lval, &obj, &id)) | ||
goto error; | ||
if (!OBJ_GET_PROPERTY(cx, obj, id, &rval)) | ||
goto error; | ||
STORE_OPND(-1, rval); | ||
if (op == JSOP_CALLXMLNAME) | ||
PUSH_OPND(OBJECT_TO_JSVAL(obj)); | ||
END_CASE(JSOP_XMLNAME) | ||
BEGIN_CASE(JSOP_DESCENDANTS) | ||
BEGIN_CASE(JSOP_DELDESC) | ||
FETCH_OBJECT(cx, -2, lval, obj); | ||
rval = FETCH_OPND(-1); | ||
if (!js_GetXMLDescendants(cx, obj, rval, &rval)) | ||
goto error; | ||
if (op == JSOP_DELDESC) { | ||
regs.sp[-1] = rval; /* set local root */ | ||
if (!js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval))) | ||
goto error; | ||
rval = JSVAL_TRUE; /* always succeed */ | ||
} | ||
regs.sp--; | ||
STORE_OPND(-1, rval); | ||
END_CASE(JSOP_DESCENDANTS) | ||
BEGIN_CASE(JSOP_FILTER) | ||
/* | ||
* We push the hole value before jumping to [enditer] so we can | ||
* detect the first iteration and direct js_StepXMLListFilter to | ||
* initialize filter's state. | ||
*/ | ||
PUSH_OPND(JSVAL_HOLE); | ||
len = GET_JUMP_OFFSET(regs.pc); | ||
JS_ASSERT(len > 0); | ||
END_VARLEN_CASE | ||
BEGIN_CASE(JSOP_ENDFILTER) | ||
cond = (regs.sp[-1] != JSVAL_HOLE); | ||
if (cond) { | ||
/* Exit the "with" block left from the previous iteration. */ | ||
js_LeaveWith(cx); | ||
} | ||
if (!js_StepXMLListFilter(cx, cond)) | ||
goto error; | ||
if (regs.sp[-1] != JSVAL_NULL) { | ||
/* | ||
* Decrease sp after EnterWith returns as we use sp[-1] there | ||
* to root temporaries. | ||
*/ | ||
JS_ASSERT(VALUE_IS_XML(cx, regs.sp[-1])); | ||
if (!js_EnterWith(cx, -2)) | ||
goto error; | ||
regs.sp--; | ||
len = GET_JUMP_OFFSET(regs.pc); | ||
JS_ASSERT(len < 0); | ||
BRANCH(len); | ||
} | ||
regs.sp--; | ||
END_CASE(JSOP_ENDFILTER); | ||
BEGIN_CASE(JSOP_TOXML) | ||
rval = FETCH_OPND(-1); | ||
obj = js_ValueToXMLObject(cx, rval); | ||
if (!obj) | ||
goto error; | ||
STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); | ||
END_CASE(JSOP_TOXML) | ||
BEGIN_CASE(JSOP_TOXMLLIST) | ||
rval = FETCH_OPND(-1); | ||
obj = js_ValueToXMLListObject(cx, rval); | ||
if (!obj) | ||
goto error; | ||
STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); | ||
END_CASE(JSOP_TOXMLLIST) | ||
BEGIN_CASE(JSOP_XMLTAGEXPR) | ||
rval = FETCH_OPND(-1); | ||
str = js_ValueToString(cx, rval); | ||
if (!str) | ||
goto error; | ||
STORE_OPND(-1, STRING_TO_JSVAL(str)); | ||
END_CASE(JSOP_XMLTAGEXPR) | ||
BEGIN_CASE(JSOP_XMLELTEXPR) | ||
rval = FETCH_OPND(-1); | ||
if (VALUE_IS_XML(cx, rval)) { | ||
str = js_ValueToXMLString(cx, rval); | ||
&n |