| 1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- | |
| 2 | * vim: set ts=8 sw=4 et tw=78: | |
| 3 | * | |
| 4 | * ***** BEGIN LICENSE BLOCK ***** | |
| 5 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | |
| 6 | * | |
| 7 | * The contents of this file are subject to the Mozilla Public License Version | |
| 8 | * 1.1 (the "License"); you may not use this file except in compliance with | |
| 9 | * the License. You may obtain a copy of the License at | |
| 10 | * http://www.mozilla.org/MPL/ | |
| 11 | * | |
| 12 | * Software distributed under the License is distributed on an "AS IS" basis, | |
| 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |
| 14 | * for the specific language governing rights and limitations under the | |
| 15 | * License. | |
| 16 | * | |
| 17 | * The Original Code is Mozilla Communicator client code, released | |
| 18 | * March 31, 1998. | |
| 19 | * | |
| 20 | * The Initial Developer of the Original Code is | |
| 21 | * Netscape Communications Corporation. | |
| 22 | * Portions created by the Initial Developer are Copyright (C) 1998 | |
| 23 | * the Initial Developer. All Rights Reserved. | |
| 24 | * | |
| 25 | * Contributor(s): | |
| 26 | * | |
| 27 | * Alternatively, the contents of this file may be used under the terms of | |
| 28 | * either of the GNU General Public License Version 2 or later (the "GPL"), | |
| 29 | * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | |
| 30 | * in which case the provisions of the GPL or the LGPL are applicable instead | |
| 31 | * of those above. If you wish to allow use of your version of this file only | |
| 32 | * under the terms of either the GPL or the LGPL, and not to allow others to | |
| 33 | * use your version of this file under the terms of the MPL, indicate your | |
| 34 | * decision by deleting the provisions above and replace them with the notice | |
| 35 | * and other provisions required by the GPL or the LGPL. If you do not delete | |
| 36 | * the provisions above, a recipient may use your version of this file under | |
| 37 | * the terms of any one of the MPL, the GPL or the LGPL. | |
| 38 | * | |
| 39 | * ***** END LICENSE BLOCK ***** */ | |
| 40 | ||
| 41 | /* | |
| 42 | * JS function support. | |
| 43 | */ | |
| 44 | #include "jsstddef.h" | |
| 45 | #include <string.h> | |
| 46 | #include "jstypes.h" | |
| 47 | #include "jsbit.h" | |
| 48 | #include "jsutil.h" /* Added by JSIFY */ | |
| 49 | #include "jsapi.h" | |
| 50 | #include "jsarray.h" | |
| 51 | #include "jsatom.h" | |
| 52 | #include "jscntxt.h" | |
| 53 | #include "jsconfig.h" | |
| 54 | #include "jsdbgapi.h" | |
| 55 | #include "jsfun.h" | |
| 56 | #include "jsgc.h" | |
| 57 | #include "jsinterp.h" | |
| 58 | #include "jslock.h" | |
| 59 | #include "jsnum.h" | |
| 60 | #include "jsobj.h" | |
| 61 | #include "jsopcode.h" | |
| 62 | #include "jsparse.h" | |
| 63 | #include "jsscan.h" | |
| 64 | #include "jsscope.h" | |
| 65 | #include "jsscript.h" | |
| 66 | #include "jsstr.h" | |
| 67 | #include "jsexn.h" | |
| 68 | ||
| 69 | /* Generic function/call/arguments tinyids -- also reflected bit numbers. */ | |
| 70 | enum { | |
| 71 | CALL_ARGUMENTS = -1, /* predefined arguments local variable */ | |
| 72 | CALL_CALLEE = -2, /* reference to active function's object */ | |
| 73 | ARGS_LENGTH = -3, /* number of actual args, arity if inactive */ | |
| 74 | ARGS_CALLEE = -4, /* reference from arguments to active funobj */ | |
| 75 | FUN_ARITY = -5, /* number of formal parameters; desired argc */ | |
| 76 | FUN_NAME = -6, /* function name, "" if anonymous */ | |
| 77 | FUN_CALLER = -7 /* Function.prototype.caller, backward compat */ | |
| 78 | }; | |
| 79 | ||
| 80 | #if JSFRAME_OVERRIDE_BITS < 8 | |
| 81 | # error "not enough override bits in JSStackFrame.flags!" | |
| 82 | #endif | |
| 83 | ||
| 84 | #define TEST_OVERRIDE_BIT(fp, tinyid) \ | |
| 85 | ((fp)->flags & JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1))) | |
| 86 | ||
| 87 | #define SET_OVERRIDE_BIT(fp, tinyid) \ | |
| 88 | ((fp)->flags |= JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1))) | |
| 89 | ||
| 90 | #if JS_HAS_ARGS_OBJECT | |
| 91 | ||
| 92 | JSBool | |
| 93 | js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp) | |
| 94 | 0 | { |
| 95 | JSObject *argsobj; | |
| 96 | ||
| 97 | 0 | if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { |
| 98 | JS_ASSERT(fp->callobj); | |
| 99 | 0 | return OBJ_GET_PROPERTY(cx, fp->callobj, |
| 100 | ATOM_TO_JSID(cx->runtime->atomState | |
| 101 | .argumentsAtom), | |
| 102 | vp); | |
| 103 | } | |
| 104 | 0 | argsobj = js_GetArgsObject(cx, fp); |
| 105 | 0 | if (!argsobj) |
| 106 | 0 | return JS_FALSE; |
| 107 | 0 | *vp = OBJECT_TO_JSVAL(argsobj); |
| 108 | 0 | return JS_TRUE; |
| 109 | } | |
| 110 | ||
| 111 | static JSBool | |
| 112 | MarkArgDeleted(JSContext *cx, JSStackFrame *fp, uintN slot) | |
| 113 | 0 | { |
| 114 | JSObject *argsobj; | |
| 115 | jsval bmapval, bmapint; | |
| 116 | size_t nbits, nbytes; | |
| 117 | jsbitmap *bitmap; | |
| 118 | ||
| 119 | 0 | argsobj = fp->argsobj; |
| 120 | 0 | (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); |
| 121 | 0 | nbits = fp->argc; |
| 122 | JS_ASSERT(slot < nbits); | |
| 123 | 0 | if (JSVAL_IS_VOID(bmapval)) { |
| 124 | 0 | if (nbits <= JSVAL_INT_BITS) { |
| 125 | 0 | bmapint = 0; |
| 126 | 0 | bitmap = (jsbitmap *) &bmapint; |
| 127 | } else { | |
| 128 | 0 | nbytes = JS_HOWMANY(nbits, JS_BITS_PER_WORD) * sizeof(jsbitmap); |
| 129 | 0 | bitmap = (jsbitmap *) JS_malloc(cx, nbytes); |
| 130 | 0 | if (!bitmap) |
| 131 | 0 | return JS_FALSE; |
| 132 | 0 | memset(bitmap, 0, nbytes); |
| 133 | 0 | bmapval = PRIVATE_TO_JSVAL(bitmap); |
| 134 | 0 | JS_SetReservedSlot(cx, argsobj, 0, bmapval); |
| 135 | } | |
| 136 | } else { | |
| 137 | 0 | if (nbits <= JSVAL_INT_BITS) { |
| 138 | 0 | bmapint = JSVAL_TO_INT(bmapval); |
| 139 | 0 | bitmap = (jsbitmap *) &bmapint; |
| 140 | } else { | |
| 141 | 0 | bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval); |
| 142 | } | |
| 143 | } | |
| 144 | 0 | JS_SET_BIT(bitmap, slot); |
| 145 | 0 | if (bitmap == (jsbitmap *) &bmapint) { |
| 146 | 0 | bmapval = INT_TO_JSVAL(bmapint); |
| 147 | 0 | JS_SetReservedSlot(cx, argsobj, 0, bmapval); |
| 148 | } | |
| 149 | 0 | return JS_TRUE; |
| 150 | } | |
| 151 | ||
| 152 | /* NB: Infallible predicate, false does not mean error/exception. */ | |
| 153 | static JSBool | |
| 154 | ArgWasDeleted(JSContext *cx, JSStackFrame *fp, uintN slot) | |
| 155 | 0 | { |
| 156 | JSObject *argsobj; | |
| 157 | jsval bmapval, bmapint; | |
| 158 | jsbitmap *bitmap; | |
| 159 | ||
| 160 | 0 | argsobj = fp->argsobj; |
| 161 | 0 | (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); |
| 162 | 0 | if (JSVAL_IS_VOID(bmapval)) |
| 163 | 0 | return JS_FALSE; |
| 164 | 0 | if (fp->argc <= JSVAL_INT_BITS) { |
| 165 | 0 | bmapint = JSVAL_TO_INT(bmapval); |
| 166 | 0 | bitmap = (jsbitmap *) &bmapint; |
| 167 | } else { | |
| 168 | 0 | bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval); |
| 169 | } | |
| 170 | 0 | return JS_TEST_BIT(bitmap, slot) != 0; |
| 171 | } | |
| 172 | ||
| 173 | JSBool | |
| 174 | js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, | |
| 175 | JSObject **objp, jsval *vp) | |
| 176 | 0 | { |
| 177 | jsval val; | |
| 178 | JSObject *obj; | |
| 179 | uintN slot; | |
| 180 | ||
| 181 | 0 | if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { |
| 182 | JS_ASSERT(fp->callobj); | |
| 183 | 0 | if (!OBJ_GET_PROPERTY(cx, fp->callobj, |
| 184 | ATOM_TO_JSID(cx->runtime->atomState | |
| 185 | .argumentsAtom), | |
| 186 | &val)) { | |
| 187 | 0 | return JS_FALSE; |
| 188 | } | |
| 189 | 0 | if (JSVAL_IS_PRIMITIVE(val)) { |
| 190 | 0 | obj = js_ValueToNonNullObject(cx, val); |
| 191 | 0 | if (!obj) |
| 192 | 0 | return JS_FALSE; |
| 193 | } else { | |
| 194 | 0 | obj = JSVAL_TO_OBJECT(val); |
| 195 | } | |
| 196 | 0 | *objp = obj; |
| 197 | 0 | return OBJ_GET_PROPERTY(cx, obj, id, vp); |
| 198 | } | |
| 199 | ||
| 200 | 0 | *objp = NULL; |
| 201 | 0 | *vp = JSVAL_VOID; |
| 202 | 0 | if (JSID_IS_INT(id)) { |
| 203 | 0 | slot = (uintN) JSID_TO_INT(id); |
| 204 | 0 | if (slot < fp->argc) { |
| 205 | 0 | if (fp->argsobj && ArgWasDeleted(cx, fp, slot)) |
| 206 | 0 | return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); |
| 207 | 0 | *vp = fp->argv[slot]; |
| 208 | } else { | |
| 209 | /* | |
| 210 | * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share | |
| 211 | * storage between the formal parameter and arguments[k] for all | |
| 212 | * k >= fp->argc && k < fp->fun->nargs. For example, in | |
| 213 | * | |
| 214 | * function f(x) { x = 42; return arguments[0]; } | |
| 215 | * f(); | |
| 216 | * | |
| 217 | * the call to f should return undefined, not 42. If fp->argsobj | |
| 218 | * is null at this point, as it would be in the example, return | |
| 219 | * undefined in *vp. | |
| 220 | */ | |
| 221 | 0 | if (fp->argsobj) |
| 222 | 0 | return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); |
| 223 | } | |
| 224 | } else { | |
| 225 | 0 | if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) { |
| 226 | 0 | if (fp->argsobj && TEST_OVERRIDE_BIT(fp, ARGS_LENGTH)) |
| 227 | 0 | return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); |
| 228 | 0 | *vp = INT_TO_JSVAL((jsint) fp->argc); |
| 229 | } | |
| 230 | } | |
| 231 | 0 | return JS_TRUE; |
| 232 | } | |
| 233 | ||
| 234 | JSObject * | |
| 235 | js_GetArgsObject(JSContext *cx, JSStackFrame *fp) | |
| 236 | 0 | { |
| 237 | JSObject *argsobj; | |
| 238 | ||
| 239 | /* Skip eval and debugger frames. */ | |
| 240 | 0 | while (fp->flags & JSFRAME_SPECIAL) |
| 241 | 0 | fp = fp->down; |
| 242 | ||
| 243 | /* Create an arguments object for fp only if it lacks one. */ | |
| 244 | 0 | argsobj = fp->argsobj; |
| 245 | 0 | if (argsobj) |
| 246 | 0 | return argsobj; |
| 247 | ||
| 248 | /* Link the new object to fp so it can get actual argument values. */ | |
| 249 | 0 | argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL); |
| 250 | 0 | if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) { |
| 251 | 0 | cx->newborn[GCX_OBJECT] = NULL; |
| 252 | 0 | return NULL; |
| 253 | } | |
| 254 | 0 | fp->argsobj = argsobj; |
| 255 | 0 | return argsobj; |
| 256 | } | |
| 257 | ||
| 258 | static JSBool | |
| 259 | args_enumerate(JSContext *cx, JSObject *obj); | |
| 260 | ||
| 261 | JSBool | |
| 262 | js_PutArgsObject(JSContext *cx, JSStackFrame *fp) | |
| 263 | 0 | { |
| 264 | JSObject *argsobj; | |
| 265 | jsval bmapval, rval; | |
| 266 | JSBool ok; | |
| 267 | JSRuntime *rt; | |
| 268 | ||
| 269 | /* | |
| 270 | * Reuse args_enumerate here to reflect fp's actual arguments as indexed | |
| 271 | * elements of argsobj. Do this first, before clearing and freeing the | |
| 272 | * deleted argument slot bitmap, because args_enumerate depends on that. | |
| 273 | */ | |
| 274 | 0 | argsobj = fp->argsobj; |
| 275 | 0 | ok = args_enumerate(cx, argsobj); |
| 276 | ||
| 277 | /* | |
| 278 | * Now clear the deleted argument number bitmap slot and free the bitmap, | |
| 279 | * if one was actually created due to 'delete arguments[0]' or similar. | |
| 280 | */ | |
| 281 | 0 | (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); |
| 282 | 0 | if (!JSVAL_IS_VOID(bmapval)) { |
| 283 | 0 | JS_SetReservedSlot(cx, argsobj, 0, JSVAL_VOID); |
| 284 | 0 | if (fp->argc > JSVAL_INT_BITS) |
| 285 | 0 | JS_free(cx, JSVAL_TO_PRIVATE(bmapval)); |
| 286 | } | |
| 287 | ||
| 288 | /* | |
| 289 | * Now get the prototype properties so we snapshot fp->fun and fp->argc | |
| 290 | * before fp goes away. | |
| 291 | */ | |
| 292 | 0 | rt = cx->runtime; |
| 293 | 0 | ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom), |
| 294 | &rval); | |
| 295 | 0 | ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom), |
| 296 | &rval); | |
| 297 | 0 | ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom), |
| 298 | &rval); | |
| 299 | 0 | ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom), |
| 300 | &rval); | |
| 301 | ||
| 302 | /* | |
| 303 | * Clear the private pointer to fp, which is about to go away (js_Invoke). | |
| 304 | * Do this last because the args_enumerate and js_GetProperty calls above | |
| 305 | * need to follow the private slot to find fp. | |
| 306 | */ | |
| 307 | 0 | ok &= JS_SetPrivate(cx, argsobj, NULL); |
| 308 | 0 | fp->argsobj = NULL; |
| 309 | 0 | return ok; |
| 310 | } | |
| 311 | ||
| 312 | static JSBool | |
| 313 | args_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | |
| 314 | 0 | { |
| 315 | jsint slot; | |
| 316 | JSStackFrame *fp; | |
| 317 | ||
| 318 | 0 | if (!JSVAL_IS_INT(id)) |
| 319 | 0 | return JS_TRUE; |
| 320 | 0 | fp = (JSStackFrame *) |
| 321 | JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); | |
| 322 | 0 | if (!fp) |
| 323 | 0 | return JS_TRUE; |
| 324 | JS_ASSERT(fp->argsobj); | |
| 325 | ||
| 326 | 0 | slot = JSVAL_TO_INT(id); |
| 327 | 0 | switch (slot) { |
| 328 | case ARGS_CALLEE: | |
| 329 | case ARGS_LENGTH: | |
| 330 | 0 | SET_OVERRIDE_BIT(fp, slot); |
| 331 | 0 | break; |
| 332 | ||
| 333 | default: | |
| 334 | 0 | if ((uintN)slot < fp->argc && !MarkArgDeleted(cx, fp, slot)) |
| 335 | 0 | return JS_FALSE; |
| 336 | break; | |
| 337 | } | |
| 338 | 0 | return JS_TRUE; |
| 339 | } | |
| 340 | ||
| 341 | static JSBool | |
| 342 | args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | |
| 343 | 0 | { |
| 344 | jsint slot; | |
| 345 | JSStackFrame *fp; | |
| 346 | ||
| 347 | 0 | if (!JSVAL_IS_INT(id)) |
| 348 | 0 | return JS_TRUE; |
| 349 | 0 | fp = (JSStackFrame *) |
| 350 | JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); | |
| 351 | 0 | if (!fp) |
| 352 | 0 | return JS_TRUE; |
| 353 | JS_ASSERT(fp->argsobj); | |
| 354 | ||
| 355 | 0 | slot = JSVAL_TO_INT(id); |
| 356 | 0 | switch (slot) { |
| 357 | case ARGS_CALLEE: | |
| 358 | 0 | if (!TEST_OVERRIDE_BIT(fp, slot)) |
| 359 | 0 | *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); |
| 360 | 0 | break; |
| 361 | ||
| 362 | case ARGS_LENGTH: | |
| 363 | 0 | if (!TEST_OVERRIDE_BIT(fp, slot)) |
| 364 | 0 | *vp = INT_TO_JSVAL((jsint)fp->argc); |
| 365 | 0 | break; |
| 366 | ||
| 367 | default: | |
| 368 | 0 | if ((uintN)slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) |
| 369 | 0 | *vp = fp->argv[slot]; |
| 370 | break; | |
| 371 | } | |
| 372 | 0 | return JS_TRUE; |
| 373 | } | |
| 374 | ||
| 375 | static JSBool | |
| 376 | args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | |
| 377 | 0 | { |
| 378 | JSStackFrame *fp; | |
| 379 | jsint slot; | |
| 380 | ||
| 381 | 0 | if (!JSVAL_IS_INT(id)) |
| 382 | 0 | return JS_TRUE; |
| 383 | 0 | fp = (JSStackFrame *) |
| 384 | JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); | |
| 385 | 0 | if (!fp) |
| 386 | 0 | return JS_TRUE; |
| 387 | JS_ASSERT(fp->argsobj); | |
| 388 | ||
| 389 | 0 | slot = JSVAL_TO_INT(id); |
| 390 | 0 | switch (slot) { |
| 391 | case ARGS_CALLEE: | |
| 392 | case ARGS_LENGTH: | |
| 393 | 0 | SET_OVERRIDE_BIT(fp, slot); |
| 394 | 0 | break; |
| 395 | ||
| 396 | default: | |
| 397 | 0 | if (fp->fun->interpreted && |
| 398 | (uintN)slot < fp->argc && | |
| 399 | !ArgWasDeleted(cx, fp, slot)) { | |
| 400 | 0 | fp->argv[slot] = *vp; |
| 401 | } | |
| 402 | break; | |
| 403 | } | |
| 404 | 0 | return JS_TRUE; |
| 405 | } | |
| 406 | ||
| 407 | static JSBool | |
| 408 | args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, | |
| 409 | JSObject **objp) | |
| 410 | 0 | { |
| 411 | JSStackFrame *fp; | |
| 412 | uintN slot; | |
| 413 | JSString *str; | |
| 414 | JSAtom *atom; | |
| 415 | intN tinyid; | |
| 416 | jsval value; | |
| 417 | ||
| 418 | 0 | *objp = NULL; |
| 419 | 0 | fp = (JSStackFrame *) |
| 420 | JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); | |
| 421 | 0 | if (!fp) |
| 422 | 0 | return JS_TRUE; |
| 423 | JS_ASSERT(fp->argsobj); | |
| 424 | ||
| 425 | 0 | if (JSVAL_IS_INT(id)) { |
| 426 | 0 | slot = JSVAL_TO_INT(id); |
| 427 | 0 | if (slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) { |
| 428 | /* XXX ECMA specs DontEnum, contrary to other array-like objects */ | |
| 429 | 0 | if (!js_DefineProperty(cx, obj, INT_JSVAL_TO_JSID(id), |
| 430 | fp->argv[slot], | |
| 431 | args_getProperty, args_setProperty, | |
| 432 | JS_VERSION_IS_ECMA(cx) | |
| 433 | ? 0 | |
| 434 | : JSPROP_ENUMERATE, | |
| 435 | NULL)) { | |
| 436 | 0 | return JS_FALSE; |
| 437 | } | |
| 438 | 0 | *objp = obj; |
| 439 | } | |
| 440 | } else { | |
| 441 | 0 | str = JSVAL_TO_STRING(id); |
| 442 | 0 | atom = cx->runtime->atomState.lengthAtom; |
| 443 | 0 | if (str == ATOM_TO_STRING(atom)) { |
| 444 | 0 | tinyid = ARGS_LENGTH; |
| 445 | 0 | value = INT_TO_JSVAL(fp->argc); |
| 446 | } else { | |
| 447 | 0 | atom = cx->runtime->atomState.calleeAtom; |
| 448 | 0 | if (str == ATOM_TO_STRING(atom)) { |
| 449 | 0 | tinyid = ARGS_CALLEE; |
| 450 | 0 | value = fp->argv ? fp->argv[-2] |
| 451 | : OBJECT_TO_JSVAL(fp->fun->object); | |
| 452 | } else { | |
| 453 | 0 | atom = NULL; |
| 454 | ||
| 455 | /* Quell GCC overwarnings. */ | |
| 456 | 0 | tinyid = 0; |
| 457 | 0 | value = JSVAL_NULL; |
| 458 | } | |
| 459 | } | |
| 460 | ||
| 461 | 0 | if (atom && !TEST_OVERRIDE_BIT(fp, tinyid)) { |
| 462 | 0 | if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, |
| 463 | args_getProperty, args_setProperty, 0, | |
| 464 | SPROP_HAS_SHORTID, tinyid, NULL)) { | |
| 465 | 0 | return JS_FALSE; |
| 466 | } | |
| 467 | 0 | *objp = obj; |
| 468 | } | |
| 469 | } | |
| 470 | ||
| 471 | 0 | return JS_TRUE; |
| 472 | } | |
| 473 | ||
| 474 | static JSBool | |
| 475 | args_enumerate(JSContext *cx, JSObject *obj) | |
| 476 | 0 | { |
| 477 | JSStackFrame *fp; | |
| 478 | JSObject *pobj; | |
| 479 | JSProperty *prop; | |
| 480 | uintN slot, argc; | |
| 481 | ||
| 482 | 0 | fp = (JSStackFrame *) |
| 483 | JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); | |
| 484 | 0 | if (!fp) |
| 485 | 0 | return JS_TRUE; |
| 486 | JS_ASSERT(fp->argsobj); | |
| 487 | ||
| 488 | /* | |
| 489 | * Trigger reflection with value snapshot in args_resolve using a series | |
| 490 | * of js_LookupProperty calls. We handle length, callee, and the indexed | |
| 491 | * argument properties. We know that args_resolve covers all these cases | |
| 492 | * and creates direct properties of obj, but that it may fail to resolve | |
| 493 | * length or callee if overridden. | |
| 494 | */ | |
| 495 | 0 | if (!js_LookupProperty(cx, obj, |
| 496 | ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), | |
| 497 | &pobj, &prop)) { | |
| 498 | 0 | return JS_FALSE; |
| 499 | } | |
| 500 | 0 | if (prop) |
| 501 | 0 | OBJ_DROP_PROPERTY(cx, pobj, prop); |
| 502 | ||
| 503 | 0 | if (!js_LookupProperty(cx, obj, |
| 504 | ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), | |
| 505 | &pobj, &prop)) { | |
| 506 | 0 | return JS_FALSE; |
| 507 | } | |
| 508 | 0 | if (prop) |
| 509 | 0 | OBJ_DROP_PROPERTY(cx, pobj, prop); |
| 510 | ||
| 511 | 0 | argc = fp->argc; |
| 512 | 0 | for (slot = 0; slot < argc; slot++) { |
| 513 | 0 | if (!js_LookupProperty(cx, obj, INT_TO_JSID((jsint)slot), &pobj, &prop)) |
| 514 | 0 | return JS_FALSE; |
| 515 | 0 | if (prop) |
| 516 | 0 | OBJ_DROP_PROPERTY(cx, pobj, prop); |
| 517 | } | |
| 518 | 0 | return JS_TRUE; |
| 519 | } | |
| 520 | ||
| 521 | /* | |
| 522 | * The Arguments class is not initialized via JS_InitClass, and must not be, | |
| 523 | * because its name is "Object". Per ECMA, that causes instances of it to | |
| 524 | * delegate to the object named by Object.prototype. It also ensures that | |
| 525 | * arguments.toString() returns "[object Object]". | |
| 526 | * | |
| 527 | * The JSClass functions below collaborate to lazily reflect and synchronize | |
| 528 | * actual argument values, argument count, and callee function object stored | |
| 529 | * in a JSStackFrame with their corresponding property values in the frame's | |
| 530 | * arguments object. | |
| 531 | */ | |
| 532 | JSClass js_ArgumentsClass = { | |
| 533 | js_Object_str, | |
| 534 | JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1), | |
| 535 | JS_PropertyStub, args_delProperty, | |
| 536 | args_getProperty, args_setProperty, | |
| 537 | args_enumerate, (JSResolveOp) args_resolve, | |
| 538 | JS_ConvertStub, JS_FinalizeStub, | |
| 539 | JSCLASS_NO_OPTIONAL_MEMBERS | |
| 540 | }; | |
| 541 | ||
| 542 | #endif /* JS_HAS_ARGS_OBJECT */ | |
| 543 | ||
| 544 | #if JS_HAS_CALL_OBJECT | |
| 545 | ||
| 546 | JSObject * | |
| 547 | js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent) | |
| 548 | 0 | { |
| 549 | JSObject *callobj, *funobj; | |
| 550 | ||
| 551 | /* Create a call object for fp only if it lacks one. */ | |
| 552 | JS_ASSERT(fp->fun); | |
| 553 | 0 | callobj = fp->callobj; |
| 554 | 0 | if (callobj) |
| 555 | 0 | return callobj; |
| 556 | JS_ASSERT(fp->fun); | |
| 557 | ||
| 558 | /* The default call parent is its function's parent (static link). */ | |
| 559 | 0 | if (!parent) { |
| 560 | 0 | funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object; |
| 561 | 0 | if (funobj) |
| 562 | 0 | parent = OBJ_GET_PARENT(cx, funobj); |
| 563 | } | |
| 564 | ||
| 565 | /* Create the call object and link it to its stack frame. */ | |
| 566 | 0 | callobj = js_NewObject(cx, &js_CallClass, NULL, parent); |
| 567 | 0 | if (!callobj || !JS_SetPrivate(cx, callobj, fp)) { |
| 568 | 0 | cx->newborn[GCX_OBJECT] = NULL; |
| 569 | 0 | return NULL; |
| 570 | } | |
| 571 | 0 | fp->callobj = callobj; |
| 572 | ||
| 573 | /* Make callobj be the scope chain and the variables object. */ | |
| 574 | 0 | fp->scopeChain = callobj; |
| 575 | 0 | fp->varobj = callobj; |
| 576 | 0 | return callobj; |
| 577 | } | |
| 578 | ||
| 579 | static JSBool | |
| 580 | call_enumerate(JSContext *cx, JSObject *obj); | |
| 581 | ||
| 582 | JSBool | |
| 583 | js_PutCallObject(JSContext *cx, JSStackFrame *fp) | |
| 584 | 0 | { |
| 585 | JSObject *callobj; | |
| 586 | JSBool ok; | |
| 587 | jsid argsid; | |
| 588 | jsval aval; | |
| 589 | ||
| 590 | /* | |
| 591 | * Reuse call_enumerate here to reflect all actual args and vars into the | |
| 592 | * call object from fp. | |
| 593 | */ | |
| 594 | 0 | callobj = fp->callobj; |
| 595 | 0 | if (!callobj) |
| 596 | 0 | return JS_TRUE; |
| 597 | 0 | ok = call_enumerate(cx, callobj); |
| 598 | ||
| 599 | /* | |
| 600 | * Get the arguments object to snapshot fp's actual argument values. | |
| 601 | */ | |
| 602 | 0 | if (fp->argsobj) { |
| 603 | 0 | argsid = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom); |
| 604 | 0 | ok &= js_GetProperty(cx, callobj, argsid, &aval); |
| 605 | 0 | ok &= js_SetProperty(cx, callobj, argsid, &aval); |
| 606 | 0 | ok &= js_PutArgsObject(cx, fp); |
| 607 | } | |
| 608 | ||
| 609 | /* | |
| 610 | * Clear the private pointer to fp, which is about to go away (js_Invoke). | |
| 611 | * Do this last because the call_enumerate and js_GetProperty calls above | |
| 612 | * need to follow the private slot to find fp. | |
| 613 | */ | |
| 614 | 0 | ok &= JS_SetPrivate(cx, callobj, NULL); |
| 615 | 0 | fp->callobj = NULL; |
| 616 | 0 | return ok; |
| 617 | } | |
| 618 | ||
| 619 | static JSPropertySpec call_props[] = { | |
| 620 | {js_arguments_str, CALL_ARGUMENTS, JSPROP_PERMANENT,0,0}, | |
| 621 | {"__callee__", CALL_CALLEE, 0,0,0}, | |
| 622 | {0,0,0,0,0} | |
| 623 | }; | |
| 624 | ||
| 625 | static JSBool | |
| 626 | call_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | |
| 627 | 0 | { |
| 628 | JSStackFrame *fp; | |
| 629 | jsint slot; | |
| 630 | ||
| 631 | 0 | if (!JSVAL_IS_INT(id)) |
| 632 | 0 | return JS_TRUE; |
| 633 | 0 | fp = (JSStackFrame *) JS_GetPrivate(cx, obj); |
| 634 | 0 | if (!fp) |
| 635 | 0 | return JS_TRUE; |
| 636 | JS_ASSERT(fp->fun); | |
| 637 | ||
| 638 | 0 | slot = JSVAL_TO_INT(id); |
| 639 | 0 | switch (slot) { |
| 640 | case CALL_ARGUMENTS: | |
| 641 | 0 | if (!TEST_OVERRIDE_BIT(fp, slot)) { |
| 642 | 0 | JSObject *argsobj = js_GetArgsObject(cx, fp); |
| 643 | 0 | if (!argsobj) |
| 644 | 0 | return JS_FALSE; |
| 645 | 0 | *vp = OBJECT_TO_JSVAL(argsobj); |
| 646 | } | |
| 647 | 0 | break; |
| 648 | ||
| 649 | case CALL_CALLEE: | |
| 650 | 0 | if (!TEST_OVERRIDE_BIT(fp, slot)) |
| 651 | 0 | *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); |
| 652 | 0 | break; |
| 653 | ||
| 654 | default: | |
| 655 | 0 | if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs)) |
| 656 | 0 | *vp = fp->argv[slot]; |
| 657 | break; | |
| 658 | } | |
| 659 | 0 | return JS_TRUE; |
| 660 | } | |
| 661 | ||
| 662 | static JSBool | |
| 663 | call_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | |
| 664 | 0 | { |
| 665 | JSStackFrame *fp; | |
| 666 | jsint slot; | |
| 667 | ||
| 668 | 0 | if (!JSVAL_IS_INT(id)) |
| 669 | 0 | return JS_TRUE; |
| 670 | 0 | fp = (JSStackFrame *) JS_GetPrivate(cx, obj); |
| 671 | 0 | if (!fp) |
| 672 | 0 | return JS_TRUE; |
| 673 | JS_ASSERT(fp->fun); | |
| 674 | ||
| 675 | 0 | slot = JSVAL_TO_INT(id); |
| 676 | 0 | switch (slot) { |
| 677 | case CALL_ARGUMENTS: | |
| 678 | case CALL_CALLEE: | |
| 679 | 0 | SET_OVERRIDE_BIT(fp, slot); |
| 680 | 0 | break; |
| 681 | ||
| 682 | default: | |
| 683 | 0 | if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs)) |
| 684 | 0 | fp->argv[slot] = *vp; |
| 685 | break; | |
| 686 | } | |
| 687 | 0 | return JS_TRUE; |
| 688 | } | |
| 689 | ||
| 690 | JSBool | |
| 691 | js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | |
| 692 | 0 | { |
| 693 | JSStackFrame *fp; | |
| 694 | ||
| 695 | JS_ASSERT(JSVAL_IS_INT(id)); | |
| 696 | 0 | fp = (JSStackFrame *) JS_GetPrivate(cx, obj); |
| 697 | 0 | if (fp) { |
| 698 | /* XXX no jsint slot commoning here to avoid MSVC1.52 crashes */ | |
| 699 | 0 | if ((uintN)JSVAL_TO_INT(id) < fp->nvars) |
| 700 | 0 | *vp = fp->vars[JSVAL_TO_INT(id)]; |
| 701 | } | |
| 702 | 0 | return JS_TRUE; |
| 703 | } | |
| 704 | ||
| 705 | JSBool | |
| 706 | js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | |
| 707 | 0 | { |
| 708 | JSStackFrame *fp; | |
| 709 | ||
| 710 | JS_ASSERT(JSVAL_IS_INT(id)); | |
| 711 | 0 | fp = (JSStackFrame *) JS_GetPrivate(cx, obj); |
| 712 | 0 | if (fp) { |
| 713 | /* XXX jsint slot is block-local here to avoid MSVC1.52 crashes */ | |
| 714 | 0 | jsint slot = JSVAL_TO_INT(id); |
| 715 | 0 | if ((uintN)slot < fp->nvars) |
| 716 | 0 | fp->vars[slot] = *vp; |
| 717 | } | |
| 718 | 0 | return JS_TRUE; |
| 719 | } | |
| 720 | ||
| 721 | static JSBool | |
| 722 | call_enumerate(JSContext *cx, JSObject *obj) | |
| 723 | 0 | { |
| 724 | JSStackFrame *fp; | |
| 725 | JSObject *funobj, *pobj; | |
| 726 | JSScope *scope; | |
| 727 | JSScopeProperty *sprop, *cprop; | |
| 728 | JSPropertyOp getter; | |
| 729 | jsval *vec; | |
| 730 | JSAtom *atom; | |
| 731 | JSProperty *prop; | |
| 732 | ||
| 733 | 0 | fp = (JSStackFrame *) JS_GetPrivate(cx, obj); |
| 734 | 0 | if (!fp) |
| 735 | 0 | return JS_TRUE; |
| 736 | ||
| 737 | /* | |
| 738 | * Do not enumerate a cloned function object at fp->argv[-2], it may have | |
| 739 | * gained its own (mutable) scope (e.g., a brutally-shared XUL script sets | |
| 740 | * the clone's prototype property). We must enumerate the function object | |
| 741 | * that was decorated with parameter and local variable properties by the | |
| 742 | * compiler when the compiler created fp->fun, namely fp->fun->object. | |
| 743 | * | |
| 744 | * Contrast with call_resolve, where we prefer fp->argv[-2], because we'll | |
| 745 | * use js_LookupProperty to find any overridden properties in that object, | |
| 746 | * if it was a mutated clone; and if not, we will search its prototype, | |
| 747 | * fp->fun->object, to find compiler-created params and locals. | |
| 748 | */ | |
| 749 | 0 | funobj = fp->fun->object; |
| 750 | 0 | if (!funobj) |
| 751 | 0 | return JS_TRUE; |
| 752 | ||
| 753 | /* | |
| 754 | * Reflect actual args from fp->argv for formal parameters, and local vars | |
| 755 | * and functions in fp->vars for declared variables and nested-at-top-level | |
| 756 | * local functions. | |
| 757 | */ | |
| 758 | 0 | scope = OBJ_SCOPE(funobj); |
| 759 | 0 | for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { |
| 760 | 0 | getter = sprop->getter; |
| 761 | 0 | if (getter == js_GetArgument) |
| 762 | 0 | vec = fp->argv; |
| 763 | 0 | else if (getter == js_GetLocalVariable) |
| 764 | 0 | vec = fp->vars; |
| 765 | else | |
| 766 | 0 | continue; |
| 767 | ||
| 768 | /* Trigger reflection by looking up the unhidden atom for sprop->id. */ | |
| 769 | JS_ASSERT(JSID_IS_ATOM(sprop->id)); | |
| 770 | 0 | atom = JSID_TO_ATOM(sprop->id); |
| 771 | JS_ASSERT(atom->flags & ATOM_HIDDEN); | |
| 772 | 0 | atom = atom->entry.value; |
| 773 | ||
| 774 | 0 | if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) |
| 775 | 0 | return JS_FALSE; |
| 776 | ||
| 777 | /* | |
| 778 | * If we found the property in a different object, don't try sticking | |
| 779 | * it into wrong slots vector. This can occur because we have a mutable | |
| 780 | * __proto__ slot, and cloned function objects rely on their __proto__ | |
| 781 | * to delegate to the object that contains the var and arg properties. | |
| 782 | */ | |
| 783 | 0 | if (!prop || pobj != obj) { |
| 784 | 0 | if (prop) |
| 785 | 0 | OBJ_DROP_PROPERTY(cx, pobj, prop); |
| 786 | 0 | continue; |
| 787 | } | |
| 788 | 0 | cprop = (JSScopeProperty *)prop; |
| 789 | 0 | LOCKED_OBJ_SET_SLOT(obj, cprop->slot, vec[(uint16) sprop->shortid]); |
| 790 | 0 | OBJ_DROP_PROPERTY(cx, obj, prop); |
| 791 | } | |
| 792 | ||
| 793 | 0 | return JS_TRUE; |
| 794 | } | |
| 795 | ||
| 796 | static JSBool | |
| 797 | call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, | |
| 798 | JSObject **objp) | |
| 799 | 0 | { |
| 800 | JSStackFrame *fp; | |
| 801 | JSObject *funobj; | |
| 802 | JSString *str; | |
| 803 | JSAtom *atom; | |
| 804 | JSObject *obj2; | |
| 805 | JSProperty *prop; | |
| 806 | JSScopeProperty *sprop; | |
| 807 | JSPropertyOp getter, setter; | |
| 808 | uintN attrs, slot, nslots, spflags; | |
| 809 | jsval *vp, value; | |
| 810 | intN shortid; | |
| 811 | ||
| 812 | 0 | fp = (JSStackFrame *) JS_GetPrivate(cx, obj); |
| 813 | 0 | if (!fp) |
| 814 | 0 | return JS_TRUE; |
| 815 | JS_ASSERT(fp->fun); | |
| 816 | ||
| 817 | 0 | if (!JSVAL_IS_STRING(id)) |
| 818 | 0 | return JS_TRUE; |
| 819 | ||
| 820 | 0 | funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object; |
| 821 | 0 | if (!funobj) |
| 822 | 0 | return JS_TRUE; |
| 823 | JS_ASSERT((JSFunction *) JS_GetPrivate(cx, funobj) == fp->fun); | |
| 824 | ||
| 825 | 0 | str = JSVAL_TO_STRING(id); |
| 826 | 0 | atom = js_AtomizeString(cx, str, 0); |
| 827 | 0 | if (!atom) |
| 828 | 0 | return JS_FALSE; |
| 829 | 0 | if (!js_LookupHiddenProperty(cx, funobj, ATOM_TO_JSID(atom), &obj2, &prop)) |
| 830 | 0 | return JS_FALSE; |
| 831 | ||
| 832 | 0 | if (prop) { |
| 833 | 0 | if (!OBJ_IS_NATIVE(obj2)) { |
| 834 | 0 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 835 | 0 | return JS_TRUE; |
| 836 | } | |
| 837 | ||
| 838 | 0 | sprop = (JSScopeProperty *) prop; |
| 839 | 0 | getter = sprop->getter; |
| 840 | 0 | attrs = sprop->attrs & ~JSPROP_SHARED; |
| 841 | 0 | slot = (uintN) sprop->shortid; |
| 842 | 0 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 843 | ||
| 844 | /* Ensure we found an arg or var property for the same function. */ | |
| 845 | 0 | if ((sprop->flags & SPROP_IS_HIDDEN) && |
| 846 | (obj2 == funobj || | |
| 847 | (JSFunction *) JS_GetPrivate(cx, obj2) == fp->fun)) { | |
| 848 | 0 | if (getter == js_GetArgument) { |
| 849 | 0 | vp = fp->argv; |
| 850 | 0 | nslots = JS_MAX(fp->argc, fp->fun->nargs); |
| 851 | 0 | getter = setter = NULL; |
| 852 | } else { | |
| 853 | JS_ASSERT(getter == js_GetLocalVariable); | |
| 854 | 0 | vp = fp->vars; |
| 855 | 0 | nslots = fp->nvars; |
| 856 | 0 | getter = js_GetCallVariable; |
| 857 | 0 | setter = js_SetCallVariable; |
| 858 | } | |
| 859 | 0 | if (slot < nslots) { |
| 860 | 0 | value = vp[slot]; |
| 861 | 0 | spflags = SPROP_HAS_SHORTID; |
| 862 | 0 | shortid = (intN) slot; |
| 863 | } else { | |
| 864 | 0 | value = JSVAL_VOID; |
| 865 | 0 | spflags = 0; |
| 866 | 0 | shortid = 0; |
| 867 | } | |
| 868 | 0 | if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, |
| 869 | getter, setter, attrs, | |
| 870 | spflags, shortid, NULL)) { | |
| 871 | 0 | return JS_FALSE; |
| 872 | } | |
| 873 | 0 | *objp = obj; |
| 874 | } | |
| 875 | } | |
| 876 | ||
| 877 | 0 | return JS_TRUE; |
| 878 | } | |
| 879 | ||
| 880 | static JSBool | |
| 881 | call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) | |
| 882 | 0 | { |
| 883 | JSStackFrame *fp; | |
| 884 | ||
| 885 | 0 | if (type == JSTYPE_FUNCTION) { |
| 886 | 0 | fp = (JSStackFrame *) JS_GetPrivate(cx, obj); |
| 887 | 0 | if (fp) { |
| 888 | JS_ASSERT(fp->fun); | |
| 889 | 0 | *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); |
| 890 | } | |
| 891 | } | |
| 892 | 0 | return JS_TRUE; |
| 893 | } | |
| 894 | ||
| 895 | JSClass js_CallClass = { | |
| 896 | js_Call_str, | |
| 897 | JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, | |
| 898 | JS_PropertyStub, JS_PropertyStub, | |
| 899 | call_getProperty, call_setProperty, | |
| 900 | call_enumerate, (JSResolveOp)call_resolve, | |
| 901 | call_convert, JS_FinalizeStub, | |
| 902 | JSCLASS_NO_OPTIONAL_MEMBERS | |
| 903 | }; | |
| 904 | ||
| 905 | #endif /* JS_HAS_CALL_OBJECT */ | |
| 906 | ||
| 907 | /* | |
| 908 | * ECMA-262 specifies that length is a property of function object instances, | |
| 909 | * but we can avoid that space cost by delegating to a prototype property that | |
| 910 | * is JSPROP_PERMANENT and JSPROP_SHARED. Each fun_getProperty call computes | |
| 911 | * a fresh length value based on the arity of the individual function object's | |
| 912 | * private data. | |
| 913 | * | |
| 914 | * The extensions below other than length, i.e., the ones not in ECMA-262, | |
| 915 | * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility | |
| 916 | * with ECMA we must allow a delegating object to override them. | |
| 917 | */ | |
| 918 | #define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED) | |
| 919 | ||
| 920 | static JSPropertySpec function_props[] = { | |
| 921 | {js_arguments_str, CALL_ARGUMENTS, JSPROP_PERMANENT, 0,0}, | |
| 922 | {js_arity_str, FUN_ARITY, JSPROP_PERMANENT, 0,0}, | |
| 923 | {js_caller_str, FUN_CALLER, JSPROP_PERMANENT, 0,0}, | |
| 924 | {js_length_str, ARGS_LENGTH, LENGTH_PROP_ATTRS, 0,0}, | |
| 925 | {js_name_str, FUN_NAME, JSPROP_PERMANENT, 0,0}, | |
| 926 | {0,0,0,0,0} | |
| 927 | }; | |
| 928 | ||
| 929 | static JSBool | |
| 930 | fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | |
| 931 | 0 | { |
| 932 | jsint slot; | |
| 933 | JSFunction *fun; | |
| 934 | JSStackFrame *fp; | |
| 935 | ||
| 936 | 0 | if (!JSVAL_IS_INT(id)) |
| 937 | 0 | return JS_TRUE; |
| 938 | 0 | slot = JSVAL_TO_INT(id); |
| 939 | ||
| 940 | /* | |
| 941 | * Loop because getter and setter can be delegated from another class, | |
| 942 | * but loop only for ARGS_LENGTH because we must pretend that f.length | |
| 943 | * is in each function instance f, per ECMA-262, instead of only in the | |
| 944 | * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED | |
| 945 | * to make it appear so). | |
| 946 | * | |
| 947 | * This code couples tightly to the attributes for the function_props[] | |
| 948 | * initializers above, and to js_SetProperty and js_HasOwnPropertyHelper. | |
| 949 | * | |
| 950 | * It's important to allow delegating objects, even though they inherit | |
| 951 | * this getter (fun_getProperty), to override arguments, arity, caller, | |
| 952 | * and name. If we didn't return early for slot != ARGS_LENGTH, we would | |
| 953 | * clobber *vp with the native property value, instead of letting script | |
| 954 | * override that value in delegating objects. | |
| 955 | * | |
| 956 | * Note how that clobbering is what simulates JSPROP_READONLY for all of | |
| 957 | * the non-standard properties when the directly addressed object (obj) | |
| 958 | * is a function object (i.e., when this loop does not iterate). | |
| 959 | */ | |
| 960 | 0 | while (!(fun = (JSFunction *) |
| 961 | JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) { | |
| 962 | 0 | if (slot != ARGS_LENGTH) |
| 963 | 0 | return JS_TRUE; |
| 964 | 0 | obj = OBJ_GET_PROTO(cx, obj); |
| 965 | 0 | if (!obj) |
| 966 | 0 | return JS_TRUE; |
| 967 | } | |
| 968 | ||
| 969 | /* Find fun's top-most activation record. */ | |
| 970 | 0 | for (fp = cx->fp; fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL)); |
| 971 | 0 | fp = fp->down) { |
| 972 | continue; | |
| 973 | } | |
| 974 | ||
| 975 | 0 | switch (slot) { |
| 976 | case CALL_ARGUMENTS: | |
| 977 | #if JS_HAS_ARGS_OBJECT | |
| 978 | /* Warn if strict about f.arguments or equivalent unqualified uses. */ | |
| 979 | 0 | if (!JS_ReportErrorFlagsAndNumber(cx, |
| 980 | JSREPORT_WARNING | JSREPORT_STRICT, | |
| 981 | js_GetErrorMessage, NULL, | |
| 982 | JSMSG_DEPRECATED_USAGE, | |
| 983 | js_arguments_str)) { | |
| 984 | 0 | return JS_FALSE; |
| 985 | } | |
| 986 | 0 | if (fp) { |
| 987 | 0 | if (!js_GetArgsValue(cx, fp, vp)) |
| 988 | 0 | return JS_FALSE; |
| 989 | } else { | |
| 990 | 0 | *vp = JSVAL_NULL; |
| 991 | } | |
| 992 | 0 | break; |
| 993 | #else /* !JS_HAS_ARGS_OBJECT */ | |
| 994 | *vp = OBJECT_TO_JSVAL(fp ? obj : NULL); | |
| 995 | break; | |
| 996 | #endif /* !JS_HAS_ARGS_OBJECT */ | |
| 997 | ||
| 998 | case ARGS_LENGTH: | |
| 999 | 0 | if (!JS_VERSION_IS_ECMA(cx)) |
| 1000 | 0 | *vp = INT_TO_JSVAL((jsint)(fp && fp->fun ? fp->argc : fun->nargs)); |
| 1001 | else | |
| 1002 | case FUN_ARITY: | |
| 1003 | 0 | *vp = INT_TO_JSVAL((jsint)fun->nargs); |
| 1004 | 0 | break; |
| 1005 | ||
| 1006 | case FUN_NAME: | |
| 1007 | 0 | *vp = fun->atom |
| 1008 | ? ATOM_KEY(fun->atom) | |
| 1009 | : STRING_TO_JSVAL(cx->runtime->emptyString); | |
| 1010 | 0 | break; |
| 1011 | ||
| 1012 | case FUN_CALLER: | |
| 1013 | 0 | while (fp && (fp->flags & JSFRAME_SKIP_CALLER) && fp->down) |
| 1014 | 0 | fp = fp->down; |
| 1015 | 0 | if (fp && fp->down && fp->down->fun && fp->down->argv) |
| 1016 | 0 | *vp = fp->down->argv[-2]; |
| 1017 | else | |
| 1018 | 0 | *vp = JSVAL_NULL; |
| 1019 | 0 | if (!JSVAL_IS_PRIMITIVE(*vp) && cx->runtime->checkObjectAccess) { |
| 1020 | 0 | id = ATOM_KEY(cx->runtime->atomState.callerAtom); |
| 1021 | 0 | if (!cx->runtime->checkObjectAccess(cx, obj, id, JSACC_READ, vp)) |
| 1022 | 0 | return JS_FALSE; |
| 1023 | } | |
| 1024 | 0 | break; |
| 1025 | ||
| 1026 | default: | |
| 1027 | /* XXX fun[0] and fun.arguments[0] are equivalent. */ | |
| 1028 | 0 | if (fp && fp->fun && (uintN)slot < fp->fun->nargs) |
| 1029 | 0 | *vp = fp->argv[slot]; |
| 1030 | break; | |
| 1031 | } | |
| 1032 | ||
| 1033 | 0 | return JS_TRUE; |
| 1034 | } | |
| 1035 | ||
| 1036 | static JSBool | |
| 1037 | fun_enumerate(JSContext *cx, JSObject *obj) | |
| 1038 | 0 | { |
| 1039 | jsid prototypeId; | |
| 1040 | JSObject *pobj; | |
| 1041 | JSProperty *prop; | |
| 1042 | ||
| 1043 | 0 | prototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); |
| 1044 | 0 | if (!OBJ_LOOKUP_PROPERTY(cx, obj, prototypeId, &pobj, &prop)) |
| 1045 | 0 | return JS_FALSE; |
| 1046 | 0 | if (prop) |
| 1047 | 0 | OBJ_DROP_PROPERTY(cx, pobj, prop); |
| 1048 | 0 | return JS_TRUE; |
| 1049 | } | |
| 1050 | ||
| 1051 | static JSBool | |
| 1052 | fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, | |
| 1053 | JSObject **objp) | |
| 1054 | 324713 | { |
| 1055 | JSFunction *fun; | |
| 1056 | JSString *str; | |
| 1057 | JSAtom *prototypeAtom; | |
| 1058 | ||
| 1059 | 324713 | if (!JSVAL_IS_STRING(id)) |
| 1060 | 0 | return JS_TRUE; |
| 1061 | ||
| 1062 | /* No valid function object should lack private data, but check anyway. */ | |
| 1063 | 324713 | fun = (JSFunction *)JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL); |
| 1064 | 324713 | if (!fun || !fun->object) |
| 1065 | 0 | return JS_TRUE; |
| 1066 | ||
| 1067 | /* No need to reflect fun.prototype in 'fun.prototype = ...'. */ | |
| 1068 | 324713 | if (flags & JSRESOLVE_ASSIGNING) |
| 1069 | 0 | return JS_TRUE; |
| 1070 | ||
| 1071 | /* | |
| 1072 | * Ok, check whether id is 'prototype' and bootstrap the function object's | |
| 1073 | * prototype property. | |
| 1074 | */ | |
| 1075 | 324713 | str = JSVAL_TO_STRING(id); |
| 1076 | 324713 | prototypeAtom = cx->runtime->atomState.classPrototypeAtom; |
| 1077 | 324713 | if (str == ATOM_TO_STRING(prototypeAtom)) { |
| 1078 | JSObject *proto, *parentProto; | |
| 1079 | jsval pval; | |
| 1080 | ||
| 1081 | 69 | proto = parentProto = NULL; |
| 1082 | 69 | if (fun->object != obj && fun->object) { |
| 1083 | /* | |
| 1084 | * Clone of a function: make its prototype property value have the | |
| 1085 | * same class as the clone-parent's prototype. | |
| 1086 | */ | |
| 1087 | 0 | if (!OBJ_GET_PROPERTY(cx, fun->object, ATOM_TO_JSID(prototypeAtom), |
| 1088 | &pval)) { | |
| 1089 | 0 | return JS_FALSE; |
| 1090 | } | |
| 1091 | 0 | if (!JSVAL_IS_PRIMITIVE(pval)) { |
| 1092 | /* | |
| 1093 | * We are about to allocate a new object, so hack the newborn | |
| 1094 | * root until then to protect pval in case it is figuratively | |
| 1095 | * up in the air, with no strong refs protecting it. | |
| 1096 | */ | |
| 1097 | 0 | cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(pval); |
| 1098 | 0 | parentProto = JSVAL_TO_OBJECT(pval); |
| 1099 | } | |
| 1100 | } | |
| 1101 | ||
| 1102 | /* | |
| 1103 | * Beware of the wacky case of a user function named Object -- trying | |
| 1104 | * to find a prototype for that will recur back here _ad perniciem_. | |
| 1105 | */ | |
| 1106 | 69 | if (!parentProto && fun->atom == cx->runtime->atomState.ObjectAtom) |
| 1107 | 0 | return JS_TRUE; |
| 1108 | ||
| 1109 | /* | |
| 1110 | * If resolving "prototype" in a clone, clone the parent's prototype. | |
| 1111 | * Pass the constructor's (obj's) parent as the prototype parent, to | |
| 1112 | * avoid defaulting to parentProto.constructor.__parent__. | |
| 1113 | */ | |
| 1114 | 69 | proto = js_NewObject(cx, &js_ObjectClass, parentProto, |
| 1115 | OBJ_GET_PARENT(cx, obj)); | |
| 1116 | 69 | if (!proto) |
| 1117 | 0 | return JS_FALSE; |
| 1118 | ||
| 1119 | /* | |
| 1120 | * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for | |
| 1121 | * user-defined functions, but DontEnum | ReadOnly | DontDelete for | |
| 1122 | * native "system" constructors such as Object or Function. So lazily | |
| 1123 | * set the former here in fun_resolve, but eagerly define the latter | |
| 1124 | * in JS_InitClass, with the right attributes. | |
| 1125 | */ | |
| 1126 | 69 | if (!js_SetClassPrototype(cx, obj, proto, |
| 1127 | JSPROP_ENUMERATE | JSPROP_PERMANENT)) { | |
| 1128 | 0 | cx->newborn[GCX_OBJECT] = NULL; |
| 1129 | 0 | return JS_FALSE; |
| 1130 | } | |
| 1131 | 69 | *objp = obj; |
| 1132 | } | |
| 1133 | ||
| 1134 | 324713 | return JS_TRUE; |
| 1135 | } | |
| 1136 | ||
| 1137 | static JSBool | |
| 1138 | fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) | |
| 1139 | 0 | { |
| 1140 | 0 | switch (type) { |
| 1141 | case JSTYPE_FUNCTION: | |
| 1142 | 0 | *vp = OBJECT_TO_JSVAL(obj); |
| 1143 | 0 | return JS_TRUE; |
| 1144 | default: | |
| 1145 | 0 | return js_TryValueOf(cx, obj, type, vp); |
| 1146 | } | |
| 1147 | } | |
| 1148 | ||
| 1149 | static void | |
| 1150 | fun_finalize(JSContext *cx, JSObject *obj) | |
| 1151 | 17539 | { |
| 1152 | JSFunction *fun; | |
| 1153 | ||
| 1154 | /* No valid function object should lack private data, but check anyway. */ | |
| 1155 | 17539 | fun = (JSFunction *) JS_GetPrivate(cx, obj); |
| 1156 | 17539 | if (!fun) |
| 1157 | 0 | return; |
| 1158 | 17539 | if (fun->object == obj) |
| 1159 | 17539 | fun->object = NULL; |
| 1160 | 17539 | JS_ATOMIC_DECREMENT(&fun->nrefs); |
| 1161 | 17539 | if (fun->nrefs) |
| 1162 | 0 | return; |
| 1163 | ||
| 1164 | /* Null-check required since the parser sets interpreted very early. */ | |
| 1165 | 17539 | if (fun->interpreted && fun->u.script) |
| 1166 | 7747 | js_DestroyScript(cx, fun->u.script); |
| 1167 | } | |
| 1168 | ||
| 1169 | #if JS_HAS_XDR | |
| 1170 | ||
| 1171 | #include "jsxdrapi.h" | |
| 1172 | ||
| 1173 | enum { | |
| 1174 | JSXDR_FUNARG = 1, | |
| 1175 | JSXDR_FUNVAR = 2, | |
| 1176 | JSXDR_FUNCONST = 3 | |
| 1177 | }; | |
| 1178 | ||
| 1179 | /* XXX store parent and proto, if defined */ | |
| 1180 | static JSBool | |
| 1181 | fun_xdrObject(JSXDRState *xdr, JSObject **objp) | |
| 1182 | 0 | { |
| 1183 | JSContext *cx; | |
| 1184 | JSFunction *fun; | |
| 1185 | JSString *atomstr; | |
| 1186 | JSTempValueRooter tvr; | |
| 1187 | uint32 flagsword; /* originally only flags was JS_XDRUint8'd */ | |
| 1188 | char *propname; | |
| 1189 | JSScopeProperty *sprop; | |
| 1190 | uint32 userid; /* NB: holds a signed int-tagged jsval */ | |
| 1191 | JSAtom *atom; | |
| 1192 | uintN i, n, dupflag; | |
| 1193 | uint32 type; | |
| 1194 | JSBool ok; | |
| 1195 | #ifdef DEBUG | |
| 1196 | uintN nvars = 0, nargs = 0; | |
| 1197 | #endif | |
| 1198 | ||
| 1199 | 0 | cx = xdr->cx; |
| 1200 | 0 | if (xdr->mode == JSXDR_ENCODE) { |
| 1201 | /* | |
| 1202 | * No valid function object should lack private data, but fail soft | |
| 1203 | * (return true, no error report) in case one does due to API pilot | |
| 1204 | * or internal error. | |
| 1205 | */ | |
| 1206 | 0 | fun = (JSFunction *) JS_GetPrivate(cx, *objp); |
| 1207 | 0 | if (!fun) |
| 1208 | 0 | return JS_TRUE; |
| 1209 | 0 | if (!fun->interpreted) { |
| 1210 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 1211 | JSMSG_NOT_SCRIPTED_FUNCTION, | |
| 1212 | JS_GetFunctionName(fun)); | |
| 1213 | 0 | return JS_FALSE; |
| 1214 | } | |
| 1215 | 0 | atomstr = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL; |
| 1216 | 0 | flagsword = ((uint32)fun->nregexps << 16) | fun->flags; |
| 1217 | } else { | |
| 1218 | 0 | fun = js_NewFunction(cx, NULL, NULL, 0, 0, NULL, NULL); |
| 1219 | 0 | if (!fun) |
| 1220 | 0 | return JS_FALSE; |
| 1221 | 0 | atomstr = NULL; |
| 1222 | } | |
| 1223 | ||
| 1224 | /* From here on, control flow must flow through label out. */ | |
| 1225 | 0 | JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(fun->object), &tvr); |
| 1226 | 0 | ok = JS_TRUE; |
| 1227 | ||
| 1228 | 0 | if (!JS_XDRStringOrNull(xdr, &atomstr) || |
| 1229 | !JS_XDRUint16(xdr, &fun->nargs) || | |
| 1230 | !JS_XDRUint16(xdr, &fun->extra) || | |
| 1231 | !JS_XDRUint16(xdr, &fun->nvars) || | |
| 1232 | !JS_XDRUint32(xdr, &flagsword)) { | |
| 1233 | goto bad; | |
| 1234 | } | |
| 1235 | ||
| 1236 | /* do arguments and local vars */ | |
| 1237 | 0 | if (fun->object) { |
| 1238 | 0 | n = fun->nargs + fun->nvars; |
| 1239 | 0 | if (xdr->mode == JSXDR_ENCODE) { |
| 1240 | JSScope *scope; | |
| 1241 | JSScopeProperty **spvec, *auto_spvec[8]; | |
| 1242 | void *mark; | |
| 1243 | ||
| 1244 | 0 | if (n <= sizeof auto_spvec / sizeof auto_spvec[0]) { |
| 1245 | 0 | spvec = auto_spvec; |
| 1246 | 0 | mark = NULL; |
| 1247 | } else { | |
| 1248 | 0 | mark = JS_ARENA_MARK(&cx->tempPool); |
| 1249 | 0 | JS_ARENA_ALLOCATE_CAST(spvec, JSScopeProperty **, &cx->tempPool, |
| 1250 | n * sizeof(JSScopeProperty *)); | |
| 1251 | 0 | if (!spvec) { |
| 1252 | 0 | JS_ReportOutOfMemory(cx); |
| 1253 | 0 | goto bad; |
| 1254 | } | |
| 1255 | } | |
| 1256 | 0 | scope = OBJ_SCOPE(fun->object); |
| 1257 | 0 | for (sprop = SCOPE_LAST_PROP(scope); sprop; |
| 1258 | 0 | sprop = sprop->parent) { |
| 1259 | 0 | if (sprop->getter == js_GetArgument) { |
| 1260 | JS_ASSERT(nargs++ <= fun->nargs); | |
| 1261 | 0 | spvec[sprop->shortid] = sprop; |
| 1262 | 0 | } else if (sprop->getter == js_GetLocalVariable) { |
| 1263 | JS_ASSERT(nvars++ <= fun->nvars); | |
| 1264 | 0 | spvec[fun->nargs + sprop->shortid] = sprop; |
| 1265 | } | |
| 1266 | } | |
| 1267 | 0 | for (i = 0; i < n; i++) { |
| 1268 | 0 | sprop = spvec[i]; |
| 1269 | JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); | |
| 1270 | 0 | type = (i < fun->nargs) |
| 1271 | ? JSXDR_FUNARG | |
| 1272 | : (sprop->attrs & JSPROP_READONLY) | |
| 1273 | ? JSXDR_FUNCONST | |
| 1274 | : JSXDR_FUNVAR; | |
| 1275 | 0 | userid = INT_TO_JSVAL(sprop->shortid); |
| 1276 | /* XXX lossy conversion, need new XDR version for ECMAv3 */ | |
| 1277 | 0 | propname = JS_GetStringBytes(ATOM_TO_STRING(JSID_TO_ATOM(sprop->id))); |
| 1278 | 0 | if (!propname || |
| 1279 | !JS_XDRUint32(xdr, &type) || | |
| 1280 | !JS_XDRUint32(xdr, &userid) || | |
| 1281 | !JS_XDRCString(xdr, &propname)) { | |
| 1282 | 0 | if (mark) |
| 1283 | 0 | JS_ARENA_RELEASE(&cx->tempPool, mark); |
| 1284 | 0 | goto bad; |
| 1285 | } | |
| 1286 | } | |
| 1287 | 0 | if (mark) |
| 1288 | 0 | JS_ARENA_RELEASE(&cx->tempPool, mark); |
| 1289 | } else { | |
| 1290 | JSPropertyOp getter, setter; | |
| 1291 | ||
| 1292 | 0 | for (i = n; i != 0; i--) { |
| 1293 | 0 | uintN attrs = JSPROP_PERMANENT; |
| 1294 | ||
| 1295 | 0 | if (!JS_XDRUint32(xdr, &type) || |
| 1296 | !JS_XDRUint32(xdr, &userid) || | |
| 1297 | !JS_XDRCString(xdr, &propname)) { | |
| 1298 | goto bad; | |
| 1299 | } | |
| 1300 | JS_ASSERT(type == JSXDR_FUNARG || type == JSXDR_FUNVAR || | |
| 1301 | type == JSXDR_FUNCONST); | |
| 1302 | 0 | if (type == JSXDR_FUNARG) { |
| 1303 | 0 | getter = js_GetArgument; |
| 1304 | 0 | setter = js_SetArgument; |
| 1305 | JS_ASSERT(nargs++ <= fun->nargs); | |
| 1306 | 0 | } else if (type == JSXDR_FUNVAR || type == JSXDR_FUNCONST) { |
| 1307 | 0 | getter = js_GetLocalVariable; |
| 1308 | 0 | setter = js_SetLocalVariable; |
| 1309 | 0 | if (type == JSXDR_FUNCONST) |
| 1310 | 0 | attrs |= JSPROP_READONLY; |
| 1311 | JS_ASSERT(nvars++ <= fun->nvars); | |
| 1312 | } else { | |
| 1313 | 0 | getter = NULL; |
| 1314 | 0 | setter = NULL; |
| 1315 | } | |
| 1316 | 0 | atom = js_Atomize(cx, propname, strlen(propname), 0); |
| 1317 | 0 | JS_free(cx, propname); |
| 1318 | 0 | if (!atom) |
| 1319 | 0 | goto bad; |
| 1320 | ||
| 1321 | /* Flag duplicate argument if atom is bound in fun->object. */ | |
| 1322 | 0 | dupflag = SCOPE_GET_PROPERTY(OBJ_SCOPE(fun->object), |
| 1323 | ATOM_TO_JSID(atom)) | |
| 1324 | ? SPROP_IS_DUPLICATE | |
| 1325 | : 0; | |
| 1326 | ||
| 1327 | 0 | if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(atom), |
| 1328 | getter, setter, SPROP_INVALID_SLOT, | |
| 1329 | attrs | JSPROP_SHARED, | |
| 1330 | dupflag | SPROP_HAS_SHORTID, | |
| 1331 | JSVAL_TO_INT(userid))) { | |
| 1332 | 0 | goto bad; |
| 1333 | } | |
| 1334 | } | |
| 1335 | } | |
| 1336 | } | |
| 1337 | ||
| 1338 | 0 | if (!js_XDRScript(xdr, &fun->u.script, NULL)) |
| 1339 | 0 | goto bad; |
| 1340 | ||
| 1341 | 0 | if (xdr->mode == JSXDR_DECODE) { |
| 1342 | 0 | fun->interpreted = JS_TRUE; |
| 1343 | 0 | fun->flags = (uint8) flagsword; |
| 1344 | 0 | fun->nregexps = (uint16) (flagsword >> 16); |
| 1345 | ||
| 1346 | 0 | *objp = fun->object; |
| 1347 | 0 | if (atomstr) { |
| 1348 | /* XXX only if this was a top-level function! */ | |
| 1349 | 0 | fun->atom = js_AtomizeString(cx, atomstr, 0); |
| 1350 | 0 | if (!fun->atom) |
| 1351 | 0 | goto bad; |
| 1352 | } | |
| 1353 | ||
| 1354 | 0 | js_CallNewScriptHook(cx, fun->u.script, fun); |
| 1355 | } | |
| 1356 | ||
| 1357 | 0 | out: |
| 1358 | 0 | JS_POP_TEMP_ROOT(cx, &tvr); |
| 1359 | 0 | return ok; |
| 1360 | ||
| 1361 | 0 | bad: |
| 1362 | 0 | ok = JS_FALSE; |
| 1363 | 0 | goto out; |
| 1364 | } | |
| 1365 | ||
| 1366 | #else /* !JS_HAS_XDR */ | |
| 1367 | ||
| 1368 | #define fun_xdrObject NULL | |
| 1369 | ||
| 1370 | #endif /* !JS_HAS_XDR */ | |
| 1371 | ||
| 1372 | #if JS_HAS_INSTANCEOF | |
| 1373 | ||
| 1374 | /* | |
| 1375 | * [[HasInstance]] internal method for Function objects: fetch the .prototype | |
| 1376 | * property of its 'this' parameter, and walks the prototype chain of v (only | |
| 1377 | * if v is an object) returning true if .prototype is found. | |
| 1378 | */ | |
| 1379 | static JSBool | |
| 1380 | fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) | |
| 1381 | 730 | { |
| 1382 | jsval pval; | |
| 1383 | JSString *str; | |
| 1384 | ||
| 1385 | 730 | if (!OBJ_GET_PROPERTY(cx, obj, |
| 1386 | ATOM_TO_JSID(cx->runtime->atomState | |
| 1387 | .classPrototypeAtom), | |
| 1388 | &pval)) { | |
| 1389 | 0 | return JS_FALSE; |
| 1390 | } | |
| 1391 | ||
| 1392 | 730 | if (JSVAL_IS_PRIMITIVE(pval)) { |
| 1393 | /* | |
| 1394 | * Throw a runtime error if instanceof is called on a function that | |
| 1395 | * has a non-object as its .prototype value. | |
| 1396 | */ | |
| 1397 | 0 | str = js_DecompileValueGenerator(cx, -1, OBJECT_TO_JSVAL(obj), NULL); |
| 1398 | 0 | if (str) { |
| 1399 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 1400 | JSMSG_BAD_PROTOTYPE, JS_GetStringBytes(str)); | |
| 1401 | } | |
| 1402 | 0 | return JS_FALSE; |
| 1403 | } | |
| 1404 | ||
| 1405 | 730 | return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp); |
| 1406 | } | |
| 1407 | ||
| 1408 | #else /* !JS_HAS_INSTANCEOF */ | |
| 1409 | ||
| 1410 | #define fun_hasInstance NULL | |
| 1411 | ||
| 1412 | #endif /* !JS_HAS_INSTANCEOF */ | |
| 1413 | ||
| 1414 | static uint32 | |
| 1415 | fun_mark(JSContext *cx, JSObject *obj, void *arg) | |
| 1416 | 13609 | { |
| 1417 | JSFunction *fun; | |
| 1418 | ||
| 1419 | 13609 | fun = (JSFunction *) JS_GetPrivate(cx, obj); |
| 1420 | 13609 | if (fun) { |
| 1421 | 13609 | JS_MarkGCThing(cx, fun, js_private_str, arg); |
| 1422 | 13609 | if (fun->atom) |
| 1423 | 13521 | GC_MARK_ATOM(cx, fun->atom, arg); |
| 1424 | 13609 | if (fun->interpreted && fun->u.script) |
| 1425 | 3853 | js_MarkScript(cx, fun->u.script, arg); |
| 1426 | } | |
| 1427 | 13609 | return 0; |
| 1428 | } | |
| 1429 | ||
| 1430 | static uint32 | |
| 1431 | fun_reserveSlots(JSContext *cx, JSObject *obj) | |
| 1432 | 885 | { |
| 1433 | JSFunction *fun; | |
| 1434 | ||
| 1435 | 885 | fun = (JSFunction *) JS_GetPrivate(cx, obj); |
| 1436 | 885 | return fun ? fun->nregexps : 0; |
| 1437 | } | |
| 1438 | ||
| 1439 | /* | |
| 1440 | * Reserve two slots in all function objects for XPConnect. Note that this | |
| 1441 | * does not bloat every instance, only those on which reserved slots are set, | |
| 1442 | * and those on which ad-hoc properties are defined. | |
| 1443 | */ | |
| 1444 | JS_FRIEND_DATA(JSClass) js_FunctionClass = { | |
| 1445 | js_Function_str, | |
| 1446 | JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2), | |
| 1447 | JS_PropertyStub, JS_PropertyStub, | |
| 1448 | fun_getProperty, JS_PropertyStub, | |
| 1449 | fun_enumerate, (JSResolveOp)fun_resolve, | |
| 1450 | fun_convert, fun_finalize, | |
| 1451 | NULL, NULL, | |
| 1452 | NULL, NULL, | |
| 1453 | fun_xdrObject, fun_hasInstance, | |
| 1454 | fun_mark, fun_reserveSlots | |
| 1455 | }; | |
| 1456 | ||
| 1457 | JSBool | |
| 1458 | js_fun_toString(JSContext *cx, JSObject *obj, uint32 indent, | |
| 1459 | uintN argc, jsval *argv, jsval *rval) | |
| 1460 | 0 | { |
| 1461 | jsval fval; | |
| 1462 | JSFunction *fun; | |
| 1463 | JSString *str; | |
| 1464 | ||
| 1465 | 0 | if (!argv) { |
| 1466 | JS_ASSERT(JS_ObjectIsFunction(cx, obj)); | |
| 1467 | } else { | |
| 1468 | 0 | fval = argv[-1]; |
| 1469 | 0 | if (!JSVAL_IS_FUNCTION(cx, fval)) { |
| 1470 | /* | |
| 1471 | * If we don't have a function to start off with, try converting | |
| 1472 | * the object to a function. If that doesn't work, complain. | |
| 1473 | */ | |
| 1474 | 0 | if (JSVAL_IS_OBJECT(fval)) { |
| 1475 | 0 | obj = JSVAL_TO_OBJECT(fval); |
| 1476 | 0 | if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, JSTYPE_FUNCTION, |
| 1477 | &fval)) { | |
| 1478 | 0 | return JS_FALSE; |
| 1479 | } | |
| 1480 | 0 | argv[-1] = fval; |
| 1481 | } | |
| 1482 | 0 | if (!JSVAL_IS_FUNCTION(cx, fval)) { |
| 1483 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 1484 | JSMSG_INCOMPATIBLE_PROTO, | |
| 1485 | js_Function_str, js_toString_str, | |
| 1486 | JS_GetTypeName(cx, | |
| 1487 | JS_TypeOfValue(cx, fval))); | |
| 1488 | 0 | return JS_FALSE; |
| 1489 | } | |
| 1490 | } | |
| 1491 | ||
| 1492 | 0 | obj = JSVAL_TO_OBJECT(fval); |
| 1493 | } | |
| 1494 | ||
| 1495 | 0 | fun = (JSFunction *) JS_GetPrivate(cx, obj); |
| 1496 | 0 | if (!fun) |
| 1497 | 0 | return JS_TRUE; |
| 1498 | 0 | if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) |
| 1499 | 0 | return JS_FALSE; |
| 1500 | 0 | str = JS_DecompileFunction(cx, fun, (uintN)indent); |
| 1501 | 0 | if (!str) |
| 1502 | 0 | return JS_FALSE; |
| 1503 | 0 | *rval = STRING_TO_JSVAL(str); |
| 1504 | 0 | return JS_TRUE; |
| 1505 | } | |
| 1506 | ||
| 1507 | static JSBool | |
| 1508 | fun_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | |
| 1509 | 0 | { |
| 1510 | 0 | return js_fun_toString(cx, obj, 0, argc, argv, rval); |
| 1511 | } | |
| 1512 | ||
| 1513 | #if JS_HAS_TOSOURCE | |
| 1514 | static JSBool | |
| 1515 | fun_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | |
| 1516 | 0 | { |
| 1517 | 0 | return js_fun_toString(cx, obj, JS_DONT_PRETTY_PRINT, argc, argv, rval); |
| 1518 | } | |
| 1519 | #endif | |
| 1520 | ||
| 1521 | static const char call_str[] = "call"; | |
| 1522 | ||
| 1523 | #if JS_HAS_CALL_FUNCTION | |
| 1524 | static JSBool | |
| 1525 | fun_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | |
| 1526 | 0 | { |
| 1527 | jsval fval, *sp, *oldsp; | |
| 1528 | JSString *str; | |
| 1529 | void *mark; | |
| 1530 | uintN i; | |
| 1531 | JSStackFrame *fp; | |
| 1532 | JSBool ok; | |
| 1533 | ||
| 1534 | 0 | if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1])) |
| 1535 | 0 | return JS_FALSE; |
| 1536 | 0 | fval = argv[-1]; |
| 1537 | ||
| 1538 | 0 | if (!JSVAL_IS_FUNCTION(cx, fval)) { |
| 1539 | 0 | str = JS_ValueToString(cx, fval); |
| 1540 | 0 | if (str) { |
| 1541 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 1542 | JSMSG_INCOMPATIBLE_PROTO, | |
| 1543 | js_Function_str, call_str, | |
| 1544 | JS_GetStringBytes(str)); | |
| 1545 | } | |
| 1546 | 0 | return JS_FALSE; |
| 1547 | } | |
| 1548 | ||
| 1549 | 0 | if (argc == 0) { |
| 1550 | /* Call fun with its global object as the 'this' param if no args. */ | |
| 1551 | 0 | obj = NULL; |
| 1552 | } else { | |
| 1553 | /* Otherwise convert the first arg to 'this' and skip over it. */ | |
| 1554 | 0 | if (!js_ValueToObject(cx, argv[0], &obj)) |
| 1555 | 0 | return JS_FALSE; |
| 1556 | 0 | argc--; |
| 1557 | 0 | argv++; |
| 1558 | } | |
| 1559 | ||
| 1560 | /* Allocate stack space for fval, obj, and the args. */ | |
| 1561 | 0 | sp = js_AllocStack(cx, 2 + argc, &mark); |
| 1562 | 0 | if (!sp) |
| 1563 | 0 | return JS_FALSE; |
| 1564 | ||
| 1565 | /* Push fval, obj, and the args. */ | |
| 1566 | 0 | *sp++ = fval; |
| 1567 | 0 | *sp++ = OBJECT_TO_JSVAL(obj); |
| 1568 | 0 | for (i = 0; i < argc; i++) |
| 1569 | 0 | *sp++ = argv[i]; |
| 1570 | ||
| 1571 | /* Lift current frame to include the args and do the call. */ | |
| 1572 | 0 | fp = cx->fp; |
| 1573 | 0 | oldsp = fp->sp; |
| 1574 | 0 | fp->sp = sp; |
| 1575 | 0 | ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL | JSINVOKE_SKIP_CALLER); |
| 1576 | ||
| 1577 | /* Store rval and pop stack back to our frame's sp. */ | |
| 1578 | 0 | *rval = fp->sp[-1]; |
| 1579 | 0 | fp->sp = oldsp; |
| 1580 | 0 | js_FreeStack(cx, mark); |
| 1581 | 0 | return ok; |
| 1582 | } | |
| 1583 | #endif /* JS_HAS_CALL_FUNCTION */ | |
| 1584 | ||
| 1585 | #if JS_HAS_APPLY_FUNCTION | |
| 1586 | static JSBool | |
| 1587 | fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | |
| 1588 | 0 | { |
| 1589 | jsval fval, *sp, *oldsp; | |
| 1590 | JSString *str; | |
| 1591 | JSObject *aobj; | |
| 1592 | jsuint length; | |
| 1593 | void *mark; | |
| 1594 | uintN i; | |
| 1595 | JSBool ok; | |
| 1596 | JSStackFrame *fp; | |
| 1597 | ||
| 1598 | 0 | if (argc == 0) { |
| 1599 | /* Will get globalObject as 'this' and no other arguments. */ | |
| 1600 | 0 | return fun_call(cx, obj, argc, argv, rval); |
| 1601 | } | |
| 1602 | ||
| 1603 | 0 | if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1])) |
| 1604 | 0 | return JS_FALSE; |
| 1605 | 0 | fval = argv[-1]; |
| 1606 | ||
| 1607 | 0 | if (!JSVAL_IS_FUNCTION(cx, fval)) { |
| 1608 | 0 | str = JS_ValueToString(cx, fval); |
| 1609 | 0 | if (str) { |
| 1610 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 1611 | JSMSG_INCOMPATIBLE_PROTO, | |
| 1612 | js_Function_str, "apply", | |
| 1613 | JS_GetStringBytes(str)); | |
| 1614 | } | |
| 1615 | 0 | return JS_FALSE; |
| 1616 | } | |
| 1617 | ||
| 1618 | /* Quell GCC overwarnings. */ | |
| 1619 | 0 | aobj = NULL; |
| 1620 | 0 | length = 0; |
| 1621 | ||
| 1622 | 0 | if (argc >= 2) { |
| 1623 | /* If the 2nd arg is null or void, call the function with 0 args. */ | |
| 1624 | 0 | if (JSVAL_IS_NULL(argv[1]) || JSVAL_IS_VOID(argv[1])) { |
| 1625 | 0 | argc = 0; |
| 1626 | } else { | |
| 1627 | /* The second arg must be an array (or arguments object). */ | |
| 1628 | 0 | if (JSVAL_IS_PRIMITIVE(argv[1]) || |
| 1629 | (aobj = JSVAL_TO_OBJECT(argv[1]), | |
| 1630 | OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass && | |
| 1631 | OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass)) | |
| 1632 | { | |
| 1633 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 1634 | JSMSG_BAD_APPLY_ARGS); | |
| 1635 | 0 | return JS_FALSE; |
| 1636 | } | |
| 1637 | 0 | if (!js_GetLengthProperty(cx, aobj, &length)) |
| 1638 | 0 | return JS_FALSE; |
| 1639 | } | |
| 1640 | } | |
| 1641 | ||
| 1642 | /* Convert the first arg to 'this' and skip over it. */ | |
| 1643 | 0 | if (!js_ValueToObject(cx, argv[0], &obj)) |
| 1644 | 0 | return JS_FALSE; |
| 1645 | ||
| 1646 | /* Allocate stack space for fval, obj, and the args. */ | |
| 1647 | 0 | argc = (uintN)JS_MIN(length, ARGC_LIMIT - 1); |
| 1648 | 0 | sp = js_AllocStack(cx, 2 + argc, &mark); |
| 1649 | 0 | if (!sp) |
| 1650 | 0 | return JS_FALSE; |
| 1651 | ||
| 1652 | /* Push fval, obj, and aobj's elements as args. */ | |
| 1653 | 0 | *sp++ = fval; |
| 1654 | 0 | *sp++ = OBJECT_TO_JSVAL(obj); |
| 1655 | 0 | for (i = 0; i < argc; i++) { |
| 1656 | 0 | ok = JS_GetElement(cx, aobj, (jsint)i, sp); |
| 1657 | 0 | if (!ok) |
| 1658 | 0 | goto out; |
| 1659 | 0 | sp++; |
| 1660 | } | |
| 1661 | ||
| 1662 | /* Lift current frame to include the args and do the call. */ | |
| 1663 | 0 | fp = cx->fp; |
| 1664 | 0 | oldsp = fp->sp; |
| 1665 | 0 | fp->sp = sp; |
| 1666 | 0 | ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL | JSINVOKE_SKIP_CALLER); |
| 1667 | ||
| 1668 | /* Store rval and pop stack back to our frame's sp. */ | |
| 1669 | 0 | *rval = fp->sp[-1]; |
| 1670 | 0 | fp->sp = oldsp; |
| 1671 | 0 | out: |
| 1672 | 0 | js_FreeStack(cx, mark); |
| 1673 | 0 | return ok; |
| 1674 | } | |
| 1675 | #endif /* JS_HAS_APPLY_FUNCTION */ | |
| 1676 | ||
| 1677 | static JSFunctionSpec function_methods[] = { | |
| 1678 | #if JS_HAS_TOSOURCE | |
| 1679 | {js_toSource_str, fun_toSource, 0,0,0}, | |
| 1680 | #endif | |
| 1681 | {js_toString_str, fun_toString, 1,0,0}, | |
| 1682 | #if JS_HAS_APPLY_FUNCTION | |
| 1683 | {"apply", fun_apply, 2,0,0}, | |
| 1684 | #endif | |
| 1685 | #if JS_HAS_CALL_FUNCTION | |
| 1686 | {call_str, fun_call, 1,0,0}, | |
| 1687 | #endif | |
| 1688 | {0,0,0,0,0} | |
| 1689 | }; | |
| 1690 | ||
| 1691 | JSBool | |
| 1692 | js_IsIdentifier(JSString *str) | |
| 1693 | 0 | { |
| 1694 | size_t n; | |
| 1695 | jschar *s, c; | |
| 1696 | ||
| 1697 | 0 | n = JSSTRING_LENGTH(str); |
| 1698 | 0 | if (n == 0) |
| 1699 | 0 | return JS_FALSE; |
| 1700 | 0 | s = JSSTRING_CHARS(str); |
| 1701 | 0 | c = *s; |
| 1702 | 0 | if (!JS_ISIDSTART(c)) |
| 1703 | 0 | return JS_FALSE; |
| 1704 | 0 | for (n--; n != 0; n--) { |
| 1705 | 0 | c = *++s; |
| 1706 | 0 | if (!JS_ISIDENT(c)) |
| 1707 | 0 | return JS_FALSE; |
| 1708 | } | |
| 1709 | 0 | return JS_TRUE; |
| 1710 | } | |
| 1711 | ||
| 1712 | static JSBool | |
| 1713 | Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | |
| 1714 | 0 | { |
| 1715 | JSStackFrame *fp, *caller; | |
| 1716 | JSFunction *fun; | |
| 1717 | JSObject *parent; | |
| 1718 | uintN i, n, lineno, dupflag; | |
| 1719 | JSAtom *atom; | |
| 1720 | const char *filename; | |
| 1721 | JSObject *obj2; | |
| 1722 | JSProperty *prop; | |
| 1723 | JSScopeProperty *sprop; | |
| 1724 | JSString *str, *arg; | |
| 1725 | void *mark; | |
| 1726 | JSTokenStream *ts; | |
| 1727 | JSPrincipals *principals; | |
| 1728 | jschar *collected_args, *cp; | |
| 1729 | size_t arg_length, args_length, old_args_length; | |
| 1730 | JSTokenType tt; | |
| 1731 | JSBool ok; | |
| 1732 | ||
| 1733 | 0 | fp = cx->fp; |
| 1734 | 0 | if (fp && !(fp->flags & JSFRAME_CONSTRUCTING)) { |
| 1735 | 0 | obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL); |
| 1736 | 0 | if (!obj) |
| 1737 | 0 | return JS_FALSE; |
| 1738 | 0 | *rval = OBJECT_TO_JSVAL(obj); |
| 1739 | } | |
| 1740 | 0 | fun = (JSFunction *) JS_GetPrivate(cx, obj); |
| 1741 | 0 | if (fun) |
| 1742 | 0 | return JS_TRUE; |
| 1743 | ||
| 1744 | #if JS_HAS_CALL_OBJECT | |
| 1745 | /* | |
| 1746 | * NB: (new Function) is not lexically closed by its caller, it's just an | |
| 1747 | * anonymous function in the top-level scope that its constructor inhabits. | |
| 1748 | * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42, | |
| 1749 | * and so would a call to f from another top-level's script or function. | |
| 1750 | * | |
| 1751 | * In older versions, before call objects, a new Function was adopted by | |
| 1752 | * its running context's globalObject, which might be different from the | |
| 1753 | * top-level reachable from scopeChain (in HTML frames, e.g.). | |
| 1754 | */ | |
| 1755 | 0 | parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2])); |
| 1756 | #else | |
| 1757 | /* Set up for dynamic parenting (see js_Invoke in jsinterp.c). */ | |
| 1758 | parent = NULL; | |
| 1759 | #endif | |
| 1760 | ||
| 1761 | 0 | fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA, parent, |
| 1762 | JS_VERSION_IS_ECMA(cx) | |
| 1763 | ? cx->runtime->atomState.anonymousAtom | |
| 1764 | : NULL); | |
| 1765 | ||
| 1766 | 0 | if (!fun) |
| 1767 | 0 | return JS_FALSE; |
| 1768 | ||
| 1769 | /* | |
| 1770 | * Function is static and not called directly by other functions in this | |
| 1771 | * file, therefore it is callable only as a native function by js_Invoke. | |
| 1772 | * Find the scripted caller, possibly skipping other native frames such as | |
| 1773 | * are built for Function.prototype.call or .apply activations that invoke | |
| 1774 | * Function indirectly from a script. | |
| 1775 | */ | |
| 1776 | JS_ASSERT(!fp->script && fp->fun && fp->fun->u.native == Function); | |
| 1777 | 0 | caller = JS_GetScriptedCaller(cx, fp); |
| 1778 | 0 | if (caller) { |
| 1779 | 0 | filename = caller->script->filename; |
| 1780 | 0 | lineno = js_PCToLineNumber(cx, caller->script, caller->pc); |
| 1781 | 0 | principals = JS_EvalFramePrincipals(cx, fp, caller); |
| 1782 | } else { | |
| 1783 | 0 | filename = NULL; |
| 1784 | 0 | lineno = 0; |
| 1785 | 0 | principals = NULL; |
| 1786 | } | |
| 1787 | ||
| 1788 | /* Belt-and-braces: check that the caller has access to parent. */ | |
| 1789 | 0 | if (!js_CheckPrincipalsAccess(cx, parent, principals, js_Function_str)) |
| 1790 | 0 | return JS_FALSE; |
| 1791 | ||
| 1792 | 0 | n = argc ? argc - 1 : 0; |
| 1793 | 0 | if (n > 0) { |
| 1794 | /* | |
| 1795 | * Collect the function-argument arguments into one string, separated | |
| 1796 | * by commas, then make a tokenstream from that string, and scan it to | |
| 1797 | * get the arguments. We need to throw the full scanner at the | |
| 1798 | * problem, because the argument string can legitimately contain | |
| 1799 | * comments and linefeeds. XXX It might be better to concatenate | |
| 1800 | * everything up into a function definition and pass it to the | |
| 1801 | * compiler, but doing it this way is less of a delta from the old | |
| 1802 | * code. See ECMA 15.3.2.1. | |
| 1803 | */ | |
| 1804 | 0 | args_length = 0; |
| 1805 | 0 | for (i = 0; i < n; i++) { |
| 1806 | /* Collect the lengths for all the function-argument arguments. */ | |
| 1807 | 0 | arg = js_ValueToString(cx, argv[i]); |
| 1808 | 0 | if (!arg) |
| 1809 | 0 | return JS_FALSE; |
| 1810 | 0 | argv[i] = STRING_TO_JSVAL(arg); |
| 1811 | ||
| 1812 | /* | |
| 1813 | * Check for overflow. The < test works because the maximum | |
| 1814 | * JSString length fits in 2 fewer bits than size_t has. | |
| 1815 | */ | |
| 1816 | 0 | old_args_length = args_length; |
| 1817 | 0 | args_length = old_args_length + JSSTRING_LENGTH(arg); |
| 1818 | 0 | if (args_length < old_args_length) { |
| 1819 | 0 | JS_ReportOutOfMemory(cx); |
| 1820 | 0 | return JS_FALSE; |
| 1821 | } | |
| 1822 | } | |
| 1823 | ||
| 1824 | /* Add 1 for each joining comma and check for overflow (two ways). */ | |
| 1825 | 0 | old_args_length = args_length; |
| 1826 | 0 | args_length = old_args_length + n - 1; |
| 1827 | 0 | if (args_length < old_args_length || |
| 1828 | args_length >= ~(size_t)0 / sizeof(jschar)) { | |
| 1829 | 0 | JS_ReportOutOfMemory(cx); |
| 1830 | 0 | return JS_FALSE; |
| 1831 | } | |
| 1832 | ||
| 1833 | /* | |
| 1834 | * Allocate a string to hold the concatenated arguments, including room | |
| 1835 | * for a terminating 0. Mark cx->tempPool for later release, to free | |
| 1836 | * collected_args and its tokenstream in one swoop. | |
| 1837 | */ | |
| 1838 | 0 | mark = JS_ARENA_MARK(&cx->tempPool); |
| 1839 | 0 | JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool, |
| 1840 | (args_length+1) * sizeof(jschar)); | |
| 1841 | 0 | if (!cp) { |
| 1842 | 0 | JS_ReportOutOfMemory(cx); |
| 1843 | 0 | return JS_FALSE; |
| 1844 | } | |
| 1845 | 0 | collected_args = cp; |
| 1846 | ||
| 1847 | /* | |
| 1848 | * Concatenate the arguments into the new string, separated by commas. | |
| 1849 | */ | |
| 1850 | 0 | for (i = 0; i < n; i++) { |
| 1851 | 0 | arg = JSVAL_TO_STRING(argv[i]); |
| 1852 | 0 | arg_length = JSSTRING_LENGTH(arg); |
| 1853 | 0 | (void) js_strncpy(cp, JSSTRING_CHARS(arg), arg_length); |
| 1854 | 0 | cp += arg_length; |
| 1855 | ||
| 1856 | /* Add separating comma or terminating 0. */ | |
| 1857 | 0 | *cp++ = (i + 1 < n) ? ',' : 0; |
| 1858 | } | |
| 1859 | ||
| 1860 | /* | |
| 1861 | * Make a tokenstream (allocated from cx->tempPool) that reads from | |
| 1862 | * the given string. | |
| 1863 | */ | |
| 1864 | 0 | ts = js_NewTokenStream(cx, collected_args, args_length, filename, |
| 1865 | lineno, principals); | |
| 1866 | 0 | if (!ts) { |
| 1867 | 0 | JS_ARENA_RELEASE(&cx->tempPool, mark); |
| 1868 | 0 | return JS_FALSE; |
| 1869 | } | |
| 1870 | ||
| 1871 | /* The argument string may be empty or contain no tokens. */ | |
| 1872 | 0 | tt = js_GetToken(cx, ts); |
| 1873 | 0 | if (tt != TOK_EOF) { |
| 1874 | for (;;) { | |
| 1875 | /* | |
| 1876 | * Check that it's a name. This also implicitly guards against | |
| 1877 | * TOK_ERROR, which was already reported. | |
| 1878 | */ | |
| 1879 | 0 | if (tt != TOK_NAME) |
| 1880 | 0 | goto bad_formal; |
| 1881 | ||
| 1882 | /* | |
| 1883 | * Get the atom corresponding to the name from the tokenstream; | |
| 1884 | * we're assured at this point that it's a valid identifier. | |
| 1885 | */ | |
| 1886 | 0 | atom = CURRENT_TOKEN(ts).t_atom; |
| 1887 | 0 | if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), |
| 1888 | &obj2, &prop)) { | |
| 1889 | 0 | goto bad_formal; |
| 1890 | } | |
| 1891 | 0 | sprop = (JSScopeProperty *) prop; |
| 1892 | 0 | dupflag = 0; |
| 1893 | 0 | if (sprop) { |
| 1894 | 0 | ok = JS_TRUE; |
| 1895 | 0 | if (obj2 == obj) { |
| 1896 | 0 | const char *name = js_AtomToPrintableString(cx, atom); |
| 1897 | ||
| 1898 | /* | |
| 1899 | * A duplicate parameter name. We force a duplicate | |
| 1900 | * node on the SCOPE_LAST_PROP(scope) list with the | |
| 1901 | * same id, distinguished by the SPROP_IS_DUPLICATE | |
| 1902 | * flag, and not mapped by an entry in scope. | |
| 1903 | */ | |
| 1904 | JS_ASSERT(sprop->getter == js_GetArgument); | |
| 1905 | 0 | ok = name && |
| 1906 | js_ReportCompileErrorNumber(cx, ts, | |
| 1907 | JSREPORT_TS | | |
| 1908 | JSREPORT_WARNING | | |
| 1909 | JSREPORT_STRICT, | |
| 1910 | JSMSG_DUPLICATE_FORMAL, | |
| 1911 | name); | |
| 1912 | ||
| 1913 | 0 | dupflag = SPROP_IS_DUPLICATE; |
| 1914 | } | |
| 1915 | 0 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 1916 | 0 | if (!ok) |
| 1917 | 0 | goto bad_formal; |
| 1918 | 0 | sprop = NULL; |
| 1919 | } | |
| 1920 | 0 | if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(atom), |
| 1921 | js_GetArgument, js_SetArgument, | |
| 1922 | SPROP_INVALID_SLOT, | |
| 1923 | JSPROP_PERMANENT | JSPROP_SHARED, | |
| 1924 | dupflag | SPROP_HAS_SHORTID, | |
| 1925 | fun->nargs)) { | |
| 1926 | 0 | goto bad_formal; |
| 1927 | } | |
| 1928 | 0 | if (fun->nargs == JS_BITMASK(16)) { |
| 1929 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 1930 | JSMSG_TOO_MANY_FUN_ARGS); | |
| 1931 | 0 | goto bad; |
| 1932 | } | |
| 1933 | 0 | fun->nargs++; |
| 1934 | ||
| 1935 | /* | |
| 1936 | * Get the next token. Stop on end of stream. Otherwise | |
| 1937 | * insist on a comma, get another name, and iterate. | |
| 1938 | */ | |
| 1939 | 0 | tt = js_GetToken(cx, ts); |
| 1940 | 0 | if (tt == TOK_EOF) |
| 1941 | 0 | break; |
| 1942 | 0 | if (tt != TOK_COMMA) |
| 1943 | 0 | goto bad_formal; |
| 1944 | 0 | tt = js_GetToken(cx, ts); |
| 1945 | 0 | } |
| 1946 | } | |
| 1947 | ||
| 1948 | /* Clean up. */ | |
| 1949 | 0 | ok = js_CloseTokenStream(cx, ts); |
| 1950 | 0 | JS_ARENA_RELEASE(&cx->tempPool, mark); |
| 1951 | 0 | if (!ok) |
| 1952 | 0 | return JS_FALSE; |
| 1953 | } | |
| 1954 | ||
| 1955 | 0 | if (argc) { |
| 1956 | 0 | str = js_ValueToString(cx, argv[argc-1]); |
| 1957 | } else { | |
| 1958 | /* Can't use cx->runtime->emptyString because we're called too early. */ | |
| 1959 | 0 | str = js_NewStringCopyZ(cx, js_empty_ucstr, 0); |
| 1960 | } | |
| 1961 | 0 | if (!str) |
| 1962 | 0 | return JS_FALSE; |
| 1963 | 0 | if (argv) { |
| 1964 | /* Use the last arg (or this if argc == 0) as a local GC root. */ | |
| 1965 | 0 | argv[(intN)(argc-1)] = STRING_TO_JSVAL(str); |
| 1966 | } | |
| 1967 | ||
| 1968 | 0 | mark = JS_ARENA_MARK(&cx->tempPool); |
| 1969 | 0 | ts = js_NewTokenStream(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), |
| 1970 | filename, lineno, principals); | |
| 1971 | 0 | if (!ts) { |
| 1972 | 0 | ok = JS_FALSE; |
| 1973 | } else { | |
| 1974 | 0 | ok = js_CompileFunctionBody(cx, ts, fun) && |
| 1975 | js_CloseTokenStream(cx, ts); | |
| 1976 | } | |
| 1977 | 0 | JS_ARENA_RELEASE(&cx->tempPool, mark); |
| 1978 | 0 | return ok; |
| 1979 | ||
| 1980 | 0 | bad_formal: |
| 1981 | /* | |
| 1982 | * Report "malformed formal parameter" iff no illegal char or similar | |
| 1983 | * scanner error was already reported. | |
| 1984 | */ | |
| 1985 | 0 | if (!(ts->flags & TSF_ERROR)) |
| 1986 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_FORMAL); |
| 1987 | ||
| 1988 | 0 | bad: |
| 1989 | /* | |
| 1990 | * Clean up the arguments string and tokenstream if we failed to parse | |
| 1991 | * the arguments. | |
| 1992 | */ | |
| 1993 | 0 | (void)js_CloseTokenStream(cx, ts); |
| 1994 | 0 | JS_ARENA_RELEASE(&cx->tempPool, mark); |
| 1995 | 0 | return JS_FALSE; |
| 1996 | } | |
| 1997 | ||
| 1998 | JSObject * | |
| 1999 | js_InitFunctionClass(JSContext *cx, JSObject *obj) | |
| 2000 | 34 | { |
| 2001 | JSObject *proto; | |
| 2002 | JSAtom *atom; | |
| 2003 | JSFunction *fun; | |
| 2004 | ||
| 2005 | 34 | proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1, |
| 2006 | function_props, function_methods, NULL, NULL); | |
| 2007 | 34 | if (!proto) |
| 2008 | 0 | return NULL; |
| 2009 | 34 | atom = js_Atomize(cx, js_FunctionClass.name, strlen(js_FunctionClass.name), |
| 2010 | 0); | |
| 2011 | 34 | if (!atom) |
| 2012 | 0 | goto bad; |
| 2013 | 34 | fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, NULL); |
| 2014 | 34 | if (!fun) |
| 2015 | 0 | goto bad; |
| 2016 | 34 | fun->u.script = js_NewScript(cx, 0, 0, 0); |
| 2017 | 34 | if (!fun->u.script) |
| 2018 | 0 | goto bad; |
| 2019 | 34 | fun->interpreted = JS_TRUE; |
| 2020 | 34 | return proto; |
| 2021 | ||
| 2022 | 0 | bad: |
| 2023 | 0 | cx->newborn[GCX_OBJECT] = NULL; |
| 2024 | 0 | return NULL; |
| 2025 | } | |
| 2026 | ||
| 2027 | #if JS_HAS_CALL_OBJECT | |
| 2028 | JSObject * | |
| 2029 | js_InitCallClass(JSContext *cx, JSObject *obj) | |
| 2030 | 34 | { |
| 2031 | JSObject *proto; | |
| 2032 | ||
| 2033 | 34 | proto = JS_InitClass(cx, obj, NULL, &js_CallClass, NULL, 0, |
| 2034 | call_props, NULL, NULL, NULL); | |
| 2035 | 34 | if (!proto) |
| 2036 | 0 | return NULL; |
| 2037 | ||
| 2038 | /* | |
| 2039 | * Null Call.prototype's proto slot so that Object.prototype.* does not | |
| 2040 | * pollute the scope of heavyweight functions. | |
| 2041 | */ | |
| 2042 | 34 | OBJ_SET_PROTO(cx, proto, NULL); |
| 2043 | 34 | return proto; |
| 2044 | } | |
| 2045 | #endif | |
| 2046 | ||
| 2047 | JSFunction * | |
| 2048 | js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, | |
| 2049 | uintN flags, JSObject *parent, JSAtom *atom) | |
| 2050 | 17539 | { |
| 2051 | JSFunction *fun; | |
| 2052 | JSTempValueRooter tvr; | |
| 2053 | ||
| 2054 | /* If funobj is null, allocate an object for it. */ | |
| 2055 | 17539 | if (funobj) { |
| 2056 | 34 | OBJ_SET_PARENT(cx, funobj, parent); |
| 2057 | } else { | |
| 2058 | 17505 | funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent); |
| 2059 | 17505 | if (!funobj) |
| 2060 | 0 | return NULL; |
| 2061 | } | |
| 2062 | ||
| 2063 | /* Protect fun from any potential last-ditch GCs. */ | |
| 2064 | 17539 | JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(funobj), &tvr); |
| 2065 | ||
| 2066 | /* | |
| 2067 | * Allocate fun after allocating funobj so slot allocation in js_NewObject | |
| 2068 | * does not wipe out fun from cx->newborn[GCX_PRIVATE]. | |
| 2069 | */ | |
| 2070 | 17539 | fun = (JSFunction *) js_NewGCThing(cx, GCX_PRIVATE, sizeof(JSFunction)); |
| 2071 | 17539 | if (!fun) |
| 2072 | 0 | goto out; |
| 2073 | ||
| 2074 | /* Initialize all function members. */ | |
| 2075 | 17539 | fun->nrefs = 0; |
| 2076 | 17539 | fun->object = NULL; |
| 2077 | 17539 | fun->u.native = native; |
| 2078 | 17539 | fun->nargs = nargs; |
| 2079 | 17539 | fun->extra = 0; |
| 2080 | 17539 | fun->nvars = 0; |
| 2081 | 17539 | fun->flags = flags & JSFUN_FLAGS_MASK; |
| 2082 | 17539 | fun->interpreted = JS_FALSE; |
| 2083 | 17539 | fun->nregexps = 0; |
| 2084 | 17539 | fun->spare = 0; |
| 2085 | 17539 | fun->atom = atom; |
| 2086 | 17539 | fun->clasp = NULL; |
| 2087 | ||
| 2088 | /* Link fun to funobj and vice versa. */ | |
| 2089 | 17539 | if (!js_LinkFunctionObject(cx, fun, funobj)) { |
| 2090 | 0 | cx->newborn[GCX_OBJECT] = NULL; |
| 2091 | 0 | fun = NULL; |
| 2092 | } | |
| 2093 | ||
| 2094 | 17539 | out: |
| 2095 | 17539 | JS_POP_TEMP_ROOT(cx, &tvr); |
| 2096 | 17539 | return fun; |
| 2097 | } | |
| 2098 | ||
| 2099 | JSObject * | |
| 2100 | js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) | |
| 2101 | 0 | { |
| 2102 | JSObject *newfunobj; | |
| 2103 | JSFunction *fun; | |
| 2104 | ||
| 2105 | JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass); | |
| 2106 | 0 | newfunobj = js_NewObject(cx, &js_FunctionClass, funobj, parent); |
| 2107 | 0 | if (!newfunobj) |
| 2108 | 0 | return NULL; |
| 2109 | 0 | fun = (JSFunction *) JS_GetPrivate(cx, funobj); |
| 2110 | 0 | if (!js_LinkFunctionObject(cx, fun, newfunobj)) { |
| 2111 | 0 | cx->newborn[GCX_OBJECT] = NULL; |
| 2112 | 0 | return NULL; |
| 2113 | } | |
| 2114 | 0 | return newfunobj; |
| 2115 | } | |
| 2116 | ||
| 2117 | JSBool | |
| 2118 | js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *funobj) | |
| 2119 | 17539 | { |
| 2120 | 17539 | if (!fun->object) |
| 2121 | 17539 | fun->object = funobj; |
| 2122 | 17539 | if (!JS_SetPrivate(cx, funobj, fun)) |
| 2123 | 0 | return JS_FALSE; |
| 2124 | 17539 | JS_ATOMIC_INCREMENT(&fun->nrefs); |
| 2125 | 17539 | return JS_TRUE; |
| 2126 | } | |
| 2127 | ||
| 2128 | JSFunction * | |
| 2129 | js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, | |
| 2130 | uintN nargs, uintN attrs) | |
| 2131 | 9792 | { |
| 2132 | JSFunction *fun; | |
| 2133 | ||
| 2134 | 9792 | fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom); |
| 2135 | 9792 | if (!fun) |
| 2136 | 0 | return NULL; |
| 2137 | 9792 | if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), |
| 2138 | OBJECT_TO_JSVAL(fun->object), | |
| 2139 | NULL, NULL, | |
| 2140 | attrs & ~JSFUN_FLAGS_MASK, NULL)) { | |
| 2141 | 0 | return NULL; |
| 2142 | } | |
| 2143 | 9792 | return fun; |
| 2144 | } | |
| 2145 | ||
| 2146 | #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK) | |
| 2147 | # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!" | |
| 2148 | #endif | |
| 2149 | ||
| 2150 | JSFunction * | |
| 2151 | js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags) | |
| 2152 | 109913 | { |
| 2153 | jsval v; | |
| 2154 | JSObject *obj; | |
| 2155 | ||
| 2156 | 109913 | v = *vp; |
| 2157 | 109913 | obj = NULL; |
| 2158 | 109913 | if (JSVAL_IS_OBJECT(v)) { |
| 2159 | 109913 | obj = JSVAL_TO_OBJECT(v); |
| 2160 | 109913 | if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) { |
| 2161 | 0 | if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v)) |
| 2162 | 0 | return NULL; |
| 2163 | 0 | obj = JSVAL_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL; |
| 2164 | } | |
| 2165 | } | |
| 2166 | 109913 | if (!obj) { |
| 2167 | 0 | js_ReportIsNotFunction(cx, vp, flags); |
| 2168 | 0 | return NULL; |
| 2169 | } | |
| 2170 | 109913 | return (JSFunction *) JS_GetPrivate(cx, obj); |
| 2171 | } | |
| 2172 | ||
| 2173 | JSObject * | |
| 2174 | js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags) | |
| 2175 | 0 | { |
| 2176 | JSFunction *fun; | |
| 2177 | JSObject *funobj; | |
| 2178 | JSStackFrame *caller; | |
| 2179 | JSPrincipals *principals; | |
| 2180 | ||
| 2181 | 0 | if (JSVAL_IS_FUNCTION(cx, *vp)) |
| 2182 | 0 | return JSVAL_TO_OBJECT(*vp); |
| 2183 | ||
| 2184 | 0 | fun = js_ValueToFunction(cx, vp, flags); |
| 2185 | 0 | if (!fun) |
| 2186 | 0 | return NULL; |
| 2187 | 0 | funobj = fun->object; |
| 2188 | 0 | *vp = OBJECT_TO_JSVAL(funobj); |
| 2189 | ||
| 2190 | 0 | caller = JS_GetScriptedCaller(cx, cx->fp); |
| 2191 | 0 | if (caller) { |
| 2192 | 0 | principals = caller->script->principals; |
| 2193 | } else { | |
| 2194 | /* No scripted caller, don't allow access. */ | |
| 2195 | 0 | principals = NULL; |
| 2196 | } | |
| 2197 | ||
| 2198 | /* | |
| 2199 | * FIXME: Reparameterize so we don't call js_AtomToPrintableString unless | |
| 2200 | * there is an error (bug 324694). | |
| 2201 | */ | |
| 2202 | 0 | if (!js_CheckPrincipalsAccess(cx, funobj, principals, |
| 2203 | fun->atom | |
| 2204 | ? js_AtomToPrintableString(cx, fun->atom) | |
| 2205 | : js_anonymous_str)) { | |
| 2206 | 0 | return NULL; |
| 2207 | } | |
| 2208 | 0 | return funobj; |
| 2209 | } | |
| 2210 | ||
| 2211 | JSObject * | |
| 2212 | js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags) | |
| 2213 | 0 | { |
| 2214 | JSObject *callable; | |
| 2215 | ||
| 2216 | 0 | callable = JSVAL_IS_PRIMITIVE(*vp) ? NULL : JSVAL_TO_OBJECT(*vp); |
| 2217 | 0 | if (callable && |
| 2218 | ((callable->map->ops == &js_ObjectOps) | |
| 2219 | ? OBJ_GET_CLASS(cx, callable)->call | |
| 2220 | : callable->map->ops->call)) { | |
| 2221 | 0 | *vp = OBJECT_TO_JSVAL(callable); |
| 2222 | } else { | |
| 2223 | 0 | callable = js_ValueToFunctionObject(cx, vp, flags); |
| 2224 | } | |
| 2225 | 0 | return callable; |
| 2226 | } | |
| 2227 | ||
| 2228 | void | |
| 2229 | js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) | |
| 2230 | 0 | { |
| 2231 | JSType type; | |
| 2232 | JSString *fallback; | |
| 2233 | JSString *str; | |
| 2234 | ||
| 2235 | /* | |
| 2236 | * We provide the typename as the fallback to handle the case when | |
| 2237 | * valueOf is not a function, which prevents ValueToString from being | |
| 2238 | * called as the default case inside js_DecompileValueGenerator (and | |
| 2239 | * so recursing back to here). | |
| 2240 | */ | |
| 2241 | 0 | type = JS_TypeOfValue(cx, *vp); |
| 2242 | 0 | fallback = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]); |
| 2243 | 0 | str = js_DecompileValueGenerator(cx, |
| 2244 | (flags & JSV2F_SEARCH_STACK) | |
| 2245 | ? JSDVG_SEARCH_STACK | |
| 2246 | : cx->fp | |
| 2247 | ? vp - cx->fp->sp | |
| 2248 | : JSDVG_IGNORE_STACK, | |
| 2249 | *vp, | |
| 2250 | fallback); | |
| 2251 | 0 | if (str) { |
| 2252 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 2253 | (uintN)((flags & JSV2F_CONSTRUCT) | |
| 2254 | ? JSMSG_NOT_CONSTRUCTOR | |
| 2255 | : JSMSG_NOT_FUNCTION), | |
| 2256 | JS_GetStringBytes(str)); | |
| 2257 | } |