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