Code

moving trunk for module inkscape
[inkscape.git] / src / dom / js / jsfun.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
40 /*
41  * JS function support.
42  */
43 #include "jsstddef.h"
44 #include <string.h>
45 #include "jstypes.h"
46 #include "jsbit.h"
47 #include "jsutil.h" /* Added by JSIFY */
48 #include "jsapi.h"
49 #include "jsarray.h"
50 #include "jsatom.h"
51 #include "jscntxt.h"
52 #include "jsconfig.h"
53 #include "jsdbgapi.h"
54 #include "jsfun.h"
55 #include "jsgc.h"
56 #include "jsinterp.h"
57 #include "jslock.h"
58 #include "jsnum.h"
59 #include "jsobj.h"
60 #include "jsopcode.h"
61 #include "jsparse.h"
62 #include "jsscan.h"
63 #include "jsscope.h"
64 #include "jsscript.h"
65 #include "jsstr.h"
66 #include "jsexn.h"
68 /* Generic function/call/arguments tinyids -- also reflected bit numbers. */
69 enum {
70     CALL_ARGUMENTS  = -1,       /* predefined arguments local variable */
71     CALL_CALLEE     = -2,       /* reference to active function's object */
72     ARGS_LENGTH     = -3,       /* number of actual args, arity if inactive */
73     ARGS_CALLEE     = -4,       /* reference from arguments to active funobj */
74     FUN_ARITY       = -5,       /* number of formal parameters; desired argc */
75     FUN_NAME        = -6,       /* function name, "" if anonymous */
76     FUN_CALLER      = -7        /* Function.prototype.caller, backward compat */
77 };
79 #if JSFRAME_OVERRIDE_BITS < 8
80 # error "not enough override bits in JSStackFrame.flags!"
81 #endif
83 #define TEST_OVERRIDE_BIT(fp, tinyid) \
84     ((fp)->flags & JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1)))
86 #define SET_OVERRIDE_BIT(fp, tinyid) \
87     ((fp)->flags |= JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1)))
89 #if JS_HAS_ARGS_OBJECT
91 JSBool
92 js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp)
93 {
94     JSObject *argsobj;
96     if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
97         JS_ASSERT(fp->callobj);
98         return OBJ_GET_PROPERTY(cx, fp->callobj,
99                                 (jsid) cx->runtime->atomState.argumentsAtom,
100                                 vp);
101     }
102     argsobj = js_GetArgsObject(cx, fp);
103     if (!argsobj)
104         return JS_FALSE;
105     *vp = OBJECT_TO_JSVAL(argsobj);
106     return JS_TRUE;
109 static JSBool
110 MarkArgDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
112     JSObject *argsobj;
113     jsval bmapval, bmapint;
114     size_t nbits, nbytes;
115     jsbitmap *bitmap;
117     argsobj = fp->argsobj;
118     (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
119     nbits = JS_MAX(fp->argc, fp->fun->nargs);
120     JS_ASSERT(slot < nbits);
121     if (JSVAL_IS_VOID(bmapval)) {
122         if (nbits <= JSVAL_INT_BITS) {
123             bmapint = 0;
124             bitmap = (jsbitmap *) &bmapint;
125         } else {
126             nbytes = JS_HOWMANY(nbits, JS_BITS_PER_WORD) * sizeof(jsbitmap);
127             bitmap = (jsbitmap *) JS_malloc(cx, nbytes);
128             if (!bitmap)
129                 return JS_FALSE;
130             memset(bitmap, 0, nbytes);
131             bmapval = PRIVATE_TO_JSVAL(bitmap);
132             JS_SetReservedSlot(cx, argsobj, 0, bmapval);
133         }
134     } else {
135         if (nbits <= JSVAL_INT_BITS) {
136             bmapint = JSVAL_TO_INT(bmapval);
137             bitmap = (jsbitmap *) &bmapint;
138         } else {
139             bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval);
140         }
141     }
142     JS_SET_BIT(bitmap, slot);
143     if (bitmap == (jsbitmap *) &bmapint) {
144         bmapval = INT_TO_JSVAL(bmapint);
145         JS_SetReservedSlot(cx, argsobj, 0, bmapval);
146     }
147     return JS_TRUE;
150 /* NB: Infallible predicate, false does not mean error/exception. */
151 static JSBool
152 ArgWasDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
154     JSObject *argsobj;
155     jsval bmapval, bmapint;
156     jsbitmap *bitmap;
158     argsobj = fp->argsobj;
159     (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
160     if (JSVAL_IS_VOID(bmapval))
161         return JS_FALSE;
162     if (JS_MAX(fp->argc, fp->fun->nargs) <= JSVAL_INT_BITS) {
163         bmapint = JSVAL_TO_INT(bmapval);
164         bitmap = (jsbitmap *) &bmapint;
165     } else {
166         bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval);
167     }
168     return JS_TEST_BIT(bitmap, slot) != 0;
171 JSBool
172 js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id,
173                    JSObject **objp, jsval *vp)
175     jsval val;
176     JSObject *obj;
177     uintN slot;
179     if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
180         JS_ASSERT(fp->callobj);
181         if (!OBJ_GET_PROPERTY(cx, fp->callobj,
182                               (jsid) cx->runtime->atomState.argumentsAtom,
183                               &val)) {
184             return JS_FALSE;
185         }
186         if (JSVAL_IS_PRIMITIVE(val)) {
187             obj = js_ValueToNonNullObject(cx, val);
188             if (!obj)
189                 return JS_FALSE;
190         } else {
191             obj = JSVAL_TO_OBJECT(val);
192         }
193         *objp = obj;
194         return OBJ_GET_PROPERTY(cx, obj, id, vp);
195     }
197     *objp = NULL;
198     *vp = JSVAL_VOID;
199     if (JSVAL_IS_INT(id)) {
200         slot = (uintN) JSVAL_TO_INT(id);
201         if (slot < JS_MAX(fp->argc, fp->fun->nargs)) {
202             if (fp->argsobj && ArgWasDeleted(cx, fp, slot))
203                 return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
204             *vp = fp->argv[slot];
205         }
206     } else {
207         if (id == (jsid) cx->runtime->atomState.lengthAtom) {
208             if (fp->argsobj && TEST_OVERRIDE_BIT(fp, ARGS_LENGTH))
209                 return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
210             *vp = INT_TO_JSVAL((jsint) fp->argc);
211         }
212     }
213     return JS_TRUE;
216 JSObject *
217 js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
219     JSObject *argsobj;
221     /* Create an arguments object for fp only if it lacks one. */
222     JS_ASSERT(fp->fun);
223     argsobj = fp->argsobj;
224     if (argsobj)
225         return argsobj;
227     /* Link the new object to fp so it can get actual argument values. */
228     argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL);
229     if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) {
230         cx->newborn[GCX_OBJECT] = NULL;
231         return NULL;
232     }
233     fp->argsobj = argsobj;
234     return argsobj;
237 static JSBool
238 args_enumerate(JSContext *cx, JSObject *obj);
240 JSBool
241 js_PutArgsObject(JSContext *cx, JSStackFrame *fp)
243     JSObject *argsobj;
244     jsval bmapval, rval;
245     JSBool ok;
246     JSRuntime *rt;
248     /*
249      * Reuse args_enumerate here to reflect fp's actual arguments as indexed
250      * elements of argsobj.  Do this first, before clearing and freeing the
251      * deleted argument slot bitmap, because args_enumerate depends on that.
252      */
253     argsobj = fp->argsobj;
254     ok = args_enumerate(cx, argsobj);
256     /*
257      * Now clear the deleted argument number bitmap slot and free the bitmap,
258      * if one was actually created due to 'delete arguments[0]' or similar.
259      */
260     (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
261     if (!JSVAL_IS_VOID(bmapval)) {
262         JS_SetReservedSlot(cx, argsobj, 0, JSVAL_VOID);
263         if (JS_MAX(fp->argc, fp->fun->nargs) > JSVAL_INT_BITS)
264             JS_free(cx, JSVAL_TO_PRIVATE(bmapval));
265     }
267     /*
268      * Now get the prototype properties so we snapshot fp->fun and fp->argc
269      * before fp goes away.
270      */
271     rt = cx->runtime;
272     ok &= js_GetProperty(cx, argsobj, (jsid)rt->atomState.calleeAtom, &rval);
273     ok &= js_SetProperty(cx, argsobj, (jsid)rt->atomState.calleeAtom, &rval);
274     ok &= js_GetProperty(cx, argsobj, (jsid)rt->atomState.lengthAtom, &rval);
275     ok &= js_SetProperty(cx, argsobj, (jsid)rt->atomState.lengthAtom, &rval);
277     /*
278      * Clear the private pointer to fp, which is about to go away (js_Invoke).
279      * Do this last because the args_enumerate and js_GetProperty calls above
280      * need to follow the private slot to find fp.
281      */
282     ok &= JS_SetPrivate(cx, argsobj, NULL);
283     fp->argsobj = NULL;
284     return ok;
287 static JSBool
288 args_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
290     jsint slot;
291     JSStackFrame *fp;
293     if (!JSVAL_IS_INT(id))
294         return JS_TRUE;
295     fp = (JSStackFrame *)
296          JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
297     if (!fp)
298         return JS_TRUE;
299     JS_ASSERT(fp->argsobj);
300     JS_ASSERT(fp->fun);
302     slot = JSVAL_TO_INT(id);
303     switch (slot) {
304       case ARGS_CALLEE:
305       case ARGS_LENGTH:
306         SET_OVERRIDE_BIT(fp, slot);
307         break;
309       default:
310         if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs) &&
311             !MarkArgDeleted(cx, fp, slot)) {
312             return JS_FALSE;
313         }
314         break;
315     }
316     return JS_TRUE;
319 static JSBool
320 args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
322     jsint slot;
323     JSStackFrame *fp;
325     if (!JSVAL_IS_INT(id))
326         return JS_TRUE;
327     fp = (JSStackFrame *)
328          JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
329     if (!fp)
330         return JS_TRUE;
331     JS_ASSERT(fp->argsobj);
332     JS_ASSERT(fp->fun);
334     slot = JSVAL_TO_INT(id);
335     switch (slot) {
336       case ARGS_CALLEE:
337         if (!TEST_OVERRIDE_BIT(fp, slot))
338             *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object);
339         break;
341       case ARGS_LENGTH:
342         if (!TEST_OVERRIDE_BIT(fp, slot))
343             *vp = INT_TO_JSVAL((jsint)fp->argc);
344         break;
346       default:
347         if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs) &&
348             !ArgWasDeleted(cx, fp, slot)) {
349             *vp = fp->argv[slot];
350         }
351         break;
352     }
353     return JS_TRUE;
356 static JSBool
357 args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
359     JSStackFrame *fp;
360     jsint slot;
362     if (!JSVAL_IS_INT(id))
363         return JS_TRUE;
364     fp = (JSStackFrame *)
365          JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
366     if (!fp)
367         return JS_TRUE;
368     JS_ASSERT(fp->argsobj);
369     JS_ASSERT(fp->fun);
371     slot = JSVAL_TO_INT(id);
372     switch (slot) {
373       case ARGS_CALLEE:
374       case ARGS_LENGTH:
375         SET_OVERRIDE_BIT(fp, slot);
376         break;
378       default:
379         if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs) &&
380             !ArgWasDeleted(cx, fp, slot)) {
381             fp->argv[slot] = *vp;
382         }
383         break;
384     }
385     return JS_TRUE;
388 static JSBool
389 args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
390              JSObject **objp)
392     JSStackFrame *fp;
393     uintN slot;
394     JSString *str;
395     JSAtom *atom;
396     intN tinyid;
397     jsval value;
399     *objp = NULL;
400     fp = (JSStackFrame *)
401          JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
402     if (!fp)
403         return JS_TRUE;
404     JS_ASSERT(fp->argsobj);
405     JS_ASSERT(fp->fun);
407     if (JSVAL_IS_INT(id)) {
408         slot = JSVAL_TO_INT(id);
409         if (slot < JS_MAX(fp->argc, fp->fun->nargs) &&
410             !ArgWasDeleted(cx, fp, slot)) {
411             /* XXX ECMA specs DontEnum, contrary to other array-like objects */
412             if (!js_DefineProperty(cx, obj, (jsid) id, fp->argv[slot],
413                                    args_getProperty, args_setProperty,
414                                    JSVERSION_IS_ECMA(cx->version)
415                                    ? 0
416                                    : JSPROP_ENUMERATE,
417                                    NULL)) {
418                 return JS_FALSE;
419             }
420             *objp = obj;
421         }
422     } else {
423         str = JSVAL_TO_STRING(id);
424         atom = cx->runtime->atomState.lengthAtom;
425         if (str == ATOM_TO_STRING(atom)) {
426             tinyid = ARGS_LENGTH;
427             value = INT_TO_JSVAL(fp->argc);
428         } else {
429             atom = cx->runtime->atomState.calleeAtom;
430             if (str == ATOM_TO_STRING(atom)) {
431                 tinyid = ARGS_CALLEE;
432                 value = fp->argv ? fp->argv[-2]
433                                  : OBJECT_TO_JSVAL(fp->fun->object);
434             } else {
435                 atom = NULL;
437                 /* Quell GCC overwarnings. */
438                 tinyid = 0;
439                 value = JSVAL_NULL;
440             }
441         }
443         if (atom && !TEST_OVERRIDE_BIT(fp, tinyid)) {
444             if (!js_DefineNativeProperty(cx, obj, (jsid) atom, value,
445                                          args_getProperty, args_setProperty, 0,
446                                          SPROP_HAS_SHORTID, tinyid, NULL)) {
447                 return JS_FALSE;
448             }
449             *objp = obj;
450         }
451     }
453     return JS_TRUE;
456 static JSBool
457 args_enumerate(JSContext *cx, JSObject *obj)
459     JSStackFrame *fp;
460     JSObject *pobj;
461     JSProperty *prop;
462     uintN slot, nargs;
464     fp = (JSStackFrame *)
465          JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
466     if (!fp)
467         return JS_TRUE;
468     JS_ASSERT(fp->argsobj);
469     JS_ASSERT(fp->fun);
471     /*
472      * Trigger reflection with value snapshot in args_resolve using a series
473      * of js_LookupProperty calls.  We handle length, callee, and the indexed
474      * argument properties.  We know that args_resolve covers all these cases
475      * and creates direct properties of obj, but that it may fail to resolve
476      * length or callee if overridden.
477      */
478     if (!js_LookupProperty(cx, obj, (jsid) cx->runtime->atomState.lengthAtom,
479                            &pobj, &prop)) {
480         return JS_FALSE;
481     }
482     if (prop)
483         OBJ_DROP_PROPERTY(cx, pobj, prop);
485     if (!js_LookupProperty(cx, obj, (jsid) cx->runtime->atomState.calleeAtom,
486                            &pobj, &prop)) {
487         return JS_FALSE;
488     }
489     if (prop)
490         OBJ_DROP_PROPERTY(cx, pobj, prop);
492     nargs = JS_MAX(fp->argc, fp->fun->nargs);
493     for (slot = 0; slot < nargs; slot++) {
494         if (!js_LookupProperty(cx, obj, (jsid) INT_TO_JSVAL((jsint)slot),
495                                &pobj, &prop)) {
496             return JS_FALSE;
497         }
498         if (prop)
499             OBJ_DROP_PROPERTY(cx, pobj, prop);
500     }
501     return JS_TRUE;
504 /*
505  * The Arguments class is not initialized via JS_InitClass, and must not be,
506  * because its name is "Object".  Per ECMA, that causes instances of it to
507  * delegate to the object named by Object.prototype.  It also ensures that
508  * arguments.toString() returns "[object Object]".
509  *
510  * The JSClass functions below collaborate to lazily reflect and synchronize
511  * actual argument values, argument count, and callee function object stored
512  * in a JSStackFrame with their corresponding property values in the frame's
513  * arguments object.
514  */
515 JSClass js_ArgumentsClass = {
516     js_Object_str,
517     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1),
518     JS_PropertyStub,  args_delProperty,
519     args_getProperty, args_setProperty,
520     args_enumerate,   (JSResolveOp) args_resolve,
521     JS_ConvertStub,   JS_FinalizeStub,
522     JSCLASS_NO_OPTIONAL_MEMBERS
523 };
525 #endif /* JS_HAS_ARGS_OBJECT */
527 #if JS_HAS_CALL_OBJECT
529 JSObject *
530 js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent)
532     JSObject *callobj, *funobj;
534     /* Create a call object for fp only if it lacks one. */
535     JS_ASSERT(fp->fun);
536     callobj = fp->callobj;
537     if (callobj)
538         return callobj;
539     JS_ASSERT(fp->fun);
541     /* The default call parent is its function's parent (static link). */
542     if (!parent) {
543         funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object;
544         if (funobj)
545             parent = OBJ_GET_PARENT(cx, funobj);
546     }
548     /* Create the call object and link it to its stack frame. */
549     callobj = js_NewObject(cx, &js_CallClass, NULL, parent);
550     if (!callobj || !JS_SetPrivate(cx, callobj, fp)) {
551         cx->newborn[GCX_OBJECT] = NULL;
552         return NULL;
553     }
554     fp->callobj = callobj;
556     /* Make callobj be the scope chain and the variables object. */
557     fp->scopeChain = callobj;
558     fp->varobj = callobj;
559     return callobj;
562 static JSBool
563 call_enumerate(JSContext *cx, JSObject *obj);
565 JSBool
566 js_PutCallObject(JSContext *cx, JSStackFrame *fp)
568     JSObject *callobj;
569     JSBool ok;
570     jsid argsid;
571     jsval aval;
573     /*
574      * Reuse call_enumerate here to reflect all actual args and vars into the
575      * call object from fp.
576      */
577     callobj = fp->callobj;
578     if (!callobj)
579         return JS_TRUE;
580     ok = call_enumerate(cx, callobj);
582     /*
583      * Get the arguments object to snapshot fp's actual argument values.
584      */
585     if (fp->argsobj) {
586         argsid = (jsid) cx->runtime->atomState.argumentsAtom;
587         ok &= js_GetProperty(cx, callobj, argsid, &aval);
588         ok &= js_SetProperty(cx, callobj, argsid, &aval);
589         ok &= js_PutArgsObject(cx, fp);
590     }
592     /*
593      * Clear the private pointer to fp, which is about to go away (js_Invoke).
594      * Do this last because the call_enumerate and js_GetProperty calls above
595      * need to follow the private slot to find fp.
596      */
597     ok &= JS_SetPrivate(cx, callobj, NULL);
598     fp->callobj = NULL;
599     return ok;
602 static JSPropertySpec call_props[] = {
603     {js_arguments_str,  CALL_ARGUMENTS, JSPROP_PERMANENT,0,0},
604     {"__callee__",      CALL_CALLEE,    0,0,0},
605     {0,0,0,0,0}
606 };
608 static JSBool
609 call_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
611     JSStackFrame *fp;
612     jsint slot;
614     if (!JSVAL_IS_INT(id))
615         return JS_TRUE;
616     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
617     if (!fp)
618         return JS_TRUE;
619     JS_ASSERT(fp->fun);
621     slot = JSVAL_TO_INT(id);
622     switch (slot) {
623       case CALL_ARGUMENTS:
624         if (!TEST_OVERRIDE_BIT(fp, slot)) {
625             JSObject *argsobj = js_GetArgsObject(cx, fp);
626             if (!argsobj)
627                 return JS_FALSE;
628             *vp = OBJECT_TO_JSVAL(argsobj);
629         }
630         break;
632       case CALL_CALLEE:
633         if (!TEST_OVERRIDE_BIT(fp, slot))
634             *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object);
635         break;
637       default:
638         if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs))
639             *vp = fp->argv[slot];
640         break;
641     }
642     return JS_TRUE;
645 static JSBool
646 call_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
648     JSStackFrame *fp;
649     jsint slot;
651     if (!JSVAL_IS_INT(id))
652         return JS_TRUE;
653     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
654     if (!fp)
655         return JS_TRUE;
656     JS_ASSERT(fp->fun);
658     slot = JSVAL_TO_INT(id);
659     switch (slot) {
660       case CALL_ARGUMENTS:
661       case CALL_CALLEE:
662         SET_OVERRIDE_BIT(fp, slot);
663         break;
665       default:
666         if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs))
667             fp->argv[slot] = *vp;
668         break;
669     }
670     return JS_TRUE;
673 JSBool
674 js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
676     JSStackFrame *fp;
678     JS_ASSERT(JSVAL_IS_INT(id));
679     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
680     if (fp) {
681         /* XXX no jsint slot commoning here to avoid MSVC1.52 crashes */
682         if ((uintN)JSVAL_TO_INT(id) < fp->nvars)
683             *vp = fp->vars[JSVAL_TO_INT(id)];
684     }
685     return JS_TRUE;
688 JSBool
689 js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
691     JSStackFrame *fp;
693     JS_ASSERT(JSVAL_IS_INT(id));
694     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
695     if (fp) {
696         /* XXX jsint slot is block-local here to avoid MSVC1.52 crashes */
697         jsint slot = JSVAL_TO_INT(id);
698         if ((uintN)slot < fp->nvars)
699             fp->vars[slot] = *vp;
700     }
701     return JS_TRUE;
704 static JSBool
705 call_enumerate(JSContext *cx, JSObject *obj)
707     JSStackFrame *fp;
708     JSObject *funobj;
709     JSScope *scope;
710     JSScopeProperty *sprop, *cprop;
711     JSPropertyOp getter;
712     jsval *vec;
713     JSProperty *prop;
715     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
716     if (!fp)
717         return JS_TRUE;
719     /*
720      * Do not enumerate a cloned function object at fp->argv[-2], it may have
721      * gained its own (mutable) scope (e.g., a brutally-shared XUL script sets
722      * the clone's prototype property).  We must enumerate the function object
723      * that was decorated with parameter and local variable properties by the
724      * compiler when the compiler created fp->fun, namely fp->fun->object.
725      *
726      * Contrast with call_resolve, where we prefer fp->argv[-2], because we'll
727      * use js_LookupProperty to find any overridden properties in that object,
728      * if it was a mutated clone; and if not, we will search its prototype,
729      * fp->fun->object, to find compiler-created params and locals.
730      */
731     funobj = fp->fun->object;
732     if (!funobj)
733         return JS_TRUE;
735     /*
736      * Reflect actual args from fp->argv for formal parameters, and local vars
737      * and functions in fp->vars for declared variables and nested-at-top-level
738      * local functions.
739      */
740     scope = OBJ_SCOPE(funobj);
741     for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
742         getter = sprop->getter;
743         if (getter == js_GetArgument)
744             vec = fp->argv;
745         else if (getter == js_GetLocalVariable)
746             vec = fp->vars;
747         else
748             continue;
750         /* Trigger reflection in call_resolve by doing a lookup. */
751         if (!js_LookupProperty(cx, obj, sprop->id, &obj, &prop))
752             return JS_FALSE;
753         JS_ASSERT(obj && prop);
754         cprop = (JSScopeProperty *)prop;
755         LOCKED_OBJ_SET_SLOT(obj, cprop->slot, vec[sprop->shortid]);
756         OBJ_DROP_PROPERTY(cx, obj, prop);
757     }
759     return JS_TRUE;
762 static JSBool
763 call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
764              JSObject **objp)
766     JSStackFrame *fp;
767     JSObject *funobj;
768     JSString *str;
769     JSAtom *atom;
770     JSObject *obj2;
771     JSScopeProperty *sprop;
772     jsid propid;
773     JSPropertyOp getter, setter;
774     uintN attrs, slot, nslots, spflags;
775     jsval *vp, value;
776     intN shortid;
778     fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
779     if (!fp)
780         return JS_TRUE;
781     JS_ASSERT(fp->fun);
783     if (!JSVAL_IS_STRING(id))
784         return JS_TRUE;
786     funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object;
787     if (!funobj)
788         return JS_TRUE;
790     str = JSVAL_TO_STRING(id);
791     atom = js_AtomizeString(cx, str, 0);
792     if (!atom)
793         return JS_FALSE;
794     if (!js_LookupProperty(cx, funobj, (jsid)atom, &obj2,
795                            (JSProperty **)&sprop)) {
796         return JS_FALSE;
797     }
799     if (sprop && OBJ_IS_NATIVE(obj2)) {
800         propid = sprop->id;
801         getter = sprop->getter;
802         attrs = sprop->attrs & ~JSPROP_SHARED;
803         slot = (uintN) sprop->shortid;
804         OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
805         if (getter == js_GetArgument || getter == js_GetLocalVariable) {
806             if (getter == js_GetArgument) {
807                 vp = fp->argv;
808                 nslots = JS_MAX(fp->argc, fp->fun->nargs);
809                 getter = setter = NULL;
810             } else {
811                 vp = fp->vars;
812                 nslots = fp->nvars;
813                 getter = js_GetCallVariable;
814                 setter = js_SetCallVariable;
815             }
816             if (slot < nslots) {
817                 value = vp[slot];
818                 spflags = SPROP_HAS_SHORTID;
819                 shortid = (intN) slot;
820             } else {
821                 value = JSVAL_VOID;
822                 spflags = 0;
823                 shortid = 0;
824             }
825             if (!js_DefineNativeProperty(cx, obj, propid, value,
826                                          getter, setter, attrs,
827                                          spflags, shortid, NULL)) {
828                 return JS_FALSE;
829             }
830             *objp = obj;
831         }
832     }
833     return JS_TRUE;
836 static JSBool
837 call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
839     JSStackFrame *fp;
841     if (type == JSTYPE_FUNCTION) {
842         fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
843         if (fp) {
844             JS_ASSERT(fp->fun);
845             *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object);
846         }
847     }
848     return JS_TRUE;
851 JSClass js_CallClass = {
852     js_Call_str,
853     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
854     JS_PropertyStub,  JS_PropertyStub,
855     call_getProperty, call_setProperty,
856     call_enumerate,   (JSResolveOp)call_resolve,
857     call_convert,     JS_FinalizeStub,
858     JSCLASS_NO_OPTIONAL_MEMBERS
859 };
861 #endif /* JS_HAS_CALL_OBJECT */
863 /* SHARED because fun_getProperty always computes a new value. */
864 #define FUNCTION_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED)
866 static JSPropertySpec function_props[] = {
867     {js_arguments_str, CALL_ARGUMENTS, FUNCTION_PROP_ATTRS,0,0},
868     {js_arity_str,     FUN_ARITY,      FUNCTION_PROP_ATTRS,0,0},
869     {js_length_str,    ARGS_LENGTH,    FUNCTION_PROP_ATTRS,0,0},
870     {js_name_str,      FUN_NAME,       FUNCTION_PROP_ATTRS,0,0},
871     {js_caller_str,    FUN_CALLER,     FUNCTION_PROP_ATTRS,0,0},
872     {0,0,0,0,0}
873 };
875 static JSBool
876 fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
878     jsint slot;
879     JSFunction *fun;
880     JSStackFrame *fp;
881 #if defined _MSC_VER &&_MSC_VER <= 800
882     /* MSVC1.5 coredumps */
883     jsval bogus = *vp;
884 #endif
886     if (!JSVAL_IS_INT(id))
887         return JS_TRUE;
888     slot = JSVAL_TO_INT(id);
890     /* No valid function object should lack private data, but check anyway. */
891     fun = (JSFunction *)JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL);
892     if (!fun)
893         return JS_TRUE;
895     /* Find fun's top-most activation record. */
896     for (fp = cx->fp; fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL));
897          fp = fp->down) {
898         continue;
899     }
901     switch (slot) {
902       case CALL_ARGUMENTS:
903 #if JS_HAS_ARGS_OBJECT
904         /* Warn if strict about f.arguments or equivalent unqualified uses. */
905         if (!JS_ReportErrorFlagsAndNumber(cx,
906                                           JSREPORT_WARNING | JSREPORT_STRICT,
907                                           js_GetErrorMessage, NULL,
908                                           JSMSG_DEPRECATED_USAGE,
909                                           js_arguments_str)) {
910             return JS_FALSE;
911         }
912         if (fp) {
913             if (!js_GetArgsValue(cx, fp, vp))
914                 return JS_FALSE;
915         } else {
916             *vp = JSVAL_NULL;
917         }
918         break;
919 #else  /* !JS_HAS_ARGS_OBJECT */
920         *vp = OBJECT_TO_JSVAL(fp ? obj : NULL);
921         break;
922 #endif /* !JS_HAS_ARGS_OBJECT */
924       case ARGS_LENGTH:
925         if (!JSVERSION_IS_ECMA(cx->version))
926             *vp = INT_TO_JSVAL((jsint)(fp && fp->fun ? fp->argc : fun->nargs));
927         else
928       case FUN_ARITY:
929             *vp = INT_TO_JSVAL((jsint)fun->nargs);
930         break;
932       case FUN_NAME:
933         *vp = fun->atom
934               ? ATOM_KEY(fun->atom)
935               : STRING_TO_JSVAL(cx->runtime->emptyString);
936         break;
938       case FUN_CALLER:
939         while (fp && (fp->flags & JSFRAME_SKIP_CALLER) && fp->down)
940             fp = fp->down;
941         if (fp && fp->down && fp->down->fun && fp->down->argv)
942             *vp = fp->down->argv[-2];
943         else
944             *vp = JSVAL_NULL;
945         if (!JSVAL_IS_PRIMITIVE(*vp) && cx->runtime->checkObjectAccess) {
946             id = ATOM_KEY(cx->runtime->atomState.callerAtom);
947             if (!cx->runtime->checkObjectAccess(cx, obj, id, JSACC_READ, vp))
948                 return JS_FALSE;
949         }
950         break;
952       default:
953         /* XXX fun[0] and fun.arguments[0] are equivalent. */
954         if (fp && fp->fun && (uintN)slot < fp->fun->nargs)
955 #if defined _MSC_VER &&_MSC_VER <= 800
956           /* MSVC1.5 coredumps */
957           if (bogus == *vp)
958 #endif
959             *vp = fp->argv[slot];
960         break;
961     }
963     return JS_TRUE;
966 static JSBool
967 fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
968             JSObject **objp)
970     JSFunction *fun;
971     JSString *str;
972     JSAtom *prototypeAtom;
974     if (!JSVAL_IS_STRING(id))
975         return JS_TRUE;
977     /* No valid function object should lack private data, but check anyway. */
978     fun = (JSFunction *)JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL);
979     if (!fun || !fun->object)
980         return JS_TRUE;
982     /* No need to reflect fun.prototype in 'fun.prototype = ...'. */
983     if (flags & JSRESOLVE_ASSIGNING)
984         return JS_TRUE;
986     /*
987      * Ok, check whether id is 'prototype' and bootstrap the function object's
988      * prototype property.
989      */
990     str = JSVAL_TO_STRING(id);
991     prototypeAtom = cx->runtime->atomState.classPrototypeAtom;
992     if (str == ATOM_TO_STRING(prototypeAtom)) {
993         JSObject *proto, *parentProto;
994         jsval pval;
996         proto = parentProto = NULL;
997         if (fun->object != obj && fun->object) {
998             /*
999              * Clone of a function: make its prototype property value have the
1000              * same class as the clone-parent's prototype.
1001              */
1002             if (!OBJ_GET_PROPERTY(cx, fun->object, (jsid)prototypeAtom, &pval))
1003                 return JS_FALSE;
1004             if (JSVAL_IS_OBJECT(pval))
1005                 parentProto = JSVAL_TO_OBJECT(pval);
1006         }
1008         /*
1009          * Beware of the wacky case of a user function named Object -- trying
1010          * to find a prototype for that will recur back here ad perniciem.
1011          */
1012         if (!parentProto && fun->atom == cx->runtime->atomState.ObjectAtom)
1013             return JS_TRUE;
1015         /*
1016          * If resolving "prototype" in a clone, clone the parent's prototype.
1017          * Pass the constructor's (obj's) parent as the prototype parent, to
1018          * avoid defaulting to parentProto.constructor.__parent__.
1019          */
1020         proto = js_NewObject(cx, &js_ObjectClass, parentProto,
1021                              OBJ_GET_PARENT(cx, obj));
1022         if (!proto)
1023             return JS_FALSE;
1025         /*
1026          * ECMA says that constructor.prototype is DontEnum | DontDelete for
1027          * user-defined functions, but DontEnum | ReadOnly | DontDelete for
1028          * native "system" constructors such as Object or Function.  So lazily
1029          * set the former here in fun_resolve, but eagerly define the latter
1030          * in JS_InitClass, with the right attributes.
1031          */
1032         if (!js_SetClassPrototype(cx, obj, proto, JSPROP_PERMANENT)) {
1033             cx->newborn[GCX_OBJECT] = NULL;
1034             return JS_FALSE;
1035         }
1036         *objp = obj;
1037     }
1039     return JS_TRUE;
1042 static JSBool
1043 fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
1045     switch (type) {
1046       case JSTYPE_FUNCTION:
1047         *vp = OBJECT_TO_JSVAL(obj);
1048         return JS_TRUE;
1049       default:
1050         return js_TryValueOf(cx, obj, type, vp);
1051     }
1054 static void
1055 fun_finalize(JSContext *cx, JSObject *obj)
1057     JSFunction *fun;
1059     /* No valid function object should lack private data, but check anyway. */
1060     fun = (JSFunction *) JS_GetPrivate(cx, obj);
1061     if (!fun)
1062         return;
1063     if (fun->object == obj)
1064         fun->object = NULL;
1065     JS_ATOMIC_DECREMENT(&fun->nrefs);
1066     if (fun->nrefs)
1067         return;
1068     if (fun->script)
1069         js_DestroyScript(cx, fun->script);
1070     JS_free(cx, fun);
1073 #if JS_HAS_XDR
1075 #include "jsxdrapi.h"
1077 enum {
1078     JSXDR_FUNARG = 1,
1079     JSXDR_FUNVAR = 2,
1080     JSXDR_FUNCONST = 3
1081 };
1083 /* XXX store parent and proto, if defined */
1084 static JSBool
1085 fun_xdrObject(JSXDRState *xdr, JSObject **objp)
1087     JSContext *cx;
1088     JSFunction *fun;
1089     JSString *atomstr;
1090     char *propname;
1091     JSScopeProperty *sprop;
1092     uint32 userid;              /* NB: holds a signed int-tagged jsval */
1093     JSAtom *atom;
1094     uintN i, n, dupflag;
1095     uint32 type;
1096 #ifdef DEBUG
1097     uintN nvars = 0, nargs = 0;
1098 #endif
1100     cx = xdr->cx;
1101     if (xdr->mode == JSXDR_ENCODE) {
1102         /*
1103          * No valid function object should lack private data, but fail soft
1104          * (return true, no error report) in case one does due to API pilot
1105          * or internal error.
1106          */
1107         fun = (JSFunction *) JS_GetPrivate(cx, *objp);
1108         if (!fun)
1109             return JS_TRUE;
1110         atomstr = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;
1111     } else {
1112         fun = js_NewFunction(cx, NULL, NULL, 0, 0, NULL, NULL);
1113         if (!fun)
1114             return JS_FALSE;
1115         atomstr = NULL;
1116     }
1118     if (!JS_XDRStringOrNull(xdr, &atomstr) ||
1119         !JS_XDRUint16(xdr, &fun->nargs) ||
1120         !JS_XDRUint16(xdr, &fun->extra) ||
1121         !JS_XDRUint16(xdr, &fun->nvars) ||
1122         !JS_XDRUint8(xdr, &fun->flags)) {
1123         return JS_FALSE;
1124     }
1126     /* do arguments and local vars */
1127     if (fun->object) {
1128         n = fun->nargs + fun->nvars;
1129         if (xdr->mode == JSXDR_ENCODE) {
1130             JSScope *scope;
1131             JSScopeProperty **spvec, *auto_spvec[8];
1132             void *mark;
1134             if (n <= sizeof auto_spvec / sizeof auto_spvec[0]) {
1135                 spvec = auto_spvec;
1136                 mark = NULL;
1137             } else {
1138                 mark = JS_ARENA_MARK(&cx->tempPool);
1139                 JS_ARENA_ALLOCATE_CAST(spvec, JSScopeProperty **, &cx->tempPool,
1140                                        n * sizeof(JSScopeProperty *));
1141                 if (!spvec) {
1142                     JS_ReportOutOfMemory(cx);
1143                     return JS_FALSE;
1144                 }
1145             }
1146             scope = OBJ_SCOPE(fun->object);
1147             for (sprop = SCOPE_LAST_PROP(scope); sprop;
1148                  sprop = sprop->parent) {
1149                 if (sprop->getter == js_GetArgument) {
1150                     JS_ASSERT(nargs++ <= fun->nargs);
1151                     spvec[sprop->shortid] = sprop;
1152                 } else if (sprop->getter == js_GetLocalVariable) {
1153                     JS_ASSERT(nvars++ <= fun->nvars);
1154                     spvec[fun->nargs + sprop->shortid] = sprop;
1155                 }
1156             }
1157             for (i = 0; i < n; i++) {
1158                 sprop = spvec[i];
1159                 JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
1160                 type = (i < fun->nargs)
1161                        ? JSXDR_FUNARG
1162                        : (sprop->attrs & JSPROP_READONLY)
1163                        ? JSXDR_FUNCONST
1164                        : JSXDR_FUNVAR;
1165                 userid = INT_TO_JSVAL(sprop->shortid);
1166                 /* XXX lossy conversion, need new XDR version for ECMAv3 */
1167                 propname = JS_GetStringBytes(ATOM_TO_STRING((JSAtom *)sprop->id));
1168                 if (!propname ||
1169                     !JS_XDRUint32(xdr, &type) ||
1170                     !JS_XDRUint32(xdr, &userid) ||
1171                     !JS_XDRCString(xdr, &propname)) {
1172                     if (mark)
1173                         JS_ARENA_RELEASE(&cx->tempPool, mark);
1174                     return JS_FALSE;
1175                 }
1176             }
1177             if (mark)
1178                 JS_ARENA_RELEASE(&cx->tempPool, mark);
1179         } else {
1180             JSPropertyOp getter, setter;
1182             for (i = n; i != 0; i--) {
1183                 uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
1185                 if (!JS_XDRUint32(xdr, &type) ||
1186                     !JS_XDRUint32(xdr, &userid) ||
1187                     !JS_XDRCString(xdr, &propname)) {
1188                     return JS_FALSE;
1189                 }
1190                 JS_ASSERT(type == JSXDR_FUNARG || type == JSXDR_FUNVAR ||
1191                           type == JSXDR_FUNCONST);
1192                 if (type == JSXDR_FUNARG) {
1193                     getter = js_GetArgument;
1194                     setter = js_SetArgument;
1195                     JS_ASSERT(nargs++ <= fun->nargs);
1196                 } else if (type == JSXDR_FUNVAR || type == JSXDR_FUNCONST) {
1197                     getter = js_GetLocalVariable;
1198                     setter = js_SetLocalVariable;
1199                     if (type == JSXDR_FUNCONST)
1200                         attrs |= JSPROP_READONLY;
1201                     JS_ASSERT(nvars++ <= fun->nvars);
1202                 } else {
1203                     getter = NULL;
1204                     setter = NULL;
1205                 }
1206                 atom = js_Atomize(cx, propname, strlen(propname), 0);
1207                 JS_free(cx, propname);
1208                 if (!atom)
1209                     return JS_FALSE;
1211                 /* Flag duplicate argument if atom is bound in fun->object. */
1212                 dupflag = SCOPE_GET_PROPERTY(OBJ_SCOPE(fun->object), (jsid)atom)
1213                           ? SPROP_IS_DUPLICATE
1214                           : 0;
1216                 if (!js_AddNativeProperty(cx, fun->object, (jsid)atom,
1217                                           getter, setter, SPROP_INVALID_SLOT,
1218                                           attrs | JSPROP_SHARED,
1219                                           SPROP_HAS_SHORTID | dupflag,
1220                                           JSVAL_TO_INT(userid))) {
1221                     return JS_FALSE;
1222                 }
1223             }
1224         }
1225     }
1227     if (!js_XDRScript(xdr, &fun->script, NULL))
1228         return JS_FALSE;
1230     if (xdr->mode == JSXDR_DECODE) {
1231         *objp = fun->object;
1232         if (atomstr) {
1233             fun->atom = js_AtomizeString(cx, atomstr, 0);
1234             if (!fun->atom)
1235                 return JS_FALSE;
1237             if (!OBJ_DEFINE_PROPERTY(cx, cx->globalObject,
1238                                      (jsid)fun->atom, OBJECT_TO_JSVAL(*objp),
1239                                      NULL, NULL, JSPROP_ENUMERATE,
1240                                      NULL)) {
1241                 return JS_FALSE;
1242             }
1243         }
1245         js_CallNewScriptHook(cx, fun->script, fun);
1246     }
1248     return JS_TRUE;
1251 #else  /* !JS_HAS_XDR */
1253 #define fun_xdrObject NULL
1255 #endif /* !JS_HAS_XDR */
1257 #if JS_HAS_INSTANCEOF
1259 /*
1260  * [[HasInstance]] internal method for Function objects: fetch the .prototype
1261  * property of its 'this' parameter, and walks the prototype chain of v (only
1262  * if v is an object) returning true if .prototype is found.
1263  */
1264 static JSBool
1265 fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
1267     jsval pval, cval;
1268     JSString *str;
1269     JSObject *proto, *obj2;
1270     JSFunction *cfun, *ofun;
1272     if (!OBJ_GET_PROPERTY(cx, obj,
1273                           (jsid)cx->runtime->atomState.classPrototypeAtom,
1274                           &pval)) {
1275         return JS_FALSE;
1276     }
1278     if (JSVAL_IS_PRIMITIVE(pval)) {
1279         /*
1280          * Throw a runtime error if instanceof is called on a function that
1281          * has a non-object as its .prototype value.
1282          */
1283         str = js_DecompileValueGenerator(cx, -1, OBJECT_TO_JSVAL(obj), NULL);
1284         if (str) {
1285             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1286                                  JSMSG_BAD_PROTOTYPE, JS_GetStringBytes(str));
1287         }
1288         return JS_FALSE;
1289     }
1291     proto = JSVAL_TO_OBJECT(pval);
1292     if (!js_IsDelegate(cx, proto, v, bp))
1293         return JS_FALSE;
1295     if (!*bp && !JSVAL_IS_PRIMITIVE(v)) {
1296         /*
1297          * Extension for "brutal sharing" of standard class constructors: if
1298          * a script is compiled using a single, shared set of constructors, in
1299          * particular Function and RegExp, but executed many times using other
1300          * sets of standard constructors, then (/re/ instanceof RegExp), e.g.,
1301          * will be false.
1302          *
1303          * We extend instanceof in this case to look for a matching native or
1304          * script underlying the function object found in the 'constructor'
1305          * property of the object in question (which is JSVAL_TO_OBJECT(v)),
1306          * or found in the 'constructor' property of one of its prototypes.
1307          *
1308          * See also jsexn.c, where the *Error constructors are defined, each
1309          * with its own native function, to satisfy (e instanceof Error) even
1310          * when exceptions cross standard-class sharing boundaries.  Note that
1311          * Error.prototype may not lie on e's __proto__ chain in that case.
1312          */
1313         obj2 = JSVAL_TO_OBJECT(v);
1314         do {
1315             if (!OBJ_GET_PROPERTY(cx, obj2,
1316                                   (jsid)cx->runtime->atomState.constructorAtom,
1317                                   &cval)) {
1318                 return JS_FALSE;
1319             }
1321             if (JSVAL_IS_FUNCTION(cx, cval)) {
1322                 cfun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(cval));
1323                 ofun = (JSFunction *) JS_GetPrivate(cx, obj);
1324                 if (cfun->native == ofun->native &&
1325                     cfun->script == ofun->script) {
1326                     *bp = JS_TRUE;
1327                     break;
1328                 }
1329             }
1330         } while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL);
1331     }
1333     return JS_TRUE;
1336 #else  /* !JS_HAS_INSTANCEOF */
1338 #define fun_hasInstance NULL
1340 #endif /* !JS_HAS_INSTANCEOF */
1342 static uint32
1343 fun_mark(JSContext *cx, JSObject *obj, void *arg)
1345     JSFunction *fun;
1347     fun = (JSFunction *) JS_GetPrivate(cx, obj);
1348     if (fun) {
1349         if (fun->atom)
1350             GC_MARK_ATOM(cx, fun->atom, arg);
1351         if (fun->script)
1352             js_MarkScript(cx, fun->script, arg);
1353     }
1354     return 0;
1357 /*
1358  * Reserve two slots in all function objects for XPConnect.  Note that this
1359  * does not bloat every instance, only those on which reserved slots are set,
1360  * and those on which ad-hoc properties are defined.
1361  */
1362 JSClass js_FunctionClass = {
1363     js_Function_str,
1364     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2),
1365     JS_PropertyStub,  JS_PropertyStub,
1366     fun_getProperty,  JS_PropertyStub,
1367     JS_EnumerateStub, (JSResolveOp)fun_resolve,
1368     fun_convert,      fun_finalize,
1369     NULL,             NULL,
1370     NULL,             NULL,
1371     fun_xdrObject,    fun_hasInstance,
1372     fun_mark,         0
1373 };
1375 JSBool
1376 js_fun_toString(JSContext *cx, JSObject *obj, uint32 indent,
1377                 uintN argc, jsval *argv, jsval *rval)
1379     jsval fval;
1380     JSFunction *fun;
1381     JSString *str;
1383     if (!argv) {
1384         JS_ASSERT(JS_ObjectIsFunction(cx, obj));
1385     } else {
1386         fval = argv[-1];
1387         if (!JSVAL_IS_FUNCTION(cx, fval)) {
1388             /*
1389              * If we don't have a function to start off with, try converting
1390              * the object to a function.  If that doesn't work, complain.
1391              */
1392             if (JSVAL_IS_OBJECT(fval)) {
1393                 obj = JSVAL_TO_OBJECT(fval);
1394                 if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, JSTYPE_FUNCTION,
1395                                                      &fval)) {
1396                     return JS_FALSE;
1397                 }
1398             }
1399             if (!JSVAL_IS_FUNCTION(cx, fval)) {
1400                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1401                                      JSMSG_INCOMPATIBLE_PROTO,
1402                                      js_Function_str, js_toString_str,
1403                                      JS_GetTypeName(cx,
1404                                                     JS_TypeOfValue(cx, fval)));
1405                 return JS_FALSE;
1406             }
1407         }
1409         obj = JSVAL_TO_OBJECT(fval);
1410     }
1412     fun = (JSFunction *) JS_GetPrivate(cx, obj);
1413     if (!fun)
1414         return JS_TRUE;
1415     if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent))
1416         return JS_FALSE;
1417     str = JS_DecompileFunction(cx, fun, (uintN)indent);
1418     if (!str)
1419         return JS_FALSE;
1420     *rval = STRING_TO_JSVAL(str);
1421     return JS_TRUE;
1424 static JSBool
1425 fun_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1427     return js_fun_toString(cx, obj, 0, argc, argv, rval);
1430 #if JS_HAS_TOSOURCE
1431 static JSBool
1432 fun_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1434     return js_fun_toString(cx, obj, JS_DONT_PRETTY_PRINT, argc, argv, rval);
1436 #endif
1438 static const char js_call_str[] = "call";
1440 #if JS_HAS_CALL_FUNCTION
1441 static JSBool
1442 fun_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1444     jsval fval, *sp, *oldsp;
1445     void *mark;
1446     uintN i;
1447     JSStackFrame *fp;
1448     JSBool ok;
1450     if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1]))
1451         return JS_FALSE;
1452     fval = argv[-1];
1454     if (!JSVAL_IS_FUNCTION(cx, fval)) {
1455         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1456                              JSMSG_INCOMPATIBLE_PROTO,
1457                              js_Function_str, js_call_str,
1458                              JS_GetStringBytes(JS_ValueToString(cx, fval)));
1459         return JS_FALSE;
1460     }
1462     if (argc == 0) {
1463         /* Call fun with its parent as the 'this' parameter if no args. */
1464         obj = OBJ_GET_PARENT(cx, obj);
1465     } else {
1466         /* Otherwise convert the first arg to 'this' and skip over it. */
1467         if (!js_ValueToObject(cx, argv[0], &obj))
1468             return JS_FALSE;
1469         argc--;
1470         argv++;
1471     }
1473     /* Allocate stack space for fval, obj, and the args. */
1474     sp = js_AllocStack(cx, 2 + argc, &mark);
1475     if (!sp)
1476         return JS_FALSE;
1478     /* Push fval, obj, and the args. */
1479     *sp++ = fval;
1480     *sp++ = OBJECT_TO_JSVAL(obj);
1481     for (i = 0; i < argc; i++)
1482         *sp++ = argv[i];
1484     /* Lift current frame to include the args and do the call. */
1485     fp = cx->fp;
1486     oldsp = fp->sp;
1487     fp->sp = sp;
1488     ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL | JSINVOKE_SKIP_CALLER);
1490     /* Store rval and pop stack back to our frame's sp. */
1491     *rval = fp->sp[-1];
1492     fp->sp = oldsp;
1493     js_FreeStack(cx, mark);
1494     return ok;
1496 #endif /* JS_HAS_CALL_FUNCTION */
1498 #if JS_HAS_APPLY_FUNCTION
1499 static JSBool
1500 fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1502     jsval fval, *sp, *oldsp;
1503     JSObject *aobj;
1504     jsuint length;
1505     void *mark;
1506     uintN i;
1507     JSBool ok;
1508     JSStackFrame *fp;
1510     if (argc == 0) {
1511         /* Will get globalObject as 'this' and no other agurments. */
1512         return fun_call(cx, obj, argc, argv, rval);
1513     }
1515     if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1]))
1516         return JS_FALSE;
1517     fval = argv[-1];
1519     if (!JSVAL_IS_FUNCTION(cx, fval)) {
1520         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1521                              JSMSG_INCOMPATIBLE_PROTO,
1522                              js_Function_str, "apply",
1523                              JS_GetStringBytes(JS_ValueToString(cx, fval)));
1524         return JS_FALSE;
1525     }
1527     /* Quell GCC overwarnings. */
1528     aobj = NULL;
1529     length = 0;
1531     if (argc >= 2) {
1532         /* If the 2nd arg is null or void, call the function with 0 args. */
1533         if (JSVAL_IS_NULL(argv[1]) || JSVAL_IS_VOID(argv[1])) {
1534             argc = 0;
1535         } else {
1536             /* The second arg must be an array (or arguments object). */
1537             if (JSVAL_IS_PRIMITIVE(argv[1]) ||
1538                 (aobj = JSVAL_TO_OBJECT(argv[1]),
1539                 OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass &&
1540                 OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass))
1541             {
1542                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1543                                      JSMSG_BAD_APPLY_ARGS);
1544                 return JS_FALSE;
1545             }
1546             if (!js_GetLengthProperty(cx, aobj, &length))
1547                 return JS_FALSE;
1548         }
1549     }
1551     /* Convert the first arg to 'this' and skip over it. */
1552     if (!js_ValueToObject(cx, argv[0], &obj))
1553         return JS_FALSE;
1555     /* Allocate stack space for fval, obj, and the args. */
1556     argc = (uintN)JS_MIN(length, ARGC_LIMIT - 1);
1557     sp = js_AllocStack(cx, 2 + argc, &mark);
1558     if (!sp)
1559         return JS_FALSE;
1561     /* Push fval, obj, and aobj's elements as args. */
1562     *sp++ = fval;
1563     *sp++ = OBJECT_TO_JSVAL(obj);
1564     for (i = 0; i < argc; i++) {
1565         ok = JS_GetElement(cx, aobj, (jsint)i, sp);
1566         if (!ok)
1567             goto out;
1568         sp++;
1569     }
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 out:
1581     js_FreeStack(cx, mark);
1582     return ok;
1584 #endif /* JS_HAS_APPLY_FUNCTION */
1586 static JSFunctionSpec function_methods[] = {
1587 #if JS_HAS_TOSOURCE
1588     {js_toSource_str,   fun_toSource,   0,0,0},
1589 #endif
1590     {js_toString_str,   fun_toString,   1,0,0},
1591 #if JS_HAS_APPLY_FUNCTION
1592     {"apply",           fun_apply,      2,0,0},
1593 #endif
1594 #if JS_HAS_CALL_FUNCTION
1595     {js_call_str,       fun_call,       1,0,0},
1596 #endif
1597     {0,0,0,0,0}
1598 };
1600 JSBool
1601 js_IsIdentifier(JSString *str)
1603     size_t n;
1604     jschar *s, c;
1606     n = JSSTRING_LENGTH(str);
1607     if (n == 0)
1608         return JS_FALSE;
1609     s = JSSTRING_CHARS(str);
1610     c = *s;
1611     if (!JS_ISIDENT_START(c))
1612         return JS_FALSE;
1613     for (n--; n != 0; n--) {
1614         c = *++s;
1615         if (!JS_ISIDENT(c))
1616             return JS_FALSE;
1617     }
1618     return JS_TRUE;
1621 static JSBool
1622 Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1624     JSStackFrame *fp, *caller;
1625     JSFunction *fun;
1626     JSObject *parent;
1627     uintN i, n, lineno, dupflag;
1628     JSAtom *atom;
1629     const char *filename;
1630     JSObject *obj2;
1631     JSScopeProperty *sprop;
1632     JSString *str, *arg;
1633     void *mark;
1634     JSTokenStream *ts;
1635     JSPrincipals *principals;
1636     jschar *collected_args, *cp;
1637     size_t arg_length, args_length;
1638     JSTokenType tt;
1639     JSBool ok;
1641     fp = cx->fp;
1642     if (fp && !(fp->flags & JSFRAME_CONSTRUCTING)) {
1643         obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL);
1644         if (!obj)
1645             return JS_FALSE;
1646         *rval = OBJECT_TO_JSVAL(obj);
1647     }
1648     fun = (JSFunction *) JS_GetPrivate(cx, obj);
1649     if (fun)
1650         return JS_TRUE;
1652 #if JS_HAS_CALL_OBJECT
1653     /*
1654      * NB: (new Function) is not lexically closed by its caller, it's just an
1655      * anonymous function in the top-level scope that its constructor inhabits.
1656      * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
1657      * and so would a call to f from another top-level's script or function.
1658      *
1659      * In older versions, before call objects, a new Function was adopted by
1660      * its running context's globalObject, which might be different from the
1661      * top-level reachable from scopeChain (in HTML frames, e.g.).
1662      */
1663     parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]));
1664 #else
1665     /* Set up for dynamic parenting (see js_Invoke in jsinterp.c). */
1666     parent = NULL;
1667 #endif
1669     fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA, parent,
1670                          JSVERSION_IS_ECMA(cx->version)
1671                          ? cx->runtime->atomState.anonymousAtom
1672                          : NULL);
1674     if (!fun)
1675         return JS_FALSE;
1677     /*
1678      * Function is static and not called directly by other functions in this
1679      * file, therefore it is callable only as a native function by js_Invoke.
1680      * Find the scripted caller, possibly skipping other native frames such as
1681      * are built for Function.prototype.call or .apply activations that invoke
1682      * Function indirectly from a script.
1683      */
1684     JS_ASSERT(!fp->script && fp->fun && fp->fun->native == Function);
1685     caller = JS_GetScriptedCaller(cx, fp);
1686     if (caller) {
1687         filename = caller->script->filename;
1688         lineno = js_PCToLineNumber(cx, caller->script, caller->pc);
1689         principals = JS_EvalFramePrincipals(cx, fp, caller);
1690     } else {
1691         filename = NULL;
1692         lineno = 0;
1693         principals = NULL;
1694     }
1696     n = argc ? argc - 1 : 0;
1697     if (n > 0) {
1698         /*
1699          * Collect the function-argument arguments into one string, separated
1700          * by commas, then make a tokenstream from that string, and scan it to
1701          * get the arguments.  We need to throw the full scanner at the
1702          * problem, because the argument string can legitimately contain
1703          * comments and linefeeds.  XXX It might be better to concatenate
1704          * everything up into a function definition and pass it to the
1705          * compiler, but doing it this way is less of a delta from the old
1706          * code.  See ECMA 15.3.2.1.
1707          */
1708         args_length = 0;
1709         for (i = 0; i < n; i++) {
1710             /* Collect the lengths for all the function-argument arguments. */
1711             arg = js_ValueToString(cx, argv[i]);
1712             if (!arg)
1713                 return JS_FALSE;
1714             argv[i] = STRING_TO_JSVAL(arg);
1715             args_length += JSSTRING_LENGTH(arg);
1716         }
1717         /* Add 1 for each joining comma. */
1718         args_length += n - 1;
1720         /*
1721          * Allocate a string to hold the concatenated arguments, including room
1722          * for a terminating 0.  Mark cx->tempPool for later release, to free
1723          * collected_args and its tokenstream in one swoop.
1724          */
1725         mark = JS_ARENA_MARK(&cx->tempPool);
1726         JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool,
1727                                (args_length+1) * sizeof(jschar));
1728         if (!cp)
1729             return JS_FALSE;
1730         collected_args = cp;
1732         /*
1733          * Concatenate the arguments into the new string, separated by commas.
1734          */
1735         for (i = 0; i < n; i++) {
1736             arg = JSVAL_TO_STRING(argv[i]);
1737             arg_length = JSSTRING_LENGTH(arg);
1738             (void) js_strncpy(cp, JSSTRING_CHARS(arg), arg_length);
1739             cp += arg_length;
1741             /* Add separating comma or terminating 0. */
1742             *cp++ = (i + 1 < n) ? ',' : 0;
1743         }
1745         /*
1746          * Make a tokenstream (allocated from cx->tempPool) that reads from
1747          * the given string.
1748          */
1749         ts = js_NewTokenStream(cx, collected_args, args_length, filename,
1750                                lineno, principals);
1751         if (!ts) {
1752             JS_ARENA_RELEASE(&cx->tempPool, mark);
1753             return JS_FALSE;
1754         }
1756         /* The argument string may be empty or contain no tokens. */
1757         tt = js_GetToken(cx, ts);
1758         if (tt != TOK_EOF) {
1759             for (;;) {
1760                 /*
1761                  * Check that it's a name.  This also implicitly guards against
1762                  * TOK_ERROR, which was already reported.
1763                  */
1764                 if (tt != TOK_NAME)
1765                     goto bad_formal;
1767                 /*
1768                  * Get the atom corresponding to the name from the tokenstream;
1769                  * we're assured at this point that it's a valid identifier.
1770                  */
1771                 atom = CURRENT_TOKEN(ts).t_atom;
1772                 if (!js_LookupProperty(cx, obj, (jsid)atom, &obj2,
1773                                        (JSProperty **)&sprop)) {
1774                     goto bad_formal;
1775                 }
1776                 dupflag = 0;
1777                 if (sprop) {
1778                     ok = JS_TRUE;
1779                     if (obj2 == obj) {
1780                         const char *name = js_AtomToPrintableString(cx, atom);
1782                         /*
1783                          * A duplicate parameter name. We force a duplicate
1784                          * node on the SCOPE_LAST_PROP(scope) list with the
1785                          * same id, distinguished by the SPROP_IS_DUPLICATE
1786                          * flag, and not mapped by an entry in scope.
1787                          */
1788                         JS_ASSERT(sprop->getter == js_GetArgument);
1789                         ok = name &&
1790                              js_ReportCompileErrorNumber(cx, ts, NULL,
1791                                                          JSREPORT_WARNING |
1792                                                          JSREPORT_STRICT,
1793                                                          JSMSG_DUPLICATE_FORMAL,
1794                                                          name);
1796                         dupflag = SPROP_IS_DUPLICATE;
1797                     }
1798                     OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);
1799                     if (!ok)
1800                         goto bad_formal;
1801                     sprop = NULL;
1802                 }
1803                 if (!js_AddNativeProperty(cx, fun->object, (jsid)atom,
1804                                           js_GetArgument, js_SetArgument,
1805                                           SPROP_INVALID_SLOT,
1806                                           JSPROP_ENUMERATE | JSPROP_PERMANENT |
1807                                           JSPROP_SHARED,
1808                                           SPROP_HAS_SHORTID | dupflag,
1809                                           fun->nargs)) {
1810                     goto bad_formal;
1811                 }
1812                 fun->nargs++;
1814                 /*
1815                  * Get the next token.  Stop on end of stream.  Otherwise
1816                  * insist on a comma, get another name, and iterate.
1817                  */
1818                 tt = js_GetToken(cx, ts);
1819                 if (tt == TOK_EOF)
1820                     break;
1821                 if (tt != TOK_COMMA)
1822                     goto bad_formal;
1823                 tt = js_GetToken(cx, ts);
1824             }
1825         }
1827         /* Clean up. */
1828         ok = js_CloseTokenStream(cx, ts);
1829         JS_ARENA_RELEASE(&cx->tempPool, mark);
1830         if (!ok)
1831             return JS_FALSE;
1832     }
1834     if (argc) {
1835         str = js_ValueToString(cx, argv[argc-1]);
1836     } else {
1837         /* Can't use cx->runtime->emptyString because we're called too early. */
1838         str = js_NewStringCopyZ(cx, js_empty_ucstr, 0);
1839     }
1840     if (!str)
1841         return JS_FALSE;
1842     if (argv) {
1843         /* Use the last arg (or this if argc == 0) as a local GC root. */
1844         argv[(intN)(argc-1)] = STRING_TO_JSVAL(str);
1845     }
1847     mark = JS_ARENA_MARK(&cx->tempPool);
1848     ts = js_NewTokenStream(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
1849                            filename, lineno, principals);
1850     if (!ts) {
1851         ok = JS_FALSE;
1852     } else {
1853         ok = js_CompileFunctionBody(cx, ts, fun) &&
1854              js_CloseTokenStream(cx, ts);
1855     }
1856     JS_ARENA_RELEASE(&cx->tempPool, mark);
1857     return ok;
1859 bad_formal:
1860     /*
1861      * Report "malformed formal parameter" iff no illegal char or similar
1862      * scanner error was already reported.
1863      */
1864     if (!(ts->flags & TSF_ERROR))
1865         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_FORMAL);
1867     /*
1868      * Clean up the arguments string and tokenstream if we failed to parse
1869      * the arguments.
1870      */
1871     (void)js_CloseTokenStream(cx, ts);
1872     JS_ARENA_RELEASE(&cx->tempPool, mark);
1873     return JS_FALSE;
1876 JSObject *
1877 js_InitFunctionClass(JSContext *cx, JSObject *obj)
1879     JSObject *proto;
1880     JSAtom *atom;
1881     JSFunction *fun;
1883     proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
1884                          function_props, function_methods, NULL, NULL);
1885     if (!proto)
1886         return NULL;
1887     atom = js_Atomize(cx, js_FunctionClass.name, strlen(js_FunctionClass.name),
1888                       0);
1889     if (!atom)
1890         goto bad;
1891     fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, NULL);
1892     if (!fun)
1893         goto bad;
1894     fun->script = js_NewScript(cx, 0, 0, 0);
1895     if (!fun->script)
1896         goto bad;
1897     return proto;
1899 bad:
1900     cx->newborn[GCX_OBJECT] = NULL;
1901     return NULL;
1904 #if JS_HAS_CALL_OBJECT
1905 JSObject *
1906 js_InitCallClass(JSContext *cx, JSObject *obj)
1908     return JS_InitClass(cx, obj, NULL, &js_CallClass, NULL, 0,
1909                         call_props, NULL, NULL, NULL);
1911 #endif
1913 JSFunction *
1914 js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
1915                uintN flags, JSObject *parent, JSAtom *atom)
1917     JSFunction *fun;
1919     /* Allocate a function struct. */
1920     fun = (JSFunction *) JS_malloc(cx, sizeof *fun);
1921     if (!fun)
1922         return NULL;
1924     /* If funobj is null, allocate an object for it. */
1925     if (funobj) {
1926         OBJ_SET_PARENT(cx, funobj, parent);
1927     } else {
1928         funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent);
1929         if (!funobj) {
1930             JS_free(cx, fun);
1931             return NULL;
1932         }
1933     }
1935     /* Initialize all function members. */
1936     fun->nrefs = 0;
1937     fun->object = NULL;
1938     fun->native = native;
1939     fun->script = NULL;
1940     fun->nargs = nargs;
1941     fun->extra = 0;
1942     fun->nvars = 0;
1943     fun->flags = flags & JSFUN_FLAGS_MASK;
1944     fun->spare = 0;
1945     fun->atom = atom;
1946     fun->clasp = NULL;
1948     /* Link fun to funobj and vice versa. */
1949     if (!js_LinkFunctionObject(cx, fun, funobj)) {
1950         cx->newborn[GCX_OBJECT] = NULL;
1951         JS_free(cx, fun);
1952         return NULL;
1953     }
1954     return fun;
1957 JSObject *
1958 js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
1960     JSObject *newfunobj;
1961     JSFunction *fun;
1963     JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass);
1964     newfunobj = js_NewObject(cx, &js_FunctionClass, funobj, parent);
1965     if (!newfunobj)
1966         return NULL;
1967     fun = (JSFunction *) JS_GetPrivate(cx, funobj);
1968     if (!js_LinkFunctionObject(cx, fun, newfunobj)) {
1969         cx->newborn[GCX_OBJECT] = NULL;
1970         return NULL;
1971     }
1972     return newfunobj;
1975 JSBool
1976 js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *funobj)
1978     if (!fun->object)
1979         fun->object = funobj;
1980     if (!JS_SetPrivate(cx, funobj, fun))
1981         return JS_FALSE;
1982     JS_ATOMIC_INCREMENT(&fun->nrefs);
1983     return JS_TRUE;
1986 JSFunction *
1987 js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
1988                   uintN nargs, uintN attrs)
1990     JSFunction *fun;
1992     fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom);
1993     if (!fun)
1994         return NULL;
1995     if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, OBJECT_TO_JSVAL(fun->object),
1996                              NULL, NULL, attrs & ~JSFUN_FLAGS_MASK, NULL)) {
1997         return NULL;
1998     }
1999     return fun;
2002 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
2003 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
2004 #endif
2006 JSFunction *
2007 js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags)
2009     jsval v;
2010     JSObject *obj;
2012     v = *vp;
2013     obj = NULL;
2014     if (JSVAL_IS_OBJECT(v)) {
2015         obj = JSVAL_TO_OBJECT(v);
2016         if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) {
2017             if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v))
2018                 return NULL;
2019             obj = JSVAL_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL;
2020         }
2021     }
2022     if (!obj) {
2023         js_ReportIsNotFunction(cx, vp, flags);
2024         return NULL;
2025     }
2026     return (JSFunction *) JS_GetPrivate(cx, obj);
2029 void
2030 js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
2032     JSType type;
2033     JSString *fallback;
2034     JSString *str;
2036     /*
2037      * We provide the typename as the fallback to handle the case when
2038      * valueOf is not a function, which prevents ValueToString from being
2039      * called as the default case inside js_DecompileValueGenerator (and
2040      * so recursing back to here).
2041      */
2042     type = JS_TypeOfValue(cx, *vp);
2043     fallback = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]);
2044     str = js_DecompileValueGenerator(cx,
2045                                      (flags & JSV2F_SEARCH_STACK)
2046                                      ? JSDVG_SEARCH_STACK
2047                                      : cx->fp
2048                                      ? vp - cx->fp->sp
2049                                      : JSDVG_IGNORE_STACK,
2050                                      *vp,
2051                                      fallback);
2052     if (str) {
2053         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2054                              (uintN)((flags & JSV2F_CONSTRUCT)
2055                                      ? JSMSG_NOT_CONSTRUCTOR
2056                                      : JSMSG_NOT_FUNCTION),
2057                              JS_GetStringBytes(str));
2058     }