/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsfun.cpp
C++ | 2101 lines | 1530 code | 230 blank | 341 comment | 355 complexity | f75c575ae46a95c89a215ee036a1f7df MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
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. */ 81enum { 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 100JSBool 101js_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 119static JSBool 120MarkArgDeleted(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. */ 161static JSBool 162ArgWasDeleted(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 181JSBool 182js_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 239JSObject * 240js_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 285static JSBool 286args_enumerate(JSContext *cx, JSObject *obj); 287 288JS_FRIEND_API(JSBool) 289js_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 339static JSBool 340args_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 368static JSBool 369args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) 370{ 371 jsint slot; 372 JSStackFrame *fp; 373 374 if (!JSVAL_IS_INT(id)) 375 return JS_TRUE; 376 fp = (JSStackFrame *) 377 JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); 378 if (!fp) 379 return JS_TRUE; 380 JS_ASSERT(fp->argsobj); 381 382 slot = JSVAL_TO_INT(id); 383 switch (slot) { 384 case ARGS_CALLEE: 385 if (!TEST_OVERRIDE_BIT(fp, slot)) 386 *vp = OBJECT_TO_JSVAL(fp->callee); 387 break; 388 389 case ARGS_LENGTH: 390 if (!TEST_OVERRIDE_BIT(fp, slot)) 391 *vp = INT_TO_JSVAL((jsint)fp->argc); 392 break; 393 394 default: 395 if ((uintN)slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) 396 *vp = fp->argv[slot]; 397 break; 398 } 399 return JS_TRUE; 400} 401 402static JSBool 403args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) 404{ 405 JSStackFrame *fp; 406 jsint slot; 407 408 if (!JSVAL_IS_INT(id)) 409 return JS_TRUE; 410 fp = (JSStackFrame *) 411 JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); 412 if (!fp) 413 return JS_TRUE; 414 JS_ASSERT(fp->argsobj); 415 416 slot = JSVAL_TO_INT(id); 417 switch (slot) { 418 case ARGS_CALLEE: 419 case ARGS_LENGTH: 420 SET_OVERRIDE_BIT(fp, slot); 421 break; 422 423 default: 424 if (FUN_INTERPRETED(fp->fun) && 425 (uintN)slot < fp->argc && 426 !ArgWasDeleted(cx, fp, slot)) { 427 fp->argv[slot] = *vp; 428 } 429 break; 430 } 431 return JS_TRUE; 432} 433 434static JSBool 435args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, 436 JSObject **objp) 437{ 438 JSStackFrame *fp; 439 uintN slot; 440 JSString *str; 441 JSAtom *atom; 442 intN tinyid; 443 jsval value; 444 445 *objp = NULL; 446 fp = (JSStackFrame *) 447 JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); 448 if (!fp) 449 return JS_TRUE; 450 JS_ASSERT(fp->argsobj); 451 452 if (JSVAL_IS_INT(id)) { 453 slot = JSVAL_TO_INT(id); 454 if (slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) { 455 /* XXX ECMA specs DontEnum, contrary to other array-like objects */ 456 if (!js_DefineProperty(cx, obj, INT_JSVAL_TO_JSID(id), 457 fp->argv[slot], 458 args_getProperty, args_setProperty, 459 0, NULL)) { 460 return JS_FALSE; 461 } 462 *objp = obj; 463 } 464 } else { 465 str = JSVAL_TO_STRING(id); 466 atom = cx->runtime->atomState.lengthAtom; 467 if (str == ATOM_TO_STRING(atom)) { 468 tinyid = ARGS_LENGTH; 469 value = INT_TO_JSVAL(fp->argc); 470 } else { 471 atom = cx->runtime->atomState.calleeAtom; 472 if (str == ATOM_TO_STRING(atom)) { 473 tinyid = ARGS_CALLEE; 474 value = OBJECT_TO_JSVAL(fp->callee); 475 } else { 476 atom = NULL; 477 478 /* Quell GCC overwarnings. */ 479 tinyid = 0; 480 value = JSVAL_NULL; 481 } 482 } 483 484 if (atom && !TEST_OVERRIDE_BIT(fp, tinyid)) { 485 if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, 486 args_getProperty, args_setProperty, 0, 487 SPROP_HAS_SHORTID, tinyid, NULL)) { 488 return JS_FALSE; 489 } 490 *objp = obj; 491 } 492 } 493 494 return JS_TRUE; 495} 496 497static JSBool 498args_enumerate(JSContext *cx, JSObject *obj) 499{ 500 JSStackFrame *fp; 501 JSObject *pobj; 502 JSProperty *prop; 503 uintN slot, argc; 504 505 fp = (JSStackFrame *) 506 JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); 507 if (!fp) 508 return JS_TRUE; 509 JS_ASSERT(fp->argsobj); 510 511 /* 512 * Trigger reflection with value snapshot in args_resolve using a series 513 * of js_LookupProperty calls. We handle length, callee, and the indexed 514 * argument properties. We know that args_resolve covers all these cases 515 * and creates direct properties of obj, but that it may fail to resolve 516 * length or callee if overridden. 517 */ 518 if (!js_LookupProperty(cx, obj, 519 ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), 520 &pobj, &prop)) { 521 return JS_FALSE; 522 } 523 if (prop) 524 OBJ_DROP_PROPERTY(cx, pobj, prop); 525 526 if (!js_LookupProperty(cx, obj, 527 ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), 528 &pobj, &prop)) { 529 return JS_FALSE; 530 } 531 if (prop) 532 OBJ_DROP_PROPERTY(cx, pobj, prop); 533 534 argc = fp->argc; 535 for (slot = 0; slot < argc; slot++) { 536 if (!js_LookupProperty(cx, obj, INT_TO_JSID((jsint)slot), &pobj, &prop)) 537 return JS_FALSE; 538 if (prop) 539 OBJ_DROP_PROPERTY(cx, pobj, prop); 540 } 541 return JS_TRUE; 542} 543 544#if JS_HAS_GENERATORS 545/* 546 * If a generator-iterator's arguments or call object escapes, it needs to 547 * mark its generator object. 548 */ 549static void 550args_or_call_trace(JSTracer *trc, JSObject *obj) 551{ 552 JSStackFrame *fp; 553 554 fp = (JSStackFrame *) JS_GetPrivate(trc->context, obj); 555 if (fp && (fp->flags & JSFRAME_GENERATOR)) { 556 JS_CALL_OBJECT_TRACER(trc, FRAME_TO_GENERATOR(fp)->obj, 557 "FRAME_TO_GENERATOR(fp)->obj"); 558 } 559} 560#else 561# define args_or_call_trace NULL 562#endif 563 564/* 565 * The Arguments class is not initialized via JS_InitClass, and must not be, 566 * because its name is "Object". Per ECMA, that causes instances of it to 567 * delegate to the object named by Object.prototype. It also ensures that 568 * arguments.toString() returns "[object Object]". 569 * 570 * The JSClass functions below collaborate to lazily reflect and synchronize 571 * actual argument values, argument count, and callee function object stored 572 * in a JSStackFrame with their corresponding property values in the frame's 573 * arguments object. 574 */ 575JSClass js_ArgumentsClass = { 576 js_Object_str, 577 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) | 578 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object), 579 JS_PropertyStub, args_delProperty, 580 args_getProperty, args_setProperty, 581 args_enumerate, (JSResolveOp) args_resolve, 582 JS_ConvertStub, JS_FinalizeStub, 583 NULL, NULL, 584 NULL, NULL, 585 NULL, NULL, 586 JS_CLASS_TRACE(args_or_call_trace), NULL 587}; 588 589#define JSSLOT_SCRIPTED_FUNCTION (JSSLOT_PRIVATE + 1) 590#define JSSLOT_CALL_ARGUMENTS (JSSLOT_PRIVATE + 2) 591#define CALL_CLASS_FIXED_RESERVED_SLOTS 2 592 593JSObject * 594js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent) 595{ 596 JSObject *callobj, *funobj; 597 598 /* Create a call object for fp only if it lacks one. */ 599 JS_ASSERT(fp->fun); 600 callobj = fp->callobj; 601 if (callobj) 602 return callobj; 603 604 /* The default call parent is its function's parent (static link). */ 605 if (!parent) { 606 funobj = fp->callee; 607 if (funobj) 608 parent = OBJ_GET_PARENT(cx, funobj); 609 } 610 611 /* Create the call object and link it to its stack frame. */ 612 callobj = js_NewObject(cx, &js_CallClass, NULL, parent, 0); 613 if (!callobj) 614 return NULL; 615 616 JS_SetPrivate(cx, callobj, fp); 617 STOBJ_SET_SLOT(callobj, JSSLOT_SCRIPTED_FUNCTION, 618 OBJECT_TO_JSVAL(FUN_OBJECT(fp->fun))); 619 fp->callobj = callobj; 620 621 /* Make callobj be the scope chain and the variables object. */ 622 JS_ASSERT(fp->scopeChain == parent); 623 fp->scopeChain = callobj; 624 fp->varobj = callobj; 625 return callobj; 626} 627 628JSFunction * 629js_GetCallObjectFunction(JSObject *obj) 630{ 631 jsval v; 632 633 JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass); 634 v = STOBJ_GET_SLOT(obj, JSSLOT_SCRIPTED_FUNCTION); 635 if (JSVAL_IS_VOID(v)) { 636 /* Newborn or prototype object. */ 637 return NULL; 638 } 639 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); 640 return (JSFunction *) JSVAL_TO_OBJECT(v); 641} 642 643JS_FRIEND_API(JSBool) 644js_PutCallObject(JSContext *cx, JSStackFrame *fp) 645{ 646 JSObject *callobj; 647 JSBool ok; 648 JSFunction *fun; 649 uintN n; 650 JSScope *scope; 651 652 /* 653 * Since for a call object all fixed slots happen to be taken, we can copy 654 * arguments and variables straight into JSObject.dslots. 655 */ 656 JS_STATIC_ASSERT(JS_INITIAL_NSLOTS - JSSLOT_PRIVATE == 657 1 + CALL_CLASS_FIXED_RESERVED_SLOTS); 658 659 callobj = fp->callobj; 660 if (!callobj) 661 return JS_TRUE; 662 663 /* 664 * Get the arguments object to snapshot fp's actual argument values. 665 */ 666 ok = JS_TRUE; 667 if (fp->argsobj) { 668 if (!TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { 669 STOBJ_SET_SLOT(callobj, JSSLOT_CALL_ARGUMENTS, 670 OBJECT_TO_JSVAL(fp->argsobj)); 671 } 672 ok &= js_PutArgsObject(cx, fp); 673 } 674 675 fun = fp->fun; 676 JS_ASSERT(fun == js_GetCallObjectFunction(callobj)); 677 n = JS_GET_LOCAL_NAME_COUNT(fun); 678 if (n != 0) { 679 JS_LOCK_OBJ(cx, callobj); 680 n += JS_INITIAL_NSLOTS; 681 if (n > STOBJ_NSLOTS(callobj)) 682 ok &= js_ReallocSlots(cx, callobj, n, JS_TRUE); 683 scope = OBJ_SCOPE(callobj); 684 if (ok) { 685 memcpy(callobj->dslots, fp->argv, fun->nargs * sizeof(jsval)); 686 memcpy(callobj->dslots + fun->nargs, fp->slots, 687 fun->u.i.nvars * sizeof(jsval)); 688 if (scope->object == callobj && n > scope->map.freeslot) 689 scope->map.freeslot = n; 690 } 691 JS_UNLOCK_SCOPE(cx, scope); 692 } 693 694 /* 695 * Clear the private pointer to fp, which is about to go away (js_Invoke). 696 * Do this last because js_GetProperty calls above need to follow the 697 * private slot to find fp. 698 */ 699 JS_SetPrivate(cx, callobj, NULL); 700 fp->callobj = NULL; 701 return ok; 702} 703 704static JSBool 705call_enumerate(JSContext *cx, JSObject *obj) 706{ 707 JSFunction *fun; 708 uintN n, i; 709 void *mark; 710 jsuword *names; 711 JSBool ok; 712 JSAtom *name; 713 JSObject *pobj; 714 JSProperty *prop; 715 716 fun = js_GetCallObjectFunction(obj); 717 n = JS_GET_LOCAL_NAME_COUNT(fun); 718 if (n == 0) 719 return JS_TRUE; 720 721 mark = JS_ARENA_MARK(&cx->tempPool); 722 723 MUST_FLOW_THROUGH("out"); 724 names = js_GetLocalNameArray(cx, fun, &cx->tempPool); 725 if (!names) { 726 ok = JS_FALSE; 727 goto out; 728 } 729 730 for (i = 0; i != n; ++i) { 731 name = JS_LOCAL_NAME_TO_ATOM(names[i]); 732 if (!name) 733 continue; 734 735 /* 736 * Trigger reflection by looking up the name of the argument or 737 * variable. 738 */ 739 ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(name), &pobj, &prop); 740 if (!ok) 741 goto out; 742 743 /* 744 * At this point the call object always has a property corresponding 745 * to the local name because call_resolve creates the property using 746 * JSPROP_PERMANENT. 747 */ 748 JS_ASSERT(prop && pobj == obj); 749 OBJ_DROP_PROPERTY(cx, pobj, prop); 750 } 751 ok = JS_TRUE; 752 753 out: 754 JS_ARENA_RELEASE(&cx->tempPool, mark); 755 return ok; 756} 757 758typedef enum JSCallPropertyKind { 759 JSCPK_ARGUMENTS, 760 JSCPK_ARG, 761 JSCPK_VAR 762} JSCallPropertyKind; 763 764static JSBool 765CallPropertyOp(JSContext *cx, JSObject *obj, jsid id, jsval *vp, 766 JSCallPropertyKind kind, JSBool setter) 767{ 768 JSFunction *fun; 769 JSStackFrame *fp; 770 uintN i; 771 jsval *array; 772 773 if (STOBJ_GET_CLASS(obj) != &js_CallClass) 774 return JS_TRUE; 775 776 fun = js_GetCallObjectFunction(obj); 777 fp = (JSStackFrame *) JS_GetPrivate(cx, obj); 778 779 if (kind == JSCPK_ARGUMENTS) { 780 if (setter) { 781 if (fp) 782 SET_OVERRIDE_BIT(fp, CALL_ARGUMENTS); 783 STOBJ_SET_SLOT(obj, JSSLOT_CALL_ARGUMENTS, *vp); 784 } else { 785 if (fp && !TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { 786 JSObject *argsobj; 787 788 argsobj = js_GetArgsObject(cx, fp); 789 if (!argsobj) 790 return JS_FALSE; 791 *vp = OBJECT_TO_JSVAL(argsobj); 792 } else { 793 *vp = STOBJ_GET_SLOT(obj, JSSLOT_CALL_ARGUMENTS); 794 } 795 } 796 return JS_TRUE; 797 } 798 799 JS_ASSERT((int16) JSVAL_TO_INT(id) == JSVAL_TO_INT(id)); 800 i = (uint16) JSVAL_TO_INT(id); 801 JS_ASSERT_IF(kind == JSCPK_ARG, i < fun->nargs); 802 JS_ASSERT_IF(kind == JSCPK_VAR, i < fun->u.i.nvars); 803 804 if (!fp) { 805 i += CALL_CLASS_FIXED_RESERVED_SLOTS; 806 if (kind == JSCPK_VAR) 807 i += fun->nargs; 808 else 809 JS_ASSERT(kind == JSCPK_ARG); 810 return setter 811 ? JS_SetReservedSlot(cx, obj, i, *vp) 812 : JS_GetReservedSlot(cx, obj, i, vp); 813 } 814 815 if (kind == JSCPK_ARG) { 816 array = fp->argv; 817 } else { 818 JS_ASSERT(kind == JSCPK_VAR); 819 array = fp->slots; 820 } 821 if (setter) 822 array[i] = *vp; 823 else 824 *vp = array[i]; 825 return JS_TRUE; 826} 827 828static JSBool 829GetCallArguments(JSContext *cx, JSObject *obj, jsid id, jsval *vp) 830{ 831 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARGUMENTS, JS_FALSE); 832} 833 834static JSBool 835SetCallArguments(JSContext *cx, JSObject *obj, jsid id, jsval *vp) 836{ 837 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARGUMENTS, JS_TRUE); 838} 839 840JSBool 841js_GetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp) 842{ 843 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG, JS_FALSE); 844} 845 846static JSBool 847SetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp) 848{ 849 return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG, JS_TRUE); 850} 851 852JSBool 853js_GetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp) 854{ 855 return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_FALSE); 856} 857 858static JSBool 859SetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp) 860{ 861 return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_TRUE); 862} 863 864static JSBool 865call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags, 866 JSObject **objp) 867{ 868 JSFunction *fun; 869 jsid id; 870 JSLocalKind localKind; 871 JSPropertyOp getter, setter; 872 uintN slot, attrs; 873 874 if (!JSVAL_IS_STRING(idval)) 875 return JS_TRUE; 876 877 fun = js_GetCallObjectFunction(obj); 878 if (!fun) 879 return JS_TRUE; 880 881 if (!js_ValueToStringId(cx, idval, &id)) 882 return JS_FALSE; 883 884 localKind = js_LookupLocal(cx, fun, JSID_TO_ATOM(id), &slot); 885 if (localKind != JSLOCAL_NONE) { 886 JS_ASSERT((uint16) slot == slot); 887 attrs = JSPROP_PERMANENT | JSPROP_SHARED; 888 if (localKind == JSLOCAL_ARG) { 889 JS_ASSERT(slot < fun->nargs); 890 getter = js_GetCallArg; 891 setter = SetCallArg; 892 } else { 893 JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST); 894 JS_ASSERT(slot < fun->u.i.nvars); 895 getter = js_GetCallVar; 896 setter = SetCallVar; 897 if (localKind == JSLOCAL_CONST) 898 attrs |= JSPROP_READONLY; 899 } 900 if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, getter, setter, 901 attrs, SPROP_HAS_SHORTID, (int16) slot, 902 NULL)) { 903 return JS_FALSE; 904 } 905 *objp = obj; 906 return JS_TRUE; 907 } 908 909 /* 910 * Resolve arguments so that we never store a particular Call object's 911 * arguments object reference in a Call prototype's |arguments| slot. 912 */ 913 if (id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) { 914 if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, 915 GetCallArguments, SetCallArguments, 916 JSPROP_PERMANENT | JSPROP_SHARED, 917 0, 0, NULL)) { 918 return JS_FALSE; 919 } 920 *objp = obj; 921 return JS_TRUE; 922 } 923 return JS_TRUE; 924} 925 926static JSBool 927call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) 928{ 929 JSStackFrame *fp; 930 931 if (type == JSTYPE_FUNCTION) { 932 fp = (JSStackFrame *) JS_GetPrivate(cx, obj); 933 if (fp) { 934 JS_ASSERT(fp->fun); 935 *vp = OBJECT_TO_JSVAL(fp->callee); 936 } 937 } 938 return JS_TRUE; 939} 940 941static uint32 942call_reserveSlots(JSContext *cx, JSObject *obj) 943{ 944 JSFunction *fun; 945 946 fun = js_GetCallObjectFunction(obj); 947 return JS_GET_LOCAL_NAME_COUNT(fun); 948} 949 950JS_FRIEND_DATA(JSClass) js_CallClass = { 951 js_Call_str, 952 JSCLASS_HAS_PRIVATE | 953 JSCLASS_HAS_RESERVED_SLOTS(CALL_CLASS_FIXED_RESERVED_SLOTS) | 954 JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS | 955 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Call), 956 JS_PropertyStub, JS_PropertyStub, 957 JS_PropertyStub, JS_PropertyStub, 958 call_enumerate, (JSResolveOp)call_resolve, 959 call_convert, JS_FinalizeStub, 960 NULL, NULL, 961 NULL, NULL, 962 NULL, NULL, 963 JS_CLASS_TRACE(args_or_call_trace), call_reserveSlots 964}; 965 966static JSBool 967fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) 968{ 969 jsint slot; 970 JSFunction *fun; 971 JSStackFrame *fp; 972 JSSecurityCallbacks *callbacks; 973 974 if (!JSVAL_IS_INT(id)) 975 return JS_TRUE; 976 slot = JSVAL_TO_INT(id); 977 978 /* 979 * Loop because getter and setter can be delegated from another class, 980 * but loop only for ARGS_LENGTH because we must pretend that f.length 981 * is in each function instance f, per ECMA-262, instead of only in the 982 * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED 983 * to make it appear so). 984 * 985 * This code couples tightly to the attributes for the function_props[] 986 * initializers above, and to js_SetProperty and js_HasOwnProperty. 987 * 988 * It's important to allow delegating objects, even though they inherit 989 * this getter (fun_getProperty), to override arguments, arity, caller, 990 * and name. If we didn't return early for slot != ARGS_LENGTH, we would 991 * clobber *vp with the native property value, instead of letting script 992 * override that value in delegating objects. 993 * 994 * Note how that clobbering is what simulates JSPROP_READONLY for all of 995 * the non-standard properties when the directly addressed object (obj) 996 * is a function object (i.e., when this loop does not iterate). 997 */ 998 while (!(fun = (JSFunction *) 999 JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) { 1000 if (slot != ARGS_LENGTH) 1001 return JS_TRUE; 1002 obj = OBJ_GET_PROTO(cx, obj); 1003 if (!obj) 1004 return JS_TRUE; 1005 } 1006 1007 /* Find fun's top-most activation record. */ 1008 for (fp = cx->fp; fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL)); 1009 fp = fp->down) { 1010 continue; 1011 } 1012 1013 switch (slot) { 1014 case CALL_ARGUMENTS: 1015 /* Warn if strict about f.arguments or equivalent unqualified uses. */ 1016 if (!JS_ReportErrorFlagsAndNumber(cx, 1017 JSREPORT_WARNING | JSREPORT_STRICT, 1018 js_GetErrorMessage, NULL, 1019 JSMSG_DEPRECATED_USAGE, 1020 js_arguments_str)) { 1021 return JS_FALSE; 1022 } 1023 if (fp) { 1024 if (!js_GetArgsValue(cx, fp, vp)) 1025 return JS_FALSE; 1026 } else { 1027 *vp = JSVAL_NULL; 1028 } 1029 break; 1030 1031 case ARGS_LENGTH: 1032 case FUN_ARITY: 1033 *vp = INT_TO_JSVAL((jsint)fun->nargs); 1034 break; 1035 1036 case FUN_NAME: 1037 *vp = fun->atom 1038 ? ATOM_KEY(fun->atom) 1039 : STRING_TO_JSVAL(cx->runtime->emptyString); 1040 break; 1041 1042 case FUN_CALLER: 1043 if (fp && fp->down && fp->down->fun) 1044 *vp = OBJECT_TO_JSVAL(fp->down->callee); 1045 else 1046 *vp = JSVAL_NULL; 1047 if (!JSVAL_IS_PRIMITIVE(*vp)) { 1048 callbacks = JS_GetSecurityCallbacks(cx); 1049 if (callbacks && callbacks->checkObjectAccess) { 1050 id = ATOM_KEY(cx->runtime->atomState.callerAtom); 1051 if (!callbacks->checkObjectAccess(cx, obj, id, JSACC_READ, vp)) 1052 return JS_FALSE; 1053 } 1054 } 1055 break; 1056 1057 default: 1058 /* XXX fun[0] and fun.arguments[0] are equivalent. */ 1059 if (fp && fp->fun && (uintN)slot < fp->fun->nargs) 1060 *vp = fp->argv[slot]; 1061 break; 1062 } 1063 1064 return JS_TRUE; 1065} 1066 1067/* 1068 * ECMA-262 specifies that length is a property of function object instances, 1069 * but we can avoid that space cost by delegating to a prototype property that 1070 * is JSPROP_PERMANENT and JSPROP_SHARED. Each fun_getProperty call computes 1071 * a fresh length value based on the arity of the individual function object's 1072 * private data. 1073 * 1074 * The extensions below other than length, i.e., the ones not in ECMA-262, 1075 * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility 1076 * with ECMA we must allow a delegating object to override them. Therefore to 1077 * avoid entraining garbage in Function.prototype slots, they must be resolved 1078 * in non-prototype function objects, wherefore the lazy_function_props table 1079 * and fun_resolve's use of it. 1080 */ 1081#define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED) 1082 1083static JSPropertySpec function_props[] = { 1084 {js_length_str, ARGS_LENGTH, LENGTH_PROP_ATTRS, fun_getProperty, JS_PropertyStub}, 1085 {0,0,0,0,0} 1086}; 1087 1088typedef struct LazyFunctionProp { 1089 uint16 atomOffset; 1090 int8 tinyid; 1091 uint8 attrs; 1092} LazyFunctionProp; 1093 1094/* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */ 1095static LazyFunctionProp lazy_function_props[] = { 1096 {ATOM_OFFSET(arguments), CALL_ARGUMENTS, JSPROP_PERMANENT}, 1097 {ATOM_OFFSET(arity), FUN_ARITY, JSPROP_PERMANENT}, 1098 {ATOM_OFFSET(caller), FUN_CALLER, JSPROP_PERMANENT}, 1099 {ATOM_OFFSET(name), FUN_NAME, JSPROP_PERMANENT}, 1100}; 1101 1102static JSBool 1103fun_enumerate(JSContext *cx, JSObject *obj) 1104{ 1105 jsid prototypeId; 1106 JSObject *pobj; 1107 JSProperty *prop; 1108 1109 prototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); 1110 if (!OBJ_LOOKUP_PROPERTY(cx, obj, prototypeId, &pobj, &prop)) 1111 return JS_FALSE; 1112 if (prop) 1113 OBJ_DROP_PROPERTY(cx, pobj, prop); 1114 return JS_TRUE; 1115} 1116 1117static JSBool 1118fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, 1119 JSObject **objp) 1120{ 1121 JSFunction *fun; 1122 JSAtom *atom; 1123 uintN i; 1124 1125 if (!JSVAL_IS_STRING(id)) 1126 return JS_TRUE; 1127 1128 fun = GET_FUNCTION_PRIVATE(cx, obj); 1129 1130 /* 1131 * No need to reflect fun.prototype in 'fun.prototype = ... '. 1132 * 1133 * This is not just an optimization, because we must not resolve when 1134 * defining hidden properties during compilation. The setup code for the 1135 * prototype and the lazy properties below eventually calls the property 1136 * hooks for the function object. That in turn calls fun_reserveSlots to 1137 * get the number of the reserved slots which is just the number of 1138 * regular expressions literals in the function. When compiling, that 1139 * number is not yet ready so we must make sure that fun_resolve does 1140 * nothing until the code for the function is generated. 1141 */ 1142 if (flags & JSRESOLVE_ASSIGNING) 1143 return JS_TRUE; 1144 1145 /* 1146 * Ok, check whether id is 'prototype' and bootstrap the function object's 1147 * prototype property. 1148 */ 1149 atom = cx->runtime->atomState.classPrototypeAtom; 1150 if (id == ATOM_KEY(atom)) { 1151 JSObject *proto; 1152 1153 /* 1154 * Beware of the wacky case of a user function named Object -- trying 1155 * to find a prototype for that will recur back here _ad perniciem_. 1156 */ 1157 if (fun->atom == CLASS_ATOM(cx, Object)) 1158 return JS_TRUE; 1159 1160 /* 1161 * Make the prototype object to have the same parent as the function 1162 * object itself. 1163 */ 1164 proto = js_NewObject(cx, &js_ObjectClass, NULL, OBJ_GET_PARENT(cx, obj), 1165 0); 1166 if (!proto) 1167 return JS_FALSE; 1168 1169 /* 1170 * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for 1171 * user-defined functions, but DontEnum | ReadOnly | DontDelete for 1172 * native "system" constructors such as Object or Function. So lazily 1173 * set the former here in fun_resolve, but eagerly define the latter 1174 * in JS_InitClass, with the right attributes. 1175 */ 1176 if (!js_SetClassPrototype(cx, obj, proto, 1177 JSPROP_ENUMERATE | JSPROP_PERMANENT)) { 1178 cx->weakRoots.newborn[GCX_OBJECT] = NULL; 1179 return JS_FALSE; 1180 } 1181 *objp = obj; 1182 return JS_TRUE; 1183 } 1184 1185 for (i = 0; i < JS_ARRAY_LENGTH(lazy_function_props); i++) { 1186 LazyFunctionProp *lfp = &lazy_function_props[i]; 1187 1188 atom = OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset); 1189 if (id == ATOM_KEY(atom)) { 1190 if (!js_DefineNativeProperty(cx, obj, 1191 ATOM_TO_JSID(atom), JSVAL_VOID, 1192 fun_getProperty, JS_PropertyStub, 1193 lfp->attrs, SPROP_HAS_SHORTID, 1194 lfp->tinyid, NULL)) { 1195 return JS_FALSE; 1196 } 1197 *objp = obj; 1198 return JS_TRUE; 1199 } 1200 } 1201 1202 return JS_TRUE; 1203} 1204 1205static JSBool 1206fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) 1207{ 1208 switch (type) { 1209 case JSTYPE_FUNCTION: 1210 *vp = OBJECT_TO_JSVAL(obj); 1211 return JS_TRUE; 1212 default: 1213 return js_TryValueOf(cx, obj, type, vp); 1214 } 1215} 1216 1217#if JS_HAS_XDR 1218 1219/* XXX store parent and proto, if defined */ 1220static JSBool 1221fun_xdrObject(JSXDRState *xdr, JSObject **objp) 1222{ 1223 JSContext *cx; 1224 JSFunction *fun; 1225 uint32 nullAtom; /* flag to indicate if fun->atom is NULL */ 1226 uintN nargs, nvars, n; 1227 uint32 localsword; /* word to xdr argument and variable counts */ 1228 uint32 flagsword; /* originally only flags was JS_XDRUint8'd */ 1229 JSTempValueRooter tvr; 1230 JSBool ok; 1231 1232 cx = xdr->cx; 1233 if (xdr->mode == JSXDR_ENCODE) { 1234 fun = GET_FUNCTION_PRIVATE(cx, *objp); 1235 if (!FUN_INTERPRETED(fun)) { 1236 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, 1237 JSMSG_NOT_SCRIPTED_FUNCTION, 1238 JS_GetFunctionName(fun)); 1239 return JS_FALSE; 1240 } 1241 nullAtom = !fun->atom; 1242 nargs = fun->nargs; 1243 nvars = fun->u.i.nvars; 1244 localsword = (nargs << 16) | nvars; 1245 flagsword = fun->flags; 1246 } else { 1247 fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL); 1248 if (!fun) 1249 return JS_FALSE; 1250 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun)); 1251 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun)); 1252#ifdef __GNUC__ 1253 nvars = nargs = 0; /* quell GCC uninitialized warning */ 1254#endif 1255 } 1256 1257 /* From here on, control flow must flow through label out. */ 1258 JS_PUSH_TEMP_ROOT_OBJECT(cx, FUN_OBJECT(fun), &tvr); 1259 ok = JS_TRUE; 1260 1261 if (!JS_XDRUint32(xdr, &nullAtom)) 1262 goto bad; 1263 if (!nullAtom && !js_XDRStringAtom(xdr, &fun->atom)) 1264 goto bad; 1265 if (!JS_XDRUint32(xdr, &localsword) || 1266 !JS_XDRUint32(xdr, &flagsword)) { 1267 goto bad; 1268 } 1269 1270 if (xdr->mode == JSXDR_DECODE) { 1271 nargs = localsword >> 16; 1272 nvars = localsword & JS_BITMASK(16); 1273 JS_ASSERT(flagsword | JSFUN_INTERPRETED); 1274 fun->flags = (uint16) flagsword; 1275 } 1276 1277 /* do arguments and local vars */ 1278 n = nargs + nvars; 1279 if (n != 0) { 1280 void *mark; 1281 uintN i; 1282 uintN bitmapLength; 1283 uint32 *bitmap; 1284 jsuword *names; 1285 JSAtom *name; 1286 JSLocalKind localKind; 1287 1288 mark = JS_ARENA_MARK(&xdr->cx->tempPool); 1289 1290 /* 1291 * From this point the control must flow via the label release_mark. 1292 * 1293 * To xdr the names we prefix the names with a bitmap descriptor and 1294 * then xdr the names as strings. For argument names (indexes below 1295 * nargs) the corresponding bit in the bitmap is unset when the name 1296 * is null. Such null names are not encoded or decoded. For variable 1297 * names (indexes starting from nargs) bitmap's bit is set when the 1298 * name is declared as const, not as ordinary var. 1299 * */ 1300 bitmapLength = JS_HOWMANY(n, JS_BITS_PER_UINT32); 1301 JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &xdr->cx->tempPool, 1302 bitmapLength * sizeof *bitmap); 1303 if (!bitmap) { 1304 js_ReportOutOfScriptQuota(xdr->cx); 1305 ok = JS_FALSE; 1306 goto release_mark; 1307 } 1308 if (xdr->mode == JSXDR_ENCODE) { 1309 names = js_GetLocalNameArray(xdr->cx, fun, &xdr->cx->tempPool); 1310 if (!names) { 1311 ok = JS_FALSE; 1312 goto release_mark; 1313 } 1314 memset(bitmap, 0, bitmapLength * sizeof *bitmap); 1315 for (i = 0; i != n; ++i) { 1316 if (i < fun->nargs 1317 ? JS_LOCAL_NAME_TO_ATOM(names[i]) != NULL 1318 : JS_LOCAL_NAME_IS_CONST(names[i])) { 1319 bitmap[i >> JS_BITS_PER_UINT32_LOG2] |= 1320 JS_BIT(i & (JS_BITS_PER_UINT32 - 1)); 1321 } 1322 } 1323 } 1324#ifdef __GNUC__ 1325 else { 1326 names = NULL; /* quell GCC uninitialized warning */ 1327 } 1328#endif 1329 for (i = 0; i != bitmapLength; ++i) { 1330 ok = JS_XDRUint32(xdr, &bitmap[i]); 1331 if (!ok) 1332 goto release_mark; 1333 } 1334 for (i = 0; i != n; ++i) { 1335 if (i < nargs && 1336 !(bitmap[i >> JS_BITS_PER_UINT32_LOG2] & 1337 JS_BIT(i & (JS_BITS_PER_UINT32 - 1)))) { 1338 if (xdr->mode == JSXDR_DECODE) { 1339 ok = js_AddLocal(xdr->cx, fun, NULL, JSLOCAL_ARG); 1340 if (!ok) 1341 goto release_mark; 1342 } else { 1343 JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names[i])); 1344 } 1345 continue; 1346 } 1347 if (xdr->mode == JSXDR_ENCODE) 1348 name = JS_LOCAL_NAME_TO_ATOM(names[i]); 1349 ok = js_XDRStringAtom(xdr, &name); 1350 if (!ok) 1351 goto release_mark; 1352 if (xdr->mode == JSXDR_DECODE) { 1353 localKind = (i < nargs) 1354 ? JSLOCAL_ARG 1355 : bitmap[i >> JS_BITS_PER_UINT32_LOG2] & 1356 JS_BIT(i & (JS_BITS_PER_UINT32 - 1)) 1357 ? JSLOCAL_CONST 1358 : JSLOCAL_VAR; 1359 ok = js_AddLocal(xdr->cx, fun, name, localKind); 1360 if (!ok) 1361 goto release_mark; 1362 } 1363 } 1364 ok = JS_TRUE; 1365 1366 release_mark: 1367 JS_ARENA_RELEASE(&xdr->cx->tempPool, mark); 1368 if (!ok) 1369 goto out; 1370 1371 if (xdr->mode == JSXDR_DECODE) 1372 js_FreezeLocalNames(cx, fun); 1373 } 1374 1375 if (!js_XDRScript(xdr, &fun->u.i.script, NULL)) 1376 goto bad; 1377 1378 if (xdr->mode == JSXDR_DECODE) { 1379 *objp = FUN_OBJECT(fun); 1380#ifdef CHECK_SCRIPT_OWNER 1381 fun->u.i.script->owner = NULL; 1382#endif 1383 js_CallNewScriptHook(cx, fun->u.i.script, fun); 1384 } 1385 1386out: 1387 JS_POP_TEMP_ROOT(cx, &tvr); 1388 return ok; 1389 1390bad: 1391 ok = JS_FALSE; 1392 goto out; 1393} 1394 1395#else /* !JS_HAS_XDR */ 1396 1397#define fun_xdrObject NULL 1398 1399#endif /* !JS_HAS_XDR */ 1400 1401/* 1402 * [[HasInstance]] internal method for Function objects: fetch the .prototype 1403 * property of its 'this' parameter, and walks the prototype chain of v (only 1404 * if v is an object) returning true if .prototype is found. 1405 */ 1406static JSBool 1407fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) 1408{ 1409 jsval pval; 1410 1411 if (!OBJ_GET_PROPERTY(cx, obj, 1412 ATOM_TO_JSID(cx->runtime->atomState 1413 .classPrototypeAtom), 1414 &pval)) { 1415 return JS_FALSE; 1416 } 1417 1418 if (JSVAL_IS_PRIMITIVE(pval)) { 1419 /* 1420 * Throw a runtime error if instanceof is called on a function that 1421 * has a non-object as its .prototype value. 1422 */ 1423 js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE, 1424 -1, OBJECT_TO_JSVAL(obj), NULL); 1425 return JS_FALSE; 1426 } 1427 1428 return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp); 1429} 1430 1431static void 1432TraceLocalNames(JSTracer *trc, JSFunction *fun); 1433 1434static void 1435DestroyLocalNames(JSContext *cx, JSFunction *fun); 1436 1437static void 1438fun_trace(JSTracer *trc, JSObject *obj) 1439{ 1440 JSFunction *fun; 1441 1442 /* A newborn function object may have a not yet initialized private slot. */ 1443 fun = (JSFunction *) JS_GetPrivate(trc->context, obj); 1444 if (!fun) 1445 return; 1446 1447 if (FUN_OBJECT(fun) != obj) { 1448 /* obj is cloned function object, trace the original. */ 1449 JS_CALL_TRACER(trc, FUN_OBJECT(fun), JSTRACE_OBJECT, "private"); 1450 return; 1451 } 1452 if (fun->atom) 1453 JS_CALL_STRING_TRACER(trc, ATOM_TO_STRING(fun->atom), "atom"); 1454 if (FUN_INTERPRETED(fun)) { 1455 if (fun->u.i.script) 1456 js_TraceScript(trc, fun->u.i.script); 1457 TraceLocalNames(trc, fun); 1458 } 1459} 1460 1461static void 1462fun_finalize(JSContext *cx, JSObject *obj) 1463{ 1464 JSFunction *fun; 1465 1466 /* Ignore newborn and cloned function objects. */ 1467 fun = (JSFunction *) JS_GetPrivate(cx, obj); 1468 if (!fun || FUN_OBJECT(fun) != obj) 1469 return; 1470 1471 /* 1472 * Null-check of u.i.script is required since the parser sets interpreted 1473 * very early. 1474 */ 1475 if (FUN_INTERPRETED(fun)) { 1476 if (fun->u.i.script) 1477 js_DestroyScript(cx, fun->u.i.script); 1478 DestroyLocalNames(cx, fun); 1479 } 1480} 1481 1482static uint32 1483fun_reserveSlots(JSContext *cx, JSObject *obj) 1484{ 1485 JSFunction *fun; 1486 uint32 nslots; 1487 1488 /* 1489 * We use JS_GetPrivate and not GET_FUNCTION_PRIVATE because during 1490 * js_InitFunctionClass invocation the function is called before the 1491 * private slot of the function object is set. 1492 */ 1493 fun = (JSFunction *) JS_GetPrivate(cx, obj); 1494 nslots = 0; 1495 if (fun && FUN_INTERPRETED(fun) && fun->u.i.script) { 1496 if (fun->u.i.script->upvarsOffset != 0) 1497 nslots = JS_SCRIPT_UPVARS(fun->u.i.script)->length; 1498 if (fun->u.i.script->regexpsOffset != 0) 1499 nslots += JS_SCRIPT_REGEXPS(fun->u.i.script)->length; 1500 } 1501 return nslots; 1502} 1503 1504/* 1505 * Reserve two slots in all function objects for XPConnect. Note that this 1506 * does not bloat every instance, only those on which reserved slots are set, 1507 * and those on which ad-hoc properties are defined. 1508 */ 1509JS_FRIEND_DATA(JSClass) js_FunctionClass = { 1510 js_Function_str, 1511 JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2) | 1512 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Function), 1513 JS_PropertyStub, JS_PropertyStub, 1514 JS_PropertyStub, JS_PropertyStub, 1515 fun_enumerate, (JSResolveOp)fun_resolve, 1516 fun_convert, fun_finalize, 1517 NULL, NULL, 1518 NULL, NULL, 1519 fun_xdrObject, fun_hasInstance, 1520 JS_CLASS_TRACE(fun_trace), fun_reserveSlots 1521}; 1522 1523static JSBool 1524fun_toStringHelper(JSContext *cx, uint32 indent, uintN argc, jsval *vp) 1525{ 1526 jsval fval; 1527 JSObject *obj; 1528 JSFunction *fun; 1529 JSString *str; 1530 1531 fval = JS_THIS(cx, vp); 1532 if (JSVAL_IS_NULL(fval)) 1533 return JS_FALSE; 1534 1535 if (!VALUE_IS_FUNCTION(cx, fval)) { 1536 /* 1537 * If we don't have a function to start off with, try converting the 1538 * object to a function. If that doesn't work, complain. 1539 */ 1540 if (!JSVAL_IS_PRIMITIVE(fval)) { 1541 obj = JSVAL_TO_OBJECT(fval); 1542 if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, JSTYPE_FUNCTION, 1543 &fval)) { 1544 return JS_FALSE; 1545 } 1546 vp[1] = fval; 1547 } 1548 if (!VALUE_IS_FUNCTION(cx, fval)) { 1549 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, 1550 JSMSG_INCOMPATIBLE_PROTO, 1551 js_Function_str, js_toString_str, 1552 JS_GetTypeName(cx, JS_TypeOfValue(cx, fval))); 1553 return JS_FALSE; 1554 } 1555 } 1556 1557 obj = JSVAL_TO_OBJECT(fval); 1558 if (argc != 0) { 1559 indent = js_ValueToECMAUint32(cx, &vp[2]); 1560 if (JSVAL_IS_NULL(vp[2])) 1561 return JS_FALSE; 1562 } 1563 1564 JS_ASSERT(JS_ObjectIsFunction(cx, obj)); 1565 fun = GET_FUNCTION_PRIVATE(cx, obj); 1566 if (!fun) 1567 return JS_TRUE; 1568 str = JS_DecompileFunction(cx, fun, (uintN)indent); 1569 if (!str) 1570 return JS_FALSE; 1571 *vp = STRING_TO_JSVAL(str); 1572 return JS_TRUE; 1573} 1574 1575static JSBool 1576fun_toString(JSContext *cx, uintN argc, jsval *vp) 1577{ 1578 return fun_toStringHelper(cx, 0, argc, vp); 1579} 1580 1581#if JS_HAS_TOSOURCE 1582static JSBool 1583fun_toSource(JSContext *cx, uintN argc, jsval *vp) 1584{ 1585 return fun_toStringHelper(cx, JS_DONT_PRETTY_PRINT, argc, vp); 1586} 1587#endif 1588 1589JSBool 1590js_fun_call(JSContext *cx, uintN argc, jsval *vp) 1591{ 1592 JSObject *obj; 1593 jsval fval, *argv, *invokevp; 1594 JSString *str; 1595 void *mark; 1596 JSBool ok; 1597 1598 obj = JS_THIS_OBJECT(cx, vp); 1599 if (!obj || !OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &vp[1])) 1600 return JS_FALSE; 1601 fval = vp[1]; 1602 1603 if (!VALUE_IS_FUNCTION(cx, fval)) { 1604 str = JS_ValueToString(cx, fval); 1605 if (str) { 1606 const char *bytes = js_GetStringBytes(cx, str); 1607 1608 if (bytes) { 1609 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, 1610 JSMSG_INCOMPATIBLE_PROTO, 1611 js_Function_str, js_call_str, 1612 bytes); 1613 } 1614 } 1615 return JS_FALSE; 1616 } 1617 1618 argv = vp + 2; 1619 if (argc == 0) { 1620 /* Call fun with its global object as the 'this' param if no args. */ 1621 obj…
Large files files are truncated, but you can click here to view the full file