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 <string.h>
45 :
46 : #include "mozilla/Util.h"
47 :
48 : #include "jstypes.h"
49 : #include "jsutil.h"
50 : #include "jsapi.h"
51 : #include "jsarray.h"
52 : #include "jsatom.h"
53 : #include "jsbool.h"
54 : #include "jscntxt.h"
55 : #include "jsversion.h"
56 : #include "jsfun.h"
57 : #include "jsgc.h"
58 : #include "jsgcmark.h"
59 : #include "jsinterp.h"
60 : #include "jslock.h"
61 : #include "jsnum.h"
62 : #include "jsobj.h"
63 : #include "jsopcode.h"
64 : #include "jspropertytree.h"
65 : #include "jsproxy.h"
66 : #include "jsscope.h"
67 : #include "jsscript.h"
68 : #include "jsstr.h"
69 : #include "jsexn.h"
70 :
71 : #include "frontend/BytecodeCompiler.h"
72 : #include "frontend/BytecodeEmitter.h"
73 : #include "frontend/TokenStream.h"
74 : #include "vm/Debugger.h"
75 : #include "vm/MethodGuard.h"
76 : #include "vm/ScopeObject.h"
77 :
78 : #if JS_HAS_GENERATORS
79 : # include "jsiter.h"
80 : #endif
81 :
82 : #if JS_HAS_XDR
83 : # include "jsxdrapi.h"
84 : #endif
85 :
86 : #ifdef JS_METHODJIT
87 : #include "methodjit/MethodJIT.h"
88 : #endif
89 :
90 : #include "jsatominlines.h"
91 : #include "jsfuninlines.h"
92 : #include "jsinferinlines.h"
93 : #include "jsobjinlines.h"
94 : #include "jsscriptinlines.h"
95 : #include "vm/ArgumentsObject-inl.h"
96 : #include "vm/ScopeObject-inl.h"
97 : #include "vm/Stack-inl.h"
98 :
99 : using namespace mozilla;
100 : using namespace js;
101 : using namespace js::gc;
102 : using namespace js::types;
103 :
104 : js::ArgumentsObject *
105 620688 : ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee)
106 : {
107 620688 : JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
108 :
109 620688 : JSObject *proto = callee.global().getOrCreateObjectPrototype(cx);
110 620688 : if (!proto)
111 0 : return NULL;
112 :
113 1241376 : RootedVarTypeObject type(cx);
114 :
115 620688 : type = proto->getNewType(cx);
116 620688 : if (!type)
117 0 : return NULL;
118 :
119 620688 : bool strict = callee.toFunction()->inStrictMode();
120 620688 : Class *clasp = strict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass;
121 :
122 1241376 : RootedVarShape emptyArgumentsShape(cx);
123 : emptyArgumentsShape =
124 : EmptyShape::getInitialShape(cx, clasp, proto,
125 : proto->getParent(), FINALIZE_KIND,
126 620688 : BaseShape::INDEXED);
127 620688 : if (!emptyArgumentsShape)
128 0 : return NULL;
129 :
130 : ArgumentsData *data = (ArgumentsData *)
131 620688 : cx->malloc_(offsetof(ArgumentsData, slots) + argc * sizeof(Value));
132 620688 : if (!data)
133 0 : return NULL;
134 :
135 620688 : data->callee.init(ObjectValue(callee));
136 2435195 : for (HeapValue *vp = data->slots; vp != data->slots + argc; vp++)
137 1814507 : vp->init(UndefinedValue());
138 :
139 : /* We have everything needed to fill in the object, so make the object. */
140 620688 : JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyArgumentsShape, type, NULL);
141 620688 : if (!obj)
142 0 : return NULL;
143 :
144 620688 : ArgumentsObject &argsobj = obj->asArguments();
145 :
146 620688 : JS_ASSERT(UINT32_MAX > (uint64_t(argc) << PACKED_BITS_COUNT));
147 620688 : argsobj.initInitialLength(argc);
148 620688 : argsobj.initData(data);
149 620688 : argsobj.setStackFrame(NULL);
150 :
151 620688 : JS_ASSERT(argsobj.numFixedSlots() >= NormalArgumentsObject::RESERVED_SLOTS);
152 620688 : JS_ASSERT(argsobj.numFixedSlots() >= StrictArgumentsObject::RESERVED_SLOTS);
153 :
154 620688 : return &argsobj;
155 : }
156 :
157 : struct STATIC_SKIP_INFERENCE PutArg
158 : {
159 620670 : PutArg(JSCompartment *comp, HeapValue *dst) : dst(dst), compartment(comp) {}
160 : HeapValue *dst;
161 : JSCompartment *compartment;
162 1814507 : bool operator()(unsigned, Value *src) {
163 1814507 : JS_ASSERT(dst->isMagic(JS_ARGS_HOLE) || dst->isUndefined());
164 1814507 : if (!dst->isMagic(JS_ARGS_HOLE))
165 1814156 : dst->set(compartment, *src);
166 1814507 : ++dst;
167 1814507 : return true;
168 : }
169 : };
170 :
171 : ArgumentsObject *
172 670300 : js_GetArgsObject(JSContext *cx, StackFrame *fp)
173 : {
174 : /*
175 : * Arguments and Call objects are owned by the enclosing non-eval function
176 : * frame, thus any eval frames must be skipped before testing hasArgsObj.
177 : */
178 670300 : JS_ASSERT(fp->isFunctionFrame());
179 1340600 : while (fp->isEvalInFunction())
180 0 : fp = fp->prev();
181 :
182 : /*
183 : * Mark all functions which have ever had arguments objects constructed,
184 : * which will prevent lazy arguments optimizations in the method JIT.
185 : */
186 670300 : if (!fp->script()->createdArgs)
187 3299 : types::MarkArgumentsCreated(cx, fp->script());
188 :
189 : /* Create an arguments object for fp only if it lacks one. */
190 670300 : JS_ASSERT_IF(fp->fun()->isHeavyweight(), fp->hasCallObj());
191 670300 : if (fp->hasArgsObj())
192 50143 : return &fp->argsObj();
193 :
194 620157 : ArgumentsObject *argsobj = ArgumentsObject::create(cx, fp->numActualArgs(), fp->callee());
195 620157 : if (!argsobj)
196 0 : return argsobj;
197 :
198 : /*
199 : * Strict mode functions have arguments objects that copy the initial
200 : * actual parameter values. It is the caller's responsibility to get the
201 : * arguments object before any parameters are modified! (The emitter
202 : * ensures this by synthesizing an arguments access at the start of any
203 : * strict mode function that contains an assignment to a parameter, or
204 : * that calls eval.) Non-strict mode arguments use the frame pointer to
205 : * retrieve up-to-date parameter values.
206 : */
207 620157 : if (argsobj->isStrictArguments())
208 36267 : fp->forEachCanonicalActualArg(PutArg(cx->compartment, argsobj->data()->slots));
209 : else
210 583890 : argsobj->setStackFrame(fp);
211 :
212 620157 : fp->setArgsObj(*argsobj);
213 620157 : return argsobj;
214 : }
215 :
216 : void
217 620130 : js_PutArgsObject(StackFrame *fp)
218 : {
219 620130 : ArgumentsObject &argsobj = fp->argsObj();
220 620130 : if (argsobj.isNormalArguments()) {
221 583872 : JS_ASSERT(argsobj.maybeStackFrame() == fp);
222 583872 : JSCompartment *comp = fp->scopeChain().compartment();
223 583872 : fp->forEachCanonicalActualArg(PutArg(comp, argsobj.data()->slots));
224 583872 : argsobj.setStackFrame(NULL);
225 : } else {
226 36258 : JS_ASSERT(!argsobj.maybeStackFrame());
227 : }
228 620130 : }
229 :
230 : static JSBool
231 837 : args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
232 : {
233 837 : ArgumentsObject &argsobj = obj->asArguments();
234 837 : if (JSID_IS_INT(id)) {
235 405 : unsigned arg = unsigned(JSID_TO_INT(id));
236 405 : if (arg < argsobj.initialLength())
237 405 : argsobj.setElement(arg, MagicValue(JS_ARGS_HOLE));
238 432 : } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
239 432 : argsobj.markLengthOverridden();
240 0 : } else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) {
241 0 : argsobj.asNormalArguments().clearCallee();
242 : }
243 837 : return true;
244 : }
245 :
246 : static JSBool
247 3117 : ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
248 : {
249 3117 : if (!obj->isNormalArguments())
250 0 : return true;
251 :
252 3117 : NormalArgumentsObject &argsobj = obj->asNormalArguments();
253 3117 : if (JSID_IS_INT(id)) {
254 : /*
255 : * arg can exceed the number of arguments if a script changed the
256 : * prototype to point to another Arguments object with a bigger argc.
257 : */
258 1362 : unsigned arg = unsigned(JSID_TO_INT(id));
259 1362 : if (arg < argsobj.initialLength()) {
260 1362 : JS_ASSERT(!argsobj.element(arg).isMagic(JS_ARGS_HOLE));
261 1362 : if (StackFrame *fp = argsobj.maybeStackFrame())
262 1269 : *vp = fp->canonicalActualArg(arg);
263 : else
264 93 : *vp = argsobj.element(arg);
265 : }
266 1755 : } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
267 240 : if (!argsobj.hasOverriddenLength())
268 240 : vp->setInt32(argsobj.initialLength());
269 : } else {
270 1515 : JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
271 1515 : const Value &v = argsobj.callee();
272 1515 : if (!v.isMagic(JS_ARGS_HOLE))
273 1515 : *vp = v;
274 : }
275 3117 : return true;
276 : }
277 :
278 : static JSBool
279 909 : ArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
280 : {
281 909 : if (!obj->isNormalArguments())
282 0 : return true;
283 :
284 909 : NormalArgumentsObject &argsobj = obj->asNormalArguments();
285 :
286 909 : if (JSID_IS_INT(id)) {
287 504 : unsigned arg = unsigned(JSID_TO_INT(id));
288 504 : if (arg < argsobj.initialLength()) {
289 504 : if (StackFrame *fp = argsobj.maybeStackFrame()) {
290 468 : JSScript *script = fp->functionScript();
291 468 : if (script->usesArguments) {
292 468 : if (arg < fp->numFormalArgs())
293 297 : TypeScript::SetArgument(cx, script, arg, *vp);
294 468 : fp->canonicalActualArg(arg) = *vp;
295 : }
296 468 : return true;
297 : }
298 : }
299 : } else {
300 405 : JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) ||
301 405 : JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
302 : }
303 :
304 : /*
305 : * For simplicity we use delete/define to replace the property with one
306 : * backed by the default Object getter and setter. Note that we rely on
307 : * args_delProperty to clear the corresponding reserved slot so the GC can
308 : * collect its value. Note also that we must define the property instead
309 : * of setting it in case the user has changed the prototype to an object
310 : * that has a setter for this id.
311 : */
312 882 : AutoValueRooter tvr(cx);
313 441 : return js_DeleteGeneric(cx, &argsobj, id, tvr.addr(), false) &&
314 441 : js_DefineProperty(cx, &argsobj, id, vp, NULL, NULL, JSPROP_ENUMERATE);
315 : }
316 :
317 : static JSBool
318 8219 : args_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
319 : JSObject **objp)
320 : {
321 8219 : *objp = NULL;
322 :
323 8219 : NormalArgumentsObject &argsobj = obj->asNormalArguments();
324 :
325 8219 : unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
326 8219 : if (JSID_IS_INT(id)) {
327 6563 : uint32_t arg = uint32_t(JSID_TO_INT(id));
328 6563 : if (arg >= argsobj.initialLength() || argsobj.element(arg).isMagic(JS_ARGS_HOLE))
329 4896 : return true;
330 :
331 1667 : attrs |= JSPROP_ENUMERATE;
332 1656 : } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
333 661 : if (argsobj.hasOverriddenLength())
334 27 : return true;
335 : } else {
336 995 : if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom))
337 362 : return true;
338 :
339 633 : if (argsobj.callee().isMagic(JS_ARGS_HOLE))
340 0 : return true;
341 : }
342 :
343 2934 : Value undef = UndefinedValue();
344 2934 : if (!js_DefineProperty(cx, &argsobj, id, &undef, ArgGetter, ArgSetter, attrs))
345 0 : return JS_FALSE;
346 :
347 2934 : *objp = &argsobj;
348 2934 : return true;
349 : }
350 :
351 : static JSBool
352 180 : args_enumerate(JSContext *cx, JSObject *obj)
353 : {
354 180 : NormalArgumentsObject &argsobj = obj->asNormalArguments();
355 :
356 : /*
357 : * Trigger reflection in args_resolve using a series of js_LookupProperty
358 : * calls.
359 : */
360 180 : int argc = int(argsobj.initialLength());
361 540 : for (int i = -2; i != argc; i++) {
362 : jsid id = (i == -2)
363 180 : ? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
364 : : (i == -1)
365 180 : ? ATOM_TO_JSID(cx->runtime->atomState.calleeAtom)
366 720 : : INT_TO_JSID(i);
367 :
368 : JSObject *pobj;
369 : JSProperty *prop;
370 360 : if (!js_LookupProperty(cx, &argsobj, id, &pobj, &prop))
371 0 : return false;
372 : }
373 180 : return true;
374 : }
375 :
376 : static JSBool
377 337 : StrictArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
378 : {
379 337 : if (!obj->isStrictArguments())
380 0 : return true;
381 :
382 337 : StrictArgumentsObject &argsobj = obj->asStrictArguments();
383 :
384 337 : if (JSID_IS_INT(id)) {
385 : /*
386 : * arg can exceed the number of arguments if a script changed the
387 : * prototype to point to another Arguments object with a bigger argc.
388 : */
389 206 : unsigned arg = unsigned(JSID_TO_INT(id));
390 206 : if (arg < argsobj.initialLength()) {
391 206 : const Value &v = argsobj.element(arg);
392 206 : if (!v.isMagic(JS_ARGS_HOLE))
393 206 : *vp = v;
394 : }
395 : } else {
396 131 : JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
397 131 : if (!argsobj.hasOverriddenLength())
398 131 : vp->setInt32(argsobj.initialLength());
399 : }
400 337 : return true;
401 : }
402 :
403 : static JSBool
404 279 : StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
405 : {
406 279 : if (!obj->isStrictArguments())
407 0 : return true;
408 :
409 279 : StrictArgumentsObject &argsobj = obj->asStrictArguments();
410 :
411 279 : if (JSID_IS_INT(id)) {
412 279 : unsigned arg = unsigned(JSID_TO_INT(id));
413 279 : if (arg < argsobj.initialLength()) {
414 279 : argsobj.setElement(arg, *vp);
415 279 : return true;
416 : }
417 : } else {
418 0 : JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
419 : }
420 :
421 : /*
422 : * For simplicity we use delete/set to replace the property with one
423 : * backed by the default Object getter and setter. Note that we rely on
424 : * args_delProperty to clear the corresponding reserved slot so the GC can
425 : * collect its value.
426 : */
427 0 : AutoValueRooter tvr(cx);
428 0 : return js_DeleteGeneric(cx, &argsobj, id, tvr.addr(), strict) &&
429 0 : js_SetPropertyHelper(cx, &argsobj, id, 0, vp, strict);
430 : }
431 :
432 : static JSBool
433 1206 : strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObject **objp)
434 : {
435 1206 : *objp = NULL;
436 :
437 1206 : StrictArgumentsObject &argsobj = obj->asStrictArguments();
438 :
439 1206 : unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
440 1206 : PropertyOp getter = StrictArgGetter;
441 1206 : StrictPropertyOp setter = StrictArgSetter;
442 :
443 1206 : if (JSID_IS_INT(id)) {
444 1093 : uint32_t arg = uint32_t(JSID_TO_INT(id));
445 1093 : if (arg >= argsobj.initialLength() || argsobj.element(arg).isMagic(JS_ARGS_HOLE))
446 603 : return true;
447 :
448 490 : attrs |= JSPROP_ENUMERATE;
449 113 : } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
450 113 : if (argsobj.hasOverriddenLength())
451 0 : return true;
452 : } else {
453 0 : if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom) &&
454 0 : !JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) {
455 0 : return true;
456 : }
457 :
458 0 : attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
459 0 : getter = CastAsPropertyOp(argsobj.global().getThrowTypeError());
460 0 : setter = CastAsStrictPropertyOp(argsobj.global().getThrowTypeError());
461 : }
462 :
463 603 : Value undef = UndefinedValue();
464 603 : if (!js_DefineProperty(cx, &argsobj, id, &undef, getter, setter, attrs))
465 0 : return false;
466 :
467 603 : *objp = &argsobj;
468 603 : return true;
469 : }
470 :
471 : static JSBool
472 0 : strictargs_enumerate(JSContext *cx, JSObject *obj)
473 : {
474 0 : StrictArgumentsObject *argsobj = &obj->asStrictArguments();
475 :
476 : /*
477 : * Trigger reflection in strictargs_resolve using a series of
478 : * js_LookupProperty calls.
479 : */
480 : JSObject *pobj;
481 : JSProperty *prop;
482 :
483 : // length
484 0 : if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &pobj, &prop))
485 0 : return false;
486 :
487 : // callee
488 0 : if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), &pobj, &prop))
489 0 : return false;
490 :
491 : // caller
492 0 : if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.callerAtom), &pobj, &prop))
493 0 : return false;
494 :
495 0 : for (uint32_t i = 0, argc = argsobj->initialLength(); i < argc; i++) {
496 0 : if (!js_LookupProperty(cx, argsobj, INT_TO_JSID(i), &pobj, &prop))
497 0 : return false;
498 : }
499 :
500 0 : return true;
501 : }
502 :
503 : static void
504 620688 : args_finalize(JSContext *cx, JSObject *obj)
505 : {
506 620688 : cx->free_(reinterpret_cast<void *>(obj->asArguments().data()));
507 620688 : }
508 :
509 : static void
510 2859 : args_trace(JSTracer *trc, JSObject *obj)
511 : {
512 2859 : ArgumentsObject &argsobj = obj->asArguments();
513 2859 : ArgumentsData *data = argsobj.data();
514 2859 : MarkValue(trc, &data->callee, js_callee_str);
515 2859 : MarkValueRange(trc, argsobj.initialLength(), data->slots, js_arguments_str);
516 :
517 : /*
518 : * If a generator's arguments or call object escapes, and the generator
519 : * frame is not executing, the generator object needs to be marked because
520 : * it is not otherwise reachable. An executing generator is rooted by its
521 : * invocation. To distinguish the two cases (which imply different access
522 : * paths to the generator object), we use the JSFRAME_FLOATING_GENERATOR
523 : * flag, which is only set on the StackFrame kept in the generator object's
524 : * JSGenerator.
525 : */
526 : #if JS_HAS_GENERATORS
527 2859 : StackFrame *fp = argsobj.maybeStackFrame();
528 2859 : if (fp && fp->isFloatingGenerator())
529 386 : MarkObject(trc, &js_FloatingFrameToGenerator(fp)->obj, "generator object");
530 : #endif
531 2859 : }
532 :
533 : /*
534 : * The classes below collaborate to lazily reflect and synchronize actual
535 : * argument values, argument count, and callee function object stored in a
536 : * StackFrame with their corresponding property values in the frame's
537 : * arguments object.
538 : */
539 : Class js::NormalArgumentsObjectClass = {
540 : "Arguments",
541 : JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
542 : JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) |
543 : JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
544 : JSCLASS_FOR_OF_ITERATION,
545 : JS_PropertyStub, /* addProperty */
546 : args_delProperty,
547 : JS_PropertyStub, /* getProperty */
548 : JS_StrictPropertyStub, /* setProperty */
549 : args_enumerate,
550 : reinterpret_cast<JSResolveOp>(args_resolve),
551 : JS_ConvertStub,
552 : args_finalize, /* finalize */
553 : NULL, /* checkAccess */
554 : NULL, /* call */
555 : NULL, /* construct */
556 : NULL, /* hasInstance */
557 : args_trace,
558 : {
559 : NULL, /* equality */
560 : NULL, /* outerObject */
561 : NULL, /* innerObject */
562 : JS_ElementIteratorStub,
563 : NULL, /* unused */
564 : false, /* isWrappedNative */
565 : }
566 : };
567 :
568 : /*
569 : * Strict mode arguments is significantly less magical than non-strict mode
570 : * arguments, so it is represented by a different class while sharing some
571 : * functionality.
572 : */
573 : Class js::StrictArgumentsObjectClass = {
574 : "Arguments",
575 : JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
576 : JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) |
577 : JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
578 : JSCLASS_FOR_OF_ITERATION,
579 : JS_PropertyStub, /* addProperty */
580 : args_delProperty,
581 : JS_PropertyStub, /* getProperty */
582 : JS_StrictPropertyStub, /* setProperty */
583 : strictargs_enumerate,
584 : reinterpret_cast<JSResolveOp>(strictargs_resolve),
585 : JS_ConvertStub,
586 : args_finalize, /* finalize */
587 : NULL, /* checkAccess */
588 : NULL, /* call */
589 : NULL, /* construct */
590 : NULL, /* hasInstance */
591 : args_trace,
592 : {
593 : NULL, /* equality */
594 : NULL, /* outerObject */
595 : NULL, /* innerObject */
596 : JS_ElementIteratorStub,
597 : NULL, /* unused */
598 : false, /* isWrappedNative */
599 : }
600 : };
601 :
602 : bool
603 5455 : StackFrame::getValidCalleeObject(JSContext *cx, Value *vp)
604 : {
605 5455 : if (!isFunctionFrame()) {
606 297 : vp->setNull();
607 297 : return true;
608 : }
609 :
610 5158 : JSFunction *fun = this->callee().toFunction();
611 5158 : vp->setObject(*fun);
612 :
613 : /*
614 : * Check for an escape attempt by a joined function object, which must go
615 : * through the frame's |this| object's method read barrier for the method
616 : * atom by which it was uniquely associated with a property.
617 : */
618 5158 : const Value &thisv = functionThis();
619 5158 : if (thisv.isObject() && fun->methodAtom() && !fun->isClonedMethod()) {
620 0 : JSObject *thisp = &thisv.toObject();
621 0 : JSObject *first_barriered_thisp = NULL;
622 :
623 0 : do {
624 : /*
625 : * While a non-native object is responsible for handling its
626 : * entire prototype chain, notable non-natives including dense
627 : * and typed arrays have native prototypes, so keep going.
628 : */
629 0 : if (!thisp->isNative())
630 0 : continue;
631 :
632 0 : const Shape *shape = thisp->nativeLookup(cx, ATOM_TO_JSID(fun->methodAtom()));
633 0 : if (shape) {
634 : /*
635 : * Two cases follow: the method barrier was not crossed
636 : * yet, so we cross it here; the method barrier *was*
637 : * crossed but after the call, in which case we fetch
638 : * and validate the cloned (unjoined) funobj from the
639 : * method property's slot.
640 : *
641 : * In either case we must allow for the method property
642 : * to have been replaced, or its value overwritten.
643 : */
644 0 : if (shape->isMethod() && thisp->nativeGetMethod(shape) == fun) {
645 0 : if (!thisp->methodReadBarrier(cx, *shape, vp))
646 0 : return false;
647 0 : overwriteCallee(vp->toObject());
648 0 : return true;
649 : }
650 :
651 0 : if (shape->hasSlot()) {
652 0 : Value v = thisp->getSlot(shape->slot());
653 : JSFunction *clone;
654 :
655 0 : if (IsFunctionObject(v, &clone) &&
656 0 : clone->isInterpreted() &&
657 0 : clone->script() == fun->script() &&
658 0 : clone->methodObj() == thisp) {
659 : /*
660 : * N.B. If the method barrier was on a function
661 : * with singleton type, then while crossing the
662 : * method barrier CloneFunctionObject will have
663 : * ignored the attempt to clone the function.
664 : */
665 0 : JS_ASSERT_IF(!clone->hasSingletonType(), clone != fun);
666 0 : *vp = v;
667 0 : overwriteCallee(*clone);
668 0 : return true;
669 : }
670 : }
671 : }
672 :
673 0 : if (!first_barriered_thisp)
674 0 : first_barriered_thisp = thisp;
675 0 : } while ((thisp = thisp->getProto()) != NULL);
676 :
677 0 : if (!first_barriered_thisp)
678 0 : return true;
679 :
680 : /*
681 : * At this point, we couldn't find an already-existing clone (or
682 : * force to exist a fresh clone) created via thisp's method read
683 : * barrier, so we must clone fun and store it in fp's callee to
684 : * avoid re-cloning upon repeated foo.caller access.
685 : *
686 : * This must mean the code in js_DeleteGeneric could not find this
687 : * stack frame on the stack when the method was deleted. We've lost
688 : * track of the method, so we associate it with the first barriered
689 : * object found starting from thisp on the prototype chain.
690 : */
691 0 : JSFunction *newfunobj = CloneFunctionObject(cx, fun);
692 0 : if (!newfunobj)
693 0 : return false;
694 0 : newfunobj->setMethodObj(*first_barriered_thisp);
695 0 : overwriteCallee(*newfunobj);
696 0 : vp->setObject(*newfunobj);
697 0 : return true;
698 : }
699 :
700 5158 : return true;
701 : }
702 :
703 : static JSBool
704 1942 : fun_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
705 : {
706 3884 : while (!obj->isFunction()) {
707 0 : obj = obj->getProto();
708 0 : if (!obj)
709 0 : return true;
710 : }
711 1942 : JSFunction *fun = obj->toFunction();
712 :
713 : /*
714 : * Mark the function's script as uninlineable, to expand any of its
715 : * frames on the stack before we go looking for them. This allows the
716 : * below walk to only check each explicit frame rather than needing to
717 : * check any calls that were inlined.
718 : */
719 1942 : if (fun->isInterpreted()) {
720 1942 : fun->script()->uninlineable = true;
721 1942 : MarkTypeObjectFlags(cx, fun, OBJECT_FLAG_UNINLINEABLE);
722 : }
723 :
724 : /* Set to early to null in case of error */
725 1942 : vp->setNull();
726 :
727 : /* Find fun's top-most activation record. */
728 1942 : StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
729 2185 : for (; fp; fp = fp->prev()) {
730 2111 : if (!fp->isFunctionFrame() || fp->isEvalFrame())
731 81 : continue;
732 : Value callee;
733 2030 : if (!fp->getValidCalleeObject(cx, &callee))
734 0 : return false;
735 2030 : if (&callee.toObject() == fun)
736 1868 : break;
737 : }
738 1942 : if (!fp)
739 74 : return true;
740 :
741 : #ifdef JS_METHODJIT
742 1868 : if (JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom) && fp && fp->prev()) {
743 : /*
744 : * If the frame was called from within an inlined frame, mark the
745 : * innermost function as uninlineable to expand its frame and allow us
746 : * to recover its callee object.
747 : */
748 : JSInlinedSite *inlined;
749 1337 : jsbytecode *prevpc = fp->prev()->pcQuadratic(cx->stack, fp, &inlined);
750 1337 : if (inlined) {
751 0 : mjit::JITChunk *chunk = fp->prev()->jit()->chunk(prevpc);
752 0 : JSFunction *fun = chunk->inlineFrames()[inlined->inlineIndex].fun;
753 0 : fun->script()->uninlineable = true;
754 0 : MarkTypeObjectFlags(cx, fun, OBJECT_FLAG_UNINLINEABLE);
755 : }
756 : }
757 : #endif
758 :
759 1868 : if (JSID_IS_ATOM(id, cx->runtime->atomState.argumentsAtom)) {
760 : /* Warn if strict about f.arguments or equivalent unqualified uses. */
761 531 : if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage,
762 531 : NULL, JSMSG_DEPRECATED_USAGE, js_arguments_str)) {
763 0 : return false;
764 : }
765 :
766 : /*
767 : * Purposefully disconnect the returned arguments object from the frame
768 : * by always creating a new copy that does not alias formal parameters.
769 : * This allows function-local analysis to determine that formals are
770 : * not aliased and generally simplifies arguments objects.
771 : */
772 531 : ArgumentsObject *argsobj = ArgumentsObject::create(cx, fp->numActualArgs(), fp->callee());
773 531 : if (!argsobj)
774 0 : return false;
775 :
776 531 : fp->forEachCanonicalActualArg(PutArg(cx->compartment, argsobj->data()->slots));
777 531 : *vp = ObjectValue(*argsobj);
778 531 : return true;
779 : }
780 :
781 1337 : if (JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom)) {
782 1337 : if (!fp->prev())
783 0 : return true;
784 :
785 1337 : StackFrame *frame = fp->prev();
786 2674 : while (frame && frame->isDummyFrame())
787 0 : frame = frame->prev();
788 :
789 1337 : if (frame && !frame->getValidCalleeObject(cx, vp))
790 0 : return false;
791 :
792 1337 : if (!vp->isObject()) {
793 297 : JS_ASSERT(vp->isNull());
794 297 : return true;
795 : }
796 :
797 : /* Censor the caller if it is from another compartment. */
798 1040 : JSObject &caller = vp->toObject();
799 1040 : if (caller.compartment() != cx->compartment) {
800 0 : vp->setNull();
801 1040 : } else if (caller.isFunction()) {
802 1040 : JSFunction *callerFun = caller.toFunction();
803 1040 : if (callerFun->isInterpreted() && callerFun->inStrictMode()) {
804 : JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
805 0 : JSMSG_CALLER_IS_STRICT);
806 0 : return false;
807 : }
808 : }
809 :
810 1040 : return true;
811 : }
812 :
813 0 : JS_NOT_REACHED("fun_getProperty");
814 : return false;
815 : }
816 :
817 :
818 :
819 : /* NB: no sentinels at ends -- use ArrayLength to bound loops.
820 : * Properties censored into [[ThrowTypeError]] in strict mode. */
821 : static const uint16_t poisonPillProps[] = {
822 : ATOM_OFFSET(arguments),
823 : ATOM_OFFSET(caller),
824 : };
825 :
826 : static JSBool
827 249735 : fun_enumerate(JSContext *cx, JSObject *obj)
828 : {
829 249735 : JS_ASSERT(obj->isFunction());
830 :
831 499470 : RootObject root(cx, &obj);
832 :
833 : jsid id;
834 : bool found;
835 :
836 249735 : if (!obj->isBoundFunction()) {
837 249735 : id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
838 249735 : if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
839 0 : return false;
840 : }
841 :
842 249735 : id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
843 249735 : if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
844 0 : return false;
845 :
846 249735 : id = ATOM_TO_JSID(cx->runtime->atomState.nameAtom);
847 249735 : if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
848 0 : return false;
849 :
850 749205 : for (unsigned i = 0; i < ArrayLength(poisonPillProps); i++) {
851 499470 : const uint16_t offset = poisonPillProps[i];
852 499470 : id = ATOM_TO_JSID(OFFSET_TO_ATOM(cx->runtime, offset));
853 499470 : if (!obj->hasProperty(cx, id, &found, JSRESOLVE_QUALIFIED))
854 0 : return false;
855 : }
856 :
857 249735 : return true;
858 : }
859 :
860 : static JSObject *
861 40694 : ResolveInterpretedFunctionPrototype(JSContext *cx, JSObject *obj)
862 : {
863 : #ifdef DEBUG
864 40694 : JSFunction *fun = obj->toFunction();
865 40694 : JS_ASSERT(fun->isInterpreted());
866 40694 : JS_ASSERT(!fun->isFunctionPrototype());
867 : #endif
868 :
869 : /*
870 : * Assert that fun is not a compiler-created function object, which
871 : * must never leak to script or embedding code and then be mutated.
872 : * Also assert that obj is not bound, per the ES5 15.3.4.5 ref above.
873 : */
874 40694 : JS_ASSERT(!IsInternalFunctionObject(obj));
875 40694 : JS_ASSERT(!obj->isBoundFunction());
876 :
877 : /*
878 : * Make the prototype object an instance of Object with the same parent
879 : * as the function object itself.
880 : */
881 40694 : JSObject *objProto = obj->global().getOrCreateObjectPrototype(cx);
882 40694 : if (!objProto)
883 0 : return NULL;
884 40694 : JSObject *proto = NewObjectWithGivenProto(cx, &ObjectClass, objProto, NULL);
885 40694 : if (!proto || !proto->setSingletonType(cx))
886 0 : return NULL;
887 :
888 : /*
889 : * Per ES5 15.3.5.2 a user-defined function's .prototype property is
890 : * initially non-configurable, non-enumerable, and writable. Per ES5 13.2
891 : * the prototype's .constructor property is configurable, non-enumerable,
892 : * and writable.
893 : */
894 81388 : if (!obj->defineProperty(cx, cx->runtime->atomState.classPrototypeAtom,
895 : ObjectValue(*proto), JS_PropertyStub, JS_StrictPropertyStub,
896 40694 : JSPROP_PERMANENT) ||
897 : !proto->defineProperty(cx, cx->runtime->atomState.constructorAtom,
898 40694 : ObjectValue(*obj), JS_PropertyStub, JS_StrictPropertyStub, 0))
899 : {
900 0 : return NULL;
901 : }
902 :
903 40694 : return proto;
904 : }
905 :
906 : static JSBool
907 2036991 : fun_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
908 : JSObject **objp)
909 : {
910 2036991 : if (!JSID_IS_ATOM(id))
911 1484 : return true;
912 :
913 4071014 : RootedVarFunction fun(cx);
914 2035507 : fun = obj->toFunction();
915 :
916 2035507 : if (JSID_IS_ATOM(id, cx->runtime->atomState.classPrototypeAtom)) {
917 : /*
918 : * Native or "built-in" functions do not have a .prototype property per
919 : * ECMA-262, or (Object.prototype, Function.prototype, etc.) have that
920 : * property created eagerly.
921 : *
922 : * ES5 15.3.4: the non-native function object named Function.prototype
923 : * does not have a .prototype property.
924 : *
925 : * ES5 15.3.4.5: bound functions don't have a prototype property. The
926 : * isNative() test covers this case because bound functions are native
927 : * functions by definition/construction.
928 : */
929 122091 : if (fun->isNative() || fun->isFunctionPrototype())
930 81397 : return true;
931 :
932 40694 : if (!ResolveInterpretedFunctionPrototype(cx, fun))
933 0 : return false;
934 40694 : *objp = fun;
935 40694 : return true;
936 : }
937 :
938 3576269 : if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) ||
939 1662853 : JSID_IS_ATOM(id, cx->runtime->atomState.nameAtom)) {
940 501442 : JS_ASSERT(!IsInternalFunctionObject(obj));
941 :
942 : Value v;
943 501442 : if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
944 250563 : v.setInt32(fun->nargs);
945 : else
946 250879 : v.setString(fun->atom ? fun->atom : cx->runtime->emptyString);
947 :
948 501442 : if (!DefineNativeProperty(cx, fun, id, v, JS_PropertyStub, JS_StrictPropertyStub,
949 501442 : JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
950 0 : return false;
951 : }
952 501442 : *objp = fun;
953 501442 : return true;
954 : }
955 :
956 3487512 : for (unsigned i = 0; i < ArrayLength(poisonPillProps); i++) {
957 2574473 : const uint16_t offset = poisonPillProps[i];
958 :
959 2574473 : if (JSID_IS_ATOM(id, OFFSET_TO_ATOM(cx->runtime, offset))) {
960 498935 : JS_ASSERT(!IsInternalFunctionObject(fun));
961 :
962 : PropertyOp getter;
963 : StrictPropertyOp setter;
964 498935 : unsigned attrs = JSPROP_PERMANENT;
965 498935 : if (fun->isInterpreted() ? fun->inStrictMode() : fun->isBoundFunction()) {
966 0 : JSObject *throwTypeError = fun->global().getThrowTypeError();
967 :
968 0 : getter = CastAsPropertyOp(throwTypeError);
969 0 : setter = CastAsStrictPropertyOp(throwTypeError);
970 0 : attrs |= JSPROP_GETTER | JSPROP_SETTER;
971 : } else {
972 498935 : getter = fun_getProperty;
973 498935 : setter = JS_StrictPropertyStub;
974 : }
975 :
976 498935 : if (!DefineNativeProperty(cx, fun, id, UndefinedValue(), getter, setter,
977 498935 : attrs, 0, 0)) {
978 0 : return false;
979 : }
980 498935 : *objp = fun;
981 498935 : return true;
982 : }
983 : }
984 :
985 913039 : return true;
986 : }
987 :
988 : #if JS_HAS_XDR
989 :
990 : /* XXX store parent and proto, if defined */
991 : JSBool
992 623626 : js::XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
993 : {
994 : JSContext *cx;
995 : JSFunction *fun;
996 : uint32_t firstword; /* flag telling whether fun->atom is non-null,
997 : plus for fun->u.i.skipmin, fun->u.i.wrapper,
998 : and 14 bits reserved for future use */
999 : uint32_t flagsword; /* word for argument count and fun->flags */
1000 :
1001 623626 : cx = xdr->cx;
1002 : JSScript *script;
1003 623626 : if (xdr->mode == JSXDR_ENCODE) {
1004 449470 : fun = (*objp)->toFunction();
1005 449470 : if (!fun->isInterpreted()) {
1006 0 : JSAutoByteString funNameBytes;
1007 0 : if (const char *name = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
1008 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_SCRIPTED_FUNCTION,
1009 0 : name);
1010 : }
1011 0 : return false;
1012 : }
1013 449470 : firstword = !!fun->atom;
1014 449470 : flagsword = (fun->nargs << 16) | fun->flags;
1015 449470 : script = fun->script();
1016 : } else {
1017 348312 : RootedVarObject parent(cx, NULL);
1018 174156 : fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, parent, NULL);
1019 174156 : if (!fun)
1020 0 : return false;
1021 174156 : if (!fun->clearParent(cx))
1022 0 : return false;
1023 174156 : if (!fun->clearType(cx))
1024 0 : return false;
1025 348312 : script = NULL;
1026 : }
1027 :
1028 623626 : if (!JS_XDRUint32(xdr, &firstword))
1029 0 : return false;
1030 623626 : if ((firstword & 1U) && !js_XDRAtom(xdr, &fun->atom))
1031 0 : return false;
1032 623626 : if (!JS_XDRUint32(xdr, &flagsword))
1033 0 : return false;
1034 :
1035 623626 : if (!XDRScript(xdr, &script))
1036 0 : return false;
1037 :
1038 623626 : if (xdr->mode == JSXDR_DECODE) {
1039 174156 : fun->nargs = flagsword >> 16;
1040 174156 : JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED);
1041 174156 : fun->flags = uint16_t(flagsword);
1042 174156 : fun->setScript(script);
1043 174156 : if (!script->typeSetFunction(cx, fun))
1044 0 : return false;
1045 174156 : JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
1046 174156 : js_CallNewScriptHook(cx, fun->script(), fun);
1047 174156 : *objp = fun;
1048 : }
1049 :
1050 623626 : return true;
1051 : }
1052 :
1053 : #endif /* JS_HAS_XDR */
1054 :
1055 : /*
1056 : * [[HasInstance]] internal method for Function objects: fetch the .prototype
1057 : * property of its 'this' parameter, and walks the prototype chain of v (only
1058 : * if v is an object) returning true if .prototype is found.
1059 : */
1060 : static JSBool
1061 1060694 : fun_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
1062 : {
1063 2121388 : while (obj->isFunction()) {
1064 1060694 : if (!obj->isBoundFunction())
1065 1060694 : break;
1066 0 : obj = obj->toFunction()->getBoundFunctionTarget();
1067 : }
1068 :
1069 : Value pval;
1070 1060694 : if (!obj->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &pval))
1071 0 : return JS_FALSE;
1072 :
1073 1060694 : if (pval.isPrimitive()) {
1074 : /*
1075 : * Throw a runtime error if instanceof is called on a function that
1076 : * has a non-object as its .prototype value.
1077 : */
1078 7 : js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE, -1, ObjectValue(*obj), NULL);
1079 7 : return JS_FALSE;
1080 : }
1081 :
1082 1060687 : *bp = js_IsDelegate(cx, &pval.toObject(), *v);
1083 1060687 : return JS_TRUE;
1084 : }
1085 :
1086 : inline void
1087 42598904 : JSFunction::trace(JSTracer *trc)
1088 : {
1089 42598904 : if (isFlatClosure() && hasFlatClosureUpvars()) {
1090 1442469 : if (HeapValue *upvars = getFlatClosureUpvars())
1091 1442469 : MarkValueRange(trc, script()->bindings.countUpvars(), upvars, "upvars");
1092 : }
1093 :
1094 42598904 : if (isExtended()) {
1095 6857965 : MarkValueRange(trc, ArrayLength(toExtended()->extendedSlots),
1096 13715930 : toExtended()->extendedSlots, "nativeReserved");
1097 : }
1098 :
1099 42598904 : if (atom)
1100 34810200 : MarkStringUnbarriered(trc, &atom, "atom");
1101 :
1102 42598904 : if (isInterpreted()) {
1103 13649237 : if (u.i.script_)
1104 13649237 : MarkScriptUnbarriered(trc, &u.i.script_, "script");
1105 13649237 : if (u.i.env_)
1106 7118894 : MarkObjectUnbarriered(trc, &u.i.env_, "fun_callscope");
1107 : }
1108 42598904 : }
1109 :
1110 : static void
1111 42598904 : fun_trace(JSTracer *trc, JSObject *obj)
1112 : {
1113 42598904 : obj->toFunction()->trace(trc);
1114 42598904 : }
1115 :
1116 : static void
1117 13871831 : fun_finalize(JSContext *cx, JSObject *obj)
1118 : {
1119 13871831 : if (obj->toFunction()->isFlatClosure())
1120 684577 : obj->toFunction()->finalizeUpvars();
1121 13871831 : }
1122 :
1123 : size_t
1124 16046 : JSFunction::sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const
1125 : {
1126 16884 : return (isFlatClosure() && hasFlatClosureUpvars()) ?
1127 439 : mallocSizeOf(getFlatClosureUpvars()) :
1128 17323 : 0;
1129 : }
1130 :
1131 : /*
1132 : * Reserve two slots in all function objects for XPConnect. Note that this
1133 : * does not bloat every instance, only those on which reserved slots are set,
1134 : * and those on which ad-hoc properties are defined.
1135 : */
1136 : JS_FRIEND_DATA(Class) js::FunctionClass = {
1137 : js_Function_str,
1138 : JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
1139 : JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
1140 : JS_PropertyStub, /* addProperty */
1141 : JS_PropertyStub, /* delProperty */
1142 : JS_PropertyStub, /* getProperty */
1143 : JS_StrictPropertyStub, /* setProperty */
1144 : fun_enumerate,
1145 : (JSResolveOp)fun_resolve,
1146 : JS_ConvertStub,
1147 : fun_finalize,
1148 : NULL, /* checkAccess */
1149 : NULL, /* call */
1150 : NULL, /* construct */
1151 : fun_hasInstance,
1152 : fun_trace
1153 : };
1154 :
1155 : JSString *
1156 270098 : fun_toStringHelper(JSContext *cx, JSObject *obj, unsigned indent)
1157 : {
1158 270098 : if (!obj->isFunction()) {
1159 9 : if (IsFunctionProxy(obj))
1160 9 : return Proxy::fun_toString(cx, obj, indent);
1161 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1162 : JSMSG_INCOMPATIBLE_PROTO,
1163 : js_Function_str, js_toString_str,
1164 0 : "object");
1165 0 : return NULL;
1166 : }
1167 :
1168 270089 : JSFunction *fun = obj->toFunction();
1169 270089 : if (!fun)
1170 0 : return NULL;
1171 :
1172 270089 : if (!indent && !cx->compartment->toSourceCache.empty()) {
1173 258337 : ToSourceCache::Ptr p = cx->compartment->toSourceCache.ref().lookup(fun);
1174 258337 : if (p)
1175 257565 : return p->value;
1176 : }
1177 :
1178 12524 : JSString *str = JS_DecompileFunction(cx, fun, indent);
1179 12524 : if (!str)
1180 0 : return NULL;
1181 :
1182 12524 : if (!indent) {
1183 1991 : Maybe<ToSourceCache> &lazy = cx->compartment->toSourceCache;
1184 :
1185 1991 : if (lazy.empty()) {
1186 1219 : lazy.construct();
1187 1219 : if (!lazy.ref().init())
1188 0 : return NULL;
1189 : }
1190 :
1191 1991 : if (!lazy.ref().put(fun, str))
1192 0 : return NULL;
1193 : }
1194 :
1195 12524 : return str;
1196 : }
1197 :
1198 : static JSBool
1199 259556 : fun_toString(JSContext *cx, unsigned argc, Value *vp)
1200 : {
1201 259556 : JS_ASSERT(IsFunctionObject(vp[0]));
1202 259556 : uint32_t indent = 0;
1203 :
1204 259556 : if (argc != 0 && !ToUint32(cx, vp[2], &indent))
1205 0 : return false;
1206 :
1207 259556 : JSObject *obj = ToObject(cx, &vp[1]);
1208 259556 : if (!obj)
1209 0 : return false;
1210 :
1211 259556 : JSString *str = fun_toStringHelper(cx, obj, indent);
1212 259556 : if (!str)
1213 0 : return false;
1214 :
1215 259556 : vp->setString(str);
1216 259556 : return true;
1217 : }
1218 :
1219 : #if JS_HAS_TOSOURCE
1220 : static JSBool
1221 10533 : fun_toSource(JSContext *cx, unsigned argc, Value *vp)
1222 : {
1223 10533 : JS_ASSERT(IsFunctionObject(vp[0]));
1224 :
1225 10533 : JSObject *obj = ToObject(cx, &vp[1]);
1226 10533 : if (!obj)
1227 0 : return false;
1228 :
1229 10533 : JSString *str = fun_toStringHelper(cx, obj, JS_DONT_PRETTY_PRINT);
1230 10533 : if (!str)
1231 0 : return false;
1232 :
1233 10533 : vp->setString(str);
1234 10533 : return true;
1235 : }
1236 : #endif
1237 :
1238 : JSBool
1239 77702 : js_fun_call(JSContext *cx, unsigned argc, Value *vp)
1240 : {
1241 77702 : Value fval = vp[1];
1242 :
1243 77702 : if (!js_IsCallable(fval)) {
1244 18 : ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &FunctionClass);
1245 18 : return false;
1246 : }
1247 :
1248 77684 : Value *argv = vp + 2;
1249 : Value thisv;
1250 77684 : if (argc == 0) {
1251 403 : thisv.setUndefined();
1252 : } else {
1253 77281 : thisv = argv[0];
1254 :
1255 77281 : argc--;
1256 77281 : argv++;
1257 : }
1258 :
1259 : /* Allocate stack space for fval, obj, and the args. */
1260 155368 : InvokeArgsGuard args;
1261 77684 : if (!cx->stack.pushInvokeArgs(cx, argc, &args))
1262 0 : return JS_FALSE;
1263 :
1264 : /* Push fval, thisv, and the args. */
1265 77684 : args.calleev() = fval;
1266 77684 : args.thisv() = thisv;
1267 77684 : PodCopy(args.array(), argv, argc);
1268 :
1269 77684 : bool ok = Invoke(cx, args);
1270 77684 : *vp = args.rval();
1271 77684 : return ok;
1272 : }
1273 :
1274 : /* ES5 15.3.4.3 */
1275 : JSBool
1276 529009 : js_fun_apply(JSContext *cx, unsigned argc, Value *vp)
1277 : {
1278 : /* Step 1. */
1279 529009 : Value fval = vp[1];
1280 529009 : if (!js_IsCallable(fval)) {
1281 8 : ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &FunctionClass);
1282 8 : return false;
1283 : }
1284 :
1285 : /* Step 2. */
1286 529001 : if (argc < 2 || vp[3].isNullOrUndefined())
1287 8250 : return js_fun_call(cx, (argc > 0) ? 1 : 0, vp);
1288 :
1289 : /* N.B. Changes need to be propagated to stubs::SplatApplyArgs. */
1290 :
1291 : /* Step 3. */
1292 520751 : if (!vp[3].isObject()) {
1293 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS, js_apply_str);
1294 0 : return false;
1295 : }
1296 :
1297 : /*
1298 : * Steps 4-5 (note erratum removing steps originally numbered 5 and 7 in
1299 : * original version of ES5).
1300 : */
1301 520751 : JSObject *aobj = &vp[3].toObject();
1302 : uint32_t length;
1303 520751 : if (!js_GetLengthProperty(cx, aobj, &length))
1304 0 : return false;
1305 :
1306 : /* Step 6. */
1307 520751 : if (length > StackSpace::ARGS_LENGTH_MAX) {
1308 22 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_APPLY_ARGS);
1309 22 : return false;
1310 : }
1311 :
1312 1041458 : InvokeArgsGuard args;
1313 520729 : if (!cx->stack.pushInvokeArgs(cx, length, &args))
1314 0 : return false;
1315 :
1316 : /* Push fval, obj, and aobj's elements as args. */
1317 520729 : args.calleev() = fval;
1318 520729 : args.thisv() = vp[2];
1319 :
1320 : /* Steps 7-8. */
1321 520729 : if (!GetElements(cx, aobj, length, args.array()))
1322 0 : return false;
1323 :
1324 : /* Step 9. */
1325 520729 : if (!Invoke(cx, args))
1326 4290 : return false;
1327 516439 : *vp = args.rval();
1328 516439 : return true;
1329 : }
1330 :
1331 : namespace js {
1332 :
1333 : JSBool
1334 : CallOrConstructBoundFunction(JSContext *cx, unsigned argc, Value *vp);
1335 :
1336 : }
1337 :
1338 : static const uint32_t JSSLOT_BOUND_FUNCTION_THIS = 0;
1339 : static const uint32_t JSSLOT_BOUND_FUNCTION_ARGS_COUNT = 1;
1340 :
1341 : static const uint32_t BOUND_FUNCTION_RESERVED_SLOTS = 2;
1342 :
1343 : inline bool
1344 5488 : JSFunction::initBoundFunction(JSContext *cx, const Value &thisArg,
1345 : const Value *args, unsigned argslen)
1346 : {
1347 5488 : JS_ASSERT(isFunction());
1348 :
1349 : /*
1350 : * Convert to a dictionary to set the BOUND_FUNCTION flag and increase
1351 : * the slot span to cover the arguments and additional slots for the 'this'
1352 : * value and arguments count.
1353 : */
1354 5488 : if (!toDictionaryMode(cx))
1355 0 : return false;
1356 :
1357 5488 : if (!setFlag(cx, BaseShape::BOUND_FUNCTION))
1358 0 : return false;
1359 :
1360 5488 : if (!setSlotSpan(cx, BOUND_FUNCTION_RESERVED_SLOTS + argslen))
1361 0 : return false;
1362 :
1363 5488 : setSlot(JSSLOT_BOUND_FUNCTION_THIS, thisArg);
1364 5488 : setSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT, PrivateUint32Value(argslen));
1365 :
1366 5488 : initSlotRange(BOUND_FUNCTION_RESERVED_SLOTS, args, argslen);
1367 :
1368 5488 : return true;
1369 : }
1370 :
1371 : inline JSObject *
1372 9515 : JSFunction::getBoundFunctionTarget() const
1373 : {
1374 9515 : JS_ASSERT(isFunction());
1375 9515 : JS_ASSERT(isBoundFunction());
1376 :
1377 : /* Bound functions abuse |parent| to store their target function. */
1378 9515 : return getParent();
1379 : }
1380 :
1381 : inline const js::Value &
1382 9515 : JSFunction::getBoundFunctionThis() const
1383 : {
1384 9515 : JS_ASSERT(isFunction());
1385 9515 : JS_ASSERT(isBoundFunction());
1386 :
1387 9515 : return getSlot(JSSLOT_BOUND_FUNCTION_THIS);
1388 : }
1389 :
1390 : inline const js::Value &
1391 802 : JSFunction::getBoundFunctionArgument(unsigned which) const
1392 : {
1393 802 : JS_ASSERT(isFunction());
1394 802 : JS_ASSERT(isBoundFunction());
1395 802 : JS_ASSERT(which < getBoundFunctionArgumentCount());
1396 :
1397 802 : return getSlot(BOUND_FUNCTION_RESERVED_SLOTS + which);
1398 : }
1399 :
1400 : inline size_t
1401 10317 : JSFunction::getBoundFunctionArgumentCount() const
1402 : {
1403 10317 : JS_ASSERT(isFunction());
1404 10317 : JS_ASSERT(isBoundFunction());
1405 :
1406 10317 : return getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
1407 : }
1408 :
1409 : namespace js {
1410 :
1411 : /* ES5 15.3.4.5.1 and 15.3.4.5.2. */
1412 : JSBool
1413 9515 : CallOrConstructBoundFunction(JSContext *cx, unsigned argc, Value *vp)
1414 : {
1415 9515 : JSFunction *fun = vp[0].toObject().toFunction();
1416 9515 : JS_ASSERT(fun->isBoundFunction());
1417 :
1418 9515 : bool constructing = IsConstructing(vp);
1419 :
1420 : /* 15.3.4.5.1 step 1, 15.3.4.5.2 step 3. */
1421 9515 : unsigned argslen = fun->getBoundFunctionArgumentCount();
1422 :
1423 9515 : if (argc + argslen > StackSpace::ARGS_LENGTH_MAX) {
1424 0 : js_ReportAllocationOverflow(cx);
1425 0 : return false;
1426 : }
1427 :
1428 : /* 15.3.4.5.1 step 3, 15.3.4.5.2 step 1. */
1429 9515 : JSObject *target = fun->getBoundFunctionTarget();
1430 :
1431 : /* 15.3.4.5.1 step 2. */
1432 9515 : const Value &boundThis = fun->getBoundFunctionThis();
1433 :
1434 19030 : InvokeArgsGuard args;
1435 9515 : if (!cx->stack.pushInvokeArgs(cx, argc + argslen, &args))
1436 0 : return false;
1437 :
1438 : /* 15.3.4.5.1, 15.3.4.5.2 step 4. */
1439 10317 : for (unsigned i = 0; i < argslen; i++)
1440 802 : args[i] = fun->getBoundFunctionArgument(i);
1441 9515 : PodCopy(args.array() + argslen, vp + 2, argc);
1442 :
1443 : /* 15.3.4.5.1, 15.3.4.5.2 step 5. */
1444 9515 : args.calleev().setObject(*target);
1445 :
1446 9515 : if (!constructing)
1447 9299 : args.thisv() = boundThis;
1448 :
1449 9515 : if (constructing ? !InvokeConstructor(cx, args) : !Invoke(cx, args))
1450 14 : return false;
1451 :
1452 9501 : *vp = args.rval();
1453 9501 : return true;
1454 : }
1455 :
1456 : }
1457 :
1458 : #if JS_HAS_GENERATORS
1459 : static JSBool
1460 0 : fun_isGenerator(JSContext *cx, unsigned argc, Value *vp)
1461 : {
1462 : JSFunction *fun;
1463 0 : if (!IsFunctionObject(vp[1], &fun)) {
1464 0 : JS_SET_RVAL(cx, vp, BooleanValue(false));
1465 0 : return true;
1466 : }
1467 :
1468 0 : bool result = false;
1469 0 : if (fun->isInterpreted()) {
1470 0 : JSScript *script = fun->script();
1471 0 : JS_ASSERT(script->length != 0);
1472 0 : result = script->code[0] == JSOP_GENERATOR;
1473 : }
1474 :
1475 0 : JS_SET_RVAL(cx, vp, BooleanValue(result));
1476 0 : return true;
1477 : }
1478 : #endif
1479 :
1480 : /* ES5 15.3.4.5. */
1481 : static JSBool
1482 5488 : fun_bind(JSContext *cx, unsigned argc, Value *vp)
1483 : {
1484 5488 : CallArgs args = CallArgsFromVp(argc, vp);
1485 :
1486 : /* Step 1. */
1487 5488 : Value &thisv = args.thisv();
1488 :
1489 : /* Step 2. */
1490 5488 : if (!js_IsCallable(thisv)) {
1491 0 : ReportIncompatibleMethod(cx, args, &FunctionClass);
1492 0 : return false;
1493 : }
1494 :
1495 10976 : RootedVarObject target(cx);
1496 5488 : target = &thisv.toObject();
1497 :
1498 : /* Step 3. */
1499 5488 : Value *boundArgs = NULL;
1500 5488 : unsigned argslen = 0;
1501 5488 : if (args.length() > 1) {
1502 2309 : boundArgs = args.array() + 1;
1503 2309 : argslen = args.length() - 1;
1504 : }
1505 :
1506 : /* Steps 15-16. */
1507 5488 : unsigned length = 0;
1508 5488 : if (target->isFunction()) {
1509 5488 : unsigned nargs = target->toFunction()->nargs;
1510 5488 : if (nargs > argslen)
1511 1118 : length = nargs - argslen;
1512 : }
1513 :
1514 : /* Step 4-6, 10-11. */
1515 5488 : JSAtom *name = target->isFunction() ? target->toFunction()->atom : NULL;
1516 :
1517 : JSObject *funobj =
1518 : js_NewFunction(cx, NULL, CallOrConstructBoundFunction, length,
1519 5488 : JSFUN_CONSTRUCTOR, target, name);
1520 5488 : if (!funobj)
1521 0 : return false;
1522 :
1523 : /* NB: Bound functions abuse |parent| to store their target. */
1524 5488 : if (!funobj->setParent(cx, target))
1525 0 : return false;
1526 :
1527 : /* Steps 7-9. */
1528 5488 : Value thisArg = args.length() >= 1 ? args[0] : UndefinedValue();
1529 5488 : if (!funobj->toFunction()->initBoundFunction(cx, thisArg, boundArgs, argslen))
1530 0 : return false;
1531 :
1532 : /* Steps 17, 19-21 are handled by fun_resolve. */
1533 : /* Step 18 is the default for new functions. */
1534 :
1535 : /* Step 22. */
1536 5488 : args.rval().setObject(*funobj);
1537 5488 : return true;
1538 : }
1539 :
1540 : /*
1541 : * Report "malformed formal parameter" iff no illegal char or similar scanner
1542 : * error was already reported.
1543 : */
1544 : static bool
1545 9 : OnBadFormal(JSContext *cx, TokenKind tt)
1546 : {
1547 9 : if (tt != TOK_ERROR)
1548 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_FORMAL);
1549 : else
1550 9 : JS_ASSERT(cx->isExceptionPending());
1551 9 : return false;
1552 : }
1553 :
1554 : namespace js {
1555 :
1556 : JSFunctionSpec function_methods[] = {
1557 : #if JS_HAS_TOSOURCE
1558 : JS_FN(js_toSource_str, fun_toSource, 0,0),
1559 : #endif
1560 : JS_FN(js_toString_str, fun_toString, 0,0),
1561 : JS_FN(js_apply_str, js_fun_apply, 2,0),
1562 : JS_FN(js_call_str, js_fun_call, 1,0),
1563 : JS_FN("bind", fun_bind, 1,0),
1564 : #if JS_HAS_GENERATORS
1565 : JS_FN("isGenerator", fun_isGenerator,0,0),
1566 : #endif
1567 : JS_FS_END
1568 : };
1569 :
1570 : JSBool
1571 10414 : Function(JSContext *cx, unsigned argc, Value *vp)
1572 : {
1573 10414 : CallArgs args = CallArgsFromVp(argc, vp);
1574 :
1575 : /* Block this call if security callbacks forbid it. */
1576 20828 : RootedVar<GlobalObject*> global(cx);
1577 10414 : global = &args.callee().global();
1578 10414 : if (!global->isRuntimeCodeGenEnabled(cx)) {
1579 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_FUNCTION);
1580 0 : return false;
1581 : }
1582 :
1583 20828 : Bindings bindings(cx);
1584 :
1585 : const char *filename;
1586 : unsigned lineno;
1587 : JSPrincipals *originPrincipals;
1588 10414 : CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals);
1589 10414 : JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
1590 :
1591 10414 : unsigned n = args.length() ? args.length() - 1 : 0;
1592 10414 : if (n > 0) {
1593 : /*
1594 : * Collect the function-argument arguments into one string, separated
1595 : * by commas, then make a tokenstream from that string, and scan it to
1596 : * get the arguments. We need to throw the full scanner at the
1597 : * problem, because the argument string can legitimately contain
1598 : * comments and linefeeds. XXX It might be better to concatenate
1599 : * everything up into a function definition and pass it to the
1600 : * compiler, but doing it this way is less of a delta from the old
1601 : * code. See ECMA 15.3.2.1.
1602 : */
1603 4727 : size_t args_length = 0;
1604 9463 : for (unsigned i = 0; i < n; i++) {
1605 : /* Collect the lengths for all the function-argument arguments. */
1606 4736 : JSString *arg = ToString(cx, args[i]);
1607 4736 : if (!arg)
1608 0 : return false;
1609 4736 : args[i].setString(arg);
1610 :
1611 : /*
1612 : * Check for overflow. The < test works because the maximum
1613 : * JSString length fits in 2 fewer bits than size_t has.
1614 : */
1615 4736 : size_t old_args_length = args_length;
1616 4736 : args_length = old_args_length + arg->length();
1617 4736 : if (args_length < old_args_length) {
1618 0 : js_ReportAllocationOverflow(cx);
1619 0 : return false;
1620 : }
1621 : }
1622 :
1623 : /* Add 1 for each joining comma and check for overflow (two ways). */
1624 4727 : size_t old_args_length = args_length;
1625 4727 : args_length = old_args_length + n - 1;
1626 4727 : if (args_length < old_args_length ||
1627 : args_length >= ~(size_t)0 / sizeof(jschar)) {
1628 0 : js_ReportAllocationOverflow(cx);
1629 0 : return false;
1630 : }
1631 :
1632 : /*
1633 : * Allocate a string to hold the concatenated arguments, including room
1634 : * for a terminating 0. Mark cx->tempLifeAlloc for later release, to
1635 : * free collected_args and its tokenstream in one swoop.
1636 : */
1637 9454 : LifoAllocScope las(&cx->tempLifoAlloc());
1638 4727 : jschar *cp = cx->tempLifoAlloc().newArray<jschar>(args_length + 1);
1639 4727 : if (!cp) {
1640 0 : js_ReportOutOfMemory(cx);
1641 0 : return false;
1642 : }
1643 4727 : jschar *collected_args = cp;
1644 :
1645 : /*
1646 : * Concatenate the arguments into the new string, separated by commas.
1647 : */
1648 9463 : for (unsigned i = 0; i < n; i++) {
1649 4736 : JSString *arg = args[i].toString();
1650 4736 : size_t arg_length = arg->length();
1651 4736 : const jschar *arg_chars = arg->getChars(cx);
1652 4736 : if (!arg_chars)
1653 0 : return false;
1654 4736 : (void) js_strncpy(cp, arg_chars, arg_length);
1655 4736 : cp += arg_length;
1656 :
1657 : /* Add separating comma or terminating 0. */
1658 4736 : *cp++ = (i + 1 < n) ? ',' : 0;
1659 : }
1660 :
1661 : /* Initialize a tokenstream that reads from the given string. */
1662 9454 : TokenStream ts(cx, principals, originPrincipals);
1663 4727 : if (!ts.init(collected_args, args_length, filename, lineno, cx->findVersion()))
1664 0 : return false;
1665 :
1666 : /* The argument string may be empty or contain no tokens. */
1667 4727 : TokenKind tt = ts.getToken();
1668 4727 : if (tt != TOK_EOF) {
1669 18 : for (;;) {
1670 : /*
1671 : * Check that it's a name. This also implicitly guards against
1672 : * TOK_ERROR, which was already reported.
1673 : */
1674 4745 : if (tt != TOK_NAME)
1675 9 : return OnBadFormal(cx, tt);
1676 :
1677 : /* Check for a duplicate parameter name. */
1678 4736 : PropertyName *name = ts.currentToken().name();
1679 4736 : if (bindings.hasBinding(cx, name)) {
1680 0 : JSAutoByteString bytes;
1681 0 : if (!js_AtomToPrintableString(cx, name, &bytes))
1682 0 : return false;
1683 0 : if (!ReportCompileErrorNumber(cx, &ts, NULL,
1684 : JSREPORT_WARNING | JSREPORT_STRICT,
1685 0 : JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
1686 : {
1687 0 : return false;
1688 : }
1689 : }
1690 :
1691 : uint16_t dummy;
1692 4736 : if (!bindings.addArgument(cx, name, &dummy))
1693 0 : return false;
1694 :
1695 : /*
1696 : * Get the next token. Stop on end of stream. Otherwise
1697 : * insist on a comma, get another name, and iterate.
1698 : */
1699 4736 : tt = ts.getToken();
1700 4736 : if (tt == TOK_EOF)
1701 : break;
1702 18 : if (tt != TOK_COMMA)
1703 0 : return OnBadFormal(cx, tt);
1704 18 : tt = ts.getToken();
1705 : }
1706 : }
1707 : }
1708 :
1709 20810 : JS::Anchor<JSString *> strAnchor(NULL);
1710 : const jschar *chars;
1711 : size_t length;
1712 :
1713 10405 : if (args.length()) {
1714 10198 : JSString *str = ToString(cx, args[args.length() - 1]);
1715 10198 : if (!str)
1716 0 : return false;
1717 10198 : strAnchor.set(str);
1718 10198 : chars = str->getChars(cx);
1719 10198 : length = str->length();
1720 : } else {
1721 207 : chars = cx->runtime->emptyString->chars();
1722 207 : length = 0;
1723 : }
1724 :
1725 : /*
1726 : * NB: (new Function) is not lexically closed by its caller, it's just an
1727 : * anonymous function in the top-level scope that its constructor inhabits.
1728 : * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
1729 : * and so would a call to f from another top-level's script or function.
1730 : */
1731 : JSFunction *fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED,
1732 10405 : global, cx->runtime->atomState.anonymousAtom);
1733 10405 : if (!fun)
1734 0 : return false;
1735 :
1736 : bool ok = frontend::CompileFunctionBody(cx, fun, principals, originPrincipals,
1737 : &bindings, chars, length, filename, lineno,
1738 10405 : cx->findVersion());
1739 10405 : args.rval().setObject(*fun);
1740 10405 : return ok;
1741 : }
1742 :
1743 : bool
1744 10454 : IsBuiltinFunctionConstructor(JSFunction *fun)
1745 : {
1746 10454 : return fun->maybeNative() == Function;
1747 : }
1748 :
1749 : const Shape *
1750 0 : LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj)
1751 : {
1752 : #ifdef DEBUG
1753 0 : JSFunction *fun = funobj->toFunction();
1754 0 : JS_ASSERT(fun->isInterpreted());
1755 0 : JS_ASSERT(!fun->isFunctionPrototype());
1756 0 : JS_ASSERT(!funobj->isBoundFunction());
1757 : #endif
1758 :
1759 0 : jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
1760 0 : const Shape *shape = funobj->nativeLookup(cx, id);
1761 0 : if (!shape) {
1762 0 : if (!ResolveInterpretedFunctionPrototype(cx, funobj))
1763 0 : return NULL;
1764 0 : shape = funobj->nativeLookup(cx, id);
1765 : }
1766 0 : JS_ASSERT(!shape->configurable());
1767 0 : JS_ASSERT(shape->isDataDescriptor());
1768 0 : JS_ASSERT(shape->hasSlot());
1769 0 : JS_ASSERT(!shape->isMethod());
1770 0 : return shape;
1771 : }
1772 :
1773 : } /* namespace js */
1774 :
1775 : JSFunction *
1776 11495510 : js_NewFunction(JSContext *cx, JSObject *funobj, Native native, unsigned nargs,
1777 : unsigned flags, HandleObject parent, JSAtom *atom, js::gc::AllocKind kind)
1778 : {
1779 11495510 : JS_ASSERT(kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
1780 11495510 : JS_ASSERT(sizeof(JSFunction) <= gc::Arena::thingSize(JSFunction::FinalizeKind));
1781 11495510 : JS_ASSERT(sizeof(FunctionExtended) <= gc::Arena::thingSize(JSFunction::ExtendedFinalizeKind));
1782 :
1783 : JSFunction *fun;
1784 :
1785 11495510 : if (funobj) {
1786 115911 : JS_ASSERT(funobj->isFunction());
1787 115911 : JS_ASSERT(funobj->getParent() == parent);
1788 : } else {
1789 11379599 : funobj = NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent), kind);
1790 11379599 : if (!funobj)
1791 0 : return NULL;
1792 : }
1793 11495510 : fun = static_cast<JSFunction *>(funobj);
1794 :
1795 : /* Initialize all function members. */
1796 11495510 : fun->nargs = uint16_t(nargs);
1797 11495510 : fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK);
1798 11495510 : if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) {
1799 1204089 : JS_ASSERT(!native);
1800 1204089 : fun->script().init(NULL);
1801 1204089 : fun->initEnvironment(parent);
1802 : } else {
1803 10291421 : fun->u.n.clasp = NULL;
1804 10291421 : fun->u.n.native = native;
1805 10291421 : JS_ASSERT(fun->u.n.native);
1806 : }
1807 11495510 : if (kind == JSFunction::ExtendedFinalizeKind) {
1808 2696899 : fun->flags |= JSFUN_EXTENDED;
1809 2696899 : fun->initializeExtended();
1810 : }
1811 11495510 : fun->atom = atom;
1812 :
1813 11495510 : if (native && !fun->setSingletonType(cx))
1814 0 : return NULL;
1815 :
1816 11495510 : return fun;
1817 : }
1818 :
1819 : JSFunction * JS_FASTCALL
1820 2380496 : js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
1821 : JSObject *proto, gc::AllocKind kind)
1822 : {
1823 2380496 : JS_ASSERT(parent);
1824 2380496 : JS_ASSERT(proto);
1825 :
1826 2380496 : JSObject *cloneobj = NewObjectWithClassProto(cx, &FunctionClass, NULL, SkipScopeParent(parent), kind);
1827 2380496 : if (!cloneobj)
1828 0 : return NULL;
1829 2380496 : JSFunction *clone = static_cast<JSFunction *>(cloneobj);
1830 :
1831 2380496 : clone->nargs = fun->nargs;
1832 2380496 : clone->flags = fun->flags & ~JSFUN_EXTENDED;
1833 2380496 : if (fun->isInterpreted()) {
1834 2380496 : clone->initScript(fun->script());
1835 2380496 : clone->initEnvironment(parent);
1836 : } else {
1837 0 : clone->u.n = fun->u.n;
1838 : }
1839 2380496 : clone->atom = fun->atom;
1840 :
1841 2380496 : if (kind == JSFunction::ExtendedFinalizeKind) {
1842 572345 : clone->flags |= JSFUN_EXTENDED;
1843 572345 : clone->initializeExtended();
1844 : }
1845 :
1846 2380496 : if (cx->compartment == fun->compartment()) {
1847 : /*
1848 : * We can use the same type as the original function provided that (a)
1849 : * its prototype is correct, and (b) its type is not a singleton. The
1850 : * first case will hold in all compileAndGo code, and the second case
1851 : * will have been caught by CloneFunctionObject coming from function
1852 : * definitions or read barriers, so will not get here.
1853 : */
1854 2378217 : if (fun->getProto() == proto && !fun->hasSingletonType())
1855 964555 : clone->setType(fun->type());
1856 : } else {
1857 : /*
1858 : * Across compartments we have to clone the script for interpreted
1859 : * functions.
1860 : */
1861 2279 : if (clone->isInterpreted()) {
1862 2279 : JSScript *script = clone->script();
1863 2279 : JS_ASSERT(script);
1864 2279 : JS_ASSERT(script->compartment() == fun->compartment());
1865 2279 : JS_ASSERT(script->compartment() != cx->compartment);
1866 :
1867 2279 : clone->script().init(NULL);
1868 2279 : JSScript *cscript = CloneScript(cx, script);
1869 2279 : if (!cscript)
1870 0 : return NULL;
1871 :
1872 2279 : cscript->globalObject = &clone->global();
1873 2279 : clone->setScript(cscript);
1874 2279 : if (!cscript->typeSetFunction(cx, clone))
1875 0 : return NULL;
1876 :
1877 2279 : js_CallNewScriptHook(cx, clone->script(), clone);
1878 2279 : Debugger::onNewScript(cx, clone->script(), NULL);
1879 : }
1880 : }
1881 2380496 : return clone;
1882 : }
1883 :
1884 : /*
1885 : * Create a new flat closure, but don't initialize the imported upvar
1886 : * values. The tracer calls this function and then initializes the upvar
1887 : * slots on trace.
1888 : */
1889 : JSFunction * JS_FASTCALL
1890 572345 : js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
1891 : {
1892 572345 : JS_ASSERT(fun->isFlatClosure());
1893 572345 : JS_ASSERT(JSScript::isValidOffset(fun->script()->upvarsOffset) ==
1894 572345 : fun->script()->bindings.hasUpvars());
1895 1716311 : JS_ASSERT_IF(JSScript::isValidOffset(fun->script()->upvarsOffset),
1896 1716311 : fun->script()->upvars()->length == fun->script()->bindings.countUpvars());
1897 :
1898 572345 : JSFunction *closure = CloneFunctionObject(cx, fun, scopeChain, JSFunction::ExtendedFinalizeKind);
1899 572345 : if (!closure)
1900 0 : return closure;
1901 :
1902 572345 : uint32_t nslots = fun->script()->bindings.countUpvars();
1903 572345 : if (nslots == 0)
1904 362 : return closure;
1905 :
1906 571983 : HeapValue *data = (HeapValue *) cx->malloc_(nslots * sizeof(HeapValue));
1907 571983 : if (!data)
1908 0 : return NULL;
1909 :
1910 571983 : closure->setExtendedSlot(JSFunction::FLAT_CLOSURE_UPVARS_SLOT, PrivateValue(data));
1911 571983 : return closure;
1912 : }
1913 :
1914 : JSFunction *
1915 572345 : js_NewFlatClosure(JSContext *cx, JSFunction *fun)
1916 : {
1917 : /*
1918 : * Flat closures cannot yet be partial, that is, all upvars must be copied,
1919 : * or the closure won't be flattened. Therefore they do not need to search
1920 : * enclosing scope objects via JSOP_NAME, etc.
1921 : */
1922 572345 : JSObject *scopeChain = &cx->fp()->scopeChain();
1923 :
1924 572345 : JSFunction *closure = js_AllocFlatClosure(cx, fun, scopeChain);
1925 572345 : if (!closure || !fun->script()->bindings.hasUpvars())
1926 362 : return closure;
1927 :
1928 571983 : unsigned level = fun->script()->staticLevel;
1929 571983 : JSUpvarArray *uva = fun->script()->upvars();
1930 :
1931 1315975 : for (uint32_t i = 0, n = uva->length; i < n; i++)
1932 743992 : closure->initFlatClosureUpvar(i, GetUpvar(cx, level, uva->vector[i]));
1933 :
1934 571983 : return closure;
1935 : }
1936 :
1937 : JSFunction *
1938 7225446 : js_DefineFunction(JSContext *cx, HandleObject obj, jsid id, Native native,
1939 : unsigned nargs, unsigned attrs, AllocKind kind)
1940 : {
1941 14450892 : RootId idRoot(cx, &id);
1942 :
1943 : PropertyOp gop;
1944 : StrictPropertyOp sop;
1945 :
1946 14450892 : RootedVarFunction fun(cx);
1947 :
1948 7225446 : if (attrs & JSFUN_STUB_GSOPS) {
1949 : /*
1950 : * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
1951 : * the defined property's attributes. This allows us to encode another,
1952 : * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
1953 : * for more on this.
1954 : */
1955 6956876 : attrs &= ~JSFUN_STUB_GSOPS;
1956 6956876 : gop = JS_PropertyStub;
1957 6956876 : sop = JS_StrictPropertyStub;
1958 : } else {
1959 268570 : gop = NULL;
1960 268570 : sop = NULL;
1961 : }
1962 :
1963 : fun = js_NewFunction(cx, NULL, native, nargs,
1964 : attrs & (JSFUN_FLAGS_MASK),
1965 : obj,
1966 7225446 : JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL,
1967 7225446 : kind);
1968 7225446 : if (!fun)
1969 0 : return NULL;
1970 :
1971 7225446 : if (!obj->defineGeneric(cx, id, ObjectValue(*fun), gop, sop, attrs & ~JSFUN_FLAGS_MASK))
1972 0 : return NULL;
1973 :
1974 7225446 : return fun;
1975 : }
1976 :
1977 : JS_STATIC_ASSERT((JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK) == 0);
1978 :
1979 : JSFunction *
1980 6436 : js_ValueToFunction(JSContext *cx, const Value *vp, unsigned flags)
1981 : {
1982 : JSFunction *fun;
1983 6436 : if (!IsFunctionObject(*vp, &fun)) {
1984 1809 : js_ReportIsNotFunction(cx, vp, flags);
1985 1809 : return NULL;
1986 : }
1987 4627 : return fun;
1988 : }
1989 :
1990 : JSObject *
1991 159871 : js_ValueToCallableObject(JSContext *cx, Value *vp, unsigned flags)
1992 : {
1993 159871 : if (vp->isObject()) {
1994 159871 : JSObject *callable = &vp->toObject();
1995 159871 : if (callable->isCallable())
1996 159862 : return callable;
1997 : }
1998 :
1999 9 : js_ReportIsNotFunction(cx, vp, flags);
2000 9 : return NULL;
2001 : }
2002 :
2003 : void
2004 2992 : js_ReportIsNotFunction(JSContext *cx, const Value *vp, unsigned flags)
2005 : {
2006 2992 : const char *name = NULL, *source = NULL;
2007 5984 : AutoValueRooter tvr(cx);
2008 2992 : unsigned error = (flags & JSV2F_CONSTRUCT) ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION;
2009 :
2010 : /*
2011 : * We try to the print the code that produced vp if vp is a value in the
2012 : * most recent interpreted stack frame. Note that additional values, not
2013 : * directly produced by the script, may have been pushed onto the frame's
2014 : * expression stack (e.g. by pushInvokeArgs) thereby incrementing sp past
2015 : * the depth simulated by ReconstructPCStack.
2016 : *
2017 : * Conversely, values may have been popped from the stack in preparation
2018 : * for a call (e.g., by SplatApplyArgs). Since we must pass an offset from
2019 : * the top of the simulated stack to js_ReportValueError3, we do bounds
2020 : * checking using the minimum of both the simulated and actual stack depth.
2021 : */
2022 2992 : ptrdiff_t spindex = 0;
2023 :
2024 2992 : FrameRegsIter i(cx);
2025 2992 : if (!i.done()) {
2026 2992 : unsigned depth = js_ReconstructStackDepth(cx, i.fp()->script(), i.pc());
2027 2992 : Value *simsp = i.fp()->base() + depth;
2028 2992 : if (i.fp()->base() <= vp && vp < Min(simsp, i.sp()))
2029 1156 : spindex = vp - simsp;
2030 : }
2031 :
2032 2992 : if (!spindex)
2033 1836 : spindex = ((flags & JSV2F_SEARCH_STACK) ? JSDVG_SEARCH_STACK : JSDVG_IGNORE_STACK);
2034 :
2035 2992 : js_ReportValueError3(cx, error, spindex, *vp, NULL, name, source);
2036 2992 : }
|