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

Contents of /trunk/js/jsfun.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (show annotations)
Thu Oct 23 19:03:33 2008 UTC (10 years, 11 months ago) by siliconforks
File size: 83241 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


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

  ViewVC Help
Powered by ViewVC 1.1.24