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