Code

fix 1243587 and misc fixes
[inkscape.git] / src / dom / js / jsfun.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sw=4 et tw=78:
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 ***** */
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 "jsconfig.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"
69 /* Generic function/call/arguments tinyids -- also reflected bit numbers. */
70 enum {
71     CALL_ARGUMENTS  = -1,       /* predefined arguments local variable */
72     CALL_CALLEE     = -2,       /* reference to active function's object */
73     ARGS_LENGTH     = -3,       /* number of actual args, arity if inactive */
74     ARGS_CALLEE     = -4,       /* reference from arguments to active funobj */
75     FUN_ARITY       = -5,       /* number of formal parameters; desired argc */
76     FUN_NAME        = -6,       /* function name, "" if anonymous */
77     FUN_CALLER      = -7        /* Function.prototype.caller, backward compat */
78 };
80 #if JSFRAME_OVERRIDE_BITS < 8
81 # error "not enough override bits in JSStackFrame.flags!"
82 #endif
84 #define TEST_OVERRIDE_BIT(fp, tinyid) \
85     ((fp)->flags & JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1)))
87 #define SET_OVERRIDE_BIT(fp, tinyid) \
88     ((fp)->flags |= JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1)))
90 #if JS_HAS_ARGS_OBJECT
92 JSBool
93 js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp)
94 {
95     JSObject *argsobj;
97     if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
98         JS_ASSERT(fp->callobj);
99         return OBJ_GET_PROPERTY(cx, fp->callobj,
100                                 ATOM_TO_JSID(cx->runtime->atomState
101                                              .argumentsAtom),
102                                 vp);
103     }
104     argsobj = js_GetArgsObject(cx, fp);
105     if (!argsobj)
106         return JS_FALSE;
107     *vp = OBJECT_TO_JSVAL(argsobj);
108     return JS_TRUE;
111 static JSBool
112 MarkArgDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
114     JSObject *argsobj;
115     jsval bmapval, bmapint;
116     size_t nbits, nbytes;
117     jsbitmap *bitmap;
119     argsobj = fp->argsobj;
120     (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
121     nbits = fp->argc;
122     JS_ASSERT(slot < nbits);
123     if (JSVAL_IS_VOID(bmapval)) {
124         if (nbits <= JSVAL_INT_BITS) {
125             bmapint = 0;
126             bitmap = (jsbitmap *) &bmapint;
127         } else {
128             nbytes = JS_HOWMANY(nbits, JS_BITS_PER_WORD) * sizeof(jsbitmap);
129             bitmap = (jsbitmap *) JS_malloc(cx, nbytes);
130             if (!bitmap)
131                 return JS_FALSE;
132             memset(bitmap, 0, nbytes);
133             bmapval = PRIVATE_TO_JSVAL(bitmap);
134             JS_SetReservedSlot(cx, argsobj, 0, bmapval);
135         }
136     } else {
137         if (nbits <= JSVAL_INT_BITS) {
138             bmapint = JSVAL_TO_INT(bmapval);
139             bitmap = (jsbitmap *) &bmapint;
140         } else {
141             bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval);
142         }
143     }
144     JS_SET_BIT(bitmap, slot);
145     if (bitmap == (jsbitmap *) &bmapint) {
146         bmapval = INT_TO_JSVAL(bmapint);
147         JS_SetReservedSlot(cx, argsobj, 0, bmapval);
148     }
149     return JS_TRUE;
152 /* NB: Infallible predicate, false does not mean error/exception. */
153 static JSBool
154 ArgWasDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
156     JSObject *argsobj;
157     jsval bmapval, bmapint;
158     jsbitmap *bitmap;
160     argsobj = fp->argsobj;
161     (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
162     if (JSVAL_IS_VOID(bmapval))
163         return JS_FALSE;
164     if (fp->argc <= JSVAL_INT_BITS) {
165         bmapint = JSVAL_TO_INT(bmapval);
166         bitmap = (jsbitmap *) &bmapint;
167     } else {
168         bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval);
169     }
170     return JS_TEST_BIT(bitmap, slot) != 0;
173 JSBool
174 js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id,
175                    JSObject **objp, jsval *vp)
177     jsval val;
178     JSObject *obj;
179     uintN slot;
181     if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
182         JS_ASSERT(fp->callobj);
183         if (!OBJ_GET_PROPERTY(cx, fp->callobj,
184                               ATOM_TO_JSID(cx->runtime->atomState
185                                            .argumentsAtom),
186                               &val)) {
187             return JS_FALSE;
188         }
189         if (JSVAL_IS_PRIMITIVE(val)) {
190             obj = js_ValueToNonNullObject(cx, val);
191             if (!obj)
192                 return JS_FALSE;
193         } else {
194             obj = JSVAL_TO_OBJECT(val);
195         }
196         *objp = obj;
197         return OBJ_GET_PROPERTY(cx, obj, id, vp);
198     }
200     *objp = NULL;
201     *vp = JSVAL_VOID;
202     if (JSID_IS_INT(id)) {
203         slot = (uintN) JSID_TO_INT(id);
204         if (slot < fp->argc) {
205             if (fp->argsobj && ArgWasDeleted(cx, fp, slot))
206                 return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
207             *vp = fp->argv[slot];
208         } else {
209             /*
210              * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share
211              * storage between the formal parameter and arguments[k] for all
212              * k >= fp->argc && k < fp->fun->nargs.  For example, in
213              *
214              *   function f(x) { x = 42; return arguments[0]; }
215              *   f();
216              *
217              * the call to f should return undefined, not 42.  If fp->argsobj
218              * is null at this point, as it would be in the example, return
219              * undefined in *vp.
220              */
221             if (fp->argsobj)
222                 return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
223         }
224     } else {
225         if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
226             if (fp->argsobj && TEST_OVERRIDE_BIT(fp, ARGS_LENGTH))
227                 return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
228             *vp = INT_TO_JSVAL((jsint) fp->argc);
229         }
230     }
231     return JS_TRUE;
234 JSObject *
235 js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
237     JSObject *argsobj;
239     /* Skip eval and debugger frames. */
240     while (fp->flags & JSFRAME_SPECIAL)
241         fp = fp->down;
243     /* Create an arguments object for fp only if it lacks one. */
244     argsobj = fp->argsobj;
245     if (argsobj)
246         return argsobj;
248     /* Link the new object to fp so it can get actual argument values. */
249     argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL);
250     if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) {
251         cx->newborn[GCX_OBJECT] = NULL;
252         return NULL;
253     }
254     fp->argsobj = argsobj;
255     return argsobj;
258 static JSBool
259 args_enumerate(JSContext *cx, JSObject *obj);
261 JSBool
262 js_PutArgsObject(JSContext *cx, JSStackFrame *fp)
264     JSObject *argsobj;
265     jsval bmapval, rval;
266     JSBool ok;
267     JSRuntime *rt;
269     /*
270      * Reuse args_enumerate here to reflect fp's actual arguments as indexed
271      * elements of argsobj.  Do this first, before clearing and freeing the
272      * deleted argument slot bitmap, because args_enumerate depends on that.
273      */
274     argsobj = fp->argsobj;
275     ok = args_enumerate(cx, argsobj);
277     /*
278      * Now clear the deleted argument number bitmap slot and free the bitmap,
279      * if one was actually created due to 'delete arguments[0]' or similar.
280      */
281     (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
282     if (!JSVAL_IS_VOID(bmapval)) {
283         JS_SetReservedSlot(cx, argsobj, 0, JSVAL_VOID);
284         if (fp->argc > JSVAL_INT_BITS)
285             JS_free(cx, JSVAL_TO_PRIVATE(bmapval));
286     }
288     /*
289      * Now get the prototype properties so we snapshot fp->fun and fp->argc
290      * before fp goes away.
291      */
292     rt = cx->runtime;
293     ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom),
294                          &rval);
295     ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom),
296                          &rval);
297     ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom),
298                          &rval);
299     ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom),
300                          &rval);
302     /*
303      * Clear the private pointer to fp, which is about to go away (js_Invoke).
304      * Do this last because the args_enumerate and js_GetProperty calls above
305      * need to follow the private slot to find fp.
306      */
307     ok &= JS_SetPrivate(cx, argsobj, NULL);
308     fp->argsobj = NULL;
309     return ok;
312 static JSBool
313 args_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
315     jsint slot;
316     JSStackFrame *fp;
318     if (!JSVAL_IS_INT(id))
319         return JS_TRUE;
320     fp = (JSStackFrame *)
321          JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
322     if (!fp)
323         return JS_TRUE;
324     JS_ASSERT(fp->argsobj);
326     slot = JSVAL_TO_INT(id);
327     switch (slot) {
328       case ARGS_CALLEE:
329       case ARGS_LENGTH:
330         SET_OVERRIDE_BIT(fp, slot);
331         break;
333       default:
334         if ((uintN)slot < fp->argc && !MarkArgDeleted(cx, fp, slot))
335             return JS_FALSE;
336         break;
337     }
338     return JS_TRUE;
341 static JSBool
342 args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
344     jsint slot;
345     JSStackFrame *fp;
347     if (!JSVAL_IS_INT(id))
348         return JS_TRUE;
349     fp = (JSStackFrame *)
350          JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
351     if (!fp)
352         return JS_TRUE;
353     JS_ASSERT(fp->argsobj);
355     slot = JSVAL_TO_INT(id);
356     switch (slot) {
357       case ARGS_CALLEE:
358         if (!TEST_OVERRIDE_BIT(fp, slot))
359             *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object);
360         break;
362       case ARGS_LENGTH:
363         if (!TEST_OVERRIDE_BIT(fp, slot))
364             *vp = INT_TO_JSVAL((jsint)fp->argc);
365         break;
367       default:
368         if ((uintN)slot < fp->argc && !ArgWasDeleted(cx, fp, slot))
369             *vp = fp->argv[slot];
370         break;
371     }
372     return JS_TRUE;
375 static JSBool
376 args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
378     JSStackFrame *fp;
379     jsint slot;
381     if (!JSVAL_IS_INT(id))
382         return JS_TRUE;
383     fp = (JSStackFrame *)
384          JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
385     if (!fp)
386         return JS_TRUE;
387     JS_ASSERT(fp->argsobj);
389     slot = JSVAL_TO_INT(id);
390     switch (slot) {
391       case ARGS_CALLEE:
392       case ARGS_LENGTH:
393         SET_OVERRIDE_BIT(fp, slot);
394         break;
396       default:
397         if (fp->fun->interpreted &&
398             (uintN)slot < fp->argc &&
399             !ArgWasDeleted(cx, fp, slot)) {
400             fp->argv[slot] = *vp;
401         }
402         break;
403     }
404     return JS_TRUE;
407 static JSBool
408 args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
409              JSObject **objp)
411     JSStackFrame *fp;
412     uintN slot;
413     JSString *str;
414     JSAtom *atom;
415     intN tinyid;
416     jsval value;
418     *objp = NULL;
419     fp = (JSStackFrame *)
420          JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
421     if (!fp)
422         return JS_TRUE;
423     JS_ASSERT(fp->argsobj);
425     if (JSVAL_IS_INT(id)) {
426         slot = JSVAL_TO_INT(id);
427         if (slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) {
428             /* XXX ECMA specs DontEnum, contrary to other array-like objects */
429             if (!js_DefineProperty(cx, obj, INT_JSVAL_TO_JSID(id),
430                                    fp->argv[slot],
431                                    args_getProperty, args_setProperty,
432                                    JS_VERSION_IS_ECMA(cx)
433                                    ? 0
434                                    : JSPROP_ENUMERATE,
435                                    NULL)) {
436                 return JS_FALSE;
437             }
438             *objp = obj;
439         }
440     } else {
441         str = JSVAL_TO_STRING(id);
442         atom = cx->runtime->atomState.lengthAtom;
443         if (str == ATOM_TO_STRING(atom)) {
444             tinyid = ARGS_LENGTH;
445             value = INT_TO_JSVAL(fp->argc);
446         } else {
447             atom = cx->runtime->atomState.calleeAtom;
448             if (str == ATOM_TO_STRING(atom)) {
449                 tinyid = ARGS_CALLEE;
450                 value = fp->argv ? fp->argv[-2]
451                                  : OBJECT_TO_JSVAL(fp->fun->object);
452             } else {
453                 atom = NULL;
455                 /* Quell GCC overwarnings. */
456                 tinyid = 0;
457                 value = JSVAL_NULL;
458             }
459         }
461         if (atom && !TEST_OVERRIDE_BIT(fp, tinyid)) {
462             if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value,
463                                          args_getProperty, args_setProperty, 0,
464                                          SPROP_HAS_SHORTID, tinyid, NULL)) {
465                 return JS_FALSE;
466             }
467             *objp = obj;
468         }
469     }
471     return JS_TRUE;
474 static JSBool
475 args_enumerate(JSContext *cx, JSObject *obj)
477     JSStackFrame *fp;
478     JSObject *pobj;
479     JSProperty *prop;
480     uintN slot, argc;
482     fp = (JSStackFrame *)
483          JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
484     if (!fp)
485         return JS_TRUE;
486     JS_ASSERT(fp->argsobj);
488     /*
489      * Trigger reflection with value snapshot in args_resolve using a series
490      * of js_LookupProperty calls.  We handle length, callee, and the indexed
491      * argument properties.  We know that args_resolve covers all these cases
492      * and creates direct properties of obj, but that it may fail to resolve
493      * length or callee if overridden.
494      */
495     if (!js_LookupProperty(cx, obj,
496                            ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
497                            &pobj, &prop)) {
498         return JS_FALSE;
499     }
500     if (prop)
501         OBJ_DROP_PROPERTY(cx, pobj, prop);
503     if (!js_LookupProperty(cx, obj,
504                            ATOM_TO_JSID(cx->runtime->atomState.calleeAtom),
505                            &pobj, &prop)) {
506         return JS_FALSE;
507     }
508     if (prop)
509         OBJ_DROP_PROPERTY(cx, pobj, prop);
511     argc = fp->argc;
512     for (slot = 0; slot < argc; slot++) {
513         if (!js_LookupProperty(cx, obj, INT_TO_JSID((jsint)slot), &pobj, &prop))
514             return JS_FALSE;
515         if (prop)
516             OBJ_DROP_PROPERTY(cx, pobj, prop);
517     }
518     return JS_TRUE;
521 /*
522  * The Arguments class is not initialized via JS_InitClass, and must not be,
523  * because its name is "Object".  Per ECMA, that causes instances of it to
524  * delegate to the object named by Object.prototype.  It also ensures that
525  * arguments.toString() returns "[object Object]".
526  *
527  * The JSClass functions below collaborate to lazily reflect and synchronize
528  * actual argument values, argument count, and callee function object stored
529  * in a JSStackFrame with their corresponding property values in the frame's
530  * arguments object.
531  */
532 JSClass js_ArgumentsClass = {
533     js_Object_str,
534     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1),
535     JS_PropertyStub,  args_delProperty,
536     args_getProperty, args_setProperty,
537     args_enumerate,   (JSResolveOp) args_resolve,
538     JS_ConvertStub,   JS_FinalizeStub,
539     JSCLASS_NO_OPTIONAL_MEMBERS
540 };
542 #endif /* JS_HAS_ARGS_OBJECT */
544 #if JS_HAS_CALL_OBJECT
546 JSObject *
547 js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent)
549     JSObject *callobj, *funobj;
551     /* Create a call object for fp only if it lacks one. */
552     JS_ASSERT(fp->fun);
553     callobj = fp->callobj;
554     if (callobj)
555         return callobj;
556     JS_ASSERT(fp->fun);
558     /* The default call parent is its function's parent (static link). */
559     if (!parent) {
560         funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object;
561         if (funobj)
562             parent = OBJ_GET_PARENT(cx, funobj);
563     }
565     /* Create the call object and link it to its stack frame. */
566     callobj = js_NewObject(cx, &js_CallClass, NULL, parent);
567     if (!callobj || !JS_SetPrivate(cx, callobj, fp)) {
568         cx->newborn[GCX_OBJECT] = NULL;
569         return NULL;
570     }
571     fp->callobj = callobj;
573     /* Make callobj be the scope chain and the variables object. */
574     fp->scopeChain = callobj;
575     fp->varobj = callobj;
576     return callobj;
579 static JSBool
580 call_enumerate(JSContext *cx, JSObject *obj);
582 JSBool
583 js_PutCallObject(JSContext *cx, JSStackFrame *fp)
585     JSObject *callobj;
586     JSBool ok;
587     jsid argsid;
588     jsval aval;
590     /*
591      * Reuse call_enumerate here to reflect all actual args and vars into the
592      * call object from fp.
593      */
594     callobj = fp->callobj;
595     if (!callobj)
596         return JS_TRUE;
597     ok = call_enumerate(cx, callobj);
599     /*
600      * Get the arguments object to snapshot fp's actual argument values.
601      */
602     if (fp->argsobj) {
603         argsid = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom);
604         ok &= js_GetProperty(cx, callobj, argsid, &aval);
605         ok &= js_SetProperty(cx, callobj, argsid, &aval);
606         ok &= js_PutArgsObject(cx, fp);
607     }
609     /*
610      * Clear the private pointer to fp, which is about to go away (js_Invoke).
611      * Do this last because the call_enumerate and js_GetProperty calls above
612      * need to follow the private slot to find fp.
613      */
614     ok &= JS_SetPrivate(cx, callobj, NULL);
615     fp->callobj = NULL;
616     return ok;
619 static JSPropertySpec call_props[] = {
620     {js_arguments_str,  CALL_ARGUMENTS, JSPROP_PERMANENT,0,0},
621     {"__callee__",      CALL_CALLEE,    0,0,0},
622     {0,0,0,0,0}
623 };
625 static JSBool
626 call_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
628     JSStackFrame *fp;
629     jsint slot;
631     if (!JSVAL_IS_INT(id))
632         return JS_TRUE;
633     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
634     if (!fp)
635         return JS_TRUE;
636     JS_ASSERT(fp->fun);
638     slot = JSVAL_TO_INT(id);
639     switch (slot) {
640       case CALL_ARGUMENTS:
641         if (!TEST_OVERRIDE_BIT(fp, slot)) {
642             JSObject *argsobj = js_GetArgsObject(cx, fp);
643             if (!argsobj)
644                 return JS_FALSE;
645             *vp = OBJECT_TO_JSVAL(argsobj);
646         }
647         break;
649       case CALL_CALLEE:
650         if (!TEST_OVERRIDE_BIT(fp, slot))
651             *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object);
652         break;
654       default:
655         if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs))
656             *vp = fp->argv[slot];
657         break;
658     }
659     return JS_TRUE;
662 static JSBool
663 call_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
665     JSStackFrame *fp;
666     jsint slot;
668     if (!JSVAL_IS_INT(id))
669         return JS_TRUE;
670     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
671     if (!fp)
672         return JS_TRUE;
673     JS_ASSERT(fp->fun);
675     slot = JSVAL_TO_INT(id);
676     switch (slot) {
677       case CALL_ARGUMENTS:
678       case CALL_CALLEE:
679         SET_OVERRIDE_BIT(fp, slot);
680         break;
682       default:
683         if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs))
684             fp->argv[slot] = *vp;
685         break;
686     }
687     return JS_TRUE;
690 JSBool
691 js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
693     JSStackFrame *fp;
695     JS_ASSERT(JSVAL_IS_INT(id));
696     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
697     if (fp) {
698         /* XXX no jsint slot commoning here to avoid MSVC1.52 crashes */
699         if ((uintN)JSVAL_TO_INT(id) < fp->nvars)
700             *vp = fp->vars[JSVAL_TO_INT(id)];
701     }
702     return JS_TRUE;
705 JSBool
706 js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
708     JSStackFrame *fp;
710     JS_ASSERT(JSVAL_IS_INT(id));
711     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
712     if (fp) {
713         /* XXX jsint slot is block-local here to avoid MSVC1.52 crashes */
714         jsint slot = JSVAL_TO_INT(id);
715         if ((uintN)slot < fp->nvars)
716             fp->vars[slot] = *vp;
717     }
718     return JS_TRUE;
721 static JSBool
722 call_enumerate(JSContext *cx, JSObject *obj)
724     JSStackFrame *fp;
725     JSObject *funobj, *pobj;
726     JSScope *scope;
727     JSScopeProperty *sprop, *cprop;
728     JSPropertyOp getter;
729     jsval *vec;
730     JSAtom *atom;
731     JSProperty *prop;
733     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
734     if (!fp)
735         return JS_TRUE;
737     /*
738      * Do not enumerate a cloned function object at fp->argv[-2], it may have
739      * gained its own (mutable) scope (e.g., a brutally-shared XUL script sets
740      * the clone's prototype property).  We must enumerate the function object
741      * that was decorated with parameter and local variable properties by the
742      * compiler when the compiler created fp->fun, namely fp->fun->object.
743      *
744      * Contrast with call_resolve, where we prefer fp->argv[-2], because we'll
745      * use js_LookupProperty to find any overridden properties in that object,
746      * if it was a mutated clone; and if not, we will search its prototype,
747      * fp->fun->object, to find compiler-created params and locals.
748      */
749     funobj = fp->fun->object;
750     if (!funobj)
751         return JS_TRUE;
753     /*
754      * Reflect actual args from fp->argv for formal parameters, and local vars
755      * and functions in fp->vars for declared variables and nested-at-top-level
756      * local functions.
757      */
758     scope = OBJ_SCOPE(funobj);
759     for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
760         getter = sprop->getter;
761         if (getter == js_GetArgument)
762             vec = fp->argv;
763         else if (getter == js_GetLocalVariable)
764             vec = fp->vars;
765         else
766             continue;
768         /* Trigger reflection by looking up the unhidden atom for sprop->id. */
769         JS_ASSERT(JSID_IS_ATOM(sprop->id));
770         atom = JSID_TO_ATOM(sprop->id);
771         JS_ASSERT(atom->flags & ATOM_HIDDEN);
772         atom = atom->entry.value;
774         if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
775             return JS_FALSE;
777         /*
778          * If we found the property in a different object, don't try sticking
779          * it into wrong slots vector. This can occur because we have a mutable
780          * __proto__ slot, and cloned function objects rely on their __proto__
781          * to delegate to the object that contains the var and arg properties.
782          */
783         if (!prop || pobj != obj) {
784             if (prop)
785                 OBJ_DROP_PROPERTY(cx, pobj, prop);
786             continue;
787         }
788         cprop = (JSScopeProperty *)prop;
789         LOCKED_OBJ_SET_SLOT(obj, cprop->slot, vec[(uint16) sprop->shortid]);
790         OBJ_DROP_PROPERTY(cx, obj, prop);
791     }
793     return JS_TRUE;
796 static JSBool
797 call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
798              JSObject **objp)
800     JSStackFrame *fp;
801     JSObject *funobj;
802     JSString *str;
803     JSAtom *atom;
804     JSObject *obj2;
805     JSProperty *prop;
806     JSScopeProperty *sprop;
807     JSPropertyOp getter, setter;
808     uintN attrs, slot, nslots, spflags;
809     jsval *vp, value;
810     intN shortid;
812     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
813     if (!fp)
814         return JS_TRUE;
815     JS_ASSERT(fp->fun);
817     if (!JSVAL_IS_STRING(id))
818         return JS_TRUE;
820     funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object;
821     if (!funobj)
822         return JS_TRUE;
823     JS_ASSERT((JSFunction *) JS_GetPrivate(cx, funobj) == fp->fun);
825     str = JSVAL_TO_STRING(id);
826     atom = js_AtomizeString(cx, str, 0);
827     if (!atom)
828         return JS_FALSE;
829     if (!js_LookupHiddenProperty(cx, funobj, ATOM_TO_JSID(atom), &obj2, &prop))
830         return JS_FALSE;
832     if (prop) {
833         if (!OBJ_IS_NATIVE(obj2)) {
834             OBJ_DROP_PROPERTY(cx, obj2, prop);
835             return JS_TRUE;
836         }
838         sprop = (JSScopeProperty *) prop;
839         getter = sprop->getter;
840         attrs = sprop->attrs & ~JSPROP_SHARED;
841         slot = (uintN) sprop->shortid;
842         OBJ_DROP_PROPERTY(cx, obj2, prop);
844         /* Ensure we found an arg or var property for the same function. */
845         if ((sprop->flags & SPROP_IS_HIDDEN) &&
846             (obj2 == funobj ||
847              (JSFunction *) JS_GetPrivate(cx, obj2) == fp->fun)) {
848             if (getter == js_GetArgument) {
849                 vp = fp->argv;
850                 nslots = JS_MAX(fp->argc, fp->fun->nargs);
851                 getter = setter = NULL;
852             } else {
853                 JS_ASSERT(getter == js_GetLocalVariable);
854                 vp = fp->vars;
855                 nslots = fp->nvars;
856                 getter = js_GetCallVariable;
857                 setter = js_SetCallVariable;
858             }
859             if (slot < nslots) {
860                 value = vp[slot];
861                 spflags = SPROP_HAS_SHORTID;
862                 shortid = (intN) slot;
863             } else {
864                 value = JSVAL_VOID;
865                 spflags = 0;
866                 shortid = 0;
867             }
868             if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value,
869                                          getter, setter, attrs,
870                                          spflags, shortid, NULL)) {
871                 return JS_FALSE;
872             }
873             *objp = obj;
874         }
875     }
877     return JS_TRUE;
880 static JSBool
881 call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
883     JSStackFrame *fp;
885     if (type == JSTYPE_FUNCTION) {
886         fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
887         if (fp) {
888             JS_ASSERT(fp->fun);
889             *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object);
890         }
891     }
892     return JS_TRUE;
895 JSClass js_CallClass = {
896     js_Call_str,
897     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
898     JS_PropertyStub,  JS_PropertyStub,
899     call_getProperty, call_setProperty,
900     call_enumerate,   (JSResolveOp)call_resolve,
901     call_convert,     JS_FinalizeStub,
902     JSCLASS_NO_OPTIONAL_MEMBERS
903 };
905 #endif /* JS_HAS_CALL_OBJECT */
907 /*
908  * ECMA-262 specifies that length is a property of function object instances,
909  * but we can avoid that space cost by delegating to a prototype property that
910  * is JSPROP_PERMANENT and JSPROP_SHARED.  Each fun_getProperty call computes
911  * a fresh length value based on the arity of the individual function object's
912  * private data.
913  *
914  * The extensions below other than length, i.e., the ones not in ECMA-262,
915  * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility
916  * with ECMA we must allow a delegating object to override them.
917  */
918 #define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED)
920 static JSPropertySpec function_props[] = {
921     {js_arguments_str, CALL_ARGUMENTS, JSPROP_PERMANENT,  0,0},
922     {js_arity_str,     FUN_ARITY,      JSPROP_PERMANENT,  0,0},
923     {js_caller_str,    FUN_CALLER,     JSPROP_PERMANENT,  0,0},
924     {js_length_str,    ARGS_LENGTH,    LENGTH_PROP_ATTRS, 0,0},
925     {js_name_str,      FUN_NAME,       JSPROP_PERMANENT,  0,0},
926     {0,0,0,0,0}
927 };
929 static JSBool
930 fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
932     jsint slot;
933     JSFunction *fun;
934     JSStackFrame *fp;
936     if (!JSVAL_IS_INT(id))
937         return JS_TRUE;
938     slot = JSVAL_TO_INT(id);
940     /*
941      * Loop because getter and setter can be delegated from another class,
942      * but loop only for ARGS_LENGTH because we must pretend that f.length
943      * is in each function instance f, per ECMA-262, instead of only in the
944      * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
945      * to make it appear so).
946      *
947      * This code couples tightly to the attributes for the function_props[]
948      * initializers above, and to js_SetProperty and js_HasOwnPropertyHelper.
949      *
950      * It's important to allow delegating objects, even though they inherit
951      * this getter (fun_getProperty), to override arguments, arity, caller,
952      * and name.  If we didn't return early for slot != ARGS_LENGTH, we would
953      * clobber *vp with the native property value, instead of letting script
954      * override that value in delegating objects.
955      *
956      * Note how that clobbering is what simulates JSPROP_READONLY for all of
957      * the non-standard properties when the directly addressed object (obj)
958      * is a function object (i.e., when this loop does not iterate).
959      */
960     while (!(fun = (JSFunction *)
961                    JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) {
962         if (slot != ARGS_LENGTH)
963             return JS_TRUE;
964         obj = OBJ_GET_PROTO(cx, obj);
965         if (!obj)
966             return JS_TRUE;
967     }
969     /* Find fun's top-most activation record. */
970     for (fp = cx->fp; fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL));
971          fp = fp->down) {
972         continue;
973     }
975     switch (slot) {
976       case CALL_ARGUMENTS:
977 #if JS_HAS_ARGS_OBJECT
978         /* Warn if strict about f.arguments or equivalent unqualified uses. */
979         if (!JS_ReportErrorFlagsAndNumber(cx,
980                                           JSREPORT_WARNING | JSREPORT_STRICT,
981                                           js_GetErrorMessage, NULL,
982                                           JSMSG_DEPRECATED_USAGE,
983                                           js_arguments_str)) {
984             return JS_FALSE;
985         }
986         if (fp) {
987             if (!js_GetArgsValue(cx, fp, vp))
988                 return JS_FALSE;
989         } else {
990             *vp = JSVAL_NULL;
991         }
992         break;
993 #else  /* !JS_HAS_ARGS_OBJECT */
994         *vp = OBJECT_TO_JSVAL(fp ? obj : NULL);
995         break;
996 #endif /* !JS_HAS_ARGS_OBJECT */
998       case ARGS_LENGTH:
999         if (!JS_VERSION_IS_ECMA(cx))
1000             *vp = INT_TO_JSVAL((jsint)(fp && fp->fun ? fp->argc : fun->nargs));
1001         else
1002       case FUN_ARITY:
1003             *vp = INT_TO_JSVAL((jsint)fun->nargs);
1004         break;
1006       case FUN_NAME:
1007         *vp = fun->atom
1008               ? ATOM_KEY(fun->atom)
1009               : STRING_TO_JSVAL(cx->runtime->emptyString);
1010         break;
1012       case FUN_CALLER:
1013         while (fp && (fp->flags & JSFRAME_SKIP_CALLER) && fp->down)
1014             fp = fp->down;
1015         if (fp && fp->down && fp->down->fun && fp->down->argv)
1016             *vp = fp->down->argv[-2];
1017         else
1018             *vp = JSVAL_NULL;
1019         if (!JSVAL_IS_PRIMITIVE(*vp) && cx->runtime->checkObjectAccess) {
1020             id = ATOM_KEY(cx->runtime->atomState.callerAtom);
1021             if (!cx->runtime->checkObjectAccess(cx, obj, id, JSACC_READ, vp))
1022                 return JS_FALSE;
1023         }
1024         break;
1026       default:
1027         /* XXX fun[0] and fun.arguments[0] are equivalent. */
1028         if (fp && fp->fun && (uintN)slot < fp->fun->nargs)
1029             *vp = fp->argv[slot];
1030         break;
1031     }
1033     return JS_TRUE;
1036 static JSBool
1037 fun_enumerate(JSContext *cx, JSObject *obj)
1039     jsid prototypeId;
1040     JSObject *pobj;
1041     JSProperty *prop;
1043     prototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
1044     if (!OBJ_LOOKUP_PROPERTY(cx, obj, prototypeId, &pobj, &prop))
1045         return JS_FALSE;
1046     if (prop)
1047         OBJ_DROP_PROPERTY(cx, pobj, prop);
1048     return JS_TRUE;
1051 static JSBool
1052 fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
1053             JSObject **objp)
1055     JSFunction *fun;
1056     JSString *str;
1057     JSAtom *prototypeAtom;
1059     if (!JSVAL_IS_STRING(id))
1060         return JS_TRUE;
1062     /* No valid function object should lack private data, but check anyway. */
1063     fun = (JSFunction *)JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL);
1064     if (!fun || !fun->object)
1065         return JS_TRUE;
1067     /* No need to reflect fun.prototype in 'fun.prototype = ...'. */
1068     if (flags & JSRESOLVE_ASSIGNING)
1069         return JS_TRUE;
1071     /*
1072      * Ok, check whether id is 'prototype' and bootstrap the function object's
1073      * prototype property.
1074      */
1075     str = JSVAL_TO_STRING(id);
1076     prototypeAtom = cx->runtime->atomState.classPrototypeAtom;
1077     if (str == ATOM_TO_STRING(prototypeAtom)) {
1078         JSObject *proto, *parentProto;
1079         jsval pval;
1081         proto = parentProto = NULL;
1082         if (fun->object != obj && fun->object) {
1083             /*
1084              * Clone of a function: make its prototype property value have the
1085              * same class as the clone-parent's prototype.
1086              */
1087             if (!OBJ_GET_PROPERTY(cx, fun->object, ATOM_TO_JSID(prototypeAtom),
1088                                   &pval)) {
1089                 return JS_FALSE;
1090             }
1091             if (!JSVAL_IS_PRIMITIVE(pval)) {
1092                 /*
1093                  * We are about to allocate a new object, so hack the newborn
1094                  * root until then to protect pval in case it is figuratively
1095                  * up in the air, with no strong refs protecting it.
1096                  */
1097                 cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(pval);
1098                 parentProto = JSVAL_TO_OBJECT(pval);
1099             }
1100         }
1102         /*
1103          * Beware of the wacky case of a user function named Object -- trying
1104          * to find a prototype for that will recur back here _ad perniciem_.
1105          */
1106         if (!parentProto && fun->atom == cx->runtime->atomState.ObjectAtom)
1107             return JS_TRUE;
1109         /*
1110          * If resolving "prototype" in a clone, clone the parent's prototype.
1111          * Pass the constructor's (obj's) parent as the prototype parent, to
1112          * avoid defaulting to parentProto.constructor.__parent__.
1113          */
1114         proto = js_NewObject(cx, &js_ObjectClass, parentProto,
1115                              OBJ_GET_PARENT(cx, obj));
1116         if (!proto)
1117             return JS_FALSE;
1119         /*
1120          * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for
1121          * user-defined functions, but DontEnum | ReadOnly | DontDelete for
1122          * native "system" constructors such as Object or Function.  So lazily
1123          * set the former here in fun_resolve, but eagerly define the latter
1124          * in JS_InitClass, with the right attributes.
1125          */
1126         if (!js_SetClassPrototype(cx, obj, proto,
1127                                   JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
1128             cx->newborn[GCX_OBJECT] = NULL;
1129             return JS_FALSE;
1130         }
1131         *objp = obj;
1132     }
1134     return JS_TRUE;
1137 static JSBool
1138 fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
1140     switch (type) {
1141       case JSTYPE_FUNCTION:
1142         *vp = OBJECT_TO_JSVAL(obj);
1143         return JS_TRUE;
1144       default:
1145         return js_TryValueOf(cx, obj, type, vp);
1146     }
1149 static void
1150 fun_finalize(JSContext *cx, JSObject *obj)
1152     JSFunction *fun;
1154     /* No valid function object should lack private data, but check anyway. */
1155     fun = (JSFunction *) JS_GetPrivate(cx, obj);
1156     if (!fun)
1157         return;
1158     if (fun->object == obj)
1159         fun->object = NULL;
1160     JS_ATOMIC_DECREMENT(&fun->nrefs);
1161     if (fun->nrefs)
1162         return;
1164     /* Null-check required since the parser sets interpreted very early. */
1165     if (fun->interpreted && fun->u.script)
1166         js_DestroyScript(cx, fun->u.script);
1169 #if JS_HAS_XDR
1171 #include "jsxdrapi.h"
1173 enum {
1174     JSXDR_FUNARG = 1,
1175     JSXDR_FUNVAR = 2,
1176     JSXDR_FUNCONST = 3
1177 };
1179 /* XXX store parent and proto, if defined */
1180 static JSBool
1181 fun_xdrObject(JSXDRState *xdr, JSObject **objp)
1183     JSContext *cx;
1184     JSFunction *fun;
1185     JSString *atomstr;
1186     JSTempValueRooter tvr;
1187     uint32 flagsword;           /* originally only flags was JS_XDRUint8'd */
1188     char *propname;
1189     JSScopeProperty *sprop;
1190     uint32 userid;              /* NB: holds a signed int-tagged jsval */
1191     JSAtom *atom;
1192     uintN i, n, dupflag;
1193     uint32 type;
1194     JSBool ok;
1195 #ifdef DEBUG
1196     uintN nvars = 0, nargs = 0;
1197 #endif
1199     cx = xdr->cx;
1200     if (xdr->mode == JSXDR_ENCODE) {
1201         /*
1202          * No valid function object should lack private data, but fail soft
1203          * (return true, no error report) in case one does due to API pilot
1204          * or internal error.
1205          */
1206         fun = (JSFunction *) JS_GetPrivate(cx, *objp);
1207         if (!fun)
1208             return JS_TRUE;
1209         if (!fun->interpreted) {
1210             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1211                                  JSMSG_NOT_SCRIPTED_FUNCTION,
1212                                  JS_GetFunctionName(fun));
1213             return JS_FALSE;
1214         }
1215         atomstr = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;
1216         flagsword = ((uint32)fun->nregexps << 16) | fun->flags;
1217     } else {
1218         fun = js_NewFunction(cx, NULL, NULL, 0, 0, NULL, NULL);
1219         if (!fun)
1220             return JS_FALSE;
1221         atomstr = NULL;
1222     }
1224     /* From here on, control flow must flow through label out. */
1225     JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(fun->object), &tvr);
1226     ok = JS_TRUE;
1228     if (!JS_XDRStringOrNull(xdr, &atomstr) ||
1229         !JS_XDRUint16(xdr, &fun->nargs) ||
1230         !JS_XDRUint16(xdr, &fun->extra) ||
1231         !JS_XDRUint16(xdr, &fun->nvars) ||
1232         !JS_XDRUint32(xdr, &flagsword)) {
1233         goto bad;
1234     }
1236     /* do arguments and local vars */
1237     if (fun->object) {
1238         n = fun->nargs + fun->nvars;
1239         if (xdr->mode == JSXDR_ENCODE) {
1240             JSScope *scope;
1241             JSScopeProperty **spvec, *auto_spvec[8];
1242             void *mark;
1244             if (n <= sizeof auto_spvec / sizeof auto_spvec[0]) {
1245                 spvec = auto_spvec;
1246                 mark = NULL;
1247             } else {
1248                 mark = JS_ARENA_MARK(&cx->tempPool);
1249                 JS_ARENA_ALLOCATE_CAST(spvec, JSScopeProperty **, &cx->tempPool,
1250                                        n * sizeof(JSScopeProperty *));
1251                 if (!spvec) {
1252                     JS_ReportOutOfMemory(cx);
1253                     goto bad;
1254                 }
1255             }
1256             scope = OBJ_SCOPE(fun->object);
1257             for (sprop = SCOPE_LAST_PROP(scope); sprop;
1258                  sprop = sprop->parent) {
1259                 if (sprop->getter == js_GetArgument) {
1260                     JS_ASSERT(nargs++ <= fun->nargs);
1261                     spvec[sprop->shortid] = sprop;
1262                 } else if (sprop->getter == js_GetLocalVariable) {
1263                     JS_ASSERT(nvars++ <= fun->nvars);
1264                     spvec[fun->nargs + sprop->shortid] = sprop;
1265                 }
1266             }
1267             for (i = 0; i < n; i++) {
1268                 sprop = spvec[i];
1269                 JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
1270                 type = (i < fun->nargs)
1271                        ? JSXDR_FUNARG
1272                        : (sprop->attrs & JSPROP_READONLY)
1273                        ? JSXDR_FUNCONST
1274                        : JSXDR_FUNVAR;
1275                 userid = INT_TO_JSVAL(sprop->shortid);
1276                 /* XXX lossy conversion, need new XDR version for ECMAv3 */
1277                 propname = JS_GetStringBytes(ATOM_TO_STRING(JSID_TO_ATOM(sprop->id)));
1278                 if (!propname ||
1279                     !JS_XDRUint32(xdr, &type) ||
1280                     !JS_XDRUint32(xdr, &userid) ||
1281                     !JS_XDRCString(xdr, &propname)) {
1282                     if (mark)
1283                         JS_ARENA_RELEASE(&cx->tempPool, mark);
1284                     goto bad;
1285                 }
1286             }
1287             if (mark)
1288                 JS_ARENA_RELEASE(&cx->tempPool, mark);
1289         } else {
1290             JSPropertyOp getter, setter;
1292             for (i = n; i != 0; i--) {
1293                 uintN attrs = JSPROP_PERMANENT;
1295                 if (!JS_XDRUint32(xdr, &type) ||
1296                     !JS_XDRUint32(xdr, &userid) ||
1297                     !JS_XDRCString(xdr, &propname)) {
1298                     goto bad;
1299                 }
1300                 JS_ASSERT(type == JSXDR_FUNARG || type == JSXDR_FUNVAR ||
1301                           type == JSXDR_FUNCONST);
1302                 if (type == JSXDR_FUNARG) {
1303                     getter = js_GetArgument;
1304                     setter = js_SetArgument;
1305                     JS_ASSERT(nargs++ <= fun->nargs);
1306                 } else if (type == JSXDR_FUNVAR || type == JSXDR_FUNCONST) {
1307                     getter = js_GetLocalVariable;
1308                     setter = js_SetLocalVariable;
1309                     if (type == JSXDR_FUNCONST)
1310                         attrs |= JSPROP_READONLY;
1311                     JS_ASSERT(nvars++ <= fun->nvars);
1312                 } else {
1313                     getter = NULL;
1314                     setter = NULL;
1315                 }
1316                 atom = js_Atomize(cx, propname, strlen(propname), 0);
1317                 JS_free(cx, propname);
1318                 if (!atom)
1319                     goto bad;
1321                 /* Flag duplicate argument if atom is bound in fun->object. */
1322                 dupflag = SCOPE_GET_PROPERTY(OBJ_SCOPE(fun->object),
1323                                              ATOM_TO_JSID(atom))
1324                           ? SPROP_IS_DUPLICATE
1325                           : 0;
1327                 if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(atom),
1328                                           getter, setter, SPROP_INVALID_SLOT,
1329                                           attrs | JSPROP_SHARED,
1330                                           dupflag | SPROP_HAS_SHORTID,
1331                                           JSVAL_TO_INT(userid))) {
1332                     goto bad;
1333                 }
1334             }
1335         }
1336     }
1338     if (!js_XDRScript(xdr, &fun->u.script, NULL))
1339         goto bad;
1341     if (xdr->mode == JSXDR_DECODE) {
1342         fun->interpreted = JS_TRUE;
1343         fun->flags = (uint8) flagsword;
1344         fun->nregexps = (uint16) (flagsword >> 16);
1346         *objp = fun->object;
1347         if (atomstr) {
1348             /* XXX only if this was a top-level function! */
1349             fun->atom = js_AtomizeString(cx, atomstr, 0);
1350             if (!fun->atom)
1351                 goto bad;
1352         }
1354         js_CallNewScriptHook(cx, fun->u.script, fun);
1355     }
1357 out:
1358     JS_POP_TEMP_ROOT(cx, &tvr);
1359     return ok;
1361 bad:
1362     ok = JS_FALSE;
1363     goto out;
1366 #else  /* !JS_HAS_XDR */
1368 #define fun_xdrObject NULL
1370 #endif /* !JS_HAS_XDR */
1372 #if JS_HAS_INSTANCEOF
1374 /*
1375  * [[HasInstance]] internal method for Function objects: fetch the .prototype
1376  * property of its 'this' parameter, and walks the prototype chain of v (only
1377  * if v is an object) returning true if .prototype is found.
1378  */
1379 static JSBool
1380 fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
1382     jsval pval;
1383     JSString *str;
1385     if (!OBJ_GET_PROPERTY(cx, obj,
1386                           ATOM_TO_JSID(cx->runtime->atomState
1387                                        .classPrototypeAtom),
1388                           &pval)) {
1389         return JS_FALSE;
1390     }
1392     if (JSVAL_IS_PRIMITIVE(pval)) {
1393         /*
1394          * Throw a runtime error if instanceof is called on a function that
1395          * has a non-object as its .prototype value.
1396          */
1397         str = js_DecompileValueGenerator(cx, -1, OBJECT_TO_JSVAL(obj), NULL);
1398         if (str) {
1399             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1400                                  JSMSG_BAD_PROTOTYPE, JS_GetStringBytes(str));
1401         }
1402         return JS_FALSE;
1403     }
1405     return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp);
1408 #else  /* !JS_HAS_INSTANCEOF */
1410 #define fun_hasInstance NULL
1412 #endif /* !JS_HAS_INSTANCEOF */
1414 static uint32
1415 fun_mark(JSContext *cx, JSObject *obj, void *arg)
1417     JSFunction *fun;
1419     fun = (JSFunction *) JS_GetPrivate(cx, obj);
1420     if (fun) {
1421         JS_MarkGCThing(cx, fun, js_private_str, arg);
1422         if (fun->atom)
1423             GC_MARK_ATOM(cx, fun->atom, arg);
1424         if (fun->interpreted && fun->u.script)
1425             js_MarkScript(cx, fun->u.script, arg);
1426     }
1427     return 0;
1430 static uint32
1431 fun_reserveSlots(JSContext *cx, JSObject *obj)
1433     JSFunction *fun;
1435     fun = (JSFunction *) JS_GetPrivate(cx, obj);
1436     return fun ? fun->nregexps : 0;
1439 /*
1440  * Reserve two slots in all function objects for XPConnect.  Note that this
1441  * does not bloat every instance, only those on which reserved slots are set,
1442  * and those on which ad-hoc properties are defined.
1443  */
1444 JS_FRIEND_DATA(JSClass) js_FunctionClass = {
1445     js_Function_str,
1446     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2),
1447     JS_PropertyStub,  JS_PropertyStub,
1448     fun_getProperty,  JS_PropertyStub,
1449     fun_enumerate,    (JSResolveOp)fun_resolve,
1450     fun_convert,      fun_finalize,
1451     NULL,             NULL,
1452     NULL,             NULL,
1453     fun_xdrObject,    fun_hasInstance,
1454     fun_mark,         fun_reserveSlots
1455 };
1457 JSBool
1458 js_fun_toString(JSContext *cx, JSObject *obj, uint32 indent,
1459                 uintN argc, jsval *argv, jsval *rval)
1461     jsval fval;
1462     JSFunction *fun;
1463     JSString *str;
1465     if (!argv) {
1466         JS_ASSERT(JS_ObjectIsFunction(cx, obj));
1467     } else {
1468         fval = argv[-1];
1469         if (!JSVAL_IS_FUNCTION(cx, fval)) {
1470             /*
1471              * If we don't have a function to start off with, try converting
1472              * the object to a function.  If that doesn't work, complain.
1473              */
1474             if (JSVAL_IS_OBJECT(fval)) {
1475                 obj = JSVAL_TO_OBJECT(fval);
1476                 if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, JSTYPE_FUNCTION,
1477                                                      &fval)) {
1478                     return JS_FALSE;
1479                 }
1480                 argv[-1] = fval;
1481             }
1482             if (!JSVAL_IS_FUNCTION(cx, fval)) {
1483                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1484                                      JSMSG_INCOMPATIBLE_PROTO,
1485                                      js_Function_str, js_toString_str,
1486                                      JS_GetTypeName(cx,
1487                                                     JS_TypeOfValue(cx, fval)));
1488                 return JS_FALSE;
1489             }
1490         }
1492         obj = JSVAL_TO_OBJECT(fval);
1493     }
1495     fun = (JSFunction *) JS_GetPrivate(cx, obj);
1496     if (!fun)
1497         return JS_TRUE;
1498     if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent))
1499         return JS_FALSE;
1500     str = JS_DecompileFunction(cx, fun, (uintN)indent);
1501     if (!str)
1502         return JS_FALSE;
1503     *rval = STRING_TO_JSVAL(str);
1504     return JS_TRUE;
1507 static JSBool
1508 fun_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1510     return js_fun_toString(cx, obj, 0, argc, argv, rval);
1513 #if JS_HAS_TOSOURCE
1514 static JSBool
1515 fun_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1517     return js_fun_toString(cx, obj, JS_DONT_PRETTY_PRINT, argc, argv, rval);
1519 #endif
1521 static const char call_str[] = "call";
1523 #if JS_HAS_CALL_FUNCTION
1524 static JSBool
1525 fun_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1527     jsval fval, *sp, *oldsp;
1528     JSString *str;
1529     void *mark;
1530     uintN i;
1531     JSStackFrame *fp;
1532     JSBool ok;
1534     if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1]))
1535         return JS_FALSE;
1536     fval = argv[-1];
1538     if (!JSVAL_IS_FUNCTION(cx, fval)) {
1539         str = JS_ValueToString(cx, fval);
1540         if (str) {
1541             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1542                                  JSMSG_INCOMPATIBLE_PROTO,
1543                                  js_Function_str, call_str,
1544                                  JS_GetStringBytes(str));
1545         }
1546         return JS_FALSE;
1547     }
1549     if (argc == 0) {
1550         /* Call fun with its global object as the 'this' param if no args. */
1551         obj = NULL;
1552     } else {
1553         /* Otherwise convert the first arg to 'this' and skip over it. */
1554         if (!js_ValueToObject(cx, argv[0], &obj))
1555             return JS_FALSE;
1556         argc--;
1557         argv++;
1558     }
1560     /* Allocate stack space for fval, obj, and the args. */
1561     sp = js_AllocStack(cx, 2 + argc, &mark);
1562     if (!sp)
1563         return JS_FALSE;
1565     /* Push fval, obj, and the args. */
1566     *sp++ = fval;
1567     *sp++ = OBJECT_TO_JSVAL(obj);
1568     for (i = 0; i < argc; i++)
1569         *sp++ = argv[i];
1571     /* Lift current frame to include the args and do the call. */
1572     fp = cx->fp;
1573     oldsp = fp->sp;
1574     fp->sp = sp;
1575     ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL | JSINVOKE_SKIP_CALLER);
1577     /* Store rval and pop stack back to our frame's sp. */
1578     *rval = fp->sp[-1];
1579     fp->sp = oldsp;
1580     js_FreeStack(cx, mark);
1581     return ok;
1583 #endif /* JS_HAS_CALL_FUNCTION */
1585 #if JS_HAS_APPLY_FUNCTION
1586 static JSBool
1587 fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1589     jsval fval, *sp, *oldsp;
1590     JSString *str;
1591     JSObject *aobj;
1592     jsuint length;
1593     void *mark;
1594     uintN i;
1595     JSBool ok;
1596     JSStackFrame *fp;
1598     if (argc == 0) {
1599         /* Will get globalObject as 'this' and no other arguments. */
1600         return fun_call(cx, obj, argc, argv, rval);
1601     }
1603     if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1]))
1604         return JS_FALSE;
1605     fval = argv[-1];
1607     if (!JSVAL_IS_FUNCTION(cx, fval)) {
1608         str = JS_ValueToString(cx, fval);
1609         if (str) {
1610             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1611                                  JSMSG_INCOMPATIBLE_PROTO,
1612                                  js_Function_str, "apply",
1613                                  JS_GetStringBytes(str));
1614         }
1615         return JS_FALSE;
1616     }
1618     /* Quell GCC overwarnings. */
1619     aobj = NULL;
1620     length = 0;
1622     if (argc >= 2) {
1623         /* If the 2nd arg is null or void, call the function with 0 args. */
1624         if (JSVAL_IS_NULL(argv[1]) || JSVAL_IS_VOID(argv[1])) {
1625             argc = 0;
1626         } else {
1627             /* The second arg must be an array (or arguments object). */
1628             if (JSVAL_IS_PRIMITIVE(argv[1]) ||
1629                 (aobj = JSVAL_TO_OBJECT(argv[1]),
1630                 OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass &&
1631                 OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass))
1632             {
1633                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1634                                      JSMSG_BAD_APPLY_ARGS);
1635                 return JS_FALSE;
1636             }
1637             if (!js_GetLengthProperty(cx, aobj, &length))
1638                 return JS_FALSE;
1639         }
1640     }
1642     /* Convert the first arg to 'this' and skip over it. */
1643     if (!js_ValueToObject(cx, argv[0], &obj))
1644         return JS_FALSE;
1646     /* Allocate stack space for fval, obj, and the args. */
1647     argc = (uintN)JS_MIN(length, ARGC_LIMIT - 1);
1648     sp = js_AllocStack(cx, 2 + argc, &mark);
1649     if (!sp)
1650         return JS_FALSE;
1652     /* Push fval, obj, and aobj's elements as args. */
1653     *sp++ = fval;
1654     *sp++ = OBJECT_TO_JSVAL(obj);
1655     for (i = 0; i < argc; i++) {
1656         ok = JS_GetElement(cx, aobj, (jsint)i, sp);
1657         if (!ok)
1658             goto out;
1659         sp++;
1660     }
1662     /* Lift current frame to include the args and do the call. */
1663     fp = cx->fp;
1664     oldsp = fp->sp;
1665     fp->sp = sp;
1666     ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL | JSINVOKE_SKIP_CALLER);
1668     /* Store rval and pop stack back to our frame's sp. */
1669     *rval = fp->sp[-1];
1670     fp->sp = oldsp;
1671 out:
1672     js_FreeStack(cx, mark);
1673     return ok;
1675 #endif /* JS_HAS_APPLY_FUNCTION */
1677 static JSFunctionSpec function_methods[] = {
1678 #if JS_HAS_TOSOURCE
1679     {js_toSource_str,   fun_toSource,   0,0,0},
1680 #endif
1681     {js_toString_str,   fun_toString,   1,0,0},
1682 #if JS_HAS_APPLY_FUNCTION
1683     {"apply",           fun_apply,      2,0,0},
1684 #endif
1685 #if JS_HAS_CALL_FUNCTION
1686     {call_str,          fun_call,       1,0,0},
1687 #endif
1688     {0,0,0,0,0}
1689 };
1691 JSBool
1692 js_IsIdentifier(JSString *str)
1694     size_t n;
1695     jschar *s, c;
1697     n = JSSTRING_LENGTH(str);
1698     if (n == 0)
1699         return JS_FALSE;
1700     s = JSSTRING_CHARS(str);
1701     c = *s;
1702     if (!JS_ISIDSTART(c))
1703         return JS_FALSE;
1704     for (n--; n != 0; n--) {
1705         c = *++s;
1706         if (!JS_ISIDENT(c))
1707             return JS_FALSE;
1708     }
1709     return JS_TRUE;
1712 static JSBool
1713 Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1715     JSStackFrame *fp, *caller;
1716     JSFunction *fun;
1717     JSObject *parent;
1718     uintN i, n, lineno, dupflag;
1719     JSAtom *atom;
1720     const char *filename;
1721     JSObject *obj2;
1722     JSProperty *prop;
1723     JSScopeProperty *sprop;
1724     JSString *str, *arg;
1725     void *mark;
1726     JSTokenStream *ts;
1727     JSPrincipals *principals;
1728     jschar *collected_args, *cp;
1729     size_t arg_length, args_length, old_args_length;
1730     JSTokenType tt;
1731     JSBool ok;
1733     fp = cx->fp;
1734     if (fp && !(fp->flags & JSFRAME_CONSTRUCTING)) {
1735         obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL);
1736         if (!obj)
1737             return JS_FALSE;
1738         *rval = OBJECT_TO_JSVAL(obj);
1739     }
1740     fun = (JSFunction *) JS_GetPrivate(cx, obj);
1741     if (fun)
1742         return JS_TRUE;
1744 #if JS_HAS_CALL_OBJECT
1745     /*
1746      * NB: (new Function) is not lexically closed by its caller, it's just an
1747      * anonymous function in the top-level scope that its constructor inhabits.
1748      * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
1749      * and so would a call to f from another top-level's script or function.
1750      *
1751      * In older versions, before call objects, a new Function was adopted by
1752      * its running context's globalObject, which might be different from the
1753      * top-level reachable from scopeChain (in HTML frames, e.g.).
1754      */
1755     parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]));
1756 #else
1757     /* Set up for dynamic parenting (see js_Invoke in jsinterp.c). */
1758     parent = NULL;
1759 #endif
1761     fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA, parent,
1762                          JS_VERSION_IS_ECMA(cx)
1763                          ? cx->runtime->atomState.anonymousAtom
1764                          : NULL);
1766     if (!fun)
1767         return JS_FALSE;
1769     /*
1770      * Function is static and not called directly by other functions in this
1771      * file, therefore it is callable only as a native function by js_Invoke.
1772      * Find the scripted caller, possibly skipping other native frames such as
1773      * are built for Function.prototype.call or .apply activations that invoke
1774      * Function indirectly from a script.
1775      */
1776     JS_ASSERT(!fp->script && fp->fun && fp->fun->u.native == Function);
1777     caller = JS_GetScriptedCaller(cx, fp);
1778     if (caller) {
1779         filename = caller->script->filename;
1780         lineno = js_PCToLineNumber(cx, caller->script, caller->pc);
1781         principals = JS_EvalFramePrincipals(cx, fp, caller);
1782     } else {
1783         filename = NULL;
1784         lineno = 0;
1785         principals = NULL;
1786     }
1788     /* Belt-and-braces: check that the caller has access to parent. */
1789     if (!js_CheckPrincipalsAccess(cx, parent, principals, js_Function_str))
1790         return JS_FALSE;
1792     n = argc ? argc - 1 : 0;
1793     if (n > 0) {
1794         /*
1795          * Collect the function-argument arguments into one string, separated
1796          * by commas, then make a tokenstream from that string, and scan it to
1797          * get the arguments.  We need to throw the full scanner at the
1798          * problem, because the argument string can legitimately contain
1799          * comments and linefeeds.  XXX It might be better to concatenate
1800          * everything up into a function definition and pass it to the
1801          * compiler, but doing it this way is less of a delta from the old
1802          * code.  See ECMA 15.3.2.1.
1803          */
1804         args_length = 0;
1805         for (i = 0; i < n; i++) {
1806             /* Collect the lengths for all the function-argument arguments. */
1807             arg = js_ValueToString(cx, argv[i]);
1808             if (!arg)
1809                 return JS_FALSE;
1810             argv[i] = STRING_TO_JSVAL(arg);
1812             /*
1813              * Check for overflow.  The < test works because the maximum
1814              * JSString length fits in 2 fewer bits than size_t has.
1815              */
1816             old_args_length = args_length;
1817             args_length = old_args_length + JSSTRING_LENGTH(arg);
1818             if (args_length < old_args_length) {
1819                 JS_ReportOutOfMemory(cx);
1820                 return JS_FALSE;
1821             }
1822         }
1824         /* Add 1 for each joining comma and check for overflow (two ways). */
1825         old_args_length = args_length;
1826         args_length = old_args_length + n - 1;
1827         if (args_length < old_args_length ||
1828             args_length >= ~(size_t)0 / sizeof(jschar)) {
1829             JS_ReportOutOfMemory(cx);
1830             return JS_FALSE;
1831         }
1833         /*
1834          * Allocate a string to hold the concatenated arguments, including room
1835          * for a terminating 0.  Mark cx->tempPool for later release, to free
1836          * collected_args and its tokenstream in one swoop.
1837          */
1838         mark = JS_ARENA_MARK(&cx->tempPool);
1839         JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool,
1840                                (args_length+1) * sizeof(jschar));
1841         if (!cp) {
1842             JS_ReportOutOfMemory(cx);
1843             return JS_FALSE;
1844         }
1845         collected_args = cp;
1847         /*
1848          * Concatenate the arguments into the new string, separated by commas.
1849          */
1850         for (i = 0; i < n; i++) {
1851             arg = JSVAL_TO_STRING(argv[i]);
1852             arg_length = JSSTRING_LENGTH(arg);
1853             (void) js_strncpy(cp, JSSTRING_CHARS(arg), arg_length);
1854             cp += arg_length;
1856             /* Add separating comma or terminating 0. */
1857             *cp++ = (i + 1 < n) ? ',' : 0;
1858         }
1860         /*
1861          * Make a tokenstream (allocated from cx->tempPool) that reads from
1862          * the given string.
1863          */
1864         ts = js_NewTokenStream(cx, collected_args, args_length, filename,
1865                                lineno, principals);
1866         if (!ts) {
1867             JS_ARENA_RELEASE(&cx->tempPool, mark);
1868             return JS_FALSE;
1869         }
1871         /* The argument string may be empty or contain no tokens. */
1872         tt = js_GetToken(cx, ts);
1873         if (tt != TOK_EOF) {
1874             for (;;) {
1875                 /*
1876                  * Check that it's a name.  This also implicitly guards against
1877                  * TOK_ERROR, which was already reported.
1878                  */
1879                 if (tt != TOK_NAME)
1880                     goto bad_formal;
1882                 /*
1883                  * Get the atom corresponding to the name from the tokenstream;
1884                  * we're assured at this point that it's a valid identifier.
1885                  */
1886                 atom = CURRENT_TOKEN(ts).t_atom;
1887                 if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom),
1888                                              &obj2, &prop)) {
1889                     goto bad_formal;
1890                 }
1891                 sprop = (JSScopeProperty *) prop;
1892                 dupflag = 0;
1893                 if (sprop) {
1894                     ok = JS_TRUE;
1895                     if (obj2 == obj) {
1896                         const char *name = js_AtomToPrintableString(cx, atom);
1898                         /*
1899                          * A duplicate parameter name. We force a duplicate
1900                          * node on the SCOPE_LAST_PROP(scope) list with the
1901                          * same id, distinguished by the SPROP_IS_DUPLICATE
1902                          * flag, and not mapped by an entry in scope.
1903                          */
1904                         JS_ASSERT(sprop->getter == js_GetArgument);
1905                         ok = name &&
1906                              js_ReportCompileErrorNumber(cx, ts,
1907                                                          JSREPORT_TS |
1908                                                          JSREPORT_WARNING |
1909                                                          JSREPORT_STRICT,
1910                                                          JSMSG_DUPLICATE_FORMAL,
1911                                                          name);
1913                         dupflag = SPROP_IS_DUPLICATE;
1914                     }
1915                     OBJ_DROP_PROPERTY(cx, obj2, prop);
1916                     if (!ok)
1917                         goto bad_formal;
1918                     sprop = NULL;
1919                 }
1920                 if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(atom),
1921                                           js_GetArgument, js_SetArgument,
1922                                           SPROP_INVALID_SLOT,
1923                                           JSPROP_PERMANENT | JSPROP_SHARED,
1924                                           dupflag | SPROP_HAS_SHORTID,
1925                                           fun->nargs)) {
1926                     goto bad_formal;
1927                 }
1928                 if (fun->nargs == JS_BITMASK(16)) {
1929                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1930                                          JSMSG_TOO_MANY_FUN_ARGS);
1931                     goto bad;
1932                 }
1933                 fun->nargs++;
1935                 /*
1936                  * Get the next token.  Stop on end of stream.  Otherwise
1937                  * insist on a comma, get another name, and iterate.
1938                  */
1939                 tt = js_GetToken(cx, ts);
1940                 if (tt == TOK_EOF)
1941                     break;
1942                 if (tt != TOK_COMMA)
1943                     goto bad_formal;
1944                 tt = js_GetToken(cx, ts);
1945             }
1946         }
1948         /* Clean up. */
1949         ok = js_CloseTokenStream(cx, ts);
1950         JS_ARENA_RELEASE(&cx->tempPool, mark);
1951         if (!ok)
1952             return JS_FALSE;
1953     }
1955     if (argc) {
1956         str = js_ValueToString(cx, argv[argc-1]);
1957     } else {
1958         /* Can't use cx->runtime->emptyString because we're called too early. */
1959         str = js_NewStringCopyZ(cx, js_empty_ucstr, 0);
1960     }
1961     if (!str)
1962         return JS_FALSE;
1963     if (argv) {
1964         /* Use the last arg (or this if argc == 0) as a local GC root. */
1965         argv[(intN)(argc-1)] = STRING_TO_JSVAL(str);
1966     }
1968     mark = JS_ARENA_MARK(&cx->tempPool);
1969     ts = js_NewTokenStream(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
1970                            filename, lineno, principals);
1971     if (!ts) {
1972         ok = JS_FALSE;
1973     } else {
1974         ok = js_CompileFunctionBody(cx, ts, fun) &&
1975              js_CloseTokenStream(cx, ts);
1976     }
1977     JS_ARENA_RELEASE(&cx->tempPool, mark);
1978     return ok;
1980 bad_formal:
1981     /*
1982      * Report "malformed formal parameter" iff no illegal char or similar
1983      * scanner error was already reported.
1984      */
1985     if (!(ts->flags & TSF_ERROR))
1986         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_FORMAL);
1988 bad:
1989     /*
1990      * Clean up the arguments string and tokenstream if we failed to parse
1991      * the arguments.
1992      */
1993     (void)js_CloseTokenStream(cx, ts);
1994     JS_ARENA_RELEASE(&cx->tempPool, mark);
1995     return JS_FALSE;
1998 JSObject *
1999 js_InitFunctionClass(JSContext *cx, JSObject *obj)
2001     JSObject *proto;
2002     JSAtom *atom;
2003     JSFunction *fun;
2005     proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
2006                          function_props, function_methods, NULL, NULL);
2007     if (!proto)
2008         return NULL;
2009     atom = js_Atomize(cx, js_FunctionClass.name, strlen(js_FunctionClass.name),
2010                       0);
2011     if (!atom)
2012         goto bad;
2013     fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, NULL);
2014     if (!fun)
2015         goto bad;
2016     fun->u.script = js_NewScript(cx, 0, 0, 0);
2017     if (!fun->u.script)
2018         goto bad;
2019     fun->interpreted = JS_TRUE;
2020     return proto;
2022 bad:
2023     cx->newborn[GCX_OBJECT] = NULL;
2024     return NULL;
2027 #if JS_HAS_CALL_OBJECT
2028 JSObject *
2029 js_InitCallClass(JSContext *cx, JSObject *obj)
2031     JSObject *proto;
2033     proto = JS_InitClass(cx, obj, NULL, &js_CallClass, NULL, 0,
2034                          call_props, NULL, NULL, NULL);
2035     if (!proto)
2036         return NULL;
2038     /*
2039      * Null Call.prototype's proto slot so that Object.prototype.* does not
2040      * pollute the scope of heavyweight functions.
2041      */
2042     OBJ_SET_PROTO(cx, proto, NULL);
2043     return proto;
2045 #endif
2047 JSFunction *
2048 js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
2049                uintN flags, JSObject *parent, JSAtom *atom)
2051     JSFunction *fun;
2052     JSTempValueRooter tvr;
2054     /* If funobj is null, allocate an object for it. */
2055     if (funobj) {
2056         OBJ_SET_PARENT(cx, funobj, parent);
2057     } else {
2058         funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent);
2059         if (!funobj)
2060             return NULL;
2061     }
2063     /* Protect fun from any potential last-ditch GCs. */
2064     JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(funobj), &tvr);
2066     /*
2067      * Allocate fun after allocating funobj so slot allocation in js_NewObject
2068      * does not wipe out fun from cx->newborn[GCX_PRIVATE].
2069      */
2070     fun = (JSFunction *) js_NewGCThing(cx, GCX_PRIVATE, sizeof(JSFunction));
2071     if (!fun)
2072         goto out;
2074     /* Initialize all function members. */
2075     fun->nrefs = 0;
2076     fun->object = NULL;
2077     fun->u.native = native;
2078     fun->nargs = nargs;
2079     fun->extra = 0;
2080     fun->nvars = 0;
2081     fun->flags = flags & JSFUN_FLAGS_MASK;
2082     fun->interpreted = JS_FALSE;
2083     fun->nregexps = 0;
2084     fun->spare = 0;
2085     fun->atom = atom;
2086     fun->clasp = NULL;
2088     /* Link fun to funobj and vice versa. */
2089     if (!js_LinkFunctionObject(cx, fun, funobj)) {
2090         cx->newborn[GCX_OBJECT] = NULL;
2091         fun = NULL;
2092     }
2094 out:
2095     JS_POP_TEMP_ROOT(cx, &tvr);
2096     return fun;
2099 JSObject *
2100 js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
2102     JSObject *newfunobj;
2103     JSFunction *fun;
2105     JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass);
2106     newfunobj = js_NewObject(cx, &js_FunctionClass, funobj, parent);
2107     if (!newfunobj)
2108         return NULL;
2109     fun = (JSFunction *) JS_GetPrivate(cx, funobj);
2110     if (!js_LinkFunctionObject(cx, fun, newfunobj)) {
2111         cx->newborn[GCX_OBJECT] = NULL;
2112         return NULL;
2113     }
2114     return newfunobj;
2117 JSBool
2118 js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *funobj)
2120     if (!fun->object)
2121         fun->object = funobj;
2122     if (!JS_SetPrivate(cx, funobj, fun))
2123         return JS_FALSE;
2124     JS_ATOMIC_INCREMENT(&fun->nrefs);
2125     return JS_TRUE;
2128 JSFunction *
2129 js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
2130                   uintN nargs, uintN attrs)
2132     JSFunction *fun;
2134     fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom);
2135     if (!fun)
2136         return NULL;
2137     if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom),
2138                              OBJECT_TO_JSVAL(fun->object),
2139                              NULL, NULL,
2140                              attrs & ~JSFUN_FLAGS_MASK, NULL)) {
2141         return NULL;
2142     }
2143     return fun;
2146 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
2147 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
2148 #endif
2150 JSFunction *
2151 js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags)
2153     jsval v;
2154     JSObject *obj;
2156     v = *vp;
2157     obj = NULL;
2158     if (JSVAL_IS_OBJECT(v)) {
2159         obj = JSVAL_TO_OBJECT(v);
2160         if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) {
2161             if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v))
2162                 return NULL;
2163             obj = JSVAL_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL;
2164         }
2165     }
2166     if (!obj) {
2167         js_ReportIsNotFunction(cx, vp, flags);
2168         return NULL;
2169     }
2170     return (JSFunction *) JS_GetPrivate(cx, obj);
2173 JSObject *
2174 js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags)
2176     JSFunction *fun;
2177     JSObject *funobj;
2178     JSStackFrame *caller;
2179     JSPrincipals *principals;
2181     if (JSVAL_IS_FUNCTION(cx, *vp))
2182         return JSVAL_TO_OBJECT(*vp);
2184     fun = js_ValueToFunction(cx, vp, flags);
2185     if (!fun)
2186         return NULL;
2187     funobj = fun->object;
2188     *vp = OBJECT_TO_JSVAL(funobj);
2190     caller = JS_GetScriptedCaller(cx, cx->fp);
2191     if (caller) {
2192         principals = caller->script->principals;
2193     } else {
2194         /* No scripted caller, don't allow access. */
2195         principals = NULL;
2196     }
2198     /*
2199      * FIXME: Reparameterize so we don't call js_AtomToPrintableString unless
2200      *        there is an error (bug 324694).
2201      */
2202     if (!js_CheckPrincipalsAccess(cx, funobj, principals,
2203                                   fun->atom
2204                                   ? js_AtomToPrintableString(cx, fun->atom)
2205                                   : js_anonymous_str)) {
2206         return NULL;
2207     }
2208     return funobj;
2211 JSObject *
2212 js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags)
2214     JSObject *callable;
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;
2228 void
2229 js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
2231     JSType type;
2232     JSString *fallback;
2233     JSString *str;
2235     /*
2236      * We provide the typename as the fallback to handle the case when
2237      * valueOf is not a function, which prevents ValueToString from being
2238      * called as the default case inside js_DecompileValueGenerator (and
2239      * so recursing back to here).
2240      */
2241     type = JS_TypeOfValue(cx, *vp);
2242     fallback = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]);
2243     str = js_DecompileValueGenerator(cx,
2244                                      (flags & JSV2F_SEARCH_STACK)
2245                                      ? JSDVG_SEARCH_STACK
2246                                      : cx->fp
2247                                      ? vp - cx->fp->sp
2248                                      : JSDVG_IGNORE_STACK,
2249                                      *vp,
2250                                      fallback);
2251     if (str) {
2252         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2253                              (uintN)((flags & JSV2F_CONSTRUCT)
2254                                      ? JSMSG_NOT_CONSTRUCTOR
2255                                      : JSMSG_NOT_FUNCTION),
2256                              JS_GetStringBytes(str));
2257     }