/[jscoverage]/trunk/js/jsfun.cpp
ViewVC logotype

Contents of /trunk/js/jsfun.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 399 - (show annotations)
Tue Dec 9 03:37:47 2008 UTC (10 years, 11 months ago) by siliconforks
File size: 83619 byte(s)
Use SpiderMonkey from Firefox 3.1b2.

1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
3 *
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
24 *
25 * Contributor(s):
26 *
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
38 *
39 * ***** END LICENSE BLOCK ***** */
40
41 /*
42 * JS function support.
43 */
44 #include "jsstddef.h"
45 #include <string.h>
46 #include "jstypes.h"
47 #include "jsbit.h"
48 #include "jsutil.h" /* Added by JSIFY */
49 #include "jsapi.h"
50 #include "jsarray.h"
51 #include "jsatom.h"
52 #include "jsbuiltins.h"
53 #include "jscntxt.h"
54 #include "jsversion.h"
55 #include "jsdbgapi.h"
56 #include "jsemit.h"
57 #include "jsfun.h"
58 #include "jsgc.h"
59 #include "jsinterp.h"
60 #include "jslock.h"
61 #include "jsnum.h"
62 #include "jsobj.h"
63 #include "jsopcode.h"
64 #include "jsparse.h"
65 #include "jsscan.h"
66 #include "jsscope.h"
67 #include "jsscript.h"
68 #include "jsstr.h"
69 #include "jsexn.h"
70 #include "jsstaticcheck.h"
71
72 #if JS_HAS_GENERATORS
73 # include "jsiter.h"
74 #endif
75
76 #if JS_HAS_XDR
77 # include "jsxdrapi.h"
78 #endif
79
80 /* Generic function/call/arguments tinyids -- also reflected bit numbers. */
81 enum {
82 CALL_ARGUMENTS = -1, /* predefined arguments local variable */
83 ARGS_LENGTH = -2, /* number of actual args, arity if inactive */
84 ARGS_CALLEE = -3, /* reference from arguments to active funobj */
85 FUN_ARITY = -4, /* number of formal parameters; desired argc */
86 FUN_NAME = -5, /* function name, "" if anonymous */
87 FUN_CALLER = -6 /* Function.prototype.caller, backward compat */
88 };
89
90 #if JSFRAME_OVERRIDE_BITS < 8
91 # error "not enough override bits in JSStackFrame.flags!"
92 #endif
93
94 #define TEST_OVERRIDE_BIT(fp, tinyid) \
95 ((fp)->flags & JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1)))
96
97 #define SET_OVERRIDE_BIT(fp, tinyid) \
98 ((fp)->flags |= JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1)))
99
100 JSBool
101 js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp)
102 {
103 JSObject *argsobj;
104
105 if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
106 JS_ASSERT(fp->callobj);
107 return OBJ_GET_PROPERTY(cx, fp->callobj,
108 ATOM_TO_JSID(cx->runtime->atomState
109 .argumentsAtom),
110 vp);
111 }
112 argsobj = js_GetArgsObject(cx, fp);
113 if (!argsobj)
114 return JS_FALSE;
115 *vp = OBJECT_TO_JSVAL(argsobj);
116 return JS_TRUE;
117 }
118
119 static JSBool
120 MarkArgDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
121 {
122 JSObject *argsobj;
123 jsval bmapval, bmapint;
124 size_t nbits, nbytes;
125 jsbitmap *bitmap;
126
127 argsobj = fp->argsobj;
128 (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
129 nbits = fp->argc;
130 JS_ASSERT(slot < nbits);
131 if (JSVAL_IS_VOID(bmapval)) {
132 if (nbits <= JSVAL_INT_BITS) {
133 bmapint = 0;
134 bitmap = (jsbitmap *) &bmapint;
135 } else {
136 nbytes = JS_HOWMANY(nbits, JS_BITS_PER_WORD) * sizeof(jsbitmap);
137 bitmap = (jsbitmap *) JS_malloc(cx, nbytes);
138 if (!bitmap)
139 return JS_FALSE;
140 memset(bitmap, 0, nbytes);
141 bmapval = PRIVATE_TO_JSVAL(bitmap);
142 JS_SetReservedSlot(cx, argsobj, 0, bmapval);
143 }
144 } else {
145 if (nbits <= JSVAL_INT_BITS) {
146 bmapint = JSVAL_TO_INT(bmapval);
147 bitmap = (jsbitmap *) &bmapint;
148 } else {
149 bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval);
150 }
151 }
152 JS_SET_BIT(bitmap, slot);
153 if (bitmap == (jsbitmap *) &bmapint) {
154 bmapval = INT_TO_JSVAL(bmapint);
155 JS_SetReservedSlot(cx, argsobj, 0, bmapval);
156 }
157 return JS_TRUE;
158 }
159
160 /* NB: Infallible predicate, false does not mean error/exception. */
161 static JSBool
162 ArgWasDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
163 {
164 JSObject *argsobj;
165 jsval bmapval, bmapint;
166 jsbitmap *bitmap;
167
168 argsobj = fp->argsobj;
169 (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
170 if (JSVAL_IS_VOID(bmapval))
171 return JS_FALSE;
172 if (fp->argc <= JSVAL_INT_BITS) {
173 bmapint = JSVAL_TO_INT(bmapval);
174 bitmap = (jsbitmap *) &bmapint;
175 } else {
176 bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval);
177 }
178 return JS_TEST_BIT(bitmap, slot) != 0;
179 }
180
181 JSBool
182 js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp)
183 {
184 jsval val;
185 JSObject *obj;
186 uintN slot;
187
188 if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
189 JS_ASSERT(fp->callobj);
190 if (!OBJ_GET_PROPERTY(cx, fp->callobj,
191 ATOM_TO_JSID(cx->runtime->atomState
192 .argumentsAtom),
193 &val)) {
194 return JS_FALSE;
195 }
196 if (JSVAL_IS_PRIMITIVE(val)) {
197 obj = js_ValueToNonNullObject(cx, val);
198 if (!obj)
199 return JS_FALSE;
200 } else {
201 obj = JSVAL_TO_OBJECT(val);
202 }
203 return OBJ_GET_PROPERTY(cx, obj, id, vp);
204 }
205
206 *vp = JSVAL_VOID;
207 if (JSID_IS_INT(id)) {
208 slot = (uintN) JSID_TO_INT(id);
209 if (slot < fp->argc) {
210 if (fp->argsobj && ArgWasDeleted(cx, fp, slot))
211 return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
212 *vp = fp->argv[slot];
213 } else {
214 /*
215 * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share
216 * storage between the formal parameter and arguments[k] for all
217 * fp->argc <= k && k < fp->fun->nargs. For example, in
218 *
219 * function f(x) { x = 42; return arguments[0]; }
220 * f();
221 *
222 * the call to f should return undefined, not 42. If fp->argsobj
223 * is null at this point, as it would be in the example, return
224 * undefined in *vp.
225 */
226 if (fp->argsobj)
227 return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
228 }
229 } else {
230 if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
231 if (fp->argsobj && TEST_OVERRIDE_BIT(fp, ARGS_LENGTH))
232 return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
233 *vp = INT_TO_JSVAL((jsint) fp->argc);
234 }
235 }
236 return JS_TRUE;
237 }
238
239 JSObject *
240 js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
241 {
242 JSObject *argsobj, *global, *parent;
243
244 /*
245 * We must be in a function activation; the function must be lightweight
246 * or else fp must have a variable object.
247 */
248 JS_ASSERT(fp->fun && (!(fp->fun->flags & JSFUN_HEAVYWEIGHT) || fp->varobj));
249
250 /* Skip eval and debugger frames. */
251 while (fp->flags & JSFRAME_SPECIAL)
252 fp = fp->down;
253
254 /* Create an arguments object for fp only if it lacks one. */
255 argsobj = fp->argsobj;
256 if (argsobj)
257 return argsobj;
258
259 /* Link the new object to fp so it can get actual argument values. */
260 argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL, 0);
261 if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) {
262 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
263 return NULL;
264 }
265
266 /*
267 * Give arguments an intrinsic scope chain link to fp's global object.
268 * Since the arguments object lacks a prototype because js_ArgumentsClass
269 * is not initialized, js_NewObject won't assign a default parent to it.
270 *
271 * Therefore if arguments is used as the head of an eval scope chain (via
272 * a direct or indirect call to eval(program, arguments)), any reference
273 * to a standard class object in the program will fail to resolve due to
274 * js_GetClassPrototype not being able to find a global object containing
275 * the standard prototype by starting from arguments and following parent.
276 */
277 global = fp->scopeChain;
278 while ((parent = OBJ_GET_PARENT(cx, global)) != NULL)
279 global = parent;
280 STOBJ_SET_PARENT(argsobj, global);
281 fp->argsobj = argsobj;
282 return argsobj;
283 }
284
285 static JSBool
286 args_enumerate(JSContext *cx, JSObject *obj);
287
288 JS_FRIEND_API(JSBool)
289 js_PutArgsObject(JSContext *cx, JSStackFrame *fp)
290 {
291 JSObject *argsobj;
292 jsval bmapval, rval;
293 JSBool ok;
294 JSRuntime *rt;
295
296 /*
297 * Reuse args_enumerate here to reflect fp's actual arguments as indexed
298 * elements of argsobj. Do this first, before clearing and freeing the
299 * deleted argument slot bitmap, because args_enumerate depends on that.
300 */
301 argsobj = fp->argsobj;
302 ok = args_enumerate(cx, argsobj);
303
304 /*
305 * Now clear the deleted argument number bitmap slot and free the bitmap,
306 * if one was actually created due to 'delete arguments[0]' or similar.
307 */
308 (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
309 if (!JSVAL_IS_VOID(bmapval)) {
310 JS_SetReservedSlot(cx, argsobj, 0, JSVAL_VOID);
311 if (fp->argc > JSVAL_INT_BITS)
312 JS_free(cx, JSVAL_TO_PRIVATE(bmapval));
313 }
314
315 /*
316 * Now get the prototype properties so we snapshot fp->fun and fp->argc
317 * before fp goes away.
318 */
319 rt = cx->runtime;
320 ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom),
321 &rval);
322 ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom),
323 &rval);
324 ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom),
325 &rval);
326 ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom),
327 &rval);
328
329 /*
330 * Clear the private pointer to fp, which is about to go away (js_Invoke).
331 * Do this last because the args_enumerate and js_GetProperty calls above
332 * need to follow the private slot to find fp.
333 */
334 ok &= JS_SetPrivate(cx, argsobj, NULL);
335 fp->argsobj = NULL;
336 return ok;
337 }
338
339 static JSBool
340 args_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
341 {
342 jsint slot;
343 JSStackFrame *fp;
344
345 if (!JSVAL_IS_INT(id))
346 return JS_TRUE;
347 fp = (JSStackFrame *)
348 JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
349 if (!fp)
350 return JS_TRUE;
351 JS_ASSERT(fp->argsobj);
352
353 slot = JSVAL_TO_INT(id);
354 switch (slot) {
355 case ARGS_CALLEE:
356 case ARGS_LENGTH:
357 SET_OVERRIDE_BIT(fp, slot);
358 break;
359
360 default:
361 if ((uintN)slot < fp->argc && !MarkArgDeleted(cx, fp, slot))
362 return JS_FALSE;
363 break;
364 }
365 return JS_TRUE;
366 }
367
368 static JSBool
369 args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
370 {
371 jsint slot;
372 JSStackFrame *fp;
373
374 if (!JSVAL_IS_INT(id))
375 return JS_TRUE;
376 fp = (JSStackFrame *)
377 JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
378 if (!fp)
379 return JS_TRUE;
380 JS_ASSERT(fp->argsobj);
381
382 slot = JSVAL_TO_INT(id);
383 switch (slot) {
384 case ARGS_CALLEE:
385 if (!TEST_OVERRIDE_BIT(fp, slot))
386 *vp = OBJECT_TO_JSVAL(fp->callee);
387 break;
388
389 case ARGS_LENGTH:
390 if (!TEST_OVERRIDE_BIT(fp, slot))
391 *vp = INT_TO_JSVAL((jsint)fp->argc);
392 break;
393
394 default:
395 if ((uintN)slot < fp->argc && !ArgWasDeleted(cx, fp, slot))
396 *vp = fp->argv[slot];
397 break;
398 }
399 return JS_TRUE;
400 }
401
402 static JSBool
403 args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
404 {
405 JSStackFrame *fp;
406 jsint slot;
407
408 if (!JSVAL_IS_INT(id))
409 return JS_TRUE;
410 fp = (JSStackFrame *)
411 JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
412 if (!fp)
413 return JS_TRUE;
414 JS_ASSERT(fp->argsobj);
415
416 slot = JSVAL_TO_INT(id);
417 switch (slot) {
418 case ARGS_CALLEE:
419 case ARGS_LENGTH:
420 SET_OVERRIDE_BIT(fp, slot);
421 break;
422
423 default:
424 if (FUN_INTERPRETED(fp->fun) &&
425 (uintN)slot < fp->argc &&
426 !ArgWasDeleted(cx, fp, slot)) {
427 fp->argv[slot] = *vp;
428 }
429 break;
430 }
431 return JS_TRUE;
432 }
433
434 static JSBool
435 args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
436 JSObject **objp)
437 {
438 JSStackFrame *fp;
439 uintN slot;
440 JSString *str;
441 JSAtom *atom;
442 intN tinyid;
443 jsval value;
444
445 *objp = NULL;
446 fp = (JSStackFrame *)
447 JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
448 if (!fp)
449 return JS_TRUE;
450 JS_ASSERT(fp->argsobj);
451
452 if (JSVAL_IS_INT(id)) {
453 slot = JSVAL_TO_INT(id);
454 if (slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) {
455 /* XXX ECMA specs DontEnum, contrary to other array-like objects */
456 if (!js_DefineProperty(cx, obj, INT_JSVAL_TO_JSID(id),
457 fp->argv[slot],
458 args_getProperty, args_setProperty,
459 0, NULL)) {
460 return JS_FALSE;
461 }
462 *objp = obj;
463 }
464 } else {
465 str = JSVAL_TO_STRING(id);
466 atom = cx->runtime->atomState.lengthAtom;
467 if (str == ATOM_TO_STRING(atom)) {
468 tinyid = ARGS_LENGTH;
469 value = INT_TO_JSVAL(fp->argc);
470 } else {
471 atom = cx->runtime->atomState.calleeAtom;
472 if (str == ATOM_TO_STRING(atom)) {
473 tinyid = ARGS_CALLEE;
474 value = OBJECT_TO_JSVAL(fp->callee);
475 } else {
476 atom = NULL;
477
478 /* Quell GCC overwarnings. */
479 tinyid = 0;
480 value = JSVAL_NULL;
481 }
482 }
483
484 if (atom && !TEST_OVERRIDE_BIT(fp, tinyid)) {
485 if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value,
486 args_getProperty, args_setProperty, 0,
487 SPROP_HAS_SHORTID, tinyid, NULL)) {
488 return JS_FALSE;
489 }
490 *objp = obj;
491 }
492 }
493
494 return JS_TRUE;
495 }
496
497 static JSBool
498 args_enumerate(JSContext *cx, JSObject *obj)
499 {
500 JSStackFrame *fp;
501 JSObject *pobj;
502 JSProperty *prop;
503 uintN slot, argc;
504
505 fp = (JSStackFrame *)
506 JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
507 if (!fp)
508 return JS_TRUE;
509 JS_ASSERT(fp->argsobj);
510
511 /*
512 * Trigger reflection with value snapshot in args_resolve using a series
513 * of js_LookupProperty calls. We handle length, callee, and the indexed
514 * argument properties. We know that args_resolve covers all these cases
515 * and creates direct properties of obj, but that it may fail to resolve
516 * length or callee if overridden.
517 */
518 if (!js_LookupProperty(cx, obj,
519 ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
520 &pobj, &prop)) {
521 return JS_FALSE;
522 }
523 if (prop)
524 OBJ_DROP_PROPERTY(cx, pobj, prop);
525
526 if (!js_LookupProperty(cx, obj,
527 ATOM_TO_JSID(cx->runtime->atomState.calleeAtom),
528 &pobj, &prop)) {
529 return JS_FALSE;
530 }
531 if (prop)
532 OBJ_DROP_PROPERTY(cx, pobj, prop);
533
534 argc = fp->argc;
535 for (slot = 0; slot < argc; slot++) {
536 if (!js_LookupProperty(cx, obj, INT_TO_JSID((jsint)slot), &pobj, &prop))
537 return JS_FALSE;
538 if (prop)
539 OBJ_DROP_PROPERTY(cx, pobj, prop);
540 }
541 return JS_TRUE;
542 }
543
544 #if JS_HAS_GENERATORS
545 /*
546 * If a generator-iterator's arguments or call object escapes, it needs to
547 * mark its generator object.
548 */
549 static void
550 args_or_call_trace(JSTracer *trc, JSObject *obj)
551 {
552 JSStackFrame *fp;
553
554 fp = (JSStackFrame *) JS_GetPrivate(trc->context, obj);
555 if (fp && (fp->flags & JSFRAME_GENERATOR)) {
556 JS_CALL_OBJECT_TRACER(trc, FRAME_TO_GENERATOR(fp)->obj,
557 "FRAME_TO_GENERATOR(fp)->obj");
558 }
559 }
560 #else
561 # define args_or_call_trace NULL
562 #endif
563
564 /*
565 * The Arguments class is not initialized via JS_InitClass, and must not be,
566 * because its name is "Object". Per ECMA, that causes instances of it to
567 * delegate to the object named by Object.prototype. It also ensures that
568 * arguments.toString() returns "[object Object]".
569 *
570 * The JSClass functions below collaborate to lazily reflect and synchronize
571 * actual argument values, argument count, and callee function object stored
572 * in a JSStackFrame with their corresponding property values in the frame's
573 * arguments object.
574 */
575 JSClass js_ArgumentsClass = {
576 js_Object_str,
577 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) |
578 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
579 JS_PropertyStub, args_delProperty,
580 args_getProperty, args_setProperty,
581 args_enumerate, (JSResolveOp) args_resolve,
582 JS_ConvertStub, JS_FinalizeStub,
583 NULL, NULL,
584 NULL, NULL,
585 NULL, NULL,
586 JS_CLASS_TRACE(args_or_call_trace), NULL
587 };
588
589 #define JSSLOT_SCRIPTED_FUNCTION (JSSLOT_PRIVATE + 1)
590 #define JSSLOT_CALL_ARGUMENTS (JSSLOT_PRIVATE + 2)
591 #define CALL_CLASS_FIXED_RESERVED_SLOTS 2
592
593 JSObject *
594 js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent)
595 {
596 JSObject *callobj, *funobj;
597
598 /* Create a call object for fp only if it lacks one. */
599 JS_ASSERT(fp->fun);
600 callobj = fp->callobj;
601 if (callobj)
602 return callobj;
603
604 /* The default call parent is its function's parent (static link). */
605 if (!parent) {
606 funobj = fp->callee;
607 if (funobj)
608 parent = OBJ_GET_PARENT(cx, funobj);
609 }
610
611 /* Create the call object and link it to its stack frame. */
612 callobj = js_NewObject(cx, &js_CallClass, NULL, parent, 0);
613 if (!callobj)
614 return NULL;
615
616 JS_SetPrivate(cx, callobj, fp);
617 STOBJ_SET_SLOT(callobj, JSSLOT_SCRIPTED_FUNCTION,
618 OBJECT_TO_JSVAL(FUN_OBJECT(fp->fun)));
619 fp->callobj = callobj;
620
621 /* Make callobj be the scope chain and the variables object. */
622 JS_ASSERT(fp->scopeChain == parent);
623 fp->scopeChain = callobj;
624 fp->varobj = callobj;
625 return callobj;
626 }
627
628 JSFunction *
629 js_GetCallObjectFunction(JSObject *obj)
630 {
631 jsval v;
632
633 JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass);
634 v = STOBJ_GET_SLOT(obj, JSSLOT_SCRIPTED_FUNCTION);
635 if (JSVAL_IS_VOID(v)) {
636 /* Newborn or prototype object. */
637 return NULL;
638 }
639 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
640 return (JSFunction *) JSVAL_TO_OBJECT(v);
641 }
642
643 JS_FRIEND_API(JSBool)
644 js_PutCallObject(JSContext *cx, JSStackFrame *fp)
645 {
646 JSObject *callobj;
647 JSBool ok;
648 JSFunction *fun;
649 uintN n;
650 JSScope *scope;
651
652 /*
653 * Since for a call object all fixed slots happen to be taken, we can copy
654 * arguments and variables straight into JSObject.dslots.
655 */
656 JS_STATIC_ASSERT(JS_INITIAL_NSLOTS - JSSLOT_PRIVATE ==
657 1 + CALL_CLASS_FIXED_RESERVED_SLOTS);
658
659 callobj = fp->callobj;
660 if (!callobj)
661 return JS_TRUE;
662
663 /*
664 * Get the arguments object to snapshot fp's actual argument values.
665 */
666 ok = JS_TRUE;
667 if (fp->argsobj) {
668 if (!TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
669 STOBJ_SET_SLOT(callobj, JSSLOT_CALL_ARGUMENTS,
670 OBJECT_TO_JSVAL(fp->argsobj));
671 }
672 ok &= js_PutArgsObject(cx, fp);
673 }
674
675 fun = fp->fun;
676 JS_ASSERT(fun == js_GetCallObjectFunction(callobj));
677 n = JS_GET_LOCAL_NAME_COUNT(fun);
678 if (n != 0) {
679 JS_LOCK_OBJ(cx, callobj);
680 n += JS_INITIAL_NSLOTS;
681 if (n > STOBJ_NSLOTS(callobj))
682 ok &= js_ReallocSlots(cx, callobj, n, JS_TRUE);
683 scope = OBJ_SCOPE(callobj);
684 if (ok) {
685 memcpy(callobj->dslots, fp->argv, fun->nargs * sizeof(jsval));
686 memcpy(callobj->dslots + fun->nargs, fp->slots,
687 fun->u.i.nvars * sizeof(jsval));
688 if (scope->object == callobj && n > scope->map.freeslot)
689 scope->map.freeslot = n;
690 }
691 JS_UNLOCK_SCOPE(cx, scope);
692 }
693
694 /*
695 * Clear the private pointer to fp, which is about to go away (js_Invoke).
696 * Do this last because js_GetProperty calls above need to follow the
697 * private slot to find fp.
698 */
699 JS_SetPrivate(cx, callobj, NULL);
700 fp->callobj = NULL;
701 return ok;
702 }
703
704 static JSBool
705 call_enumerate(JSContext *cx, JSObject *obj)
706 {
707 JSFunction *fun;
708 uintN n, i;
709 void *mark;
710 jsuword *names;
711 JSBool ok;
712 JSAtom *name;
713 JSObject *pobj;
714 JSProperty *prop;
715
716 fun = js_GetCallObjectFunction(obj);
717 n = JS_GET_LOCAL_NAME_COUNT(fun);
718 if (n == 0)
719 return JS_TRUE;
720
721 mark = JS_ARENA_MARK(&cx->tempPool);
722
723 MUST_FLOW_THROUGH("out");
724 names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
725 if (!names) {
726 ok = JS_FALSE;
727 goto out;
728 }
729
730 for (i = 0; i != n; ++i) {
731 name = JS_LOCAL_NAME_TO_ATOM(names[i]);
732 if (!name)
733 continue;
734
735 /*
736 * Trigger reflection by looking up the name of the argument or
737 * variable.
738 */
739 ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(name), &pobj, &prop);
740 if (!ok)
741 goto out;
742
743 /*
744 * At this point the call object always has a property corresponding
745 * to the local name because call_resolve creates the property using
746 * JSPROP_PERMANENT.
747 */
748 JS_ASSERT(prop && pobj == obj);
749 OBJ_DROP_PROPERTY(cx, pobj, prop);
750 }
751 ok = JS_TRUE;
752
753 out:
754 JS_ARENA_RELEASE(&cx->tempPool, mark);
755 return ok;
756 }
757
758 typedef enum JSCallPropertyKind {
759 JSCPK_ARGUMENTS,
760 JSCPK_ARG,
761 JSCPK_VAR
762 } JSCallPropertyKind;
763
764 static JSBool
765 CallPropertyOp(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
766 JSCallPropertyKind kind, JSBool setter)
767 {
768 JSFunction *fun;
769 JSStackFrame *fp;
770 uintN i;
771 jsval *array;
772
773 if (STOBJ_GET_CLASS(obj) != &js_CallClass)
774 return JS_TRUE;
775
776 fun = js_GetCallObjectFunction(obj);
777 fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
778
779 if (kind == JSCPK_ARGUMENTS) {
780 if (setter) {
781 if (fp)
782 SET_OVERRIDE_BIT(fp, CALL_ARGUMENTS);
783 STOBJ_SET_SLOT(obj, JSSLOT_CALL_ARGUMENTS, *vp);
784 } else {
785 if (fp && !TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
786 JSObject *argsobj;
787
788 argsobj = js_GetArgsObject(cx, fp);
789 if (!argsobj)
790 return JS_FALSE;
791 *vp = OBJECT_TO_JSVAL(argsobj);
792 } else {
793 *vp = STOBJ_GET_SLOT(obj, JSSLOT_CALL_ARGUMENTS);
794 }
795 }
796 return JS_TRUE;
797 }
798
799 JS_ASSERT((int16) JSVAL_TO_INT(id) == JSVAL_TO_INT(id));
800 i = (uint16) JSVAL_TO_INT(id);
801 JS_ASSERT_IF(kind == JSCPK_ARG, i < fun->nargs);
802 JS_ASSERT_IF(kind == JSCPK_VAR, i < fun->u.i.nvars);
803
804 if (!fp) {
805 i += CALL_CLASS_FIXED_RESERVED_SLOTS;
806 if (kind == JSCPK_VAR)
807 i += fun->nargs;
808 else
809 JS_ASSERT(kind == JSCPK_ARG);
810 return setter
811 ? JS_SetReservedSlot(cx, obj, i, *vp)
812 : JS_GetReservedSlot(cx, obj, i, vp);
813 }
814
815 if (kind == JSCPK_ARG) {
816 array = fp->argv;
817 } else {
818 JS_ASSERT(kind == JSCPK_VAR);
819 array = fp->slots;
820 }
821 if (setter)
822 array[i] = *vp;
823 else
824 *vp = array[i];
825 return JS_TRUE;
826 }
827
828 static JSBool
829 GetCallArguments(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
830 {
831 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARGUMENTS, JS_FALSE);
832 }
833
834 static JSBool
835 SetCallArguments(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
836 {
837 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARGUMENTS, JS_TRUE);
838 }
839
840 JSBool
841 js_GetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
842 {
843 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG, JS_FALSE);
844 }
845
846 static JSBool
847 SetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
848 {
849 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG, JS_TRUE);
850 }
851
852 JSBool
853 js_GetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
854 {
855 return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_FALSE);
856 }
857
858 static JSBool
859 SetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
860 {
861 return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_TRUE);
862 }
863
864 static JSBool
865 call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags,
866 JSObject **objp)
867 {
868 JSFunction *fun;
869 jsid id;
870 JSLocalKind localKind;
871 JSPropertyOp getter, setter;
872 uintN slot, attrs;
873
874 if (!JSVAL_IS_STRING(idval))
875 return JS_TRUE;
876
877 fun = js_GetCallObjectFunction(obj);
878 if (!fun)
879 return JS_TRUE;
880
881 if (!js_ValueToStringId(cx, idval, &id))
882 return JS_FALSE;
883
884 localKind = js_LookupLocal(cx, fun, JSID_TO_ATOM(id), &slot);
885 if (localKind != JSLOCAL_NONE) {
886 JS_ASSERT((uint16) slot == slot);
887 attrs = JSPROP_PERMANENT | JSPROP_SHARED;
888 if (localKind == JSLOCAL_ARG) {
889 JS_ASSERT(slot < fun->nargs);
890 getter = js_GetCallArg;
891 setter = SetCallArg;
892 } else {
893 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
894 JS_ASSERT(slot < fun->u.i.nvars);
895 getter = js_GetCallVar;
896 setter = SetCallVar;
897 if (localKind == JSLOCAL_CONST)
898 attrs |= JSPROP_READONLY;
899 }
900 if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, getter, setter,
901 attrs, SPROP_HAS_SHORTID, (int16) slot,
902 NULL)) {
903 return JS_FALSE;
904 }
905 *objp = obj;
906 return JS_TRUE;
907 }
908
909 /*
910 * Resolve arguments so that we never store a particular Call object's
911 * arguments object reference in a Call prototype's |arguments| slot.
912 */
913 if (id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) {
914 if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID,
915 GetCallArguments, SetCallArguments,
916 JSPROP_PERMANENT | JSPROP_SHARED,
917 0, 0, NULL)) {
918 return JS_FALSE;
919 }
920 *objp = obj;
921 return JS_TRUE;
922 }
923 return JS_TRUE;
924 }
925
926 static JSBool
927 call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
928 {
929 JSStackFrame *fp;
930
931 if (type == JSTYPE_FUNCTION) {
932 fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
933 if (fp) {
934 JS_ASSERT(fp->fun);
935 *vp = OBJECT_TO_JSVAL(fp->callee);
936 }
937 }
938 return JS_TRUE;
939 }
940
941 static uint32
942 call_reserveSlots(JSContext *cx, JSObject *obj)
943 {
944 JSFunction *fun;
945
946 fun = js_GetCallObjectFunction(obj);
947 return JS_GET_LOCAL_NAME_COUNT(fun);
948 }
949
950 JS_FRIEND_DATA(JSClass) js_CallClass = {
951 js_Call_str,
952 JSCLASS_HAS_PRIVATE |
953 JSCLASS_HAS_RESERVED_SLOTS(CALL_CLASS_FIXED_RESERVED_SLOTS) |
954 JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS |
955 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Call),
956 JS_PropertyStub, JS_PropertyStub,
957 JS_PropertyStub, JS_PropertyStub,
958 call_enumerate, (JSResolveOp)call_resolve,
959 call_convert, JS_FinalizeStub,
960 NULL, NULL,
961 NULL, NULL,
962 NULL, NULL,
963 JS_CLASS_TRACE(args_or_call_trace), call_reserveSlots
964 };
965
966 static JSBool
967 fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
968 {
969 jsint slot;
970 JSFunction *fun;
971 JSStackFrame *fp;
972 JSSecurityCallbacks *callbacks;
973
974 if (!JSVAL_IS_INT(id))
975 return JS_TRUE;
976 slot = JSVAL_TO_INT(id);
977
978 /*
979 * Loop because getter and setter can be delegated from another class,
980 * but loop only for ARGS_LENGTH because we must pretend that f.length
981 * is in each function instance f, per ECMA-262, instead of only in the
982 * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
983 * to make it appear so).
984 *
985 * This code couples tightly to the attributes for the function_props[]
986 * initializers above, and to js_SetProperty and js_HasOwnProperty.
987 *
988 * It's important to allow delegating objects, even though they inherit
989 * this getter (fun_getProperty), to override arguments, arity, caller,
990 * and name. If we didn't return early for slot != ARGS_LENGTH, we would
991 * clobber *vp with the native property value, instead of letting script
992 * override that value in delegating objects.
993 *
994 * Note how that clobbering is what simulates JSPROP_READONLY for all of
995 * the non-standard properties when the directly addressed object (obj)
996 * is a function object (i.e., when this loop does not iterate).
997 */
998 while (!(fun = (JSFunction *)
999 JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) {
1000 if (slot != ARGS_LENGTH)
1001 return JS_TRUE;
1002 obj = OBJ_GET_PROTO(cx, obj);
1003 if (!obj)
1004 return JS_TRUE;
1005 }
1006
1007 /* Find fun's top-most activation record. */
1008 for (fp = cx->fp; fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL));
1009 fp = fp->down) {
1010 continue;
1011 }
1012
1013 switch (slot) {
1014 case CALL_ARGUMENTS:
1015 /* Warn if strict about f.arguments or equivalent unqualified uses. */
1016 if (!JS_ReportErrorFlagsAndNumber(cx,
1017 JSREPORT_WARNING | JSREPORT_STRICT,
1018 js_GetErrorMessage, NULL,
1019 JSMSG_DEPRECATED_USAGE,
1020 js_arguments_str)) {
1021 return JS_FALSE;
1022 }
1023 if (fp) {
1024 if (!js_GetArgsValue(cx, fp, vp))
1025 return JS_FALSE;
1026 } else {
1027 *vp = JSVAL_NULL;
1028 }
1029 break;
1030
1031 case ARGS_LENGTH:
1032 case FUN_ARITY:
1033 *vp = INT_TO_JSVAL((jsint)fun->nargs);
1034 break;
1035
1036 case FUN_NAME:
1037 *vp = fun->atom
1038 ? ATOM_KEY(fun->atom)
1039 : STRING_TO_JSVAL(cx->runtime->emptyString);
1040 break;
1041
1042 case FUN_CALLER:
1043 if (fp && fp->down && fp->down->fun)
1044 *vp = OBJECT_TO_JSVAL(fp->down->callee);
1045 else
1046 *vp = JSVAL_NULL;
1047 if (!JSVAL_IS_PRIMITIVE(*vp)) {
1048 callbacks = JS_GetSecurityCallbacks(cx);
1049 if (callbacks && callbacks->checkObjectAccess) {
1050 id = ATOM_KEY(cx->runtime->atomState.callerAtom);
1051 if (!callbacks->checkObjectAccess(cx, obj, id, JSACC_READ, vp))
1052 return JS_FALSE;
1053 }
1054 }
1055 break;
1056
1057 default:
1058 /* XXX fun[0] and fun.arguments[0] are equivalent. */
1059 if (fp && fp->fun && (uintN)slot < fp->fun->nargs)
1060 *vp = fp->argv[slot];
1061 break;
1062 }
1063
1064 return JS_TRUE;
1065 }
1066
1067 /*
1068 * ECMA-262 specifies that length is a property of function object instances,
1069 * but we can avoid that space cost by delegating to a prototype property that
1070 * is JSPROP_PERMANENT and JSPROP_SHARED. Each fun_getProperty call computes
1071 * a fresh length value based on the arity of the individual function object's
1072 * private data.
1073 *
1074 * The extensions below other than length, i.e., the ones not in ECMA-262,
1075 * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility
1076 * with ECMA we must allow a delegating object to override them. Therefore to
1077 * avoid entraining garbage in Function.prototype slots, they must be resolved
1078 * in non-prototype function objects, wherefore the lazy_function_props table
1079 * and fun_resolve's use of it.
1080 */
1081 #define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED)
1082
1083 static JSPropertySpec function_props[] = {
1084 {js_length_str, ARGS_LENGTH, LENGTH_PROP_ATTRS, fun_getProperty, JS_PropertyStub},
1085 {0,0,0,0,0}
1086 };
1087
1088 typedef struct LazyFunctionProp {
1089 uint16 atomOffset;
1090 int8 tinyid;
1091 uint8 attrs;
1092 } LazyFunctionProp;
1093
1094 /* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */
1095 static LazyFunctionProp lazy_function_props[] = {
1096 {ATOM_OFFSET(arguments), CALL_ARGUMENTS, JSPROP_PERMANENT},
1097 {ATOM_OFFSET(arity), FUN_ARITY, JSPROP_PERMANENT},
1098 {ATOM_OFFSET(caller), FUN_CALLER, JSPROP_PERMANENT},
1099 {ATOM_OFFSET(name), FUN_NAME, JSPROP_PERMANENT},
1100 };
1101
1102 static JSBool
1103 fun_enumerate(JSContext *cx, JSObject *obj)
1104 {
1105 jsid prototypeId;
1106 JSObject *pobj;
1107 JSProperty *prop;
1108
1109 prototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
1110 if (!OBJ_LOOKUP_PROPERTY(cx, obj, prototypeId, &pobj, &prop))
1111 return JS_FALSE;
1112 if (prop)
1113 OBJ_DROP_PROPERTY(cx, pobj, prop);
1114 return JS_TRUE;
1115 }
1116
1117 static JSBool
1118 fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
1119 JSObject **objp)
1120 {
1121 JSFunction *fun;
1122 JSAtom *atom;
1123 uintN i;
1124
1125 if (!JSVAL_IS_STRING(id))
1126 return JS_TRUE;
1127
1128 fun = GET_FUNCTION_PRIVATE(cx, obj);
1129
1130 /*
1131 * No need to reflect fun.prototype in 'fun.prototype = ... '.
1132 *
1133 * This is not just an optimization, because we must not resolve when
1134 * defining hidden properties during compilation. The setup code for the
1135 * prototype and the lazy properties below eventually calls the property
1136 * hooks for the function object. That in turn calls fun_reserveSlots to
1137 * get the number of the reserved slots which is just the number of
1138 * regular expressions literals in the function. When compiling, that
1139 * number is not yet ready so we must make sure that fun_resolve does
1140 * nothing until the code for the function is generated.
1141 */
1142 if (flags & JSRESOLVE_ASSIGNING)
1143 return JS_TRUE;
1144
1145 /*
1146 * Ok, check whether id is 'prototype' and bootstrap the function object's
1147 * prototype property.
1148 */
1149 atom = cx->runtime->atomState.classPrototypeAtom;
1150 if (id == ATOM_KEY(atom)) {
1151 JSObject *proto;
1152
1153 /*
1154 * Beware of the wacky case of a user function named Object -- trying
1155 * to find a prototype for that will recur back here _ad perniciem_.
1156 */
1157 if (fun->atom == CLASS_ATOM(cx, Object))
1158 return JS_TRUE;
1159
1160 /*
1161 * Make the prototype object to have the same parent as the function
1162 * object itself.
1163 */
1164 proto = js_NewObject(cx, &js_ObjectClass, NULL, OBJ_GET_PARENT(cx, obj),
1165 0);
1166 if (!proto)
1167 return JS_FALSE;
1168
1169 /*
1170 * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for
1171 * user-defined functions, but DontEnum | ReadOnly | DontDelete for
1172 * native "system" constructors such as Object or Function. So lazily
1173 * set the former here in fun_resolve, but eagerly define the latter
1174 * in JS_InitClass, with the right attributes.
1175 */
1176 if (!js_SetClassPrototype(cx, obj, proto,
1177 JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
1178 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
1179 return JS_FALSE;
1180 }
1181 *objp = obj;
1182 return JS_TRUE;
1183 }
1184
1185 for (i = 0; i < JS_ARRAY_LENGTH(lazy_function_props); i++) {
1186 LazyFunctionProp *lfp = &lazy_function_props[i];
1187
1188 atom = OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset);
1189 if (id == ATOM_KEY(atom)) {
1190 if (!js_DefineNativeProperty(cx, obj,
1191 ATOM_TO_JSID(atom), JSVAL_VOID,
1192 fun_getProperty, JS_PropertyStub,
1193 lfp->attrs, SPROP_HAS_SHORTID,
1194 lfp->tinyid, NULL)) {
1195 return JS_FALSE;
1196 }
1197 *objp = obj;
1198 return JS_TRUE;
1199 }
1200 }
1201
1202 return JS_TRUE;
1203 }
1204
1205 static JSBool
1206 fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
1207 {
1208 switch (type) {
1209 case JSTYPE_FUNCTION:
1210 *vp = OBJECT_TO_JSVAL(obj);
1211 return JS_TRUE;
1212 default:
1213 return js_TryValueOf(cx, obj, type, vp);
1214 }
1215 }
1216
1217 #if JS_HAS_XDR
1218
1219 /* XXX store parent and proto, if defined */
1220 static JSBool
1221 fun_xdrObject(JSXDRState *xdr, JSObject **objp)
1222 {
1223 JSContext *cx;
1224 JSFunction *fun;
1225 uint32 nullAtom; /* flag to indicate if fun->atom is NULL */
1226 uintN nargs, nvars, n;
1227 uint32 localsword; /* word to xdr argument and variable counts */
1228 uint32 flagsword; /* originally only flags was JS_XDRUint8'd */
1229 JSTempValueRooter tvr;
1230 JSBool ok;
1231
1232 cx = xdr->cx;
1233 if (xdr->mode == JSXDR_ENCODE) {
1234 fun = GET_FUNCTION_PRIVATE(cx, *objp);
1235 if (!FUN_INTERPRETED(fun)) {
1236 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1237 JSMSG_NOT_SCRIPTED_FUNCTION,
1238 JS_GetFunctionName(fun));
1239 return JS_FALSE;
1240 }
1241 nullAtom = !fun->atom;
1242 nargs = fun->nargs;
1243 nvars = fun->u.i.nvars;
1244 localsword = (nargs << 16) | nvars;
1245 flagsword = fun->flags;
1246 } else {
1247 fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
1248 if (!fun)
1249 return JS_FALSE;
1250 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
1251 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
1252 #ifdef __GNUC__
1253 nvars = nargs = 0; /* quell GCC uninitialized warning */
1254 #endif
1255 }
1256
1257 /* From here on, control flow must flow through label out. */
1258 JS_PUSH_TEMP_ROOT_OBJECT(cx, FUN_OBJECT(fun), &tvr);
1259 ok = JS_TRUE;
1260
1261 if (!JS_XDRUint32(xdr, &nullAtom))
1262 goto bad;
1263 if (!nullAtom && !js_XDRStringAtom(xdr, &fun->atom))
1264 goto bad;
1265 if (!JS_XDRUint32(xdr, &localsword) ||
1266 !JS_XDRUint32(xdr, &flagsword)) {
1267 goto bad;
1268 }
1269
1270 if (xdr->mode == JSXDR_DECODE) {
1271 nargs = localsword >> 16;
1272 nvars = localsword & JS_BITMASK(16);
1273 JS_ASSERT(flagsword | JSFUN_INTERPRETED);
1274 fun->flags = (uint16) flagsword;
1275 }
1276
1277 /* do arguments and local vars */
1278 n = nargs + nvars;
1279 if (n != 0) {
1280 void *mark;
1281 uintN i;
1282 uintN bitmapLength;
1283 uint32 *bitmap;
1284 jsuword *names;
1285 JSAtom *name;
1286 JSLocalKind localKind;
1287
1288 mark = JS_ARENA_MARK(&xdr->cx->tempPool);
1289
1290 /*
1291 * From this point the control must flow via the label release_mark.
1292 *
1293 * To xdr the names we prefix the names with a bitmap descriptor and
1294 * then xdr the names as strings. For argument names (indexes below
1295 * nargs) the corresponding bit in the bitmap is unset when the name
1296 * is null. Such null names are not encoded or decoded. For variable
1297 * names (indexes starting from nargs) bitmap's bit is set when the
1298 * name is declared as const, not as ordinary var.
1299 * */
1300 bitmapLength = JS_HOWMANY(n, JS_BITS_PER_UINT32);
1301 JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &xdr->cx->tempPool,
1302 bitmapLength * sizeof *bitmap);
1303 if (!bitmap) {
1304 js_ReportOutOfScriptQuota(xdr->cx);
1305 ok = JS_FALSE;
1306 goto release_mark;
1307 }
1308 if (xdr->mode == JSXDR_ENCODE) {
1309 names = js_GetLocalNameArray(xdr->cx, fun, &xdr->cx->tempPool);
1310 if (!names) {
1311 ok = JS_FALSE;
1312 goto release_mark;
1313 }
1314 memset(bitmap, 0, bitmapLength * sizeof *bitmap);
1315 for (i = 0; i != n; ++i) {
1316 if (i < fun->nargs
1317 ? JS_LOCAL_NAME_TO_ATOM(names[i]) != NULL
1318 : JS_LOCAL_NAME_IS_CONST(names[i])) {
1319 bitmap[i >> JS_BITS_PER_UINT32_LOG2] |=
1320 JS_BIT(i & (JS_BITS_PER_UINT32 - 1));
1321 }
1322 }
1323 }
1324 #ifdef __GNUC__
1325 else {
1326 names = NULL; /* quell GCC uninitialized warning */
1327 }
1328 #endif
1329 for (i = 0; i != bitmapLength; ++i) {
1330 ok = JS_XDRUint32(xdr, &bitmap[i]);
1331 if (!ok)
1332 goto release_mark;
1333 }
1334 for (i = 0; i != n; ++i) {
1335 if (i < nargs &&
1336 !(bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
1337 JS_BIT(i & (JS_BITS_PER_UINT32 - 1)))) {
1338 if (xdr->mode == JSXDR_DECODE) {
1339 ok = js_AddLocal(xdr->cx, fun, NULL, JSLOCAL_ARG);
1340 if (!ok)
1341 goto release_mark;
1342 } else {
1343 JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names[i]));
1344 }
1345 continue;
1346 }
1347 if (xdr->mode == JSXDR_ENCODE)
1348 name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1349 ok = js_XDRStringAtom(xdr, &name);
1350 if (!ok)
1351 goto release_mark;
1352 if (xdr->mode == JSXDR_DECODE) {
1353 localKind = (i < nargs)
1354 ? JSLOCAL_ARG
1355 : bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
1356 JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
1357 ? JSLOCAL_CONST
1358 : JSLOCAL_VAR;
1359 ok = js_AddLocal(xdr->cx, fun, name, localKind);
1360 if (!ok)
1361 goto release_mark;
1362 }
1363 }
1364 ok = JS_TRUE;
1365
1366 release_mark:
1367 JS_ARENA_RELEASE(&xdr->cx->tempPool, mark);
1368 if (!ok)
1369 goto out;
1370
1371 if (xdr->mode == JSXDR_DECODE)
1372 js_FreezeLocalNames(cx, fun);
1373 }
1374
1375 if (!js_XDRScript(xdr, &fun->u.i.script, NULL))
1376 goto bad;
1377
1378 if (xdr->mode == JSXDR_DECODE) {
1379 *objp = FUN_OBJECT(fun);
1380 #ifdef CHECK_SCRIPT_OWNER
1381 fun->u.i.script->owner = NULL;
1382 #endif
1383 js_CallNewScriptHook(cx, fun->u.i.script, fun);
1384 }
1385
1386 out:
1387 JS_POP_TEMP_ROOT(cx, &tvr);
1388 return ok;
1389
1390 bad:
1391 ok = JS_FALSE;
1392 goto out;
1393 }
1394
1395 #else /* !JS_HAS_XDR */
1396
1397 #define fun_xdrObject NULL
1398
1399 #endif /* !JS_HAS_XDR */
1400
1401 /*
1402 * [[HasInstance]] internal method for Function objects: fetch the .prototype
1403 * property of its 'this' parameter, and walks the prototype chain of v (only
1404 * if v is an object) returning true if .prototype is found.
1405 */
1406 static JSBool
1407 fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
1408 {
1409 jsval pval;
1410
1411 if (!OBJ_GET_PROPERTY(cx, obj,
1412 ATOM_TO_JSID(cx->runtime->atomState
1413 .classPrototypeAtom),
1414 &pval)) {
1415 return JS_FALSE;
1416 }
1417
1418 if (JSVAL_IS_PRIMITIVE(pval)) {
1419 /*
1420 * Throw a runtime error if instanceof is called on a function that
1421 * has a non-object as its .prototype value.
1422 */
1423 js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE,
1424 -1, OBJECT_TO_JSVAL(obj), NULL);
1425 return JS_FALSE;
1426 }
1427
1428 return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp);
1429 }
1430
1431 static void
1432 TraceLocalNames(JSTracer *trc, JSFunction *fun);
1433
1434 static void
1435 DestroyLocalNames(JSContext *cx, JSFunction *fun);
1436
1437 static void
1438 fun_trace(JSTracer *trc, JSObject *obj)
1439 {
1440 JSFunction *fun;
1441
1442 /* A newborn function object may have a not yet initialized private slot. */
1443 fun = (JSFunction *) JS_GetPrivate(trc->context, obj);
1444 if (!fun)
1445 return;
1446
1447 if (FUN_OBJECT(fun) != obj) {
1448 /* obj is cloned function object, trace the original. */
1449 JS_CALL_TRACER(trc, FUN_OBJECT(fun), JSTRACE_OBJECT, "private");
1450 return;
1451 }
1452 if (fun->atom)
1453 JS_CALL_STRING_TRACER(trc, ATOM_TO_STRING(fun->atom), "atom");
1454 if (FUN_INTERPRETED(fun)) {
1455 if (fun->u.i.script)
1456 js_TraceScript(trc, fun->u.i.script);
1457 TraceLocalNames(trc, fun);
1458 }
1459 }
1460
1461 static void
1462 fun_finalize(JSContext *cx, JSObject *obj)
1463 {
1464 JSFunction *fun;
1465
1466 /* Ignore newborn and cloned function objects. */
1467 fun = (JSFunction *) JS_GetPrivate(cx, obj);
1468 if (!fun || FUN_OBJECT(fun) != obj)
1469 return;
1470
1471 /*
1472 * Null-check of u.i.script is required since the parser sets interpreted
1473 * very early.
1474 */
1475 if (FUN_INTERPRETED(fun)) {
1476 if (fun->u.i.script)
1477 js_DestroyScript(cx, fun->u.i.script);
1478 DestroyLocalNames(cx, fun);
1479 }
1480 }
1481
1482 static uint32
1483 fun_reserveSlots(JSContext *cx, JSObject *obj)
1484 {
1485 JSFunction *fun;
1486 uint32 nslots;
1487
1488 /*
1489 * We use JS_GetPrivate and not GET_FUNCTION_PRIVATE because during
1490 * js_InitFunctionClass invocation the function is called before the
1491 * private slot of the function object is set.
1492 */
1493 fun = (JSFunction *) JS_GetPrivate(cx, obj);
1494 nslots = 0;
1495 if (fun && FUN_INTERPRETED(fun) && fun->u.i.script) {
1496 if (fun->u.i.script->upvarsOffset != 0)
1497 nslots = JS_SCRIPT_UPVARS(fun->u.i.script)->length;
1498 if (fun->u.i.script->regexpsOffset != 0)
1499 nslots += JS_SCRIPT_REGEXPS(fun->u.i.script)->length;
1500 }
1501 return nslots;
1502 }
1503
1504 /*
1505 * Reserve two slots in all function objects for XPConnect. Note that this
1506 * does not bloat every instance, only those on which reserved slots are set,
1507 * and those on which ad-hoc properties are defined.
1508 */
1509 JS_FRIEND_DATA(JSClass) js_FunctionClass = {
1510 js_Function_str,
1511 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2) |
1512 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
1513 JS_PropertyStub, JS_PropertyStub,
1514 JS_PropertyStub, JS_PropertyStub,
1515 fun_enumerate, (JSResolveOp)fun_resolve,
1516 fun_convert, fun_finalize,
1517 NULL, NULL,
1518 NULL, NULL,
1519 fun_xdrObject, fun_hasInstance,
1520 JS_CLASS_TRACE(fun_trace), fun_reserveSlots
1521 };
1522
1523 static JSBool
1524 fun_toStringHelper(JSContext *cx, uint32 indent, uintN argc, jsval *vp)
1525 {
1526 jsval fval;
1527 JSObject *obj;
1528 JSFunction *fun;
1529 JSString *str;
1530
1531 fval = JS_THIS(cx, vp);
1532 if (JSVAL_IS_NULL(fval))
1533 return JS_FALSE;
1534
1535 if (!VALUE_IS_FUNCTION(cx, fval)) {
1536 /*
1537 * If we don't have a function to start off with, try converting the
1538 * object to a function. If that doesn't work, complain.
1539 */
1540 if (!JSVAL_IS_PRIMITIVE(fval)) {
1541 obj = JSVAL_TO_OBJECT(fval);
1542 if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, JSTYPE_FUNCTION,
1543 &fval)) {
1544 return JS_FALSE;
1545 }
1546 vp[1] = fval;
1547 }
1548 if (!VALUE_IS_FUNCTION(cx, fval)) {
1549 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1550 JSMSG_INCOMPATIBLE_PROTO,
1551 js_Function_str, js_toString_str,
1552 JS_GetTypeName(cx, JS_TypeOfValue(cx, fval)));
1553 return JS_FALSE;
1554 }
1555 }
1556
1557 obj = JSVAL_TO_OBJECT(fval);
1558 if (argc != 0) {
1559 indent = js_ValueToECMAUint32(cx, &vp[2]);
1560 if (JSVAL_IS_NULL(vp[2]))
1561 return JS_FALSE;
1562 }
1563
1564 JS_ASSERT(JS_ObjectIsFunction(cx, obj));
1565 fun = GET_FUNCTION_PRIVATE(cx, obj);
1566 if (!fun)
1567 return JS_TRUE;
1568 str = JS_DecompileFunction(cx, fun, (uintN)indent);
1569 if (!str)
1570 return JS_FALSE;
1571 *vp = STRING_TO_JSVAL(str);
1572 return JS_TRUE;
1573 }
1574
1575 static JSBool
1576 fun_toString(JSContext *cx, uintN argc, jsval *vp)
1577 {
1578 return fun_toStringHelper(cx, 0, argc, vp);
1579 }
1580
1581 #if JS_HAS_TOSOURCE
1582 static JSBool
1583 fun_toSource(JSContext *cx, uintN argc, jsval *vp)
1584 {
1585 return fun_toStringHelper(cx, JS_DONT_PRETTY_PRINT, argc, vp);
1586 }
1587 #endif
1588
1589 JSBool
1590 js_fun_call(JSContext *cx, uintN argc, jsval *vp)
1591 {
1592 JSObject *obj;
1593 jsval fval, *argv, *invokevp;
1594 JSString *str;
1595 void *mark;
1596 JSBool ok;
1597
1598 obj = JS_THIS_OBJECT(cx, vp);
1599 if (!obj || !OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &vp[1]))
1600 return JS_FALSE;
1601 fval = vp[1];
1602
1603 if (!VALUE_IS_FUNCTION(cx, fval)) {
1604 str = JS_ValueToString(cx, fval);
1605 if (str) {
1606 const char *bytes = js_GetStringBytes(cx, str);
1607
1608 if (bytes) {
1609 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1610 JSMSG_INCOMPATIBLE_PROTO,
1611 js_Function_str, js_call_str,
1612 bytes);
1613 }
1614 }
1615 return JS_FALSE;
1616 }
1617
1618 argv = vp + 2;
1619 if (argc == 0) {
1620 /* Call fun with its global object as the 'this' param if no args. */
1621 obj = NULL;
1622 } else {
1623 /* Otherwise convert the first arg to 'this' and skip over it. */
1624 if (!JSVAL_IS_PRIMITIVE(argv[0]))
1625 obj = JSVAL_TO_OBJECT(argv[0]);
1626 else if (!js_ValueToObject(cx, argv[0], &obj))
1627 return JS_FALSE;
1628 argc--;
1629 argv++;
1630 }
1631
1632 /* Allocate stack space for fval, obj, and the args. */
1633 invokevp = js_AllocStack(cx, 2 + argc, &mark);
1634 if (!invokevp)
1635 return JS_FALSE;
1636
1637 /* Push fval, obj, and the args. */
1638 invokevp[0] = fval;
1639 invokevp[1] = OBJECT_TO_JSVAL(obj);
1640 memcpy(invokevp + 2, argv, argc * sizeof *argv);
1641
1642 ok = js_Invoke(cx, argc, invokevp, 0);
1643 *vp = *invokevp;
1644 js_FreeStack(cx, mark);
1645 return ok;
1646 }
1647
1648 JSBool
1649 js_fun_apply(JSContext *cx, uintN argc, jsval *vp)
1650 {
1651 JSObject *obj, *aobj;
1652 jsval fval, *invokevp, *sp;
1653 JSString *str;
1654 jsuint length;
1655 JSBool arraylike, ok;
1656 void *mark;
1657 uintN i;
1658
1659 if (argc == 0) {
1660 /* Will get globalObject as 'this' and no other arguments. */
1661 return js_fun_call(cx, argc, vp);
1662 }
1663
1664 obj = JS_THIS_OBJECT(cx, vp);
1665 if (!obj || !OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &vp[1]))
1666 return JS_FALSE;
1667 fval = vp[1];
1668
1669 if (!VALUE_IS_FUNCTION(cx, fval)) {
1670 str = JS_ValueToString(cx, fval);
1671 if (str) {
1672 const char *bytes = js_GetStringBytes(cx, str);
1673
1674 if (bytes) {
1675 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1676 JSMSG_INCOMPATIBLE_PROTO,
1677 js_Function_str, js_apply_str,
1678 bytes);
1679 }
1680 }
1681 return JS_FALSE;
1682 }
1683
1684 /* Quell GCC overwarnings. */
1685 aobj = NULL;
1686 length = 0;
1687
1688 if (argc >= 2) {
1689 /* If the 2nd arg is null or void, call the function with 0 args. */
1690 if (JSVAL_IS_NULL(vp[3]) || JSVAL_IS_VOID(vp[3])) {
1691 argc = 0;
1692 } else {
1693 /* The second arg must be an array (or arguments object). */
1694 arraylike = JS_FALSE;
1695 if (!JSVAL_IS_PRIMITIVE(vp[3])) {
1696 aobj = JSVAL_TO_OBJECT(vp[3]);
1697 if (!js_IsArrayLike(cx, aobj, &arraylike, &length))
1698 return JS_FALSE;
1699 }
1700 if (!arraylike) {
1701 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1702 JSMSG_BAD_APPLY_ARGS, js_apply_str);
1703 return JS_FALSE;
1704 }
1705 }
1706 }
1707
1708 /* Convert the first arg to 'this' and skip over it. */
1709 if (!JSVAL_IS_PRIMITIVE(vp[2]))
1710 obj = JSVAL_TO_OBJECT(vp[2]);
1711 else if (!js_ValueToObject(cx, vp[2], &obj))
1712 return JS_FALSE;
1713
1714 /* Allocate stack space for fval, obj, and the args. */
1715 argc = (uintN)JS_MIN(length, ARRAY_INIT_LIMIT - 1);
1716 invokevp = js_AllocStack(cx, 2 + argc, &mark);
1717 if (!invokevp)
1718 return JS_FALSE;
1719
1720 /* Push fval, obj, and aobj's elements as args. */
1721 sp = invokevp;
1722 *sp++ = fval;
1723 *sp++ = OBJECT_TO_JSVAL(obj);
1724 for (i = 0; i < argc; i++) {
1725 ok = JS_GetElement(cx, aobj, (jsint)i, sp);
1726 if (!ok)
1727 goto out;
1728 sp++;
1729 }
1730
1731 ok = js_Invoke(cx, argc, invokevp, 0);
1732 *vp = *invokevp;
1733 out:
1734 js_FreeStack(cx, mark);
1735 return ok;
1736 }
1737
1738 #ifdef NARCISSUS
1739 static JSBool
1740 fun_applyConstructor(JSContext *cx, uintN argc, jsval *vp)
1741 {
1742 JSObject *aobj;
1743 uintN length, i;
1744 void *mark;
1745 jsval *invokevp, *sp;
1746 JSBool ok;
1747
1748 if (JSVAL_IS_PRIMITIVE(vp[2]) ||
1749 (aobj = JSVAL_TO_OBJECT(vp[2]),
1750 OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass &&
1751 OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass)) {
1752 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1753 JSMSG_BAD_APPLY_ARGS, "__applyConstruct__");
1754 return JS_FALSE;
1755 }
1756
1757 if (!js_GetLengthProperty(cx, aobj, &length))
1758 return JS_FALSE;
1759
1760 if (length >= ARRAY_INIT_LIMIT)
1761 length = ARRAY_INIT_LIMIT - 1;
1762 invokevp = js_AllocStack(cx, 2 + length, &mark);
1763 if (!invokevp)
1764 return JS_FALSE;
1765
1766 sp = invokevp;
1767 *sp++ = vp[1];
1768 *sp++ = JSVAL_NULL; /* this is filled automagically */
1769 for (i = 0; i < length; i++) {
1770 ok = JS_GetElement(cx, aobj, (jsint)i, sp);
1771 if (!ok)
1772 goto out;
1773 sp++;
1774 }
1775
1776 ok = js_InvokeConstructor(cx, length, JS_TRUE, invokevp);
1777 *vp = *invokevp;
1778 out:
1779 js_FreeStack(cx, mark);
1780 return ok;
1781 }
1782 #endif
1783
1784 static JSFunctionSpec function_methods[] = {
1785 #if JS_HAS_TOSOURCE
1786 JS_FN(js_toSource_str, fun_toSource, 0,0),
1787 #endif
1788 JS_FN(js_toString_str, fun_toString, 0,0),
1789 JS_FN(js_apply_str, js_fun_apply, 2,0),
1790 JS_FN(js_call_str, js_fun_call, 1,0),
1791 #ifdef NARCISSUS
1792 JS_FN("__applyConstructor__", fun_applyConstructor, 1,0),
1793 #endif
1794 JS_FS_END
1795 };
1796
1797 static JSBool
1798 Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1799 {
1800 JSStackFrame *fp, *caller;
1801 JSFunction *fun;
1802 JSObject *parent;
1803 uintN i, n, lineno;
1804 JSAtom *atom;
1805 const char *filename;
1806 JSBool ok;
1807 JSString *str, *arg;
1808 JSTokenStream ts;
1809 JSPrincipals *principals;
1810 jschar *collected_args, *cp;
1811 void *mark;
1812 size_t arg_length, args_length, old_args_length;
1813 JSTokenType tt;
1814
1815 fp = cx->fp;
1816 if (!(fp->flags & JSFRAME_CONSTRUCTING)) {
1817 obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL, 0);
1818 if (!obj)
1819 return JS_FALSE;
1820 *rval = OBJECT_TO_JSVAL(obj);
1821 } else {
1822 /*
1823 * The constructor is called before the private slot is initialized so
1824 * we must use JS_GetPrivate, not GET_FUNCTION_PRIVATE here.
1825 */
1826 if (JS_GetPrivate(cx, obj))
1827 return JS_TRUE;
1828 }
1829
1830 /*
1831 * NB: (new Function) is not lexically closed by its caller, it's just an
1832 * anonymous function in the top-level scope that its constructor inhabits.
1833 * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
1834 * and so would a call to f from another top-level's script or function.
1835 *
1836 * In older versions, before call objects, a new Function was adopted by
1837 * its running context's globalObject, which might be different from the
1838 * top-level reachable from scopeChain (in HTML frames, e.g.).
1839 */
1840 parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]));
1841
1842 fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED,
1843 parent, cx->runtime->atomState.anonymousAtom);
1844
1845 if (!fun)
1846 return JS_FALSE;
1847
1848 /*
1849 * Function is static and not called directly by other functions in this
1850 * file, therefore it is callable only as a native function by js_Invoke.
1851 * Find the scripted caller, possibly skipping other native frames such as
1852 * are built for Function.prototype.call or .apply activations that invoke
1853 * Function indirectly from a script.
1854 */
1855 JS_ASSERT(!fp->script && fp->fun && fp->fun->u.n.native == Function);
1856 caller = JS_GetScriptedCaller(cx, fp);
1857 if (caller) {
1858 principals = JS_EvalFramePrincipals(cx, fp, caller);
1859 filename = js_ComputeFilename(cx, caller, principals, &lineno);
1860 } else {
1861 filename = NULL;
1862 lineno = 0;
1863 principals = NULL;
1864 }
1865
1866 /* Belt-and-braces: check that the caller has access to parent. */
1867 if (!js_CheckPrincipalsAccess(cx, parent, principals,
1868 CLASS_ATOM(cx, Function))) {
1869 return JS_FALSE;
1870 }
1871
1872 n = argc ? argc - 1 : 0;
1873 if (n > 0) {
1874 enum { OK, BAD, BAD_FORMAL } state;
1875
1876 /*
1877 * Collect the function-argument arguments into one string, separated
1878 * by commas, then make a tokenstream from that string, and scan it to
1879 * get the arguments. We need to throw the full scanner at the
1880 * problem, because the argument string can legitimately contain
1881 * comments and linefeeds. XXX It might be better to concatenate
1882 * everything up into a function definition and pass it to the
1883 * compiler, but doing it this way is less of a delta from the old
1884 * code. See ECMA 15.3.2.1.
1885 */
1886 state = BAD_FORMAL;
1887 args_length = 0;
1888 for (i = 0; i < n; i++) {
1889 /* Collect the lengths for all the function-argument arguments. */
1890 arg = js_ValueToString(cx, argv[i]);
1891 if (!arg)
1892 return JS_FALSE;
1893 argv[i] = STRING_TO_JSVAL(arg);
1894
1895 /*
1896 * Check for overflow. The < test works because the maximum
1897 * JSString length fits in 2 fewer bits than size_t has.
1898 */
1899 old_args_length = args_length;
1900 args_length = old_args_length + JSSTRING_LENGTH(arg);
1901 if (args_length < old_args_length) {
1902 js_ReportAllocationOverflow(cx);
1903 return JS_FALSE;
1904 }
1905 }
1906
1907 /* Add 1 for each joining comma and check for overflow (two ways). */
1908 old_args_length = args_length;
1909 args_length = old_args_length + n - 1;
1910 if (args_length < old_args_length ||
1911 args_length >= ~(size_t)0 / sizeof(jschar)) {
1912 js_ReportAllocationOverflow(cx);
1913 return JS_FALSE;
1914 }
1915
1916 /*
1917 * Allocate a string to hold the concatenated arguments, including room
1918 * for a terminating 0. Mark cx->tempPool for later release, to free
1919 * collected_args and its tokenstream in one swoop.
1920 */
1921 mark = JS_ARENA_MARK(&cx->tempPool);
1922 JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool,
1923 (args_length+1) * sizeof(jschar));
1924 if (!cp) {
1925 js_ReportOutOfScriptQuota(cx);
1926 return JS_FALSE;
1927 }
1928 collected_args = cp;
1929
1930 /*
1931 * Concatenate the arguments into the new string, separated by commas.
1932 */
1933 for (i = 0; i < n; i++) {
1934 arg = JSVAL_TO_STRING(argv[i]);
1935 arg_length = JSSTRING_LENGTH(arg);
1936 (void) js_strncpy(cp, JSSTRING_CHARS(arg), arg_length);
1937 cp += arg_length;
1938
1939 /* Add separating comma or terminating 0. */
1940 *cp++ = (i + 1 < n) ? ',' : 0;
1941 }
1942
1943 /* Initialize a tokenstream that reads from the given string. */
1944 if (!js_InitTokenStream(cx, &ts, collected_args, args_length,
1945 NULL, filename, lineno)) {
1946 JS_ARENA_RELEASE(&cx->tempPool, mark);
1947 return JS_FALSE;
1948 }
1949
1950 /* The argument string may be empty or contain no tokens. */
1951 tt = js_GetToken(cx, &ts);
1952 if (tt != TOK_EOF) {
1953 for (;;) {
1954 /*
1955 * Check that it's a name. This also implicitly guards against
1956 * TOK_ERROR, which was already reported.
1957 */
1958 if (tt != TOK_NAME)
1959 goto after_args;
1960
1961 /*
1962 * Get the atom corresponding to the name from the token
1963 * stream; we're assured at this point that it's a valid
1964 * identifier.
1965 */
1966 atom = CURRENT_TOKEN(&ts).t_atom;
1967
1968 /* Check for a duplicate parameter name. */
1969 if (js_LookupLocal(cx, fun, atom, NULL) != JSLOCAL_NONE) {
1970 const char *name;
1971
1972 name = js_AtomToPrintableString(cx, atom);
1973 ok = name &&
1974 js_ReportCompileErrorNumber(cx, &ts, NULL,
1975 JSREPORT_WARNING |
1976 JSREPORT_STRICT,
1977 JSMSG_DUPLICATE_FORMAL,
1978 name);
1979 if (!ok)
1980 goto after_args;
1981 }
1982 if (!js_AddLocal(cx, fun, atom, JSLOCAL_ARG))
1983 goto after_args;
1984
1985 /*
1986 * Get the next token. Stop on end of stream. Otherwise
1987 * insist on a comma, get another name, and iterate.
1988 */
1989 tt = js_GetToken(cx, &ts);
1990 if (tt == TOK_EOF)
1991 break;
1992 if (tt != TOK_COMMA)
1993 goto after_args;
1994 tt = js_GetToken(cx, &ts);
1995 }
1996 }
1997
1998 state = OK;
1999 after_args:
2000 if (state == BAD_FORMAL && !(ts.flags & TSF_ERROR)) {
2001 /*
2002 * Report "malformed formal parameter" iff no illegal char or
2003 * similar scanner error was already reported.
2004 */
2005 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2006 JSMSG_BAD_FORMAL);
2007 }
2008 js_CloseTokenStream(cx, &ts);
2009 JS_ARENA_RELEASE(&cx->tempPool, mark);
2010 if (state != OK)
2011 return JS_FALSE;
2012 }
2013
2014 if (argc) {
2015 str = js_ValueToString(cx, argv[argc-1]);
2016 if (!str)
2017 return JS_FALSE;
2018 argv[argc-1] = STRING_TO_JSVAL(str);
2019 } else {
2020 str = cx->runtime->emptyString;
2021 }
2022
2023 return js_CompileFunctionBody(cx, fun, principals,
2024 JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
2025 filename, lineno);
2026 }
2027
2028 JSObject *
2029 js_InitFunctionClass(JSContext *cx, JSObject *obj)
2030 {
2031 JSObject *proto;
2032 JSFunction *fun;
2033
2034 proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
2035 function_props, function_methods, NULL, NULL);
2036 if (!proto)
2037 return NULL;
2038 fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
2039 if (!fun)
2040 goto bad;
2041 fun->u.i.script = js_NewScript(cx, 1, 1, 0, 0, 0, 0, 0);
2042 if (!fun->u.i.script)
2043 goto bad;
2044 fun->u.i.script->code[0] = JSOP_STOP;
2045 *SCRIPT_NOTES(fun->u.i.script) = SRC_NULL;
2046 #ifdef CHECK_SCRIPT_OWNER
2047 fun->u.i.script->owner = NULL;
2048 #endif
2049 return proto;
2050
2051 bad:
2052 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
2053 return NULL;
2054 }
2055
2056 JSObject *
2057 js_InitCallClass(JSContext *cx, JSObject *obj)
2058 {
2059 JSObject *proto;
2060
2061 proto = JS_InitClass(cx, obj, NULL, &js_CallClass, NULL, 0,
2062 NULL, NULL, NULL, NULL);
2063 if (!proto)
2064 return NULL;
2065
2066 /*
2067 * Null Call.prototype's proto slot so that Object.prototype.* does not
2068 * pollute the scope of heavyweight functions.
2069 */
2070 OBJ_CLEAR_PROTO(cx, proto);
2071 return proto;
2072 }
2073
2074 JSFunction *
2075 js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
2076 uintN flags, JSObject *parent, JSAtom *atom)
2077 {
2078 JSFunction *fun;
2079
2080 if (funobj) {
2081 JS_ASSERT(HAS_FUNCTION_CLASS(funobj));
2082 OBJ_SET_PARENT(cx, funobj, parent);
2083 } else {
2084 funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent, 0);
2085 if (!funobj)
2086 return NULL;
2087 }
2088 JS_ASSERT(JSVAL_IS_VOID(funobj->fslots[JSSLOT_PRIVATE]));
2089 fun = (JSFunction *) funobj;
2090
2091 /* Initialize all function members. */
2092 fun->nargs = nargs;
2093 fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_INTERPRETED | JSFUN_TRACEABLE);
2094 if (flags & JSFUN_INTERPRETED) {
2095 JS_ASSERT(!native);
2096 JS_ASSERT(nargs == 0);
2097 fun->u.i.nvars = 0;
2098 fun->u.i.nupvars = 0;
2099 fun->u.i.script = NULL;
2100 #ifdef DEBUG
2101 fun->u.i.names.taggedAtom = 0;
2102 #endif
2103 } else {
2104 fun->u.n.extra = 0;
2105 fun->u.n.spare = 0;
2106 if (flags & JSFUN_TRACEABLE) {
2107 #ifdef JS_TRACER
2108 JSTraceableNative *trcinfo = (JSTraceableNative *) native;
2109 fun->u.n.native = (JSNative) trcinfo->native;
2110 FUN_TRCINFO(fun) = trcinfo;
2111 #else
2112 JS_ASSERT(0);
2113 #endif
2114 } else {
2115 fun->u.n.native = native;
2116 FUN_CLASP(fun) = NULL;
2117 }
2118 }
2119 fun->atom = atom;
2120
2121 /* Set private to self to indicate non-cloned fully initialized function. */
2122 FUN_OBJECT(fun)->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun);
2123 return fun;
2124 }
2125
2126 JSObject *
2127 js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent)
2128 {
2129 JSObject *clone;
2130
2131 /*
2132 * The cloned function object does not need the extra fields beyond
2133 * JSObject as it points to fun via the private slot.
2134 */
2135 clone = js_NewObject(cx, &js_FunctionClass, NULL, parent,
2136 sizeof(JSObject));
2137 if (!clone)
2138 return NULL;
2139 clone->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun);
2140 return clone;
2141 }
2142
2143 JSFunction *
2144 js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
2145 uintN nargs, uintN attrs)
2146 {
2147 JSFunction *fun;
2148 JSPropertyOp gsop;
2149
2150 fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom);
2151 if (!fun)
2152 return NULL;
2153 gsop = (attrs & JSFUN_STUB_GSOPS) ? JS_PropertyStub : NULL;
2154 if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom),
2155 OBJECT_TO_JSVAL(FUN_OBJECT(fun)),
2156 gsop, gsop,
2157 attrs & ~JSFUN_FLAGS_MASK, NULL)) {
2158 return NULL;
2159 }
2160 return fun;
2161 }
2162
2163 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
2164 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
2165 #endif
2166
2167 JSFunction *
2168 js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags)
2169 {
2170 jsval v;
2171 JSObject *obj;
2172
2173 v = *vp;
2174 obj = NULL;
2175 if (JSVAL_IS_OBJECT(v)) {
2176 obj = JSVAL_TO_OBJECT(v);
2177 if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) {
2178 if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v))
2179 return NULL;
2180 obj = VALUE_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL;
2181 }
2182 }
2183 if (!obj) {
2184 js_ReportIsNotFunction(cx, vp, flags);
2185 return NULL;
2186 }
2187 return GET_FUNCTION_PRIVATE(cx, obj);
2188 }
2189
2190 JSObject *
2191 js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags)
2192 {
2193 JSFunction *fun;
2194 JSStackFrame *caller;
2195 JSPrincipals *principals;
2196
2197 if (VALUE_IS_FUNCTION(cx, *vp))
2198 return JSVAL_TO_OBJECT(*vp);
2199
2200 fun = js_ValueToFunction(cx, vp, flags);
2201 if (!fun)
2202 return NULL;
2203 *vp = OBJECT_TO_JSVAL(FUN_OBJECT(fun));
2204
2205 caller = JS_GetScriptedCaller(cx, cx->fp);
2206 if (caller) {
2207 principals = JS_StackFramePrincipals(cx, caller);
2208 } else {
2209 /* No scripted caller, don't allow access. */
2210 principals = NULL;
2211 }
2212
2213 if (!js_CheckPrincipalsAccess(cx, FUN_OBJECT(fun), principals,
2214 fun->atom
2215 ? fun->atom
2216 : cx->runtime->atomState.anonymousAtom)) {
2217 return NULL;
2218 }
2219 return FUN_OBJECT(fun);
2220 }
2221
2222 JSObject *
2223 js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags)
2224 {
2225 JSObject *callable;
2226
2227 callable = JSVAL_IS_PRIMITIVE(*vp) ? NULL : JSVAL_TO_OBJECT(*vp);
2228 if (callable &&
2229 ((callable->map->ops == &js_ObjectOps)
2230 ? OBJ_GET_CLASS(cx, callable)->call
2231 : callable->map->ops->call)) {
2232 *vp = OBJECT_TO_JSVAL(callable);
2233 } else {
2234 callable = js_ValueToFunctionObject(cx, vp, flags);
2235 }
2236 return callable;
2237 }
2238
2239 void
2240 js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
2241 {
2242 JSStackFrame *fp;
2243 uintN error;
2244 const char *name, *source;
2245 JSTempValueRooter tvr;
2246
2247 for (fp = cx->fp; fp && !fp->regs; fp = fp->down)
2248 continue;
2249 name = source = NULL;
2250 JS_PUSH_TEMP_ROOT_STRING(cx, NULL, &tvr);
2251 if (flags & JSV2F_ITERATOR) {
2252 error = JSMSG_BAD_ITERATOR;
2253 name = js_iterator_str;
2254 tvr.u.string = js_ValueToSource(cx, *vp);
2255 if (!tvr.u.string)
2256 goto out;
2257 tvr.u.string = js_QuoteString(cx, tvr.u.string, 0);
2258 if (!tvr.u.string)
2259 goto out;
2260 source = js_GetStringBytes(cx, tvr.u.string);
2261 if (!source)
2262 goto out;
2263 } else if (flags & JSV2F_CONSTRUCT) {
2264 error = JSMSG_NOT_CONSTRUCTOR;
2265 } else {
2266 error = JSMSG_NOT_FUNCTION;
2267 }
2268
2269 js_ReportValueError3(cx, error,
2270 (fp && fp->regs &&
2271 StackBase(fp) <= vp && vp < fp->regs->sp)
2272 ? vp - fp->regs->sp
2273 : (flags & JSV2F_SEARCH_STACK)
2274 ? JSDVG_SEARCH_STACK
2275 : JSDVG_IGNORE_STACK,
2276 *vp, NULL,
2277 name, source);
2278
2279 out:
2280 JS_POP_TEMP_ROOT(cx, &tvr);
2281 }
2282
2283 /*
2284 * When a function has between 2 and MAX_ARRAY_LOCALS arguments and variables,
2285 * their name are stored as the JSLocalNames.array.
2286 */
2287 #define MAX_ARRAY_LOCALS 8
2288
2289 JS_STATIC_ASSERT(2 <= MAX_ARRAY_LOCALS);
2290 JS_STATIC_ASSERT(MAX_ARRAY_LOCALS < JS_BITMASK(16));
2291
2292 /*
2293 * We use the lowest bit of the string atom to distinguish const from var
2294 * name when there is only single name or when names are stored as an array.
2295 */
2296 JS_STATIC_ASSERT((JSVAL_STRING & 1) == 0);
2297
2298 /*
2299 * When we use a hash table to store the local names, we use a singly linked
2300 * list to record the indexes of duplicated parameter names to preserve the
2301 * duplicates for the decompiler.
2302 */
2303 typedef struct JSNameIndexPair JSNameIndexPair;
2304
2305 struct JSNameIndexPair {
2306 JSAtom *name;
2307 uint16 index;
2308 JSNameIndexPair *link;
2309 };
2310
2311 struct JSLocalNameMap {
2312 JSDHashTable names;
2313 JSNameIndexPair *lastdup;
2314 };
2315
2316 typedef struct JSLocalNameHashEntry {
2317 JSDHashEntryHdr hdr;
2318 JSAtom *name;
2319 uint16 index;
2320 uint8 localKind;
2321 } JSLocalNameHashEntry;
2322
2323 static void
2324 FreeLocalNameHash(JSContext *cx, JSLocalNameMap *map)
2325 {
2326 JSNameIndexPair *dup, *next;
2327
2328 for (dup = map->lastdup; dup; dup = next) {
2329 next = dup->link;
2330 JS_free(cx, dup);
2331 }
2332 JS_DHashTableFinish(&map->names);
2333 JS_free(cx, map);
2334 }
2335
2336 static JSBool
2337 HashLocalName(JSContext *cx, JSLocalNameMap *map, JSAtom *name,
2338 JSLocalKind localKind, uintN index)
2339 {
2340 JSLocalNameHashEntry *entry;
2341 JSNameIndexPair *dup;
2342
2343 JS_ASSERT(index <= JS_BITMASK(16));
2344 #if JS_HAS_DESTRUCTURING
2345 if (!name) {
2346 /* A destructuring pattern does not need a hash entry. */
2347 JS_ASSERT(localKind == JSLOCAL_ARG);
2348 return JS_TRUE;
2349 }
2350 #endif
2351 JS_ASSERT(ATOM_IS_STRING(name));
2352 entry = (JSLocalNameHashEntry *)
2353 JS_DHashTableOperate(&map->names, name, JS_DHASH_ADD);
2354 if (!entry) {
2355 JS_ReportOutOfMemory(cx);
2356 return JS_FALSE;
2357 }
2358 if (entry->name) {
2359 JS_ASSERT(entry->name == name);
2360 JS_ASSERT(entry->localKind == JSLOCAL_ARG);
2361 dup = (JSNameIndexPair *) JS_malloc(cx, sizeof *dup);
2362 if (!dup)
2363 return JS_FALSE;
2364 dup->name = entry->name;
2365 dup->index = entry->index;
2366 dup->link = map->lastdup;
2367 map->lastdup = dup;
2368 }
2369 entry->name = name;
2370 entry->index = (uint16) index;
2371 entry->localKind = (uint8) localKind;
2372 return JS_TRUE;
2373 }
2374
2375 JSBool
2376 js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind)
2377 {
2378 jsuword taggedAtom;
2379 uint16 *indexp;
2380 uintN n, i;
2381 jsuword *array;
2382 JSLocalNameMap *map;
2383
2384 JS_ASSERT(FUN_INTERPRETED(fun));
2385 JS_ASSERT(!fun->u.i.script);
2386 JS_ASSERT(((jsuword) atom & 1) == 0);
2387 taggedAtom = (jsuword) atom;
2388 if (kind == JSLOCAL_ARG) {
2389 indexp = &fun->nargs;
2390 } else if (kind == JSLOCAL_UPVAR) {
2391 indexp = &fun->u.i.nupvars;
2392 } else {
2393 indexp = &fun->u.i.nvars;
2394 if (kind == JSLOCAL_CONST)
2395 taggedAtom |= 1;
2396 else
2397 JS_ASSERT(kind == JSLOCAL_VAR);
2398 }
2399 n = JS_GET_LOCAL_NAME_COUNT(fun);
2400 if (n == 0) {
2401 JS_ASSERT(fun->u.i.names.taggedAtom == 0);
2402 fun->u.i.names.taggedAtom = taggedAtom;
2403 } else if (n < MAX_ARRAY_LOCALS) {
2404 if (n > 1) {
2405 array = fun->u.i.names.array;
2406 } else {
2407 array = (jsuword *) JS_malloc(cx, MAX_ARRAY_LOCALS * sizeof *array);
2408 if (!array)
2409 return JS_FALSE;
2410 array[0] = fun->u.i.names.taggedAtom;
2411 fun->u.i.names.array = array;
2412 }
2413 if (kind == JSLOCAL_ARG) {
2414 /*
2415 * A destructuring argument pattern adds variables, not arguments,
2416 * so for the following arguments nvars != 0.
2417 */
2418 #if JS_HAS_DESTRUCTURING
2419 if (fun->u.i.nvars != 0) {
2420 memmove(array + fun->nargs + 1, array + fun->nargs,
2421 fun->u.i.nvars * sizeof *array);
2422 }
2423 #else
2424 JS_ASSERT(fun->u.i.nvars == 0);
2425 #endif
2426 array[fun->nargs] = taggedAtom;
2427 } else {
2428 array[n] = taggedAtom;
2429 }
2430 } else if (n == MAX_ARRAY_LOCALS) {
2431 array = fun->u.i.names.array;
2432 map = (JSLocalNameMap *) JS_malloc(cx, sizeof *map);
2433 if (!map)
2434 return JS_FALSE;
2435 if (!JS_DHashTableInit(&map->names, JS_DHashGetStubOps(),
2436 NULL, sizeof(JSLocalNameHashEntry),
2437 JS_DHASH_DEFAULT_CAPACITY(MAX_ARRAY_LOCALS
2438 * 2))) {
2439 JS_ReportOutOfMemory(cx);
2440 JS_free(cx, map);
2441 return JS_FALSE;
2442 }
2443
2444 map->lastdup = NULL;
2445 for (i = 0; i != MAX_ARRAY_LOCALS; ++i) {
2446 taggedAtom = array[i];
2447 if (!HashLocalName(cx, map, (JSAtom *) (taggedAtom & ~1),
2448 (i < fun->nargs)
2449 ? JSLOCAL_ARG
2450 : (taggedAtom & 1) ? JSLOCAL_CONST : JSLOCAL_VAR,
2451 (i < fun->nargs) ? i : i - fun->nargs)) {
2452 FreeLocalNameHash(cx, map);
2453 return JS_FALSE;
2454 }
2455 }
2456 if (!HashLocalName(cx, map, atom, kind, *indexp)) {
2457 FreeLocalNameHash(cx, map);
2458 return JS_FALSE;
2459 }
2460
2461 /*
2462 * At this point the entry is added and we cannot fail. It is time
2463 * to replace fun->u.i.names with the built map.
2464 */
2465 fun->u.i.names.map = map;
2466 JS_free(cx, array);
2467 } else {
2468 if (*indexp == JS_BITMASK(16)) {
2469 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2470 (kind == JSLOCAL_ARG)
2471 ? JSMSG_TOO_MANY_FUN_ARGS
2472 : JSMSG_TOO_MANY_LOCALS);
2473 return JS_FALSE;
2474 }
2475 if (!HashLocalName(cx, fun->u.i.names.map, atom, kind, *indexp))
2476 return JS_FALSE;
2477 }
2478
2479 /* Update the argument or variable counter. */
2480 ++*indexp;
2481 return JS_TRUE;
2482 }
2483
2484 JSLocalKind
2485 js_LookupLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, uintN *indexp)
2486 {
2487 uintN n, i, upvar_base;
2488 jsuword *array;
2489 JSLocalNameHashEntry *entry;
2490
2491 JS_ASSERT(FUN_INTERPRETED(fun));
2492 n = JS_GET_LOCAL_NAME_COUNT(fun);
2493 if (n == 0)
2494 return JSLOCAL_NONE;
2495 if (n <= MAX_ARRAY_LOCALS) {
2496 array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
2497
2498 /* Search from the tail to pick up the last duplicated name. */
2499 i = n;
2500 upvar_base = JS_UPVAR_LOCAL_NAME_START(fun);
2501 do {
2502 --i;
2503 if (atom == JS_LOCAL_NAME_TO_ATOM(array[i])) {
2504 if (i < fun->nargs) {
2505 if (indexp)
2506 *indexp = i;
2507 return JSLOCAL_ARG;
2508 }
2509 if (i >= upvar_base) {
2510 if (indexp)
2511 *indexp = i - upvar_base;
2512 return JSLOCAL_UPVAR;
2513 }
2514 if (indexp)
2515 *indexp = i - fun->nargs;
2516 return JS_LOCAL_NAME_IS_CONST(array[i])
2517 ? JSLOCAL_CONST
2518 : JSLOCAL_VAR;
2519 }
2520 } while (i != 0);
2521 } else {
2522 entry = (JSLocalNameHashEntry *)
2523 JS_DHashTableOperate(&fun->u.i.names.map->names, atom,
2524 JS_DHASH_LOOKUP);
2525 if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) {
2526 JS_ASSERT(entry->localKind != JSLOCAL_NONE);
2527 if (indexp)
2528 *indexp = entry->index;
2529 return (JSLocalKind) entry->localKind;
2530 }
2531 }
2532 return JSLOCAL_NONE;
2533 }
2534
2535 typedef struct JSLocalNameEnumeratorArgs {
2536 JSFunction *fun;
2537 jsuword *names;
2538 #ifdef DEBUG
2539 uintN nCopiedArgs;
2540 uintN nCopiedVars;
2541 #endif
2542 } JSLocalNameEnumeratorArgs;
2543
2544 static JSDHashOperator
2545 get_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
2546 uint32 number, void *arg)
2547 {
2548 JSLocalNameHashEntry *entry;
2549 JSLocalNameEnumeratorArgs *args;
2550 uint i;
2551 jsuword constFlag;
2552
2553 entry = (JSLocalNameHashEntry *) hdr;
2554 args = (JSLocalNameEnumeratorArgs *) arg;
2555 JS_ASSERT(entry->name);
2556 if (entry->localKind == JSLOCAL_ARG) {
2557 JS_ASSERT(entry->index < args->fun->nargs);
2558 JS_ASSERT(args->nCopiedArgs++ < args->fun->nargs);
2559 i = entry->index;
2560 constFlag = 0;
2561 } else {
2562 JS_ASSERT(entry->localKind == JSLOCAL_VAR ||
2563 entry->localKind == JSLOCAL_CONST);
2564 JS_ASSERT(entry->index < args->fun->u.i.nvars);
2565 JS_ASSERT(args->nCopiedVars++ < args->fun->u.i.nvars);
2566 i = args->fun->nargs + entry->index;
2567 constFlag = (entry->localKind == JSLOCAL_CONST);
2568 }
2569 args->names[i] = (jsuword) entry->name | constFlag;
2570 return JS_DHASH_NEXT;
2571 }
2572
2573 jsuword *
2574 js_GetLocalNameArray(JSContext *cx, JSFunction *fun, JSArenaPool *pool)
2575 {
2576 uintN n;
2577 jsuword *names;
2578 JSLocalNameMap *map;
2579 JSLocalNameEnumeratorArgs args;
2580 JSNameIndexPair *dup;
2581
2582 JS_ASSERT(FUN_INTERPRETED(fun));
2583 n = JS_GET_LOCAL_NAME_COUNT(fun);
2584 JS_ASSERT(n != 0);
2585
2586 if (n <= MAX_ARRAY_LOCALS)
2587 return (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
2588
2589 /*
2590 * No need to check for overflow of the allocation size as we are making a
2591 * copy of already allocated data. As such it must fit size_t.
2592 */
2593 JS_ARENA_ALLOCATE_CAST(names, jsuword *, pool, (size_t) n * sizeof *names);
2594 if (!names) {
2595 js_ReportOutOfScriptQuota(cx);
2596 return NULL;
2597 }
2598
2599 #if JS_HAS_DESTRUCTURING
2600 /* Some parameter names can be NULL due to destructuring patterns. */
2601 memset(names, 0, fun->nargs * sizeof *names);
2602 #endif
2603 map = fun->u.i.names.map;
2604 args.fun = fun;
2605 args.names = names;
2606 #ifdef DEBUG
2607 args.nCopiedArgs = 0;
2608 args.nCopiedVars = 0;
2609 #endif
2610 JS_DHashTableEnumerate(&map->names, get_local_names_enumerator, &args);
2611 for (dup = map->lastdup; dup; dup = dup->link) {
2612 JS_ASSERT(dup->index < fun->nargs);
2613 JS_ASSERT(args.nCopiedArgs++ < fun->nargs);
2614 names[dup->index] = (jsuword) dup->name;
2615 }
2616 #if !JS_HAS_DESTRUCTURING
2617 JS_ASSERT(args.nCopiedArgs == fun->nargs);
2618 #endif
2619 JS_ASSERT(args.nCopiedVars == fun->u.i.nvars);
2620
2621 return names;
2622 }
2623
2624 static JSDHashOperator
2625 trace_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
2626 uint32 number, void *arg)
2627 {
2628 JSLocalNameHashEntry *entry;
2629 JSTracer *trc;
2630
2631 entry = (JSLocalNameHashEntry *) hdr;
2632 JS_ASSERT(entry->name);
2633 trc = (JSTracer *) arg;
2634 JS_SET_TRACING_INDEX(trc,
2635 entry->localKind == JSLOCAL_ARG ? "arg" : "var",
2636 entry->index);
2637 JS_CallTracer(trc, ATOM_TO_STRING(entry->name), JSTRACE_STRING);
2638 return JS_DHASH_NEXT;
2639 }
2640
2641 static void
2642 TraceLocalNames(JSTracer *trc, JSFunction *fun)
2643 {
2644 uintN n, i;
2645 JSAtom *atom;
2646 jsuword *array;
2647
2648 JS_ASSERT(FUN_INTERPRETED(fun));
2649 n = JS_GET_LOCAL_NAME_COUNT(fun);
2650 if (n == 0)
2651 return;
2652 if (n <= MAX_ARRAY_LOCALS) {
2653 array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
2654 i = n;
2655 do {
2656 --i;
2657 atom = (JSAtom *) (array[i] & ~1);
2658 if (atom) {
2659 JS_SET_TRACING_INDEX(trc,
2660 i < fun->nargs ? "arg" : "var",
2661 i < fun->nargs ? i : i - fun->nargs);
2662 JS_CallTracer(trc, ATOM_TO_STRING(atom), JSTRACE_STRING);
2663 }
2664 } while (i != 0);
2665 } else {
2666 JS_DHashTableEnumerate(&fun->u.i.names.map->names,
2667 trace_local_names_enumerator, trc);
2668
2669 /*
2670 * No need to trace the list of duplicates in map->lastdup as the
2671 * names there are traced when enumerating the hash table.
2672 */
2673 }
2674 }
2675
2676 void
2677 DestroyLocalNames(JSContext *cx, JSFunction *fun)
2678 {
2679 uintN n;
2680
2681 n = fun->nargs + fun->u.i.nvars;
2682 if (n <= 1)
2683 return;
2684 if (n <= MAX_ARRAY_LOCALS)
2685 JS_free(cx, fun->u.i.names.array);
2686 else
2687 FreeLocalNameHash(cx, fun->u.i.names.map);
2688 }
2689
2690 void
2691 js_FreezeLocalNames(JSContext *cx, JSFunction *fun)
2692 {
2693 uintN n;
2694 jsuword *array;
2695
2696 JS_ASSERT(FUN_INTERPRETED(fun));
2697 JS_ASSERT(!fun->u.i.script);
2698 n = fun->nargs + fun->u.i.nvars + fun->u.i.nupvars;
2699 if (2 <= n && n < MAX_ARRAY_LOCALS) {
2700 /* Shrink over-allocated array ignoring realloc failures. */
2701 array = (jsuword *) JS_realloc(cx, fun->u.i.names.array,
2702 n * sizeof *array);
2703 if (array)
2704 fun->u.i.names.array = array;
2705 }
2706 }

  ViewVC Help
Powered by ViewVC 1.1.24