Line data Source code
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 "mozilla/PodOperations.h"
10 :
11 : #include "jit/JitFrames.h"
12 : #include "vm/AsyncFunction.h"
13 : #include "vm/GlobalObject.h"
14 : #include "vm/Stack.h"
15 :
16 : #include "jsobjinlines.h"
17 :
18 : #include "gc/Nursery-inl.h"
19 : #include "vm/NativeObject-inl.h"
20 : #include "vm/Stack-inl.h"
21 :
22 : using namespace js;
23 : using namespace js::gc;
24 :
25 : /* static */ size_t
26 0 : RareArgumentsData::bytesRequired(size_t numActuals)
27 : {
28 0 : size_t extraBytes = NumWordsForBitArrayOfLength(numActuals) * sizeof(size_t);
29 0 : return offsetof(RareArgumentsData, deletedBits_) + extraBytes;
30 : }
31 :
32 : /* static */ RareArgumentsData*
33 0 : RareArgumentsData::create(JSContext* cx, ArgumentsObject* obj)
34 : {
35 0 : size_t bytes = RareArgumentsData::bytesRequired(obj->initialLength());
36 :
37 0 : uint8_t* data = AllocateObjectBuffer<uint8_t>(cx, obj, bytes);
38 0 : if (!data)
39 0 : return nullptr;
40 :
41 0 : mozilla::PodZero(data, bytes);
42 :
43 0 : return new(data) RareArgumentsData();
44 : }
45 :
46 : bool
47 0 : ArgumentsObject::createRareData(JSContext* cx)
48 : {
49 0 : MOZ_ASSERT(!data()->rareData);
50 :
51 0 : RareArgumentsData* rareData = RareArgumentsData::create(cx, this);
52 0 : if (!rareData)
53 0 : return false;
54 :
55 0 : data()->rareData = rareData;
56 0 : return true;
57 : }
58 :
59 : bool
60 0 : ArgumentsObject::markElementDeleted(JSContext* cx, uint32_t i)
61 : {
62 0 : RareArgumentsData* data = getOrCreateRareData(cx);
63 0 : if (!data)
64 0 : return false;
65 :
66 0 : data->markElementDeleted(initialLength(), i);
67 0 : return true;
68 : }
69 :
70 : static void
71 127 : CopyStackFrameArguments(const AbstractFramePtr frame, GCPtrValue* dst, unsigned totalArgs)
72 : {
73 127 : MOZ_ASSERT_IF(frame.isInterpreterFrame(), !frame.asInterpreterFrame()->runningInJit());
74 :
75 127 : MOZ_ASSERT(Max(frame.numActualArgs(), frame.numFormalArgs()) == totalArgs);
76 :
77 : /* Copy arguments. */
78 127 : Value* src = frame.argv();
79 127 : Value* end = src + totalArgs;
80 595 : while (src != end)
81 234 : (dst++)->init(*src++);
82 127 : }
83 :
84 : /* static */ void
85 127 : ArgumentsObject::MaybeForwardToCallObject(AbstractFramePtr frame, ArgumentsObject* obj,
86 : ArgumentsData* data)
87 : {
88 127 : JSScript* script = frame.script();
89 127 : if (frame.callee()->needsCallObject() && script->argumentsAliasesFormals()) {
90 6 : obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(frame.callObj()));
91 24 : for (PositionalFormalParameterIter fi(script); fi; fi++) {
92 18 : if (fi.closedOver())
93 14 : data->args[fi.argumentSlot()] = MagicEnvSlotValue(fi.location().slot());
94 : }
95 : }
96 127 : }
97 :
98 : /* static */ void
99 0 : ArgumentsObject::MaybeForwardToCallObject(jit::JitFrameLayout* frame, HandleObject callObj,
100 : ArgumentsObject* obj, ArgumentsData* data)
101 : {
102 0 : JSFunction* callee = jit::CalleeTokenToFunction(frame->calleeToken());
103 0 : JSScript* script = callee->nonLazyScript();
104 0 : if (callee->needsCallObject() && script->argumentsAliasesFormals()) {
105 0 : MOZ_ASSERT(callObj && callObj->is<CallObject>());
106 0 : obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(*callObj.get()));
107 0 : for (PositionalFormalParameterIter fi(script); fi; fi++) {
108 0 : if (fi.closedOver())
109 0 : data->args[fi.argumentSlot()] = MagicEnvSlotValue(fi.location().slot());
110 : }
111 : }
112 0 : }
113 :
114 : struct CopyFrameArgs
115 : {
116 : AbstractFramePtr frame_;
117 :
118 127 : explicit CopyFrameArgs(AbstractFramePtr frame)
119 127 : : frame_(frame)
120 127 : { }
121 :
122 127 : void copyArgs(JSContext*, GCPtrValue* dst, unsigned totalArgs) const {
123 127 : CopyStackFrameArguments(frame_, dst, totalArgs);
124 127 : }
125 :
126 : /*
127 : * If a call object exists and the arguments object aliases formals, the
128 : * call object is the canonical location for formals.
129 : */
130 127 : void maybeForwardToCallObject(ArgumentsObject* obj, ArgumentsData* data) {
131 127 : ArgumentsObject::MaybeForwardToCallObject(frame_, obj, data);
132 127 : }
133 : };
134 :
135 : struct CopyJitFrameArgs
136 : {
137 : jit::JitFrameLayout* frame_;
138 : HandleObject callObj_;
139 :
140 0 : CopyJitFrameArgs(jit::JitFrameLayout* frame, HandleObject callObj)
141 0 : : frame_(frame), callObj_(callObj)
142 0 : { }
143 :
144 0 : void copyArgs(JSContext*, GCPtrValue* dstBase, unsigned totalArgs) const {
145 0 : unsigned numActuals = frame_->numActualArgs();
146 0 : unsigned numFormals = jit::CalleeTokenToFunction(frame_->calleeToken())->nargs();
147 0 : MOZ_ASSERT(numActuals <= totalArgs);
148 0 : MOZ_ASSERT(numFormals <= totalArgs);
149 0 : MOZ_ASSERT(Max(numActuals, numFormals) == totalArgs);
150 :
151 : /* Copy all arguments. */
152 0 : Value* src = frame_->argv() + 1; /* +1 to skip this. */
153 0 : Value* end = src + numActuals;
154 0 : GCPtrValue* dst = dstBase;
155 0 : while (src != end)
156 0 : (dst++)->init(*src++);
157 :
158 0 : if (numActuals < numFormals) {
159 0 : GCPtrValue* dstEnd = dstBase + totalArgs;
160 0 : while (dst != dstEnd)
161 0 : (dst++)->init(UndefinedValue());
162 : }
163 0 : }
164 :
165 : /*
166 : * If a call object exists and the arguments object aliases formals, the
167 : * call object is the canonical location for formals.
168 : */
169 0 : void maybeForwardToCallObject(ArgumentsObject* obj, ArgumentsData* data) {
170 0 : ArgumentsObject::MaybeForwardToCallObject(frame_, callObj_, obj, data);
171 0 : }
172 : };
173 :
174 : struct CopyScriptFrameIterArgs
175 : {
176 : ScriptFrameIter& iter_;
177 :
178 0 : explicit CopyScriptFrameIterArgs(ScriptFrameIter& iter)
179 0 : : iter_(iter)
180 0 : { }
181 :
182 0 : void copyArgs(JSContext* cx, GCPtrValue* dstBase, unsigned totalArgs) const {
183 : /* Copy actual arguments. */
184 0 : iter_.unaliasedForEachActual(cx, CopyToHeap(dstBase));
185 :
186 : /* Define formals which are not part of the actuals. */
187 0 : unsigned numActuals = iter_.numActualArgs();
188 0 : unsigned numFormals = iter_.calleeTemplate()->nargs();
189 0 : MOZ_ASSERT(numActuals <= totalArgs);
190 0 : MOZ_ASSERT(numFormals <= totalArgs);
191 0 : MOZ_ASSERT(Max(numActuals, numFormals) == totalArgs);
192 :
193 0 : if (numActuals < numFormals) {
194 0 : GCPtrValue* dst = dstBase + numActuals;
195 0 : GCPtrValue* dstEnd = dstBase + totalArgs;
196 0 : while (dst != dstEnd)
197 0 : (dst++)->init(UndefinedValue());
198 : }
199 0 : }
200 :
201 : /*
202 : * Ion frames are copying every argument onto the stack, other locations are
203 : * invalid.
204 : */
205 0 : void maybeForwardToCallObject(ArgumentsObject* obj, ArgumentsData* data) {
206 0 : if (!iter_.isIon())
207 0 : ArgumentsObject::MaybeForwardToCallObject(iter_.abstractFramePtr(), obj, data);
208 0 : }
209 : };
210 :
211 : ArgumentsObject*
212 12 : ArgumentsObject::createTemplateObject(JSContext* cx, bool mapped)
213 : {
214 : const Class* clasp = mapped
215 12 : ? &MappedArgumentsObject::class_
216 12 : : &UnmappedArgumentsObject::class_;
217 :
218 24 : RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, cx->global()));
219 12 : if (!proto)
220 0 : return nullptr;
221 :
222 24 : RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, clasp, TaggedProto(proto.get())));
223 12 : if (!group)
224 0 : return nullptr;
225 :
226 24 : RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(proto),
227 24 : FINALIZE_KIND, BaseShape::INDEXED));
228 12 : if (!shape)
229 0 : return nullptr;
230 :
231 24 : AutoSetNewObjectMetadata metadata(cx);
232 : JSObject* base;
233 12 : JS_TRY_VAR_OR_RETURN_NULL(cx, base, NativeObject::create(cx, FINALIZE_KIND, gc::TenuredHeap,
234 : shape, group));
235 :
236 12 : ArgumentsObject* obj = &base->as<js::ArgumentsObject>();
237 12 : obj->initFixedSlot(ArgumentsObject::DATA_SLOT, PrivateValue(nullptr));
238 12 : return obj;
239 : }
240 :
241 : ArgumentsObject*
242 135 : JSCompartment::maybeArgumentsTemplateObject(bool mapped) const
243 : {
244 135 : return mapped ? mappedArgumentsTemplate_ : unmappedArgumentsTemplate_;
245 : }
246 :
247 : ArgumentsObject*
248 127 : JSCompartment::getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped)
249 : {
250 : ReadBarriered<ArgumentsObject*>& obj =
251 127 : mapped ? mappedArgumentsTemplate_ : unmappedArgumentsTemplate_;
252 :
253 127 : ArgumentsObject* templateObj = obj;
254 127 : if (templateObj)
255 115 : return templateObj;
256 :
257 12 : templateObj = ArgumentsObject::createTemplateObject(cx, mapped);
258 12 : if (!templateObj)
259 0 : return nullptr;
260 :
261 12 : obj.set(templateObj);
262 12 : return templateObj;
263 : }
264 :
265 : template <typename CopyArgs>
266 : /* static */ ArgumentsObject*
267 127 : ArgumentsObject::create(JSContext* cx, HandleFunction callee, unsigned numActuals, CopyArgs& copy)
268 : {
269 127 : bool mapped = callee->nonLazyScript()->hasMappedArgsObj();
270 127 : ArgumentsObject* templateObj = cx->compartment()->getOrCreateArgumentsTemplateObject(cx, mapped);
271 127 : if (!templateObj)
272 0 : return nullptr;
273 :
274 254 : RootedShape shape(cx, templateObj->lastProperty());
275 254 : RootedObjectGroup group(cx, templateObj->group());
276 :
277 127 : unsigned numFormals = callee->nargs();
278 127 : unsigned numArgs = Max(numActuals, numFormals);
279 127 : unsigned numBytes = ArgumentsData::bytesRequired(numArgs);
280 :
281 254 : Rooted<ArgumentsObject*> obj(cx);
282 127 : ArgumentsData* data = nullptr;
283 : {
284 : // The copyArgs call below can allocate objects, so add this block scope
285 : // to make sure we set the metadata for this arguments object first.
286 254 : AutoSetNewObjectMetadata metadata(cx);
287 :
288 : JSObject* base;
289 127 : JS_TRY_VAR_OR_RETURN_NULL(cx, base, NativeObject::create(cx, FINALIZE_KIND,
290 : gc::DefaultHeap, shape, group));
291 127 : obj = &base->as<ArgumentsObject>();
292 :
293 127 : data =
294 127 : reinterpret_cast<ArgumentsData*>(AllocateObjectBuffer<uint8_t>(cx, obj, numBytes));
295 127 : if (!data) {
296 : // Make the object safe for GC.
297 0 : obj->initFixedSlot(DATA_SLOT, PrivateValue(nullptr));
298 0 : return nullptr;
299 : }
300 :
301 127 : data->numArgs = numArgs;
302 127 : data->rareData = nullptr;
303 :
304 : // Zero the argument Values. This sets each value to DoubleValue(0), which
305 : // is safe for GC tracing.
306 127 : memset(data->args, 0, numArgs * sizeof(Value));
307 127 : MOZ_ASSERT(DoubleValue(0).asRawBits() == 0x0);
308 127 : MOZ_ASSERT_IF(numArgs > 0, data->args[0].asRawBits() == 0x0);
309 :
310 127 : obj->initFixedSlot(DATA_SLOT, PrivateValue(data));
311 127 : obj->initFixedSlot(CALLEE_SLOT, ObjectValue(*callee));
312 : }
313 127 : MOZ_ASSERT(data != nullptr);
314 :
315 : /* Copy [0, numArgs) into data->slots. */
316 127 : copy.copyArgs(cx, data->args, numArgs);
317 :
318 127 : obj->initFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(numActuals << PACKED_BITS_COUNT));
319 :
320 127 : copy.maybeForwardToCallObject(obj, data);
321 :
322 127 : MOZ_ASSERT(obj->initialLength() == numActuals);
323 127 : MOZ_ASSERT(!obj->hasOverriddenLength());
324 127 : return obj;
325 : }
326 :
327 : ArgumentsObject*
328 127 : ArgumentsObject::createExpected(JSContext* cx, AbstractFramePtr frame)
329 : {
330 127 : MOZ_ASSERT(frame.script()->needsArgsObj());
331 254 : RootedFunction callee(cx, frame.callee());
332 127 : CopyFrameArgs copy(frame);
333 127 : ArgumentsObject* argsobj = create(cx, callee, frame.numActualArgs(), copy);
334 127 : if (!argsobj)
335 0 : return nullptr;
336 :
337 127 : frame.initArgsObj(*argsobj);
338 127 : return argsobj;
339 : }
340 :
341 : ArgumentsObject*
342 0 : ArgumentsObject::createUnexpected(JSContext* cx, ScriptFrameIter& iter)
343 : {
344 0 : RootedFunction callee(cx, iter.callee(cx));
345 0 : CopyScriptFrameIterArgs copy(iter);
346 0 : return create(cx, callee, iter.numActualArgs(), copy);
347 : }
348 :
349 : ArgumentsObject*
350 0 : ArgumentsObject::createUnexpected(JSContext* cx, AbstractFramePtr frame)
351 : {
352 0 : RootedFunction callee(cx, frame.callee());
353 0 : CopyFrameArgs copy(frame);
354 0 : return create(cx, callee, frame.numActualArgs(), copy);
355 : }
356 :
357 : ArgumentsObject*
358 0 : ArgumentsObject::createForIon(JSContext* cx, jit::JitFrameLayout* frame, HandleObject scopeChain)
359 : {
360 0 : jit::CalleeToken token = frame->calleeToken();
361 0 : MOZ_ASSERT(jit::CalleeTokenIsFunction(token));
362 0 : RootedFunction callee(cx, jit::CalleeTokenToFunction(token));
363 0 : RootedObject callObj(cx, scopeChain->is<CallObject>() ? scopeChain.get() : nullptr);
364 0 : CopyJitFrameArgs copy(frame, callObj);
365 0 : return create(cx, callee, frame->numActualArgs(), copy);
366 : }
367 :
368 : /* static */ ArgumentsObject*
369 0 : ArgumentsObject::finishForIon(JSContext* cx, jit::JitFrameLayout* frame,
370 : JSObject* scopeChain, ArgumentsObject* obj)
371 : {
372 : // JIT code calls this directly (no callVM), because it's faster, so we're
373 : // not allowed to GC in here.
374 0 : JS::AutoCheckCannotGC nogc;
375 :
376 0 : JSFunction* callee = jit::CalleeTokenToFunction(frame->calleeToken());
377 0 : RootedObject callObj(cx, scopeChain->is<CallObject>() ? scopeChain : nullptr);
378 0 : CopyJitFrameArgs copy(frame, callObj);
379 :
380 0 : unsigned numActuals = frame->numActualArgs();
381 0 : unsigned numFormals = callee->nargs();
382 0 : unsigned numArgs = Max(numActuals, numFormals);
383 0 : unsigned numBytes = ArgumentsData::bytesRequired(numArgs);
384 :
385 : ArgumentsData* data =
386 0 : reinterpret_cast<ArgumentsData*>(AllocateObjectBuffer<uint8_t>(cx, obj, numBytes));
387 0 : if (!data) {
388 : // Make the object safe for GC. Don't report OOM, the slow path will
389 : // retry the allocation.
390 0 : cx->recoverFromOutOfMemory();
391 0 : obj->initFixedSlot(DATA_SLOT, PrivateValue(nullptr));
392 0 : return nullptr;
393 : }
394 :
395 0 : data->numArgs = numArgs;
396 0 : data->rareData = nullptr;
397 :
398 0 : obj->initFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(numActuals << PACKED_BITS_COUNT));
399 0 : obj->initFixedSlot(DATA_SLOT, PrivateValue(data));
400 0 : obj->initFixedSlot(MAYBE_CALL_SLOT, UndefinedValue());
401 0 : obj->initFixedSlot(CALLEE_SLOT, ObjectValue(*callee));
402 :
403 0 : copy.copyArgs(cx, data->args, numArgs);
404 :
405 0 : if (callObj && callee->needsCallObject())
406 0 : copy.maybeForwardToCallObject(obj, data);
407 :
408 0 : MOZ_ASSERT(obj->initialLength() == numActuals);
409 0 : MOZ_ASSERT(!obj->hasOverriddenLength());
410 0 : return obj;
411 : }
412 :
413 : /* static */ bool
414 17 : ArgumentsObject::obj_delProperty(JSContext* cx, HandleObject obj, HandleId id,
415 : ObjectOpResult& result)
416 : {
417 17 : ArgumentsObject& argsobj = obj->as<ArgumentsObject>();
418 17 : if (JSID_IS_INT(id)) {
419 0 : unsigned arg = unsigned(JSID_TO_INT(id));
420 0 : if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) {
421 0 : if (!argsobj.markElementDeleted(cx, arg))
422 0 : return false;
423 : }
424 17 : } else if (JSID_IS_ATOM(id, cx->names().length)) {
425 17 : argsobj.markLengthOverridden();
426 0 : } else if (JSID_IS_ATOM(id, cx->names().callee)) {
427 0 : argsobj.as<MappedArgumentsObject>().markCalleeOverridden();
428 0 : } else if (JSID_IS_SYMBOL(id) && JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().iterator) {
429 0 : argsobj.markIteratorOverridden();
430 : }
431 17 : return result.succeed();
432 : }
433 :
434 : /* static */ bool
435 89 : ArgumentsObject::obj_mayResolve(const JSAtomState& names, jsid id, JSObject*)
436 : {
437 : // Arguments might resolve indexes or Symbol.iterator.
438 89 : if (!JSID_IS_ATOM(id))
439 68 : return true;
440 :
441 21 : JSAtom* atom = JSID_TO_ATOM(id);
442 : uint32_t index;
443 21 : if (atom->isIndex(&index))
444 0 : return true;
445 :
446 21 : return atom == names.length || atom == names.callee;
447 : }
448 :
449 : static bool
450 133 : MappedArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
451 : {
452 133 : MappedArgumentsObject& argsobj = obj->as<MappedArgumentsObject>();
453 133 : if (JSID_IS_INT(id)) {
454 : /*
455 : * arg can exceed the number of arguments if a script changed the
456 : * prototype to point to another Arguments object with a bigger argc.
457 : */
458 113 : unsigned arg = unsigned(JSID_TO_INT(id));
459 113 : if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg))
460 113 : vp.set(argsobj.element(arg));
461 20 : } else if (JSID_IS_ATOM(id, cx->names().length)) {
462 20 : if (!argsobj.hasOverriddenLength())
463 20 : vp.setInt32(argsobj.initialLength());
464 : } else {
465 0 : MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().callee));
466 0 : if (!argsobj.hasOverriddenCallee()) {
467 0 : RootedFunction callee(cx, &argsobj.callee());
468 0 : if (callee->isAsync())
469 0 : vp.setObject(*GetWrappedAsyncFunction(callee));
470 : else
471 0 : vp.setObject(*callee);
472 : }
473 : }
474 133 : return true;
475 : }
476 :
477 : static bool
478 66 : MappedArgSetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
479 : ObjectOpResult& result)
480 : {
481 66 : if (!obj->is<MappedArgumentsObject>())
482 0 : return result.succeed();
483 66 : Handle<MappedArgumentsObject*> argsobj = obj.as<MappedArgumentsObject>();
484 :
485 132 : Rooted<PropertyDescriptor> desc(cx);
486 66 : if (!GetOwnPropertyDescriptor(cx, argsobj, id, &desc))
487 0 : return false;
488 66 : MOZ_ASSERT(desc.object());
489 66 : unsigned attrs = desc.attributes();
490 66 : MOZ_ASSERT(!(attrs & JSPROP_READONLY));
491 66 : attrs &= (JSPROP_ENUMERATE | JSPROP_PERMANENT); /* only valid attributes */
492 :
493 132 : RootedFunction callee(cx, &argsobj->callee());
494 132 : RootedScript script(cx, JSFunction::getOrCreateScript(cx, callee));
495 66 : if (!script)
496 0 : return false;
497 :
498 66 : if (JSID_IS_INT(id)) {
499 49 : unsigned arg = unsigned(JSID_TO_INT(id));
500 49 : if (arg < argsobj->initialLength() && !argsobj->isElementDeleted(arg)) {
501 49 : argsobj->setElement(cx, arg, vp);
502 49 : if (arg < script->functionNonDelazifying()->nargs())
503 0 : TypeScript::SetArgument(cx, script, arg, vp);
504 49 : return result.succeed();
505 : }
506 : } else {
507 17 : MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().length) || JSID_IS_ATOM(id, cx->names().callee));
508 : }
509 :
510 : /*
511 : * For simplicity we use delete/define to replace the property with a
512 : * simple data property. Note that we rely on ArgumentsObject::obj_delProperty
513 : * to set the corresponding override-bit.
514 : * Note also that we must define the property instead of setting it in case
515 : * the user has changed the prototype to an object that has a setter for
516 : * this id.
517 : */
518 17 : ObjectOpResult ignored;
519 102 : return NativeDeleteProperty(cx, argsobj, id, ignored) &&
520 68 : NativeDefineProperty(cx, argsobj, id, vp, nullptr, nullptr, attrs, result);
521 : }
522 :
523 : static bool
524 0 : DefineArgumentsIterator(JSContext* cx, Handle<ArgumentsObject*> argsobj)
525 : {
526 0 : RootedId iteratorId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator));
527 0 : HandlePropertyName shName = cx->names().ArrayValues;
528 0 : RootedAtom name(cx, cx->names().values);
529 0 : RootedValue val(cx);
530 0 : if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), shName, name, 0, &val))
531 0 : return false;
532 0 : return NativeDefineProperty(cx, argsobj, iteratorId, val, nullptr, nullptr, JSPROP_RESOLVING);
533 : }
534 :
535 : /* static */ bool
536 17 : ArgumentsObject::reifyLength(JSContext* cx, Handle<ArgumentsObject*> obj)
537 : {
538 17 : if (obj->hasOverriddenLength())
539 17 : return true;
540 :
541 0 : RootedId id(cx, NameToId(cx->names().length));
542 0 : RootedValue val(cx, Int32Value(obj->initialLength()));
543 0 : if (!NativeDefineProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_RESOLVING))
544 0 : return false;
545 :
546 0 : obj->markLengthOverridden();
547 0 : return true;
548 : }
549 :
550 : /* static */ bool
551 0 : ArgumentsObject::reifyIterator(JSContext* cx, Handle<ArgumentsObject*> obj)
552 : {
553 0 : if (obj->hasOverriddenIterator())
554 0 : return true;
555 :
556 0 : if (!DefineArgumentsIterator(cx, obj))
557 0 : return false;
558 :
559 0 : obj->markIteratorOverridden();
560 0 : return true;
561 : }
562 :
563 : /* static */ bool
564 134 : MappedArgumentsObject::obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
565 : {
566 268 : Rooted<MappedArgumentsObject*> argsobj(cx, &obj->as<MappedArgumentsObject>());
567 :
568 134 : if (JSID_IS_SYMBOL(id) && JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().iterator) {
569 0 : if (argsobj->hasOverriddenIterator())
570 0 : return true;
571 :
572 0 : if (!DefineArgumentsIterator(cx, argsobj))
573 0 : return false;
574 0 : *resolvedp = true;
575 0 : return true;
576 : }
577 :
578 134 : unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE | JSPROP_RESOLVING;
579 134 : if (JSID_IS_INT(id)) {
580 97 : uint32_t arg = uint32_t(JSID_TO_INT(id));
581 97 : if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg))
582 34 : return true;
583 :
584 63 : attrs |= JSPROP_ENUMERATE;
585 37 : } else if (JSID_IS_ATOM(id, cx->names().length)) {
586 37 : if (argsobj->hasOverriddenLength())
587 17 : return true;
588 : } else {
589 0 : if (!JSID_IS_ATOM(id, cx->names().callee))
590 0 : return true;
591 :
592 0 : if (argsobj->hasOverriddenCallee())
593 0 : return true;
594 : }
595 :
596 83 : if (!NativeDefineProperty(cx, argsobj, id, UndefinedHandleValue,
597 : MappedArgGetter, MappedArgSetter, attrs))
598 : {
599 0 : return false;
600 : }
601 :
602 83 : *resolvedp = true;
603 83 : return true;
604 : }
605 :
606 : /* static */ bool
607 0 : MappedArgumentsObject::obj_enumerate(JSContext* cx, HandleObject obj)
608 : {
609 0 : Rooted<MappedArgumentsObject*> argsobj(cx, &obj->as<MappedArgumentsObject>());
610 :
611 0 : RootedId id(cx);
612 : bool found;
613 :
614 : // Trigger reflection.
615 0 : id = NameToId(cx->names().length);
616 0 : if (!HasOwnProperty(cx, argsobj, id, &found))
617 0 : return false;
618 :
619 0 : id = NameToId(cx->names().callee);
620 0 : if (!HasOwnProperty(cx, argsobj, id, &found))
621 0 : return false;
622 :
623 0 : id = SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator);
624 0 : if (!HasOwnProperty(cx, argsobj, id, &found))
625 0 : return false;
626 :
627 0 : for (unsigned i = 0; i < argsobj->initialLength(); i++) {
628 0 : id = INT_TO_JSID(i);
629 0 : if (!HasOwnProperty(cx, argsobj, id, &found))
630 0 : return false;
631 : }
632 :
633 0 : return true;
634 : }
635 :
636 : // ES 2017 draft 9.4.4.2
637 : /* static */ bool
638 17 : MappedArgumentsObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
639 : Handle<PropertyDescriptor> desc, ObjectOpResult& result)
640 : {
641 : // Step 1.
642 34 : Rooted<MappedArgumentsObject*> argsobj(cx, &obj->as<MappedArgumentsObject>());
643 :
644 : // Steps 2-3.
645 17 : bool isMapped = false;
646 17 : if (JSID_IS_INT(id)) {
647 17 : unsigned arg = unsigned(JSID_TO_INT(id));
648 17 : isMapped = arg < argsobj->initialLength() && !argsobj->isElementDeleted(arg);
649 : }
650 :
651 : // Step 4.
652 34 : Rooted<PropertyDescriptor> newArgDesc(cx, desc);
653 17 : if (!desc.isAccessorDescriptor() && isMapped) {
654 : // In this case the live mapping is supposed to keep working,
655 : // we have to pass along the Getter/Setter otherwise they are overwritten.
656 0 : newArgDesc.setGetter(MappedArgGetter);
657 0 : newArgDesc.setSetter(MappedArgSetter);
658 : }
659 :
660 : // Steps 5-6. NativeDefineProperty will lookup [[Value]] for us.
661 17 : if (!NativeDefineProperty(cx, obj.as<NativeObject>(), id, newArgDesc, result))
662 0 : return false;
663 : // Step 7.
664 17 : if (!result.ok())
665 0 : return true;
666 :
667 : // Step 8.
668 17 : if (isMapped) {
669 0 : unsigned arg = unsigned(JSID_TO_INT(id));
670 0 : if (desc.isAccessorDescriptor()) {
671 0 : if (!argsobj->markElementDeleted(cx, arg))
672 0 : return false;
673 : } else {
674 0 : if (desc.hasValue()) {
675 0 : RootedFunction callee(cx, &argsobj->callee());
676 0 : RootedScript script(cx, JSFunction::getOrCreateScript(cx, callee));
677 0 : if (!script)
678 0 : return false;
679 0 : argsobj->setElement(cx, arg, desc.value());
680 0 : if (arg < script->functionNonDelazifying()->nargs())
681 0 : TypeScript::SetArgument(cx, script, arg, desc.value());
682 : }
683 0 : if (desc.hasWritable() && !desc.writable()) {
684 0 : if (!argsobj->markElementDeleted(cx, arg))
685 0 : return false;
686 : }
687 : }
688 : }
689 :
690 : // Step 9.
691 17 : return result.succeed();
692 : }
693 :
694 : static bool
695 6 : UnmappedArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
696 : {
697 6 : UnmappedArgumentsObject& argsobj = obj->as<UnmappedArgumentsObject>();
698 :
699 6 : if (JSID_IS_INT(id)) {
700 : /*
701 : * arg can exceed the number of arguments if a script changed the
702 : * prototype to point to another Arguments object with a bigger argc.
703 : */
704 5 : unsigned arg = unsigned(JSID_TO_INT(id));
705 5 : if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg))
706 5 : vp.set(argsobj.element(arg));
707 : } else {
708 1 : MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().length));
709 1 : if (!argsobj.hasOverriddenLength())
710 1 : vp.setInt32(argsobj.initialLength());
711 : }
712 6 : return true;
713 : }
714 :
715 : static bool
716 0 : UnmappedArgSetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
717 : ObjectOpResult& result)
718 : {
719 0 : if (!obj->is<UnmappedArgumentsObject>())
720 0 : return result.succeed();
721 0 : Handle<UnmappedArgumentsObject*> argsobj = obj.as<UnmappedArgumentsObject>();
722 :
723 0 : Rooted<PropertyDescriptor> desc(cx);
724 0 : if (!GetOwnPropertyDescriptor(cx, argsobj, id, &desc))
725 0 : return false;
726 0 : MOZ_ASSERT(desc.object());
727 0 : unsigned attrs = desc.attributes();
728 0 : MOZ_ASSERT(!(attrs & JSPROP_READONLY));
729 0 : attrs &= (JSPROP_ENUMERATE | JSPROP_PERMANENT); /* only valid attributes */
730 :
731 0 : if (JSID_IS_INT(id)) {
732 0 : unsigned arg = unsigned(JSID_TO_INT(id));
733 0 : if (arg < argsobj->initialLength()) {
734 0 : argsobj->setElement(cx, arg, vp);
735 0 : return result.succeed();
736 : }
737 : } else {
738 0 : MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().length));
739 : }
740 :
741 : /*
742 : * For simplicity we use delete/define to replace the property with a
743 : * simple data property. Note that we rely on ArgumentsObject::obj_delProperty
744 : * to set the corresponding override-bit.
745 : */
746 0 : ObjectOpResult ignored;
747 0 : return NativeDeleteProperty(cx, argsobj, id, ignored) &&
748 0 : NativeDefineProperty(cx, argsobj, id, vp, nullptr, nullptr, attrs, result);
749 : }
750 :
751 : /* static */ bool
752 6 : UnmappedArgumentsObject::obj_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
753 : {
754 12 : Rooted<UnmappedArgumentsObject*> argsobj(cx, &obj->as<UnmappedArgumentsObject>());
755 :
756 6 : if (JSID_IS_SYMBOL(id) && JSID_TO_SYMBOL(id) == cx->wellKnownSymbols().iterator) {
757 0 : if (argsobj->hasOverriddenIterator())
758 0 : return true;
759 :
760 0 : if (!DefineArgumentsIterator(cx, argsobj))
761 0 : return false;
762 0 : *resolvedp = true;
763 0 : return true;
764 : }
765 :
766 6 : unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
767 6 : GetterOp getter = UnmappedArgGetter;
768 6 : SetterOp setter = UnmappedArgSetter;
769 :
770 6 : if (JSID_IS_INT(id)) {
771 5 : uint32_t arg = uint32_t(JSID_TO_INT(id));
772 5 : if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg))
773 0 : return true;
774 :
775 5 : attrs |= JSPROP_ENUMERATE;
776 1 : } else if (JSID_IS_ATOM(id, cx->names().length)) {
777 1 : if (argsobj->hasOverriddenLength())
778 0 : return true;
779 : } else {
780 0 : if (!JSID_IS_ATOM(id, cx->names().callee))
781 0 : return true;
782 :
783 0 : attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
784 0 : getter = CastAsGetterOp(argsobj->global().getThrowTypeError());
785 0 : setter = CastAsSetterOp(argsobj->global().getThrowTypeError());
786 : }
787 :
788 6 : attrs |= JSPROP_RESOLVING;
789 6 : if (!NativeDefineProperty(cx, argsobj, id, UndefinedHandleValue, getter, setter, attrs))
790 0 : return false;
791 :
792 6 : *resolvedp = true;
793 6 : return true;
794 : }
795 :
796 : /* static */ bool
797 0 : UnmappedArgumentsObject::obj_enumerate(JSContext* cx, HandleObject obj)
798 : {
799 0 : Rooted<UnmappedArgumentsObject*> argsobj(cx, &obj->as<UnmappedArgumentsObject>());
800 :
801 0 : RootedId id(cx);
802 : bool found;
803 :
804 : // Trigger reflection.
805 0 : id = NameToId(cx->names().length);
806 0 : if (!HasOwnProperty(cx, argsobj, id, &found))
807 0 : return false;
808 :
809 0 : id = NameToId(cx->names().callee);
810 0 : if (!HasOwnProperty(cx, argsobj, id, &found))
811 0 : return false;
812 :
813 0 : id = SYMBOL_TO_JSID(cx->wellKnownSymbols().iterator);
814 0 : if (!HasOwnProperty(cx, argsobj, id, &found))
815 0 : return false;
816 :
817 0 : for (unsigned i = 0; i < argsobj->initialLength(); i++) {
818 0 : id = INT_TO_JSID(i);
819 0 : if (!HasOwnProperty(cx, argsobj, id, &found))
820 0 : return false;
821 : }
822 :
823 0 : return true;
824 : }
825 :
826 : void
827 0 : ArgumentsObject::finalize(FreeOp* fop, JSObject* obj)
828 : {
829 0 : MOZ_ASSERT(!IsInsideNursery(obj));
830 0 : if (obj->as<ArgumentsObject>().data()) {
831 0 : fop->free_(obj->as<ArgumentsObject>().maybeRareData());
832 0 : fop->free_(obj->as<ArgumentsObject>().data());
833 : }
834 0 : }
835 :
836 : void
837 3 : ArgumentsObject::trace(JSTracer* trc, JSObject* obj)
838 : {
839 3 : ArgumentsObject& argsobj = obj->as<ArgumentsObject>();
840 3 : if (ArgumentsData* data = argsobj.data()) // Template objects have no ArgumentsData.
841 3 : TraceRange(trc, data->numArgs, data->begin(), js_arguments_str);
842 3 : }
843 :
844 : /* static */ size_t
845 3 : ArgumentsObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src)
846 : {
847 3 : ArgumentsObject* ndst = &dst->as<ArgumentsObject>();
848 3 : ArgumentsObject* nsrc = &src->as<ArgumentsObject>();
849 3 : MOZ_ASSERT(ndst->data() == nsrc->data());
850 :
851 3 : Nursery& nursery = dst->zone()->group()->nursery();
852 :
853 3 : size_t nbytesTotal = 0;
854 3 : if (!nursery.isInside(nsrc->data())) {
855 0 : nursery.removeMallocedBuffer(nsrc->data());
856 : } else {
857 6 : AutoEnterOOMUnsafeRegion oomUnsafe;
858 3 : uint32_t nbytes = ArgumentsData::bytesRequired(nsrc->data()->numArgs);
859 3 : uint8_t* data = nsrc->zone()->pod_malloc<uint8_t>(nbytes);
860 3 : if (!data)
861 0 : oomUnsafe.crash("Failed to allocate ArgumentsObject data while tenuring.");
862 3 : ndst->initFixedSlot(DATA_SLOT, PrivateValue(data));
863 :
864 3 : mozilla::PodCopy(data, reinterpret_cast<uint8_t*>(nsrc->data()), nbytes);
865 3 : nbytesTotal += nbytes;
866 : }
867 :
868 3 : if (RareArgumentsData* srcRareData = nsrc->maybeRareData()) {
869 0 : if (!nursery.isInside(srcRareData)) {
870 0 : nursery.removeMallocedBuffer(srcRareData);
871 : } else {
872 0 : AutoEnterOOMUnsafeRegion oomUnsafe;
873 0 : uint32_t nbytes = RareArgumentsData::bytesRequired(nsrc->initialLength());
874 0 : uint8_t* dstRareData = nsrc->zone()->pod_malloc<uint8_t>(nbytes);
875 0 : if (!dstRareData)
876 0 : oomUnsafe.crash("Failed to allocate RareArgumentsData data while tenuring.");
877 0 : ndst->data()->rareData = (RareArgumentsData*)dstRareData;
878 :
879 0 : mozilla::PodCopy(dstRareData, reinterpret_cast<uint8_t*>(srcRareData), nbytes);
880 0 : nbytesTotal += nbytes;
881 : }
882 : }
883 :
884 3 : return nbytesTotal;
885 : }
886 :
887 : /*
888 : * The classes below collaborate to lazily reflect and synchronize actual
889 : * argument values, argument count, and callee function object stored in a
890 : * stack frame with their corresponding property values in the frame's
891 : * arguments object.
892 : */
893 : const ClassOps MappedArgumentsObject::classOps_ = {
894 : nullptr, /* addProperty */
895 : ArgumentsObject::obj_delProperty,
896 : nullptr, /* getProperty */
897 : nullptr, /* setProperty */
898 : MappedArgumentsObject::obj_enumerate,
899 : nullptr, /* newEnumerate */
900 : MappedArgumentsObject::obj_resolve,
901 : ArgumentsObject::obj_mayResolve,
902 : ArgumentsObject::finalize,
903 : nullptr, /* call */
904 : nullptr, /* hasInstance */
905 : nullptr, /* construct */
906 : ArgumentsObject::trace
907 : };
908 :
909 : const ObjectOps MappedArgumentsObject::objectOps_ = {
910 : nullptr, /* lookupProperty */
911 : MappedArgumentsObject::obj_defineProperty
912 : };
913 :
914 : const Class MappedArgumentsObject::class_ = {
915 : "Arguments",
916 : JSCLASS_DELAY_METADATA_BUILDER |
917 : JSCLASS_HAS_RESERVED_SLOTS(MappedArgumentsObject::RESERVED_SLOTS) |
918 : JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
919 : JSCLASS_SKIP_NURSERY_FINALIZE |
920 : JSCLASS_BACKGROUND_FINALIZE,
921 : &MappedArgumentsObject::classOps_,
922 : nullptr,
923 : nullptr,
924 : &MappedArgumentsObject::objectOps_
925 : };
926 :
927 : /*
928 : * Unmapped arguments is significantly less magical than mapped arguments, so
929 : * it is represented by a different class while sharing some functionality.
930 : */
931 : const ClassOps UnmappedArgumentsObject::classOps_ = {
932 : nullptr, /* addProperty */
933 : ArgumentsObject::obj_delProperty,
934 : nullptr, /* getProperty */
935 : nullptr, /* setProperty */
936 : UnmappedArgumentsObject::obj_enumerate,
937 : nullptr, /* newEnumerate */
938 : UnmappedArgumentsObject::obj_resolve,
939 : ArgumentsObject::obj_mayResolve,
940 : ArgumentsObject::finalize,
941 : nullptr, /* call */
942 : nullptr, /* hasInstance */
943 : nullptr, /* construct */
944 : ArgumentsObject::trace
945 : };
946 :
947 : const Class UnmappedArgumentsObject::class_ = {
948 : "Arguments",
949 : JSCLASS_DELAY_METADATA_BUILDER |
950 : JSCLASS_HAS_RESERVED_SLOTS(UnmappedArgumentsObject::RESERVED_SLOTS) |
951 : JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
952 : JSCLASS_SKIP_NURSERY_FINALIZE |
953 : JSCLASS_BACKGROUND_FINALIZE,
954 : &UnmappedArgumentsObject::classOps_
955 : };
|