|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "vm/ArgumentsObject-inl.h" |
|
8 |
|
9 #include "jsinfer.h" |
|
10 |
|
11 #ifdef JS_ION |
|
12 #include "jit/IonFrames.h" |
|
13 #endif |
|
14 #include "vm/GlobalObject.h" |
|
15 #include "vm/Stack.h" |
|
16 |
|
17 #include "jsobjinlines.h" |
|
18 |
|
19 #include "vm/Stack-inl.h" |
|
20 |
|
21 using namespace js; |
|
22 using namespace js::gc; |
|
23 |
|
24 static void |
|
25 CopyStackFrameArguments(const AbstractFramePtr frame, HeapValue *dst, unsigned totalArgs) |
|
26 { |
|
27 JS_ASSERT_IF(frame.isInterpreterFrame(), !frame.asInterpreterFrame()->runningInJit()); |
|
28 |
|
29 JS_ASSERT(Max(frame.numActualArgs(), frame.numFormalArgs()) == totalArgs); |
|
30 |
|
31 /* Copy arguments. */ |
|
32 Value *src = frame.argv(); |
|
33 Value *end = src + totalArgs; |
|
34 while (src != end) |
|
35 (dst++)->init(*src++); |
|
36 } |
|
37 |
|
38 /* static */ void |
|
39 ArgumentsObject::MaybeForwardToCallObject(AbstractFramePtr frame, JSObject *obj, |
|
40 ArgumentsData *data) |
|
41 { |
|
42 JSScript *script = frame.script(); |
|
43 if (frame.fun()->isHeavyweight() && script->argsObjAliasesFormals()) { |
|
44 obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(frame.callObj())); |
|
45 for (AliasedFormalIter fi(script); fi; fi++) |
|
46 data->args[fi.frameIndex()] = JS::MagicValueUint32(fi.scopeSlot()); |
|
47 } |
|
48 } |
|
49 |
|
50 #if defined(JS_ION) |
|
51 /* static */ void |
|
52 ArgumentsObject::MaybeForwardToCallObject(jit::IonJSFrameLayout *frame, HandleObject callObj, |
|
53 JSObject *obj, ArgumentsData *data) |
|
54 { |
|
55 JSFunction *callee = jit::CalleeTokenToFunction(frame->calleeToken()); |
|
56 JSScript *script = callee->nonLazyScript(); |
|
57 if (callee->isHeavyweight() && script->argsObjAliasesFormals()) { |
|
58 JS_ASSERT(callObj && callObj->is<CallObject>()); |
|
59 obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(*callObj.get())); |
|
60 for (AliasedFormalIter fi(script); fi; fi++) |
|
61 data->args[fi.frameIndex()] = JS::MagicValueUint32(fi.scopeSlot()); |
|
62 } |
|
63 } |
|
64 #endif |
|
65 |
|
66 struct CopyFrameArgs |
|
67 { |
|
68 AbstractFramePtr frame_; |
|
69 |
|
70 CopyFrameArgs(AbstractFramePtr frame) |
|
71 : frame_(frame) |
|
72 { } |
|
73 |
|
74 void copyArgs(JSContext *, HeapValue *dst, unsigned totalArgs) const { |
|
75 CopyStackFrameArguments(frame_, dst, totalArgs); |
|
76 } |
|
77 |
|
78 /* |
|
79 * If a call object exists and the arguments object aliases formals, the |
|
80 * call object is the canonical location for formals. |
|
81 */ |
|
82 void maybeForwardToCallObject(JSObject *obj, ArgumentsData *data) { |
|
83 ArgumentsObject::MaybeForwardToCallObject(frame_, obj, data); |
|
84 } |
|
85 }; |
|
86 |
|
87 #if defined(JS_ION) |
|
88 struct CopyIonJSFrameArgs |
|
89 { |
|
90 jit::IonJSFrameLayout *frame_; |
|
91 HandleObject callObj_; |
|
92 |
|
93 CopyIonJSFrameArgs(jit::IonJSFrameLayout *frame, HandleObject callObj) |
|
94 : frame_(frame), callObj_(callObj) |
|
95 { } |
|
96 |
|
97 void copyArgs(JSContext *, HeapValue *dstBase, unsigned totalArgs) const { |
|
98 unsigned numActuals = frame_->numActualArgs(); |
|
99 unsigned numFormals = jit::CalleeTokenToFunction(frame_->calleeToken())->nargs(); |
|
100 JS_ASSERT(numActuals <= totalArgs); |
|
101 JS_ASSERT(numFormals <= totalArgs); |
|
102 JS_ASSERT(Max(numActuals, numFormals) == totalArgs); |
|
103 |
|
104 /* Copy all arguments. */ |
|
105 Value *src = frame_->argv() + 1; /* +1 to skip this. */ |
|
106 Value *end = src + numActuals; |
|
107 HeapValue *dst = dstBase; |
|
108 while (src != end) |
|
109 (dst++)->init(*src++); |
|
110 |
|
111 if (numActuals < numFormals) { |
|
112 HeapValue *dstEnd = dstBase + totalArgs; |
|
113 while (dst != dstEnd) |
|
114 (dst++)->init(UndefinedValue()); |
|
115 } |
|
116 } |
|
117 |
|
118 /* |
|
119 * If a call object exists and the arguments object aliases formals, the |
|
120 * call object is the canonical location for formals. |
|
121 */ |
|
122 void maybeForwardToCallObject(JSObject *obj, ArgumentsData *data) { |
|
123 ArgumentsObject::MaybeForwardToCallObject(frame_, callObj_, obj, data); |
|
124 } |
|
125 }; |
|
126 #endif |
|
127 |
|
128 struct CopyScriptFrameIterArgs |
|
129 { |
|
130 ScriptFrameIter &iter_; |
|
131 |
|
132 CopyScriptFrameIterArgs(ScriptFrameIter &iter) |
|
133 : iter_(iter) |
|
134 { } |
|
135 |
|
136 void copyArgs(JSContext *cx, HeapValue *dstBase, unsigned totalArgs) const { |
|
137 /* Copy actual arguments. */ |
|
138 iter_.unaliasedForEachActual(cx, CopyToHeap(dstBase)); |
|
139 |
|
140 /* Define formals which are not part of the actuals. */ |
|
141 unsigned numActuals = iter_.numActualArgs(); |
|
142 unsigned numFormals = iter_.callee()->nargs(); |
|
143 JS_ASSERT(numActuals <= totalArgs); |
|
144 JS_ASSERT(numFormals <= totalArgs); |
|
145 JS_ASSERT(Max(numActuals, numFormals) == totalArgs); |
|
146 |
|
147 if (numActuals < numFormals) { |
|
148 HeapValue *dst = dstBase + numActuals, *dstEnd = dstBase + totalArgs; |
|
149 while (dst != dstEnd) |
|
150 (dst++)->init(UndefinedValue()); |
|
151 } |
|
152 } |
|
153 |
|
154 /* |
|
155 * Ion frames are copying every argument onto the stack, other locations are |
|
156 * invalid. |
|
157 */ |
|
158 void maybeForwardToCallObject(JSObject *obj, ArgumentsData *data) { |
|
159 if (!iter_.isJit()) |
|
160 ArgumentsObject::MaybeForwardToCallObject(iter_.abstractFramePtr(), obj, data); |
|
161 } |
|
162 }; |
|
163 |
|
164 template <typename CopyArgs> |
|
165 /* static */ ArgumentsObject * |
|
166 ArgumentsObject::create(JSContext *cx, HandleScript script, HandleFunction callee, unsigned numActuals, |
|
167 CopyArgs ©) |
|
168 { |
|
169 RootedObject proto(cx, callee->global().getOrCreateObjectPrototype(cx)); |
|
170 if (!proto) |
|
171 return nullptr; |
|
172 |
|
173 bool strict = callee->strict(); |
|
174 const Class *clasp = strict ? &StrictArgumentsObject::class_ : &NormalArgumentsObject::class_; |
|
175 |
|
176 RootedTypeObject type(cx, cx->getNewType(clasp, proto.get())); |
|
177 if (!type) |
|
178 return nullptr; |
|
179 |
|
180 JSObject *metadata = nullptr; |
|
181 if (!NewObjectMetadata(cx, &metadata)) |
|
182 return nullptr; |
|
183 |
|
184 RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(proto), |
|
185 proto->getParent(), metadata, FINALIZE_KIND, |
|
186 BaseShape::INDEXED)); |
|
187 if (!shape) |
|
188 return nullptr; |
|
189 |
|
190 unsigned numFormals = callee->nargs(); |
|
191 unsigned numDeletedWords = NumWordsForBitArrayOfLength(numActuals); |
|
192 unsigned numArgs = Max(numActuals, numFormals); |
|
193 unsigned numBytes = offsetof(ArgumentsData, args) + |
|
194 numDeletedWords * sizeof(size_t) + |
|
195 numArgs * sizeof(Value); |
|
196 |
|
197 ArgumentsData *data = (ArgumentsData *)cx->malloc_(numBytes); |
|
198 if (!data) |
|
199 return nullptr; |
|
200 |
|
201 JSObject *obj = JSObject::create(cx, FINALIZE_KIND, GetInitialHeap(GenericObject, clasp), |
|
202 shape, type); |
|
203 if (!obj) { |
|
204 js_free(data); |
|
205 return nullptr; |
|
206 } |
|
207 |
|
208 data->numArgs = numArgs; |
|
209 data->callee.init(ObjectValue(*callee.get())); |
|
210 data->script = script; |
|
211 |
|
212 /* Copy [0, numArgs) into data->slots. */ |
|
213 HeapValue *dst = data->args, *dstEnd = data->args + numArgs; |
|
214 copy.copyArgs(cx, dst, numArgs); |
|
215 |
|
216 data->deletedBits = reinterpret_cast<size_t *>(dstEnd); |
|
217 ClearAllBitArrayElements(data->deletedBits, numDeletedWords); |
|
218 |
|
219 obj->initFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(numActuals << PACKED_BITS_COUNT)); |
|
220 obj->initFixedSlot(DATA_SLOT, PrivateValue(data)); |
|
221 |
|
222 copy.maybeForwardToCallObject(obj, data); |
|
223 |
|
224 ArgumentsObject &argsobj = obj->as<ArgumentsObject>(); |
|
225 JS_ASSERT(argsobj.initialLength() == numActuals); |
|
226 JS_ASSERT(!argsobj.hasOverriddenLength()); |
|
227 return &argsobj; |
|
228 } |
|
229 |
|
230 ArgumentsObject * |
|
231 ArgumentsObject::createExpected(JSContext *cx, AbstractFramePtr frame) |
|
232 { |
|
233 JS_ASSERT(frame.script()->needsArgsObj()); |
|
234 RootedScript script(cx, frame.script()); |
|
235 RootedFunction callee(cx, frame.callee()); |
|
236 CopyFrameArgs copy(frame); |
|
237 ArgumentsObject *argsobj = create(cx, script, callee, frame.numActualArgs(), copy); |
|
238 if (!argsobj) |
|
239 return nullptr; |
|
240 |
|
241 frame.initArgsObj(*argsobj); |
|
242 return argsobj; |
|
243 } |
|
244 |
|
245 ArgumentsObject * |
|
246 ArgumentsObject::createUnexpected(JSContext *cx, ScriptFrameIter &iter) |
|
247 { |
|
248 RootedScript script(cx, iter.script()); |
|
249 RootedFunction callee(cx, iter.callee()); |
|
250 CopyScriptFrameIterArgs copy(iter); |
|
251 return create(cx, script, callee, iter.numActualArgs(), copy); |
|
252 } |
|
253 |
|
254 ArgumentsObject * |
|
255 ArgumentsObject::createUnexpected(JSContext *cx, AbstractFramePtr frame) |
|
256 { |
|
257 RootedScript script(cx, frame.script()); |
|
258 RootedFunction callee(cx, frame.callee()); |
|
259 CopyFrameArgs copy(frame); |
|
260 return create(cx, script, callee, frame.numActualArgs(), copy); |
|
261 } |
|
262 |
|
263 #if defined(JS_ION) |
|
264 ArgumentsObject * |
|
265 ArgumentsObject::createForIon(JSContext *cx, jit::IonJSFrameLayout *frame, HandleObject scopeChain) |
|
266 { |
|
267 jit::CalleeToken token = frame->calleeToken(); |
|
268 JS_ASSERT(jit::CalleeTokenIsFunction(token)); |
|
269 RootedScript script(cx, jit::ScriptFromCalleeToken(token)); |
|
270 RootedFunction callee(cx, jit::CalleeTokenToFunction(token)); |
|
271 RootedObject callObj(cx, scopeChain->is<CallObject>() ? scopeChain.get() : nullptr); |
|
272 CopyIonJSFrameArgs copy(frame, callObj); |
|
273 return create(cx, script, callee, frame->numActualArgs(), copy); |
|
274 } |
|
275 #endif |
|
276 |
|
277 static bool |
|
278 args_delProperty(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded) |
|
279 { |
|
280 ArgumentsObject &argsobj = obj->as<ArgumentsObject>(); |
|
281 if (JSID_IS_INT(id)) { |
|
282 unsigned arg = unsigned(JSID_TO_INT(id)); |
|
283 if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) |
|
284 argsobj.markElementDeleted(arg); |
|
285 } else if (JSID_IS_ATOM(id, cx->names().length)) { |
|
286 argsobj.markLengthOverridden(); |
|
287 } else if (JSID_IS_ATOM(id, cx->names().callee)) { |
|
288 argsobj.as<NormalArgumentsObject>().clearCallee(); |
|
289 } |
|
290 *succeeded = true; |
|
291 return true; |
|
292 } |
|
293 |
|
294 static bool |
|
295 ArgGetter(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) |
|
296 { |
|
297 if (!obj->is<NormalArgumentsObject>()) |
|
298 return true; |
|
299 |
|
300 NormalArgumentsObject &argsobj = obj->as<NormalArgumentsObject>(); |
|
301 if (JSID_IS_INT(id)) { |
|
302 /* |
|
303 * arg can exceed the number of arguments if a script changed the |
|
304 * prototype to point to another Arguments object with a bigger argc. |
|
305 */ |
|
306 unsigned arg = unsigned(JSID_TO_INT(id)); |
|
307 if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) |
|
308 vp.set(argsobj.element(arg)); |
|
309 } else if (JSID_IS_ATOM(id, cx->names().length)) { |
|
310 if (!argsobj.hasOverriddenLength()) |
|
311 vp.setInt32(argsobj.initialLength()); |
|
312 } else { |
|
313 JS_ASSERT(JSID_IS_ATOM(id, cx->names().callee)); |
|
314 if (!argsobj.callee().isMagic(JS_OVERWRITTEN_CALLEE)) |
|
315 vp.set(argsobj.callee()); |
|
316 } |
|
317 return true; |
|
318 } |
|
319 |
|
320 static bool |
|
321 ArgSetter(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp) |
|
322 { |
|
323 if (!obj->is<NormalArgumentsObject>()) |
|
324 return true; |
|
325 |
|
326 unsigned attrs; |
|
327 if (!baseops::GetAttributes(cx, obj, id, &attrs)) |
|
328 return false; |
|
329 JS_ASSERT(!(attrs & JSPROP_READONLY)); |
|
330 attrs &= (JSPROP_ENUMERATE | JSPROP_PERMANENT); /* only valid attributes */ |
|
331 |
|
332 NormalArgumentsObject &argsobj = obj->as<NormalArgumentsObject>(); |
|
333 RootedScript script(cx, argsobj.containingScript()); |
|
334 |
|
335 if (JSID_IS_INT(id)) { |
|
336 unsigned arg = unsigned(JSID_TO_INT(id)); |
|
337 if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) { |
|
338 argsobj.setElement(cx, arg, vp); |
|
339 if (arg < script->functionNonDelazifying()->nargs()) |
|
340 types::TypeScript::SetArgument(cx, script, arg, vp); |
|
341 return true; |
|
342 } |
|
343 } else { |
|
344 JS_ASSERT(JSID_IS_ATOM(id, cx->names().length) || JSID_IS_ATOM(id, cx->names().callee)); |
|
345 } |
|
346 |
|
347 /* |
|
348 * For simplicity we use delete/define to replace the property with one |
|
349 * backed by the default Object getter and setter. Note that we rely on |
|
350 * args_delProperty to clear the corresponding reserved slot so the GC can |
|
351 * collect its value. Note also that we must define the property instead |
|
352 * of setting it in case the user has changed the prototype to an object |
|
353 * that has a setter for this id. |
|
354 */ |
|
355 bool succeeded; |
|
356 return baseops::DeleteGeneric(cx, obj, id, &succeeded) && |
|
357 baseops::DefineGeneric(cx, obj, id, vp, nullptr, nullptr, attrs); |
|
358 } |
|
359 |
|
360 static bool |
|
361 args_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp) |
|
362 { |
|
363 objp.set(nullptr); |
|
364 |
|
365 Rooted<NormalArgumentsObject*> argsobj(cx, &obj->as<NormalArgumentsObject>()); |
|
366 |
|
367 unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; |
|
368 if (JSID_IS_INT(id)) { |
|
369 uint32_t arg = uint32_t(JSID_TO_INT(id)); |
|
370 if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg)) |
|
371 return true; |
|
372 |
|
373 attrs |= JSPROP_ENUMERATE; |
|
374 } else if (JSID_IS_ATOM(id, cx->names().length)) { |
|
375 if (argsobj->hasOverriddenLength()) |
|
376 return true; |
|
377 } else { |
|
378 if (!JSID_IS_ATOM(id, cx->names().callee)) |
|
379 return true; |
|
380 |
|
381 if (argsobj->callee().isMagic(JS_OVERWRITTEN_CALLEE)) |
|
382 return true; |
|
383 } |
|
384 |
|
385 if (!baseops::DefineGeneric(cx, argsobj, id, UndefinedHandleValue, ArgGetter, ArgSetter, attrs)) |
|
386 return false; |
|
387 |
|
388 objp.set(argsobj); |
|
389 return true; |
|
390 } |
|
391 |
|
392 static bool |
|
393 args_enumerate(JSContext *cx, HandleObject obj) |
|
394 { |
|
395 Rooted<NormalArgumentsObject*> argsobj(cx, &obj->as<NormalArgumentsObject>()); |
|
396 RootedId id(cx); |
|
397 |
|
398 /* |
|
399 * Trigger reflection in args_resolve using a series of js_LookupProperty |
|
400 * calls. |
|
401 */ |
|
402 int argc = int(argsobj->initialLength()); |
|
403 for (int i = -2; i != argc; i++) { |
|
404 id = (i == -2) |
|
405 ? NameToId(cx->names().length) |
|
406 : (i == -1) |
|
407 ? NameToId(cx->names().callee) |
|
408 : INT_TO_JSID(i); |
|
409 |
|
410 RootedObject pobj(cx); |
|
411 RootedShape prop(cx); |
|
412 if (!baseops::LookupProperty<CanGC>(cx, argsobj, id, &pobj, &prop)) |
|
413 return false; |
|
414 } |
|
415 return true; |
|
416 } |
|
417 |
|
418 static bool |
|
419 StrictArgGetter(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) |
|
420 { |
|
421 if (!obj->is<StrictArgumentsObject>()) |
|
422 return true; |
|
423 |
|
424 StrictArgumentsObject &argsobj = obj->as<StrictArgumentsObject>(); |
|
425 |
|
426 if (JSID_IS_INT(id)) { |
|
427 /* |
|
428 * arg can exceed the number of arguments if a script changed the |
|
429 * prototype to point to another Arguments object with a bigger argc. |
|
430 */ |
|
431 unsigned arg = unsigned(JSID_TO_INT(id)); |
|
432 if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) |
|
433 vp.set(argsobj.element(arg)); |
|
434 } else { |
|
435 JS_ASSERT(JSID_IS_ATOM(id, cx->names().length)); |
|
436 if (!argsobj.hasOverriddenLength()) |
|
437 vp.setInt32(argsobj.initialLength()); |
|
438 } |
|
439 return true; |
|
440 } |
|
441 |
|
442 static bool |
|
443 StrictArgSetter(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp) |
|
444 { |
|
445 if (!obj->is<StrictArgumentsObject>()) |
|
446 return true; |
|
447 |
|
448 unsigned attrs; |
|
449 if (!baseops::GetAttributes(cx, obj, id, &attrs)) |
|
450 return false; |
|
451 JS_ASSERT(!(attrs & JSPROP_READONLY)); |
|
452 attrs &= (JSPROP_ENUMERATE | JSPROP_PERMANENT); /* only valid attributes */ |
|
453 |
|
454 Rooted<StrictArgumentsObject*> argsobj(cx, &obj->as<StrictArgumentsObject>()); |
|
455 |
|
456 if (JSID_IS_INT(id)) { |
|
457 unsigned arg = unsigned(JSID_TO_INT(id)); |
|
458 if (arg < argsobj->initialLength()) { |
|
459 argsobj->setElement(cx, arg, vp); |
|
460 return true; |
|
461 } |
|
462 } else { |
|
463 JS_ASSERT(JSID_IS_ATOM(id, cx->names().length)); |
|
464 } |
|
465 |
|
466 /* |
|
467 * For simplicity we use delete/define to replace the property with one |
|
468 * backed by the default Object getter and setter. Note that we rely on |
|
469 * args_delProperty to clear the corresponding reserved slot so the GC can |
|
470 * collect its value. |
|
471 */ |
|
472 bool succeeded; |
|
473 return baseops::DeleteGeneric(cx, argsobj, id, &succeeded) && |
|
474 baseops::DefineGeneric(cx, argsobj, id, vp, nullptr, nullptr, attrs); |
|
475 } |
|
476 |
|
477 static bool |
|
478 strictargs_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp) |
|
479 { |
|
480 objp.set(nullptr); |
|
481 |
|
482 Rooted<StrictArgumentsObject*> argsobj(cx, &obj->as<StrictArgumentsObject>()); |
|
483 |
|
484 unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; |
|
485 PropertyOp getter = StrictArgGetter; |
|
486 StrictPropertyOp setter = StrictArgSetter; |
|
487 |
|
488 if (JSID_IS_INT(id)) { |
|
489 uint32_t arg = uint32_t(JSID_TO_INT(id)); |
|
490 if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg)) |
|
491 return true; |
|
492 |
|
493 attrs |= JSPROP_ENUMERATE; |
|
494 } else if (JSID_IS_ATOM(id, cx->names().length)) { |
|
495 if (argsobj->hasOverriddenLength()) |
|
496 return true; |
|
497 } else { |
|
498 if (!JSID_IS_ATOM(id, cx->names().callee) && !JSID_IS_ATOM(id, cx->names().caller)) |
|
499 return true; |
|
500 |
|
501 attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED; |
|
502 getter = CastAsPropertyOp(argsobj->global().getThrowTypeError()); |
|
503 setter = CastAsStrictPropertyOp(argsobj->global().getThrowTypeError()); |
|
504 } |
|
505 |
|
506 if (!baseops::DefineGeneric(cx, argsobj, id, UndefinedHandleValue, getter, setter, attrs)) |
|
507 return false; |
|
508 |
|
509 objp.set(argsobj); |
|
510 return true; |
|
511 } |
|
512 |
|
513 static bool |
|
514 strictargs_enumerate(JSContext *cx, HandleObject obj) |
|
515 { |
|
516 Rooted<StrictArgumentsObject*> argsobj(cx, &obj->as<StrictArgumentsObject>()); |
|
517 |
|
518 /* |
|
519 * Trigger reflection in strictargs_resolve using a series of |
|
520 * js_LookupProperty calls. |
|
521 */ |
|
522 RootedObject pobj(cx); |
|
523 RootedShape prop(cx); |
|
524 RootedId id(cx); |
|
525 |
|
526 // length |
|
527 id = NameToId(cx->names().length); |
|
528 if (!baseops::LookupProperty<CanGC>(cx, argsobj, id, &pobj, &prop)) |
|
529 return false; |
|
530 |
|
531 // callee |
|
532 id = NameToId(cx->names().callee); |
|
533 if (!baseops::LookupProperty<CanGC>(cx, argsobj, id, &pobj, &prop)) |
|
534 return false; |
|
535 |
|
536 // caller |
|
537 id = NameToId(cx->names().caller); |
|
538 if (!baseops::LookupProperty<CanGC>(cx, argsobj, id, &pobj, &prop)) |
|
539 return false; |
|
540 |
|
541 for (uint32_t i = 0, argc = argsobj->initialLength(); i < argc; i++) { |
|
542 id = INT_TO_JSID(i); |
|
543 if (!baseops::LookupProperty<CanGC>(cx, argsobj, id, &pobj, &prop)) |
|
544 return false; |
|
545 } |
|
546 |
|
547 return true; |
|
548 } |
|
549 |
|
550 void |
|
551 ArgumentsObject::finalize(FreeOp *fop, JSObject *obj) |
|
552 { |
|
553 fop->free_(reinterpret_cast<void *>(obj->as<ArgumentsObject>().data())); |
|
554 } |
|
555 |
|
556 void |
|
557 ArgumentsObject::trace(JSTracer *trc, JSObject *obj) |
|
558 { |
|
559 ArgumentsObject &argsobj = obj->as<ArgumentsObject>(); |
|
560 ArgumentsData *data = argsobj.data(); |
|
561 MarkValue(trc, &data->callee, js_callee_str); |
|
562 MarkValueRange(trc, data->numArgs, data->args, js_arguments_str); |
|
563 MarkScriptUnbarriered(trc, &data->script, "script"); |
|
564 } |
|
565 |
|
566 /* |
|
567 * The classes below collaborate to lazily reflect and synchronize actual |
|
568 * argument values, argument count, and callee function object stored in a |
|
569 * stack frame with their corresponding property values in the frame's |
|
570 * arguments object. |
|
571 */ |
|
572 const Class NormalArgumentsObject::class_ = { |
|
573 "Arguments", |
|
574 JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS | |
|
575 JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) | |
|
576 JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_BACKGROUND_FINALIZE, |
|
577 JS_PropertyStub, /* addProperty */ |
|
578 args_delProperty, |
|
579 JS_PropertyStub, /* getProperty */ |
|
580 JS_StrictPropertyStub, /* setProperty */ |
|
581 args_enumerate, |
|
582 reinterpret_cast<JSResolveOp>(args_resolve), |
|
583 JS_ConvertStub, |
|
584 ArgumentsObject::finalize, |
|
585 nullptr, /* call */ |
|
586 nullptr, /* hasInstance */ |
|
587 nullptr, /* construct */ |
|
588 ArgumentsObject::trace |
|
589 }; |
|
590 |
|
591 /* |
|
592 * Strict mode arguments is significantly less magical than non-strict mode |
|
593 * arguments, so it is represented by a different class while sharing some |
|
594 * functionality. |
|
595 */ |
|
596 const Class StrictArgumentsObject::class_ = { |
|
597 "Arguments", |
|
598 JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS | |
|
599 JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) | |
|
600 JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_BACKGROUND_FINALIZE, |
|
601 JS_PropertyStub, /* addProperty */ |
|
602 args_delProperty, |
|
603 JS_PropertyStub, /* getProperty */ |
|
604 JS_StrictPropertyStub, /* setProperty */ |
|
605 strictargs_enumerate, |
|
606 reinterpret_cast<JSResolveOp>(strictargs_resolve), |
|
607 JS_ConvertStub, |
|
608 ArgumentsObject::finalize, |
|
609 nullptr, /* call */ |
|
610 nullptr, /* hasInstance */ |
|
611 nullptr, /* construct */ |
|
612 ArgumentsObject::trace |
|
613 }; |