1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
39
40 /* build on macs with low memory */
41 #if defined(XP_MAC) && defined(MOZ_MAC_LOWMEM)
42 #pragma optimization_level 1
43 #endif
44
45 /*
46  * JavaScript bytecode interpreter.
47  */
48 #include "jsstddef.h"
49 #include <stdio.h>
50 #include <string.h>
51 #include <math.h>
52 #include "jstypes.h"
53 #include "jsarena.h" /* Added by JSIFY */
54 #include "jsutil.h" /* Added by JSIFY */
55 #include "jsprf.h"
56 #include "jsapi.h"
57 #include "jsarray.h"
58 #include "jsatom.h"
59 #include "jsbool.h"
60 #include "jscntxt.h"
61 #include "jsconfig.h"
62 #include "jsdbgapi.h"
63 #include "jsfun.h"
64 #include "jsgc.h"
65 #include "jsinterp.h"
66 #include "jslock.h"
67 #include "jsnum.h"
68 #include "jsobj.h"
69 #include "jsopcode.h"
70 #include "jsscope.h"
71 #include "jsscript.h"
72 #include "jsstr.h"
73
74 #if JS_HAS_JIT
75 #include "jsjit.h"
76 #endif
77
78 #ifdef DEBUG
79 #define ASSERT_CACHE_IS_EMPTY(cache)                                          \
80     JS_BEGIN_MACRO                                                            \
81         JSPropertyCacheEntry *end_, *pce_, entry_;                            \
82         JSPropertyCache *cache_ = (cache);                                    \
83         JS_ASSERT(cache_->empty);                                             \
84         end_ = &cache_->table[PROPERTY_CACHE_SIZE];                           \
85         for (pce_ = &cache_->table[0]; pce_ < end_; pce_++) {                 \
86             PCE_LOAD(cache_, pce_, entry_);                                   \
87             JS_ASSERT(!PCE_OBJECT(entry_));                                   \
88             JS_ASSERT(!PCE_PROPERTY(entry_));                                 \
89         }                                                                     \
90     JS_END_MACRO
91 #else
92 #define ASSERT_CACHE_IS_EMPTY(cache) ((void)0)
93 #endif
94
95 void
96 js_FlushPropertyCache(JSContext *cx)
97 17 {
98 17     JSPropertyCache *cache;
99
100 17     cache = &cx->runtime->propertyCache;
101 17     if (cache->empty) {
102 0         ASSERT_CACHE_IS_EMPTY(cache);
103 0         return;
104     }
105 17     memset(cache->table, 0, sizeof cache->table);
106 17     cache->empty = JS_TRUE;
107 #ifdef JS_PROPERTY_CACHE_METERING
108     cache->flushes++;
109 #endif
110 }
111
112 void
113 js_DisablePropertyCache(JSContext *cx)
114 17 {
115 17     JS_ASSERT(!cx->runtime->propertyCache.disabled);
116 17     cx->runtime->propertyCache.disabled = JS_TRUE;
117 }
118
119 void
120 js_EnablePropertyCache(JSContext *cx)
121 17 {
122 17     JS_ASSERT(cx->runtime->propertyCache.disabled);
123 17     ASSERT_CACHE_IS_EMPTY(&cx->runtime->propertyCache);
124 17     cx->runtime->propertyCache.disabled = JS_FALSE;
125 }
126
127 /*
128  * Class for for/in loop property iterator objects.
129  */
130 #define JSSLOT_ITER_STATE   JSSLOT_PRIVATE
131
132 static void
133 prop_iterator_finalize(JSContext *cx, JSObject *obj)
134 22596 {
135 22596     jsval iter_state;
136 22596     jsval iteratee;
137
138     /* Protect against stillborn iterators. */
139 22596     iter_state = obj->slots[JSSLOT_ITER_STATE];
140 22596     iteratee = obj->slots[JSSLOT_PARENT];
141 22596     if (!JSVAL_IS_NULL(iter_state) && !JSVAL_IS_PRIMITIVE(iteratee)) {
142 2         OBJ_ENUMERATE(cx, JSVAL_TO_OBJECT(iteratee), JSENUMERATE_DESTROY,
143                       &iter_state, NULL);
144     }
145 22596     js_RemoveRoot(cx->runtime, &obj->slots[JSSLOT_PARENT]);
146
147     /* XXX force the GC to restart so we can collect iteratee, if possible,
148            during the current collector activation */
149 22596     cx->runtime->gcLevel++;
150 }
151
152 static JSClass prop_iterator_class = {
153     "PropertyIterator",
154     0,
155     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
156     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   prop_iterator_finalize,
157     JSCLASS_NO_OPTIONAL_MEMBERS
158 };
159
160 /*
161  * Stack macros and functions.  These all use a local variable, jsval *sp, to
162  * point to the next free stack slot.  SAVE_SP must be called before any call
163  * to a function that may invoke the interpreter.  RESTORE_SP must be called
164  * only after return from js_Invoke, because only js_Invoke changes fp->sp.
165  */
166 #define PUSH(v)         (*sp++ = (v))
167 #define POP()           (*--sp)
168 #ifdef DEBUG
169 #define SAVE_SP(fp)                                                           \
170     (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase),        \
171      (fp)->sp = sp)
172 #else
173 #define SAVE_SP(fp)     ((fp)->sp = sp)
174 #endif
175 #define RESTORE_SP(fp)  (sp = (fp)->sp)
176
177 /*
178  * Push the generating bytecode's pc onto the parallel pc stack that runs
179  * depth slots below the operands.
180  *
181  * NB: PUSH_OPND uses sp, depth, and pc from its lexical environment.  See
182  * js_Interpret for these local variables' declarations and uses.
183  */
184 #define PUSH_OPND(v)    (sp[-depth] = (jsval)pc, PUSH(v))
185 #define STORE_OPND(n,v) (sp[(n)-depth] = (jsval)pc, sp[n] = (v))
186 #define POP_OPND()      POP()
187 #define FETCH_OPND(n)   (sp[n])
188
189 /*
190  * Push the jsdouble d using sp, depth, and pc from the lexical environment.
191  * Try to convert d to a jsint that fits in a jsval, otherwise GC-alloc space
192  * for it and push a reference.
193  */
194 #define STORE_NUMBER(cx, n, d)                                                \
195     JS_BEGIN_MACRO                                                            \
196         jsint i_;                                                             \
197         jsval v_;                                                             \
198                                                                               \
199         if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) {                \
200             v_ = INT_TO_JSVAL(i_);                                            \
201         } else {                                                              \
202             ok = js_NewDoubleValue(cx, d, &v_);                               \
203             if (!ok)                                                          \
204                 goto out;                                                     \
205         }                                                                     \
206         STORE_OPND(n, v_);                                                    \
207     JS_END_MACRO
208
209 #define FETCH_NUMBER(cx, n, d)                                                \
210     JS_BEGIN_MACRO                                                            \
211         jsval v_;                                                             \
212                                                                               \
213         v_ = FETCH_OPND(n);                                                   \
214         VALUE_TO_NUMBER(cx, v_, d);                                           \
215     JS_END_MACRO
216
217 #define FETCH_INT(cx, n, i)                                                   \
218     JS_BEGIN_MACRO                                                            \
219         jsval v_ = FETCH_OPND(n);                                             \
220         if (JSVAL_IS_INT(v_)) {                                               \
221             i = JSVAL_TO_INT(v_);                                             \
222         } else {                                                              \
223             SAVE_SP(fp);                                                      \
224             ok = js_ValueToECMAInt32(cx, v_, &i);                             \
225             if (!ok)                                                          \
226                 goto out;                                                     \
227         }                                                                     \
228     JS_END_MACRO
229
230 #define FETCH_UINT(cx, n, ui)                                                 \
231     JS_BEGIN_MACRO                                                            \
232         jsval v_ = FETCH_OPND(n);                                             \
233         jsint i_;                                                             \
234         if (JSVAL_IS_INT(v_) && (i_ = JSVAL_TO_INT(v_)) >= 0) {               \
235             ui = (uint32) i_;                                                 \
236         } else {                                                              \
237             SAVE_SP(fp);                                                      \
238             ok = js_ValueToECMAUint32(cx, v_, &ui);                           \
239             if (!ok)                                                          \
240                 goto out;                                                     \
241         }                                                                     \
242     JS_END_MACRO
243
244 /*
245  * Optimized conversion macros that test for the desired type in v before
246  * homing sp and calling a conversion function.
247  */
248 #define VALUE_TO_NUMBER(cx, v, d)                                             \
249     JS_BEGIN_MACRO                                                            \
250         if (JSVAL_IS_INT(v)) {                                                \
251             d = (jsdouble)JSVAL_TO_INT(v);                                    \
252         } else if (JSVAL_IS_DOUBLE(v)) {                                      \
253             d = *JSVAL_TO_DOUBLE(v);                                          \
254         } else {                                                              \
255             SAVE_SP(fp);                                                      \
256             ok = js_ValueToNumber(cx, v, &d);                                 \
257             if (!ok)                                                          \
258                 goto out;                                                     \
259         }                                                                     \
260     JS_END_MACRO
261
262 #define POP_BOOLEAN(cx, v, b)                                                 \
263     JS_BEGIN_MACRO                                                            \
264         v = FETCH_OPND(-1);                                                   \
265         if (v == JSVAL_NULL) {                                                \
266             b = JS_FALSE;                                                     \
267         } else if (JSVAL_IS_BOOLEAN(v)) {                                     \
268             b = JSVAL_TO_BOOLEAN(v);                                          \
269         } else {                                                              \
270             SAVE_SP(fp);                                                      \
271             ok = js_ValueToBoolean(cx, v, &b);                                \
272             if (!ok)                                                          \
273                 goto out;                                                     \
274         }                                                                     \
275         sp--;                                                                 \
276     JS_END_MACRO
277
278 #define VALUE_TO_OBJECT(cx, v, obj)                                           \
279     JS_BEGIN_MACRO                                                            \
280         if (JSVAL_IS_OBJECT(v) && v != JSVAL_NULL) {                          \
281             obj = JSVAL_TO_OBJECT(v);                                         \
282         } else {                                                              \
283             SAVE_SP(fp);                                                      \
284             obj = js_ValueToNonNullObject(cx, v);                             \
285             if (!obj) {                                                       \
286                 ok = JS_FALSE;                                                \
287                 goto out;                                                     \
288             }                                                                 \
289         }                                                                     \
290     JS_END_MACRO
291
292 #if JS_BUG_VOID_TOSTRING
293 #define CHECK_VOID_TOSTRING(cx, v)                                            \
294     if (JSVAL_IS_VOID(v)) {                                                   \
295         JSString *str_;                                                       \
296         str_ = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); \
297         v = STRING_TO_JSVAL(str_);                                            \
298     }
299 #else
300 #define CHECK_VOID_TOSTRING(cx, v)  ((void)0)
301 #endif
302
303 #if JS_BUG_EAGER_TOSTRING
304 #define CHECK_EAGER_TOSTRING(hint)  (hint = JSTYPE_STRING)
305 #else
306 #define CHECK_EAGER_TOSTRING(hint)  ((void)0)
307 #endif
308
309 #define VALUE_TO_PRIMITIVE(cx, v, hint, vp)                                   \
310     JS_BEGIN_MACRO                                                            \
311         if (JSVAL_IS_PRIMITIVE(v)) {                                          \
312             CHECK_VOID_TOSTRING(cx, v);                                       \
313             *vp = v;                                                          \
314         } else {                                                              \
315             SAVE_SP(fp);                                                      \
316             CHECK_EAGER_TOSTRING(hint);                                       \
317             ok = OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, vp);         \
318             if (!ok)                                                          \
319                 goto out;                                                     \
320         }                                                                     \
321     JS_END_MACRO
322
323 JS_FRIEND_API(jsval *)
324 js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
325 186972 {
326 186972     jsval *sp;
327
328 186972     if (markp)
329 186427         *markp = JS_ARENA_MARK(&cx->stackPool);
330 186972     JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval));
331 186972     if (!sp) {
332 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_STACK_OVERFLOW,
333                              (cx->fp && cx->fp->fun)
334                              ? JS_GetFunctionName(cx->fp->fun)
335                              : "script");
336     }
337 186972     return sp;
338 }
339
340 JS_FRIEND_API(void)
341 js_FreeRawStack(JSContext *cx, void *mark)
342 1490 {
343 1490     JS_ARENA_RELEASE(&cx->stackPool, mark);
344 }
345
346 JS_FRIEND_API(jsval *)
347 js_AllocStack(JSContext *cx, uintN nslots, void **markp)
348 70132 {
349 70132     jsval *sp, *vp, *end;
350 70132     JSArena *a;
351 70132     JSStackHeader *sh;
352 70132     JSStackFrame *fp;
353
354     /* Callers don't check for zero nslots: we do to avoid empty segments. */
355 70132     if (nslots == 0) {
356 0         *markp = NULL;
357 0         return JS_ARENA_MARK(&cx->stackPool);
358     }
359
360     /* Allocate 2 extra slots for the stack segment header we'll likely need. */
361 70132     sp = js_AllocRawStack(cx, 2 + nslots, markp);
362 70132     if (!sp)
363 0         return NULL;
364
365     /* Try to avoid another header if we can piggyback on the last segment. */
366 70132     a = cx->stackPool.current;
367 70132     sh = cx->stackHeaders;
368 70132     if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) {
369         /* Extend the last stack segment, give back the 2 header slots. */
370 0         sh->nslots += nslots;
371 0         a->avail -= 2 * sizeof(jsval);
372     } else {
373         /*
374          * Need a new stack segment, so we must initialize unused slots in the
375          * current frame.  See js_GC, just before marking the "operand" jsvals,
376          * where we scan from fp->spbase to fp->sp or through fp->script->depth
377          * (whichever covers fewer slots).
378          */
379 70132         fp = cx->fp;
380 70132         if (fp && fp->script && fp->spbase) {
381 #ifdef DEBUG
382             jsuword depthdiff = fp->script->depth * sizeof(jsval);
383             JS_ASSERT(JS_UPTRDIFF(fp->sp, fp->spbase) <= depthdiff);
384             JS_ASSERT(JS_UPTRDIFF(*markp, fp->spbase) >= depthdiff);
385 #endif
386 23196             end = fp->spbase + fp->script->depth;
387 196257             for (vp = fp->sp; vp < end; vp++)
388 173061                 *vp = JSVAL_VOID;
389         }
390
391         /* Allocate and push a stack segment header from the 2 extra slots. */
392 70132         sh = (JSStackHeader *)sp;
393 70132         sh->nslots = nslots;
394 70132         sh->down = cx->stackHeaders;
395 70132         cx->stackHeaders = sh;
396 70132         sp += 2;
397     }
398
399 70132     return sp;
400 }
401
402 JS_FRIEND_API(void)
403 js_FreeStack(JSContext *cx, void *mark)
404 70132 {
405 70132     JSStackHeader *sh;
406 70132     jsuword slotdiff;
407
408     /* Check for zero nslots allocation special case. */
409 70132     if (!mark)
410 0         return;
411
412     /* We can assert because js_FreeStack always balances js_AllocStack. */
413 70132     sh = cx->stackHeaders;
414 70132     JS_ASSERT(sh);
415
416     /* If mark is in the current segment, reduce sh->nslots, else pop sh. */
417 70132     slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval);
418 70132     if (slotdiff < (jsuword)sh->nslots)
419 0         sh->nslots = slotdiff;
420     else
421 70132         cx->stackHeaders = sh->down;
422
423     /* Release the stackPool space allocated since mark was set. */
424 70132     JS_ARENA_RELEASE(&cx->stackPool, mark);
425 }
426
427 /*
428  * To economize on slots space in functions, the compiler records arguments and
429  * local variables as shared (JSPROP_SHARED) properties with well-known getters
430  * and setters: js_{Get,Set}Argument, js_{Get,Set}LocalVariable.  Now, we could
431  * record args and vars in lists or hash tables in function-private data, but
432  * that means more duplication in code, and more data at runtime in the hash
433  * table case due to round-up to powers of two, just to recapitulate the scope
434  * machinery in the function object.
435  *
436  * What's more, for a long time (to the dawn of "Mocha" in 1995), these getters
437  * and setters knew how to search active stack frames in a context to find the
438  * top activation of the function f, in order to satisfy a get or set of f.a,
439  * for argument a, or f.x, for local variable x.  You could use f.a instead of
440  * just a in function f(a) { return f.a }, for example, to return the actual
441  * parameter.
442  *
443  * ECMA requires that we give up on this ancient extension, because it is not
444  * compatible with the standard as used by real-world scripts.  While Chapter
445  * 16 does allow for additional properties to be defined on native objects by
446  * a conforming implementation, these magic getters and setters cause f.a's
447  * meaning to vary unexpectedly.  Real-world scripts set f.A = 42 to define
448  * "class static" (after Java) constants, for example, but if A also names an
449  * arg or var in f, the constant is not available while f is active, and any
450  * non-constant class-static can't be set while f is active.
451  *
452  * So, to label arg and var properties in functions without giving them magic
453  * abilities to affect active frame stack slots, while keeping the properties
454  * shared (slot-less) to save space in the common case (where no assignment
455  * sets a function property with the same name as an arg or var), the setters
456  * for args and vars must handle two special cases here.
457  *
458  * XXX functions tend to have few args and vars, so we risk O(n^2) growth here
459  * XXX ECMA *really* wants args and vars to be stored in function-private data,
460  *     not as function object properties.
461  */
462 static JSBool
463 SetFunctionSlot(JSContext *cx, JSObject *obj, JSPropertyOp setter, jsid id,
464                 jsval v)
465 0 {
466 0     uintN slot;
467 0     JSObject *origobj;
468 0     JSScope *scope;
469 0     JSScopeProperty *sprop;
470 0     JSString *str;
471 0     JSBool ok;
472
473 0     slot = (uintN) JSVAL_TO_INT(id);
474 0     if (OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) {
475         /*
476          * Given a non-function object obj that has a function object in its
477          * prototype chain, where an argument or local variable property named
478          * by (setter, slot) is being set, override the shared property in the
479          * prototype with an unshared property in obj.  This situation arises
480          * in real-world JS due to .prototype setting and collisions among a
481          * function's "static property" names and arg or var names, believe it
482          * or not.
483          */
484 0         origobj = obj;
485 0         do {
486 0             obj = OBJ_GET_PROTO(cx, obj);
487 0             if (!obj)
488 0                 return JS_TRUE;
489 0         } while (OBJ_GET_CLASS(cx, obj) != &js_FunctionClass);
490
491 0         JS_LOCK_OBJ(cx, obj);
492 0         scope = OBJ_SCOPE(obj);
493 0         for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
494 0             if (sprop->setter == setter) {
495 0                 JS_ASSERT(!JSVAL_IS_INT(sprop->id) &&
496                           ATOM_IS_STRING((JSAtom *)sprop->id) &&
497                           (sprop->flags & SPROP_HAS_SHORTID));
498
499 0                 if ((uintN) sprop->shortid == slot) {
500 0                     str = ATOM_TO_STRING((JSAtom *)sprop->id);
501 0                     JS_UNLOCK_SCOPE(cx, scope);
502
503 0                     return JS_DefineUCProperty(cx, origobj,
504                                                JSSTRING_CHARS(str),
505                                                JSSTRING_LENGTH(str),
506                                                v, NULL, NULL,
507                                                JSPROP_ENUMERATE);
508                 }
509             }
510         }
511 0         JS_UNLOCK_SCOPE(cx, scope);
512 0         return JS_TRUE;
513     }
514
515     /*
516      * Argument and local variable properties of function objects are shared
517      * by default (JSPROP_SHARED), therefore slot-less.  But if for function
518      * f(a) {}, f.a = 42 is evaluated, f.a should be 42 after the assignment,
519      * whether or not f is active.  So js_SetArgument and js_SetLocalVariable
520      * must be prepared to change an arg or var from shared to unshared status,
521      * allocating a slot in obj to hold v.
522      */
523 0     ok = JS_TRUE;
524 0     JS_LOCK_OBJ(cx, obj);
525 0     scope = OBJ_SCOPE(obj);
526 0     for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
527 0         if (sprop->setter == setter && (uintN) sprop->shortid == slot) {
528 0             if (sprop->attrs & JSPROP_SHARED) {
529 0                 sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop,
530                                                     0, ~JSPROP_SHARED,
531                                                     sprop->getter, setter);
532 0                 if (!sprop) {
533 0                     ok = JS_FALSE;
534                 } else {
535                     /* See js_SetProperty, near the bottom. */
536 0                     GC_POKE(cx, pval);
537 0                     LOCKED_OBJ_SET_SLOT(obj, sprop->slot, v);
538                 }
539             }
540 0             break;
541         }
542     }
543 0     JS_UNLOCK_SCOPE(cx, scope);
544 0     return ok;
545 }
546
547 JSBool
548 js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
549 0 {
550 0     return JS_TRUE;
551 }
552
553 JSBool
554 js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
555 0 {
556 0     return SetFunctionSlot(cx, obj, js_SetArgument, id, *vp);
557 }
558
559 JSBool
560 js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
561 0 {
562 0     return JS_TRUE;
563 }
564
565 JSBool
566 js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
567 0 {
568 0     return SetFunctionSlot(cx, obj, js_SetLocalVariable, id, *vp);
569 }
570
571 /*
572  * Compute the 'this' parameter and store it in frame as frame.thisp.
573  * Activation objects ("Call" objects not created with "new Call()", i.e.,
574  * "Call" objects that have private data) may not be referred to by 'this',
575  * as dictated by ECMA.
576  *
577  * N.B.: fp->argv must be set, fp->argv[-1] the nominal 'this' paramter as
578  * a jsval, and fp->argv[-2] must be the callee object reference, usually a
579  * function object.  Also, fp->flags must contain JSFRAME_CONSTRUCTING if we
580  * are preparing for a constructor call.
581  */
582 static JSBool
583 ComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp)
584 665718 {
585 665718     JSObject *parent;
586
587 665718     if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) {
588         /* Some objects (e.g., With) delegate 'this' to another object. */
589 664376         thisp = OBJ_THIS_OBJECT(cx, thisp);
590 664376         if (!thisp)
591 0             return JS_FALSE;
592
593         /* Default return value for a constructor is the new object. */
594 664376         if (fp->flags & JSFRAME_CONSTRUCTING)
595 60949             fp->rval = OBJECT_TO_JSVAL(thisp);
596     } else {
597         /*
598          * ECMA requires "the global object", but in the presence of multiple
599          * top-level objects (windows, frames, or certain layers in the client
600          * object model), we prefer fun's parent.  An example that causes this
601          * code to run:
602          *
603          *   // in window w1
604          *   function f() { return this }
605          *   function g() { return f }
606          *
607          *   // in window w2
608          *   var h = w1.g()
609          *   alert(h() == w1)
610          *
611          * The alert should display "true".
612          */
613 1342         JS_ASSERT(!(fp->flags & JSFRAME_CONSTRUCTING));
614 1342         if (JSVAL_IS_PRIMITIVE(fp->argv[-2]) ||
615             !(parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fp->argv[-2])))) {
616 0             thisp = cx->globalObject;
617         } else {
618             /* walk up to find the top-level object */
619 1342             thisp = parent;
620 1342             while ((parent = OBJ_GET_PARENT(cx, thisp)) != NULL)
621 0                 thisp = parent;
622         }
623     }
624 665718     fp->thisp = thisp;
625 665718     fp->argv[-1] = OBJECT_TO_JSVAL(thisp);
626 665718     return JS_TRUE;
627 }
628
629 /*
630  * Find a function reference and its 'this' object implicit first parameter
631  * under argc arguments on cx's stack, and call the function.  Push missing
632  * required arguments, allocate declared local variables, and pop everything
633  * when done.  Then push the return value.
634  */
635 JS_FRIEND_API(JSBool)
636 js_Invoke(JSContext *cx, uintN argc, uintN flags)
637 550913 {
638 550913     void *mark;
639 550913     JSStackFrame *fp, frame;
640 550913     jsval *sp, *newsp, *limit;
641 550913     jsval *vp, v;
642 550913     JSObject *funobj, *parent, *thisp;
643 550913     JSBool ok;
644 550913     JSClass *clasp;
645 550913     JSObjectOps *ops;
646 550913     JSNative native;
647 550913     JSFunction *fun;
648 550913     JSScript *script;
649 550913     uintN minargs, nvars;
650 550913     intN nslots, nalloc, surplus;
651 550913     JSInterpreterHook hook;
652 550913     void *hookData;
653
654     /* Mark the top of stack and load frequently-used registers. */
655 550913     mark = JS_ARENA_MARK(&cx->stackPool);
656 550913     fp = cx->fp;
657 550913     sp = fp->sp;
658
659     /*
660      * Set vp to the callee value's stack slot (it's where rval goes).
661      * Once vp is set, control should flow through label out2: to return.
662      * Set frame.rval early so native class and object ops can throw and
663      * return false, causing a goto out2 with ok set to false.  Also set
664      * frame.flags to flags so that ComputeThis can test bits in it.
665      */
666 550913     vp = sp - (2 + argc);
667 550913     v = *vp;
668 550913     frame.rval = JSVAL_VOID;
669 550913     frame.flags = flags;
670 550913     thisp = JSVAL_TO_OBJECT(vp[1]);
671
672     /*
673      * A callee must be an object reference, unless its |this| parameter
674      * implements the __noSuchMethod__ method, in which case that method will
675      * be called like so:
676      *
677      *   thisp.__noSuchMethod__(id, args)
678      *
679      * where id is the name of the method that this invocation attempted to
680      * call by name, and args is an Array containing this invocation's actual
681      * parameters.
682      */
683 550913     if (JSVAL_IS_PRIMITIVE(v)) {
684 #if JS_HAS_NO_SUCH_METHOD
685 0         jsbytecode *pc;
686 0         jsatomid atomIndex;
687 0         JSAtom *atom;
688 0         JSObject *argsobj;
689 0         JSArena *a;
690
691 0         if (!fp->script || (flags & JSINVOKE_INTERNAL))
692 0             goto bad;
693
694         /*
695          * We must ComputeThis here to censor Call objects; performance hit,
696          * but at least it's idempotent.
697          *
698          * Normally, we call ComputeThis after all frame members have been
699          * set, and in particular, after any revision of the callee value at
700          * *vp  due to clasp->convert (see below).  This matters because
701          * ComputeThis may access *vp via fp->argv[-2], to follow the parent
702          * chain to a global object to use as the |this| parameter.
703          *
704          * Obviously, here in the JSVAL_IS_PRIMITIVE(v) case, there can't be
705          * any such defaulting of |this| to callee (v, *vp) ancestor.
706          */
707 0         frame.argv = vp + 2;
708 0         ok = ComputeThis(cx, thisp, &frame);
709 0         if (!ok)
710 0             goto out2;
711 0         thisp = frame.thisp;
712
713 0         ok = OBJ_GET_PROPERTY(cx, thisp,
714                               (jsid)cx->runtime->atomState.noSuchMethodAtom,
715                               &v);
716 0         if (!ok)
717 0             goto out2;
718 0         if (JSVAL_IS_PRIMITIVE(v))
719 0             goto bad;
720
721 0         pc = (jsbytecode *) vp[-(intN)fp->script->depth];
722 0         switch ((JSOp) *pc) {
723           case JSOP_NAME:
724           case JSOP_GETPROP:
725 0             atomIndex = GET_ATOM_INDEX(pc);
726 0             atom = js_GetAtom(cx, &fp->script->atomMap, atomIndex);
727 0             argsobj = js_NewArrayObject(cx, argc, vp + 2);
728 0             if (!argsobj) {
729 0                 ok = JS_FALSE;
730 0                 goto out2;
731             }
732
733 0             sp = vp + 4;
734 0             if (argc < 2) {
735 0                 a = cx->stackPool.current;
736 0                 if ((jsuword)sp > a->limit) {
737                     /*
738                      * Arguments must be contiguous, and must include argv[-1]
739                      * and argv[-2], so allocate more stack, advance sp, and
740                      * set newsp[1] to thisp (vp[1]).  The other argv elements
741                      * will be set below, using negative indexing from sp.
742                      */
743 0                     newsp = js_AllocRawStack(cx, 4, NULL);
744 0                     if (!newsp) {
745 0                         ok = JS_FALSE;
746 0                         goto out2;
747                     }
748 0                     newsp[1] = OBJECT_TO_JSVAL(thisp);
749 0                     sp = newsp + 4;
750 0                 } else if ((jsuword)sp > a->avail) {
751                     /*
752                      * Inline, optimized version of JS_ARENA_ALLOCATE to claim
753                      * the small number of words not already allocated as part
754                      * of the caller's operand stack.
755                      */
756                     JS_ArenaCountAllocation(pool, (jsuword)sp - a->avail);
757 0                     a->avail = (jsuword)sp;
758                 }
759             }
760
761 0             sp[-4] = v;
762 0             JS_ASSERT(sp[-3] == OBJECT_TO_JSVAL(thisp));
763 0             sp[-2] = ATOM_KEY(atom);
764 0             sp[-1] = OBJECT_TO_JSVAL(argsobj);
765 0             fp->sp = sp;
766 0             argc = 2;
767             break;
768
769           default:
770 550913             goto bad;
771         }
772 #else
773         goto bad;
774 #endif
775     }
776
777 550913     funobj = JSVAL_TO_OBJECT(v);
778 550913     parent = OBJ_GET_PARENT(cx, funobj);
779 550913     clasp = OBJ_GET_CLASS(cx, funobj);
780 550913     if (clasp != &js_FunctionClass) {
781         /* Function is inlined, all other classes use object ops. */
782 0         ops = funobj->map->ops;
783
784         /*
785          * XXX
786          * Try converting to function, for closure and API compatibility.
787          * We attempt the conversion under all circumstances for 1.2, but
788          * only if there is a call op defined otherwise.
789          */
790 0         if (cx->version == JSVERSION_1_2 ||
791             ((ops == &js_ObjectOps) ? clasp->call : ops->call)) {
792 0             ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
793 0             if (!ok)
794 0                 goto out2;
795
796 0             if (JSVAL_IS_FUNCTION(cx, v)) {
797                 /* Make vp refer to funobj to keep it available as argv[-2]. */
798 0                 *vp = v;
799 0                 funobj = JSVAL_TO_OBJECT(v);
800 0                 parent = OBJ_GET_PARENT(cx, funobj);
801 0                 goto have_fun;
802             }
803         }
804 0         fun = NULL;
805 0         script = NULL;
806 0         minargs = nvars = 0;
807
808         /* Try a call or construct native object op. */
809 0         native = (flags & JSINVOKE_CONSTRUCT) ? ops->construct : ops->call;
810 0         if (!native)
811 0             goto bad;
812     } else {
813 have_fun:
814         /* Get private data and set derived locals from it. */
815 550913         fun = (JSFunction *) JS_GetPrivate(cx, funobj);
816 550913         native = fun->native;
817 550913         script = fun->script;
818 550913         minargs = fun->nargs + fun->extra;
819 550913         nvars = fun->nvars;
820
821         /* Handle bound method special case. */
822 550913         if (fun->flags & JSFUN_BOUND_METHOD)
823 0             thisp = parent;
824     }
825
826     /* Initialize the rest of frame, except for sp (set by SAVE_SP later). */
827 550913     frame.varobj = NULL;
828 550913     frame.callobj = frame.argsobj = NULL;
829 550913     frame.script = script;
830 550913     frame.fun = fun;
831 550913     frame.argc = argc;
832 550913     frame.argv = sp - argc;
833 550913     frame.nvars = nvars;
834 550913     frame.vars = sp;
835 550913     frame.down = fp;
836 550913     frame.annotation = NULL;
837 550913     frame.scopeChain = NULL;    /* set below for real, after cx->fp is set */
838 550913     frame.pc = NULL;
839 550913     frame.spbase = NULL;
840 550913     frame.sharpDepth = 0;
841 550913     frame.sharpArray = NULL;
842 550913     frame.dormantNext = NULL;
843 550913     frame.objAtomMap = NULL;
844
845     /* Compute the 'this' parameter and store it in frame as frame.thisp. */
846 550913     ok = ComputeThis(cx, thisp, &frame);
847 550913     if (!ok)
848 0         goto out2;
849
850     /* From here on, control must flow through label out: to return. */
851 550913     cx->fp = &frame;
852
853     /* Init these now in case we goto out before first hook call. */
854 550913     hook = cx->runtime->callHook;
855 550913     hookData = NULL;
856
857     /* Check for missing arguments expected by the function. */
858 550913     nslots = (intN)((argc < minargs) ? minargs - argc : 0);
859 550913     if (nslots) {
860         /* All arguments must be contiguous, so we may have to copy actuals. */
861 45820         nalloc = nslots;
862 45820         limit = (jsval *) cx->stackPool.current->limit;
863 45820         if (sp + nslots > limit) {
864             /* Hit end of arena: we have to copy argv[-2..(argc+nslots-1)]. */
865 0             nalloc += 2 + argc;
866         } else {
867             /* Take advantage of surplus slots in the caller's frame depth. */
868 45820             surplus = (jsval *)mark - sp;
869 45820             JS_ASSERT(surplus >= 0);
870 45820             nalloc -= surplus;
871         }
872
873         /* Check whether we have enough space in the caller's frame. */
874 45820         if (nalloc > 0) {
875             /* Need space for actuals plus missing formals minus surplus. */
876 303             newsp = js_AllocRawStack(cx, (uintN)nalloc, NULL);
877 303             if (!newsp) {
878 0                 ok = JS_FALSE;
879 0                 goto out;
880             }
881
882             /* If we couldn't allocate contiguous args, copy actuals now. */
883 303             if (newsp != mark) {
884 0                 JS_ASSERT(sp + nslots > limit);
885 0                 JS_ASSERT(2 + argc + nslots == (uintN)nalloc);
886 0                 *newsp++ = vp[0];
887 0                 *newsp++ = vp[1];
888 0                 if (argc)
889 0                     memcpy(newsp, frame.argv, argc * sizeof(jsval));
890 0                 frame.argv = newsp;
891 0                 sp = frame.vars = newsp + argc;
892             }
893         }
894
895         /* Advance frame.vars to make room for the missing args. */
896 45820         frame.vars += nslots;
897
898         /* Push void to initialize missing args. */
899 91640         while (--nslots >= 0)
900 45820             PUSH(JSVAL_VOID);
901     }
902
903     /* Now allocate stack space for local variables. */
904 550913     nslots = (intN)frame.nvars;
905 550913     if (nslots) {
906 242         surplus = (intN)((jsval *)cx->stackPool.current->avail - frame.vars);
907 242         if (surplus < nslots) {
908 242             newsp = js_AllocRawStack(cx, (uintN)nslots, NULL);
909 242             if (!newsp) {
910 0                 ok = JS_FALSE;
911 0                 goto out;
912             }
913 242             if (newsp != sp) {
914                 /* NB: Discontinuity between argv and vars. */
915 0                 sp = frame.vars = newsp;
916             }
917         }
918
919         /* Push void to initialize local variables. */
920 484         while (--nslots >= 0)
921 242             PUSH(JSVAL_VOID);
922     }
923
924     /* Store the current sp in frame before calling fun. */
925 550913     SAVE_SP(&frame);
926
927     /* call the hook if present */
928 550913     if (hook && (native || script))
929 0         hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->callHookData);
930
931     /* Call the function, either a native method or an interpreted script. */
932 550913     if (native) {
933 #if JS_HAS_LVALUE_RETURN
934         /* Set by JS_SetCallReturnValue2, used to return reference types. */
935 550671         cx->rval2set = JS_FALSE;
936 #endif
937
938         /* If native, use caller varobj and scopeChain for eval. */
939 550671         frame.varobj = fp->varobj;
940 550671         frame.scopeChain = fp->scopeChain;
941 550671         ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
942         JS_RUNTIME_METER(cx->runtime, nativeCalls);
943 242     } else if (script) {
944         /* Use parent scope so js_GetCallObject can find the right "Call". */
945 242         frame.scopeChain = parent;
946 242         if (fun->flags & JSFUN_HEAVYWEIGHT) {
947 #if JS_HAS_CALL_OBJECT
948             /* Scope with a call object parented by the callee's parent. */
949 0             if (!js_GetCallObject(cx, &frame, parent)) {
950 0                 ok = JS_FALSE;
951 0                 goto out;
952             }
953 #else
954             /* Bad old code used the function as a proxy for all calls to it. */
955             frame.scopeChain = funobj;
956 #endif
957         }
958 242         ok = js_Interpret(cx, &v);
959     } else {
960         /* fun might be onerror trying to report a syntax error in itself. */
961 0         frame.scopeChain = NULL;
962 0         ok = JS_TRUE;
963     }
964
965 out:
966 550913     if (hookData) {
967 0         hook = cx->runtime->callHook;
968 0         if (hook)
969 0             hook(cx, &frame, JS_FALSE, &ok, hookData);
970     }
971 #if JS_HAS_CALL_OBJECT
972     /* If frame has a call object, sync values and clear back-pointer. */
973 550913     if (frame.callobj)
974 0         ok &= js_PutCallObject(cx, &frame);
975 #endif
976 #if JS_HAS_ARGS_OBJECT
977     /* If frame has an arguments object, sync values and clear back-pointer. */
978 550913     if (frame.argsobj)
979 0         ok &= js_PutArgsObject(cx, &frame);
980 #endif
981
982     /* Restore cx->fp now that we're done releasing frame objects. */
983 550913     cx->fp = fp;
984
985 out2:
986     /* Pop everything we may have allocated off the stack. */
987 550913     JS_ARENA_RELEASE(&cx->stackPool, mark);
988
989     /* Store the return value and restore sp just above it. */
990 550913     *vp = frame.rval;
991 550913     fp->sp = vp + 1;
992
993     /*
994      * Store the location of the JSOP_CALL or JSOP_EVAL that generated the
995      * return value, but only if this is an external (compiled from script
996      * source) call that has stack budget for the generating pc.
997      */
998 550913     if (fp->script && !(flags & JSINVOKE_INTERNAL))
999 480781         vp[-(intN)fp->script->depth] = (jsval)fp->pc;
1000 550913     return ok;
1001
1002 bad:
1003 0     js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_CONSTRUCT);
1004 0     ok = JS_FALSE;
1005 0     goto out2;
1006 }
1007
1008 JSBool
1009 js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
1010                   uintN argc, jsval *argv, jsval *rval)
1011 70132 {
1012 70132     JSStackFrame *fp, *oldfp, frame;
1013 70132     jsval *oldsp, *sp;
1014 70132     void *mark;
1015 70132     uintN i;
1016 70132     JSBool ok;
1017
1018 70132     fp = oldfp = cx->fp;
1019 70132     if (!fp) {
1020 17         memset(&frame, 0, sizeof frame);
1021 17         cx->fp = fp = &frame;
1022     }
1023 70132     oldsp = fp->sp;
1024 70132     sp = js_AllocStack(cx, 2 + argc, &mark);
1025 70132     if (!sp) {
1026 0         ok = JS_FALSE;
1027 0         goto out;
1028     }
1029
1030 70132     PUSH(fval);
1031 70132     PUSH(OBJECT_TO_JSVAL(obj));
1032 92762     for (i = 0; i < argc; i++)
1033 22630         PUSH(argv[i]);
1034 70132     fp->sp = sp;
1035 70132     ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL);
1036 70132     if (ok) {
1037 70132         RESTORE_SP(fp);
1038 70132         *rval = POP_OPND();
1039     }
1040
1041 70132     js_FreeStack(cx, mark);
1042 out:
1043 70132     fp->sp = oldsp;
1044 70132     if (oldfp != fp)
1045 17         cx->fp = oldfp;
1046
1047 70132     return ok;
1048 }
1049
1050 JSBool
1051 js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
1052                     JSAccessMode mode, uintN argc, jsval *argv, jsval *rval)
1053 0 {
1054     /*
1055      * Check general (not object-ops/class-specific) access from the running
1056      * script to obj.id only if id has a scripted getter or setter that we're
1057      * about to invoke.  If we don't check this case, nothing else will -- no
1058      * other native code has the chance to check.
1059      *
1060      * Contrast this non-native (scripted) case with native getter and setter
1061      * accesses, where the native itself must do an access check, if security
1062      * policies requires it.  We make a checkAccess or checkObjectAccess call
1063      * back to the embedding program only in those cases where we're not going
1064      * to call an embedding-defined native function, getter, setter, or class
1065      * hook anyway.  Where we do call such a native, there's no need for the
1066      * engine to impose a separate access check callback on all embeddings --
1067      * many embeddings have no security policy at all.
1068      */
1069 0     JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE);
1070 0     if (cx->runtime->checkObjectAccess &&
1071         JSVAL_IS_FUNCTION(cx, fval) &&
1072         ((JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval)))->script &&
1073         !cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode,
1074                                         &fval)) {
1075 0         return JS_FALSE;
1076     }
1077
1078 0     return js_InternalCall(cx, obj, fval, argc, argv, rval);
1079 }
1080
1081 JSBool
1082 js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
1083            JSStackFrame *down, uintN special, jsval *result)
1084 1248 {
1085 1248     JSStackFrame *oldfp, frame;
1086 1248     JSObject *obj, *tmp;
1087 1248     JSBool ok;
1088 1248     JSInterpreterHook hook;
1089 1248     void *hookData;
1090
1091 1248     hook = cx->runtime->executeHook;
1092 1248     hookData = NULL;
1093 1248     oldfp = cx->fp;
1094 1248     frame.callobj = frame.argsobj = NULL;
1095 1248     frame.script = script;
1096 1248     if (down) {
1097         /* Propagate arg/var state for eval and the debugger API. */
1098 0         frame.varobj = down->varobj;
1099 0         frame.fun = down->fun;
1100 0         frame.thisp = down->thisp;
1101 0         frame.argc = down->argc;
1102 0         frame.argv = down->argv;
1103 0         frame.nvars = down->nvars;
1104 0         frame.vars = down->vars;
1105 0         frame.annotation = down->annotation;
1106 0         frame.sharpArray = down->sharpArray;
1107     } else {
1108 1248         obj = chain;
1109 1248         if (cx->options & JSOPTION_VAROBJFIX) {
1110 0             while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
1111 0                 obj = tmp;
1112         }
1113 1248         frame.varobj = obj;
1114 1248         frame.fun = NULL;
1115 1248         frame.thisp = chain;
1116 1248         frame.argc = frame.nvars = 0;
1117 1248         frame.argv = frame.vars = NULL;
1118 1248         frame.annotation = NULL;
1119 1248         frame.sharpArray = NULL;
1120     }
1121 1248     frame.rval = JSVAL_VOID;
1122 1248     frame.down = down;
1123 1248     frame.scopeChain = chain;
1124 1248     frame.pc = NULL;
1125 1248     frame.sp = oldfp ? oldfp->sp : NULL;
1126 1248     frame.spbase = NULL;
1127 1248     frame.sharpDepth = 0;
1128 1248     frame.flags = special;
1129 1248     frame.dormantNext = NULL;
1130 1248     frame.objAtomMap = NULL;
1131
1132     /*
1133      * Here we wrap the call to js_Interpret with code to (conditionally)
1134      * save and restore the old stack frame chain into a chain of 'dormant'
1135      * frame chains.  Since we are replacing cx->fp, we were running into
1136      * the problem that if GC was called under this frame, some of the GC
1137      * things associated with the old frame chain (available here only in
1138      * the C variable 'oldfp') were not rooted and were being collected.
1139      *
1140      * So, now we preserve the links to these 'dormant' frame chains in cx
1141      * before calling js_Interpret and cleanup afterwards.  The GC walks
1142      * these dormant chains and marks objects in the same way that it marks
1143      * objects in the primary cx->fp chain.
1144      */
1145 1248     if (oldfp && oldfp != down) {
1146 1248         JS_ASSERT(!oldfp->dormantNext);
1147 1248         oldfp->dormantNext = cx->dormantFrameChain;
1148 1248         cx->dormantFrameChain = oldfp;
1149     }
1150
1151 1248     cx->fp = &frame;
1152 1248     if (hook)
1153 0         hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->executeHookData);
1154
1155 1248     ok = js_Interpret(cx, result);
1156
1157 1248     if (hookData) {
1158 0         hook = cx->runtime->executeHook;
1159 0         if (hook)
1160 0             hook(cx, &frame, JS_FALSE, &ok, hookData);
1161     }
1162 1248     cx->fp = oldfp;
1163
1164 1248     if (oldfp && oldfp != down) {
1165 1248         JS_ASSERT(cx->dormantFrameChain == oldfp);
1166 1248         cx->dormantFrameChain = oldfp->dormantNext;
1167 1248         oldfp->dormantNext = NULL;
1168     }
1169
1170 1248     return ok;
1171 }
1172
1173 #if JS_HAS_EXPORT_IMPORT
1174 /*
1175  * If id is JSVAL_VOID, import all exported properties from obj.
1176  */
1177 static JSBool
1178 ImportProperty(JSContext *cx, JSObject *obj, jsid id)
1179 {
1180     JSBool ok;
1181     JSIdArray *ida;
1182     JSProperty *prop;
1183     JSObject *obj2, *target, *funobj, *closure;
1184     JSString *str;
1185     uintN attrs;
1186     jsint i;
1187     jsval value;
1188
1189     if (JSVAL_IS_VOID(id)) {
1190         ida = JS_Enumerate(cx, obj);
1191         if (!ida)
1192             return JS_FALSE;
1193         ok = JS_TRUE;
1194         if (ida->length == 0)
1195             goto out;
1196     } else {
1197         ida = NULL;
1198         if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1199             return JS_FALSE;
1200         if (!prop) {
1201             str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
1202                                              ID_TO_VALUE(id), NULL);
1203             if (str)
1204                 js_ReportIsNotDefined(cx, JS_GetStringBytes(str));
1205             return JS_FALSE;
1206         }
1207         ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
1208         OBJ_DROP_PROPERTY(cx, obj2, prop);
1209         if (!ok)
1210             return JS_FALSE;
1211         if (!(attrs & JSPROP_EXPORTED)) {
1212             str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
1213                                              ID_TO_VALUE(id), NULL);
1214             if (str) {
1215                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1216                                      JSMSG_NOT_EXPORTED,
1217                                      JS_GetStringBytes(str));
1218             }
1219             return JS_FALSE;
1220         }
1221     }
1222
1223     target = cx->fp->varobj;
1224     i = 0;
1225     do {
1226         if (ida) {
1227             id = ida->vector[i];
1228             ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs);
1229             if (!ok)
1230                 goto out;
1231             if (!(attrs & JSPROP_EXPORTED))
1232                 continue;
1233         }
1234         ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_IMPORT, &value, &attrs);
1235         if (!ok)
1236             goto out;
1237         if (JSVAL_IS_FUNCTION(cx, value)) {
1238             funobj = JSVAL_TO_OBJECT(value);
1239             closure = js_CloneFunctionObject(cx, funobj, obj);
1240             if (!closure) {
1241                 ok = JS_FALSE;
1242                 goto out;
1243             }
1244             value = OBJECT_TO_JSVAL(closure);
1245         }
1246
1247         /*
1248          * Handle the case of importing a property that refers to a local
1249          * variable or formal parameter of a function activation.  These
1250          * properties are accessed by opcodes using stack slot numbers
1251          * generated by the compiler rather than runtime name-lookup.  These
1252          * local references, therefore, bypass the normal scope chain lookup.
1253          * So, instead of defining a new property in the activation object,
1254          * modify the existing value in the stack slot.
1255          */
1256         if (OBJ_GET_CLASS(cx, target) == &js_CallClass) {
1257             ok = OBJ_LOOKUP_PROPERTY(cx, target, id, &obj2, &prop);
1258             if (!ok)
1259                 goto out;
1260         } else {
1261             prop = NULL;
1262         }
1263         if (prop && target == obj2) {
1264             ok = OBJ_SET_PROPERTY(cx, target, id, &value);
1265         } else {
1266             ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL,
1267                                      attrs & ~JSPROP_EXPORTED,
1268                                      NULL);
1269         }
1270         if (prop)
1271             OBJ_DROP_PROPERTY(cx, obj2, prop);
1272         if (!ok)
1273             goto out;
1274     } while (ida && ++i < ida->length);
1275
1276 out:
1277     if (ida)
1278         JS_DestroyIdArray(cx, ida);
1279     return ok;
1280 }
1281 #endif /* JS_HAS_EXPORT_IMPORT */
1282
1283 JSBool
1284 js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
1285                       JSBool *foundp)
1286 7541 {
1287 7541     JSObject *obj2;
1288 7541     JSProperty *prop;
1289 7541     JSBool ok;
1290 7541     uintN oldAttrs, report;
1291 7541     JSBool isFunction;
1292 7541     jsval value;
1293 7541     const char *type, *name;
1294
1295 7541     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1296 0         return JS_FALSE;
1297 7541     *foundp = (prop != NULL);
1298 7541     if (!prop)
1299 2736         return JS_TRUE;
1300 4805     ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs);
1301 4805     OBJ_DROP_PROPERTY(cx, obj2, prop);
1302 4805     if (!ok)
1303 0         return JS_FALSE;
1304
1305     /* If either property is readonly, we have an error. */
1306 4805     report = ((oldAttrs | attrs) & JSPROP_READONLY)
1307              ? JSREPORT_ERROR
1308              : JSREPORT_WARNING | JSREPORT_STRICT;
1309
1310 4805     if (report != JSREPORT_ERROR) {
1311         /*
1312          * Allow redeclaration of variables and functions, but insist that the
1313          * new value is not a getter if the old value was, ditto for setters --
1314          * unless prop is impermanent (in which case anyone could delete it and
1315          * redefine it, willy-nilly).
1316          */
1317 4805         if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1318 4805             return JS_TRUE;
1319 0         if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
1320 0             return JS_TRUE;
1321 0         if (!(oldAttrs & JSPROP_PERMANENT))
1322 0             return JS_TRUE;
1323 0         report = JSREPORT_ERROR;
1324     }
1325
1326 0     isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
1327 0     if (!isFunction) {
1328 0         if (!OBJ_GET_PROPERTY(cx, obj, id, &value))
1329 0             return JS_FALSE;
1330 0         isFunction = JSVAL_IS_FUNCTION(cx, value);
1331     }
1332 0     type = (oldAttrs & attrs & JSPROP_GETTER)
1333            ? js_getter_str
1334            : (oldAttrs & attrs & JSPROP_SETTER)
1335            ? js_setter_str
1336            : (oldAttrs & JSPROP_READONLY)
1337            ? js_const_str
1338            : isFunction
1339            ? js_function_str
1340            : js_var_str;
1341 0     name = js_AtomToPrintableString(cx, (JSAtom *)id);
1342 0     if (!name)
1343 0         return JS_FALSE;
1344 0     return JS_ReportErrorFlagsAndNumber(cx, report,
1345                                         js_GetErrorMessage, NULL,
1346                                         JSMSG_REDECLARED_VAR,
1347                                         type, name);
1348 }
1349
1350 #ifndef MAX_INTERP_LEVEL
1351 #if defined(XP_OS2)
1352 #define MAX_INTERP_LEVEL 250
1353 #elif defined _MSC_VER && _MSC_VER <= 800
1354 #define MAX_INTERP_LEVEL 30
1355 #else
1356 #define MAX_INTERP_LEVEL 1000
1357 #endif
1358 #endif
1359
1360 #define MAX_INLINE_CALL_COUNT 1000
1361
1362 JSBool
1363 js_Interpret(JSContext *cx, jsval *result)
1364 1490 {
1365 1490     JSRuntime *rt;
1366 1490     JSStackFrame *fp;
1367 1490     JSScript *script;
1368 1490     uintN inlineCallCount;
1369 1490     JSObject *obj, *obj2, *proto, *parent;
1370 1490     JSVersion currentVersion, originalVersion;
1371 1490     JSBranchCallback onbranch;
1372 1490     JSBool ok, cond;
1373 1490     JSTrapHandler interruptHandler;
1374 1490     jsint depth, len;
1375 1490     jsval *sp, *newsp;
1376 1490     void *mark;
1377 1490     jsbytecode *pc, *pc2, *endpc;
1378 1490     JSOp op, op2;
1379 1490     const JSCodeSpec *cs;
1380 1490     JSAtom *atom;
1381 1490     uintN argc, slot, attrs;
1382 1490     jsval *vp, lval, rval, ltmp, rtmp;
1383 1490     jsid id;
1384 1490     JSObject *withobj, *origobj, *propobj;
1385 1490     jsval iter_state;
1386 1490     JSProperty *prop;
1387 1490     JSScopeProperty *sprop;
1388 1490     JSString *str, *str2;
1389 1490     jsint i, j;
1390 1490     jsdouble d, d2;
1391 1490     JSClass *clasp, *funclasp;
1392 1490     JSFunction *fun;
1393 1490     JSType type;
1394 #ifdef DEBUG
1395     FILE *tracefp;
1396 #endif
1397 #if JS_HAS_EXPORT_IMPORT
1398     JSIdArray *ida;
1399 #endif
1400 #if JS_HAS_SWITCH_STATEMENT
1401 1490     jsint low, high, off, npairs;
1402 1490     JSBool match;
1403 #endif
1404 #if JS_HAS_GETTER_SETTER
1405 1490     JSPropertyOp getter, setter;
1406 #endif
1407 1490     int stackDummy;
1408
1409 1490     *result = JSVAL_VOID;
1410 1490     rt = cx->runtime;
1411
1412     /* Set registerized frame pointer and derived script pointer. */
1413 1490     fp = cx->fp;
1414 1490     script = fp->script;
1415
1416     /* Count of JS function calls that nest in this C js_Interpret frame. */
1417 1490     inlineCallCount = 0;
1418
1419     /*
1420      * Optimized Get and SetVersion for proper script language versioning.
1421      *
1422      * If any native method or JSClass/JSObjectOps hook calls JS_SetVersion
1423      * and changes cx->version, the effect will "stick" and we will stop
1424      * maintaining currentVersion.  This is relied upon by testsuites, for
1425      * the most part -- web browsers select version before compiling and not
1426      * at run-time.
1427      */
1428 1490     currentVersion = script->version;
1429 1490     originalVersion = cx->version;
1430 1490     if (currentVersion != originalVersion)
1431 0         JS_SetVersion(cx, currentVersion);
1432
1433     /*
1434      * Prepare to call a user-supplied branch handler, and abort the script
1435      * if it returns false.
1436      */
1437 1490     onbranch = cx->branchCallback;
1438 1490     ok = JS_TRUE;
1439 #define CHECK_BRANCH(len)                                                     \
1440     JS_BEGIN_MACRO                                                            \
1441         if (len <= 0 && onbranch) {                                           \
1442             SAVE_SP(fp);                                                      \
1443             if (!(ok = (*onbranch)(cx, script)))                              \
1444                 goto out;                                                     \
1445         }                                                                     \
1446     JS_END_MACRO
1447
1448     /*
1449      * Load the debugger's interrupt hook here and after calling out to native
1450      * functions (but not to getters, setters, or other native hooks), so we do
1451      * not have to reload it each time through the interpreter loop -- we hope
1452      * the compiler can keep it in a register.
1453      * XXX if it spills, we still lose
1454      */
1455 #define LOAD_INTERRUPT_HANDLER(rt)  (interruptHandler = (rt)->interruptHandler)
1456
1457 1490     LOAD_INTERRUPT_HANDLER(rt);
1458
1459 1490     pc = script->code;
1460 1490     endpc = pc + script->length;
1461 1490     depth = (jsint) script->depth;
1462 1490     len = -1;
1463
1464     /* Check for too much js_Interpret nesting, or too deep a C stack. */
1465 1490     if (++cx->interpLevel == MAX_INTERP_LEVEL ||
1466         !JS_CHECK_STACK_SIZE(cx, stackDummy)) {
1467 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
1468 0         ok = JS_FALSE;
1469 0         goto out;
1470     }
1471
1472     /*
1473      * Allocate operand and pc stack slots for the script's worst-case depth.
1474      */
1475 1490     newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark);
1476 1490     if (!newsp) {
1477 0         ok = JS_FALSE;
1478 0         goto out;
1479     }
1480 1490     sp = newsp + depth;
1481 1490     fp->spbase = sp;
1482 1490     SAVE_SP(fp);
1483
1484 7446540     while (pc < endpc) {
1485 7414510         fp->pc = pc;
1486 7414510         op = (JSOp) *pc;
1487       do_op:
1488 7414510         cs = &js_CodeSpec[op];
1489 7414510         len = cs->length;
1490
1491 #ifdef DEBUG
1492         tracefp = (FILE *) cx->tracefp;
1493         if (tracefp) {
1494             intN nuses, n;
1495
1496             fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, script, pc));
1497             js_Disassemble1(cx, script, pc,
1498                             PTRDIFF(pc, script->code, jsbytecode), JS_FALSE,
1499                             tracefp);
1500             nuses = cs->nuses;
1501             if (nuses) {
1502                 SAVE_SP(fp);
1503                 for (n = -nuses; n < 0; n++) {
1504                     str = js_DecompileValueGenerator(cx, n, sp[n], NULL);
1505                     if (str != NULL) {
1506                         fprintf(tracefp, "%s %s",
1507                                 (n == -nuses) ? "  inputs:" : ",",
1508                                 JS_GetStringBytes(str));
1509                     }
1510                 }
1511                 fprintf(tracefp, " @ %d\n", sp - fp->spbase);
1512             }
1513         }
1514 #endif
1515
1516 7414510         if (interruptHandler) {
1517 0             SAVE_SP(fp);
1518             switch (interruptHandler(cx, script, pc, &rval,
1519 0                                      rt->interruptHandlerData)) {
1520               case JSTRAP_ERROR:
1521 0                 ok = JS_FALSE;
1522 0                 goto out;
1523               case JSTRAP_CONTINUE:
1524 0                 break;
1525               case JSTRAP_RETURN:
1526 0                 fp->rval = rval;
1527 0                 goto out;
1528 #if JS_HAS_EXCEPTIONS
1529               case JSTRAP_THROW:
1530 0                 cx->throwing = JS_TRUE;
1531 0                 cx->exception = rval;
1532 0                 ok = JS_FALSE;
1533 0                 goto out;
1534 #endif /* JS_HAS_EXCEPTIONS */
1535               default:;
1536             }
1537 0             LOAD_INTERRUPT_HANDLER(rt);
1538         }
1539
1540 7414510         switch (op) {
1541           case JSOP_NOP:
1542 4298             break;
1543
1544           case JSOP_GROUP:
1545 4298             obj = NULL;
1546 4298             break;
1547
1548           case JSOP_PUSH:
1549 32641             PUSH_OPND(JSVAL_VOID);
1550 32641             break;
1551
1552           case JSOP_POP:
1553 462043             sp--;
1554 462043             break;
1555
1556           case JSOP_POP2:
1557 24693             sp -= 2;
1558 24693             break;
1559
1560           case JSOP_SWAP:
1561             /*
1562              * N.B. JSOP_SWAP doesn't swap the corresponding generating pcs
1563              * for the operands it swaps.
1564              */
1565 0             ltmp = sp[-1];
1566 0             sp[-1] = sp[-2];
1567 0             sp[-2] = ltmp;
1568 0             break;
1569
1570           case JSOP_POPV:
1571 277201             *result = POP_OPND();
1572 277201             break;
1573
1574           case JSOP_ENTERWITH:
1575 0             rval = FETCH_OPND(-1);
1576 0             VALUE_TO_OBJECT(cx, rval, obj);
1577 0             withobj = js_NewObject(cx, &js_WithClass, obj, fp->scopeChain);
1578 0             if (!withobj)
1579 0                 goto out;
1580 0             fp->scopeChain = withobj;
1581 0             STORE_OPND(-1, OBJECT_TO_JSVAL(withobj));
1582 0             break;
1583
1584           case JSOP_LEAVEWITH:
1585 0             rval = POP_OPND();
1586 0             JS_ASSERT(JSVAL_IS_OBJECT(rval));
1587 0             withobj = JSVAL_TO_OBJECT(rval);
1588 0             JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
1589
1590 0             rval = OBJ_GET_SLOT(cx, withobj, JSSLOT_PARENT);
1591 0             JS_ASSERT(JSVAL_IS_OBJECT(rval));
1592 0             fp->scopeChain = JSVAL_TO_OBJECT(rval);
1593 0             break;
1594
1595           case JSOP_SETRVAL:
1596 0             fp->rval = POP_OPND();
1597 0             break;
1598
1599           case JSOP_RETURN:
1600 84265             CHECK_BRANCH(-1);
1601 84265             fp->rval = POP_OPND();
1602             /* FALL THROUGH */
1603
1604           case JSOP_RETRVAL:    /* fp->rval already set */
1605 84265             if (inlineCallCount)
1606           inline_return:
1607             {
1608 114805                 JSInlineFrame *ifp = (JSInlineFrame *) fp;
1609 114805                 void *hookData = ifp->hookData;
1610
1611 114805                 if (hookData) {
1612 0                     JSInterpreterHook hook = cx->runtime->callHook;
1613 0                     if (hook) {
1614 0                         hook(cx, fp, JS_FALSE, &ok, hookData);
1615 0                         LOAD_INTERRUPT_HANDLER(rt);
1616                     }
1617                 }
1618 #if JS_HAS_ARGS_OBJECT
1619 114805                 if (fp->argsobj)
1620 0                     ok &= js_PutArgsObject(cx, fp);
1621 #endif
1622
1623                 /* Restore context version only if callee hasn't set version. */
1624 114805                 if (cx->version == currentVersion) {
1625 114805                     currentVersion = ifp->callerVersion;
1626 114805                     if (currentVersion != cx->version)
1627 0                         JS_SetVersion(cx, currentVersion);
1628                 }
1629
1630                 /* Store the return value in the caller's operand frame. */
1631 114805                 vp = fp->argv - 2;
1632 114805                 *vp = fp->rval;
1633
1634                 /* Restore cx->fp and release the inline frame's space. */
1635 114805                 cx->fp = fp = fp->down;
1636 114805                 JS_ARENA_RELEASE(&cx->stackPool, ifp->mark);
1637
1638                 /* Restore sp to point just above the return value. */
1639 114805                 fp->sp = vp + 1;
1640 114805                 RESTORE_SP(fp);
1641
1642                 /* Restore the calling script's interpreter registers. */
1643 114805                 script = fp->script;
1644 114805                 depth = (jsint) script->depth;
1645 114805                 pc = fp->pc;
1646 114805                 endpc = script->code + script->length;
1647
1648                 /* Store the generating pc for the return value. */
1649 114805                 vp[-depth] = (jsval)pc;
1650
1651                 /* Set remaining variables for 'goto advance_pc'. */
1652 114805                 op = (JSOp) *pc;
1653 114805                 cs = &js_CodeSpec[op];
1654 114805                 len = cs->length;
1655
1656                 /* Resume execution in the calling frame. */
1657 114805                 inlineCallCount--;
1658 114805                 if (ok)
1659 114805                     goto advance_pc;
1660             }
1661 0             goto out;
1662
1663 #if JS_HAS_SWITCH_STATEMENT
1664           case JSOP_DEFAULT:
1665 0             (void) POP();
1666             /* FALL THROUGH */
1667 #endif
1668           case JSOP_GOTO:
1669 67773             len = GET_JUMP_OFFSET(pc);
1670 67773             CHECK_BRANCH(len);
1671 372834             break;
1672
1673           case JSOP_IFEQ:
1674 372834             POP_BOOLEAN(cx, rval, cond);
1675 372834             if (cond == JS_FALSE) {
1676 227473                 len = GET_JUMP_OFFSET(pc);
1677 227473                 CHECK_BRANCH(len);
1678             }
1679 0             break;
1680
1681           case JSOP_IFNE:
1682 0             POP_BOOLEAN(cx, rval, cond);
1683 0             if (cond != JS_FALSE) {
1684 0                 len = GET_JUMP_OFFSET(pc);
1685 0                 CHECK_BRANCH(len);
1686             }
1687 24960             break;
1688
1689           case JSOP_OR:
1690 24960             POP_BOOLEAN(cx, rval, cond);
1691 24960             if (cond == JS_TRUE) {
1692 4087                 len = GET_JUMP_OFFSET(pc);
1693 4087                 PUSH_OPND(rval);
1694             }
1695 4087             break;
1696
1697           case JSOP_AND:
1698 76691             POP_BOOLEAN(cx, rval, cond);
1699 76691             if (cond == JS_FALSE) {
1700 16458                 len = GET_JUMP_OFFSET(pc);
1701 16458                 PUSH_OPND(rval);
1702             }
1703 16458             break;
1704
1705
1706 #if JS_HAS_SWITCH_STATEMENT
1707           case JSOP_DEFAULTX:
1708 0             (void) POP();
1709             /* FALL THROUGH */
1710 #endif
1711           case JSOP_GOTOX:
1712 0             len = GET_JUMPX_OFFSET(pc);
1713 0             CHECK_BRANCH(len);
1714 0             break;
1715
1716           case JSOP_IFEQX:
1717 0             POP_BOOLEAN(cx, rval, cond);
1718 0             if (cond == JS_FALSE) {
1719 0                 len = GET_JUMPX_OFFSET(pc);
1720 0                 CHECK_BRANCH(len);
1721             }
1722 0             break;
1723
1724           case JSOP_IFNEX:
1725 0             POP_BOOLEAN(cx, rval, cond);
1726 0             if (cond != JS_FALSE) {
1727 0                 len = GET_JUMPX_OFFSET(pc);
1728 0                 CHECK_BRANCH(len);
1729             }
1730 0             break;
1731
1732           case JSOP_ORX:
1733 0             POP_BOOLEAN(cx, rval, cond);
1734 0             if (cond == JS_TRUE) {
1735 0                 len = GET_JUMPX_OFFSET(pc);
1736 0                 PUSH_OPND(rval);
1737             }
1738 0             break;
1739
1740           case JSOP_ANDX:
1741 0             POP_BOOLEAN(cx, rval, cond);
1742 0             if (cond == JS_FALSE) {
1743 0                 len = GET_JUMPX_OFFSET(pc);
1744 0                 PUSH_OPND(rval);
1745             }
1746 0             break;
1747
1748           case JSOP_TOOBJECT:
1749 24693             SAVE_SP(fp);
1750 24693             ok = js_ValueToObject(cx, FETCH_OPND(-1), &obj);
1751 24693             if (!ok)
1752 0                 goto out;
1753 24693             STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
1754 24693             break;
1755
1756 #define FETCH_ELEMENT_ID(n, id)                                               \
1757     JS_BEGIN_MACRO                                                            \
1758         /* If the index is not a jsint, atomize it. */                        \
1759         id = (jsid) FETCH_OPND(n);                                            \
1760         if (JSVAL_IS_INT(id)) {                                               \
1761             atom = NULL;                                                      \
1762         } else {                                                              \
1763             SAVE_SP(fp);                                                      \
1764             atom = js_ValueToStringAtom(cx, (jsval)id);                       \
1765             if (!atom) {                                                      \
1766                 ok = JS_FALSE;                                                \
1767                 goto out;                                                     \
1768             }                                                                 \
1769             id = (jsid)atom;                                                  \
1770         }                                                                     \
1771     JS_END_MACRO
1772
1773 #define POP_ELEMENT_ID(id)                                                    \
1774     JS_BEGIN_MACRO                                                            \
1775         FETCH_ELEMENT_ID(-1, id);                                             \
1776         sp--;                                                                 \
1777     JS_END_MACRO
1778
1779 #if JS_HAS_IN_OPERATOR
1780           case JSOP_IN:
1781 0             rval = FETCH_OPND(-1);
1782 0             if (JSVAL_IS_PRIMITIVE(rval)) {
1783 0                 str = js_DecompileValueGenerator(cx, -1, rval, NULL);
1784 0                 if (str) {
1785 0                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1786                                          JSMSG_IN_NOT_OBJECT,
1787                                          JS_GetStringBytes(str));
1788                 }
1789 0                 ok = JS_FALSE;
1790 0                 goto out;
1791             }
1792 0             sp--;
1793 0             obj = JSVAL_TO_OBJECT(rval);
1794 0             FETCH_ELEMENT_ID(-1, id);
1795 0             ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
1796 0             if (!ok)
1797 0                 goto out;
1798 0             STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL));
1799 0             if (prop)
1800 0