/[jscoverage]/trunk/js/jsfun.cpp
ViewVC logotype

Annotation of /trunk/js/jsfun.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (hide annotations)
Thu Oct 23 19:03:33 2008 UTC (10 years, 11 months ago) by siliconforks
File size: 83241 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


1 siliconforks 332 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2     * vim: set ts=8 sw=4 et tw=99:
3     *
4     * ***** BEGIN LICENSE BLOCK *****
5     * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6     *
7     * The contents of this file are subject to the Mozilla Public License Version
8     * 1.1 (the "License"); you may not use this file except in compliance with
9     * the License. You may obtain a copy of the License at
10     * http://www.mozilla.org/MPL/
11     *
12     * Software distributed under the License is distributed on an "AS IS" basis,
13     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14     * for the specific language governing rights and limitations under the
15     * License.
16     *
17     * The Original Code is Mozilla Communicator client code, released
18     * March 31, 1998.
19     *
20     * The Initial Developer of the Original Code is
21     * Netscape Communications Corporation.
22     * Portions created by the Initial Developer are Copyright (C) 1998
23     * the Initial Developer. All Rights Reserved.
24     *
25     * Contributor(s):
26     *
27     * Alternatively, the contents of this file may be used under the terms of
28     * either of the GNU General Public License Version 2 or later (the "GPL"),
29     * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30     * in which case the provisions of the GPL or the LGPL are applicable instead
31     * of those above. If you wish to allow use of your version of this file only
32     * under the terms of either the GPL or the LGPL, and not to allow others to
33     * use your version of this file under the terms of the MPL, indicate your
34     * decision by deleting the provisions above and replace them with the notice
35     * and other provisions required by the GPL or the LGPL. If you do not delete
36     * the provisions above, a recipient may use your version of this file under
37     * the terms of any one of the MPL, the GPL or the LGPL.
38     *
39     * ***** END LICENSE BLOCK ***** */
40    
41     /*
42     * JS function support.
43     */
44     #include "jsstddef.h"
45     #include <string.h>
46     #include "jstypes.h"
47     #include "jsbit.h"
48     #include "jsutil.h" /* Added by JSIFY */
49     #include "jsapi.h"
50     #include "jsarray.h"
51     #include "jsatom.h"
52     #include "jscntxt.h"
53     #include "jsversion.h"
54     #include "jsdbgapi.h"
55     #include "jsfun.h"
56     #include "jsgc.h"
57     #include "jsinterp.h"
58     #include "jslock.h"
59     #include "jsnum.h"
60     #include "jsobj.h"
61     #include "jsopcode.h"
62     #include "jsparse.h"
63     #include "jsscan.h"
64     #include "jsscope.h"
65     #include "jsscript.h"
66     #include "jsstr.h"
67     #include "jsexn.h"
68     #include "jsstaticcheck.h"
69    
70     #if JS_HAS_GENERATORS
71     # include "jsiter.h"
72     #endif
73    
74     #if JS_HAS_XDR
75     # include "jsxdrapi.h"
76     #endif
77    
78     /* Generic function/call/arguments tinyids -- also reflected bit numbers. */
79     enum {
80     CALL_ARGUMENTS = -1, /* predefined arguments local variable */
81     ARGS_LENGTH = -2, /* number of actual args, arity if inactive */
82     ARGS_CALLEE = -3, /* reference from arguments to active funobj */
83     FUN_ARITY = -4, /* number of formal parameters; desired argc */
84     FUN_NAME = -5, /* function name, "" if anonymous */
85     FUN_CALLER = -6 /* Function.prototype.caller, backward compat */
86     };
87    
88     #if JSFRAME_OVERRIDE_BITS < 8
89     # error "not enough override bits in JSStackFrame.flags!"
90     #endif
91    
92     #define TEST_OVERRIDE_BIT(fp, tinyid) \
93     ((fp)->flags & JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1)))
94    
95     #define SET_OVERRIDE_BIT(fp, tinyid) \
96     ((fp)->flags |= JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1)))
97    
98     JSBool
99     js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp)
100     {
101     JSObject *argsobj;
102    
103     if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
104     JS_ASSERT(fp->callobj);
105     return OBJ_GET_PROPERTY(cx, fp->callobj,
106     ATOM_TO_JSID(cx->runtime->atomState
107     .argumentsAtom),
108     vp);
109     }
110     argsobj = js_GetArgsObject(cx, fp);
111     if (!argsobj)
112     return JS_FALSE;
113     *vp = OBJECT_TO_JSVAL(argsobj);
114     return JS_TRUE;
115     }
116    
117     static JSBool
118     MarkArgDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
119     {
120     JSObject *argsobj;
121     jsval bmapval, bmapint;
122     size_t nbits, nbytes;
123     jsbitmap *bitmap;
124    
125     argsobj = fp->argsobj;
126     (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
127     nbits = fp->argc;
128     JS_ASSERT(slot < nbits);
129     if (JSVAL_IS_VOID(bmapval)) {
130     if (nbits <= JSVAL_INT_BITS) {
131     bmapint = 0;
132     bitmap = (jsbitmap *) &bmapint;
133     } else {
134     nbytes = JS_HOWMANY(nbits, JS_BITS_PER_WORD) * sizeof(jsbitmap);
135     bitmap = (jsbitmap *) JS_malloc(cx, nbytes);
136     if (!bitmap)
137     return JS_FALSE;
138     memset(bitmap, 0, nbytes);
139     bmapval = PRIVATE_TO_JSVAL(bitmap);
140     JS_SetReservedSlot(cx, argsobj, 0, bmapval);
141     }
142     } else {
143     if (nbits <= JSVAL_INT_BITS) {
144     bmapint = JSVAL_TO_INT(bmapval);
145     bitmap = (jsbitmap *) &bmapint;
146     } else {
147     bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval);
148     }
149     }
150     JS_SET_BIT(bitmap, slot);
151     if (bitmap == (jsbitmap *) &bmapint) {
152     bmapval = INT_TO_JSVAL(bmapint);
153     JS_SetReservedSlot(cx, argsobj, 0, bmapval);
154     }
155     return JS_TRUE;
156     }
157    
158     /* NB: Infallible predicate, false does not mean error/exception. */
159     static JSBool
160     ArgWasDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
161     {
162     JSObject *argsobj;
163     jsval bmapval, bmapint;
164     jsbitmap *bitmap;
165    
166     argsobj = fp->argsobj;
167     (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
168     if (JSVAL_IS_VOID(bmapval))
169     return JS_FALSE;
170     if (fp->argc <= JSVAL_INT_BITS) {
171     bmapint = JSVAL_TO_INT(bmapval);
172     bitmap = (jsbitmap *) &bmapint;
173     } else {
174     bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval);
175     }
176     return JS_TEST_BIT(bitmap, slot) != 0;
177     }
178    
179     JSBool
180     js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp)
181     {
182     jsval val;
183     JSObject *obj;
184     uintN slot;
185    
186     if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
187     JS_ASSERT(fp->callobj);
188     if (!OBJ_GET_PROPERTY(cx, fp->callobj,
189     ATOM_TO_JSID(cx->runtime->atomState
190     .argumentsAtom),
191     &val)) {
192     return JS_FALSE;
193     }
194     if (JSVAL_IS_PRIMITIVE(val)) {
195     obj = js_ValueToNonNullObject(cx, val);
196     if (!obj)
197     return JS_FALSE;
198     } else {
199     obj = JSVAL_TO_OBJECT(val);
200     }
201     return OBJ_GET_PROPERTY(cx, obj, id, vp);
202     }
203    
204     *vp = JSVAL_VOID;
205     if (JSID_IS_INT(id)) {
206     slot = (uintN) JSID_TO_INT(id);
207     if (slot < fp->argc) {
208     if (fp->argsobj && ArgWasDeleted(cx, fp, slot))
209     return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
210     *vp = fp->argv[slot];
211     } else {
212     /*
213     * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share
214     * storage between the formal parameter and arguments[k] for all
215     * fp->argc <= k && k < fp->fun->nargs. For example, in
216     *
217     * function f(x) { x = 42; return arguments[0]; }
218     * f();
219     *
220     * the call to f should return undefined, not 42. If fp->argsobj
221     * is null at this point, as it would be in the example, return
222     * undefined in *vp.
223     */
224     if (fp->argsobj)
225     return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
226     }
227     } else {
228     if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
229     if (fp->argsobj && TEST_OVERRIDE_BIT(fp, ARGS_LENGTH))
230     return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
231     *vp = INT_TO_JSVAL((jsint) fp->argc);
232     }
233     }
234     return JS_TRUE;
235     }
236    
237     JSObject *
238     js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
239     {
240     JSObject *argsobj, *global, *parent;
241    
242     /*
243     * We must be in a function activation; the function must be lightweight
244     * or else fp must have a variable object.
245     */
246     JS_ASSERT(fp->fun && (!(fp->fun->flags & JSFUN_HEAVYWEIGHT) || fp->varobj));
247    
248     /* Skip eval and debugger frames. */
249     while (fp->flags & JSFRAME_SPECIAL)
250     fp = fp->down;
251    
252     /* Create an arguments object for fp only if it lacks one. */
253     argsobj = fp->argsobj;
254     if (argsobj)
255     return argsobj;
256    
257     /* Link the new object to fp so it can get actual argument values. */
258     argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL, 0);
259     if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) {
260     cx->weakRoots.newborn[GCX_OBJECT] = NULL;
261     return NULL;
262     }
263    
264     /*
265     * Give arguments an intrinsic scope chain link to fp's global object.
266     * Since the arguments object lacks a prototype because js_ArgumentsClass
267     * is not initialized, js_NewObject won't assign a default parent to it.
268     *
269     * Therefore if arguments is used as the head of an eval scope chain (via
270     * a direct or indirect call to eval(program, arguments)), any reference
271     * to a standard class object in the program will fail to resolve due to
272     * js_GetClassPrototype not being able to find a global object containing
273     * the standard prototype by starting from arguments and following parent.
274     */
275     global = fp->scopeChain;
276     while ((parent = OBJ_GET_PARENT(cx, global)) != NULL)
277     global = parent;
278     STOBJ_SET_PARENT(argsobj, global);
279     fp->argsobj = argsobj;
280     return argsobj;
281     }
282    
283     static JSBool
284     args_enumerate(JSContext *cx, JSObject *obj);
285    
286     JS_FRIEND_API(JSBool)
287     js_PutArgsObject(JSContext *cx, JSStackFrame *fp)
288     {
289     JSObject *argsobj;
290     jsval bmapval, rval;
291     JSBool ok;
292     JSRuntime *rt;
293    
294     /*
295     * Reuse args_enumerate here to reflect fp's actual arguments as indexed
296     * elements of argsobj. Do this first, before clearing and freeing the
297     * deleted argument slot bitmap, because args_enumerate depends on that.
298     */
299     argsobj = fp->argsobj;
300     ok = args_enumerate(cx, argsobj);
301    
302     /*
303     * Now clear the deleted argument number bitmap slot and free the bitmap,
304     * if one was actually created due to 'delete arguments[0]' or similar.
305     */
306     (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
307     if (!JSVAL_IS_VOID(bmapval)) {
308     JS_SetReservedSlot(cx, argsobj, 0, JSVAL_VOID);
309     if (fp->argc > JSVAL_INT_BITS)
310     JS_free(cx, JSVAL_TO_PRIVATE(bmapval));
311     }
312    
313     /*
314     * Now get the prototype properties so we snapshot fp->fun and fp->argc
315     * before fp goes away.
316     */
317     rt = cx->runtime;
318     ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom),
319     &rval);
320     ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom),
321     &rval);
322     ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom),
323     &rval);
324     ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom),
325     &rval);
326    
327     /*
328     * Clear the private pointer to fp, which is about to go away (js_Invoke).
329     * Do this last because the args_enumerate and js_GetProperty calls above
330     * need to follow the private slot to find fp.
331     */
332     ok &= JS_SetPrivate(cx, argsobj, NULL);
333     fp->argsobj = NULL;
334     return ok;
335     }
336    
337     static JSBool
338     args_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
339     {
340     jsint slot;
341     JSStackFrame *fp;
342    
343     if (!JSVAL_IS_INT(id))
344     return JS_TRUE;
345     fp = (JSStackFrame *)
346     JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
347     if (!fp)
348     return JS_TRUE;
349     JS_ASSERT(fp->argsobj);
350    
351     slot = JSVAL_TO_INT(id);
352     switch (slot) {
353     case ARGS_CALLEE:
354     case ARGS_LENGTH:
355     SET_OVERRIDE_BIT(fp, slot);
356     break;
357    
358     default:
359     if ((uintN)slot < fp->argc && !MarkArgDeleted(cx, fp, slot))
360     return JS_FALSE;
361     break;
362     }
363     return JS_TRUE;
364     }
365    
366     static JSBool
367     args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
368     {
369     jsint slot;
370     JSStackFrame *fp;
371    
372     if (!JSVAL_IS_INT(id))
373     return JS_TRUE;
374     fp = (JSStackFrame *)
375     JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
376     if (!fp)
377     return JS_TRUE;
378     JS_ASSERT(fp->argsobj);
379    
380     slot = JSVAL_TO_INT(id);
381     switch (slot) {
382     case ARGS_CALLEE:
383     if (!TEST_OVERRIDE_BIT(fp, slot))
384     *vp = OBJECT_TO_JSVAL(fp->callee);
385     break;
386    
387     case ARGS_LENGTH:
388     if (!TEST_OVERRIDE_BIT(fp, slot))
389     *vp = INT_TO_JSVAL((jsint)fp->argc);
390     break;
391    
392     default:
393     if ((uintN)slot < fp->argc && !ArgWasDeleted(cx, fp, slot))
394     *vp = fp->argv[slot];
395     break;
396     }
397     return JS_TRUE;
398     }
399    
400     static JSBool
401     args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
402     {
403     JSStackFrame *fp;
404     jsint slot;
405    
406     if (!JSVAL_IS_INT(id))
407     return JS_TRUE;
408     fp = (JSStackFrame *)
409     JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
410     if (!fp)
411     return JS_TRUE;
412     JS_ASSERT(fp->argsobj);
413    
414     slot = JSVAL_TO_INT(id);
415     switch (slot) {
416     case ARGS_CALLEE:
417     case ARGS_LENGTH:
418     SET_OVERRIDE_BIT(fp, slot);
419     break;
420    
421     default:
422     if (FUN_INTERPRETED(fp->fun) &&
423     (uintN)slot < fp->argc &&
424     !ArgWasDeleted(cx, fp, slot)) {
425     fp->argv[slot] = *vp;
426     }
427     break;
428     }
429     return JS_TRUE;
430     }
431    
432     static JSBool
433     args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
434     JSObject **objp)
435     {
436     JSStackFrame *fp;
437     uintN slot;
438     JSString *str;
439     JSAtom *atom;
440     intN tinyid;
441     jsval value;
442    
443     *objp = NULL;
444     fp = (JSStackFrame *)
445     JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
446     if (!fp)
447     return JS_TRUE;
448     JS_ASSERT(fp->argsobj);
449    
450     if (JSVAL_IS_INT(id)) {
451     slot = JSVAL_TO_INT(id);
452     if (slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) {
453     /* XXX ECMA specs DontEnum, contrary to other array-like objects */
454     if (!js_DefineProperty(cx, obj, INT_JSVAL_TO_JSID(id),
455     fp->argv[slot],
456     args_getProperty, args_setProperty,
457     0, NULL)) {
458     return JS_FALSE;
459     }
460     *objp = obj;
461     }
462     } else {
463     str = JSVAL_TO_STRING(id);
464     atom = cx->runtime->atomState.lengthAtom;
465     if (str == ATOM_TO_STRING(atom)) {
466     tinyid = ARGS_LENGTH;
467     value = INT_TO_JSVAL(fp->argc);
468     } else {
469     atom = cx->runtime->atomState.calleeAtom;
470     if (str == ATOM_TO_STRING(atom)) {
471     tinyid = ARGS_CALLEE;
472     value = OBJECT_TO_JSVAL(fp->callee);
473     } else {
474     atom = NULL;
475    
476     /* Quell GCC overwarnings. */
477     tinyid = 0;
478     value = JSVAL_NULL;
479     }
480     }
481    
482     if (atom && !TEST_OVERRIDE_BIT(fp, tinyid)) {
483     if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value,
484     args_getProperty, args_setProperty, 0,
485     SPROP_HAS_SHORTID, tinyid, NULL)) {
486     return JS_FALSE;
487     }
488     *objp = obj;
489     }
490     }
491    
492     return JS_TRUE;
493     }
494    
495     static JSBool
496     args_enumerate(JSContext *cx, JSObject *obj)
497     {
498     JSStackFrame *fp;
499     JSObject *pobj;
500     JSProperty *prop;
501     uintN slot, argc;
502    
503     fp = (JSStackFrame *)
504     JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
505     if (!fp)
506     return JS_TRUE;
507     JS_ASSERT(fp->argsobj);
508    
509     /*
510     * Trigger reflection with value snapshot in args_resolve using a series
511     * of js_LookupProperty calls. We handle length, callee, and the indexed
512     * argument properties. We know that args_resolve covers all these cases
513     * and creates direct properties of obj, but that it may fail to resolve
514     * length or callee if overridden.
515     */
516     if (!js_LookupProperty(cx, obj,
517     ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
518     &pobj, &prop)) {
519     return JS_FALSE;
520     }
521     if (prop)
522     OBJ_DROP_PROPERTY(cx, pobj, prop);
523    
524     if (!js_LookupProperty(cx, obj,
525     ATOM_TO_JSID(cx->runtime->atomState.calleeAtom),
526     &pobj, &prop)) {
527     return JS_FALSE;
528     }
529     if (prop)
530     OBJ_DROP_PROPERTY(cx, pobj, prop);
531    
532     argc = fp->argc;
533     for (slot = 0; slot < argc; slot++) {
534     if (!js_LookupProperty(cx, obj, INT_TO_JSID((jsint)slot), &pobj, &prop))
535     return JS_FALSE;
536     if (prop)
537     OBJ_DROP_PROPERTY(cx, pobj, prop);
538     }
539     return JS_TRUE;
540     }
541    
542     #if JS_HAS_GENERATORS
543     /*
544     * If a generator-iterator's arguments or call object escapes, it needs to
545     * mark its generator object.
546     */
547     static void
548     args_or_call_trace(JSTracer *trc, JSObject *obj)
549     {
550     JSStackFrame *fp;
551    
552     fp = (JSStackFrame *) JS_GetPrivate(trc->context, obj);
553     if (fp && (fp->flags & JSFRAME_GENERATOR)) {
554     JS_CALL_OBJECT_TRACER(trc, FRAME_TO_GENERATOR(fp)->obj,
555     "FRAME_TO_GENERATOR(fp)->obj");
556     }
557     }
558     #else
559     # define args_or_call_trace NULL
560     #endif
561    
562     /*
563     * The Arguments class is not initialized via JS_InitClass, and must not be,
564     * because its name is "Object". Per ECMA, that causes instances of it to
565     * delegate to the object named by Object.prototype. It also ensures that
566     * arguments.toString() returns "[object Object]".
567     *
568     * The JSClass functions below collaborate to lazily reflect and synchronize
569     * actual argument values, argument count, and callee function object stored
570     * in a JSStackFrame with their corresponding property values in the frame's
571     * arguments object.
572     */
573     JSClass js_ArgumentsClass = {
574     js_Object_str,
575     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) |
576     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
577     JS_PropertyStub, args_delProperty,
578     args_getProperty, args_setProperty,
579     args_enumerate, (JSResolveOp) args_resolve,
580     JS_ConvertStub, JS_FinalizeStub,
581     NULL, NULL,
582     NULL, NULL,
583     NULL, NULL,
584     JS_CLASS_TRACE(args_or_call_trace), NULL
585     };
586    
587     #define JSSLOT_SCRIPTED_FUNCTION (JSSLOT_PRIVATE + 1)
588     #define JSSLOT_CALL_ARGUMENTS (JSSLOT_PRIVATE + 2)
589     #define CALL_CLASS_FIXED_RESERVED_SLOTS 2
590    
591     JSObject *
592     js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent)
593     {
594     JSObject *callobj, *funobj;
595    
596     /* Create a call object for fp only if it lacks one. */
597     JS_ASSERT(fp->fun);
598     callobj = fp->callobj;
599     if (callobj)
600     return callobj;
601    
602     /* The default call parent is its function's parent (static link). */
603     if (!parent) {
604     funobj = fp->callee;
605     if (funobj)
606     parent = OBJ_GET_PARENT(cx, funobj);
607     }
608    
609     /* Create the call object and link it to its stack frame. */
610     callobj = js_NewObject(cx, &js_CallClass, NULL, parent, 0);
611     if (!callobj)
612     return NULL;
613    
614     JS_SetPrivate(cx, callobj, fp);
615     STOBJ_SET_SLOT(callobj, JSSLOT_SCRIPTED_FUNCTION,
616     OBJECT_TO_JSVAL(FUN_OBJECT(fp->fun)));
617     fp->callobj = callobj;
618    
619     /* Make callobj be the scope chain and the variables object. */
620     JS_ASSERT(fp->scopeChain == parent);
621     fp->scopeChain = callobj;
622     fp->varobj = callobj;
623     return callobj;
624     }
625    
626     JSFunction *
627     js_GetCallObjectFunction(JSObject *obj)
628     {
629     jsval v;
630    
631     JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass);
632     v = STOBJ_GET_SLOT(obj, JSSLOT_SCRIPTED_FUNCTION);
633     if (JSVAL_IS_VOID(v)) {
634     /* Newborn or prototype object. */
635     return NULL;
636     }
637     JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
638     return (JSFunction *) JSVAL_TO_OBJECT(v);
639     }
640    
641     JS_FRIEND_API(JSBool)
642     js_PutCallObject(JSContext *cx, JSStackFrame *fp)
643     {
644     JSObject *callobj;
645     JSBool ok;
646     JSFunction *fun;
647     uintN n;
648     JSScope *scope;
649    
650     /*
651     * Since for a call object all fixed slots happen to be taken, we can copy
652     * arguments and variables straight into JSObject.dslots.
653     */
654     JS_STATIC_ASSERT(JS_INITIAL_NSLOTS - JSSLOT_PRIVATE ==
655     1 + CALL_CLASS_FIXED_RESERVED_SLOTS);
656    
657     callobj = fp->callobj;
658     if (!callobj)
659     return JS_TRUE;
660    
661     /*
662     * Get the arguments object to snapshot fp's actual argument values.
663     */
664     ok = JS_TRUE;
665     if (fp->argsobj) {
666     if (!TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
667     STOBJ_SET_SLOT(callobj, JSSLOT_CALL_ARGUMENTS,
668     OBJECT_TO_JSVAL(fp->argsobj));
669     }
670     ok &= js_PutArgsObject(cx, fp);
671     }
672    
673     fun = fp->fun;
674     JS_ASSERT(fun == js_GetCallObjectFunction(callobj));
675     n = JS_GET_LOCAL_NAME_COUNT(fun);
676     if (n != 0) {
677     JS_LOCK_OBJ(cx, callobj);
678     n += JS_INITIAL_NSLOTS;
679     if (n > STOBJ_NSLOTS(callobj))
680     ok &= js_ReallocSlots(cx, callobj, n, JS_TRUE);
681     scope = OBJ_SCOPE(callobj);
682     if (ok) {
683     memcpy(callobj->dslots, fp->argv, fun->nargs * sizeof(jsval));
684     memcpy(callobj->dslots + fun->nargs, fp->slots,
685     fun->u.i.nvars * sizeof(jsval));
686     if (scope->object == callobj && n > scope->map.freeslot)
687     scope->map.freeslot = n;
688     }
689     JS_UNLOCK_SCOPE(cx, scope);
690     }
691    
692     /*
693     * Clear the private pointer to fp, which is about to go away (js_Invoke).
694     * Do this last because js_GetProperty calls above need to follow the
695     * private slot to find fp.
696     */
697     JS_SetPrivate(cx, callobj, NULL);
698     fp->callobj = NULL;
699     return ok;
700     }
701    
702     static JSBool
703     call_enumerate(JSContext *cx, JSObject *obj)
704     {
705     JSFunction *fun;
706     uintN n, i;
707     void *mark;
708     jsuword *names;
709     JSBool ok;
710     JSAtom *name;
711     JSObject *pobj;
712     JSProperty *prop;
713    
714     fun = js_GetCallObjectFunction(obj);
715     n = JS_GET_LOCAL_NAME_COUNT(fun);
716     if (n == 0)
717     return JS_TRUE;
718    
719     mark = JS_ARENA_MARK(&cx->tempPool);
720    
721     MUST_FLOW_THROUGH("out");
722     names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
723     if (!names) {
724     ok = JS_FALSE;
725     goto out;
726     }
727    
728     for (i = 0; i != n; ++i) {
729     name = JS_LOCAL_NAME_TO_ATOM(names[i]);
730     if (!name)
731     continue;
732    
733     /*
734     * Trigger reflection by looking up the name of the argument or
735     * variable.
736     */
737     ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(name), &pobj, &prop);
738     if (!ok)
739     goto out;
740    
741     /*
742     * At this point the call object always has a property corresponding
743     * to the local name because call_resolve creates the property using
744     * JSPROP_PERMANENT.
745     */
746     JS_ASSERT(prop && pobj == obj);
747     OBJ_DROP_PROPERTY(cx, pobj, prop);
748     }
749     ok = JS_TRUE;
750    
751     out:
752     JS_ARENA_RELEASE(&cx->tempPool, mark);
753     return ok;
754     }
755    
756     typedef enum JSCallPropertyKind {
757     JSCPK_ARGUMENTS,
758     JSCPK_ARG,
759     JSCPK_VAR
760     } JSCallPropertyKind;
761    
762     static JSBool
763     CallPropertyOp(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
764     JSCallPropertyKind kind, JSBool setter)
765     {
766     JSFunction *fun;
767     JSStackFrame *fp;
768     uintN i;
769     jsval *array;
770    
771     if (STOBJ_GET_CLASS(obj) != &js_CallClass)
772     return JS_TRUE;
773    
774     fun = js_GetCallObjectFunction(obj);
775     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
776    
777     if (kind == JSCPK_ARGUMENTS) {
778     if (setter) {
779     if (fp)
780     SET_OVERRIDE_BIT(fp, CALL_ARGUMENTS);
781     STOBJ_SET_SLOT(obj, JSSLOT_CALL_ARGUMENTS, *vp);
782     } else {
783     if (fp && !TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
784     JSObject *argsobj;
785    
786     argsobj = js_GetArgsObject(cx, fp);
787     if (!argsobj)
788     return JS_FALSE;
789     *vp = OBJECT_TO_JSVAL(argsobj);
790     } else {
791     *vp = STOBJ_GET_SLOT(obj, JSSLOT_CALL_ARGUMENTS);
792     }
793     }
794     return JS_TRUE;
795     }
796    
797     JS_ASSERT((int16) JSVAL_TO_INT(id) == JSVAL_TO_INT(id));
798     i = (uint16) JSVAL_TO_INT(id);
799     JS_ASSERT_IF(kind == JSCPK_ARG, i < fun->nargs);
800     JS_ASSERT_IF(kind == JSCPK_VAR, i < fun->u.i.nvars);
801    
802     if (!fp) {
803     i += CALL_CLASS_FIXED_RESERVED_SLOTS;
804     if (kind == JSCPK_VAR)
805     i += fun->nargs;
806     else
807     JS_ASSERT(kind == JSCPK_ARG);
808     return setter
809     ? JS_SetReservedSlot(cx, obj, i, *vp)
810     : JS_GetReservedSlot(cx, obj, i, vp);
811     }
812    
813     if (kind == JSCPK_ARG) {
814     array = fp->argv;
815     } else {
816     JS_ASSERT(kind == JSCPK_VAR);
817     array = fp->slots;
818     }
819     if (setter)
820     array[i] = *vp;
821     else
822     *vp = array[i];
823     return JS_TRUE;
824     }
825    
826     static JSBool
827     GetCallArguments(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
828     {
829     return CallPropertyOp(cx, obj, id, vp, JSCPK_ARGUMENTS, JS_FALSE);
830     }
831    
832     static JSBool
833     SetCallArguments(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
834     {
835     return CallPropertyOp(cx, obj, id, vp, JSCPK_ARGUMENTS, JS_TRUE);
836     }
837    
838     JSBool
839     js_GetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
840     {
841     return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG, JS_FALSE);
842     }
843    
844     static JSBool
845     SetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
846     {
847     return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG, JS_TRUE);
848     }
849    
850     JSBool
851     js_GetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
852     {
853     return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_FALSE);
854     }
855    
856     static JSBool
857     SetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
858     {
859     return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_TRUE);
860     }
861    
862     static JSBool
863     call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags,
864     JSObject **objp)
865     {
866     JSFunction *fun;
867     jsid id;
868     JSLocalKind localKind;
869     JSPropertyOp getter, setter;
870     uintN slot, attrs;
871    
872     if (!JSVAL_IS_STRING(idval))
873     return JS_TRUE;
874    
875     fun = js_GetCallObjectFunction(obj);
876     if (!fun)
877     return JS_TRUE;
878    
879     if (!js_ValueToStringId(cx, idval, &id))
880     return JS_FALSE;
881    
882     localKind = js_LookupLocal(cx, fun, JSID_TO_ATOM(id), &slot);
883     if (localKind != JSLOCAL_NONE) {
884     JS_ASSERT((uint16) slot == slot);
885     attrs = JSPROP_PERMANENT | JSPROP_SHARED;
886     if (localKind == JSLOCAL_ARG) {
887     JS_ASSERT(slot < fun->nargs);
888     getter = js_GetCallArg;
889     setter = SetCallArg;
890     } else {
891     JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
892     JS_ASSERT(slot < fun->u.i.nvars);
893     getter = js_GetCallVar;
894     setter = SetCallVar;
895     if (localKind == JSLOCAL_CONST)
896     attrs |= JSPROP_READONLY;
897     }
898     if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, getter, setter,
899     attrs, SPROP_HAS_SHORTID, (int16) slot,
900     NULL)) {
901     return JS_FALSE;
902     }
903     *objp = obj;
904     return JS_TRUE;
905     }
906    
907     /*
908     * Resolve arguments so that we never store a particular Call object's
909     * arguments object reference in a Call prototype's |arguments| slot.
910     */
911     if (id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) {
912     if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID,
913     GetCallArguments, SetCallArguments,
914     JSPROP_PERMANENT | JSPROP_SHARED,
915     0, 0, NULL)) {
916     return JS_FALSE;
917     }
918     *objp = obj;
919     return JS_TRUE;
920     }
921     return JS_TRUE;
922     }
923    
924     static JSBool
925     call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
926     {
927     JSStackFrame *fp;
928    
929     if (type == JSTYPE_FUNCTION) {
930     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
931     if (fp) {
932     JS_ASSERT(fp->fun);
933     *vp = OBJECT_TO_JSVAL(fp->callee);
934     }
935     }
936     return JS_TRUE;
937     }
938    
939     static uint32
940     call_reserveSlots(JSContext *cx, JSObject *obj)
941     {
942     JSFunction *fun;
943    
944     fun = js_GetCallObjectFunction(obj);
945     return JS_GET_LOCAL_NAME_COUNT(fun);
946     }
947    
948     JS_FRIEND_DATA(JSClass) js_CallClass = {
949     js_Call_str,
950     JSCLASS_HAS_PRIVATE |
951     JSCLASS_HAS_RESERVED_SLOTS(CALL_CLASS_FIXED_RESERVED_SLOTS) |
952     JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS |
953     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Call),
954     JS_PropertyStub, JS_PropertyStub,
955     JS_PropertyStub, JS_PropertyStub,
956     call_enumerate, (JSResolveOp)call_resolve,
957     call_convert, JS_FinalizeStub,
958     NULL, NULL,
959     NULL, NULL,
960     NULL, NULL,
961     JS_CLASS_TRACE(args_or_call_trace), call_reserveSlots
962     };
963    
964     static JSBool
965     fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
966     {
967     jsint slot;
968     JSFunction *fun;
969     JSStackFrame *fp;
970     JSSecurityCallbacks *callbacks;
971    
972     if (!JSVAL_IS_INT(id))
973     return JS_TRUE;
974     slot = JSVAL_TO_INT(id);
975    
976     /*
977     * Loop because getter and setter can be delegated from another class,
978     * but loop only for ARGS_LENGTH because we must pretend that f.length
979     * is in each function instance f, per ECMA-262, instead of only in the
980     * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
981     * to make it appear so).
982     *
983     * This code couples tightly to the attributes for the function_props[]
984     * initializers above, and to js_SetProperty and js_HasOwnProperty.
985     *
986     * It's important to allow delegating objects, even though they inherit
987     * this getter (fun_getProperty), to override arguments, arity, caller,
988     * and name. If we didn't return early for slot != ARGS_LENGTH, we would
989     * clobber *vp with the native property value, instead of letting script
990     * override that value in delegating objects.
991     *
992     * Note how that clobbering is what simulates JSPROP_READONLY for all of
993     * the non-standard properties when the directly addressed object (obj)
994     * is a function object (i.e., when this loop does not iterate).
995     */
996     while (!(fun = (JSFunction *)
997     JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) {
998     if (slot != ARGS_LENGTH)
999     return JS_TRUE;
1000     obj = OBJ_GET_PROTO(cx, obj);
1001     if (!obj)
1002     return JS_TRUE;
1003     }
1004    
1005     /* Find fun's top-most activation record. */
1006     for (fp = cx->fp; fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL));
1007     fp = fp->down) {
1008     continue;
1009     }
1010    
1011     switch (slot) {
1012     case CALL_ARGUMENTS:
1013     /* Warn if strict about f.arguments or equivalent unqualified uses. */
1014     if (!JS_ReportErrorFlagsAndNumber(cx,
1015     JSREPORT_WARNING | JSREPORT_STRICT,
1016     js_GetErrorMessage, NULL,
1017     JSMSG_DEPRECATED_USAGE,
1018     js_arguments_str)) {
1019     return JS_FALSE;
1020     }
1021     if (fp) {
1022     if (!js_GetArgsValue(cx, fp, vp))
1023     return JS_FALSE;
1024     } else {
1025     *vp = JSVAL_NULL;
1026     }
1027     break;
1028    
1029     case ARGS_LENGTH:
1030     case FUN_ARITY:
1031     *vp = INT_TO_JSVAL((jsint)fun->nargs);
1032     break;
1033    
1034     case FUN_NAME:
1035     *vp = fun->atom
1036     ? ATOM_KEY(fun->atom)
1037     : STRING_TO_JSVAL(cx->runtime->emptyString);
1038     break;
1039    
1040     case FUN_CALLER:
1041     if (fp && fp->down && fp->down->fun)
1042     *vp = OBJECT_TO_JSVAL(fp->down->callee);
1043     else
1044     *vp = JSVAL_NULL;
1045     if (!JSVAL_IS_PRIMITIVE(*vp)) {
1046     callbacks = JS_GetSecurityCallbacks(cx);
1047     if (callbacks && callbacks->checkObjectAccess) {
1048     id = ATOM_KEY(cx->runtime->atomState.callerAtom);
1049     if (!callbacks->checkObjectAccess(cx, obj, id, JSACC_READ, vp))
1050     return JS_FALSE;
1051     }
1052     }
1053     break;
1054    
1055     default:
1056     /* XXX fun[0] and fun.arguments[0] are equivalent. */
1057     if (fp && fp->fun && (uintN)slot < fp->fun->nargs)
1058     *vp = fp->argv[slot];
1059     break;
1060     }
1061    
1062     return JS_TRUE;
1063     }
1064    
1065     /*
1066     * ECMA-262 specifies that length is a property of function object instances,
1067     * but we can avoid that space cost by delegating to a prototype property that
1068     * is JSPROP_PERMANENT and JSPROP_SHARED. Each fun_getProperty call computes
1069     * a fresh length value based on the arity of the individual function object's
1070     * private data.
1071     *
1072     * The extensions below other than length, i.e., the ones not in ECMA-262,
1073     * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility
1074     * with ECMA we must allow a delegating object to override them. Therefore to
1075     * avoid entraining garbage in Function.prototype slots, they must be resolved
1076     * in non-prototype function objects, wherefore the lazy_function_props table
1077     * and fun_resolve's use of it.
1078     */
1079     #define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED)
1080    
1081     static JSPropertySpec function_props[] = {
1082     {js_length_str, ARGS_LENGTH, LENGTH_PROP_ATTRS, fun_getProperty, JS_PropertyStub},
1083     {0,0,0,0,0}
1084     };
1085    
1086     typedef struct LazyFunctionProp {
1087     uint16 atomOffset;
1088     int8 tinyid;
1089     uint8 attrs;
1090     } LazyFunctionProp;
1091    
1092     /* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */
1093     static LazyFunctionProp lazy_function_props[] = {
1094     {ATOM_OFFSET(arguments), CALL_ARGUMENTS, JSPROP_PERMANENT},
1095     {ATOM_OFFSET(arity), FUN_ARITY, JSPROP_PERMANENT},
1096     {ATOM_OFFSET(caller), FUN_CALLER, JSPROP_PERMANENT},
1097     {ATOM_OFFSET(name), FUN_NAME, JSPROP_PERMANENT},
1098     };
1099    
1100     static JSBool
1101     fun_enumerate(JSContext *cx, JSObject *obj)
1102     {
1103     jsid prototypeId;
1104     JSObject *pobj;
1105     JSProperty *prop;
1106    
1107     prototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
1108     if (!OBJ_LOOKUP_PROPERTY(cx, obj, prototypeId, &pobj, &prop))
1109     return JS_FALSE;
1110     if (prop)
1111     OBJ_DROP_PROPERTY(cx, pobj, prop);
1112     return JS_TRUE;
1113     }
1114    
1115     static JSBool
1116     fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
1117     JSObject **objp)
1118     {
1119     JSFunction *fun;
1120     JSAtom *atom;
1121     uintN i;
1122    
1123     if (!JSVAL_IS_STRING(id))
1124     return JS_TRUE;
1125    
1126     fun = GET_FUNCTION_PRIVATE(cx, obj);
1127    
1128     /*
1129     * No need to reflect fun.prototype in 'fun.prototype = ... '.
1130     *
1131     * This is not just an optimization, because we must not resolve when
1132     * defining hidden properties during compilation. The setup code for the
1133     * prototype and the lazy properties below eventually calls the property
1134     * hooks for the function object. That in turn calls fun_reserveSlots to
1135     * get the number of the reserved slots which is just the number of
1136     * regular expressions literals in the function. When compiling, that
1137     * number is not yet ready so we must make sure that fun_resolve does
1138     * nothing until the code for the function is generated.
1139     */
1140     if (flags & JSRESOLVE_ASSIGNING)
1141     return JS_TRUE;
1142    
1143     /*
1144     * Ok, check whether id is 'prototype' and bootstrap the function object's
1145     * prototype property.
1146     */
1147     atom = cx->runtime->atomState.classPrototypeAtom;
1148     if (id == ATOM_KEY(atom)) {
1149     JSObject *proto;
1150    
1151     /*
1152     * Beware of the wacky case of a user function named Object -- trying
1153     * to find a prototype for that will recur back here _ad perniciem_.
1154     */
1155     if (fun->atom == CLASS_ATOM(cx, Object))
1156     return JS_TRUE;
1157    
1158     /*
1159     * Make the prototype object to have the same parent as the function
1160     * object itself.
1161     */
1162     proto = js_NewObject(cx, &js_ObjectClass, NULL, OBJ_GET_PARENT(cx, obj),
1163     0);
1164     if (!proto)
1165     return JS_FALSE;
1166    
1167     /*
1168     * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for
1169     * user-defined functions, but DontEnum | ReadOnly | DontDelete for
1170     * native "system" constructors such as Object or Function. So lazily
1171     * set the former here in fun_resolve, but eagerly define the latter
1172     * in JS_InitClass, with the right attributes.
1173     */
1174     if (!js_SetClassPrototype(cx, obj, proto,
1175     JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
1176     cx->weakRoots.newborn[GCX_OBJECT] = NULL;
1177     return JS_FALSE;
1178     }
1179     *objp = obj;
1180     return JS_TRUE;
1181     }
1182    
1183     for (i = 0; i < JS_ARRAY_LENGTH(lazy_function_props); i++) {
1184     LazyFunctionProp *lfp = &lazy_function_props[i];
1185    
1186     atom = OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset);
1187     if (id == ATOM_KEY(atom)) {
1188     if (!js_DefineNativeProperty(cx, obj,
1189     ATOM_TO_JSID(atom), JSVAL_VOID,
1190     fun_getProperty, JS_PropertyStub,
1191     lfp->attrs, SPROP_HAS_SHORTID,
1192     lfp->tinyid, NULL)) {
1193     return JS_FALSE;
1194     }
1195     *objp = obj;
1196     return JS_TRUE;
1197     }
1198     }
1199    
1200     return JS_TRUE;
1201     }
1202    
1203     static JSBool
1204     fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
1205     {
1206     switch (type) {
1207     case JSTYPE_FUNCTION:
1208     *vp = OBJECT_TO_JSVAL(obj);
1209     return JS_TRUE;
1210     default:
1211     return js_TryValueOf(cx, obj, type, vp);
1212     }
1213     }
1214    
1215     #if JS_HAS_XDR
1216    
1217     /* XXX store parent and proto, if defined */
1218     static JSBool
1219     fun_xdrObject(JSXDRState *xdr, JSObject **objp)
1220     {
1221     JSContext *cx;
1222     JSFunction *fun;
1223     uint32 nullAtom; /* flag to indicate if fun->atom is NULL */
1224     uintN nargs, nvars, n;
1225     uint32 localsword; /* word to xdr argument and variable counts */
1226     uint32 flagsword; /* originally only flags was JS_XDRUint8'd */
1227     JSTempValueRooter tvr;
1228     JSBool ok;
1229    
1230     cx = xdr->cx;
1231     if (xdr->mode == JSXDR_ENCODE) {
1232     fun = GET_FUNCTION_PRIVATE(cx, *objp);
1233     if (!FUN_INTERPRETED(fun)) {
1234     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1235     JSMSG_NOT_SCRIPTED_FUNCTION,
1236     JS_GetFunctionName(fun));
1237     return JS_FALSE;
1238     }
1239     nullAtom = !fun->atom;
1240     nargs = fun->nargs;
1241     nvars = fun->u.i.nvars;
1242     localsword = (nargs << 16) | nvars;
1243     flagsword = fun->flags;
1244     } else {
1245     fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
1246     if (!fun)
1247     return JS_FALSE;
1248     STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
1249     STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
1250     #ifdef __GNUC__
1251     nvars = nargs = 0; /* quell GCC uninitialized warning */
1252     #endif
1253     }
1254    
1255     /* From here on, control flow must flow through label out. */
1256     JS_PUSH_TEMP_ROOT_OBJECT(cx, FUN_OBJECT(fun), &tvr);
1257     ok = JS_TRUE;
1258    
1259     if (!JS_XDRUint32(xdr, &nullAtom))
1260     goto bad;
1261     if (!nullAtom && !js_XDRStringAtom(xdr, &fun->atom))
1262     goto bad;
1263     if (!JS_XDRUint32(xdr, &localsword) ||
1264     !JS_XDRUint32(xdr, &flagsword)) {
1265     goto bad;
1266     }
1267    
1268     if (xdr->mode == JSXDR_DECODE) {
1269     nargs = localsword >> 16;
1270     nvars = localsword & JS_BITMASK(16);
1271     JS_ASSERT(flagsword | JSFUN_INTERPRETED);
1272     fun->flags = (uint16) flagsword;
1273     }
1274    
1275     /* do arguments and local vars */
1276     n = nargs + nvars;
1277     if (n != 0) {
1278     void *mark;
1279     uintN i;
1280     uintN bitmapLength;
1281     uint32 *bitmap;
1282     jsuword *names;
1283     JSAtom *name;
1284     JSLocalKind localKind;
1285    
1286     mark = JS_ARENA_MARK(&xdr->cx->tempPool);
1287    
1288     /*
1289     * From this point the control must flow via the label release_mark.
1290     *
1291     * To xdr the names we prefix the names with a bitmap descriptor and
1292     * then xdr the names as strings. For argument names (indexes below
1293     * nargs) the corresponding bit in the bitmap is unset when the name
1294     * is null. Such null names are not encoded or decoded. For variable
1295     * names (indexes starting from nargs) bitmap's bit is set when the
1296     * name is declared as const, not as ordinary var.
1297     * */
1298     bitmapLength = JS_HOWMANY(n, JS_BITS_PER_UINT32);
1299     JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &xdr->cx->tempPool,
1300     bitmapLength * sizeof *bitmap);
1301     if (!bitmap) {
1302     js_ReportOutOfScriptQuota(xdr->cx);
1303     ok = JS_FALSE;
1304     goto release_mark;
1305     }
1306     if (xdr->mode == JSXDR_ENCODE) {
1307     names = js_GetLocalNameArray(xdr->cx, fun, &xdr->cx->tempPool);
1308     if (!names) {
1309     ok = JS_FALSE;
1310     goto release_mark;
1311     }
1312     memset(bitmap, 0, bitmapLength * sizeof *bitmap);
1313     for (i = 0; i != n; ++i) {
1314     if (i < fun->nargs
1315     ? JS_LOCAL_NAME_TO_ATOM(names[i]) != NULL
1316     : JS_LOCAL_NAME_IS_CONST(names[i])) {
1317     bitmap[i >> JS_BITS_PER_UINT32_LOG2] |=
1318     JS_BIT(i & (JS_BITS_PER_UINT32 - 1));
1319     }
1320     }
1321     }
1322     #ifdef __GNUC__
1323     else {
1324     names = NULL; /* quell GCC uninitialized warning */
1325     }
1326     #endif
1327     for (i = 0; i != bitmapLength; ++i) {
1328     ok = JS_XDRUint32(xdr, &bitmap[i]);
1329     if (!ok)
1330     goto release_mark;
1331     }
1332     for (i = 0; i != n; ++i) {
1333     if (i < nargs &&
1334     !(bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
1335     JS_BIT(i & (JS_BITS_PER_UINT32 - 1)))) {
1336     if (xdr->mode == JSXDR_DECODE) {
1337     ok = js_AddLocal(xdr->cx, fun, NULL, JSLOCAL_ARG);
1338     if (!ok)
1339     goto release_mark;
1340     } else {
1341     JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names[i]));
1342     }
1343     continue;
1344     }
1345     if (xdr->mode == JSXDR_ENCODE)
1346     name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1347     ok = js_XDRStringAtom(xdr, &name);
1348     if (!ok)
1349     goto release_mark;
1350     if (xdr->mode == JSXDR_DECODE) {
1351     localKind = (i < nargs)
1352     ? JSLOCAL_ARG
1353     : bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
1354     JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
1355     ? JSLOCAL_CONST
1356     : JSLOCAL_VAR;
1357     ok = js_AddLocal(xdr->cx, fun, name, localKind);
1358     if (!ok)
1359     goto release_mark;
1360     }
1361     }
1362     ok = JS_TRUE;
1363    
1364     release_mark:
1365     JS_ARENA_RELEASE(&xdr->cx->tempPool, mark);
1366     if (!ok)
1367     goto out;
1368    
1369     if (xdr->mode == JSXDR_DECODE)
1370     js_FreezeLocalNames(cx, fun);
1371     }
1372    
1373     if (!js_XDRScript(xdr, &fun->u.i.script, NULL))
1374     goto bad;
1375    
1376     if (xdr->mode == JSXDR_DECODE) {
1377     *objp = FUN_OBJECT(fun);
1378     #ifdef CHECK_SCRIPT_OWNER
1379     fun->u.i.script->owner = NULL;
1380     #endif
1381     js_CallNewScriptHook(cx, fun->u.i.script, fun);
1382     }
1383    
1384     out:
1385     JS_POP_TEMP_ROOT(cx, &tvr);
1386     return ok;
1387    
1388     bad:
1389     ok = JS_FALSE;
1390     goto out;
1391     }
1392    
1393     #else /* !JS_HAS_XDR */
1394    
1395     #define fun_xdrObject NULL
1396    
1397     #endif /* !JS_HAS_XDR */
1398    
1399     /*
1400     * [[HasInstance]] internal method for Function objects: fetch the .prototype
1401     * property of its 'this' parameter, and walks the prototype chain of v (only
1402     * if v is an object) returning true if .prototype is found.
1403     */
1404     static JSBool
1405     fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
1406     {
1407     jsval pval;
1408    
1409     if (!OBJ_GET_PROPERTY(cx, obj,
1410     ATOM_TO_JSID(cx->runtime->atomState
1411     .classPrototypeAtom),
1412     &pval)) {
1413     return JS_FALSE;
1414     }
1415    
1416     if (JSVAL_IS_PRIMITIVE(pval)) {
1417     /*
1418     * Throw a runtime error if instanceof is called on a function that
1419     * has a non-object as its .prototype value.
1420     */
1421     js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE,
1422     -1, OBJECT_TO_JSVAL(obj), NULL);
1423     return JS_FALSE;
1424     }
1425    
1426     return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp);
1427     }
1428    
1429     static void
1430     TraceLocalNames(JSTracer *trc, JSFunction *fun);
1431    
1432     static void
1433     DestroyLocalNames(JSContext *cx, JSFunction *fun);
1434    
1435     static void
1436     fun_trace(JSTracer *trc, JSObject *obj)
1437     {
1438     JSFunction *fun;
1439    
1440     /* A newborn function object may have a not yet initialized private slot. */
1441     fun = (JSFunction *) JS_GetPrivate(trc->context, obj);
1442     if (!fun)
1443     return;
1444    
1445     if (FUN_OBJECT(fun) != obj) {
1446     /* obj is cloned function object, trace the original. */
1447     JS_CALL_TRACER(trc, FUN_OBJECT(fun), JSTRACE_OBJECT, "private");
1448     return;
1449     }
1450     if (fun->atom)
1451     JS_CALL_STRING_TRACER(trc, ATOM_TO_STRING(fun->atom), "atom");
1452     if (FUN_INTERPRETED(fun)) {
1453     if (fun->u.i.script)
1454     js_TraceScript(trc, fun->u.i.script);
1455     TraceLocalNames(trc, fun);
1456     }
1457     }
1458    
1459     static void
1460     fun_finalize(JSContext *cx, JSObject *obj)
1461     {
1462     JSFunction *fun;
1463    
1464     /* Ignore newborn and cloned function objects. */
1465     fun = (JSFunction *) JS_GetPrivate(cx, obj);
1466     if (!fun || FUN_OBJECT(fun) != obj)
1467     return;
1468    
1469     /*
1470     * Null-check of u.i.script is required since the parser sets interpreted
1471     * very early.
1472     */
1473     if (FUN_INTERPRETED(fun)) {
1474     if (fun->u.i.script)
1475     js_DestroyScript(cx, fun->u.i.script);
1476     DestroyLocalNames(cx, fun);
1477     }
1478     }
1479    
1480     static uint32
1481     fun_reserveSlots(JSContext *cx, JSObject *obj)
1482     {
1483     JSFunction *fun;
1484     uint32 nslots;
1485    
1486     /*
1487     * We use JS_GetPrivate and not GET_FUNCTION_PRIVATE because during
1488     * js_InitFunctionClass invocation the function is called before the
1489     * private slot of the function object is set.
1490     */
1491     fun = (JSFunction *) JS_GetPrivate(cx, obj);
1492     nslots = 0;
1493     if (fun && FUN_INTERPRETED(fun) && fun->u.i.script) {
1494     if (fun->u.i.script->upvarsOffset != 0)
1495     nslots = JS_SCRIPT_UPVARS(fun->u.i.script)->length;
1496     if (fun->u.i.script->regexpsOffset != 0)
1497     nslots += JS_SCRIPT_REGEXPS(fun->u.i.script)->length;
1498     }
1499     return nslots;
1500     }
1501    
1502     /*
1503     * Reserve two slots in all function objects for XPConnect. Note that this
1504     * does not bloat every instance, only those on which reserved slots are set,
1505     * and those on which ad-hoc properties are defined.
1506     */
1507     JS_FRIEND_DATA(JSClass) js_FunctionClass = {
1508     js_Function_str,
1509     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2) |
1510     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
1511     JS_PropertyStub, JS_PropertyStub,
1512     JS_PropertyStub, JS_PropertyStub,
1513     fun_enumerate, (JSResolveOp)fun_resolve,
1514     fun_convert, fun_finalize,
1515     NULL, NULL,
1516     NULL, NULL,
1517     fun_xdrObject, fun_hasInstance,
1518     JS_CLASS_TRACE(fun_trace), fun_reserveSlots
1519     };
1520    
1521     static JSBool
1522     fun_toStringHelper(JSContext *cx, uint32 indent, uintN argc, jsval *vp)
1523     {
1524     jsval fval;
1525     JSObject *obj;
1526     JSFunction *fun;
1527     JSString *str;
1528    
1529     fval = JS_THIS(cx, vp);
1530     if (JSVAL_IS_NULL(fval))
1531     return JS_FALSE;
1532    
1533     if (!VALUE_IS_FUNCTION(cx, fval)) {
1534     /*
1535     * If we don't have a function to start off with, try converting the
1536     * object to a function. If that doesn't work, complain.
1537     */
1538     if (!JSVAL_IS_PRIMITIVE(fval)) {
1539     obj = JSVAL_TO_OBJECT(fval);
1540     if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, JSTYPE_FUNCTION,
1541     &fval)) {
1542     return JS_FALSE;
1543     }
1544     vp[1] = fval;
1545     }
1546     if (!VALUE_IS_FUNCTION(cx, fval)) {
1547     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1548     JSMSG_INCOMPATIBLE_PROTO,
1549     js_Function_str, js_toString_str,
1550     JS_GetTypeName(cx, JS_TypeOfValue(cx, fval)));
1551     return JS_FALSE;
1552     }
1553     }
1554    
1555     obj = JSVAL_TO_OBJECT(fval);
1556     if (argc != 0) {
1557     indent = js_ValueToECMAUint32(cx, &vp[2]);
1558     if (JSVAL_IS_NULL(vp[2]))
1559     return JS_FALSE;
1560     }
1561    
1562     JS_ASSERT(JS_ObjectIsFunction(cx, obj));
1563     fun = GET_FUNCTION_PRIVATE(cx, obj);
1564     if (!fun)
1565     return JS_TRUE;
1566     str = JS_DecompileFunction(cx, fun, (uintN)indent);
1567     if (!str)
1568     return JS_FALSE;
1569     *vp = STRING_TO_JSVAL(str);
1570     return JS_TRUE;
1571     }
1572    
1573     static JSBool
1574     fun_toString(JSContext *cx, uintN argc, jsval *vp)
1575     {
1576     return fun_toStringHelper(cx, 0, argc, vp);
1577     }
1578    
1579     #if JS_HAS_TOSOURCE
1580     static JSBool
1581     fun_toSource(JSContext *cx, uintN argc, jsval *vp)
1582     {
1583     return fun_toStringHelper(cx, JS_DONT_PRETTY_PRINT, argc, vp);
1584     }
1585     #endif
1586    
1587     static const char call_str[] = "call";
1588    
1589     static JSBool
1590     fun_call(JSContext *cx, uintN argc, jsval *vp)
1591     {
1592     JSObject *obj;
1593     jsval fval, *argv, *invokevp;
1594     JSString *str;
1595     void *mark;
1596     JSBool ok;
1597    
1598     obj = JS_THIS_OBJECT(cx, vp);
1599     if (!obj || !OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &vp[1]))
1600     return JS_FALSE;
1601     fval = vp[1];
1602    
1603     if (!VALUE_IS_FUNCTION(cx, fval)) {
1604     str = JS_ValueToString(cx, fval);
1605     if (str) {
1606     const char *bytes = js_GetStringBytes(cx, str);
1607    
1608     if (bytes) {
1609     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1610     JSMSG_INCOMPATIBLE_PROTO,
1611     js_Function_str, call_str,
1612     bytes);
1613     }
1614     }
1615     return JS_FALSE;
1616     }
1617    
1618     argv = vp + 2;
1619     if (argc == 0) {
1620     /* Call fun with its global object as the 'this' param if no args. */
1621     obj = NULL;
1622     } else {
1623     /* Otherwise convert the first arg to 'this' and skip over it. */
1624     if (!JSVAL_IS_PRIMITIVE(argv[0]))
1625     obj = JSVAL_TO_OBJECT(argv[0]);
1626     else if (!js_ValueToObject(cx, argv[0], &obj))
1627     return JS_FALSE;
1628     argc--;
1629     argv++;
1630     }
1631    
1632     /* Allocate stack space for fval, obj, and the args. */
1633     invokevp = js_AllocStack(cx, 2 + argc, &mark);
1634     if (!invokevp)
1635     return JS_FALSE;
1636    
1637     /* Push fval, obj, and the args. */
1638     invokevp[0] = fval;
1639     invokevp[1] = OBJECT_TO_JSVAL(obj);
1640     memcpy(invokevp + 2, argv, argc * sizeof *argv);
1641    
1642     ok = js_Invoke(cx, argc, invokevp, 0);
1643     *vp = *invokevp;
1644     js_FreeStack(cx, mark);
1645     return ok;
1646     }
1647    
1648     JSBool
1649     js_fun_apply(JSContext *cx, uintN argc, jsval *vp)
1650     {
1651     JSObject *obj, *aobj;
1652     jsval fval, *invokevp, *sp;
1653     JSString *str;
1654     jsuint length;
1655     JSBool arraylike, ok;
1656     void *mark;
1657     uintN i;
1658    
1659     if (argc == 0) {
1660     /* Will get globalObject as 'this' and no other arguments. */
1661     return fun_call(cx, argc, vp);
1662     }
1663    
1664     obj = JS_THIS_OBJECT(cx, vp);
1665     if (!obj || !OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &vp[1]))
1666     return JS_FALSE;
1667     fval = vp[1];
1668    
1669     if (!VALUE_IS_FUNCTION(cx, fval)) {
1670     str = JS_ValueToString(cx, fval);
1671     if (str) {
1672     const char *bytes = js_GetStringBytes(cx, str);
1673    
1674     if (bytes) {
1675     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1676     JSMSG_INCOMPATIBLE_PROTO,
1677     js_Function_str, "apply",
1678     bytes);
1679     }
1680     }
1681     return JS_FALSE;
1682     }
1683    
1684     /* Quell GCC overwarnings. */
1685     aobj = NULL;
1686     length = 0;
1687    
1688     if (argc >= 2) {
1689     /* If the 2nd arg is null or void, call the function with 0 args. */
1690     if (JSVAL_IS_NULL(vp[3]) || JSVAL_IS_VOID(vp[3])) {
1691     argc = 0;
1692     } else {
1693     /* The second arg must be an array (or arguments object). */
1694     arraylike = JS_FALSE;
1695     if (!JSVAL_IS_PRIMITIVE(vp[3])) {
1696     aobj = JSVAL_TO_OBJECT(vp[3]);
1697     if (!js_IsArrayLike(cx, aobj, &arraylike, &length))
1698     return JS_FALSE;
1699     }
1700     if (!arraylike) {
1701     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1702     JSMSG_BAD_APPLY_ARGS, "apply");
1703     return JS_FALSE;
1704     }
1705     }
1706     }
1707    
1708     /* Convert the first arg to 'this' and skip over it. */
1709     if (!JSVAL_IS_PRIMITIVE(vp[2]))
1710     obj = JSVAL_TO_OBJECT(vp[2]);
1711     else if (!js_ValueToObject(cx, vp[2], &obj))
1712     return JS_FALSE;
1713    
1714     /* Allocate stack space for fval, obj, and the args. */
1715     argc = (uintN)JS_MIN(length, ARRAY_INIT_LIMIT - 1);
1716     invokevp = js_AllocStack(cx, 2 + argc, &mark);
1717     if (!invokevp)
1718     return JS_FALSE;
1719    
1720     /* Push fval, obj, and aobj's elements as args. */
1721     sp = invokevp;
1722     *sp++ = fval;
1723     *sp++ = OBJECT_TO_JSVAL(obj);
1724     for (i = 0; i < argc; i++) {
1725     ok = JS_GetElement(cx, aobj, (jsint)i, sp);
1726     if (!ok)
1727     goto out;
1728     sp++;
1729     }
1730    
1731     ok = js_Invoke(cx, argc, invokevp, 0);
1732     *vp = *invokevp;
1733     out:
1734     js_FreeStack(cx, mark);
1735     return ok;
1736     }
1737    
1738     #ifdef NARCISSUS
1739     static JSBool
1740     fun_applyConstructor(JSContext *cx, uintN argc, jsval *vp)
1741     {
1742     JSObject *aobj;
1743     uintN length, i;
1744     void *mark;
1745     jsval *invokevp, *sp;
1746     JSBool ok;
1747    
1748     if (JSVAL_IS_PRIMITIVE(vp[2]) ||
1749     (aobj = JSVAL_TO_OBJECT(vp[2]),
1750     OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass &&
1751     OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass)) {
1752     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1753     JSMSG_BAD_APPLY_ARGS, "__applyConstruct__");
1754     return JS_FALSE;
1755     }
1756    
1757     if (!js_GetLengthProperty(cx, aobj, &length))
1758     return JS_FALSE;
1759    
1760     if (length >= ARRAY_INIT_LIMIT)
1761     length = ARRAY_INIT_LIMIT - 1;
1762     invokevp = js_AllocStack(cx, 2 + length, &mark);
1763     if (!invokevp)
1764     return JS_FALSE;
1765    
1766     sp = invokevp;
1767     *sp++ = vp[1];
1768     *sp++ = JSVAL_NULL; /* this is filled automagically */
1769     for (i = 0; i < length; i++) {
1770     ok = JS_GetElement(cx, aobj, (jsint)i, sp);
1771     if (!ok)
1772     goto out;
1773     sp++;
1774     }
1775    
1776     ok = js_InvokeConstructor(cx, length, JS_TRUE, invokevp);
1777     *vp = *invokevp;
1778     out:
1779     js_FreeStack(cx, mark);
1780     return ok;
1781     }
1782     #endif
1783    
1784     static JSFunctionSpec function_methods[] = {
1785     #if JS_HAS_TOSOURCE
1786     JS_FN(js_toSource_str, fun_toSource, 0,0),
1787     #endif
1788     JS_FN(js_toString_str, fun_toString, 0,0),
1789     JS_FN("apply", js_fun_apply, 2,0),
1790     JS_FN(call_str, fun_call, 1,0),
1791     #ifdef NARCISSUS
1792     JS_FN("__applyConstructor__", fun_applyConstructor, 0,1,0),
1793     #endif
1794     JS_FS_END
1795     };
1796    
1797     static JSBool
1798     Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1799     {
1800     JSStackFrame *fp, *caller;
1801     JSFunction *fun;
1802     JSObject *parent;
1803     uintN i, n, lineno;
1804     JSAtom *atom;
1805     const char *filename;
1806     JSBool ok;
1807     JSString *str, *arg;
1808     JSTokenStream ts;
1809     JSPrincipals *principals;
1810     jschar *collected_args, *cp;
1811     void *mark;
1812     size_t arg_length, args_length, old_args_length;
1813     JSTokenType tt;
1814    
1815     fp = cx->fp;
1816     if (!(fp->flags & JSFRAME_CONSTRUCTING)) {
1817     obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL, 0);
1818     if (!obj)
1819     return JS_FALSE;
1820     *rval = OBJECT_TO_JSVAL(obj);
1821     } else {
1822     /*
1823     * The constructor is called before the private slot is initialized so
1824     * we must use JS_GetPrivate, not GET_FUNCTION_PRIVATE here.
1825     */
1826     if (JS_GetPrivate(cx, obj))
1827     return JS_TRUE;
1828     }
1829    
1830     /*
1831     * NB: (new Function) is not lexically closed by its caller, it's just an
1832     * anonymous function in the top-level scope that its constructor inhabits.
1833     * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
1834     * and so would a call to f from another top-level's script or function.
1835     *
1836     * In older versions, before call objects, a new Function was adopted by
1837     * its running context's globalObject, which might be different from the
1838     * top-level reachable from scopeChain (in HTML frames, e.g.).
1839     */
1840     parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]));
1841    
1842     fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED,
1843     parent, cx->runtime->atomState.anonymousAtom);
1844    
1845     if (!fun)
1846     return JS_FALSE;
1847    
1848     /*
1849     * Function is static and not called directly by other functions in this
1850     * file, therefore it is callable only as a native function by js_Invoke.
1851     * Find the scripted caller, possibly skipping other native frames such as
1852     * are built for Function.prototype.call or .apply activations that invoke
1853     * Function indirectly from a script.
1854     */
1855     JS_ASSERT(!fp->script && fp->fun && fp->fun->u.n.native == Function);
1856     caller = JS_GetScriptedCaller(cx, fp);
1857     if (caller) {
1858     principals = JS_EvalFramePrincipals(cx, fp, caller);
1859     filename = js_ComputeFilename(cx, caller, principals, &lineno);
1860     } else {
1861     filename = NULL;
1862     lineno = 0;
1863     principals = NULL;
1864     }
1865    
1866     /* Belt-and-braces: check that the caller has access to parent. */
1867     if (!js_CheckPrincipalsAccess(cx, parent, principals,
1868     CLASS_ATOM(cx, Function))) {
1869     return JS_FALSE;
1870     }
1871    
1872     n = argc ? argc - 1 : 0;
1873     if (n > 0) {
1874     enum { OK, BAD, BAD_FORMAL } state;
1875    
1876     /*
1877     * Collect the function-argument arguments into one string, separated
1878     * by commas, then make a tokenstream from that string, and scan it to
1879     * get the arguments. We need to throw the full scanner at the
1880     * problem, because the argument string can legitimately contain
1881     * comments and linefeeds. XXX It might be better to concatenate
1882     * everything up into a function definition and pass it to the
1883     * compiler, but doing it this way is less of a delta from the old
1884     * code. See ECMA 15.3.2.1.
1885     */
1886     state = BAD_FORMAL;
1887     args_length = 0;
1888     for (i = 0; i < n; i++) {
1889     /* Collect the lengths for all the function-argument arguments. */
1890     arg = js_ValueToString(cx, argv[i]);
1891     if (!arg)
1892     return JS_FALSE;
1893     argv[i] = STRING_TO_JSVAL(arg);
1894    
1895     /*
1896     * Check for overflow. The < test works because the maximum
1897     * JSString length fits in 2 fewer bits than size_t has.
1898     */
1899     old_args_length = args_length;
1900     args_length = old_args_length + JSSTRING_LENGTH(arg);
1901     if (args_length < old_args_length) {
1902     js_ReportAllocationOverflow(cx);
1903     return JS_FALSE;
1904     }
1905     }
1906    
1907     /* Add 1 for each joining comma and check for overflow (two ways). */
1908     old_args_length = args_length;
1909     args_length = old_args_length + n - 1;
1910     if (args_length < old_args_length ||
1911     args_length >= ~(size_t)0 / sizeof(jschar)) {
1912     js_ReportAllocationOverflow(cx);
1913     return JS_FALSE;
1914     }
1915    
1916     /*
1917     * Allocate a string to hold the concatenated arguments, including room
1918     * for a terminating 0. Mark cx->tempPool for later release, to free
1919     * collected_args and its tokenstream in one swoop.
1920     */
1921     mark = JS_ARENA_MARK(&cx->tempPool);
1922     JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool,
1923     (args_length+1) * sizeof(jschar));
1924     if (!cp) {
1925     js_ReportOutOfScriptQuota(cx);
1926     return JS_FALSE;
1927     }
1928     collected_args = cp;
1929    
1930     /*
1931     * Concatenate the arguments into the new string, separated by commas.
1932     */
1933     for (i = 0; i < n; i++) {
1934     arg = JSVAL_TO_STRING(argv[i]);
1935     arg_length = JSSTRING_LENGTH(arg);
1936     (void) js_strncpy(cp, JSSTRING_CHARS(arg), arg_length);
1937     cp += arg_length;
1938    
1939     /* Add separating comma or terminating 0. */
1940     *cp++ = (i + 1 < n) ? ',' : 0;
1941     }
1942    
1943     /* Initialize a tokenstream that reads from the given string. */
1944     if (!js_InitTokenStream(cx, &ts, collected_args, args_length,
1945     NULL, filename, lineno)) {
1946     JS_ARENA_RELEASE(&cx->tempPool, mark);
1947     return JS_FALSE;
1948     }
1949    
1950     /* The argument string may be empty or contain no tokens. */
1951     tt = js_GetToken(cx, &ts);
1952     if (tt != TOK_EOF) {
1953     for (;;) {
1954     /*
1955     * Check that it's a name. This also implicitly guards against
1956     * TOK_ERROR, which was already reported.
1957     */
1958     if (tt != TOK_NAME)
1959     goto after_args;
1960    
1961     /*
1962     * Get the atom corresponding to the name from the token
1963     * stream; we're assured at this point that it's a valid
1964     * identifier.
1965     */
1966     atom = CURRENT_TOKEN(&ts).t_atom;
1967    
1968     /* Check for a duplicate parameter name. */
1969     if (js_LookupLocal(cx, fun, atom, NULL) != JSLOCAL_NONE) {
1970     const char *name;
1971    
1972     name = js_AtomToPrintableString(cx, atom);
1973     ok = name &&
1974     js_ReportCompileErrorNumber(cx, &ts, NULL,
1975     JSREPORT_WARNING |
1976     JSREPORT_STRICT,
1977     JSMSG_DUPLICATE_FORMAL,
1978     name);
1979     if (!ok)
1980     goto after_args;
1981     }
1982     if (!js_AddLocal(cx, fun, atom, JSLOCAL_ARG))
1983     goto after_args;
1984    
1985     /*
1986     * Get the next token. Stop on end of stream. Otherwise
1987     * insist on a comma, get another name, and iterate.
1988     */
1989     tt = js_GetToken(cx, &ts);
1990     if (tt == TOK_EOF)
1991     break;
1992     if (tt != TOK_COMMA)
1993     goto after_args;
1994     tt = js_GetToken(cx, &ts);
1995     }
1996     }
1997    
1998     state = OK;
1999     after_args:
2000     if (state == BAD_FORMAL && !(ts.flags & TSF_ERROR)) {
2001     /*
2002     * Report "malformed formal parameter" iff no illegal char or
2003     * similar scanner error was already reported.
2004     */
2005     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2006     JSMSG_BAD_FORMAL);
2007     }
2008     js_CloseTokenStream(cx, &ts);
2009     JS_ARENA_RELEASE(&cx->tempPool, mark);
2010     if (state != OK)
2011     return JS_FALSE;
2012     }
2013    
2014     if (argc) {
2015     str = js_ValueToString(cx, argv[argc-1]);
2016     if (!str)
2017     return JS_FALSE;
2018     argv[argc-1] = STRING_TO_JSVAL(str);
2019     } else {
2020     str = cx->runtime->emptyString;
2021     }
2022    
2023     return js_CompileFunctionBody(cx, fun, principals,
2024     JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
2025     filename, lineno);
2026     }
2027    
2028     JSObject *
2029     js_InitFunctionClass(JSContext *cx, JSObject *obj)
2030     {
2031     JSObject *proto;
2032     JSFunction *fun;
2033    
2034     proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
2035     function_props, function_methods, NULL, NULL);
2036     if (!proto)
2037     return NULL;
2038     fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
2039     if (!fun)
2040     goto bad;
2041     fun->u.i.script = js_NewScript(cx, 1, 0, 0, 0, 0, 0, 0);
2042     if (!fun->u.i.script)
2043     goto bad;
2044     fun->u.i.script->code[0] = JSOP_STOP;
2045     #ifdef CHECK_SCRIPT_OWNER
2046     fun->u.i.script->owner = NULL;
2047     #endif
2048     return proto;
2049    
2050     bad:
2051     cx->weakRoots.newborn[GCX_OBJECT] = NULL;
2052     return NULL;
2053     }
2054    
2055     JSObject *
2056     js_InitCallClass(JSContext *cx, JSObject *obj)
2057     {
2058     JSObject *proto;
2059    
2060     proto = JS_InitClass(cx, obj, NULL, &js_CallClass, NULL, 0,
2061     NULL, NULL, NULL, NULL);
2062     if (!proto)
2063     return NULL;
2064    
2065     /*
2066     * Null Call.prototype's proto slot so that Object.prototype.* does not
2067     * pollute the scope of heavyweight functions.
2068     */
2069     OBJ_CLEAR_PROTO(cx, proto);
2070     return proto;
2071     }
2072    
2073     JSFunction *
2074     js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
2075     uintN flags, JSObject *parent, JSAtom *atom)
2076     {
2077     JSFunction *fun;
2078    
2079     if (funobj) {
2080     JS_ASSERT(HAS_FUNCTION_CLASS(funobj));
2081     OBJ_SET_PARENT(cx, funobj, parent);
2082     } else {
2083     funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent, 0);
2084     if (!funobj)
2085     return NULL;
2086     }
2087     JS_ASSERT(JSVAL_IS_VOID(funobj->fslots[JSSLOT_PRIVATE]));
2088     fun = (JSFunction *) funobj;
2089    
2090     /* Initialize all function members. */
2091     fun->nargs = nargs;
2092     fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_INTERPRETED);
2093     if (flags & JSFUN_INTERPRETED) {
2094     JS_ASSERT(!native);
2095     JS_ASSERT(nargs == 0);
2096     fun->u.i.nvars = 0;
2097     fun->u.i.nupvars = 0;
2098     fun->u.i.script = NULL;
2099     #ifdef DEBUG
2100     fun->u.i.names.taggedAtom = 0;
2101     #endif
2102     } else {
2103     fun->u.n.native = native;
2104     fun->u.n.extra = 0;
2105     fun->u.n.spare = 0;
2106     fun->u.n.clasp = NULL;
2107     }
2108     fun->atom = atom;
2109    
2110     /* Set private to self to indicate non-cloned fully initialized function. */
2111     FUN_OBJECT(fun)->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun);
2112     return fun;
2113     }
2114    
2115     JSObject *
2116     js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent)
2117     {
2118     JSObject *clone;
2119    
2120     /*
2121     * The cloned function object does not need the extra fields beyond
2122     * JSObject as it points to fun via the private slot.
2123     */
2124     clone = js_NewObject(cx, &js_FunctionClass, NULL, parent,
2125     sizeof(JSObject));
2126     if (!clone)
2127     return NULL;
2128     clone->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun);
2129     return clone;
2130     }
2131    
2132     JSFunction *
2133     js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
2134     uintN nargs, uintN attrs)
2135     {
2136     JSFunction *fun;
2137     JSPropertyOp gsop;
2138    
2139     fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom);
2140     if (!fun)
2141     return NULL;
2142     gsop = (attrs & JSFUN_STUB_GSOPS) ? JS_PropertyStub : NULL;
2143     if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom),
2144     OBJECT_TO_JSVAL(FUN_OBJECT(fun)),
2145     gsop, gsop,
2146     attrs & ~JSFUN_FLAGS_MASK, NULL)) {
2147     return NULL;
2148     }
2149     return fun;
2150     }
2151    
2152     #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
2153     # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
2154     #endif
2155    
2156     JSFunction *
2157     js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags)
2158     {
2159     jsval v;
2160     JSObject *obj;
2161    
2162     v = *vp;
2163     obj = NULL;
2164     if (JSVAL_IS_OBJECT(v)) {
2165     obj = JSVAL_TO_OBJECT(v);
2166     if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) {
2167     if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v))
2168     return NULL;
2169     obj = VALUE_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL;
2170     }
2171     }
2172     if (!obj) {
2173     js_ReportIsNotFunction(cx, vp, flags);
2174     return NULL;
2175     }
2176     return GET_FUNCTION_PRIVATE(cx, obj);
2177     }
2178    
2179     JSObject *
2180     js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags)
2181     {
2182     JSFunction *fun;
2183     JSStackFrame *caller;
2184     JSPrincipals *principals;
2185    
2186     if (VALUE_IS_FUNCTION(cx, *vp))
2187     return JSVAL_TO_OBJECT(*vp);
2188    
2189     fun = js_ValueToFunction(cx, vp, flags);
2190     if (!fun)
2191     return NULL;
2192     *vp = OBJECT_TO_JSVAL(FUN_OBJECT(fun));
2193    
2194     caller = JS_GetScriptedCaller(cx, cx->fp);
2195     if (caller) {
2196     principals = JS_StackFramePrincipals(cx, caller);
2197     } else {
2198     /* No scripted caller, don't allow access. */
2199     principals = NULL;
2200     }
2201    
2202     if (!js_CheckPrincipalsAccess(cx, FUN_OBJECT(fun), principals,
2203     fun->atom
2204     ? fun->atom
2205     : cx->runtime->atomState.anonymousAtom)) {
2206     return NULL;
2207     }
2208     return FUN_OBJECT(fun);
2209     }
2210    
2211     JSObject *
2212     js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags)
2213     {
2214     JSObject *callable;
2215    
2216     callable = JSVAL_IS_PRIMITIVE(*vp) ? NULL : JSVAL_TO_OBJECT(*vp);
2217     if (callable &&
2218     ((callable->map->ops == &js_ObjectOps)
2219     ? OBJ_GET_CLASS(cx, callable)->call
2220     : callable->map->ops->call)) {
2221     *vp = OBJECT_TO_JSVAL(callable);
2222     } else {
2223     callable = js_ValueToFunctionObject(cx, vp, flags);
2224     }
2225     return callable;
2226     }
2227    
2228     void
2229     js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
2230     {
2231     JSStackFrame *fp;
2232     uintN error;
2233     const char *name, *source;
2234     JSTempValueRooter tvr;
2235    
2236     for (fp = cx->fp; fp && !fp->regs; fp = fp->down)
2237     continue;
2238     name = source = NULL;
2239     JS_PUSH_TEMP_ROOT_STRING(cx, NULL, &tvr);
2240     if (flags & JSV2F_ITERATOR) {
2241     error = JSMSG_BAD_ITERATOR;
2242     name = js_iterator_str;
2243     tvr.u.string = js_ValueToSource(cx, *vp);
2244     if (!tvr.u.string)
2245     goto out;
2246     tvr.u.string = js_QuoteString(cx, tvr.u.string, 0);
2247     if (!tvr.u.string)
2248     goto out;
2249     source = js_GetStringBytes(cx, tvr.u.string);
2250     if (!source)
2251     goto out;
2252     } else if (flags & JSV2F_CONSTRUCT) {
2253     error = JSMSG_NOT_CONSTRUCTOR;
2254     } else {
2255     error = JSMSG_NOT_FUNCTION;
2256     }
2257    
2258     js_ReportValueError3(cx, error,
2259     (fp && fp->regs &&
2260     StackBase(fp) <= vp && vp < fp->regs->sp)
2261     ? vp - fp->regs->sp
2262     : (flags & JSV2F_SEARCH_STACK)
2263     ? JSDVG_SEARCH_STACK
2264     : JSDVG_IGNORE_STACK,
2265     *vp, NULL,
2266     name, source);
2267    
2268     out:
2269     JS_POP_TEMP_ROOT(cx, &tvr);
2270     }
2271    
2272     /*
2273     * When a function has between 2 and MAX_ARRAY_LOCALS arguments and variables,
2274     * their name are stored as the JSLocalNames.array.
2275     */
2276     #define MAX_ARRAY_LOCALS 8
2277    
2278     JS_STATIC_ASSERT(2 <= MAX_ARRAY_LOCALS);
2279     JS_STATIC_ASSERT(MAX_ARRAY_LOCALS < JS_BITMASK(16));
2280    
2281     /*
2282     * We use the lowest bit of the string atom to distinguish const from var
2283     * name when there is only single name or when names are stored as an array.
2284     */
2285     JS_STATIC_ASSERT((JSVAL_STRING & 1) == 0);
2286    
2287     /*
2288     * When we use a hash table to store the local names, we use a singly linked
2289     * list to record the indexes of duplicated parameter names to preserve the
2290     * duplicates for the decompiler.
2291     */
2292     typedef struct JSNameIndexPair JSNameIndexPair;
2293    
2294     struct JSNameIndexPair {
2295     JSAtom *name;
2296     uint16 index;
2297     JSNameIndexPair *link;
2298     };
2299    
2300     struct JSLocalNameMap {
2301     JSDHashTable names;
2302     JSNameIndexPair *lastdup;
2303     };
2304    
2305     typedef struct JSLocalNameHashEntry {
2306     JSDHashEntryHdr hdr;
2307     JSAtom *name;
2308     uint16 index;
2309     uint8 localKind;
2310     } JSLocalNameHashEntry;
2311    
2312     static void
2313     FreeLocalNameHash(JSContext *cx, JSLocalNameMap *map)
2314     {
2315     JSNameIndexPair *dup, *next;
2316    
2317     for (dup = map->lastdup; dup; dup = next) {
2318     next = dup->link;
2319     JS_free(cx, dup);
2320     }
2321     JS_DHashTableFinish(&map->names);
2322     JS_free(cx, map);
2323     }
2324    
2325     static JSBool
2326     HashLocalName(JSContext *cx, JSLocalNameMap *map, JSAtom *name,
2327     JSLocalKind localKind, uintN index)
2328     {
2329     JSLocalNameHashEntry *entry;
2330     JSNameIndexPair *dup;
2331    
2332     JS_ASSERT(index <= JS_BITMASK(16));
2333     #if JS_HAS_DESTRUCTURING
2334     if (!name) {
2335     /* A destructuring pattern does not need a hash entry. */
2336     JS_ASSERT(localKind == JSLOCAL_ARG);
2337     return JS_TRUE;
2338     }
2339     #endif
2340     JS_ASSERT(ATOM_IS_STRING(name));
2341     entry = (JSLocalNameHashEntry *)
2342     JS_DHashTableOperate(&map->names, name, JS_DHASH_ADD);
2343     if (!entry) {
2344     JS_ReportOutOfMemory(cx);
2345     return JS_FALSE;
2346     }
2347     if (entry->name) {
2348     JS_ASSERT(entry->name == name);
2349     JS_ASSERT(entry->localKind == JSLOCAL_ARG);
2350     dup = (JSNameIndexPair *) JS_malloc(cx, sizeof *dup);
2351     if (!dup)
2352     return JS_FALSE;
2353     dup->name = entry->name;
2354     dup->index = entry->index;
2355     dup->link = map->lastdup;
2356     map->lastdup = dup;
2357     }
2358     entry->name = name;
2359     entry->index = (uint16) index;
2360     entry->localKind = (uint8) localKind;
2361     return JS_TRUE;
2362     }
2363    
2364     JSBool
2365     js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind)
2366     {
2367     jsuword taggedAtom;
2368     uint16 *indexp;
2369     uintN n, i;
2370     jsuword *array;
2371     JSLocalNameMap *map;
2372    
2373     JS_ASSERT(FUN_INTERPRETED(fun));
2374     JS_ASSERT(!fun->u.i.script);
2375     JS_ASSERT(((jsuword) atom & 1) == 0);
2376     taggedAtom = (jsuword) atom;
2377     if (kind == JSLOCAL_ARG) {
2378     indexp = &fun->nargs;
2379     } else if (kind == JSLOCAL_UPVAR) {
2380     indexp = &fun->u.i.nupvars;
2381     } else {
2382     indexp = &fun->u.i.nvars;
2383     if (kind == JSLOCAL_CONST)
2384     taggedAtom |= 1;
2385     else
2386     JS_ASSERT(kind == JSLOCAL_VAR);
2387     }
2388     n = JS_GET_LOCAL_NAME_COUNT(fun);
2389     if (n == 0) {
2390     JS_ASSERT(fun->u.i.names.taggedAtom == 0);
2391     fun->u.i.names.taggedAtom = taggedAtom;
2392     } else if (n < MAX_ARRAY_LOCALS) {
2393     if (n > 1) {
2394     array = fun->u.i.names.array;
2395     } else {
2396     array = (jsuword *) JS_malloc(cx, MAX_ARRAY_LOCALS * sizeof *array);
2397     if (!array)
2398     return JS_FALSE;
2399     array[0] = fun->u.i.names.taggedAtom;
2400     fun->u.i.names.array = array;
2401     }
2402     if (kind == JSLOCAL_ARG) {
2403     /*
2404     * A destructuring argument pattern adds variables, not arguments,
2405     * so for the following arguments nvars != 0.
2406     */
2407     #if JS_HAS_DESTRUCTURING
2408     if (fun->u.i.nvars != 0) {
2409     memmove(array + fun->nargs + 1, array + fun->nargs,
2410     fun->u.i.nvars * sizeof *array);
2411     }
2412     #else
2413     JS_ASSERT(fun->u.i.nvars == 0);
2414     #endif
2415     array[fun->nargs] = taggedAtom;
2416     } else {
2417     array[n] = taggedAtom;
2418     }
2419     } else if (n == MAX_ARRAY_LOCALS) {
2420     array = fun->u.i.names.array;
2421     map = (JSLocalNameMap *) JS_malloc(cx, sizeof *map);
2422     if (!map)
2423     return JS_FALSE;
2424     if (!JS_DHashTableInit(&map->names, JS_DHashGetStubOps(),
2425     NULL, sizeof(JSLocalNameHashEntry),
2426     JS_DHASH_DEFAULT_CAPACITY(MAX_ARRAY_LOCALS
2427     * 2))) {
2428     JS_ReportOutOfMemory(cx);
2429     JS_free(cx, map);
2430     return JS_FALSE;
2431     }
2432    
2433     map->lastdup = NULL;
2434     for (i = 0; i != MAX_ARRAY_LOCALS; ++i) {
2435     taggedAtom = array[i];
2436     if (!HashLocalName(cx, map, (JSAtom *) (taggedAtom & ~1),
2437     (i < fun->nargs)
2438     ? JSLOCAL_ARG
2439     : (taggedAtom & 1) ? JSLOCAL_CONST : JSLOCAL_VAR,
2440     (i < fun->nargs) ? i : i - fun->nargs)) {
2441     FreeLocalNameHash(cx, map);
2442     return JS_FALSE;
2443     }
2444     }
2445     if (!HashLocalName(cx, map, atom, kind, *indexp)) {
2446     FreeLocalNameHash(cx, map);
2447     return JS_FALSE;
2448     }
2449    
2450     /*
2451     * At this point the entry is added and we cannot fail. It is time
2452     * to replace fun->u.i.names with the built map.
2453     */
2454     fun->u.i.names.map = map;
2455     JS_free(cx, array);
2456     } else {
2457     if (*indexp == JS_BITMASK(16)) {
2458     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2459     (kind == JSLOCAL_ARG)
2460     ? JSMSG_TOO_MANY_FUN_ARGS
2461     : JSMSG_TOO_MANY_LOCALS);
2462     return JS_FALSE;
2463     }
2464     if (!HashLocalName(cx, fun->u.i.names.map, atom, kind, *indexp))
2465     return JS_FALSE;
2466     }
2467    
2468     /* Update the argument or variable counter. */
2469     ++*indexp;
2470     return JS_TRUE;
2471     }
2472    
2473     JSLocalKind
2474     js_LookupLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, uintN *indexp)
2475     {
2476     uintN n, i, upvar_base;
2477     jsuword *array;
2478     JSLocalNameHashEntry *entry;
2479    
2480     JS_ASSERT(FUN_INTERPRETED(fun));
2481     n = JS_GET_LOCAL_NAME_COUNT(fun);
2482     if (n == 0)
2483     return JSLOCAL_NONE;
2484     if (n <= MAX_ARRAY_LOCALS) {
2485     array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
2486    
2487     /* Search from the tail to pick up the last duplicated name. */
2488     i = n;
2489     upvar_base = JS_UPVAR_LOCAL_NAME_START(fun);
2490     do {
2491     --i;
2492     if (atom == JS_LOCAL_NAME_TO_ATOM(array[i])) {
2493     if (i < fun->nargs) {
2494     if (indexp)
2495     *indexp = i;
2496     return JSLOCAL_ARG;
2497     }
2498     if (i >= upvar_base) {
2499     if (indexp)
2500     *indexp = i - upvar_base;
2501     return JSLOCAL_UPVAR;
2502     }
2503     if (indexp)
2504     *indexp = i - fun->nargs;
2505     return JS_LOCAL_NAME_IS_CONST(array[i])
2506     ? JSLOCAL_CONST
2507     : JSLOCAL_VAR;
2508     }
2509     } while (i != 0);
2510     } else {
2511     entry = (JSLocalNameHashEntry *)
2512     JS_DHashTableOperate(&fun->u.i.names.map->names, atom,
2513     JS_DHASH_LOOKUP);
2514     if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) {
2515     JS_ASSERT(entry->localKind != JSLOCAL_NONE);
2516     if (indexp)
2517     *indexp = entry->index;
2518     return (JSLocalKind) entry->localKind;
2519     }
2520     }
2521     return JSLOCAL_NONE;
2522     }
2523    
2524     typedef struct JSLocalNameEnumeratorArgs {
2525     JSFunction *fun;
2526     jsuword *names;
2527     #ifdef DEBUG
2528     uintN nCopiedArgs;
2529     uintN nCopiedVars;
2530     #endif
2531     } JSLocalNameEnumeratorArgs;
2532    
2533     static JSDHashOperator
2534     get_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
2535     uint32 number, void *arg)
2536     {
2537     JSLocalNameHashEntry *entry;
2538     JSLocalNameEnumeratorArgs *args;
2539     uint i;
2540     jsuword constFlag;
2541    
2542     entry = (JSLocalNameHashEntry *) hdr;
2543     args = (JSLocalNameEnumeratorArgs *) arg;
2544     JS_ASSERT(entry->name);
2545     if (entry->localKind == JSLOCAL_ARG) {
2546     JS_ASSERT(entry->index < args->fun->nargs);
2547     JS_ASSERT(args->nCopiedArgs++ < args->fun->nargs);
2548     i = entry->index;
2549     constFlag = 0;
2550     } else {
2551     JS_ASSERT(entry->localKind == JSLOCAL_VAR ||
2552     entry->localKind == JSLOCAL_CONST);
2553     JS_ASSERT(entry->index < args->fun->u.i.nvars);
2554     JS_ASSERT(args->nCopiedVars++ < args->fun->u.i.nvars);
2555     i = args->fun->nargs + entry->index;
2556     constFlag = (entry->localKind == JSLOCAL_CONST);
2557     }
2558     args->names[i] = (jsuword) entry->name | constFlag;
2559     return JS_DHASH_NEXT;
2560     }
2561    
2562     jsuword *
2563     js_GetLocalNameArray(JSContext *cx, JSFunction *fun, JSArenaPool *pool)
2564     {
2565     uintN n;
2566     jsuword *names;
2567     JSLocalNameMap *map;
2568     JSLocalNameEnumeratorArgs args;
2569     JSNameIndexPair *dup;
2570    
2571     JS_ASSERT(FUN_INTERPRETED(fun));
2572     n = JS_GET_LOCAL_NAME_COU