1 : /* -*- Mode: C++; tab-width: 6; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=78:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is SpiderMonkey call object code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * the Mozilla Foundation.
21 : * Portions created by the Initial Developer are Copyright (C) 2011
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Paul Biggar <pbiggar@mozilla.com> (original author)
26 : * Luke Wagner <luke@mozilla.com>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either of the GNU General Public License Version 2 or later (the "GPL"),
30 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include "jscompartment.h"
43 : #include "jsiter.h"
44 : #include "jsscope.h"
45 :
46 : #include "GlobalObject.h"
47 : #include "ScopeObject.h"
48 : #include "Xdr.h"
49 :
50 : #include "jsatominlines.h"
51 : #include "jsobjinlines.h"
52 :
53 : #include "ScopeObject-inl.h"
54 :
55 : using namespace js;
56 : using namespace js::types;
57 :
58 : void
59 744600 : js_PutCallObject(StackFrame *fp)
60 : {
61 744600 : CallObject &callobj = fp->callObj().asCall();
62 744600 : JS_ASSERT(callobj.maybeStackFrame() == fp);
63 744600 : JS_ASSERT_IF(fp->isEvalFrame(), fp->isStrictEvalFrame());
64 744600 : JS_ASSERT(fp->isEvalFrame() == callobj.isForEval());
65 :
66 744600 : JSScript *script = fp->script();
67 744600 : Bindings &bindings = script->bindings;
68 :
69 744600 : if (callobj.isForEval()) {
70 1908 : JS_ASSERT(script->strictModeCode);
71 1908 : JS_ASSERT(bindings.countArgs() == 0);
72 :
73 : /* This could be optimized as below, but keep it simple for now. */
74 1908 : callobj.copyValues(0, NULL, bindings.countVars(), fp->slots());
75 : } else {
76 742692 : JSFunction *fun = fp->fun();
77 742692 : JS_ASSERT(script == callobj.getCalleeFunction()->script());
78 742692 : JS_ASSERT(script == fun->script());
79 :
80 742692 : unsigned n = bindings.countLocalNames();
81 742692 : if (n > 0) {
82 505949 : uint32_t nvars = bindings.countVars();
83 505949 : uint32_t nargs = bindings.countArgs();
84 505949 : JS_ASSERT(fun->nargs == nargs);
85 505949 : JS_ASSERT(nvars + nargs == n);
86 :
87 505949 : JSScript *script = fun->script();
88 505949 : if (script->usesEval
89 : #ifdef JS_METHODJIT
90 : || script->debugMode
91 : #endif
92 : ) {
93 185371 : callobj.copyValues(nargs, fp->formalArgs(), nvars, fp->slots());
94 : } else {
95 : /*
96 : * For each arg & var that is closed over, copy it from the stack
97 : * into the call object. We use initArg/VarUnchecked because,
98 : * when you call a getter on a call object, js_NativeGetInline
99 : * caches the return value in the slot, so we can't assert that
100 : * it's undefined.
101 : */
102 320578 : uint32_t nclosed = script->nClosedArgs();
103 490613 : for (uint32_t i = 0; i < nclosed; i++) {
104 170035 : uint32_t e = script->getClosedArg(i);
105 : #ifdef JS_GC_ZEAL
106 170035 : callobj.setArg(e, fp->formalArg(e));
107 : #else
108 : callobj.initArgUnchecked(e, fp->formalArg(e));
109 : #endif
110 : }
111 :
112 320578 : nclosed = script->nClosedVars();
113 645717 : for (uint32_t i = 0; i < nclosed; i++) {
114 325139 : uint32_t e = script->getClosedVar(i);
115 : #ifdef JS_GC_ZEAL
116 325139 : callobj.setVar(e, fp->slots()[e]);
117 : #else
118 : callobj.initVarUnchecked(e, fp->slots()[e]);
119 : #endif
120 : }
121 : }
122 :
123 : /*
124 : * Update the args and vars for the active call if this is an outer
125 : * function in a script nesting.
126 : */
127 505949 : types::TypeScriptNesting *nesting = script->nesting();
128 505949 : if (nesting && script->isOuterFunction) {
129 276879 : nesting->argArray = callobj.argArray();
130 276879 : nesting->varArray = callobj.varArray();
131 : }
132 : }
133 :
134 : /* Clear private pointers to fp, which is about to go away. */
135 742692 : if (js_IsNamedLambda(fun)) {
136 7759 : JSObject &env = callobj.enclosingScope();
137 7759 : JS_ASSERT(env.asDeclEnv().maybeStackFrame() == fp);
138 7759 : env.setPrivate(NULL);
139 : }
140 : }
141 :
142 744600 : callobj.setStackFrame(NULL);
143 744600 : }
144 :
145 : /*
146 : * Construct a call object for the given bindings. If this is a call object
147 : * for a function invocation, callee should be the function being called.
148 : * Otherwise it must be a call object for eval of strict mode code, and callee
149 : * must be null.
150 : */
151 : CallObject *
152 745077 : CallObject::create(JSContext *cx, JSScript *script, JSObject &enclosing, JSObject *callee)
153 : {
154 1490154 : RootedVarShape shape(cx);
155 745077 : shape = script->bindings.callObjectShape(cx);
156 745077 : if (shape == NULL)
157 0 : return NULL;
158 :
159 745077 : gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots() + 1);
160 :
161 1490154 : RootedVarTypeObject type(cx);
162 :
163 745077 : type = cx->compartment->getEmptyType(cx);
164 745077 : if (!type)
165 0 : return NULL;
166 :
167 : HeapSlot *slots;
168 745077 : if (!PreallocateObjectDynamicSlots(cx, shape, &slots))
169 0 : return NULL;
170 :
171 745077 : JSObject *obj = JSObject::create(cx, kind, shape, type, slots);
172 745077 : if (!obj)
173 0 : return NULL;
174 :
175 : /*
176 : * Update the parent for bindings associated with non-compileAndGo scripts,
177 : * whose call objects do not have a consistent global variable and need
178 : * to be updated dynamically.
179 : */
180 745077 : JSObject &global = enclosing.global();
181 745077 : if (&global != obj->getParent()) {
182 1588 : JS_ASSERT(obj->getParent() == NULL);
183 1588 : if (!obj->setParent(cx, &global))
184 0 : return NULL;
185 : }
186 :
187 : #ifdef DEBUG
188 745077 : JS_ASSERT(!obj->inDictionaryMode());
189 745077 : for (Shape::Range r = obj->lastProperty(); !r.empty(); r.popFront()) {
190 505994 : const Shape &s = r.front();
191 505994 : if (s.hasSlot()) {
192 505994 : JS_ASSERT(s.slot() + 1 == obj->slotSpan());
193 505994 : break;
194 : }
195 : }
196 : #endif
197 :
198 745077 : if (!obj->asScope().setEnclosingScope(cx, enclosing))
199 0 : return NULL;
200 :
201 745077 : JS_ASSERT_IF(callee, callee->isFunction());
202 745077 : obj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee));
203 745077 : obj->initFixedSlot(ARGUMENTS_SLOT, MagicValue(JS_UNASSIGNED_ARGUMENTS));
204 :
205 : /*
206 : * If |bindings| is for a function that has extensible parents, that means
207 : * its Call should have its own shape; see BaseShape::extensibleParents.
208 : */
209 745077 : if (obj->lastProperty()->extensibleParents() && !obj->generateOwnShape(cx))
210 0 : return NULL;
211 :
212 745077 : return &obj->asCall();
213 : }
214 :
215 : CallObject *
216 743169 : CallObject::createForFunction(JSContext *cx, StackFrame *fp)
217 : {
218 743169 : JS_ASSERT(fp->isNonEvalFunctionFrame());
219 743169 : JS_ASSERT(!fp->hasCallObj());
220 :
221 743169 : JSObject *scopeChain = &fp->scopeChain();
222 2312759 : JS_ASSERT_IF(scopeChain->isWith() || scopeChain->isBlock() || scopeChain->isCall(),
223 2312759 : scopeChain->getPrivate() != fp);
224 :
225 : /*
226 : * For a named function expression Call's parent points to an environment
227 : * object holding function's name.
228 : */
229 743169 : if (JSAtom *lambdaName = CallObjectLambdaName(fp->fun())) {
230 8119 : scopeChain = DeclEnvObject::create(cx, fp);
231 8119 : if (!scopeChain)
232 0 : return NULL;
233 :
234 8119 : if (!DefineNativeProperty(cx, scopeChain, ATOM_TO_JSID(lambdaName),
235 8119 : ObjectValue(fp->callee()), NULL, NULL,
236 8119 : JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
237 0 : return NULL;
238 : }
239 : }
240 :
241 743169 : CallObject *callobj = create(cx, fp->script(), *scopeChain, &fp->callee());
242 743169 : if (!callobj)
243 0 : return NULL;
244 :
245 743169 : callobj->setStackFrame(fp);
246 743169 : fp->setScopeChainWithOwnCallObj(*callobj);
247 743169 : if (fp->hasArgsObj())
248 31208 : callobj->setArguments(ObjectValue(fp->argsObj()));
249 743169 : return callobj;
250 : }
251 :
252 : CallObject *
253 1908 : CallObject::createForStrictEval(JSContext *cx, StackFrame *fp)
254 : {
255 1908 : CallObject *callobj = create(cx, fp->script(), fp->scopeChain(), NULL);
256 1908 : if (!callobj)
257 0 : return NULL;
258 :
259 1908 : callobj->setStackFrame(fp);
260 1908 : fp->setScopeChainWithOwnCallObj(*callobj);
261 1908 : return callobj;
262 : }
263 :
264 : JSBool
265 999 : CallObject::getArgumentsOp(JSContext *cx, JSObject *obj, jsid id, Value *vp)
266 : {
267 999 : *vp = obj->asCall().arguments();
268 :
269 : /*
270 : * This can only happen through eval-in-frame. Eventually, this logic can
271 : * be hoisted into debugger scope wrappers. That will allow 'arguments' to
272 : * be a pure data property and allow call_resolve to be removed.
273 : */
274 999 : if (vp->isMagic(JS_UNASSIGNED_ARGUMENTS)) {
275 27 : StackFrame *fp = obj->asCall().maybeStackFrame();
276 27 : ArgumentsObject *argsObj = ArgumentsObject::createUnexpected(cx, fp);
277 27 : if (!argsObj)
278 0 : return false;
279 :
280 27 : *vp = ObjectValue(*argsObj);
281 27 : obj->asCall().setArguments(*vp);
282 : }
283 :
284 999 : return true;
285 : }
286 :
287 : JSBool
288 72 : CallObject::setArgumentsOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
289 : {
290 72 : JS_ASSERT(obj->asCall().maybeStackFrame());
291 72 : obj->asCall().setArguments(*vp);
292 72 : return true;
293 : }
294 :
295 : JSBool
296 230198 : CallObject::getArgOp(JSContext *cx, JSObject *obj, jsid id, Value *vp)
297 : {
298 230198 : CallObject &callobj = obj->asCall();
299 230198 : JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
300 230198 : unsigned i = (uint16_t) JSID_TO_INT(id);
301 :
302 230198 : if (StackFrame *fp = callobj.maybeStackFrame())
303 226613 : *vp = fp->formalArg(i);
304 : else
305 3585 : *vp = callobj.arg(i);
306 230198 : return true;
307 : }
308 :
309 : JSBool
310 854 : CallObject::setArgOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
311 : {
312 854 : CallObject &callobj = obj->asCall();
313 854 : JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
314 854 : unsigned i = (uint16_t) JSID_TO_INT(id);
315 :
316 854 : if (StackFrame *fp = callobj.maybeStackFrame())
317 665 : fp->formalArg(i) = *vp;
318 : else
319 189 : callobj.setArg(i, *vp);
320 :
321 854 : JSFunction *fun = callobj.getCalleeFunction();
322 854 : JSScript *script = fun->script();
323 854 : if (!script->ensureHasTypes(cx))
324 0 : return false;
325 :
326 854 : TypeScript::SetArgument(cx, script, i, *vp);
327 :
328 854 : return true;
329 : }
330 :
331 : JSBool
332 595413 : CallObject::getVarOp(JSContext *cx, JSObject *obj, jsid id, Value *vp)
333 : {
334 595413 : CallObject &callobj = obj->asCall();
335 595413 : JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
336 595413 : unsigned i = (uint16_t) JSID_TO_INT(id);
337 :
338 595413 : if (StackFrame *fp = callobj.maybeStackFrame())
339 501098 : *vp = fp->varSlot(i);
340 : else
341 94315 : *vp = callobj.var(i);
342 595413 : return true;
343 : }
344 :
345 : JSBool
346 18559 : CallObject::setVarOp(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
347 : {
348 18559 : CallObject &callobj = obj->asCall();
349 :
350 18559 : JS_ASSERT((int16_t) JSID_TO_INT(id) == JSID_TO_INT(id));
351 18559 : unsigned i = (uint16_t) JSID_TO_INT(id);
352 :
353 18559 : if (StackFrame *fp = callobj.maybeStackFrame())
354 17128 : fp->varSlot(i) = *vp;
355 : else
356 1431 : callobj.setVar(i, *vp);
357 :
358 18559 : JSFunction *fun = callobj.getCalleeFunction();
359 18559 : JSScript *script = fun->script();
360 18559 : if (!script->ensureHasTypes(cx))
361 0 : return false;
362 :
363 18559 : TypeScript::SetLocal(cx, script, i, *vp);
364 :
365 18559 : return true;
366 : }
367 :
368 : bool
369 387 : CallObject::containsVarOrArg(PropertyName *name, Value *vp, JSContext *cx)
370 : {
371 387 : jsid id = ATOM_TO_JSID(name);
372 387 : const Shape *shape = nativeLookup(cx, id);
373 387 : if (!shape)
374 0 : return false;
375 :
376 387 : PropertyOp op = shape->getterOp();
377 387 : if (op != getVarOp && op != getArgOp)
378 0 : return false;
379 :
380 387 : JS_ALWAYS_TRUE(op(cx, this, INT_TO_JSID(shape->shortid()), vp));
381 387 : return true;
382 : }
383 :
384 : static JSBool
385 486658 : call_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags, JSObject **objp)
386 : {
387 486658 : JS_ASSERT(!obj->getProto());
388 :
389 486658 : if (!JSID_IS_ATOM(id))
390 144 : return true;
391 :
392 486514 : JSObject *callee = obj->asCall().getCallee();
393 : #ifdef DEBUG
394 486514 : if (callee) {
395 484795 : JSScript *script = callee->toFunction()->script();
396 484795 : JS_ASSERT(!script->bindings.hasBinding(cx, JSID_TO_ATOM(id)));
397 : }
398 : #endif
399 :
400 : /*
401 : * Resolve arguments so that we never store a particular Call object's
402 : * arguments object reference in a Call prototype's |arguments| slot.
403 : *
404 : * Include JSPROP_ENUMERATE for consistency with all other Call object
405 : * properties; see js::Bindings::add and js::Interpret's JSOP_DEFFUN
406 : * rebinding-Call-property logic.
407 : */
408 486514 : if (callee && id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) {
409 585 : if (!DefineNativeProperty(cx, obj, id, UndefinedValue(),
410 : CallObject::getArgumentsOp, CallObject::setArgumentsOp,
411 : JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE,
412 585 : 0, 0, DNP_DONT_PURGE)) {
413 0 : return false;
414 : }
415 585 : *objp = obj;
416 585 : return true;
417 : }
418 :
419 : /* Control flow reaches here only if id was not resolved. */
420 485929 : return true;
421 : }
422 :
423 : static void
424 713670 : call_trace(JSTracer *trc, JSObject *obj)
425 : {
426 713670 : JS_ASSERT(obj->isCall());
427 :
428 : /* Mark any generator frame, as for arguments objects. */
429 : #if JS_HAS_GENERATORS
430 713670 : StackFrame *fp = (StackFrame *) obj->getPrivate();
431 713670 : if (fp && fp->isFloatingGenerator())
432 63 : MarkObject(trc, &js_FloatingFrameToGenerator(fp)->obj, "generator object");
433 : #endif
434 713670 : }
435 :
436 : JS_PUBLIC_DATA(Class) js::CallClass = {
437 : "Call",
438 : JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
439 : JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS) |
440 : JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS,
441 : JS_PropertyStub, /* addProperty */
442 : JS_PropertyStub, /* delProperty */
443 : JS_PropertyStub, /* getProperty */
444 : JS_StrictPropertyStub, /* setProperty */
445 : JS_EnumerateStub,
446 : (JSResolveOp)call_resolve,
447 : NULL, /* convert: Leave it NULL so we notice if calls ever escape */
448 : NULL, /* finalize */
449 : NULL, /* checkAccess */
450 : NULL, /* call */
451 : NULL, /* construct */
452 : NULL, /* hasInstance */
453 : call_trace
454 : };
455 :
456 : Class js::DeclEnvClass = {
457 : js_Object_str,
458 : JSCLASS_HAS_PRIVATE |
459 : JSCLASS_HAS_RESERVED_SLOTS(DeclEnvObject::RESERVED_SLOTS) |
460 : JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
461 : JS_PropertyStub, /* addProperty */
462 : JS_PropertyStub, /* delProperty */
463 : JS_PropertyStub, /* getProperty */
464 : JS_StrictPropertyStub, /* setProperty */
465 : JS_EnumerateStub,
466 : JS_ResolveStub,
467 : JS_ConvertStub
468 : };
469 :
470 : DeclEnvObject *
471 8119 : DeclEnvObject::create(JSContext *cx, StackFrame *fp)
472 : {
473 16238 : RootedVarTypeObject type(cx);
474 8119 : type = cx->compartment->getEmptyType(cx);
475 8119 : if (!type)
476 0 : return NULL;
477 :
478 16238 : RootedVarShape emptyDeclEnvShape(cx);
479 : emptyDeclEnvShape = EmptyShape::getInitialShape(cx, &DeclEnvClass, NULL,
480 8119 : &fp->scopeChain().global(),
481 8119 : FINALIZE_KIND);
482 8119 : if (!emptyDeclEnvShape)
483 0 : return NULL;
484 :
485 8119 : JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyDeclEnvShape, type, NULL);
486 8119 : if (!obj)
487 0 : return NULL;
488 :
489 8119 : obj->setPrivate(fp);
490 8119 : if (!obj->asScope().setEnclosingScope(cx, fp->scopeChain()))
491 0 : return NULL;
492 :
493 8119 : return &obj->asDeclEnv();
494 : }
495 :
496 : WithObject *
497 1827 : WithObject::create(JSContext *cx, StackFrame *fp, JSObject &proto, JSObject &enclosing,
498 : uint32_t depth)
499 : {
500 3654 : RootedVarTypeObject type(cx);
501 1827 : type = proto.getNewType(cx);
502 1827 : if (!type)
503 0 : return NULL;
504 :
505 3654 : RootedVarShape emptyWithShape(cx);
506 : emptyWithShape = EmptyShape::getInitialShape(cx, &WithClass, &proto,
507 1827 : &enclosing.global(), FINALIZE_KIND);
508 1827 : if (!emptyWithShape)
509 0 : return NULL;
510 :
511 1827 : JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyWithShape, type, NULL);
512 1827 : if (!obj)
513 0 : return NULL;
514 :
515 1827 : if (!obj->asScope().setEnclosingScope(cx, enclosing))
516 0 : return NULL;
517 :
518 1827 : obj->setReservedSlot(DEPTH_SLOT, PrivateUint32Value(depth));
519 1827 : obj->setPrivate(js_FloatingFrameIfGenerator(cx, fp));
520 :
521 1827 : JSObject *thisp = proto.thisObject(cx);
522 1827 : if (!thisp)
523 0 : return NULL;
524 :
525 1827 : obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp));
526 :
527 1827 : return &obj->asWith();
528 : }
529 :
530 : static JSBool
531 3249 : with_LookupGeneric(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, JSProperty **propp)
532 : {
533 : /* Fixes bug 463997 */
534 3249 : unsigned flags = cx->resolveFlags;
535 3249 : if (flags == RESOLVE_INFER)
536 3249 : flags = js_InferFlags(cx, flags);
537 3249 : flags |= JSRESOLVE_WITH;
538 6498 : JSAutoResolveFlags rf(cx, flags);
539 3249 : return obj->asWith().object().lookupGeneric(cx, id, objp, propp);
540 : }
541 :
542 : static JSBool
543 0 : with_LookupProperty(JSContext *cx, JSObject *obj, PropertyName *name, JSObject **objp, JSProperty **propp)
544 : {
545 0 : return with_LookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp);
546 : }
547 :
548 : static JSBool
549 0 : with_LookupElement(JSContext *cx, JSObject *obj, uint32_t index, JSObject **objp,
550 : JSProperty **propp)
551 : {
552 : jsid id;
553 0 : if (!IndexToId(cx, index, &id))
554 0 : return false;
555 0 : return with_LookupGeneric(cx, obj, id, objp, propp);
556 : }
557 :
558 : static JSBool
559 0 : with_LookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid, JSObject **objp, JSProperty **propp)
560 : {
561 0 : return with_LookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp);
562 : }
563 :
564 : static JSBool
565 27 : with_GetGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
566 : {
567 27 : return obj->asWith().object().getGeneric(cx, id, vp);
568 : }
569 :
570 : static JSBool
571 0 : with_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, Value *vp)
572 : {
573 0 : return with_GetGeneric(cx, obj, receiver, ATOM_TO_JSID(name), vp);
574 : }
575 :
576 : static JSBool
577 0 : with_GetElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp)
578 : {
579 : jsid id;
580 0 : if (!IndexToId(cx, index, &id))
581 0 : return false;
582 0 : return with_GetGeneric(cx, obj, receiver, id, vp);
583 : }
584 :
585 : static JSBool
586 0 : with_GetSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp)
587 : {
588 0 : return with_GetGeneric(cx, obj, receiver, SPECIALID_TO_JSID(sid), vp);
589 : }
590 :
591 : static JSBool
592 63 : with_SetGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
593 : {
594 63 : return obj->asWith().object().setGeneric(cx, id, vp, strict);
595 : }
596 :
597 : static JSBool
598 0 : with_SetProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict)
599 : {
600 0 : return obj->asWith().object().setProperty(cx, name, vp, strict);
601 : }
602 :
603 : static JSBool
604 0 : with_SetElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict)
605 : {
606 0 : return obj->asWith().object().setElement(cx, index, vp, strict);
607 : }
608 :
609 : static JSBool
610 0 : with_SetSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict)
611 : {
612 0 : return obj->asWith().object().setSpecial(cx, sid, vp, strict);
613 : }
614 :
615 : static JSBool
616 0 : with_GetGenericAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
617 : {
618 0 : return obj->asWith().object().getGenericAttributes(cx, id, attrsp);
619 : }
620 :
621 : static JSBool
622 0 : with_GetPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
623 : {
624 0 : return obj->asWith().object().getPropertyAttributes(cx, name, attrsp);
625 : }
626 :
627 : static JSBool
628 0 : with_GetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
629 : {
630 0 : return obj->asWith().object().getElementAttributes(cx, index, attrsp);
631 : }
632 :
633 : static JSBool
634 0 : with_GetSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
635 : {
636 0 : return obj->asWith().object().getSpecialAttributes(cx, sid, attrsp);
637 : }
638 :
639 : static JSBool
640 0 : with_SetGenericAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
641 : {
642 0 : return obj->asWith().object().setGenericAttributes(cx, id, attrsp);
643 : }
644 :
645 : static JSBool
646 0 : with_SetPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
647 : {
648 0 : return obj->asWith().object().setPropertyAttributes(cx, name, attrsp);
649 : }
650 :
651 : static JSBool
652 0 : with_SetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
653 : {
654 0 : return obj->asWith().object().setElementAttributes(cx, index, attrsp);
655 : }
656 :
657 : static JSBool
658 0 : with_SetSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
659 : {
660 0 : return obj->asWith().object().setSpecialAttributes(cx, sid, attrsp);
661 : }
662 :
663 : static JSBool
664 9 : with_DeleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict)
665 : {
666 9 : return obj->asWith().object().deleteProperty(cx, name, rval, strict);
667 : }
668 :
669 : static JSBool
670 0 : with_DeleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict)
671 : {
672 0 : return obj->asWith().object().deleteElement(cx, index, rval, strict);
673 : }
674 :
675 : static JSBool
676 0 : with_DeleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict)
677 : {
678 0 : return obj->asWith().object().deleteSpecial(cx, sid, rval, strict);
679 : }
680 :
681 : static JSBool
682 54 : with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
683 : Value *statep, jsid *idp)
684 : {
685 54 : return obj->asWith().object().enumerate(cx, enum_op, statep, idp);
686 : }
687 :
688 : static JSType
689 0 : with_TypeOf(JSContext *cx, JSObject *obj)
690 : {
691 0 : return JSTYPE_OBJECT;
692 : }
693 :
694 : static JSObject *
695 18 : with_ThisObject(JSContext *cx, JSObject *obj)
696 : {
697 18 : return &obj->asWith().withThis();
698 : }
699 :
700 : Class js::WithClass = {
701 : "With",
702 : JSCLASS_HAS_PRIVATE |
703 : JSCLASS_HAS_RESERVED_SLOTS(WithObject::RESERVED_SLOTS) |
704 : JSCLASS_IS_ANONYMOUS,
705 : JS_PropertyStub, /* addProperty */
706 : JS_PropertyStub, /* delProperty */
707 : JS_PropertyStub, /* getProperty */
708 : JS_StrictPropertyStub, /* setProperty */
709 : JS_EnumerateStub,
710 : JS_ResolveStub,
711 : JS_ConvertStub,
712 : NULL, /* finalize */
713 : NULL, /* checkAccess */
714 : NULL, /* call */
715 : NULL, /* construct */
716 : NULL, /* hasInstance */
717 : NULL, /* trace */
718 : JS_NULL_CLASS_EXT,
719 : {
720 : with_LookupGeneric,
721 : with_LookupProperty,
722 : with_LookupElement,
723 : with_LookupSpecial,
724 : NULL, /* defineGeneric */
725 : NULL, /* defineProperty */
726 : NULL, /* defineElement */
727 : NULL, /* defineSpecial */
728 : with_GetGeneric,
729 : with_GetProperty,
730 : with_GetElement,
731 : NULL, /* getElementIfPresent */
732 : with_GetSpecial,
733 : with_SetGeneric,
734 : with_SetProperty,
735 : with_SetElement,
736 : with_SetSpecial,
737 : with_GetGenericAttributes,
738 : with_GetPropertyAttributes,
739 : with_GetElementAttributes,
740 : with_GetSpecialAttributes,
741 : with_SetGenericAttributes,
742 : with_SetPropertyAttributes,
743 : with_SetElementAttributes,
744 : with_SetSpecialAttributes,
745 : with_DeleteProperty,
746 : with_DeleteElement,
747 : with_DeleteSpecial,
748 : with_Enumerate,
749 : with_TypeOf,
750 : NULL, /* fix */
751 : with_ThisObject,
752 : NULL, /* clear */
753 : }
754 : };
755 :
756 : ClonedBlockObject *
757 1927 : ClonedBlockObject::create(JSContext *cx, StaticBlockObject &block, StackFrame *fp)
758 : {
759 3854 : RootedVarTypeObject type(cx);
760 1927 : type = block.getNewType(cx);
761 1927 : if (!type)
762 0 : return NULL;
763 :
764 : HeapSlot *slots;
765 1927 : if (!PreallocateObjectDynamicSlots(cx, block.lastProperty(), &slots))
766 0 : return NULL;
767 :
768 3854 : RootedVarShape shape(cx);
769 1927 : shape = block.lastProperty();
770 :
771 1927 : JSObject *obj = JSObject::create(cx, FINALIZE_KIND, shape, type, slots);
772 1927 : if (!obj)
773 0 : return NULL;
774 :
775 : /* Set the parent if necessary, as for call objects. */
776 1927 : JSObject &global = fp->scopeChain().global();
777 1927 : if (&global != obj->getParent()) {
778 1927 : JS_ASSERT(obj->getParent() == NULL);
779 1927 : if (!obj->setParent(cx, &global))
780 0 : return NULL;
781 : }
782 :
783 1927 : JS_ASSERT(!obj->inDictionaryMode());
784 1927 : JS_ASSERT(obj->slotSpan() >= block.slotCount() + RESERVED_SLOTS);
785 :
786 1927 : obj->setReservedSlot(DEPTH_SLOT, PrivateUint32Value(block.stackDepth()));
787 1927 : obj->setPrivate(js_FloatingFrameIfGenerator(cx, fp));
788 :
789 1927 : if (obj->lastProperty()->extensibleParents() && !obj->generateOwnShape(cx))
790 0 : return NULL;
791 :
792 1927 : return &obj->asClonedBlock();
793 : }
794 :
795 : void
796 1909 : ClonedBlockObject::put(JSContext *cx)
797 : {
798 1909 : StackFrame *fp = cx->fp();
799 1909 : JS_ASSERT(maybeStackFrame() == js_FloatingFrameIfGenerator(cx, fp));
800 :
801 1909 : uint32_t count = slotCount();
802 1909 : uint32_t depth = stackDepth();
803 :
804 : /* The block and its locals must be on the current stack for GC safety. */
805 1909 : JS_ASSERT(depth <= uint32_t(cx->regs().sp - fp->base()));
806 1909 : JS_ASSERT(count <= uint32_t(cx->regs().sp - fp->base() - depth));
807 :
808 : /* See comments in CheckDestructuring in frontend/Parser.cpp. */
809 1909 : JS_ASSERT(count >= 1);
810 :
811 1909 : copySlotRange(RESERVED_SLOTS, fp->base() + depth, count);
812 :
813 : /* We must clear the private slot even with errors. */
814 1909 : setPrivate(NULL);
815 1909 : fp->setScopeChainNoCallObj(enclosingScope());
816 1909 : }
817 :
818 : static JSBool
819 2764 : block_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
820 : {
821 : /*
822 : * Block objects are never exposed to script, and the engine handles them
823 : * with care. So unlike other getters, this one can assert (rather than
824 : * check) certain invariants about obj.
825 : */
826 2764 : ClonedBlockObject &block = obj->asClonedBlock();
827 2764 : unsigned index = (unsigned) JSID_TO_INT(id);
828 2764 : JS_ASSERT(index < block.slotCount());
829 :
830 2764 : if (StackFrame *fp = block.maybeStackFrame()) {
831 2421 : fp = js_LiveFrameIfGenerator(fp);
832 2421 : index += fp->numFixed() + block.stackDepth();
833 2421 : JS_ASSERT(index < fp->numSlots());
834 2421 : *vp = fp->slots()[index];
835 2421 : return true;
836 : }
837 :
838 : /* Values are in slots immediately following the class-reserved ones. */
839 343 : JS_ASSERT(block.closedSlot(index) == *vp);
840 343 : return true;
841 : }
842 :
843 : static JSBool
844 63 : block_setProperty(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
845 : {
846 63 : ClonedBlockObject &block = obj->asClonedBlock();
847 63 : unsigned index = (unsigned) JSID_TO_INT(id);
848 63 : JS_ASSERT(index < block.slotCount());
849 :
850 63 : if (StackFrame *fp = block.maybeStackFrame()) {
851 54 : fp = js_LiveFrameIfGenerator(fp);
852 54 : index += fp->numFixed() + block.stackDepth();
853 54 : JS_ASSERT(index < fp->numSlots());
854 54 : fp->slots()[index] = *vp;
855 54 : return true;
856 : }
857 :
858 : /*
859 : * The value in *vp will be written back to the slot in obj that was
860 : * allocated when this let binding was defined.
861 : */
862 9 : return true;
863 : }
864 :
865 : bool
866 18 : ClonedBlockObject::containsVar(PropertyName *name, Value *vp, JSContext *cx)
867 : {
868 18 : jsid id = ATOM_TO_JSID(name);
869 18 : const Shape *shape = nativeLookup(cx, id);
870 18 : if (!shape)
871 0 : return false;
872 :
873 18 : JS_ASSERT(shape->getterOp() == block_getProperty);
874 18 : JS_ALWAYS_TRUE(block_getProperty(cx, this, INT_TO_JSID(shape->shortid()), vp));
875 18 : return true;
876 : }
877 :
878 : StaticBlockObject *
879 103322 : StaticBlockObject::create(JSContext *cx)
880 : {
881 206644 : RootedVarTypeObject type(cx);
882 103322 : type = cx->compartment->getEmptyType(cx);
883 103322 : if (!type)
884 0 : return NULL;
885 :
886 206644 : RootedVarShape emptyBlockShape(cx);
887 103322 : emptyBlockShape = EmptyShape::getInitialShape(cx, &BlockClass, NULL, NULL, FINALIZE_KIND);
888 103322 : if (!emptyBlockShape)
889 0 : return NULL;
890 :
891 103322 : JSObject *obj = JSObject::create(cx, FINALIZE_KIND, emptyBlockShape, type, NULL);
892 103322 : if (!obj)
893 0 : return NULL;
894 :
895 103322 : return &obj->asStaticBlock();
896 : }
897 :
898 : const Shape *
899 330952 : StaticBlockObject::addVar(JSContext *cx, jsid id, int index, bool *redeclared)
900 : {
901 330952 : JS_ASSERT(JSID_IS_ATOM(id) || (JSID_IS_INT(id) && JSID_TO_INT(id) == index));
902 :
903 330952 : *redeclared = false;
904 :
905 : /* Inline JSObject::addProperty in order to trap the redefinition case. */
906 : Shape **spp;
907 330952 : if (Shape::search(cx, lastProperty(), id, &spp, true)) {
908 171 : *redeclared = true;
909 171 : return NULL;
910 : }
911 :
912 : /*
913 : * Don't convert this object to dictionary mode so that we can clone the
914 : * block's shape later.
915 : */
916 330781 : uint32_t slot = JSSLOT_FREE(&BlockClass) + index;
917 : return addPropertyInternal(cx, id, block_getProperty, block_setProperty,
918 : slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
919 : Shape::HAS_SHORTID, index, spp,
920 330781 : /* allowDictionary = */ false);
921 : }
922 :
923 : Class js::BlockClass = {
924 : "Block",
925 : JSCLASS_HAS_PRIVATE |
926 : JSCLASS_HAS_RESERVED_SLOTS(BlockObject::RESERVED_SLOTS) |
927 : JSCLASS_IS_ANONYMOUS,
928 : JS_PropertyStub, /* addProperty */
929 : JS_PropertyStub, /* delProperty */
930 : JS_PropertyStub, /* getProperty */
931 : JS_StrictPropertyStub, /* setProperty */
932 : JS_EnumerateStub,
933 : JS_ResolveStub,
934 : JS_ConvertStub
935 : };
936 :
937 : #define NO_PARENT_INDEX UINT32_MAX
938 :
939 : static uint32_t
940 2260 : FindObjectIndex(JSObjectArray *array, JSObject *obj)
941 : {
942 : size_t i;
943 :
944 2260 : if (array) {
945 2260 : i = array->length;
946 2936 : do {
947 :
948 3152 : if (array->vector[--i] == obj)
949 216 : return i;
950 : } while (i != 0);
951 : }
952 :
953 2044 : return NO_PARENT_INDEX;
954 : }
955 :
956 : template<XDRMode mode>
957 : bool
958 4520 : js::XDRStaticBlockObject(XDRState<mode> *xdr, JSScript *script, StaticBlockObject **objp)
959 : {
960 4520 : JSContext *cx = xdr->cx();
961 :
962 4520 : StaticBlockObject *obj = NULL;
963 4520 : uint32_t parentId = 0;
964 4520 : uint32_t count = 0;
965 4520 : uint32_t depthAndCount = 0;
966 : if (mode == XDR_ENCODE) {
967 2260 : obj = *objp;
968 2260 : parentId = JSScript::isValidOffset(script->objectsOffset)
969 : ? FindObjectIndex(script->objects(), obj->enclosingBlock())
970 : : NO_PARENT_INDEX;
971 2260 : uint32_t depth = obj->stackDepth();
972 2260 : JS_ASSERT(depth <= UINT16_MAX);
973 2260 : count = obj->slotCount();
974 2260 : JS_ASSERT(count <= UINT16_MAX);
975 2260 : depthAndCount = (depth << 16) | uint16_t(count);
976 : }
977 :
978 : /* First, XDR the parent atomid. */
979 4520 : if (!xdr->codeUint32(&parentId))
980 0 : return false;
981 :
982 : if (mode == XDR_DECODE) {
983 2260 : obj = StaticBlockObject::create(cx);
984 2260 : if (!obj)
985 0 : return false;
986 2260 : *objp = obj;
987 :
988 : /*
989 : * If there's a parent id, then get the parent out of our script's
990 : * object array. We know that we XDR block object in outer-to-inner
991 : * order, which means that getting the parent now will work.
992 : */
993 2260 : obj->setEnclosingBlock(parentId == NO_PARENT_INDEX
994 : ? NULL
995 : : &script->getObject(parentId)->asStaticBlock());
996 : }
997 :
998 9040 : AutoObjectRooter tvr(cx, obj);
999 :
1000 4520 : if (!xdr->codeUint32(&depthAndCount))
1001 0 : return false;
1002 :
1003 : if (mode == XDR_DECODE) {
1004 2260 : uint32_t depth = uint16_t(depthAndCount >> 16);
1005 2260 : count = uint16_t(depthAndCount);
1006 2260 : obj->setStackDepth(depth);
1007 :
1008 : /*
1009 : * XDR the block object's properties. We know that there are 'count'
1010 : * properties to XDR, stored as id/shortid pairs.
1011 : */
1012 5295 : for (unsigned i = 0; i < count; i++) {
1013 : JSAtom *atom;
1014 3035 : if (!XDRAtom(xdr, &atom))
1015 0 : return false;
1016 :
1017 : /* The empty string indicates an int id. */
1018 : jsid id = atom != cx->runtime->emptyString
1019 : ? ATOM_TO_JSID(atom)
1020 3035 : : INT_TO_JSID(i);
1021 :
1022 : bool redeclared;
1023 3035 : if (!obj->addVar(cx, id, i, &redeclared)) {
1024 0 : JS_ASSERT(!redeclared);
1025 0 : return false;
1026 : }
1027 : }
1028 : } else {
1029 4520 : AutoShapeVector shapes(cx);
1030 2260 : shapes.growBy(count);
1031 :
1032 5295 : for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
1033 3035 : const Shape *shape = &r.front();
1034 3035 : shapes[shape->shortid()] = shape;
1035 : }
1036 :
1037 : /*
1038 : * XDR the block object's properties. We know that there are 'count'
1039 : * properties to XDR, stored as id/shortid pairs.
1040 : */
1041 5295 : for (unsigned i = 0; i < count; i++) {
1042 3035 : const Shape *shape = shapes[i];
1043 3035 : JS_ASSERT(shape->getter() == block_getProperty);
1044 3035 : JS_ASSERT(unsigned(shape->shortid()) == i);
1045 :
1046 3035 : jsid propid = shape->propid();
1047 3035 : JS_ASSERT(JSID_IS_ATOM(propid) || JSID_IS_INT(propid));
1048 :
1049 : /* The empty string indicates an int id. */
1050 : JSAtom *atom = JSID_IS_ATOM(propid)
1051 : ? JSID_TO_ATOM(propid)
1052 3035 : : cx->runtime->emptyString;
1053 :
1054 3035 : if (!XDRAtom(xdr, &atom))
1055 0 : return false;
1056 : }
1057 : }
1058 4520 : return true;
1059 : }
1060 :
1061 : template bool
1062 : js::XDRStaticBlockObject(XDRState<XDR_ENCODE> *xdr, JSScript *script, StaticBlockObject **objp);
1063 :
1064 : template bool
1065 : js::XDRStaticBlockObject(XDRState<XDR_DECODE> *xdr, JSScript *script, StaticBlockObject **objp);
|