1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
7 #include "vm/ArgumentsObject-inl.h"
9 #include "mozilla/Maybe.h"
10 #include "mozilla/PodOperations.h"
14 #include "gc/GCContext.h"
15 #include "jit/CalleeToken.h"
16 #include "jit/JitFrames.h"
17 #include "util/BitArray.h"
18 #include "vm/GlobalObject.h"
21 #include "gc/Nursery-inl.h"
22 #include "vm/FrameIter-inl.h" // js::FrameIter::unaliasedForEachActual
23 #include "vm/NativeObject-inl.h"
24 #include "vm/Stack-inl.h"
29 size_t RareArgumentsData::bytesRequired(size_t numActuals
) {
30 size_t extraBytes
= NumWordsForBitArrayOfLength(numActuals
) * sizeof(size_t);
31 return offsetof(RareArgumentsData
, deletedBits_
) + extraBytes
;
35 RareArgumentsData
* RareArgumentsData::create(JSContext
* cx
,
36 ArgumentsObject
* obj
) {
37 size_t bytes
= RareArgumentsData::bytesRequired(obj
->initialLength());
39 uint8_t* data
= AllocateCellBuffer
<uint8_t>(cx
, obj
, bytes
);
44 mozilla::PodZero(data
, bytes
);
46 AddCellMemory(obj
, bytes
, MemoryUse::RareArgumentsData
);
48 return new (data
) RareArgumentsData();
51 ArgumentsData::ArgumentsData(uint32_t numArgs
) : args(numArgs
) {
52 // |args| must be the last field.
53 static_assert(offsetof(ArgumentsData
, args
) + sizeof(args
) ==
54 sizeof(ArgumentsData
));
57 bool ArgumentsObject::createRareData(JSContext
* cx
) {
58 MOZ_ASSERT(!data()->rareData
);
60 RareArgumentsData
* rareData
= RareArgumentsData::create(cx
, this);
65 data()->rareData
= rareData
;
66 markElementOverridden();
70 bool ArgumentsObject::markElementDeleted(JSContext
* cx
, uint32_t i
) {
71 RareArgumentsData
* data
= getOrCreateRareData(cx
);
76 data
->markElementDeleted(initialLength(), i
);
81 void ArgumentsObject::MaybeForwardToCallObject(AbstractFramePtr frame
,
83 ArgumentsData
* data
) {
84 JSScript
* script
= frame
.script();
85 if (frame
.callee()->needsCallObject() && script
->argsObjAliasesFormals()) {
86 obj
->initFixedSlot(MAYBE_CALL_SLOT
, ObjectValue(frame
.callObj()));
87 for (PositionalFormalParameterIter
fi(script
); fi
; fi
++) {
88 if (fi
.closedOver()) {
89 data
->args
.setElement(obj
, fi
.argumentSlot(),
90 MagicEnvSlotValue(fi
.location().slot()));
91 obj
->markArgumentForwarded();
98 void ArgumentsObject::MaybeForwardToCallObject(JSFunction
* callee
,
100 ArgumentsObject
* obj
,
101 ArgumentsData
* data
) {
102 JSScript
* script
= callee
->nonLazyScript();
103 if (callee
->needsCallObject() && script
->argsObjAliasesFormals()) {
104 MOZ_ASSERT(callObj
&& callObj
->is
<CallObject
>());
105 obj
->initFixedSlot(MAYBE_CALL_SLOT
, ObjectValue(*callObj
));
106 for (PositionalFormalParameterIter
fi(script
); fi
; fi
++) {
107 if (fi
.closedOver()) {
108 data
->args
.setElement(obj
, fi
.argumentSlot(),
109 MagicEnvSlotValue(fi
.location().slot()));
110 obj
->markArgumentForwarded();
116 struct CopyFrameArgs
{
117 AbstractFramePtr frame_
;
119 explicit CopyFrameArgs(AbstractFramePtr frame
) : frame_(frame
) {}
121 void copyActualArgs(ArgumentsObject
* owner
, GCOwnedArray
<Value
>& args
,
122 unsigned numActuals
) const {
123 MOZ_ASSERT_IF(frame_
.isInterpreterFrame(),
124 !frame_
.asInterpreterFrame()->runningInJit());
127 Value
* src
= frame_
.argv();
128 Value
* end
= src
+ numActuals
;
129 args
.withOwner(owner
, [&](auto& args
) {
130 auto* dst
= args
.begin();
132 (dst
++)->init(*src
++);
138 * If a call object exists and the arguments object aliases formals, the
139 * call object is the canonical location for formals.
141 void maybeForwardToCallObject(ArgumentsObject
* obj
, ArgumentsData
* data
) {
142 ArgumentsObject::MaybeForwardToCallObject(frame_
, obj
, data
);
146 struct CopyJitFrameArgs
{
147 jit::JitFrameLayout
* frame_
;
148 HandleObject callObj_
;
150 CopyJitFrameArgs(jit::JitFrameLayout
* frame
, HandleObject callObj
)
151 : frame_(frame
), callObj_(callObj
) {}
153 void copyActualArgs(ArgumentsObject
* owner
, GCOwnedArray
<Value
>& args
,
154 unsigned numActuals
) const {
155 MOZ_ASSERT(frame_
->numActualArgs() == numActuals
);
157 Value
* src
= frame_
->actualArgs();
158 Value
* end
= src
+ numActuals
;
159 args
.withOwner(owner
, [&](auto& args
) {
160 auto* dst
= args
.begin();
162 (dst
++)->init(*src
++);
168 * If a call object exists and the arguments object aliases formals, the
169 * call object is the canonical location for formals.
171 void maybeForwardToCallObject(ArgumentsObject
* obj
, ArgumentsData
* data
) {
172 JSFunction
* callee
= jit::CalleeTokenToFunction(frame_
->calleeToken());
173 ArgumentsObject::MaybeForwardToCallObject(callee
, callObj_
, obj
, data
);
177 struct CopyScriptFrameIterArgs
{
178 ScriptFrameIter
& iter_
;
179 RootedValueVector actualArgs_
;
181 explicit CopyScriptFrameIterArgs(JSContext
* cx
, ScriptFrameIter
& iter
)
182 : iter_(iter
), actualArgs_(cx
) {}
184 // Used to copy arguments to actualArgs_ to simplify copyArgs and
185 // ArgumentsObject allocation.
186 [[nodiscard
]] bool init(JSContext
* cx
) {
187 unsigned numActuals
= iter_
.numActualArgs();
188 if (!actualArgs_
.reserve(numActuals
)) {
192 // Append actual arguments.
193 iter_
.unaliasedForEachActual(
194 cx
, [this](const Value
& v
) { actualArgs_
.infallibleAppend(v
); });
195 MOZ_RELEASE_ASSERT(actualArgs_
.length() == numActuals
);
199 void copyActualArgs(ArgumentsObject
* owner
, GCOwnedArray
<Value
>& args
,
200 unsigned numActuals
) const {
201 MOZ_ASSERT(actualArgs_
.length() == numActuals
);
203 args
.withOwner(owner
, [&](auto& args
) {
204 auto* dst
= args
.begin();
205 for (Value v
: actualArgs_
) {
212 * Ion frames are copying every argument onto the stack, other locations are
215 void maybeForwardToCallObject(ArgumentsObject
* obj
, ArgumentsData
* data
) {
216 if (!iter_
.isIon()) {
217 ArgumentsObject::MaybeForwardToCallObject(iter_
.abstractFramePtr(), obj
,
223 struct CopyInlinedArgs
{
224 HandleValueArray args_
;
225 HandleObject callObj_
;
226 HandleFunction callee_
;
228 CopyInlinedArgs(HandleValueArray args
, HandleObject callObj
,
229 HandleFunction callee
)
230 : args_(args
), callObj_(callObj
), callee_(callee
) {}
232 void copyActualArgs(ArgumentsObject
* owner
, GCOwnedArray
<Value
>& args
,
233 unsigned numActuals
) const {
234 MOZ_ASSERT(numActuals
<= args_
.length());
236 args
.withOwner(owner
, [&](auto& args
) {
237 auto* dst
= args
.begin();
238 for (uint32_t i
= 0; i
< numActuals
; i
++) {
239 (dst
++)->init(args_
[i
]);
245 * If a call object exists and the arguments object aliases formals, the
246 * call object is the canonical location for formals.
248 void maybeForwardToCallObject(ArgumentsObject
* obj
, ArgumentsData
* data
) {
249 ArgumentsObject::MaybeForwardToCallObject(callee_
, callObj_
, obj
, data
);
253 ArgumentsObject
* ArgumentsObject::createTemplateObject(JSContext
* cx
,
255 const JSClass
* clasp
= mapped
? &MappedArgumentsObject::class_
256 : &UnmappedArgumentsObject::class_
;
258 RootedObject
proto(cx
, &cx
->global()->getObjectPrototype());
260 constexpr ObjectFlags objectFlags
= {ObjectFlag::Indexed
};
261 Rooted
<SharedShape
*> shape(cx
, SharedShape::getInitialShape(
262 cx
, clasp
, cx
->realm(), TaggedProto(proto
),
263 FINALIZE_KIND
, objectFlags
));
268 AutoSetNewObjectMetadata
metadata(cx
);
269 auto* obj
= NativeObject::create
<ArgumentsObject
>(cx
, FINALIZE_KIND
,
270 gc::Heap::Tenured
, shape
);
275 obj
->initFixedSlot(ArgumentsObject::DATA_SLOT
, PrivateValue(nullptr));
279 ArgumentsObject
* GlobalObject::maybeArgumentsTemplateObject(bool mapped
) const {
280 return mapped
? data().mappedArgumentsTemplate
281 : data().unmappedArgumentsTemplate
;
285 ArgumentsObject
* GlobalObject::getOrCreateArgumentsTemplateObject(JSContext
* cx
,
287 GlobalObjectData
& data
= cx
->global()->data();
288 HeapPtr
<ArgumentsObject
*>& obj
=
289 mapped
? data
.mappedArgumentsTemplate
: data
.unmappedArgumentsTemplate
;
291 ArgumentsObject
* templateObj
= obj
;
296 templateObj
= ArgumentsObject::createTemplateObject(cx
, mapped
);
301 obj
.init(templateObj
);
305 template <typename CopyArgs
>
307 ArgumentsObject
* ArgumentsObject::create(JSContext
* cx
, HandleFunction callee
,
308 unsigned numActuals
, CopyArgs
& copy
) {
309 // Self-hosted code should use the more efficient ArgumentsLength and
310 // GetArgument intrinsics instead of `arguments`.
311 MOZ_ASSERT(!callee
->isSelfHostedBuiltin());
313 bool mapped
= callee
->baseScript()->hasMappedArgsObj();
314 ArgumentsObject
* templateObj
=
315 GlobalObject::getOrCreateArgumentsTemplateObject(cx
, mapped
);
320 Rooted
<SharedShape
*> shape(cx
, templateObj
->sharedShape());
322 unsigned numFormals
= callee
->nargs();
323 unsigned numArgs
= std::max(numActuals
, numFormals
);
324 unsigned numBytes
= ArgumentsData::bytesRequired(numArgs
);
326 AutoSetNewObjectMetadata
metadata(cx
);
327 auto* obj
= NativeObject::create
<ArgumentsObject
>(cx
, FINALIZE_KIND
,
328 gc::Heap::Default
, shape
);
333 ArgumentsData
* data
= reinterpret_cast<ArgumentsData
*>(
334 AllocateCellBuffer
<uint8_t>(cx
, obj
, numBytes
));
336 // Make the object safe for GC.
337 obj
->initFixedSlot(DATA_SLOT
, PrivateValue(nullptr));
341 new (data
) ArgumentsData(numArgs
);
343 InitReservedSlot(obj
, DATA_SLOT
, data
, numBytes
, MemoryUse::ArgumentsData
);
344 obj
->initFixedSlot(CALLEE_SLOT
, ObjectValue(*callee
));
345 obj
->initFixedSlot(INITIAL_LENGTH_SLOT
,
346 Int32Value(numActuals
<< PACKED_BITS_COUNT
));
348 // Copy [0, numActuals) into data->args.
349 copy
.copyActualArgs(obj
, data
->args
, numActuals
);
351 // Fill in missing arguments with |undefined|.
352 data
->args
.withOwner(obj
, [&](auto& args
) {
353 for (size_t i
= numActuals
; i
< numArgs
; i
++) {
354 args
[i
].init(UndefinedValue());
358 copy
.maybeForwardToCallObject(obj
, data
);
360 MOZ_ASSERT(obj
->initialLength() == numActuals
);
361 MOZ_ASSERT(!obj
->hasOverriddenLength());
365 ArgumentsObject
* ArgumentsObject::createExpected(JSContext
* cx
,
366 AbstractFramePtr frame
) {
367 MOZ_ASSERT(frame
.script()->needsArgsObj());
368 RootedFunction
callee(cx
, frame
.callee());
369 CopyFrameArgs
copy(frame
);
370 ArgumentsObject
* argsobj
= create(cx
, callee
, frame
.numActualArgs(), copy
);
375 frame
.initArgsObj(*argsobj
);
379 ArgumentsObject
* ArgumentsObject::createUnexpected(JSContext
* cx
,
380 ScriptFrameIter
& iter
) {
381 RootedFunction
callee(cx
, iter
.callee(cx
));
382 CopyScriptFrameIterArgs
copy(cx
, iter
);
383 if (!copy
.init(cx
)) {
386 return create(cx
, callee
, iter
.numActualArgs(), copy
);
389 ArgumentsObject
* ArgumentsObject::createUnexpected(JSContext
* cx
,
390 AbstractFramePtr frame
) {
391 RootedFunction
callee(cx
, frame
.callee());
392 CopyFrameArgs
copy(frame
);
393 return create(cx
, callee
, frame
.numActualArgs(), copy
);
396 ArgumentsObject
* ArgumentsObject::createForIon(JSContext
* cx
,
397 jit::JitFrameLayout
* frame
,
398 HandleObject scopeChain
) {
399 jit::CalleeToken token
= frame
->calleeToken();
400 MOZ_ASSERT(jit::CalleeTokenIsFunction(token
));
401 RootedFunction
callee(cx
, jit::CalleeTokenToFunction(token
));
402 RootedObject
callObj(
403 cx
, scopeChain
->is
<CallObject
>() ? scopeChain
.get() : nullptr);
404 CopyJitFrameArgs
copy(frame
, callObj
);
405 return create(cx
, callee
, frame
->numActualArgs(), copy
);
409 ArgumentsObject
* ArgumentsObject::createFromValueArray(
410 JSContext
* cx
, HandleValueArray argsArray
, HandleFunction callee
,
411 HandleObject scopeChain
, uint32_t numActuals
) {
412 MOZ_ASSERT(numActuals
<= MaxInlinedArgs
);
413 RootedObject
callObj(
414 cx
, scopeChain
->is
<CallObject
>() ? scopeChain
.get() : nullptr);
415 CopyInlinedArgs
copy(argsArray
, callObj
, callee
);
416 return create(cx
, callee
, numActuals
, copy
);
420 ArgumentsObject
* ArgumentsObject::createForInlinedIon(JSContext
* cx
,
422 HandleFunction callee
,
423 HandleObject scopeChain
,
424 uint32_t numActuals
) {
425 RootedExternalValueArray
rootedArgs(cx
, numActuals
, args
);
426 HandleValueArray argsArray
=
427 HandleValueArray::fromMarkedLocation(numActuals
, args
);
429 return createFromValueArray(cx
, argsArray
, callee
, scopeChain
, numActuals
);
432 template <typename CopyArgs
>
434 ArgumentsObject
* ArgumentsObject::finishPure(
435 JSContext
* cx
, ArgumentsObject
* obj
, JSFunction
* callee
, JSObject
* callObj
,
436 unsigned numActuals
, CopyArgs
& copy
) {
437 unsigned numFormals
= callee
->nargs();
438 unsigned numArgs
= std::max(numActuals
, numFormals
);
439 unsigned numBytes
= ArgumentsData::bytesRequired(numArgs
);
441 ArgumentsData
* data
= reinterpret_cast<ArgumentsData
*>(
442 AllocateCellBuffer
<uint8_t>(cx
, obj
, numBytes
));
444 // Make the object safe for GC. Don't report OOM, the slow path will
445 // retry the allocation.
446 cx
->recoverFromOutOfMemory();
447 obj
->initFixedSlot(DATA_SLOT
, PrivateValue(nullptr));
451 new (data
) ArgumentsData(numArgs
);
453 obj
->initFixedSlot(INITIAL_LENGTH_SLOT
,
454 Int32Value(numActuals
<< PACKED_BITS_COUNT
));
455 obj
->initFixedSlot(DATA_SLOT
, PrivateValue(data
));
456 AddCellMemory(obj
, numBytes
, MemoryUse::ArgumentsData
);
457 obj
->initFixedSlot(MAYBE_CALL_SLOT
, UndefinedValue());
458 obj
->initFixedSlot(CALLEE_SLOT
, ObjectValue(*callee
));
460 copy
.copyActualArgs(obj
, data
->args
, numActuals
);
462 // Fill in missing arguments with |undefined|.
463 data
->args
.withOwner(obj
, [&](auto& args
) {
464 for (size_t i
= numActuals
; i
< numArgs
; i
++) {
465 args
[i
].init(UndefinedValue());
469 if (callObj
&& callee
->needsCallObject()) {
470 copy
.maybeForwardToCallObject(obj
, data
);
473 MOZ_ASSERT(obj
->initialLength() == numActuals
);
474 MOZ_ASSERT(!obj
->hasOverriddenLength());
479 ArgumentsObject
* ArgumentsObject::finishForIonPure(JSContext
* cx
,
480 jit::JitFrameLayout
* frame
,
481 JSObject
* scopeChain
,
482 ArgumentsObject
* obj
) {
483 // JIT code calls this directly (no callVM), because it's faster, so we're
484 // not allowed to GC in here.
485 AutoUnsafeCallWithABI unsafe
;
487 JSFunction
* callee
= jit::CalleeTokenToFunction(frame
->calleeToken());
488 RootedObject
callObj(cx
, scopeChain
->is
<CallObject
>() ? scopeChain
: nullptr);
489 CopyJitFrameArgs
copy(frame
, callObj
);
491 unsigned numActuals
= frame
->numActualArgs();
493 return finishPure(cx
, obj
, callee
, callObj
, numActuals
, copy
);
497 ArgumentsObject
* ArgumentsObject::finishInlineForIonPure(
498 JSContext
* cx
, JSObject
* rawCallObj
, JSFunction
* rawCallee
, Value
* args
,
499 uint32_t numActuals
, ArgumentsObject
* obj
) {
500 // JIT code calls this directly (no callVM), because it's faster, so we're
501 // not allowed to GC in here.
502 AutoUnsafeCallWithABI unsafe
;
504 MOZ_ASSERT(numActuals
<= MaxInlinedArgs
);
506 RootedObject
callObj(cx
, rawCallObj
);
507 RootedFunction
callee(cx
, rawCallee
);
508 RootedExternalValueArray
rootedArgs(cx
, numActuals
, args
);
509 HandleValueArray argsArray
=
510 HandleValueArray::fromMarkedLocation(numActuals
, args
);
512 CopyInlinedArgs
copy(argsArray
, callObj
, callee
);
514 return finishPure(cx
, obj
, callee
, callObj
, numActuals
, copy
);
518 bool ArgumentsObject::obj_delProperty(JSContext
* cx
, HandleObject obj
,
519 HandleId id
, ObjectOpResult
& result
) {
520 ArgumentsObject
& argsobj
= obj
->as
<ArgumentsObject
>();
522 unsigned arg
= unsigned(id
.toInt());
523 if (argsobj
.isElement(arg
)) {
524 if (!argsobj
.markElementDeleted(cx
, arg
)) {
528 } else if (id
.isAtom(cx
->names().length
)) {
529 argsobj
.markLengthOverridden();
530 } else if (id
.isAtom(cx
->names().callee
)) {
531 argsobj
.as
<MappedArgumentsObject
>().markCalleeOverridden();
532 } else if (id
.isWellKnownSymbol(JS::SymbolCode::iterator
)) {
533 argsobj
.markIteratorOverridden();
535 return result
.succeed();
539 bool ArgumentsObject::obj_mayResolve(const JSAtomState
& names
, jsid id
,
541 // Arguments might resolve indexes, Symbol.iterator, or length/callee.
543 JSAtom
* atom
= id
.toAtom();
544 return atom
->isIndex() || atom
== names
.length
|| atom
== names
.callee
;
547 return id
.isInt() || id
.isWellKnownSymbol(JS::SymbolCode::iterator
);
550 bool js::MappedArgGetter(JSContext
* cx
, HandleObject obj
, HandleId id
,
551 MutableHandleValue vp
) {
552 MappedArgumentsObject
& argsobj
= obj
->as
<MappedArgumentsObject
>();
555 * arg can exceed the number of arguments if a script changed the
556 * prototype to point to another Arguments object with a bigger argc.
558 unsigned arg
= unsigned(id
.toInt());
559 if (argsobj
.isElement(arg
)) {
560 vp
.set(argsobj
.element(arg
));
562 } else if (id
.isAtom(cx
->names().length
)) {
563 if (!argsobj
.hasOverriddenLength()) {
564 vp
.setInt32(argsobj
.initialLength());
567 MOZ_ASSERT(id
.isAtom(cx
->names().callee
));
568 if (!argsobj
.hasOverriddenCallee()) {
569 vp
.setObject(argsobj
.callee());
575 bool js::MappedArgSetter(JSContext
* cx
, HandleObject obj
, HandleId id
,
576 HandleValue v
, ObjectOpResult
& result
) {
577 Handle
<MappedArgumentsObject
*> argsobj
= obj
.as
<MappedArgumentsObject
>();
579 Rooted
<mozilla::Maybe
<PropertyDescriptor
>> desc(cx
);
580 if (!GetOwnPropertyDescriptor(cx
, argsobj
, id
, &desc
)) {
583 MOZ_ASSERT(desc
.isSome());
584 MOZ_ASSERT(desc
->isDataDescriptor());
585 MOZ_ASSERT(desc
->writable());
586 MOZ_ASSERT(!desc
->resolving());
589 unsigned arg
= unsigned(id
.toInt());
590 if (argsobj
->isElement(arg
)) {
591 argsobj
->setElement(arg
, v
);
592 return result
.succeed();
595 MOZ_ASSERT(id
.isAtom(cx
->names().length
) || id
.isAtom(cx
->names().callee
));
599 * For simplicity we use delete/define to replace the property with a
600 * simple data property. Note that we rely on ArgumentsObject::obj_delProperty
601 * to set the corresponding override-bit.
602 * Note also that we must define the property instead of setting it in case
603 * the user has changed the prototype to an object that has a setter for
606 Rooted
<PropertyDescriptor
> desc_(cx
, *desc
);
608 ObjectOpResult ignored
;
609 return NativeDeleteProperty(cx
, argsobj
, id
, ignored
) &&
610 NativeDefineProperty(cx
, argsobj
, id
, desc_
, result
);
614 bool ArgumentsObject::getArgumentsIterator(JSContext
* cx
,
615 MutableHandleValue val
) {
616 Handle
<PropertyName
*> shName
= cx
->names().dollar_ArrayValues_
;
617 Rooted
<JSAtom
*> name(cx
, cx
->names().values
);
618 return GlobalObject::getSelfHostedFunction(cx
, cx
->global(), shName
, name
, 0,
623 bool ArgumentsObject::reifyLength(JSContext
* cx
, Handle
<ArgumentsObject
*> obj
) {
624 if (obj
->hasOverriddenLength()) {
628 RootedId
id(cx
, NameToId(cx
->names().length
));
629 RootedValue
val(cx
, Int32Value(obj
->initialLength()));
630 if (!NativeDefineDataProperty(cx
, obj
, id
, val
, JSPROP_RESOLVING
)) {
634 obj
->markLengthOverridden();
639 bool ArgumentsObject::reifyIterator(JSContext
* cx
,
640 Handle
<ArgumentsObject
*> obj
) {
641 if (obj
->hasOverriddenIterator()) {
645 RootedId
iteratorId(cx
, PropertyKey::Symbol(cx
->wellKnownSymbols().iterator
));
647 if (!ArgumentsObject::getArgumentsIterator(cx
, &val
)) {
650 if (!NativeDefineDataProperty(cx
, obj
, iteratorId
, val
, JSPROP_RESOLVING
)) {
654 obj
->markIteratorOverridden();
659 bool MappedArgumentsObject::reifyCallee(JSContext
* cx
,
660 Handle
<MappedArgumentsObject
*> obj
) {
661 if (obj
->hasOverriddenCallee()) {
665 Rooted
<PropertyKey
> key(cx
, NameToId(cx
->names().callee
));
666 Rooted
<Value
> val(cx
, ObjectValue(obj
->callee()));
667 if (!NativeDefineDataProperty(cx
, obj
, key
, val
, JSPROP_RESOLVING
)) {
671 obj
->markCalleeOverridden();
675 static bool ResolveArgumentsProperty(JSContext
* cx
,
676 Handle
<ArgumentsObject
*> obj
, HandleId id
,
677 PropertyFlags flags
, bool* resolvedp
) {
678 MOZ_ASSERT(id
.isInt() || id
.isAtom(cx
->names().length
) ||
679 id
.isAtom(cx
->names().callee
));
680 MOZ_ASSERT(flags
.isCustomDataProperty());
682 if (!NativeObject::addCustomDataProperty(cx
, obj
, id
, flags
)) {
691 bool MappedArgumentsObject::obj_resolve(JSContext
* cx
, HandleObject obj
,
692 HandleId id
, bool* resolvedp
) {
693 Rooted
<MappedArgumentsObject
*> argsobj(cx
, &obj
->as
<MappedArgumentsObject
>());
695 if (id
.isWellKnownSymbol(JS::SymbolCode::iterator
)) {
696 if (argsobj
->hasOverriddenIterator()) {
700 if (!reifyIterator(cx
, argsobj
)) {
707 PropertyFlags flags
= {PropertyFlag::CustomDataProperty
,
708 PropertyFlag::Configurable
, PropertyFlag::Writable
};
710 uint32_t arg
= uint32_t(id
.toInt());
711 if (!argsobj
->isElement(arg
)) {
715 flags
.setFlag(PropertyFlag::Enumerable
);
716 } else if (id
.isAtom(cx
->names().length
)) {
717 if (argsobj
->hasOverriddenLength()) {
721 if (!id
.isAtom(cx
->names().callee
)) {
725 if (argsobj
->hasOverriddenCallee()) {
730 return ResolveArgumentsProperty(cx
, argsobj
, id
, flags
, resolvedp
);
734 bool MappedArgumentsObject::obj_enumerate(JSContext
* cx
, HandleObject obj
) {
735 Rooted
<MappedArgumentsObject
*> argsobj(cx
, &obj
->as
<MappedArgumentsObject
>());
740 // Trigger reflection.
741 id
= NameToId(cx
->names().length
);
742 if (!HasOwnProperty(cx
, argsobj
, id
, &found
)) {
746 id
= NameToId(cx
->names().callee
);
747 if (!HasOwnProperty(cx
, argsobj
, id
, &found
)) {
751 id
= PropertyKey::Symbol(cx
->wellKnownSymbols().iterator
);
752 if (!HasOwnProperty(cx
, argsobj
, id
, &found
)) {
756 for (unsigned i
= 0; i
< argsobj
->initialLength(); i
++) {
757 id
= PropertyKey::Int(i
);
758 if (!HasOwnProperty(cx
, argsobj
, id
, &found
)) {
766 static bool DefineMappedIndex(JSContext
* cx
, Handle
<MappedArgumentsObject
*> obj
,
768 MutableHandle
<PropertyDescriptor
> desc
,
769 ObjectOpResult
& result
) {
770 // The custom data properties (see MappedArgGetter, MappedArgSetter) have to
771 // be (re)defined manually because PropertyDescriptor and NativeDefineProperty
772 // don't support these special properties.
774 // This exists in order to let JS code change the configurable/enumerable
775 // attributes for these properties.
777 // Note: because this preserves the default mapped-arguments behavior, we
778 // don't need to mark elements as overridden or deleted.
780 MOZ_ASSERT(id
.isInt());
781 MOZ_ASSERT(obj
->isElement(id
.toInt()));
782 MOZ_ASSERT(!obj
->containsDenseElement(id
.toInt()));
784 MOZ_ASSERT(!desc
.isAccessorDescriptor());
786 // Mapped properties aren't used when defining a non-writable property.
787 MOZ_ASSERT(!desc
.hasWritable() || desc
.writable());
789 // First, resolve the property to simplify the code below.
791 if (!NativeLookupOwnProperty
<CanGC
>(cx
, obj
, id
, &prop
)) {
795 MOZ_ASSERT(prop
.isNativeProperty());
797 PropertyInfo propInfo
= prop
.propertyInfo();
798 MOZ_ASSERT(propInfo
.writable());
799 MOZ_ASSERT(propInfo
.isCustomDataProperty());
801 // Change the property's attributes by implementing the relevant parts of
802 // ValidateAndApplyPropertyDescriptor (ES2021 draft, 10.1.6.3), in particular
805 // Determine whether the property should be configurable and/or enumerable.
806 bool configurable
= propInfo
.configurable();
807 bool enumerable
= propInfo
.enumerable();
809 if (desc
.hasConfigurable()) {
810 configurable
= desc
.configurable();
812 if (desc
.hasEnumerable()) {
813 enumerable
= desc
.enumerable();
816 // Property is not configurable so disallow any attribute changes.
817 if ((desc
.hasConfigurable() && desc
.configurable()) ||
818 (desc
.hasEnumerable() && enumerable
!= desc
.enumerable())) {
819 return result
.fail(JSMSG_CANT_REDEFINE_PROP
);
823 PropertyFlags flags
= propInfo
.flags();
824 flags
.setFlag(PropertyFlag::Configurable
, configurable
);
825 flags
.setFlag(PropertyFlag::Enumerable
, enumerable
);
826 if (!NativeObject::changeCustomDataPropAttributes(cx
, obj
, id
, flags
)) {
830 return result
.succeed();
833 // ES 2017 draft 9.4.4.2
835 bool MappedArgumentsObject::obj_defineProperty(JSContext
* cx
, HandleObject obj
,
837 Handle
<PropertyDescriptor
> desc
,
838 ObjectOpResult
& result
) {
840 Rooted
<MappedArgumentsObject
*> argsobj(cx
, &obj
->as
<MappedArgumentsObject
>());
843 bool isMapped
= false;
845 unsigned arg
= unsigned(id
.toInt());
846 isMapped
= argsobj
->isElement(arg
);
850 Rooted
<PropertyDescriptor
> newArgDesc(cx
, desc
);
853 bool defineMapped
= false;
854 if (!desc
.isAccessorDescriptor() && isMapped
) {
856 if (desc
.hasWritable() && !desc
.writable()) {
857 if (!desc
.hasValue()) {
858 RootedValue
v(cx
, argsobj
->element(id
.toInt()));
859 newArgDesc
.setValue(v
);
862 // In this case the live mapping is supposed to keep working.
867 // Step 6. NativeDefineProperty will lookup [[Value]] for us.
869 if (!DefineMappedIndex(cx
, argsobj
, id
, &newArgDesc
, result
)) {
873 if (!NativeDefineProperty(cx
, obj
.as
<NativeObject
>(), id
, newArgDesc
,
885 unsigned arg
= unsigned(id
.toInt());
886 if (desc
.isAccessorDescriptor()) {
887 if (!argsobj
->markElementDeleted(cx
, arg
)) {
891 if (desc
.hasValue()) {
892 argsobj
->setElement(arg
, desc
.value());
894 if (desc
.hasWritable() && !desc
.writable()) {
895 if (!argsobj
->markElementDeleted(cx
, arg
)) {
903 return result
.succeed();
906 bool js::UnmappedArgGetter(JSContext
* cx
, HandleObject obj
, HandleId id
,
907 MutableHandleValue vp
) {
908 UnmappedArgumentsObject
& argsobj
= obj
->as
<UnmappedArgumentsObject
>();
912 * arg can exceed the number of arguments if a script changed the
913 * prototype to point to another Arguments object with a bigger argc.
915 unsigned arg
= unsigned(id
.toInt());
916 if (argsobj
.isElement(arg
)) {
917 vp
.set(argsobj
.element(arg
));
920 MOZ_ASSERT(id
.isAtom(cx
->names().length
));
921 if (!argsobj
.hasOverriddenLength()) {
922 vp
.setInt32(argsobj
.initialLength());
928 bool js::UnmappedArgSetter(JSContext
* cx
, HandleObject obj
, HandleId id
,
929 HandleValue v
, ObjectOpResult
& result
) {
930 Handle
<UnmappedArgumentsObject
*> argsobj
= obj
.as
<UnmappedArgumentsObject
>();
932 Rooted
<mozilla::Maybe
<PropertyDescriptor
>> desc(cx
);
933 if (!GetOwnPropertyDescriptor(cx
, argsobj
, id
, &desc
)) {
936 MOZ_ASSERT(desc
.isSome());
937 MOZ_ASSERT(desc
->isDataDescriptor());
938 MOZ_ASSERT(desc
->writable());
939 MOZ_ASSERT(!desc
->resolving());
942 unsigned arg
= unsigned(id
.toInt());
943 if (arg
< argsobj
->initialLength()) {
944 argsobj
->setElement(arg
, v
);
945 return result
.succeed();
948 MOZ_ASSERT(id
.isAtom(cx
->names().length
));
952 * For simplicity we use delete/define to replace the property with a
953 * simple data property. Note that we rely on ArgumentsObject::obj_delProperty
954 * to set the corresponding override-bit.
956 Rooted
<PropertyDescriptor
> desc_(cx
, *desc
);
958 ObjectOpResult ignored
;
959 return NativeDeleteProperty(cx
, argsobj
, id
, ignored
) &&
960 NativeDefineProperty(cx
, argsobj
, id
, desc_
, result
);
964 bool UnmappedArgumentsObject::obj_resolve(JSContext
* cx
, HandleObject obj
,
965 HandleId id
, bool* resolvedp
) {
966 Rooted
<UnmappedArgumentsObject
*> argsobj(cx
,
967 &obj
->as
<UnmappedArgumentsObject
>());
969 if (id
.isWellKnownSymbol(JS::SymbolCode::iterator
)) {
970 if (argsobj
->hasOverriddenIterator()) {
974 if (!reifyIterator(cx
, argsobj
)) {
981 if (id
.isAtom(cx
->names().callee
)) {
982 RootedObject
throwTypeError(
983 cx
, GlobalObject::getOrCreateThrowTypeError(cx
, cx
->global()));
984 if (!throwTypeError
) {
988 unsigned attrs
= JSPROP_RESOLVING
| JSPROP_PERMANENT
;
989 if (!NativeDefineAccessorProperty(cx
, argsobj
, id
, throwTypeError
,
990 throwTypeError
, attrs
)) {
998 PropertyFlags flags
= {PropertyFlag::CustomDataProperty
,
999 PropertyFlag::Configurable
, PropertyFlag::Writable
};
1001 uint32_t arg
= uint32_t(id
.toInt());
1002 if (!argsobj
->isElement(arg
)) {
1006 flags
.setFlag(PropertyFlag::Enumerable
);
1007 } else if (id
.isAtom(cx
->names().length
)) {
1008 if (argsobj
->hasOverriddenLength()) {
1015 return ResolveArgumentsProperty(cx
, argsobj
, id
, flags
, resolvedp
);
1019 bool UnmappedArgumentsObject::obj_enumerate(JSContext
* cx
, HandleObject obj
) {
1020 Rooted
<UnmappedArgumentsObject
*> argsobj(cx
,
1021 &obj
->as
<UnmappedArgumentsObject
>());
1026 // Trigger reflection.
1027 id
= NameToId(cx
->names().length
);
1028 if (!HasOwnProperty(cx
, argsobj
, id
, &found
)) {
1032 id
= NameToId(cx
->names().callee
);
1033 if (!HasOwnProperty(cx
, argsobj
, id
, &found
)) {
1037 id
= PropertyKey::Symbol(cx
->wellKnownSymbols().iterator
);
1038 if (!HasOwnProperty(cx
, argsobj
, id
, &found
)) {
1042 for (unsigned i
= 0; i
< argsobj
->initialLength(); i
++) {
1043 id
= PropertyKey::Int(i
);
1044 if (!HasOwnProperty(cx
, argsobj
, id
, &found
)) {
1052 void ArgumentsObject::finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
1053 MOZ_ASSERT(!IsInsideNursery(obj
));
1054 ArgumentsObject
& argsobj
= obj
->as
<ArgumentsObject
>();
1055 if (argsobj
.data()) {
1056 gcx
->free_(&argsobj
, argsobj
.maybeRareData(),
1057 RareArgumentsData::bytesRequired(argsobj
.initialLength()),
1058 MemoryUse::RareArgumentsData
);
1059 gcx
->free_(&argsobj
, argsobj
.data(),
1060 ArgumentsData::bytesRequired(argsobj
.data()->numArgs()),
1061 MemoryUse::ArgumentsData
);
1065 void ArgumentsObject::trace(JSTracer
* trc
, JSObject
* obj
) {
1066 ArgumentsObject
& argsobj
= obj
->as
<ArgumentsObject
>();
1067 // Template objects have no ArgumentsData.
1068 if (ArgumentsData
* data
= argsobj
.data()) {
1069 data
->args
.trace(trc
);
1074 size_t ArgumentsObject::objectMoved(JSObject
* dst
, JSObject
* src
) {
1075 ArgumentsObject
* ndst
= &dst
->as
<ArgumentsObject
>();
1076 const ArgumentsObject
* nsrc
= &src
->as
<ArgumentsObject
>();
1077 MOZ_ASSERT(ndst
->data() == nsrc
->data());
1079 if (!IsInsideNursery(src
)) {
1083 Nursery
& nursery
= dst
->runtimeFromMainThread()->gc
.nursery();
1085 size_t nbytesTotal
= 0;
1087 ArgumentsData
* data
= nsrc
->data();
1088 uint32_t nDataBytes
= ArgumentsData::bytesRequired(nsrc
->data()->numArgs());
1089 Nursery::WasBufferMoved result
= nursery
.maybeMoveBufferOnPromotion(
1090 &data
, dst
, nDataBytes
, MemoryUse::ArgumentsData
);
1091 if (result
== Nursery::BufferMoved
) {
1092 ndst
->initFixedSlot(DATA_SLOT
, PrivateValue(data
));
1093 nbytesTotal
+= nDataBytes
;
1096 if (RareArgumentsData
* rareData
= nsrc
->maybeRareData()) {
1097 uint32_t nRareBytes
=
1098 RareArgumentsData::bytesRequired(nsrc
->initialLength());
1099 Nursery::WasBufferMoved result
= nursery
.maybeMoveBufferOnPromotion(
1100 &rareData
, dst
, nRareBytes
, MemoryUse::RareArgumentsData
);
1101 if (result
== Nursery::BufferMoved
) {
1102 ndst
->data()->rareData
= rareData
;
1103 nbytesTotal
+= nRareBytes
;
1111 * The classes below collaborate to lazily reflect and synchronize actual
1112 * argument values, argument count, and callee function object stored in a
1113 * stack frame with their corresponding property values in the frame's
1116 const JSClassOps
MappedArgumentsObject::classOps_
= {
1117 nullptr, // addProperty
1118 ArgumentsObject::obj_delProperty
, // delProperty
1119 MappedArgumentsObject::obj_enumerate
, // enumerate
1120 nullptr, // newEnumerate
1121 MappedArgumentsObject::obj_resolve
, // resolve
1122 ArgumentsObject::obj_mayResolve
, // mayResolve
1123 ArgumentsObject::finalize
, // finalize
1125 nullptr, // construct
1126 ArgumentsObject::trace
, // trace
1129 const js::ClassExtension
MappedArgumentsObject::classExt_
= {
1130 ArgumentsObject::objectMoved
, // objectMovedOp
1133 const ObjectOps
MappedArgumentsObject::objectOps_
= {
1134 nullptr, // lookupProperty
1135 MappedArgumentsObject::obj_defineProperty
, // defineProperty
1136 nullptr, // hasProperty
1137 nullptr, // getProperty
1138 nullptr, // setProperty
1139 nullptr, // getOwnPropertyDescriptor
1140 nullptr, // deleteProperty
1141 nullptr, // getElements
1142 nullptr, // funToString
1145 const JSClass
MappedArgumentsObject::class_
= {
1147 JSCLASS_DELAY_METADATA_BUILDER
|
1148 JSCLASS_HAS_RESERVED_SLOTS(MappedArgumentsObject::RESERVED_SLOTS
) |
1149 JSCLASS_HAS_CACHED_PROTO(JSProto_Object
) |
1150 JSCLASS_SKIP_NURSERY_FINALIZE
| JSCLASS_BACKGROUND_FINALIZE
,
1151 &MappedArgumentsObject::classOps_
,
1153 &MappedArgumentsObject::classExt_
,
1154 &MappedArgumentsObject::objectOps_
};
1157 * Unmapped arguments is significantly less magical than mapped arguments, so
1158 * it is represented by a different class while sharing some functionality.
1160 const JSClassOps
UnmappedArgumentsObject::classOps_
= {
1161 nullptr, // addProperty
1162 ArgumentsObject::obj_delProperty
, // delProperty
1163 UnmappedArgumentsObject::obj_enumerate
, // enumerate
1164 nullptr, // newEnumerate
1165 UnmappedArgumentsObject::obj_resolve
, // resolve
1166 ArgumentsObject::obj_mayResolve
, // mayResolve
1167 ArgumentsObject::finalize
, // finalize
1169 nullptr, // construct
1170 ArgumentsObject::trace
, // trace
1173 const js::ClassExtension
UnmappedArgumentsObject::classExt_
= {
1174 ArgumentsObject::objectMoved
, // objectMovedOp
1177 const JSClass
UnmappedArgumentsObject::class_
= {
1179 JSCLASS_DELAY_METADATA_BUILDER
|
1180 JSCLASS_HAS_RESERVED_SLOTS(UnmappedArgumentsObject::RESERVED_SLOTS
) |
1181 JSCLASS_HAS_CACHED_PROTO(JSProto_Object
) |
1182 JSCLASS_SKIP_NURSERY_FINALIZE
| JSCLASS_BACKGROUND_FINALIZE
,
1183 &UnmappedArgumentsObject::classOps_
, nullptr,
1184 &UnmappedArgumentsObject::classExt_
};