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