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