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

Contents of /trunk/js/jsfun.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 460 - (show annotations)
Sat Sep 26 23:15:22 2009 UTC (11 years ago) by siliconforks
File size: 98989 byte(s)
Upgrade to SpiderMonkey from Firefox 3.5.3.

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 JS_REQUIRES_STACK JSObject *
369 WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSObject *funobj, JSFunction *fun)
370 {
371 JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun);
372 JS_ASSERT(fun->optimizedClosure());
373 JS_ASSERT(!fun->u.i.wrapper);
374
375 /*
376 * We do not attempt to reify Call and Block objects on demand for outer
377 * scopes. This could be done (see the "v8" patch in bug 494235) but it is
378 * fragile in the face of ongoing compile-time optimization. Instead, the
379 * _DBG* opcodes used by wrappers created here must cope with unresolved
380 * upvars and throw them as reference errors. Caveat debuggers!
381 */
382 JSObject *scopeChain = js_GetScopeChain(cx, fp);
383 if (!scopeChain)
384 return NULL;
385
386 JSObject *wfunobj = js_NewObjectWithGivenProto(cx, &js_FunctionClass,
387 funobj, scopeChain, 0);
388 if (!wfunobj)
389 return NULL;
390 JSAutoTempValueRooter tvr(cx, wfunobj);
391
392 JSFunction *wfun = (JSFunction *) wfunobj;
393 wfunobj->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(wfun);
394 wfun->nargs = 0;
395 wfun->flags = fun->flags | JSFUN_HEAVYWEIGHT;
396 wfun->u.i.nvars = 0;
397 wfun->u.i.nupvars = 0;
398 wfun->u.i.skipmin = fun->u.i.skipmin;
399 wfun->u.i.wrapper = true;
400 wfun->u.i.script = NULL;
401 wfun->u.i.names.taggedAtom = NULL;
402 wfun->atom = fun->atom;
403
404 if (fun->hasLocalNames()) {
405 void *mark = JS_ARENA_MARK(&cx->tempPool);
406 jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
407 if (!names)
408 return NULL;
409
410 JSBool ok = true;
411 for (uintN i = 0, n = fun->countLocalNames(); i != n; i++) {
412 jsuword name = names[i];
413 JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(name);
414 JSLocalKind localKind = (i < fun->nargs)
415 ? JSLOCAL_ARG
416 : (i < fun->countArgsAndVars())
417 ? (JS_LOCAL_NAME_IS_CONST(name)
418 ? JSLOCAL_CONST
419 : JSLOCAL_VAR)
420 : JSLOCAL_UPVAR;
421
422 ok = js_AddLocal(cx, wfun, atom, localKind);
423 if (!ok)
424 break;
425 }
426
427 JS_ARENA_RELEASE(&cx->tempPool, mark);
428 if (!ok)
429 return NULL;
430 JS_ASSERT(wfun->nargs == fun->nargs);
431 JS_ASSERT(wfun->u.i.nvars == fun->u.i.nvars);
432 JS_ASSERT(wfun->u.i.nupvars == fun->u.i.nupvars);
433 js_FreezeLocalNames(cx, wfun);
434 }
435
436 JSScript *script = fun->u.i.script;
437 jssrcnote *snbase = SCRIPT_NOTES(script);
438 jssrcnote *sn = snbase;
439 while (!SN_IS_TERMINATOR(sn))
440 sn = SN_NEXT(sn);
441 uintN nsrcnotes = (sn - snbase) + 1;
442
443 /* NB: GC must not occur before wscript is homed in wfun->u.i.script. */
444 JSScript *wscript = js_NewScript(cx, script->length, nsrcnotes,
445 script->atomMap.length,
446 (script->objectsOffset != 0)
447 ? JS_SCRIPT_OBJECTS(script)->length
448 : 0,
449 fun->u.i.nupvars,
450 (script->regexpsOffset != 0)
451 ? JS_SCRIPT_REGEXPS(script)->length
452 : 0,
453 (script->trynotesOffset != 0)
454 ? JS_SCRIPT_TRYNOTES(script)->length
455 : 0);
456 if (!wscript)
457 return NULL;
458
459 memcpy(wscript->code, script->code, script->length);
460 wscript->main = wscript->code + (script->main - script->code);
461
462 memcpy(SCRIPT_NOTES(wscript), snbase, nsrcnotes);
463 memcpy(wscript->atomMap.vector, script->atomMap.vector,
464 wscript->atomMap.length * sizeof(JSAtom *));
465 if (script->objectsOffset != 0) {
466 memcpy(JS_SCRIPT_OBJECTS(wscript)->vector, JS_SCRIPT_OBJECTS(script)->vector,
467 JS_SCRIPT_OBJECTS(wscript)->length * sizeof(JSObject *));
468 }
469 if (script->regexpsOffset != 0) {
470 memcpy(JS_SCRIPT_REGEXPS(wscript)->vector, JS_SCRIPT_REGEXPS(script)->vector,
471 JS_SCRIPT_REGEXPS(wscript)->length * sizeof(JSObject *));
472 }
473 if (script->trynotesOffset != 0) {
474 memcpy(JS_SCRIPT_TRYNOTES(wscript)->vector, JS_SCRIPT_TRYNOTES(script)->vector,
475 JS_SCRIPT_TRYNOTES(wscript)->length * sizeof(JSTryNote));
476 }
477
478 if (wfun->u.i.nupvars != 0) {
479 JS_ASSERT(wfun->u.i.nupvars == JS_SCRIPT_UPVARS(wscript)->length);
480 memcpy(JS_SCRIPT_UPVARS(wscript)->vector, JS_SCRIPT_UPVARS(script)->vector,
481 wfun->u.i.nupvars * sizeof(uint32));
482 }
483
484 jsbytecode *pc = wscript->code;
485 while (*pc != JSOP_STOP) {
486 /* XYZZYbe should copy JSOP_TRAP? */
487 JSOp op = js_GetOpcode(cx, wscript, pc);
488 const JSCodeSpec *cs = &js_CodeSpec[op];
489 ptrdiff_t oplen = cs->length;
490 if (oplen < 0)
491 oplen = js_GetVariableBytecodeLength(pc);
492
493 /*
494 * Rewrite JSOP_{GET,CALL}DSLOT as JSOP_{GET,CALL}UPVAR_DBG for the
495 * case where fun is an escaping flat closure. This works because the
496 * UPVAR and DSLOT ops by design have the same format: an upvar index
497 * immediate operand.
498 */
499 switch (op) {
500 case JSOP_GETUPVAR: *pc = JSOP_GETUPVAR_DBG; break;
501 case JSOP_CALLUPVAR: *pc = JSOP_CALLUPVAR_DBG; break;
502 case JSOP_GETDSLOT: *pc = JSOP_GETUPVAR_DBG; break;
503 case JSOP_CALLDSLOT: *pc = JSOP_CALLUPVAR_DBG; break;
504 case JSOP_DEFFUN_FC: *pc = JSOP_DEFFUN_DBGFC; break;
505 case JSOP_DEFLOCALFUN_FC: *pc = JSOP_DEFLOCALFUN_DBGFC; break;
506 case JSOP_LAMBDA_FC: *pc = JSOP_LAMBDA_DBGFC; break;
507 default:;
508 }
509 pc += oplen;
510 }
511
512 /*
513 * Fill in the rest of wscript. This means if you add members to JSScript
514 * you must update this code. FIXME: factor into JSScript::clone method.
515 */
516 wscript->flags = script->flags;
517 wscript->version = script->version;
518 wscript->nfixed = script->nfixed;
519 wscript->filename = script->filename;
520 wscript->lineno = script->lineno;
521 wscript->nslots = script->nslots;
522 wscript->staticLevel = script->staticLevel;
523 wscript->principals = script->principals;
524 if (wscript->principals)
525 JSPRINCIPALS_HOLD(cx, wscript->principals);
526 #ifdef CHECK_SCRIPT_OWNER
527 wscript->owner = script->owner;
528 #endif
529
530 /* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */
531 FUN_SET_KIND(wfun, JSFUN_INTERPRETED);
532 wfun->u.i.script = wscript;
533 return wfunobj;
534 }
535
536 static JSBool
537 args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
538 {
539 jsint slot;
540 JSStackFrame *fp;
541
542 if (!JSVAL_IS_INT(id))
543 return JS_TRUE;
544 fp = (JSStackFrame *)
545 JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
546 if (!fp)
547 return JS_TRUE;
548 JS_ASSERT(fp->argsobj);
549
550 slot = JSVAL_TO_INT(id);
551 switch (slot) {
552 case ARGS_CALLEE:
553 if (!TEST_OVERRIDE_BIT(fp, slot)) {
554 /*
555 * If this function or one in it needs upvars that reach above it
556 * in the scope chain, it must not be a null closure (it could be a
557 * flat closure, or an unoptimized closure -- the latter itself not
558 * necessarily heavyweight). Rather than wrap here, we simply throw
559 * to reduce code size and tell debugger users the truth instead of
560 * passing off a fibbing wrapper.
561 */
562 if (fp->fun->needsWrapper()) {
563 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
564 JSMSG_OPTIMIZED_CLOSURE_LEAK);
565 return JS_FALSE;
566 }
567 *vp = OBJECT_TO_JSVAL(fp->callee);
568 }
569 break;
570
571 case ARGS_LENGTH:
572 if (!TEST_OVERRIDE_BIT(fp, slot))
573 *vp = INT_TO_JSVAL((jsint)fp->argc);
574 break;
575
576 default:
577 if ((uintN)slot < fp->argc && !ArgWasDeleted(cx, fp, slot))
578 *vp = fp->argv[slot];
579 break;
580 }
581 return JS_TRUE;
582 }
583
584 static JSBool
585 args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
586 {
587 JSStackFrame *fp;
588 jsint slot;
589
590 if (!JSVAL_IS_INT(id))
591 return JS_TRUE;
592 fp = (JSStackFrame *)
593 JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
594 if (!fp)
595 return JS_TRUE;
596 JS_ASSERT(fp->argsobj);
597
598 slot = JSVAL_TO_INT(id);
599 switch (slot) {
600 case ARGS_CALLEE:
601 case ARGS_LENGTH:
602 SET_OVERRIDE_BIT(fp, slot);
603 break;
604
605 default:
606 if (FUN_INTERPRETED(fp->fun) &&
607 (uintN)slot < fp->argc &&
608 !ArgWasDeleted(cx, fp, slot)) {
609 fp->argv[slot] = *vp;
610 }
611 break;
612 }
613 return JS_TRUE;
614 }
615
616 static JSBool
617 args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
618 JSObject **objp)
619 {
620 JSStackFrame *fp;
621 uintN slot;
622 JSString *str;
623 JSAtom *atom;
624 intN tinyid;
625 jsval value;
626
627 *objp = NULL;
628 fp = (JSStackFrame *)
629 JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
630 if (!fp)
631 return JS_TRUE;
632 JS_ASSERT(fp->argsobj);
633
634 if (JSVAL_IS_INT(id)) {
635 slot = JSVAL_TO_INT(id);
636 if (slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) {
637 /* XXX ECMA specs DontEnum, contrary to other array-like objects */
638 if (!js_DefineProperty(cx, obj, INT_JSVAL_TO_JSID(id),
639 fp->argv[slot],
640 args_getProperty, args_setProperty,
641 0, NULL)) {
642 return JS_FALSE;
643 }
644 *objp = obj;
645 }
646 } else if (JSVAL_IS_STRING(id)) {
647 str = JSVAL_TO_STRING(id);
648 atom = cx->runtime->atomState.lengthAtom;
649 if (str == ATOM_TO_STRING(atom)) {
650 tinyid = ARGS_LENGTH;
651 value = INT_TO_JSVAL(fp->argc);
652 } else {
653 atom = cx->runtime->atomState.calleeAtom;
654 if (str == ATOM_TO_STRING(atom)) {
655 tinyid = ARGS_CALLEE;
656 value = OBJECT_TO_JSVAL(fp->callee);
657 } else {
658 atom = NULL;
659
660 /* Quell GCC overwarnings. */
661 tinyid = 0;
662 value = JSVAL_NULL;
663 }
664 }
665
666 if (atom && !TEST_OVERRIDE_BIT(fp, tinyid)) {
667 if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value,
668 args_getProperty, args_setProperty, 0,
669 SPROP_HAS_SHORTID, tinyid, NULL)) {
670 return JS_FALSE;
671 }
672 *objp = obj;
673 }
674 }
675
676 return JS_TRUE;
677 }
678
679 static JSBool
680 args_enumerate(JSContext *cx, JSObject *obj)
681 {
682 JSStackFrame *fp;
683 JSObject *pobj;
684 JSProperty *prop;
685 uintN slot, argc;
686
687 fp = (JSStackFrame *)
688 JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
689 if (!fp)
690 return JS_TRUE;
691 JS_ASSERT(fp->argsobj);
692
693 /*
694 * Trigger reflection with value snapshot in args_resolve using a series
695 * of js_LookupProperty calls. We handle length, callee, and the indexed
696 * argument properties. We know that args_resolve covers all these cases
697 * and creates direct properties of obj, but that it may fail to resolve
698 * length or callee if overridden.
699 */
700 if (!js_LookupProperty(cx, obj,
701 ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
702 &pobj, &prop)) {
703 return JS_FALSE;
704 }
705 if (prop)
706 OBJ_DROP_PROPERTY(cx, pobj, prop);
707
708 if (!js_LookupProperty(cx, obj,
709 ATOM_TO_JSID(cx->runtime->atomState.calleeAtom),
710 &pobj, &prop)) {
711 return JS_FALSE;
712 }
713 if (prop)
714 OBJ_DROP_PROPERTY(cx, pobj, prop);
715
716 argc = fp->argc;
717 for (slot = 0; slot < argc; slot++) {
718 if (!js_LookupProperty(cx, obj, INT_TO_JSID((jsint)slot), &pobj, &prop))
719 return JS_FALSE;
720 if (prop)
721 OBJ_DROP_PROPERTY(cx, pobj, prop);
722 }
723 return JS_TRUE;
724 }
725
726 #if JS_HAS_GENERATORS
727 /*
728 * If a generator-iterator's arguments or call object escapes, it needs to
729 * mark its generator object.
730 */
731 static void
732 args_or_call_trace(JSTracer *trc, JSObject *obj)
733 {
734 JSStackFrame *fp;
735
736 fp = (JSStackFrame *) JS_GetPrivate(trc->context, obj);
737 if (fp && (fp->flags & JSFRAME_GENERATOR)) {
738 JS_CALL_OBJECT_TRACER(trc, FRAME_TO_GENERATOR(fp)->obj,
739 "FRAME_TO_GENERATOR(fp)->obj");
740 }
741 }
742 #else
743 # define args_or_call_trace NULL
744 #endif
745
746 /*
747 * The Arguments class is not initialized via JS_InitClass, and must not be,
748 * because its name is "Object". Per ECMA, that causes instances of it to
749 * delegate to the object named by Object.prototype. It also ensures that
750 * arguments.toString() returns "[object Object]".
751 *
752 * The JSClass functions below collaborate to lazily reflect and synchronize
753 * actual argument values, argument count, and callee function object stored
754 * in a JSStackFrame with their corresponding property values in the frame's
755 * arguments object.
756 */
757 JSClass js_ArgumentsClass = {
758 js_Object_str,
759 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) |
760 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
761 JS_PropertyStub, args_delProperty,
762 args_getProperty, args_setProperty,
763 args_enumerate, (JSResolveOp) args_resolve,
764 JS_ConvertStub, JS_FinalizeStub,
765 NULL, NULL,
766 NULL, NULL,
767 NULL, NULL,
768 JS_CLASS_TRACE(args_or_call_trace), NULL
769 };
770
771 #define JSSLOT_CALLEE (JSSLOT_PRIVATE + 1)
772 #define JSSLOT_CALL_ARGUMENTS (JSSLOT_PRIVATE + 2)
773 #define CALL_CLASS_FIXED_RESERVED_SLOTS 2
774
775 /*
776 * A Declarative Environment object stores its active JSStackFrame pointer in
777 * its private slot, just as Call and Arguments objects do.
778 */
779 JSClass js_DeclEnvClass = {
780 js_Object_str,
781 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
782 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
783 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
784 JSCLASS_NO_OPTIONAL_MEMBERS
785 };
786
787 static JS_REQUIRES_STACK JSBool
788 CheckForEscapingClosure(JSContext *cx, JSObject *obj, jsval *vp)
789 {
790 JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass ||
791 STOBJ_GET_CLASS(obj) == &js_DeclEnvClass);
792
793 jsval v = *vp;
794
795 if (VALUE_IS_FUNCTION(cx, v)) {
796 JSObject *funobj = JSVAL_TO_OBJECT(v);
797 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
798
799 /*
800 * Any escaping null or flat closure that reaches above itself or
801 * contains nested functions that reach above it must be wrapped.
802 * We can wrap only when this Call or Declarative Environment obj
803 * still has an active stack frame associated with it.
804 */
805 if (fun->needsWrapper()) {
806 JSStackFrame *fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
807 if (fp) {
808 JSObject *wrapper = WrapEscapingClosure(cx, fp, funobj, fun);
809 if (!wrapper)
810 return false;
811 *vp = OBJECT_TO_JSVAL(wrapper);
812 return true;
813 }
814
815 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
816 JSMSG_OPTIMIZED_CLOSURE_LEAK);
817 return false;
818 }
819 }
820 return true;
821 }
822
823 static JS_REQUIRES_STACK JSBool
824 CalleeGetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
825 {
826 return CheckForEscapingClosure(cx, obj, vp);
827 }
828
829 JSObject *
830 js_GetCallObject(JSContext *cx, JSStackFrame *fp)
831 {
832 JSObject *callobj;
833
834 /* Create a call object for fp only if it lacks one. */
835 JS_ASSERT(fp->fun);
836 callobj = fp->callobj;
837 if (callobj)
838 return callobj;
839
840 #ifdef DEBUG
841 /* A call object should be a frame's outermost scope chain element. */
842 JSClass *classp = OBJ_GET_CLASS(cx, fp->scopeChain);
843 if (classp == &js_WithClass || classp == &js_BlockClass || classp == &js_CallClass)
844 JS_ASSERT(OBJ_GET_PRIVATE(cx, fp->scopeChain) != fp);
845 #endif
846
847 /*
848 * Create the call object, using the frame's enclosing scope as its
849 * parent, and link the call to its stack frame. For a named function
850 * expression Call's parent points to an environment object holding
851 * function's name.
852 */
853 JSAtom *lambdaName = (fp->fun->flags & JSFUN_LAMBDA) ? fp->fun->atom : NULL;
854 if (lambdaName) {
855 JSObject *env = js_NewObjectWithGivenProto(cx, &js_DeclEnvClass, NULL,
856 fp->scopeChain, 0);
857 if (!env)
858 return NULL;
859 env->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fp);
860
861 /* Root env before js_DefineNativeProperty (-> JSClass.addProperty). */
862 fp->scopeChain = env;
863 if (!js_DefineNativeProperty(cx, fp->scopeChain, ATOM_TO_JSID(lambdaName),
864 OBJECT_TO_JSVAL(fp->callee),
865 CalleeGetter, NULL,
866 JSPROP_PERMANENT | JSPROP_READONLY,
867 0, 0, NULL)) {
868 return NULL;
869 }
870 }
871
872 callobj = js_NewObjectWithGivenProto(cx, &js_CallClass, NULL,
873 fp->scopeChain, 0);
874 if (!callobj)
875 return NULL;
876
877 JS_SetPrivate(cx, callobj, fp);
878 JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, fp->callee));
879 STOBJ_SET_SLOT(callobj, JSSLOT_CALLEE, OBJECT_TO_JSVAL(fp->callee));
880 fp->callobj = callobj;
881
882 /*
883 * Push callobj on the top of the scope chain, and make it the
884 * variables object.
885 */
886 fp->scopeChain = callobj;
887 fp->varobj = callobj;
888 return callobj;
889 }
890
891 static JSFunction *
892 GetCallObjectFunction(JSObject *obj)
893 {
894 jsval v;
895
896 JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass);
897 v = STOBJ_GET_SLOT(obj, JSSLOT_CALLEE);
898 if (JSVAL_IS_VOID(v)) {
899 /* Newborn or prototype object. */
900 return NULL;
901 }
902 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
903 return GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v));
904 }
905
906 JS_FRIEND_API(JSBool)
907 js_PutCallObject(JSContext *cx, JSStackFrame *fp)
908 {
909 JSObject *callobj;
910 JSBool ok;
911 JSFunction *fun;
912 uintN n;
913 JSScope *scope;
914
915 /*
916 * Since for a call object all fixed slots happen to be taken, we can copy
917 * arguments and variables straight into JSObject.dslots.
918 */
919 JS_STATIC_ASSERT(JS_INITIAL_NSLOTS - JSSLOT_PRIVATE ==
920 1 + CALL_CLASS_FIXED_RESERVED_SLOTS);
921
922 callobj = fp->callobj;
923 if (!callobj)
924 return JS_TRUE;
925
926 /*
927 * Get the arguments object to snapshot fp's actual argument values.
928 */
929 ok = JS_TRUE;
930 if (fp->argsobj) {
931 if (!TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
932 STOBJ_SET_SLOT(callobj, JSSLOT_CALL_ARGUMENTS,
933 OBJECT_TO_JSVAL(fp->argsobj));
934 }
935 ok &= js_PutArgsObject(cx, fp);
936 }
937
938 fun = fp->fun;
939 JS_ASSERT(fun == GetCallObjectFunction(callobj));
940 n = fun->countArgsAndVars();
941 if (n != 0) {
942 JS_LOCK_OBJ(cx, callobj);
943 n += JS_INITIAL_NSLOTS;
944 if (n > STOBJ_NSLOTS(callobj))
945 ok &= js_ReallocSlots(cx, callobj, n, JS_TRUE);
946 scope = OBJ_SCOPE(callobj);
947 if (ok) {
948 memcpy(callobj->dslots, fp->argv, fun->nargs * sizeof(jsval));
949 memcpy(callobj->dslots + fun->nargs, fp->slots,
950 fun->u.i.nvars * sizeof(jsval));
951 if (scope->object == callobj && n > scope->freeslot)
952 scope->freeslot = n;
953 }
954 JS_UNLOCK_SCOPE(cx, scope);
955 }
956
957 /*
958 * Clear private pointers to fp, which is about to go away (js_Invoke).
959 * Do this last because js_GetProperty calls above need to follow the
960 * call object's private slot to find fp.
961 */
962 if ((fun->flags & JSFUN_LAMBDA) && fun->atom) {
963 JSObject *env = STOBJ_GET_PARENT(callobj);
964
965 JS_ASSERT(STOBJ_GET_CLASS(env) == &js_DeclEnvClass);
966 JS_ASSERT(STOBJ_GET_PRIVATE(env) == fp);
967 JS_SetPrivate(cx, env, NULL);
968 }
969
970 JS_SetPrivate(cx, callobj, NULL);
971 fp->callobj = NULL;
972 return ok;
973 }
974
975 static JSBool
976 call_enumerate(JSContext *cx, JSObject *obj)
977 {
978 JSFunction *fun;
979 uintN n, i;
980 void *mark;
981 jsuword *names;
982 JSBool ok;
983 JSAtom *name;
984 JSObject *pobj;
985 JSProperty *prop;
986
987 fun = GetCallObjectFunction(obj);
988 n = fun ? fun->countArgsAndVars() : 0;
989 if (n == 0)
990 return JS_TRUE;
991
992 mark = JS_ARENA_MARK(&cx->tempPool);
993
994 MUST_FLOW_THROUGH("out");
995 names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
996 if (!names) {
997 ok = JS_FALSE;
998 goto out;
999 }
1000
1001 for (i = 0; i != n; ++i) {
1002 name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1003 if (!name)
1004 continue;
1005
1006 /*
1007 * Trigger reflection by looking up the name of the argument or
1008 * variable.
1009 */
1010 ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(name), &pobj, &prop);
1011 if (!ok)
1012 goto out;
1013
1014 /*
1015 * The call object will always have a property corresponding to the
1016 * argument or variable name because call_resolve creates the property
1017 * using JSPROP_PERMANENT.
1018 */
1019 JS_ASSERT(prop);
1020 JS_ASSERT(pobj == obj);
1021 OBJ_DROP_PROPERTY(cx, pobj, prop);
1022 }
1023 ok = JS_TRUE;
1024
1025 out:
1026 JS_ARENA_RELEASE(&cx->tempPool, mark);
1027 return ok;
1028 }
1029
1030 typedef enum JSCallPropertyKind {
1031 JSCPK_ARGUMENTS,
1032 JSCPK_ARG,
1033 JSCPK_VAR
1034 } JSCallPropertyKind;
1035
1036 static JSBool
1037 CallPropertyOp(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
1038 JSCallPropertyKind kind, JSBool setter)
1039 {
1040 JSFunction *fun;
1041 JSStackFrame *fp;
1042 uintN i;
1043 jsval *array;
1044
1045 if (STOBJ_GET_CLASS(obj) != &js_CallClass)
1046 return JS_TRUE;
1047
1048 fun = GetCallObjectFunction(obj);
1049 fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
1050
1051 if (kind == JSCPK_ARGUMENTS) {
1052 if (setter) {
1053 if (fp)
1054 SET_OVERRIDE_BIT(fp, CALL_ARGUMENTS);
1055 STOBJ_SET_SLOT(obj, JSSLOT_CALL_ARGUMENTS, *vp);
1056 } else {
1057 if (fp && !TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
1058 JSObject *argsobj;
1059
1060 argsobj = js_GetArgsObject(cx, fp);
1061 if (!argsobj)
1062 return JS_FALSE;
1063 *vp = OBJECT_TO_JSVAL(argsobj);
1064 } else {
1065 *vp = STOBJ_GET_SLOT(obj, JSSLOT_CALL_ARGUMENTS);
1066 }
1067 }
1068 return JS_TRUE;
1069 }
1070
1071 JS_ASSERT((int16) JSVAL_TO_INT(id) == JSVAL_TO_INT(id));
1072 i = (uint16) JSVAL_TO_INT(id);
1073 JS_ASSERT_IF(kind == JSCPK_ARG, i < fun->nargs);
1074 JS_ASSERT_IF(kind == JSCPK_VAR, i < fun->u.i.nvars);
1075
1076 if (!fp) {
1077 i += CALL_CLASS_FIXED_RESERVED_SLOTS;
1078 if (kind == JSCPK_VAR)
1079 i += fun->nargs;
1080 else
1081 JS_ASSERT(kind == JSCPK_ARG);
1082 return setter
1083 ? JS_SetReservedSlot(cx, obj, i, *vp)
1084 : JS_GetReservedSlot(cx, obj, i, vp);
1085 }
1086
1087 if (kind == JSCPK_ARG) {
1088 array = fp->argv;
1089 } else {
1090 JS_ASSERT(kind == JSCPK_VAR);
1091 array = fp->slots;
1092 }
1093 if (setter) {
1094 GC_POKE(cx, array[i]);
1095 array[i] = *vp;
1096 } else {
1097 *vp = array[i];
1098 }
1099 return JS_TRUE;
1100 }
1101
1102 static JSBool
1103 GetCallArguments(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1104 {
1105 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARGUMENTS, JS_FALSE);
1106 }
1107
1108 static JSBool
1109 SetCallArguments(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1110 {
1111 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARGUMENTS, JS_TRUE);
1112 }
1113
1114 JSBool
1115 js_GetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1116 {
1117 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG, JS_FALSE);
1118 }
1119
1120 static JSBool
1121 SetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1122 {
1123 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG, JS_TRUE);
1124 }
1125
1126 JSBool
1127 js_GetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1128 {
1129 return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_FALSE);
1130 }
1131
1132 JSBool
1133 js_GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1134 {
1135 if (!CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_FALSE))
1136 return JS_FALSE;
1137
1138 return CheckForEscapingClosure(cx, obj, vp);
1139 }
1140
1141 static JSBool
1142 SetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1143 {
1144 return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_TRUE);
1145 }
1146
1147 static JSBool
1148 call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags,
1149 JSObject **objp)
1150 {
1151 jsval callee;
1152 JSFunction *fun;
1153 jsid id;
1154 JSLocalKind localKind;
1155 JSPropertyOp getter, setter;
1156 uintN slot, attrs;
1157
1158 JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass);
1159 JS_ASSERT(!STOBJ_GET_PROTO(obj));
1160
1161 if (!JSVAL_IS_STRING(idval))
1162 return JS_TRUE;
1163
1164 callee = STOBJ_GET_SLOT(obj, JSSLOT_CALLEE);
1165 if (JSVAL_IS_VOID(callee))
1166 return JS_TRUE;
1167 fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(callee));
1168
1169 if (!js_ValueToStringId(cx, idval, &id))
1170 return JS_FALSE;
1171
1172 /*
1173 * Check whether the id refers to a formal parameter, local variable or
1174 * the arguments special name.
1175 *
1176 * We define all such names using JSDNP_DONT_PURGE to avoid an expensive
1177 * shape invalidation in js_DefineNativeProperty. If such an id happens to
1178 * shadow a global or upvar of the same name, any inner functions can
1179 * never access the outer binding. Thus it cannot invalidate any property
1180 * cache entries or derived trace guards for the outer binding. See also
1181 * comments in js_PurgeScopeChainHelper from jsobj.cpp.
1182 */
1183 localKind = js_LookupLocal(cx, fun, JSID_TO_ATOM(id), &slot);
1184 if (localKind != JSLOCAL_NONE && localKind != JSLOCAL_UPVAR) {
1185 JS_ASSERT((uint16) slot == slot);
1186
1187 /*
1188 * We follow 10.2.3 of ECMA 262 v3 and make argument and variable
1189 * properties of the Call objects enumerable.
1190 */
1191 attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED;
1192 if (localKind == JSLOCAL_ARG) {
1193 JS_ASSERT(slot < fun->nargs);
1194 getter = js_GetCallArg;
1195 setter = SetCallArg;
1196 } else {
1197 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
1198 JS_ASSERT(slot < fun->u.i.nvars);
1199 getter = js_GetCallVar;
1200 setter = SetCallVar;
1201 if (localKind == JSLOCAL_CONST)
1202 attrs |= JSPROP_READONLY;
1203
1204 /*
1205 * Use js_GetCallVarChecked if the local's value is a null closure.
1206 * This way we penalize performance only slightly on first use of a
1207 * null closure, not on every use.
1208 */
1209 jsval v;
1210 if (!CallPropertyOp(cx, obj, INT_TO_JSID((int16)slot), &v, JSCPK_VAR, JS_FALSE))
1211 return JS_FALSE;
1212 if (VALUE_IS_FUNCTION(cx, v) &&
1213 GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v))->needsWrapper()) {
1214 getter = js_GetCallVarChecked;
1215 }
1216 }
1217 if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, getter, setter,
1218 attrs, SPROP_HAS_SHORTID, (int16) slot,
1219 NULL, JSDNP_DONT_PURGE)) {
1220 return JS_FALSE;
1221 }
1222 *objp = obj;
1223 return JS_TRUE;
1224 }
1225
1226 /*
1227 * Resolve arguments so that we never store a particular Call object's
1228 * arguments object reference in a Call prototype's |arguments| slot.
1229 */
1230 if (id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) {
1231 if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID,
1232 GetCallArguments, SetCallArguments,
1233 JSPROP_PERMANENT | JSPROP_SHARED,
1234 0, 0, NULL, JSDNP_DONT_PURGE)) {
1235 return JS_FALSE;
1236 }
1237 *objp = obj;
1238 return JS_TRUE;
1239 }
1240
1241 /* Control flow reaches here only if id was not resolved. */
1242 return JS_TRUE;
1243 }
1244
1245 static JSBool
1246 call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
1247 {
1248 JSStackFrame *fp;
1249
1250 if (type == JSTYPE_FUNCTION) {
1251 fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
1252 if (fp) {
1253 JS_ASSERT(fp->fun);
1254 *vp = OBJECT_TO_JSVAL(fp->callee);
1255 }
1256 }
1257 return JS_TRUE;
1258 }
1259
1260 static uint32
1261 call_reserveSlots(JSContext *cx, JSObject *obj)
1262 {
1263 JSFunction *fun;
1264
1265 fun = GetCallObjectFunction(obj);
1266 return fun->countArgsAndVars();
1267 }
1268
1269 JS_FRIEND_DATA(JSClass) js_CallClass = {
1270 "Call",
1271 JSCLASS_HAS_PRIVATE |
1272 JSCLASS_HAS_RESERVED_SLOTS(CALL_CLASS_FIXED_RESERVED_SLOTS) |
1273 JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
1274 JS_PropertyStub, JS_PropertyStub,
1275 JS_PropertyStub, JS_PropertyStub,
1276 call_enumerate, (JSResolveOp)call_resolve,
1277 call_convert, JS_FinalizeStub,
1278 NULL, NULL,
1279 NULL, NULL,
1280 NULL, NULL,
1281 JS_CLASS_TRACE(args_or_call_trace), call_reserveSlots
1282 };
1283
1284 static JSBool
1285 fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1286 {
1287 jsint slot;
1288 JSFunction *fun;
1289 JSStackFrame *fp;
1290 JSSecurityCallbacks *callbacks;
1291
1292 if (!JSVAL_IS_INT(id))
1293 return JS_TRUE;
1294 slot = JSVAL_TO_INT(id);
1295
1296 /*
1297 * Loop because getter and setter can be delegated from another class,
1298 * but loop only for ARGS_LENGTH because we must pretend that f.length
1299 * is in each function instance f, per ECMA-262, instead of only in the
1300 * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
1301 * to make it appear so).
1302 *
1303 * This code couples tightly to the attributes for the function_props[]
1304 * initializers above, and to js_SetProperty and js_HasOwnProperty.
1305 *
1306 * It's important to allow delegating objects, even though they inherit
1307 * this getter (fun_getProperty), to override arguments, arity, caller,
1308 * and name. If we didn't return early for slot != ARGS_LENGTH, we would
1309 * clobber *vp with the native property value, instead of letting script
1310 * override that value in delegating objects.
1311 *
1312 * Note how that clobbering is what simulates JSPROP_READONLY for all of
1313 * the non-standard properties when the directly addressed object (obj)
1314 * is a function object (i.e., when this loop does not iterate).
1315 */
1316 while (!(fun = (JSFunction *)
1317 JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) {
1318 if (slot != ARGS_LENGTH)
1319 return JS_TRUE;
1320 obj = OBJ_GET_PROTO(cx, obj);
1321 if (!obj)
1322 return JS_TRUE;
1323 }
1324
1325 /* Find fun's top-most activation record. */
1326 for (fp = js_GetTopStackFrame(cx);
1327 fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL));
1328 fp = fp->down) {
1329 continue;
1330 }
1331
1332 switch (slot) {
1333 case CALL_ARGUMENTS:
1334 /* Warn if strict about f.arguments or equivalent unqualified uses. */
1335 if (!JS_ReportErrorFlagsAndNumber(cx,
1336 JSREPORT_WARNING | JSREPORT_STRICT,
1337 js_GetErrorMessage, NULL,
1338 JSMSG_DEPRECATED_USAGE,
1339 js_arguments_str)) {
1340 return JS_FALSE;
1341 }
1342 if (fp) {
1343 if (!js_GetArgsValue(cx, fp, vp))
1344 return JS_FALSE;
1345 } else {
1346 *vp = JSVAL_NULL;
1347 }
1348 break;
1349
1350 case ARGS_LENGTH:
1351 case FUN_ARITY:
1352 *vp = INT_TO_JSVAL((jsint)fun->nargs);
1353 break;
1354
1355 case FUN_NAME:
1356 *vp = fun->atom
1357 ? ATOM_KEY(fun->atom)
1358 : STRING_TO_JSVAL(cx->runtime->emptyString);
1359 break;
1360
1361 case FUN_CALLER:
1362 if (fp && fp->down && fp->down->fun) {
1363 JSFunction *caller = fp->down->fun;
1364 /*
1365 * See equivalent condition in args_getProperty for ARGS_CALLEE,
1366 * but here we do not want to throw, since this escape can happen
1367 * via foo.caller alone, without any debugger or indirect eval. And
1368 * it seems foo.caller is still used on the Web.
1369 */
1370 if (caller->needsWrapper()) {
1371 JSObject *wrapper = WrapEscapingClosure(cx, fp->down, FUN_OBJECT(caller), caller);
1372 if (!wrapper)
1373 return JS_FALSE;
1374 *vp = OBJECT_TO_JSVAL(wrapper);
1375 return JS_TRUE;
1376 }
1377
1378 *vp = OBJECT_TO_JSVAL(fp->down->callee);
1379 } else {
1380 *vp = JSVAL_NULL;
1381 }
1382 if (!JSVAL_IS_PRIMITIVE(*vp)) {
1383 callbacks = JS_GetSecurityCallbacks(cx);
1384 if (callbacks && callbacks->checkObjectAccess) {
1385 id = ATOM_KEY(cx->runtime->atomState.callerAtom);
1386 if (!callbacks->checkObjectAccess(cx, obj, id, JSACC_READ, vp))
1387 return JS_FALSE;
1388 }
1389 }
1390 break;
1391
1392 default:
1393 /* XXX fun[0] and fun.arguments[0] are equivalent. */
1394 if (fp && fp->fun && (uintN)slot < fp->fun->nargs)
1395 *vp = fp->argv[slot];
1396 break;
1397 }
1398
1399 return JS_TRUE;
1400 }
1401
1402 /*
1403 * ECMA-262 specifies that length is a property of function object instances,
1404 * but we can avoid that space cost by delegating to a prototype property that
1405 * is JSPROP_PERMANENT and JSPROP_SHARED. Each fun_getProperty call computes
1406 * a fresh length value based on the arity of the individual function object's
1407 * private data.
1408 *
1409 * The extensions below other than length, i.e., the ones not in ECMA-262,
1410 * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility
1411 * with ECMA we must allow a delegating object to override them. Therefore to
1412 * avoid entraining garbage in Function.prototype slots, they must be resolved
1413 * in non-prototype function objects, wherefore the lazy_function_props table
1414 * and fun_resolve's use of it.
1415 */
1416 #define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED)
1417
1418 static JSPropertySpec function_props[] = {
1419 {js_length_str, ARGS_LENGTH, LENGTH_PROP_ATTRS, fun_getProperty, JS_PropertyStub},
1420 {0,0,0,0,0}
1421 };
1422
1423 typedef struct LazyFunctionProp {
1424 uint16 atomOffset;
1425 int8 tinyid;
1426 uint8 attrs;
1427 } LazyFunctionProp;
1428
1429 /* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */
1430 static LazyFunctionProp lazy_function_props[] = {
1431 {ATOM_OFFSET(arguments), CALL_ARGUMENTS, JSPROP_PERMANENT},
1432 {ATOM_OFFSET(arity), FUN_ARITY, JSPROP_PERMANENT},
1433 {ATOM_OFFSET(caller), FUN_CALLER, JSPROP_PERMANENT},
1434 {ATOM_OFFSET(name), FUN_NAME, JSPROP_PERMANENT},
1435 };
1436
1437 static JSBool
1438 fun_enumerate(JSContext *cx, JSObject *obj)
1439 {
1440 jsid prototypeId;
1441 JSObject *pobj;
1442 JSProperty *prop;
1443
1444 prototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
1445 if (!OBJ_LOOKUP_PROPERTY(cx, obj, prototypeId, &pobj, &prop))
1446 return JS_FALSE;
1447 if (prop)
1448 OBJ_DROP_PROPERTY(cx, pobj, prop);
1449 return JS_TRUE;
1450 }
1451
1452 static JSBool
1453 fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
1454 JSObject **objp)
1455 {
1456 JSFunction *fun;
1457 JSAtom *atom;
1458 uintN i;
1459
1460 if (!JSVAL_IS_STRING(id))
1461 return JS_TRUE;
1462
1463 fun = GET_FUNCTION_PRIVATE(cx, obj);
1464
1465 /*
1466 * No need to reflect fun.prototype in 'fun.prototype = ... '.
1467 */
1468 if (flags & JSRESOLVE_ASSIGNING)
1469 return JS_TRUE;
1470
1471 /*
1472 * Ok, check whether id is 'prototype' and bootstrap the function object's
1473 * prototype property.
1474 */
1475 atom = cx->runtime->atomState.classPrototypeAtom;
1476 if (id == ATOM_KEY(atom)) {
1477 JSObject *proto;
1478
1479 /*
1480 * Beware of the wacky case of a user function named Object -- trying
1481 * to find a prototype for that will recur back here _ad perniciem_.
1482 */
1483 if (fun->atom == CLASS_ATOM(cx, Object))
1484 return JS_TRUE;
1485
1486 /*
1487 * Make the prototype object to have the same parent as the function
1488 * object itself.
1489 */
1490 proto = js_NewObject(cx, &js_ObjectClass, NULL, OBJ_GET_PARENT(cx, obj),
1491 0);
1492 if (!proto)
1493 return JS_FALSE;
1494
1495 /*
1496 * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for
1497 * user-defined functions, but DontEnum | ReadOnly | DontDelete for
1498 * native "system" constructors such as Object or Function. So lazily
1499 * set the former here in fun_resolve, but eagerly define the latter
1500 * in JS_InitClass, with the right attributes.
1501 */
1502 if (!js_SetClassPrototype(cx, obj, proto,
1503 JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
1504 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
1505 return JS_FALSE;
1506 }
1507 *objp = obj;
1508 return JS_TRUE;
1509 }
1510
1511 for (i = 0; i < JS_ARRAY_LENGTH(lazy_function_props); i++) {
1512 LazyFunctionProp *lfp = &lazy_function_props[i];
1513
1514 atom = OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset);
1515 if (id == ATOM_KEY(atom)) {
1516 if (!js_DefineNativeProperty(cx, obj,
1517 ATOM_TO_JSID(atom), JSVAL_VOID,
1518 fun_getProperty, JS_PropertyStub,
1519 lfp->attrs, SPROP_HAS_SHORTID,
1520 lfp->tinyid, NULL)) {
1521 return JS_FALSE;
1522 }
1523 *objp = obj;
1524 return JS_TRUE;
1525 }
1526 }
1527
1528 return JS_TRUE;
1529 }
1530
1531 static JSBool
1532 fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
1533 {
1534 switch (type) {
1535 case JSTYPE_FUNCTION:
1536 *vp = OBJECT_TO_JSVAL(obj);
1537 return JS_TRUE;
1538 default:
1539 return js_TryValueOf(cx, obj, type, vp);
1540 }
1541 }
1542
1543 #if JS_HAS_XDR
1544
1545 /* XXX store parent and proto, if defined */
1546 JSBool
1547 js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
1548 {
1549 JSContext *cx;
1550 JSFunction *fun;
1551 uint32 firstword; /* flag telling whether fun->atom is non-null,
1552 plus for fun->u.i.skipmin, fun->u.i.wrapper,
1553 and 14 bits reserved for future use */
1554 uintN nargs, nvars, nupvars, n;
1555 uint32 localsword; /* word for argument and variable counts */
1556 uint32 flagsword; /* word for fun->u.i.nupvars and fun->flags */
1557 JSTempValueRooter tvr;
1558 JSBool ok;
1559
1560 cx = xdr->cx;
1561 if (xdr->mode == JSXDR_ENCODE) {
1562 fun = GET_FUNCTION_PRIVATE(cx, *objp);
1563 if (!FUN_INTERPRETED(fun)) {
1564 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1565 JSMSG_NOT_SCRIPTED_FUNCTION,
1566 JS_GetFunctionName(fun));
1567 return JS_FALSE;
1568 }
1569 if (fun->u.i.wrapper) {
1570 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1571 JSMSG_XDR_CLOSURE_WRAPPER,
1572 JS_GetFunctionName(fun));
1573 return JS_FALSE;
1574 }
1575 JS_ASSERT((fun->u.i.wrapper & ~1U) == 0);
1576 firstword = (fun->u.i.skipmin << 2) | (fun->u.i.wrapper << 1) | !!fun->atom;
1577 nargs = fun->nargs;
1578 nvars = fun->u.i.nvars;
1579 nupvars = fun->u.i.nupvars;
1580 localsword = (nargs << 16) | nvars;
1581 flagsword = (nupvars << 16) | fun->flags;
1582 } else {
1583 fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
1584 if (!fun)
1585 return JS_FALSE;
1586 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
1587 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
1588 #ifdef __GNUC__
1589 nvars = nargs = nupvars = 0; /* quell GCC uninitialized warning */
1590 #endif
1591 }
1592
1593 /* From here on, control flow must flow through label out. */
1594 MUST_FLOW_THROUGH("out");
1595 JS_PUSH_TEMP_ROOT_OBJECT(cx, FUN_OBJECT(fun), &tvr);
1596 ok = JS_TRUE;
1597
1598 if (!JS_XDRUint32(xdr, &firstword))
1599 goto bad;
1600 if ((firstword & 1U) && !js_XDRStringAtom(xdr, &fun->atom))
1601 goto bad;
1602 if (!JS_XDRUint32(xdr, &localsword) ||
1603 !JS_XDRUint32(xdr, &flagsword)) {
1604 goto bad;
1605 }
1606
1607 if (xdr->mode == JSXDR_DECODE) {
1608 nargs = localsword >> 16;
1609 nvars = uint16(localsword);
1610 JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED);
1611 nupvars = flagsword >> 16;
1612 fun->flags = uint16(flagsword);
1613 fun->u.i.skipmin = uint16(firstword >> 2);
1614 fun->u.i.wrapper = (firstword >> 1) & 1;
1615 }
1616
1617 /* do arguments and local vars */
1618 n = nargs + nvars + nupvars;
1619 if (n != 0) {
1620 void *mark;
1621 uintN i;
1622 uintN bitmapLength;
1623 uint32 *bitmap;
1624 jsuword *names;
1625 JSAtom *name;
1626 JSLocalKind localKind;
1627
1628 mark = JS_ARENA_MARK(&xdr->cx->tempPool);
1629
1630 /*
1631 * From this point the control must flow via the label release_mark.
1632 *
1633 * To xdr the names we prefix the names with a bitmap descriptor and
1634 * then xdr the names as strings. For argument names (indexes below
1635 * nargs) the corresponding bit in the bitmap is unset when the name
1636 * is null. Such null names are not encoded or decoded. For variable
1637 * names (indexes starting from nargs) bitmap's bit is set when the
1638 * name is declared as const, not as ordinary var.
1639 * */
1640 MUST_FLOW_THROUGH("release_mark");
1641 bitmapLength = JS_HOWMANY(n, JS_BITS_PER_UINT32);
1642 JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &xdr->cx->tempPool,
1643 bitmapLength * sizeof *bitmap);
1644 if (!bitmap) {
1645 js_ReportOutOfScriptQuota(xdr->cx);
1646 ok = JS_FALSE;
1647 goto release_mark;
1648 }
1649 if (xdr->mode == JSXDR_ENCODE) {
1650 names = js_GetLocalNameArray(xdr->cx, fun, &xdr->cx->tempPool);
1651 if (!names) {
1652 ok = JS_FALSE;
1653 goto release_mark;
1654 }
1655 memset(bitmap, 0, bitmapLength * sizeof *bitmap);
1656 for (i = 0; i != n; ++i) {
1657 if (i < fun->nargs
1658 ? JS_LOCAL_NAME_TO_ATOM(names[i]) != NULL
1659 : JS_LOCAL_NAME_IS_CONST(names[i])) {
1660 bitmap[i >> JS_BITS_PER_UINT32_LOG2] |=
1661 JS_BIT(i & (JS_BITS_PER_UINT32 - 1));
1662 }
1663 }
1664 }
1665 #ifdef __GNUC__
1666 else {
1667 names = NULL; /* quell GCC uninitialized warning */
1668 }
1669 #endif
1670 for (i = 0; i != bitmapLength; ++i) {
1671 ok = JS_XDRUint32(xdr, &bitmap[i]);
1672 if (!ok)
1673 goto release_mark;
1674 }
1675 for (i = 0; i != n; ++i) {
1676 if (i < nargs &&
1677 !(bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
1678 JS_BIT(i & (JS_BITS_PER_UINT32 - 1)))) {
1679 if (xdr->mode == JSXDR_DECODE) {
1680 ok = js_AddLocal(xdr->cx, fun, NULL, JSLOCAL_ARG);
1681 if (!ok)
1682 goto release_mark;
1683 } else {
1684 JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names[i]));
1685 }
1686 continue;
1687 }
1688 if (xdr->mode == JSXDR_ENCODE)
1689 name = JS_LOCAL_NAME_TO_ATOM(names[i]);
1690 ok = js_XDRStringAtom(xdr, &name);
1691 if (!ok)
1692 goto release_mark;
1693 if (xdr->mode == JSXDR_DECODE) {
1694 localKind = (i < nargs)
1695 ? JSLOCAL_ARG
1696 : (i < nargs + nvars)
1697 ? (bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
1698 JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
1699 ? JSLOCAL_CONST
1700 : JSLOCAL_VAR)
1701 : JSLOCAL_UPVAR;
1702 ok = js_AddLocal(xdr->cx, fun, name, localKind);
1703 if (!ok)
1704 goto release_mark;
1705 }
1706 }
1707 ok = JS_TRUE;
1708
1709 release_mark:
1710 JS_ARENA_RELEASE(&xdr->cx->tempPool, mark);
1711 if (!ok)
1712 goto out;
1713
1714 if (xdr->mode == JSXDR_DECODE)
1715 js_FreezeLocalNames(cx, fun);
1716 }
1717
1718 if (!js_XDRScript(xdr, &fun->u.i.script, NULL))
1719 goto bad;
1720
1721 if (xdr->mode == JSXDR_DECODE) {
1722 *objp = FUN_OBJECT(fun);
1723 #ifdef CHECK_SCRIPT_OWNER
1724 fun->u.i.script->owner = NULL;
1725 #endif
1726 js_CallNewScriptHook(cx, fun->u.i.script, fun);
1727 }
1728
1729 out:
1730 JS_POP_TEMP_ROOT(cx, &tvr);
1731 return ok;
1732
1733 bad:
1734 ok = JS_FALSE;
1735 goto out;
1736 }
1737
1738 #else /* !JS_HAS_XDR */
1739
1740 #define js_XDRFunctionObject NULL
1741
1742 #endif /* !JS_HAS_XDR */
1743
1744 /*
1745 * [[HasInstance]] internal method for Function objects: fetch the .prototype
1746 * property of its 'this' parameter, and walks the prototype chain of v (only
1747 * if v is an object) returning true if .prototype is found.
1748 */
1749 static JSBool
1750 fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
1751 {
1752 jsval pval;
1753
1754 if (!OBJ_GET_PROPERTY(cx, obj,
1755 ATOM_TO_JSID(cx->runtime->atomState
1756 .classPrototypeAtom),
1757 &pval)) {
1758 return JS_FALSE;
1759 }
1760
1761 if (JSVAL_IS_PRIMITIVE(pval)) {
1762 /*
1763 * Throw a runtime error if instanceof is called on a function that
1764 * has a non-object as its .prototype value.
1765 */
1766 js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE,
1767 -1, OBJECT_TO_JSVAL(obj), NULL);
1768 return JS_FALSE;
1769 }
1770
1771 return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp);
1772 }
1773
1774 static void
1775 TraceLocalNames(JSTracer *trc, JSFunction *fun);
1776
1777 static void
1778 DestroyLocalNames(JSContext *cx, JSFunction *fun);
1779
1780 static void
1781 fun_trace(JSTracer *trc, JSObject *obj)
1782 {
1783 JSFunction *fun;
1784
1785 /* A newborn function object may have a not yet initialized private slot. */
1786 fun = (JSFunction *) JS_GetPrivate(trc->context, obj);
1787 if (!fun)
1788 return;
1789
1790 if (FUN_OBJECT(fun) != obj) {
1791 /* obj is cloned function object, trace the original. */
1792 JS_CALL_TRACER(trc, FUN_OBJECT(fun), JSTRACE_OBJECT, "private");
1793 return;
1794 }
1795 if (fun->atom)
1796 JS_CALL_STRING_TRACER(trc, ATOM_TO_STRING(fun->atom), "atom");
1797 if (FUN_INTERPRETED(fun)) {
1798 if (fun->u.i.script)
1799 js_TraceScript(trc, fun->u.i.script);
1800 TraceLocalNames(trc, fun);
1801 }
1802 }
1803
1804 static void
1805 fun_finalize(JSContext *cx, JSObject *obj)
1806 {
1807 JSFunction *fun;
1808
1809 /* Ignore newborn and cloned function objects. */
1810 fun = (JSFunction *) JS_GetPrivate(cx, obj);
1811 if (!fun || FUN_OBJECT(fun) != obj)
1812 return;
1813
1814 /*
1815 * Null-check of u.i.script is required since the parser sets interpreted
1816 * very early.
1817 */
1818 if (FUN_INTERPRETED(fun)) {
1819 if (fun->u.i.script)
1820 js_DestroyScript(cx, fun->u.i.script);
1821 DestroyLocalNames(cx, fun);
1822 }
1823 }
1824
1825 static uint32
1826 fun_reserveSlots(JSContext *cx, JSObject *obj)
1827 {
1828 JSFunction *fun;
1829 uint32 nslots;
1830
1831 /*
1832 * We use JS_GetPrivate and not GET_FUNCTION_PRIVATE because during
1833 * js_InitFunctionClass invocation the function is called before the
1834 * private slot of the function object is set.
1835 */
1836 fun = (JSFunction *) JS_GetPrivate(cx, obj);
1837 nslots = 0;
1838 if (fun && FUN_INTERPRETED(fun) && fun->u.i.script) {
1839 if (fun->u.i.nupvars != 0)
1840 nslots = JS_SCRIPT_UPVARS(fun->u.i.script)->length;
1841 if (fun->u.i.script->regexpsOffset != 0)
1842 nslots += JS_SCRIPT_REGEXPS(fun->u.i.script)->length;
1843 }
1844 return nslots;
1845 }
1846
1847 /*
1848 * Reserve two slots in all function objects for XPConnect. Note that this
1849 * does not bloat every instance, only those on which reserved slots are set,
1850 * and those on which ad-hoc properties are defined.
1851 */
1852 JS_FRIEND_DATA(JSClass) js_FunctionClass = {
1853 js_Function_str,
1854 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2) |
1855 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
1856 JS_PropertyStub, JS_PropertyStub,
1857 JS_PropertyStub, JS_PropertyStub,
1858 fun_enumerate, (JSResolveOp)fun_resolve,
1859 fun_convert, fun_finalize,
1860 NULL, NULL,
1861 NULL, NULL,
1862 js_XDRFunctionObject, fun_hasInstance,
1863 JS_CLASS_TRACE(fun_trace), fun_reserveSlots
1864 };
1865
1866 static JSBool
1867 fun_toStringHelper(JSContext *cx, uint32 indent, uintN argc, jsval *vp)
1868 {
1869 jsval fval;
1870 JSObject *obj;
1871 JSFunction *fun;
1872 JSString *str;
1873
1874 fval = JS_THIS(cx, vp);
1875 if (JSVAL_IS_NULL(fval))
1876 return JS_FALSE;
1877
1878 if (!VALUE_IS_FUNCTION(cx, fval)) {
1879 /*
1880 * If we don't have a function to start off with, try converting the
1881 * object to a function. If that doesn't work, complain.
1882 */
1883 if (!JSVAL_IS_PRIMITIVE(fval)) {
1884 obj = JSVAL_TO_OBJECT(fval);
1885 if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, JSTYPE_FUNCTION,
1886 &fval)) {
1887 return JS_FALSE;
1888 }
1889 vp[1] = fval;
1890 }
1891 if (!VALUE_IS_FUNCTION(cx, fval)) {
1892 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1893 JSMSG_INCOMPATIBLE_PROTO,
1894 js_Function_str, js_toString_str,
1895 JS_GetTypeName(cx, JS_TypeOfValue(cx, fval)));
1896 return JS_FALSE;
1897 }
1898 }
1899
1900 obj = JSVAL_TO_OBJECT(fval);
1901 if (argc != 0) {
1902 indent = js_ValueToECMAUint32(cx, &vp[2]);
1903 if (JSVAL_IS_NULL(vp[2]))
1904 return JS_FALSE;
1905 }
1906
1907 JS_ASSERT(JS_ObjectIsFunction(cx, obj));
1908 fun = GET_FUNCTION_PRIVATE(cx, obj);
1909 if (!fun)
1910 return JS_TRUE;
1911 str = JS_DecompileFunction(cx, fun, (uintN)indent);
1912 if (!str)
1913 return JS_FALSE;
1914 *vp = STRING_TO_JSVAL(str);
1915 return JS_TRUE;
1916 }
1917
1918 static JSBool
1919 fun_toString(JSContext *cx, uintN argc, jsval *vp)
1920 {
1921 return fun_toStringHelper(cx, 0, argc, vp);
1922 }
1923
1924 #if JS_HAS_TOSOURCE
1925 static JSBool
1926 fun_toSource(JSContext *cx, uintN argc, jsval *vp)
1927 {
1928 return fun_toStringHelper(cx, JS_DONT_PRETTY_PRINT, argc, vp);
1929 }
1930 #endif
1931
1932 JS_REQUIRES_STACK JSBool
1933 js_fun_call(JSContext *cx, uintN argc, jsval *vp)
1934 {
1935 JSObject *obj;
1936 jsval fval, *argv, *invokevp;
1937 JSString *str;
1938 void *mark;
1939 JSBool ok;
1940
1941 obj = JS_THIS_OBJECT(cx, vp);
1942 if (!obj || !OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &vp[1]))
1943 return JS_FALSE;
1944 fval = vp[1];
1945
1946 if (!VALUE_IS_FUNCTION(cx, fval)) {
1947 str = JS_ValueToString(cx, fval);
1948 if (str) {
1949 const char *bytes = js_GetStringBytes(cx, str);
1950
1951 if (bytes) {
1952 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1953 JSMSG_INCOMPATIBLE_PROTO,
1954 js_Function_str, js_call_str,
1955 bytes);
1956 }
1957 }
1958 return JS_FALSE;
1959 }
1960
1961 argv = vp + 2;
1962 if (argc == 0) {
1963 /* Call fun with its global object as the 'this' param if no args. */
1964 obj = NULL;
1965 } else {
1966 /* Otherwise convert the first arg to 'this' and skip over it. */
1967 if (!JSVAL_IS_PRIMITIVE(argv[0]))
1968 obj = JSVAL_TO_OBJECT(argv[0]);
1969 else if (!js_ValueToObject(cx, argv[0], &obj))
1970 return JS_FALSE;
1971 argc--;
1972 argv++;
1973 }
1974
1975 /* Allocate stack space for fval, obj, and the args. */
1976 invokevp = js_AllocStack(cx, 2 + argc, &mark);
1977 if (!invokevp)
1978 return JS_FALSE;
1979
1980 /* Push fval, obj, and the args. */
1981 invokevp[0] = fval;
1982 invokevp[1] = OBJECT_TO_JSVAL(obj);
1983 memcpy(invokevp + 2, argv, argc * sizeof *argv);
1984
1985 ok = js_Invoke(cx, argc, invokevp, 0);
1986 *vp = *invokevp;
1987 js_FreeStack(cx, mark);
1988 return ok;
1989 }
1990
1991 JS_REQUIRES_STACK JSBool
1992 js_fun_apply(JSContext *cx, uintN argc, jsval *vp)
1993 {
1994 JSObject *obj, *aobj;
1995 jsval fval, *invokevp, *sp;
1996 JSString *str;
1997 jsuint length;
1998 JSBool arraylike, ok;
1999 void *mark;
2000 uintN i;
2001
2002 if (argc == 0) {
2003 /* Will get globalObject as 'this' and no other arguments. */
2004 return js_fun_call(cx, argc, vp);
2005 }
2006
2007 obj = JS_THIS_OBJECT(cx, vp);
2008 if (!obj || !OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &vp[1]))
2009 return JS_FALSE;
2010 fval = vp[1];
2011
2012 if (!VALUE_IS_FUNCTION(cx, fval)) {
2013 str = JS_ValueToString(cx, fval);
2014 if (str) {
2015 const char *bytes = js_GetStringBytes(cx, str);
2016
2017 if (bytes) {
2018 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2019 JSMSG_INCOMPATIBLE_PROTO,
2020 js_Function_str, js_apply_str,
2021 bytes);
2022 }
2023 }
2024 return JS_FALSE;
2025 }
2026
2027 /* Quell GCC overwarnings. */
2028 aobj = NULL;
2029 length = 0;
2030
2031 if (argc >= 2) {
2032 /* If the 2nd arg is null or void, call the function with 0 args. */
2033 if (JSVAL_IS_NULL(vp[3]) || JSVAL_IS_VOID(vp[3])) {
2034 argc = 0;
2035 } else {
2036 /* The second arg must be an array (or arguments object). */
2037 arraylike = JS_FALSE;
2038 if (!JSVAL_IS_PRIMITIVE(vp[3])) {
2039 aobj = JSVAL_TO_OBJECT(vp[3]);
2040 if (!js_IsArrayLike(cx, aobj, &arraylike, &length))
2041 return JS_FALSE;
2042 }
2043 if (!arraylike) {
2044 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2045 JSMSG_BAD_APPLY_ARGS, js_apply_str);
2046 return JS_FALSE;
2047 }
2048 }
2049 }
2050
2051 /* Convert the first arg to 'this' and skip over it. */
2052 if (!JSVAL_IS_PRIMITIVE(vp[2]))
2053 obj = JSVAL_TO_OBJECT(vp[2]);
2054 else if (!js_ValueToObject(cx, vp[2], &obj))
2055 return JS_FALSE;
2056
2057 /* Allocate stack space for fval, obj, and the args. */
2058 argc = (uintN)JS_MIN(length, ARRAY_INIT_LIMIT - 1);
2059 invokevp = js_AllocStack(cx, 2 + argc, &mark);
2060 if (!invokevp)
2061 return JS_FALSE;
2062
2063 /* Push fval, obj, and aobj's elements as args. */
2064 sp = invokevp;
2065 *sp++ = fval;
2066 *sp++ = OBJECT_TO_JSVAL(obj);
2067 for (i = 0; i < argc; i++) {
2068 ok = JS_GetElement(cx, aobj, (jsint)i, sp);
2069 if (!ok)
2070 goto out;
2071 sp++;
2072 }
2073
2074 ok = js_Invoke(cx, argc, invokevp, 0);
2075 *vp = *invokevp;
2076 out:
2077 js_FreeStack(cx, mark);
2078 return ok;
2079 }
2080
2081 #ifdef NARCISSUS
2082 static JS_REQUIRES_STACK JSBool
2083 fun_applyConstructor(JSContext *cx, uintN argc, jsval *vp)
2084 {
2085 JSObject *aobj;
2086 uintN length, i;
2087 void *mark;
2088 jsval *invokevp, *sp;
2089 JSBool ok;
2090
2091 if (JSVAL_IS_PRIMITIVE(vp[2]) ||
2092 (aobj = JSVAL_TO_OBJECT(vp[2]),
2093 OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass &&
2094 OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass)) {
2095 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2096 JSMSG_BAD_APPLY_ARGS, "__applyConstruct__");
2097 return JS_FALSE;
2098 }
2099
2100 if (!js_GetLengthProperty(cx, aobj, &length))
2101 return JS_FALSE;
2102
2103 if (length >= ARRAY_INIT_LIMIT)
2104 length = ARRAY_INIT_LIMIT - 1;
2105 invokevp = js_AllocStack(cx, 2 + length, &mark);
2106 if (!invokevp)
2107 return JS_FALSE;
2108
2109 sp = invokevp;
2110 *sp++ = vp[1];
2111 *sp++ = JSVAL_NULL; /* this is filled automagically */
2112 for (i = 0; i < length; i++) {
2113 ok = JS_GetElement(cx, aobj, (jsint)i, sp);
2114 if (!ok)
2115 goto out;
2116 sp++;
2117 }
2118
2119 ok = js_InvokeConstructor(cx, length, JS_TRUE, invokevp);
2120 *vp = *invokevp;
2121 out:
2122 js_FreeStack(cx, mark);
2123 return ok;
2124 }
2125 #endif
2126
2127 static JSFunctionSpec function_methods[] = {
2128 #if JS_HAS_TOSOURCE
2129 JS_FN(js_toSource_str, fun_toSource, 0,0),
2130 #endif
2131 JS_FN(js_toString_str, fun_toString, 0,0),
2132 JS_FN(js_apply_str, js_fun_apply, 2,0),
2133 JS_FN(js_call_str, js_fun_call, 1,0),
2134 #ifdef NARCISSUS
2135 JS_FN("__applyConstructor__", fun_applyConstructor, 1,0),
2136 #endif
2137 JS_FS_END
2138 };
2139
2140 static JSBool
2141 Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
2142 {
2143 JSFunction *fun;
2144 JSObject *parent;
2145 JSStackFrame *fp, *caller;
2146 uintN i, n, lineno;
2147 JSAtom *atom;
2148 const char *filename;
2149 JSBool ok;
2150 JSString *str, *arg;
2151 JSTokenStream ts;
2152 JSPrincipals *principals;
2153 jschar *collected_args, *cp;
2154 void *mark;
2155 size_t arg_length, args_length, old_args_length;
2156 JSTokenType tt;
2157
2158 if (!JS_IsConstructing(cx)) {
2159 obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL, 0);
2160 if (!obj)
2161 return JS_FALSE;
2162 *rval = OBJECT_TO_JSVAL(obj);
2163 } else {
2164 /*
2165 * The constructor is called before the private slot is initialized so
2166 * we must use JS_GetPrivate, not GET_FUNCTION_PRIVATE here.
2167 */
2168 if (JS_GetPrivate(cx, obj))
2169 return JS_TRUE;
2170 }
2171
2172 /*
2173 * NB: (new Function) is not lexically closed by its caller, it's just an
2174 * anonymous function in the top-level scope that its constructor inhabits.
2175 * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
2176 * and so would a call to f from another top-level's script or function.
2177 *
2178 * In older versions, before call objects, a new Function was adopted by
2179 * its running context's globalObject, which might be different from the
2180 * top-level reachable from scopeChain (in HTML frames, e.g.).
2181 */
2182 parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]));
2183
2184 fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED,
2185 parent, cx->runtime->atomState.anonymousAtom);
2186
2187 if (!fun)
2188 return JS_FALSE;
2189
2190 /*
2191 * Function is static and not called directly by other functions in this
2192 * file, therefore it is callable only as a native function by js_Invoke.
2193 * Find the scripted caller, possibly skipping other native frames such as
2194 * are built for Function.prototype.call or .apply activations that invoke
2195 * Function indirectly from a script.
2196 */
2197 fp = js_GetTopStackFrame(cx);
2198 JS_ASSERT(!fp->script && fp->fun && fp->fun->u.n.native == Function);
2199 caller = js_GetScriptedCaller(cx, fp);
2200 if (caller) {
2201 principals = JS_EvalFramePrincipals(cx, fp, caller);
2202 filename = js_ComputeFilename(cx, caller, principals, &lineno);
2203 } else {
2204 filename = NULL;
2205 lineno = 0;
2206 principals = NULL;
2207 }
2208
2209 /* Belt-and-braces: check that the caller has access to parent. */
2210 if (!js_CheckPrincipalsAccess(cx, parent, principals,
2211 CLASS_ATOM(cx, Function))) {
2212 return JS_FALSE;
2213 }
2214
2215 n = argc ? argc - 1 : 0;
2216 if (n > 0) {
2217 enum { OK, BAD, BAD_FORMAL } state;
2218
2219 /*
2220 * Collect the function-argument arguments into one string, separated
2221 * by commas, then make a tokenstream from that string, and scan it to
2222 * get the arguments. We need to throw the full scanner at the
2223 * problem, because the argument string can legitimately contain
2224 * comments and linefeeds. XXX It might be better to concatenate
2225 * everything up into a function definition and pass it to the
2226 * compiler, but doing it this way is less of a delta from the old
2227 * code. See ECMA 15.3.2.1.
2228 */
2229 state = BAD_FORMAL;
2230 args_length = 0;
2231 for (i = 0; i < n; i++) {
2232 /* Collect the lengths for all the function-argument arguments. */
2233 arg = js_ValueToString(cx, argv[i]);
2234 if (!arg)
2235 return JS_FALSE;
2236 argv[i] = STRING_TO_JSVAL(arg);
2237
2238 /*
2239 * Check for overflow. The < test works because the maximum
2240 * JSString length fits in 2 fewer bits than size_t has.
2241 */
2242 old_args_length = args_length;
2243 args_length = old_args_length + JSSTRING_LENGTH(arg);
2244 if (args_length < old_args_length) {
2245 js_ReportAllocationOverflow(cx);
2246 return JS_FALSE;
2247 }
2248 }
2249
2250 /* Add 1 for each joining comma and check for overflow (two ways). */
2251 old_args_length = args_length;
2252 args_length = old_args_length + n - 1;
2253 if (args_length < old_args_length ||
2254 args_length >= ~(size_t)0 / sizeof(jschar)) {
2255 js_ReportAllocationOverflow(cx);
2256 return JS_FALSE;
2257 }
2258
2259 /*
2260 * Allocate a string to hold the concatenated arguments, including room
2261 * for a terminating 0. Mark cx->tempPool for later release, to free
2262 * collected_args and its tokenstream in one swoop.
2263 */
2264 mark = JS_ARENA_MARK(&cx->tempPool);
2265 JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool,
2266 (args_length+1) * sizeof(jschar));
2267 if (!cp) {
2268 js_ReportOutOfScriptQuota(cx);
2269 return JS_FALSE;
2270 }
2271 collected_args = cp;
2272
2273 /*
2274 * Concatenate the arguments into the new string, separated by commas.
2275 */
2276 for (i = 0; i < n; i++) {
2277 arg = JSVAL_TO_STRING(argv[i]);
2278 arg_length = JSSTRING_LENGTH(arg);
2279 (void) js_strncpy(cp, JSSTRING_CHARS(arg), arg_length);
2280 cp += arg_length;
2281
2282 /* Add separating comma or terminating 0. */
2283 *cp++ = (i + 1 < n) ? ',' : 0;
2284 }
2285
2286 /* Initialize a tokenstream that reads from the given string. */
2287 if (!js_InitTokenStream(cx, &ts, collected_args, args_length,
2288 NULL, filename, lineno)) {
2289 JS_ARENA_RELEASE(&cx->tempPool, mark);
2290 return JS_FALSE;
2291 }
2292
2293 /* The argument string may be empty or contain no tokens. */
2294 tt = js_GetToken(cx, &ts);
2295 if (tt != TOK_EOF) {
2296 for (;;) {
2297 /*
2298 * Check that it's a name. This also implicitly guards against
2299 * TOK_ERROR, which was already reported.
2300 */
2301 if (tt != TOK_NAME)
2302 goto after_args;
2303
2304 /*
2305 * Get the atom corresponding to the name from the token
2306 * stream; we're assured at this point that it's a valid
2307 * identifier.
2308 */
2309 atom = CURRENT_TOKEN(&ts).t_atom;
2310
2311 /* Check for a duplicate parameter name. */
2312 if (js_LookupLocal(cx, fun, atom, NULL) != JSLOCAL_NONE) {
2313 const char *name;
2314
2315 name = js_AtomToPrintableString(cx, atom);
2316 ok = name &&
2317 js_ReportCompileErrorNumber(cx, &ts, NULL,
2318 JSREPORT_WARNING |
2319 JSREPORT_STRICT,
2320 JSMSG_DUPLICATE_FORMAL,
2321 name);
2322 if (!ok)
2323 goto after_args;
2324 }
2325 if (!js_AddLocal(cx, fun, atom, JSLOCAL_ARG))
2326 goto after_args;
2327
2328 /*
2329 * Get the next token. Stop on end of stream. Otherwise
2330 * insist on a comma, get another name, and iterate.
2331 */
2332 tt = js_GetToken(cx, &ts);
2333 if (tt == TOK_EOF)
2334 break;
2335 if (tt != TOK_COMMA)
2336 goto after_args;
2337 tt = js_GetToken(cx, &ts);
2338 }
2339 }
2340
2341 state = OK;
2342 after_args:
2343 if (state == BAD_FORMAL && !(ts.flags & TSF_ERROR)) {
2344 /*
2345 * Report "malformed formal parameter" iff no illegal char or
2346 * similar scanner error was already reported.
2347 */
2348 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2349 JSMSG_BAD_FORMAL);
2350 }
2351 js_CloseTokenStream(cx, &ts);
2352 JS_ARENA_RELEASE(&cx->tempPool, mark);
2353 if (state != OK)
2354 return JS_FALSE;
2355 }
2356
2357 if (argc) {
2358 str = js_ValueToString(cx, argv[argc-1]);
2359 if (!str)
2360 return JS_FALSE;
2361 argv[argc-1] = STRING_TO_JSVAL(str);
2362 } else {
2363 str = cx->runtime->emptyString;
2364 }
2365
2366 return JSCompiler::compileFunctionBody(cx, fun, principals,
2367 JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
2368 filename, lineno);
2369 }
2370
2371 JSObject *
2372 js_InitFunctionClass(JSContext *cx, JSObject *obj)
2373 {
2374 JSObject *proto;
2375 JSFunction *fun;
2376
2377 proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
2378 function_props, function_methods, NULL, NULL);
2379 if (!proto)
2380 return NULL;
2381 fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
2382 if (!fun)
2383 goto bad;
2384 fun->u.i.script = js_NewScript(cx, 1, 1, 0, 0, 0, 0, 0);
2385 if (!fun->u.i.script)
2386 goto bad;
2387 fun->u.i.script->code[0] = JSOP_STOP;
2388 *SCRIPT_NOTES(fun->u.i.script) = SRC_NULL;
2389 #ifdef CHECK_SCRIPT_OWNER
2390 fun->u.i.script->owner = NULL;
2391 #endif
2392 return proto;
2393
2394 bad:
2395 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
2396 return NULL;
2397 }
2398
2399 JSFunction *
2400 js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
2401 uintN flags, JSObject *parent, JSAtom *atom)
2402 {
2403 JSFunction *fun;
2404
2405 if (funobj) {
2406 JS_ASSERT(HAS_FUNCTION_CLASS(funobj));
2407 OBJ_SET_PARENT(cx, funobj, parent);
2408 } else {
2409 funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent, 0);
2410 if (!funobj)
2411 return NULL;
2412 }
2413 JS_ASSERT(JSVAL_IS_VOID(funobj->fslots[JSSLOT_PRIVATE]));
2414 fun = (JSFunction *) funobj;
2415
2416 /* Initialize all function members. */
2417 fun->nargs = nargs;
2418 fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK | JSFUN_TRACEABLE);
2419 if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) {
2420 JS_ASSERT(!native);
2421 JS_ASSERT(nargs == 0);
2422 fun->u.i.nvars = 0;
2423 fun->u.i.nupvars = 0;
2424 fun->u.i.skipmin = 0;
2425 fun->u.i.wrapper = false;
2426 fun->u.i.script = NULL;
2427 #ifdef DEBUG
2428 fun->u.i.names.taggedAtom = 0;
2429 #endif
2430 } else {
2431 fun->u.n.extra = 0;
2432 fun->u.n.spare = 0;
2433 fun->u.n.clasp = NULL;
2434 if (flags & JSFUN_TRACEABLE) {
2435 #ifdef JS_TRACER
2436 JSTraceableNative *trcinfo =
2437 JS_FUNC_TO_DATA_PTR(JSTraceableNative *, native);
2438 fun->u.n.native = (JSNative) trcinfo->native;
2439 fun->u.n.trcinfo = trcinfo;
2440 #else
2441 fun->u.n.trcinfo = NULL;
2442 #endif
2443 } else {
2444 fun->u.n.native = native;
2445 fun->u.n.trcinfo = NULL;
2446 }
2447 JS_ASSERT(fun->u.n.native);
2448 }
2449 fun->atom = atom;
2450
2451 /* Set private to self to indicate non-cloned fully initialized function. */
2452 FUN_OBJECT(fun)->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun);
2453 return fun;
2454 }
2455
2456 JSObject *
2457 js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent)
2458 {
2459 /*
2460 * The cloned function object does not need the extra JSFunction members
2461 * beyond JSObject as it points to fun via the private slot.
2462 */
2463 JSObject *clone = js_NewObject(cx, &js_FunctionClass, NULL, parent,
2464 sizeof(JSObject));
2465 if (!clone)
2466 return NULL;
2467 clone->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun);
2468 return clone;
2469 }
2470
2471 JSObject *
2472 js_NewFlatClosure(JSContext *cx, JSFunction *fun)
2473 {
2474 JS_ASSERT(FUN_FLAT_CLOSURE(fun));
2475
2476 JSObject *closure = js_CloneFunctionObject(cx, fun, cx->fp->scopeChain);
2477 if (!closure || fun->u.i.script->upvarsOffset == 0)
2478 return closure;
2479
2480 uint32 nslots = JSSLOT_FREE(&js_FunctionClass);
2481 JS_ASSERT(nslots == JS_INITIAL_NSLOTS);
2482 nslots += fun_reserveSlots(cx, closure);
2483 if (!js_ReallocSlots(cx, closure, nslots, JS_TRUE))
2484 return NULL;
2485
2486 JSUpvarArray *uva = JS_SCRIPT_UPVARS(fun->u.i.script);
2487 JS_ASSERT(uva->length <= size_t(closure->dslots[-1]));
2488
2489 uintN level = fun->u.i.script->staticLevel;
2490 for (uint32 i = 0, n = uva->length; i < n; i++)
2491 closure->dslots[i] = js_GetUpvar(cx, level, uva->vector[i]);
2492
2493 return closure;
2494 }
2495
2496 JSObject *
2497 js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun)
2498 {
2499 JS_ASSERT(cx->fp->fun->flags & JSFUN_HEAVYWEIGHT);
2500 JS_ASSERT(!cx->fp->fun->optimizedClosure());
2501
2502 return WrapEscapingClosure(cx, cx->fp, FUN_OBJECT(fun), fun);
2503 }
2504
2505 JSFunction *
2506 js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
2507 uintN nargs, uintN attrs)
2508 {
2509 JSPropertyOp gsop;
2510 JSFunction *fun;
2511
2512 if (attrs & JSFUN_STUB_GSOPS) {
2513 /*
2514 * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
2515 * the defined property's attributes. This allows us to encode another,
2516 * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
2517 * for more on this.
2518 */
2519 attrs &= ~JSFUN_STUB_GSOPS;
2520 gsop = JS_PropertyStub;
2521 } else {
2522 gsop = NULL;
2523 }
2524 fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom);
2525 if (!fun)
2526 return NULL;
2527 if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom),
2528 OBJECT_TO_JSVAL(FUN_OBJECT(fun)),
2529 gsop, gsop,
2530 attrs & ~JSFUN_FLAGS_MASK, NULL)) {
2531 return NULL;
2532 }
2533 return fun;
2534 }
2535
2536 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
2537 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
2538 #endif
2539
2540 JSFunction *
2541 js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags)
2542 {
2543 jsval v;
2544 JSObject *obj;
2545
2546 v = *vp;
2547 obj = NULL;
2548 if (JSVAL_IS_OBJECT(v)) {
2549 obj = JSVAL_TO_OBJECT(v);
2550 if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) {
2551 if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v))
2552 return NULL;
2553 obj = VALUE_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL;
2554 }
2555 }
2556 if (!obj) {
2557 js_ReportIsNotFunction(cx, vp, flags);
2558 return NULL;
2559 }
2560 return GET_FUNCTION_PRIVATE(cx, obj);
2561 }
2562
2563 JSObject *
2564 js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags)
2565 {
2566 JSFunction *fun;
2567 JSStackFrame *caller;
2568 JSPrincipals *principals;
2569
2570 if (VALUE_IS_FUNCTION(cx, *vp))
2571 return JSVAL_TO_OBJECT(*vp);
2572
2573 fun = js_ValueToFunction(cx, vp, flags);
2574 if (!fun)
2575 return NULL;
2576 *vp = OBJECT_TO_JSVAL(FUN_OBJECT(fun));
2577
2578 caller = js_GetScriptedCaller(cx, NULL);
2579 if (caller) {
2580 principals = JS_StackFramePrincipals(cx, caller);
2581 } else {
2582 /* No scripted caller, don't allow access. */
2583 principals = NULL;
2584 }
2585
2586 if (!js_CheckPrincipalsAccess(cx, FUN_OBJECT(fun), principals,
2587 fun->atom
2588 ? fun->atom
2589 : cx->runtime->atomState.anonymousAtom)) {
2590 return NULL;
2591 }
2592 return FUN_OBJECT(fun);
2593 }
2594
2595 JSObject *
2596 js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags)
2597 {
2598 JSObject *callable = JSVAL_IS_OBJECT(*vp) ? JSVAL_TO_OBJECT(*vp) : NULL;
2599
2600 if (callable && js_IsCallable(callable, cx)) {
2601 *vp = OBJECT_TO_JSVAL(callable);
2602 return callable;
2603 }
2604 return js_ValueToFunctionObject(cx, vp, flags);
2605 }
2606
2607 void
2608 js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
2609 {
2610 JSStackFrame *fp;
2611 uintN error;
2612 const char *name, *source;
2613 JSTempValueRooter tvr;
2614
2615 for (fp = js_GetTopStackFrame(cx); fp && !fp->regs; fp = fp->down)
2616 continue;
2617 name = source = NULL;
2618 JS_PUSH_TEMP_ROOT_STRING(cx, NULL, &tvr);
2619 if (flags & JSV2F_ITERATOR) {
2620 error = JSMSG_BAD_ITERATOR;
2621 name = js_iterator_str;
2622 tvr.u.string = js_ValueToSource(cx, *vp);
2623 if (!tvr.u.string)
2624 goto out;
2625 tvr.u.string = js_QuoteString(cx, tvr.u.string, 0);
2626 if (!tvr.u.string)
2627 goto out;
2628 source = js_GetStringBytes(cx, tvr.u.string);
2629 if (!source)
2630 goto out;
2631 } else if (flags & JSV2F_CONSTRUCT) {
2632 error = JSMSG_NOT_CONSTRUCTOR;
2633 } else {
2634 error = JSMSG_NOT_FUNCTION;
2635 }
2636
2637 js_ReportValueError3(cx, error,
2638 (fp && fp->regs &&
2639 StackBase(fp) <= vp && vp < fp->regs->sp)
2640 ? vp - fp->regs->sp
2641 : (flags & JSV2F_SEARCH_STACK)
2642 ? JSDVG_SEARCH_STACK
2643 : JSDVG_IGNORE_STACK,
2644 *vp, NULL,
2645 name, source);
2646
2647 out:
2648 JS_POP_TEMP_ROOT(cx, &tvr);
2649 }
2650
2651 /*
2652 * When a function has between 2 and MAX_ARRAY_LOCALS arguments and variables,
2653 * their name are stored as the JSLocalNames.array.
2654 */
2655 #define MAX_ARRAY_LOCALS 8
2656
2657 JS_STATIC_ASSERT(2 <= MAX_ARRAY_LOCALS);
2658 JS_STATIC_ASSERT(MAX_ARRAY_LOCALS < JS_BITMASK(16));
2659
2660 /*
2661 * We use the lowest bit of the string atom to distinguish const from var
2662 * name when there is only single name or when names are stored as an array.
2663 */
2664 JS_STATIC_ASSERT((JSVAL_STRING & 1) == 0);
2665
2666 /*
2667 * When we use a hash table to store the local names, we use a singly linked
2668 * list to record the indexes of duplicated parameter names to preserve the
2669 * duplicates for the decompiler.
2670 */
2671 typedef struct JSNameIndexPair JSNameIndexPair;
2672
2673 struct JSNameIndexPair {
2674 JSAtom *name;
2675 uint16 index;
2676 JSNameIndexPair *link;
2677 };
2678
2679 struct JSLocalNameMap {
2680 JSDHashTable names;
2681 JSNameIndexPair *lastdup;
2682 };
2683
2684 typedef struct JSLocalNameHashEntry {
2685 JSDHashEntryHdr hdr;
2686 JSAtom *name;
2687 uint16 index;
2688 uint8 localKind;
2689 } JSLocalNameHashEntry;
2690
2691 static void
2692 FreeLocalNameHash(JSContext *cx, JSLocalNameMap *map)
2693 {
2694 JSNameIndexPair *dup, *next;
2695
2696 for (dup = map->lastdup; dup; dup = next) {
2697 next = dup->link;
2698 JS_free(cx, dup);
2699 }
2700 JS_DHashTableFinish(&map->names);
2701 JS_free(cx, map);
2702 }
2703
2704 static JSBool
2705 HashLocalName(JSContext *cx, JSLocalNameMap *map, JSAtom *name,
2706 JSLocalKind localKind, uintN index)
2707 {
2708 JSLocalNameHashEntry *entry;
2709 JSNameIndexPair *dup;
2710
2711 JS_ASSERT(index <= JS_BITMASK(16));
2712 #if JS_HAS_DESTRUCTURING
2713 if (!name) {
2714 /* A destructuring pattern does not need a hash entry. */
2715 JS_ASSERT(localKind == JSLOCAL_ARG);
2716 return JS_TRUE;
2717 }
2718 #endif
2719 JS_ASSERT(ATOM_IS_STRING(name));
2720 entry = (JSLocalNameHashEntry *)
2721 JS_DHashTableOperate(&map->names, name, JS_DHASH_ADD);
2722 if (!entry) {
2723 JS_ReportOutOfMemory(cx);
2724 return JS_FALSE;
2725 }
2726 if (entry->name) {
2727 JS_ASSERT(entry->name == name);
2728 JS_ASSERT(entry->localKind == JSLOCAL_ARG);
2729 dup = (JSNameIndexPair *) JS_malloc(cx, sizeof *dup);
2730 if (!dup)
2731 return JS_FALSE;
2732 dup->name = entry->name;
2733 dup->index = entry->index;
2734 dup->link = map->lastdup;
2735 map->lastdup = dup;
2736 }
2737 entry->name = name;
2738 entry->index = (uint16) index;
2739 entry->localKind = (uint8) localKind;
2740 return JS_TRUE;
2741 }
2742
2743 JSBool
2744 js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind)
2745 {
2746 jsuword taggedAtom;
2747 uint16 *indexp;
2748 uintN n, i;
2749 jsuword *array;
2750 JSLocalNameMap *map;
2751
2752 JS_ASSERT(FUN_INTERPRETED(fun));
2753 JS_ASSERT(!fun->u.i.script);
2754 JS_ASSERT(((jsuword) atom & 1) == 0);
2755 taggedAtom = (jsuword) atom;
2756 if (kind == JSLOCAL_ARG) {
2757 indexp = &fun->nargs;
2758 } else if (kind == JSLOCAL_UPVAR) {
2759 indexp = &fun->u.i.nupvars;
2760 } else {
2761 indexp = &fun->u.i.nvars;
2762 if (kind == JSLOCAL_CONST)
2763 taggedAtom |= 1;
2764 else
2765 JS_ASSERT(kind == JSLOCAL_VAR);
2766 }
2767 n = fun->countLocalNames();
2768 if (n == 0) {
2769 JS_ASSERT(fun->u.i.names.taggedAtom == 0);
2770 fun->u.i.names.taggedAtom = taggedAtom;
2771 } else if (n < MAX_ARRAY_LOCALS) {
2772 if (n > 1) {
2773 array = fun->u.i.names.array;
2774 } else {
2775 array = (jsuword *) JS_malloc(cx, MAX_ARRAY_LOCALS * sizeof *array);
2776 if (!array)
2777 return JS_FALSE;
2778 array[0] = fun->u.i.names.taggedAtom;
2779 fun->u.i.names.array = array;
2780 }
2781 if (kind == JSLOCAL_ARG) {
2782 /*
2783 * A destructuring argument pattern adds variables, not arguments,
2784 * so for the following arguments nvars != 0.
2785 */
2786 #if JS_HAS_DESTRUCTURING
2787 if (fun->u.i.nvars != 0) {
2788 memmove(array + fun->nargs + 1, array + fun->nargs,
2789 fun->u.i.nvars * sizeof *array);
2790 }
2791 #else
2792 JS_ASSERT(fun->u.i.nvars == 0);
2793 #endif
2794 array[fun->nargs] = taggedAtom;
2795 } else {
2796 array[n] = taggedAtom;
2797 }
2798 } else if (n == MAX_ARRAY_LOCALS) {
2799 array = fun->u.i.names.array;
2800 map = (JSLocalNameMap *) JS_malloc(cx, sizeof *map);
2801 if (!map)
2802 return JS_FALSE;
2803 if (!JS_DHashTableInit(&map->names, JS_DHashGetStubOps(),
2804 NULL, sizeof(JSLocalNameHashEntry),
2805 JS_DHASH_DEFAULT_CAPACITY(MAX_ARRAY_LOCALS
2806 * 2))) {
2807 JS_ReportOutOfMemory(cx);
2808 JS_free(cx, map);
2809 return JS_FALSE;
2810 }
2811
2812 map->lastdup = NULL;
2813 for (i = 0; i != MAX_ARRAY_LOCALS; ++i) {
2814 taggedAtom = array[i];
2815 uintN j = i;
2816 JSLocalKind k = JSLOCAL_ARG;
2817 if (j >= fun->nargs) {
2818 j -= fun->nargs;
2819 if (j < fun->u.i.nvars) {
2820 k = (taggedAtom & 1) ? JSLOCAL_CONST : JSLOCAL_VAR;
2821 } else {
2822 j -= fun->u.i.nvars;
2823 k = JSLOCAL_UPVAR;
2824 }
2825 }
2826 if (!HashLocalName(cx, map, (JSAtom *) (taggedAtom & ~1), k, j)) {
2827 FreeLocalNameHash(cx, map);
2828 return JS_FALSE;
2829 }
2830 }
2831 if (!HashLocalName(cx, map, atom, kind, *indexp)) {
2832 FreeLocalNameHash(cx, map);
2833 return JS_FALSE;
2834 }
2835
2836 /*
2837 * At this point the entry is added and we cannot fail. It is time
2838 * to replace fun->u.i.names with the built map.
2839 */
2840 fun->u.i.names.map = map;
2841 JS_free(cx, array);
2842 } else {
2843 if (*indexp == JS_BITMASK(16)) {
2844 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2845 (kind == JSLOCAL_ARG)
2846 ? JSMSG_TOO_MANY_FUN_ARGS
2847 : JSMSG_TOO_MANY_LOCALS);
2848 return JS_FALSE;
2849 }
2850 if (!HashLocalName(cx, fun->u.i.names.map, atom, kind, *indexp))
2851 return JS_FALSE;
2852 }
2853
2854 /* Update the argument or variable counter. */
2855 ++*indexp;
2856 return JS_TRUE;
2857 }
2858
2859 JSLocalKind
2860 js_LookupLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, uintN *indexp)
2861 {
2862 uintN n, i, upvar_base;
2863 jsuword *array;
2864 JSLocalNameHashEntry *entry;
2865
2866 JS_ASSERT(FUN_INTERPRETED(fun));
2867 n = fun->countLocalNames();
2868 if (n == 0)
2869 return JSLOCAL_NONE;
2870 if (n <= MAX_ARRAY_LOCALS) {
2871 array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
2872
2873 /* Search from the tail to pick up the last duplicated name. */
2874 i = n;
2875 upvar_base = fun->countArgsAndVars();
2876 do {
2877 --i;
2878 if (atom == JS_LOCAL_NAME_TO_ATOM(array[i])) {
2879 if (i < fun->nargs) {
2880 if (indexp)
2881 *indexp = i;
2882 return JSLOCAL_ARG;
2883 }
2884 if (i >= upvar_base) {
2885 if (indexp)
2886 *indexp = i - upvar_base;
2887 return JSLOCAL_UPVAR;
2888 }
2889 if (indexp)
2890 *indexp = i - fun->nargs;
2891 return JS_LOCAL_NAME_IS_CONST(array[i])
2892 ? JSLOCAL_CONST
2893 : JSLOCAL_VAR;
2894 }
2895 } while (i != 0);
2896 } else {
2897 entry = (JSLocalNameHashEntry *)
2898 JS_DHashTableOperate(&fun->u.i.names.map->names, atom,
2899 JS_DHASH_LOOKUP);
2900 if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) {
2901 JS_ASSERT(entry->localKind != JSLOCAL_NONE);
2902 if (indexp)
2903 *indexp = entry->index;
2904 return (JSLocalKind) entry->localKind;
2905 }
2906 }
2907 return JSLOCAL_NONE;
2908 }
2909
2910 typedef struct JSLocalNameEnumeratorArgs {
2911 JSFunction *fun;
2912 jsuword *names;
2913 #ifdef DEBUG
2914 uintN nCopiedArgs;
2915 uintN nCopiedVars;
2916 #endif
2917 } JSLocalNameEnumeratorArgs;
2918
2919 static JSDHashOperator
2920 get_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
2921 uint32 number, void *arg)
2922 {
2923 JSLocalNameHashEntry *entry;
2924 JSLocalNameEnumeratorArgs *args;
2925 uint i;
2926 jsuword constFlag;
2927
2928 entry = (JSLocalNameHashEntry *) hdr;
2929 args = (JSLocalNameEnumeratorArgs *) arg;
2930 JS_ASSERT(entry->name);
2931 if (entry->localKind == JSLOCAL_ARG) {
2932 JS_ASSERT(entry->index < args->fun->nargs);
2933 JS_ASSERT(args->nCopiedArgs++ < args->fun->nargs);
2934 i = entry->index;
2935 constFlag = 0;
2936 } else {
2937 JS_ASSERT(entry->localKind == JSLOCAL_VAR ||
2938 entry->localKind == JSLOCAL_CONST ||
2939 entry->localKind == JSLOCAL_UPVAR);
2940 JS_ASSERT(entry->index < args->fun->u.i.nvars + args->fun->u.i.nupvars);
2941 JS_ASSERT(args->nCopiedVars++ < args->fun->u.i.nvars + args->fun->u.i.nupvars);
2942 i = args->fun->nargs;
2943 if (entry->localKind == JSLOCAL_UPVAR)
2944 i += args->fun->u.i.nvars;
2945 i += entry->index;
2946 constFlag = (entry->localKind == JSLOCAL_CONST);
2947 }
2948 args->names[i] = (jsuword) entry->name | constFlag;
2949 return JS_DHASH_NEXT;
2950 }
2951
2952 jsuword *
2953 js_GetLocalNameArray(JSContext *cx, JSFunction *fun, JSArenaPool *pool)
2954 {
2955 uintN n;
2956 jsuword *names;
2957 JSLocalNameMap *map;
2958 JSLocalNameEnumeratorArgs args;
2959 JSNameIndexPair *dup;
2960
2961 JS_ASSERT(fun->hasLocalNames());
2962 n = fun->countLocalNames();
2963
2964 if (n <= MAX_ARRAY_LOCALS)
2965 return (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
2966
2967 /*
2968 * No need to check for overflow of the allocation size as we are making a
2969 * copy of already allocated data. As such it must fit size_t.
2970 */
2971 JS_ARENA_ALLOCATE_CAST(names, jsuword *, pool, (size_t) n * sizeof *names);
2972 if (!names) {
2973 js_ReportOutOfScriptQuota(cx);
2974 return NULL;
2975 }
2976
2977 #if JS_HAS_DESTRUCTURING
2978 /* Some parameter names can be NULL due to destructuring patterns. */
2979 memset(names, 0, fun->nargs * sizeof *names);
2980 #endif
2981 map = fun->u.i.names.map;
2982 args.fun = fun;
2983 args.names = names;
2984 #ifdef DEBUG
2985 args.nCopiedArgs = 0;
2986 args.nCopiedVars = 0;
2987 #endif
2988 JS_DHashTableEnumerate(&map->names, get_local_names_enumerator, &args);
2989 for (dup = map->lastdup; dup; dup = dup->link) {
2990 JS_ASSERT(dup->index < fun->nargs);
2991 JS_ASSERT(args.nCopiedArgs++ < fun->nargs);
2992 names[dup->index] = (jsuword) dup->name;
2993 }
2994 #if !JS_HAS_DESTRUCTURING
2995 JS_ASSERT(args.nCopiedArgs == fun->nargs);
2996 #endif
2997 JS_ASSERT(args.nCopiedVars == fun->u.i.nvars + fun->u.i.nupvars);
2998
2999 return names;
3000 }
3001
3002 static JSDHashOperator
3003 trace_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
3004 uint32 number, void *arg)
3005 {
3006 JSLocalNameHashEntry *entry;
3007 JSTracer *trc;
3008
3009 entry = (JSLocalNameHashEntry *) hdr;
3010 JS_ASSERT(entry->name);
3011 trc = (JSTracer *) arg;
3012 JS_SET_TRACING_INDEX(trc,
3013 entry->localKind == JSLOCAL_ARG ? "arg" : "var",
3014 entry->index);
3015 JS_CallTracer(trc, ATOM_TO_STRING(entry->name), JSTRACE_STRING);
3016 return JS_DHASH_NEXT;
3017 }
3018
3019 static void
3020 TraceLocalNames(JSTracer *trc, JSFunction *fun)
3021 {
3022 uintN n, i;
3023 JSAtom *atom;
3024 jsuword *array;
3025
3026 JS_ASSERT(FUN_INTERPRETED(fun));
3027 n = fun->countLocalNames();
3028 if (n == 0)
3029 return;
3030 if (n <= MAX_ARRAY_LOCALS) {
3031 array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
3032 i = n;
3033 do {
3034 --i;
3035 atom = (JSAtom *) (array[i] & ~1);
3036 if (atom) {
3037 JS_SET_TRACING_INDEX(trc,
3038 i < fun->nargs ? "arg" : "var",
3039 i < fun->nargs ? i : i - fun->nargs);
3040 JS_CallTracer(trc, ATOM_TO_STRING(atom), JSTRACE_STRING);
3041 }
3042 } while (i != 0);
3043 } else {
3044 JS_DHashTableEnumerate(&fun->u.i.names.map->names,
3045 trace_local_names_enumerator, trc);
3046
3047 /*
3048 * No need to trace the list of duplicates in map->lastdup as the
3049 * names there are traced when enumerating the hash table.
3050 */
3051 }
3052 }
3053
3054 void
3055 DestroyLocalNames(JSContext *cx, JSFunction *fun)
3056 {
3057 uintN n;
3058
3059 n = fun->countLocalNames();
3060 if (n <= 1)
3061 return;
3062 if (n <= MAX_ARRAY_LOCALS)
3063 JS_free(cx, fun->u.i.names.array);
3064 else
3065 FreeLocalNameHash(cx, fun->u.i.names.map);
3066 }
3067
3068 void
3069 js_FreezeLocalNames(JSContext *cx, JSFunction *fun)
3070 {
3071 uintN n;
3072 jsuword *array;
3073
3074 JS_ASSERT(FUN_INTERPRETED(fun));
3075 JS_ASSERT(!fun->u.i.script);
3076 n = fun->nargs + fun->u.i.nvars + fun->u.i.nupvars;
3077 if (2 <= n && n < MAX_ARRAY_LOCALS) {
3078 /* Shrink over-allocated array ignoring realloc failures. */
3079 array = (jsuword *) JS_realloc(cx, fun->u.i.names.array,
3080 n * sizeof *array);
3081 if (array)
3082 fun->u.i.names.array = array;
3083 }
3084 #ifdef DEBUG
3085 // XXX no JS_DHashMarkTableImmutable on 1.9.1
3086 // if (n > MAX_ARRAY_LOCALS)
3087 // JS_DHashMarkTableImmutable(&fun->u.i.names.map->names);
3088 #endif
3089 }

  ViewVC Help
Powered by ViewVC 1.1.24