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