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