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