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 "jit/VMFunctions.h"
9 #include "mozilla/FloatingPoint.h"
11 #include "builtin/MapObject.h"
12 #include "builtin/String.h"
13 #include "ds/OrderedHashTable.h"
16 #include "jit/arm/Simulator-arm.h"
17 #include "jit/AtomicOperations.h"
18 #include "jit/BaselineIC.h"
19 #include "jit/CalleeToken.h"
20 #include "jit/JitFrames.h"
21 #include "jit/JitRuntime.h"
22 #include "jit/mips32/Simulator-mips32.h"
23 #include "jit/mips64/Simulator-mips64.h"
24 #include "jit/Simulator.h"
25 #include "js/experimental/JitInfo.h"
26 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
27 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
28 #include "js/friend/WindowProxy.h" // js::IsWindow
29 #include "js/Printf.h"
30 #include "js/TraceKind.h"
31 #include "proxy/ScriptedProxyHandler.h"
32 #include "util/Unicode.h"
33 #include "vm/ArrayObject.h"
34 #include "vm/Compartment.h"
35 #include "vm/Interpreter.h"
36 #include "vm/JSAtomUtils.h" // AtomizeString
37 #include "vm/PlainObject.h" // js::PlainObject
38 #include "vm/SelfHosting.h"
39 #include "vm/StaticStrings.h"
40 #include "vm/TypedArrayObject.h"
41 #include "vm/Watchtower.h"
42 #include "wasm/WasmGcObject.h"
44 #include "debugger/DebugAPI-inl.h"
45 #include "jit/BaselineFrame-inl.h"
46 #include "jit/VMFunctionList-inl.h"
47 #include "vm/Interpreter-inl.h"
48 #include "vm/JSAtomUtils-inl.h" // TypeName
49 #include "vm/JSContext-inl.h"
50 #include "vm/JSScript-inl.h"
51 #include "vm/NativeObject-inl.h"
52 #include "vm/PlainObject-inl.h" // js::CreateThis
53 #include "vm/StringObject-inl.h"
56 using namespace js::jit
;
60 class ArgumentsObject
;
61 class NamedLambdaObject
;
62 class AsyncFunctionGeneratorObject
;
67 struct IonOsrTempData
;
71 explicit constexpr PopValues(uint8_t numValues
= 0) : numValues(numValues
) {}
75 struct ReturnTypeToDataType
{ /* Unexpected return type for a VMFunction. */
78 struct ReturnTypeToDataType
<void> {
79 static const DataType result
= Type_Void
;
82 struct ReturnTypeToDataType
<bool> {
83 static const DataType result
= Type_Bool
;
86 struct ReturnTypeToDataType
<T
*> {
87 // Assume by default that any pointer return types are cells.
88 static_assert(std::is_base_of_v
<gc::Cell
, T
>);
90 static const DataType result
= Type_Cell
;
93 // Convert argument types to properties of the argument known by the jit.
95 struct TypeToArgProperties
{
96 static const uint32_t result
=
97 (sizeof(T
) <= sizeof(void*) ? VMFunctionData::Word
98 : VMFunctionData::Double
);
101 struct TypeToArgProperties
<const Value
&> {
102 static const uint32_t result
=
103 TypeToArgProperties
<Value
>::result
| VMFunctionData::ByRef
;
106 struct TypeToArgProperties
<HandleValue
> {
107 static const uint32_t result
=
108 TypeToArgProperties
<Value
>::result
| VMFunctionData::ByRef
;
111 struct TypeToArgProperties
<MutableHandleValue
> {
112 static const uint32_t result
=
113 TypeToArgProperties
<Value
>::result
| VMFunctionData::ByRef
;
116 struct TypeToArgProperties
<HandleId
> {
117 static const uint32_t result
=
118 TypeToArgProperties
<jsid
>::result
| VMFunctionData::ByRef
;
121 struct TypeToArgProperties
<Handle
<T
*>> {
122 // Assume by default that any pointer handle types are cells.
123 static_assert(std::is_base_of_v
<gc::Cell
, T
>);
125 static const uint32_t result
=
126 TypeToArgProperties
<T
*>::result
| VMFunctionData::ByRef
;
129 struct TypeToArgProperties
<Handle
<T
>> {
130 // Fail for Handle types that aren't specialized above.
133 // Convert argument type to whether or not it should be passed in a float
134 // register on platforms that have them, like x64.
136 struct TypeToPassInFloatReg
{
137 static const uint32_t result
= 0;
140 struct TypeToPassInFloatReg
<double> {
141 static const uint32_t result
= 1;
144 // Convert argument types to root types used by the gc, see TraceJitExitFrame.
146 struct TypeToRootType
{
147 static const uint32_t result
= VMFunctionData::RootNone
;
150 struct TypeToRootType
<HandleValue
> {
151 static const uint32_t result
= VMFunctionData::RootValue
;
154 struct TypeToRootType
<MutableHandleValue
> {
155 static const uint32_t result
= VMFunctionData::RootValue
;
158 struct TypeToRootType
<HandleId
> {
159 static const uint32_t result
= VMFunctionData::RootId
;
162 struct TypeToRootType
<Handle
<T
*>> {
163 // Assume by default that any pointer types are cells.
164 static_assert(std::is_base_of_v
<gc::Cell
, T
>);
166 static constexpr uint32_t rootType() {
169 switch (JS::MapTypeToTraceKind
<T
>::kind
) {
170 case TraceKind::Object
:
171 return VMFunctionData::RootObject
;
172 case TraceKind::BigInt
:
173 return VMFunctionData::RootBigInt
;
174 case TraceKind::String
:
175 return VMFunctionData::RootString
;
176 case TraceKind::Shape
:
177 case TraceKind::Script
:
178 case TraceKind::Scope
:
179 return VMFunctionData::RootCell
;
180 case TraceKind::Symbol
:
181 case TraceKind::BaseShape
:
182 case TraceKind::Null
:
183 case TraceKind::JitCode
:
184 case TraceKind::RegExpShared
:
185 case TraceKind::GetterSetter
:
186 case TraceKind::PropMap
:
187 MOZ_CRASH("Unexpected trace kind");
191 static constexpr uint32_t result
= rootType();
194 struct TypeToRootType
<Handle
<T
>> {
195 // Fail for Handle types that aren't specialized above.
199 struct OutParamToDataType
{
200 static const DataType result
= Type_Void
;
203 struct OutParamToDataType
<const T
*> {
204 // Const pointers can't be output parameters.
205 static const DataType result
= Type_Void
;
208 struct OutParamToDataType
<uint64_t*> {
209 // Already used as an input type, so it can't be used as an output param.
210 static const DataType result
= Type_Void
;
213 struct OutParamToDataType
<JSObject
*> {
214 // Already used as an input type, so it can't be used as an output param.
215 static const DataType result
= Type_Void
;
218 struct OutParamToDataType
<JSString
*> {
219 // Already used as an input type, so it can't be used as an output param.
220 static const DataType result
= Type_Void
;
223 struct OutParamToDataType
<BaselineFrame
*> {
224 // Already used as an input type, so it can't be used as an output param.
225 static const DataType result
= Type_Void
;
228 struct OutParamToDataType
<gc::AllocSite
*> {
229 // Already used as an input type, so it can't be used as an output param.
230 static const DataType result
= Type_Void
;
233 struct OutParamToDataType
<Value
*> {
234 static const DataType result
= Type_Value
;
237 struct OutParamToDataType
<int*> {
238 static const DataType result
= Type_Int32
;
241 struct OutParamToDataType
<uint32_t*> {
242 static const DataType result
= Type_Int32
;
245 struct OutParamToDataType
<bool*> {
246 static const DataType result
= Type_Bool
;
249 struct OutParamToDataType
<double*> {
250 static const DataType result
= Type_Double
;
253 struct OutParamToDataType
<T
*> {
254 // Fail for pointer types that aren't specialized above.
257 struct OutParamToDataType
<T
**> {
258 static const DataType result
= Type_Pointer
;
261 struct OutParamToDataType
<MutableHandle
<T
>> {
262 static const DataType result
= Type_Handle
;
266 struct OutParamToRootType
{
267 static const VMFunctionData::RootType result
= VMFunctionData::RootNone
;
270 struct OutParamToRootType
<MutableHandleValue
> {
271 static const VMFunctionData::RootType result
= VMFunctionData::RootValue
;
274 struct OutParamToRootType
<MutableHandleObject
> {
275 static const VMFunctionData::RootType result
= VMFunctionData::RootObject
;
278 struct OutParamToRootType
<MutableHandleString
> {
279 static const VMFunctionData::RootType result
= VMFunctionData::RootString
;
282 struct OutParamToRootType
<MutableHandleBigInt
> {
283 static const VMFunctionData::RootType result
= VMFunctionData::RootBigInt
;
286 // Construct a bit mask from a list of types. The mask is constructed as an OR
287 // of the mask produced for each argument. The result of each argument is
288 // shifted by its index, such that the result of the first argument is on the
289 // low bits of the mask, and the result of the last argument in part of the
290 // high bits of the mask.
291 template <template <typename
> class Each
, typename ResultType
, size_t Shift
,
295 template <template <typename
> class Each
, typename ResultType
, size_t Shift
>
296 struct BitMask
<Each
, ResultType
, Shift
> {
297 static constexpr ResultType result
= ResultType();
300 template <template <typename
> class Each
, typename ResultType
, size_t Shift
,
301 typename HeadType
, typename
... TailTypes
>
302 struct BitMask
<Each
, ResultType
, Shift
, HeadType
, TailTypes
...> {
303 static_assert(ResultType(Each
<HeadType
>::result
) < (1 << Shift
),
304 "not enough bits reserved by the shift for individual results");
305 static_assert(sizeof...(TailTypes
) < (8 * sizeof(ResultType
) / Shift
),
306 "not enough bits in the result type to store all bit masks");
308 static constexpr ResultType result
=
309 ResultType(Each
<HeadType
>::result
) |
310 (BitMask
<Each
, ResultType
, Shift
, TailTypes
...>::result
<< Shift
);
313 // Helper template to build the VMFunctionData for a function.
314 template <typename
... Args
>
315 struct VMFunctionDataHelper
;
317 template <class R
, typename
... Args
>
318 struct VMFunctionDataHelper
<R (*)(JSContext
*, Args
...)>
319 : public VMFunctionData
{
320 using Fun
= R (*)(JSContext
*, Args
...);
322 static constexpr DataType
returnType() {
323 return ReturnTypeToDataType
<R
>::result
;
325 static constexpr DataType
outParam() {
326 return OutParamToDataType
<typename LastArg
<Args
...>::Type
>::result
;
328 static constexpr RootType
outParamRootType() {
329 return OutParamToRootType
<typename LastArg
<Args
...>::Type
>::result
;
331 static constexpr size_t NbArgs() { return sizeof...(Args
); }
332 static constexpr size_t explicitArgs() {
333 return NbArgs() - (outParam() != Type_Void
? 1 : 0);
335 static constexpr uint32_t argumentProperties() {
336 return BitMask
<TypeToArgProperties
, uint32_t, 2, Args
...>::result
;
338 static constexpr uint32_t argumentPassedInFloatRegs() {
339 return BitMask
<TypeToPassInFloatReg
, uint32_t, 2, Args
...>::result
;
341 static constexpr uint64_t argumentRootTypes() {
342 return BitMask
<TypeToRootType
, uint64_t, 3, Args
...>::result
;
344 constexpr explicit VMFunctionDataHelper(const char* name
)
345 : VMFunctionData(name
, explicitArgs(), argumentProperties(),
346 argumentPassedInFloatRegs(), argumentRootTypes(),
347 outParam(), outParamRootType(), returnType(),
348 /* extraValuesToPop = */ 0) {}
349 constexpr explicit VMFunctionDataHelper(const char* name
,
350 PopValues extraValuesToPop
)
351 : VMFunctionData(name
, explicitArgs(), argumentProperties(),
352 argumentPassedInFloatRegs(), argumentRootTypes(),
353 outParam(), outParamRootType(), returnType(),
354 extraValuesToPop
.numValues
) {}
357 // GCC warns when the signature does not have matching attributes (for example
358 // [[nodiscard]]). Squelch this warning to avoid a GCC-only footgun.
360 # pragma GCC diagnostic push
361 # pragma GCC diagnostic ignored "-Wignored-attributes"
364 // Generate VMFunctionData array.
365 static constexpr VMFunctionData vmFunctions
[] = {
366 #define DEF_VMFUNCTION(name, fp, valuesToPop...) \
367 VMFunctionDataHelper<decltype(&(::fp))>(#name, PopValues(valuesToPop)),
368 VMFUNCTION_LIST(DEF_VMFUNCTION
)
369 #undef DEF_VMFUNCTION
373 # pragma GCC diagnostic pop
376 // Generate arrays storing C++ function pointers. These pointers are not stored
377 // in VMFunctionData because there's no good way to cast them to void* in
378 // constexpr code. Compilers are smart enough to treat the const array below as
380 #define DEF_VMFUNCTION(name, fp, ...) (void*)(::fp),
381 static void* const vmFunctionTargets
[] = {VMFUNCTION_LIST(DEF_VMFUNCTION
)};
382 #undef DEF_VMFUNCTION
384 const VMFunctionData
& GetVMFunction(VMFunctionId id
) {
385 return vmFunctions
[size_t(id
)];
388 static DynFn
GetVMFunctionTarget(VMFunctionId id
) {
389 return DynFn
{vmFunctionTargets
[size_t(id
)]};
392 size_t NumVMFunctions() { return size_t(VMFunctionId::Count
); }
394 size_t VMFunctionData::sizeOfOutParamStackSlot() const {
397 return sizeof(Value
);
402 return sizeof(uintptr_t);
405 return sizeof(double);
408 switch (outParamRootType
) {
410 MOZ_CRASH("Handle must have root type");
416 return sizeof(uintptr_t);
418 return sizeof(Value
);
420 MOZ_CRASH("Invalid type");
426 MOZ_CRASH("Unexpected outparam type");
429 MOZ_CRASH("Invalid type");
432 bool JitRuntime::generateVMWrappers(JSContext
* cx
, MacroAssembler
& masm
,
433 PerfSpewerRangeRecorder
& rangeRecorder
) {
434 // Generate all VM function wrappers.
436 static constexpr size_t NumVMFunctions
= size_t(VMFunctionId::Count
);
438 if (!functionWrapperOffsets_
.reserve(NumVMFunctions
)) {
443 const char* lastName
= nullptr;
446 for (size_t i
= 0; i
< NumVMFunctions
; i
++) {
447 VMFunctionId id
= VMFunctionId(i
);
448 const VMFunctionData
& fun
= GetVMFunction(id
);
451 // Assert the list is sorted by name.
453 MOZ_ASSERT(strcmp(lastName
, fun
.name()) < 0,
454 "VM function list must be sorted by name");
456 lastName
= fun
.name();
459 JitSpew(JitSpew_Codegen
, "# VM function wrapper (%s)", fun
.name());
462 if (!generateVMWrapper(cx
, masm
, id
, fun
, GetVMFunctionTarget(id
),
466 #if defined(JS_ION_PERF)
467 rangeRecorder
.recordVMWrapperOffset(fun
.name());
469 rangeRecorder
.recordOffset("Trampoline: VMWrapper");
472 MOZ_ASSERT(functionWrapperOffsets_
.length() == size_t(id
));
473 functionWrapperOffsets_
.infallibleAppend(offset
);
479 bool InvokeFunction(JSContext
* cx
, HandleObject obj
, bool constructing
,
480 bool ignoresReturnValue
, uint32_t argc
, Value
* argv
,
481 MutableHandleValue rval
) {
482 RootedExternalValueArray
argvRoot(cx
, argc
+ 1 + constructing
, argv
);
484 // Data in the argument vector is arranged for a JIT -> JIT call.
485 RootedValue
thisv(cx
, argv
[0]);
486 Value
* argvWithoutThis
= argv
+ 1;
488 RootedValue
fval(cx
, ObjectValue(*obj
));
490 if (!IsConstructor(fval
)) {
491 ReportValueError(cx
, JSMSG_NOT_CONSTRUCTOR
, JSDVG_IGNORE_STACK
, fval
,
496 ConstructArgs
cargs(cx
);
497 if (!cargs
.init(cx
, argc
)) {
501 for (uint32_t i
= 0; i
< argc
; i
++) {
502 cargs
[i
].set(argvWithoutThis
[i
]);
505 RootedValue
newTarget(cx
, argvWithoutThis
[argc
]);
507 // See CreateThisFromIon for why this can be NullValue.
508 if (thisv
.isNull()) {
509 thisv
.setMagic(JS_IS_CONSTRUCTING
);
512 // If |this| hasn't been created, or is JS_UNINITIALIZED_LEXICAL,
513 // we can use normal construction code without creating an extraneous
515 if (thisv
.isMagic()) {
516 MOZ_ASSERT(thisv
.whyMagic() == JS_IS_CONSTRUCTING
||
517 thisv
.whyMagic() == JS_UNINITIALIZED_LEXICAL
);
519 RootedObject
obj(cx
);
520 if (!Construct(cx
, fval
, cargs
, newTarget
, &obj
)) {
524 rval
.setObject(*obj
);
528 // Otherwise the default |this| has already been created. We could
529 // almost perform a *call* at this point, but we'd break |new.target|
530 // in the function. So in this one weird case we call a one-off
531 // construction path that *won't* set |this| to JS_IS_CONSTRUCTING.
532 return InternalConstructWithProvidedThis(cx
, fval
, thisv
, cargs
, newTarget
,
536 InvokeArgsMaybeIgnoresReturnValue
args(cx
);
537 if (!args
.init(cx
, argc
, ignoresReturnValue
)) {
541 for (size_t i
= 0; i
< argc
; i
++) {
542 args
[i
].set(argvWithoutThis
[i
]);
545 return Call(cx
, fval
, thisv
, args
, rval
);
548 void* GetContextSensitiveInterpreterStub() {
549 return TlsContext
.get()->runtime()->jitRuntime()->interpreterStub().value
;
552 bool InvokeFromInterpreterStub(JSContext
* cx
,
553 InterpreterStubExitFrameLayout
* frame
) {
554 JitFrameLayout
* jsFrame
= frame
->jsFrame();
555 CalleeToken token
= jsFrame
->calleeToken();
557 Value
* argv
= jsFrame
->thisAndActualArgs();
558 uint32_t numActualArgs
= jsFrame
->numActualArgs();
559 bool constructing
= CalleeTokenIsConstructing(token
);
560 RootedFunction
fun(cx
, CalleeTokenToFunction(token
));
562 // Ensure new.target immediately follows the actual arguments (the arguments
563 // rectifier added padding).
564 if (constructing
&& numActualArgs
< fun
->nargs()) {
565 argv
[1 + numActualArgs
] = argv
[1 + fun
->nargs()];
568 RootedValue
rval(cx
);
569 if (!InvokeFunction(cx
, fun
, constructing
,
570 /* ignoresReturnValue = */ false, numActualArgs
, argv
,
575 // Overwrite |this| with the return value.
580 static bool CheckOverRecursedImpl(JSContext
* cx
, size_t extra
) {
581 // We just failed the jitStackLimit check. There are two possible reasons:
582 // 1) jitStackLimit was the real stack limit and we're over-recursed
583 // 2) jitStackLimit was set to JS::NativeStackLimitMin by
584 // JSContext::requestInterrupt and we need to call
585 // JSContext::handleInterrupt.
589 if (cx
->simulator()->overRecursedWithExtra(extra
)) {
590 ReportOverRecursed(cx
);
594 AutoCheckRecursionLimit
recursion(cx
);
595 if (!recursion
.checkWithExtra(cx
, extra
)) {
601 gc::MaybeVerifyBarriers(cx
);
602 return cx
->handleInterrupt();
605 bool CheckOverRecursed(JSContext
* cx
) { return CheckOverRecursedImpl(cx
, 0); }
607 bool CheckOverRecursedBaseline(JSContext
* cx
, BaselineFrame
* frame
) {
608 // The stack check in Baseline happens before pushing locals so we have to
609 // account for that by including script->nslots() in the C++ recursion check.
610 size_t extra
= frame
->script()->nslots() * sizeof(Value
);
611 return CheckOverRecursedImpl(cx
, extra
);
614 bool MutatePrototype(JSContext
* cx
, Handle
<PlainObject
*> obj
,
616 if (!value
.isObjectOrNull()) {
620 RootedObject
newProto(cx
, value
.toObjectOrNull());
621 return SetPrototype(cx
, obj
, newProto
);
624 template <EqualityKind Kind
>
625 bool StringsEqual(JSContext
* cx
, HandleString lhs
, HandleString rhs
,
627 JSLinearString
* linearLhs
= lhs
->ensureLinear(cx
);
631 JSLinearString
* linearRhs
= rhs
->ensureLinear(cx
);
636 *res
= EqualChars(linearLhs
, linearRhs
);
638 if constexpr (Kind
== EqualityKind::NotEqual
) {
644 template bool StringsEqual
<EqualityKind::Equal
>(JSContext
* cx
, HandleString lhs
,
645 HandleString rhs
, bool* res
);
646 template bool StringsEqual
<EqualityKind::NotEqual
>(JSContext
* cx
,
648 HandleString rhs
, bool* res
);
650 template <ComparisonKind Kind
>
651 bool StringsCompare(JSContext
* cx
, HandleString lhs
, HandleString rhs
,
654 if (!js::CompareStrings(cx
, lhs
, rhs
, &result
)) {
657 if (Kind
== ComparisonKind::LessThan
) {
665 template bool StringsCompare
<ComparisonKind::LessThan
>(JSContext
* cx
,
669 template bool StringsCompare
<ComparisonKind::GreaterThanOrEqual
>(
670 JSContext
* cx
, HandleString lhs
, HandleString rhs
, bool* res
);
672 JSString
* ArrayJoin(JSContext
* cx
, HandleObject array
, HandleString sep
) {
673 JS::RootedValueArray
<3> argv(cx
);
674 argv
[0].setUndefined();
675 argv
[1].setObject(*array
);
676 argv
[2].setString(sep
);
677 if (!js::array_join(cx
, 1, argv
.begin())) {
680 return argv
[0].toString();
683 bool SetArrayLength(JSContext
* cx
, HandleObject obj
, HandleValue value
,
685 Handle
<ArrayObject
*> array
= obj
.as
<ArrayObject
>();
687 RootedId
id(cx
, NameToId(cx
->names().length
));
688 ObjectOpResult result
;
690 // SetArrayLength is called by IC stubs for SetProp and SetElem on arrays'
691 // "length" property.
693 // ArraySetLength below coerces |value| before checking for length being
694 // writable, and in the case of illegal values, will throw RangeError even
695 // when "length" is not writable. This is incorrect observable behavior,
696 // as a regular [[Set]] operation will check for "length" being
697 // writable before attempting any assignment.
699 // So, perform ArraySetLength if and only if "length" is writable.
700 if (array
->lengthIsWritable()) {
701 Rooted
<PropertyDescriptor
> desc(
702 cx
, PropertyDescriptor::Data(value
, JS::PropertyAttribute::Writable
));
703 if (!ArraySetLength(cx
, array
, id
, desc
, result
)) {
707 MOZ_ALWAYS_TRUE(result
.fail(JSMSG_READ_ONLY
));
710 return result
.checkStrictModeError(cx
, obj
, id
, strict
);
713 bool CharCodeAt(JSContext
* cx
, HandleString str
, int32_t index
,
716 if (!str
->getChar(cx
, index
, &c
)) {
723 bool CodePointAt(JSContext
* cx
, HandleString str
, int32_t index
,
726 if (!str
->getCodePoint(cx
, size_t(index
), &codePoint
)) {
733 JSLinearString
* StringFromCharCodeNoGC(JSContext
* cx
, int32_t code
) {
734 AutoUnsafeCallWithABI unsafe
;
736 char16_t c
= char16_t(code
);
738 if (StaticStrings::hasUnit(c
)) {
739 return cx
->staticStrings().getUnit(c
);
742 return NewInlineString
<NoGC
>(cx
, {c
}, 1);
745 JSLinearString
* LinearizeForCharAccessPure(JSString
* str
) {
746 AutoUnsafeCallWithABI unsafe
;
748 // Should only be called on ropes.
749 MOZ_ASSERT(str
->isRope());
751 // ensureLinear is intentionally called with a nullptr to avoid OOM reporting.
752 return str
->ensureLinear(nullptr);
755 JSLinearString
* LinearizeForCharAccess(JSContext
* cx
, JSString
* str
) {
756 // Should only be called on ropes.
757 MOZ_ASSERT(str
->isRope());
759 return str
->ensureLinear(cx
);
762 template <typename CharT
>
763 static size_t StringTrimStartIndex(mozilla::Range
<CharT
> chars
) {
765 while (begin
< chars
.length() && unicode::IsSpace(chars
[begin
])) {
771 template <typename CharT
>
772 static size_t StringTrimEndIndex(mozilla::Range
<CharT
> chars
, size_t begin
) {
773 size_t end
= chars
.length();
774 while (end
> begin
&& unicode::IsSpace(chars
[end
- 1])) {
780 int32_t StringTrimStartIndex(const JSString
* str
) {
781 AutoUnsafeCallWithABI unsafe
;
783 MOZ_ASSERT(str
->isLinear());
785 const auto* linear
= &str
->asLinear();
788 if (linear
->hasLatin1Chars()) {
789 JS::AutoCheckCannotGC nogc
;
790 begin
= StringTrimStartIndex(linear
->latin1Range(nogc
));
792 JS::AutoCheckCannotGC nogc
;
793 begin
= StringTrimStartIndex(linear
->twoByteRange(nogc
));
795 return int32_t(begin
);
798 int32_t StringTrimEndIndex(const JSString
* str
, int32_t start
) {
799 AutoUnsafeCallWithABI unsafe
;
801 MOZ_ASSERT(str
->isLinear());
802 MOZ_ASSERT(start
>= 0 && size_t(start
) <= str
->length());
804 const auto* linear
= &str
->asLinear();
807 if (linear
->hasLatin1Chars()) {
808 JS::AutoCheckCannotGC nogc
;
809 end
= StringTrimEndIndex(linear
->latin1Range(nogc
), size_t(start
));
811 JS::AutoCheckCannotGC nogc
;
812 end
= StringTrimEndIndex(linear
->twoByteRange(nogc
), size_t(start
));
817 JSString
* CharCodeToLowerCase(JSContext
* cx
, int32_t code
) {
818 RootedString
str(cx
, StringFromCharCode(cx
, code
));
822 return js::StringToLowerCase(cx
, str
);
825 JSString
* CharCodeToUpperCase(JSContext
* cx
, int32_t code
) {
826 RootedString
str(cx
, StringFromCharCode(cx
, code
));
830 return js::StringToUpperCase(cx
, str
);
833 bool SetProperty(JSContext
* cx
, HandleObject obj
, Handle
<PropertyName
*> name
,
834 HandleValue value
, bool strict
, jsbytecode
* pc
) {
835 RootedId
id(cx
, NameToId(name
));
837 RootedValue
receiver(cx
, ObjectValue(*obj
));
838 ObjectOpResult result
;
839 if (MOZ_LIKELY(!obj
->getOpsSetProperty())) {
841 if (op
== JSOp::SetName
|| op
== JSOp::StrictSetName
||
842 op
== JSOp::SetGName
|| op
== JSOp::StrictSetGName
) {
843 if (!NativeSetProperty
<Unqualified
>(cx
, obj
.as
<NativeObject
>(), id
, value
,
848 if (!NativeSetProperty
<Qualified
>(cx
, obj
.as
<NativeObject
>(), id
, value
,
854 if (!SetProperty(cx
, obj
, id
, value
, receiver
, result
)) {
858 return result
.checkStrictModeError(cx
, obj
, id
, strict
);
861 bool InterruptCheck(JSContext
* cx
) {
862 gc::MaybeVerifyBarriers(cx
);
864 return CheckForInterrupt(cx
);
867 JSObject
* NewStringObject(JSContext
* cx
, HandleString str
) {
868 return StringObject::create(cx
, str
);
871 bool OperatorIn(JSContext
* cx
, HandleValue key
, HandleObject obj
, bool* out
) {
873 return ToPropertyKey(cx
, key
, &id
) && HasProperty(cx
, obj
, id
, out
);
876 bool GetIntrinsicValue(JSContext
* cx
, Handle
<PropertyName
*> name
,
877 MutableHandleValue rval
) {
878 return GlobalObject::getIntrinsicValue(cx
, cx
->global(), name
, rval
);
881 bool CreateThisFromIC(JSContext
* cx
, HandleObject callee
,
882 HandleObject newTarget
, MutableHandleValue rval
) {
883 HandleFunction fun
= callee
.as
<JSFunction
>();
884 MOZ_ASSERT(fun
->isInterpreted());
885 MOZ_ASSERT(fun
->isConstructor());
886 MOZ_ASSERT(cx
->realm() == fun
->realm(),
887 "Realm switching happens before creating this");
889 // CreateThis expects rval to be this magic value.
890 rval
.set(MagicValue(JS_IS_CONSTRUCTING
));
892 if (!js::CreateThis(cx
, fun
, newTarget
, GenericObject
, rval
)) {
896 MOZ_ASSERT_IF(rval
.isObject(), fun
->realm() == rval
.toObject().nonCCWRealm());
900 bool CreateThisFromIon(JSContext
* cx
, HandleObject callee
,
901 HandleObject newTarget
, MutableHandleValue rval
) {
902 // Return JS_IS_CONSTRUCTING for cases not supported by the inline call path.
903 rval
.set(MagicValue(JS_IS_CONSTRUCTING
));
905 if (!callee
->is
<JSFunction
>()) {
909 HandleFunction fun
= callee
.as
<JSFunction
>();
910 if (!fun
->isInterpreted() || !fun
->isConstructor()) {
914 // If newTarget is not a function or is a function with a possibly-getter
915 // .prototype property, return NullValue to signal to LCallGeneric that it has
916 // to take the slow path. Note that we return NullValue instead of a
917 // MagicValue only because it's easier and faster to check for in JIT code
918 // (if we returned a MagicValue, JIT code would have to check both the type
919 // tag and the JSWhyMagic payload).
920 if (!fun
->constructorNeedsUninitializedThis()) {
921 if (!newTarget
->is
<JSFunction
>()) {
925 JSFunction
* newTargetFun
= &newTarget
->as
<JSFunction
>();
926 if (!newTargetFun
->hasNonConfigurablePrototypeDataProperty()) {
932 AutoRealm
ar(cx
, fun
);
933 if (!js::CreateThis(cx
, fun
, newTarget
, GenericObject
, rval
)) {
937 MOZ_ASSERT_IF(rval
.isObject(), fun
->realm() == rval
.toObject().nonCCWRealm());
941 void PostWriteBarrier(JSRuntime
* rt
, js::gc::Cell
* cell
) {
942 AutoUnsafeCallWithABI unsafe
;
943 rt
->gc
.storeBuffer().putWholeCellDontCheckLast(cell
);
946 static const size_t MAX_WHOLE_CELL_BUFFER_SIZE
= 4096;
948 void PostWriteElementBarrier(JSRuntime
* rt
, JSObject
* obj
, int32_t index
) {
949 AutoUnsafeCallWithABI unsafe
;
951 MOZ_ASSERT(!IsInsideNursery(obj
));
953 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
955 MOZ_ASSERT(index
>= 0);
956 MOZ_ASSERT(uint32_t(index
) < nobj
->getDenseInitializedLength());
958 if (nobj
->isInWholeCellBuffer()) {
962 if (nobj
->getDenseInitializedLength() > MAX_WHOLE_CELL_BUFFER_SIZE
964 || rt
->hasZealMode(gc::ZealMode::ElementsBarrier
)
967 rt
->gc
.storeBuffer().putSlot(nobj
, HeapSlot::Element
,
968 nobj
->unshiftedIndex(index
), 1);
972 rt
->gc
.storeBuffer().putWholeCell(obj
);
975 void PostGlobalWriteBarrier(JSRuntime
* rt
, GlobalObject
* obj
) {
976 MOZ_ASSERT(obj
->JSObject::is
<GlobalObject
>());
978 if (!obj
->realm()->globalWriteBarriered
) {
979 AutoUnsafeCallWithABI unsafe
;
980 rt
->gc
.storeBuffer().putWholeCell(obj
);
981 obj
->realm()->globalWriteBarriered
= 1;
985 bool GetInt32FromStringPure(JSContext
* cx
, JSString
* str
, int32_t* result
) {
986 // We shouldn't GC here as this is called directly from IC code.
987 AutoUnsafeCallWithABI unsafe
;
990 if (!StringToNumberPure(cx
, str
, &d
)) {
994 return mozilla::NumberIsInt32(d
, result
);
997 int32_t GetIndexFromString(JSString
* str
) {
998 // We shouldn't GC here as this is called directly from IC code.
999 AutoUnsafeCallWithABI unsafe
;
1001 if (!str
->isLinear()) {
1005 uint32_t index
= UINT32_MAX
; // Initialize this to appease Valgrind.
1006 if (!str
->asLinear().isIndex(&index
) || index
> INT32_MAX
) {
1010 return int32_t(index
);
1013 JSObject
* WrapObjectPure(JSContext
* cx
, JSObject
* obj
) {
1014 // IC code calls this directly so we shouldn't GC.
1015 AutoUnsafeCallWithABI unsafe
;
1018 MOZ_ASSERT(cx
->compartment() != obj
->compartment());
1020 // From: Compartment::getNonWrapperObjectForCurrentCompartment
1021 // Note that if the object is same-compartment, but has been wrapped into a
1022 // different compartment, we need to unwrap it and return the bare same-
1023 // compartment object. Note again that windows are always wrapped by a
1024 // WindowProxy even when same-compartment so take care not to strip this
1025 // particular wrapper.
1026 obj
= UncheckedUnwrap(obj
, /* stopAtWindowProxy = */ true);
1027 if (cx
->compartment() == obj
->compartment()) {
1028 MOZ_ASSERT(!IsWindow(obj
));
1029 JS::ExposeObjectToActiveJS(obj
);
1033 // Try to Lookup an existing wrapper for this object. We assume that
1034 // if we can find such a wrapper, not calling preWrap is correct.
1035 if (ObjectWrapperMap::Ptr p
= cx
->compartment()->lookupWrapper(obj
)) {
1036 JSObject
* wrapped
= p
->value().get();
1038 // Ensure the wrapper is still exposed.
1039 JS::ExposeObjectToActiveJS(wrapped
);
1046 bool DebugPrologue(JSContext
* cx
, BaselineFrame
* frame
) {
1047 return DebugAPI::onEnterFrame(cx
, frame
);
1050 bool DebugEpilogueOnBaselineReturn(JSContext
* cx
, BaselineFrame
* frame
,
1051 const jsbytecode
* pc
) {
1052 if (!DebugEpilogue(cx
, frame
, pc
, true)) {
1059 bool DebugEpilogue(JSContext
* cx
, BaselineFrame
* frame
, const jsbytecode
* pc
,
1061 // If DebugAPI::onLeaveFrame returns |true| we have to return the frame's
1062 // return value. If it returns |false|, the debugger threw an exception.
1063 // In both cases we have to pop debug scopes.
1064 ok
= DebugAPI::onLeaveFrame(cx
, frame
, pc
, ok
);
1066 // Unwind to the outermost environment.
1067 EnvironmentIter
ei(cx
, frame
, pc
);
1068 UnwindAllEnvironmentsInFrame(cx
, ei
);
1071 // Pop this frame by updating packedExitFP, so that the exception
1072 // handling code will start at the previous frame.
1073 JitFrameLayout
* prefix
= frame
->framePrefix();
1074 EnsureUnwoundJitExitFrame(cx
->activation()->asJit(), prefix
);
1081 void FrameIsDebuggeeCheck(BaselineFrame
* frame
) {
1082 AutoUnsafeCallWithABI unsafe
;
1083 if (frame
->script()->isDebuggee()) {
1084 frame
->setIsDebuggee();
1088 JSObject
* CreateGeneratorFromFrame(JSContext
* cx
, BaselineFrame
* frame
) {
1089 return AbstractGeneratorObject::createFromFrame(cx
, frame
);
1092 JSObject
* CreateGenerator(JSContext
* cx
, HandleFunction callee
,
1093 HandleScript script
, HandleObject environmentChain
,
1094 HandleObject args
) {
1095 Rooted
<ArgumentsObject
*> argsObj(
1096 cx
, args
? &args
->as
<ArgumentsObject
>() : nullptr);
1097 return AbstractGeneratorObject::create(cx
, callee
, script
, environmentChain
,
1101 bool NormalSuspend(JSContext
* cx
, HandleObject obj
, BaselineFrame
* frame
,
1102 uint32_t frameSize
, const jsbytecode
* pc
) {
1103 MOZ_ASSERT(JSOp(*pc
) == JSOp::InitialYield
|| JSOp(*pc
) == JSOp::Yield
||
1104 JSOp(*pc
) == JSOp::Await
);
1106 // Minus one because we don't want to include the return value.
1107 uint32_t numSlots
= frame
->numValueSlots(frameSize
) - 1;
1108 MOZ_ASSERT(numSlots
>= frame
->script()->nfixed());
1109 return AbstractGeneratorObject::suspend(cx
, obj
, frame
, pc
, numSlots
);
1112 bool FinalSuspend(JSContext
* cx
, HandleObject obj
, const jsbytecode
* pc
) {
1113 MOZ_ASSERT(JSOp(*pc
) == JSOp::FinalYieldRval
);
1114 AbstractGeneratorObject::finalSuspend(cx
, obj
);
1118 bool InterpretResume(JSContext
* cx
, HandleObject obj
, Value
* stackValues
,
1119 MutableHandleValue rval
) {
1120 MOZ_ASSERT(obj
->is
<AbstractGeneratorObject
>());
1122 // The |stackValues| argument points to the JSOp::Resume operands on the
1123 // native stack. Because the stack grows down, these values are:
1125 // [resumeKind, argument, generator, ..]
1127 MOZ_ASSERT(stackValues
[2].toObject() == *obj
);
1129 GeneratorResumeKind resumeKind
= IntToResumeKind(stackValues
[0].toInt32());
1130 JSAtom
* kind
= ResumeKindToAtom(cx
, resumeKind
);
1132 FixedInvokeArgs
<3> args(cx
);
1134 args
[0].setObject(*obj
);
1135 args
[1].set(stackValues
[1]);
1136 args
[2].setString(kind
);
1138 return CallSelfHostedFunction(cx
, cx
->names().InterpretGeneratorResume
,
1139 UndefinedHandleValue
, args
, rval
);
1142 bool DebugAfterYield(JSContext
* cx
, BaselineFrame
* frame
) {
1143 // The BaselineFrame has just been constructed by JSOp::Resume in the
1144 // caller. We need to set its debuggee flag as necessary.
1146 // If a breakpoint is set on JSOp::AfterYield, or stepping is enabled,
1147 // we may already have done this work. Don't fire onEnterFrame again.
1148 if (frame
->script()->isDebuggee() && !frame
->isDebuggee()) {
1149 frame
->setIsDebuggee();
1150 return DebugAPI::onResumeFrame(cx
, frame
);
1156 bool GeneratorThrowOrReturn(JSContext
* cx
, BaselineFrame
* frame
,
1157 Handle
<AbstractGeneratorObject
*> genObj
,
1158 HandleValue arg
, int32_t resumeKindArg
) {
1159 GeneratorResumeKind resumeKind
= IntToResumeKind(resumeKindArg
);
1161 js::GeneratorThrowOrReturn(cx
, frame
, genObj
, arg
, resumeKind
));
1165 bool GlobalDeclInstantiationFromIon(JSContext
* cx
, HandleScript script
,
1166 const jsbytecode
* pc
) {
1167 MOZ_ASSERT(!script
->hasNonSyntacticScope());
1169 RootedObject
envChain(cx
, &cx
->global()->lexicalEnvironment());
1170 GCThingIndex lastFun
= GET_GCTHING_INDEX(pc
);
1172 return GlobalOrEvalDeclInstantiation(cx
, envChain
, script
, lastFun
);
1175 bool InitFunctionEnvironmentObjects(JSContext
* cx
, BaselineFrame
* frame
) {
1176 return frame
->initFunctionEnvironmentObjects(cx
);
1179 bool NewArgumentsObject(JSContext
* cx
, BaselineFrame
* frame
,
1180 MutableHandleValue res
) {
1181 ArgumentsObject
* obj
= ArgumentsObject::createExpected(cx
, frame
);
1185 res
.setObject(*obj
);
1189 ArrayObject
* NewArrayObjectEnsureDenseInitLength(JSContext
* cx
, int32_t count
) {
1190 MOZ_ASSERT(count
>= 0);
1192 auto* array
= NewDenseFullyAllocatedArray(cx
, count
);
1196 array
->ensureDenseInitializedLength(0, count
);
1201 ArrayObject
* InitRestParameter(JSContext
* cx
, uint32_t length
, Value
* rest
,
1202 Handle
<ArrayObject
*> arrRes
) {
1204 // Fast path: we managed to allocate the array inline; initialize the
1206 MOZ_ASSERT(arrRes
->getDenseInitializedLength() == 0);
1208 // We don't call this function if we can initialize the elements in JIT
1210 MOZ_ASSERT(length
> arrRes
->getDenseCapacity());
1212 if (!arrRes
->growElements(cx
, length
)) {
1215 arrRes
->initDenseElements(rest
, length
);
1216 arrRes
->setLength(length
);
1220 return NewDenseCopiedArray(cx
, length
, rest
);
1223 bool HandleDebugTrap(JSContext
* cx
, BaselineFrame
* frame
,
1224 const uint8_t* retAddr
) {
1225 RootedScript
script(cx
, frame
->script());
1227 if (frame
->runningInInterpreter()) {
1228 pc
= frame
->interpreterPC();
1230 BaselineScript
* blScript
= script
->baselineScript();
1231 pc
= blScript
->retAddrEntryFromReturnAddress(retAddr
).pc(script
);
1234 // The Baseline Interpreter calls HandleDebugTrap for every op when the script
1235 // is in step mode or has breakpoints. The Baseline Compiler can toggle
1236 // breakpoints more granularly for specific bytecode PCs.
1237 if (frame
->runningInInterpreter()) {
1238 MOZ_ASSERT(DebugAPI::hasAnyBreakpointsOrStepMode(script
));
1240 MOZ_ASSERT(DebugAPI::stepModeEnabled(script
) ||
1241 DebugAPI::hasBreakpointsAt(script
, pc
));
1244 if (JSOp(*pc
) == JSOp::AfterYield
) {
1245 // JSOp::AfterYield will set the frame's debuggee flag and call the
1246 // onEnterFrame handler, but if we set a breakpoint there we have to do
1248 MOZ_ASSERT(!frame
->isDebuggee());
1250 if (!DebugAfterYield(cx
, frame
)) {
1254 // If the frame is not a debuggee we're done. This can happen, for instance,
1255 // if the onEnterFrame hook called removeDebuggee.
1256 if (!frame
->isDebuggee()) {
1261 MOZ_ASSERT(frame
->isDebuggee());
1263 if (DebugAPI::stepModeEnabled(script
) && !DebugAPI::onSingleStep(cx
)) {
1267 if (DebugAPI::hasBreakpointsAt(script
, pc
) && !DebugAPI::onTrap(cx
)) {
1274 bool OnDebuggerStatement(JSContext
* cx
, BaselineFrame
* frame
) {
1275 return DebugAPI::onDebuggerStatement(cx
, frame
);
1278 bool GlobalHasLiveOnDebuggerStatement(JSContext
* cx
) {
1279 AutoUnsafeCallWithABI unsafe
;
1280 return cx
->realm()->isDebuggee() &&
1281 DebugAPI::hasDebuggerStatementHook(cx
->global());
1284 bool PushLexicalEnv(JSContext
* cx
, BaselineFrame
* frame
,
1285 Handle
<LexicalScope
*> scope
) {
1286 return frame
->pushLexicalEnvironment(cx
, scope
);
1289 bool DebugLeaveThenPopLexicalEnv(JSContext
* cx
, BaselineFrame
* frame
,
1290 const jsbytecode
* pc
) {
1291 MOZ_ALWAYS_TRUE(DebugLeaveLexicalEnv(cx
, frame
, pc
));
1292 frame
->popOffEnvironmentChain
<ScopedLexicalEnvironmentObject
>();
1296 bool FreshenLexicalEnv(JSContext
* cx
, BaselineFrame
* frame
) {
1297 return frame
->freshenLexicalEnvironment
<false>(cx
);
1300 bool DebuggeeFreshenLexicalEnv(JSContext
* cx
, BaselineFrame
* frame
,
1301 const jsbytecode
* pc
) {
1302 return frame
->freshenLexicalEnvironment
<true>(cx
, pc
);
1305 bool RecreateLexicalEnv(JSContext
* cx
, BaselineFrame
* frame
) {
1306 return frame
->recreateLexicalEnvironment
<false>(cx
);
1309 bool DebuggeeRecreateLexicalEnv(JSContext
* cx
, BaselineFrame
* frame
,
1310 const jsbytecode
* pc
) {
1311 return frame
->recreateLexicalEnvironment
<true>(cx
, pc
);
1314 bool DebugLeaveLexicalEnv(JSContext
* cx
, BaselineFrame
* frame
,
1315 const jsbytecode
* pc
) {
1316 MOZ_ASSERT_IF(!frame
->runningInInterpreter(),
1317 frame
->script()->baselineScript()->hasDebugInstrumentation());
1318 if (cx
->realm()->isDebuggee()) {
1319 DebugEnvironments::onPopLexical(cx
, frame
, pc
);
1324 bool PushClassBodyEnv(JSContext
* cx
, BaselineFrame
* frame
,
1325 Handle
<ClassBodyScope
*> scope
) {
1326 return frame
->pushClassBodyEnvironment(cx
, scope
);
1329 bool PushVarEnv(JSContext
* cx
, BaselineFrame
* frame
, Handle
<Scope
*> scope
) {
1330 return frame
->pushVarEnvironment(cx
, scope
);
1333 bool EnterWith(JSContext
* cx
, BaselineFrame
* frame
, HandleValue val
,
1334 Handle
<WithScope
*> templ
) {
1335 return EnterWithOperation(cx
, frame
, val
, templ
);
1338 bool LeaveWith(JSContext
* cx
, BaselineFrame
* frame
) {
1339 if (MOZ_UNLIKELY(frame
->isDebuggee())) {
1340 DebugEnvironments::onPopWith(frame
);
1342 frame
->popOffEnvironmentChain
<WithEnvironmentObject
>();
1346 bool InitBaselineFrameForOsr(BaselineFrame
* frame
,
1347 InterpreterFrame
* interpFrame
,
1348 uint32_t numStackValues
) {
1349 return frame
->initForOsr(interpFrame
, numStackValues
);
1352 JSString
* StringReplace(JSContext
* cx
, HandleString string
,
1353 HandleString pattern
, HandleString repl
) {
1355 MOZ_ASSERT(pattern
);
1358 return str_replace_string_raw(cx
, string
, pattern
, repl
);
1361 void AssertValidBigIntPtr(JSContext
* cx
, JS::BigInt
* bi
) {
1362 AutoUnsafeCallWithABI unsafe
;
1363 // FIXME: check runtime?
1364 MOZ_ASSERT(cx
->zone() == bi
->zone());
1365 MOZ_ASSERT(bi
->isAligned());
1366 MOZ_ASSERT(bi
->getAllocKind() == gc::AllocKind::BIGINT
);
1369 void AssertValidObjectPtr(JSContext
* cx
, JSObject
* obj
) {
1370 AutoUnsafeCallWithABI unsafe
;
1372 // Check what we can, so that we'll hopefully assert/crash if we get a
1373 // bogus object (pointer).
1374 MOZ_ASSERT(obj
->compartment() == cx
->compartment());
1375 MOZ_ASSERT(obj
->zoneFromAnyThread() == cx
->zone());
1376 MOZ_ASSERT(obj
->runtimeFromMainThread() == cx
->runtime());
1378 if (obj
->isTenured()) {
1379 MOZ_ASSERT(obj
->isAligned());
1380 gc::AllocKind kind
= obj
->asTenured().getAllocKind();
1381 MOZ_ASSERT(gc::IsObjectAllocKind(kind
));
1386 void AssertValidStringPtr(JSContext
* cx
, JSString
* str
) {
1387 AutoUnsafeCallWithABI unsafe
;
1389 // We can't closely inspect strings from another runtime.
1390 if (str
->runtimeFromAnyThread() != cx
->runtime()) {
1391 MOZ_ASSERT(str
->isPermanentAtom());
1395 if (str
->isAtom()) {
1396 MOZ_ASSERT(str
->zone()->isAtomsZone());
1398 MOZ_ASSERT(str
->zone() == cx
->zone());
1401 MOZ_ASSERT(str
->isAligned());
1402 MOZ_ASSERT(str
->length() <= JSString::MAX_LENGTH
);
1404 gc::AllocKind kind
= str
->getAllocKind();
1405 if (str
->isFatInline()) {
1406 if (str
->isAtom()) {
1407 MOZ_ASSERT(kind
== gc::AllocKind::FAT_INLINE_ATOM
);
1409 MOZ_ASSERT(kind
== gc::AllocKind::FAT_INLINE_STRING
);
1411 } else if (str
->isExternal()) {
1412 MOZ_ASSERT(kind
== gc::AllocKind::EXTERNAL_STRING
);
1413 } else if (str
->isAtom()) {
1414 MOZ_ASSERT(kind
== gc::AllocKind::ATOM
);
1415 } else if (str
->isLinear()) {
1416 MOZ_ASSERT(kind
== gc::AllocKind::STRING
||
1417 kind
== gc::AllocKind::FAT_INLINE_STRING
);
1419 MOZ_ASSERT(kind
== gc::AllocKind::STRING
);
1424 void AssertValidSymbolPtr(JSContext
* cx
, JS::Symbol
* sym
) {
1425 AutoUnsafeCallWithABI unsafe
;
1427 // We can't closely inspect symbols from another runtime.
1428 if (sym
->runtimeFromAnyThread() != cx
->runtime()) {
1429 MOZ_ASSERT(sym
->isWellKnownSymbol());
1433 MOZ_ASSERT(sym
->zone()->isAtomsZone());
1434 MOZ_ASSERT(sym
->isAligned());
1435 if (JSAtom
* desc
= sym
->description()) {
1436 AssertValidStringPtr(cx
, desc
);
1439 MOZ_ASSERT(sym
->getAllocKind() == gc::AllocKind::SYMBOL
);
1442 void AssertValidValue(JSContext
* cx
, Value
* v
) {
1443 AutoUnsafeCallWithABI unsafe
;
1444 if (v
->isObject()) {
1445 AssertValidObjectPtr(cx
, &v
->toObject());
1446 } else if (v
->isString()) {
1447 AssertValidStringPtr(cx
, v
->toString());
1448 } else if (v
->isSymbol()) {
1449 AssertValidSymbolPtr(cx
, v
->toSymbol());
1450 } else if (v
->isBigInt()) {
1451 AssertValidBigIntPtr(cx
, v
->toBigInt());
1455 bool ObjectIsCallable(JSObject
* obj
) {
1456 AutoUnsafeCallWithABI unsafe
;
1457 return obj
->isCallable();
1460 bool ObjectIsConstructor(JSObject
* obj
) {
1461 AutoUnsafeCallWithABI unsafe
;
1462 return obj
->isConstructor();
1465 JSObject
* ObjectKeys(JSContext
* cx
, HandleObject obj
) {
1466 JS::RootedValueArray
<3> argv(cx
);
1467 argv
[0].setUndefined(); // rval
1468 argv
[1].setUndefined(); // this
1469 argv
[2].setObject(*obj
); // arg0
1470 if (!js::obj_keys(cx
, 1, argv
.begin())) {
1473 return argv
[0].toObjectOrNull();
1476 bool ObjectKeysLength(JSContext
* cx
, HandleObject obj
, int32_t* length
) {
1477 MOZ_ASSERT(!obj
->is
<ProxyObject
>());
1478 return js::obj_keys_length(cx
, obj
, *length
);
1481 void JitValuePreWriteBarrier(JSRuntime
* rt
, Value
* vp
) {
1482 AutoUnsafeCallWithABI unsafe
;
1483 MOZ_ASSERT(vp
->isGCThing());
1484 MOZ_ASSERT(!vp
->toGCThing()->isMarkedBlack());
1485 gc::ValuePreWriteBarrier(*vp
);
1488 void JitStringPreWriteBarrier(JSRuntime
* rt
, JSString
** stringp
) {
1489 AutoUnsafeCallWithABI unsafe
;
1490 MOZ_ASSERT(*stringp
);
1491 MOZ_ASSERT(!(*stringp
)->isMarkedBlack());
1492 gc::PreWriteBarrier(*stringp
);
1495 void JitObjectPreWriteBarrier(JSRuntime
* rt
, JSObject
** objp
) {
1496 AutoUnsafeCallWithABI unsafe
;
1498 MOZ_ASSERT(!(*objp
)->isMarkedBlack());
1499 gc::PreWriteBarrier(*objp
);
1502 void JitShapePreWriteBarrier(JSRuntime
* rt
, Shape
** shapep
) {
1503 AutoUnsafeCallWithABI unsafe
;
1504 MOZ_ASSERT(!(*shapep
)->isMarkedBlack());
1505 gc::PreWriteBarrier(*shapep
);
1508 void JitWasmAnyRefPreWriteBarrier(JSRuntime
* rt
, wasm::AnyRef
* refp
) {
1509 AutoUnsafeCallWithABI unsafe
;
1510 MOZ_ASSERT(refp
->isGCThing());
1511 MOZ_ASSERT(!(*refp
).toGCThing()->isMarkedBlack());
1512 gc::WasmAnyRefPreWriteBarrier(*refp
);
1515 bool ThrowRuntimeLexicalError(JSContext
* cx
, unsigned errorNumber
) {
1516 ScriptFrameIter
iter(cx
);
1517 RootedScript
script(cx
, iter
.script());
1518 ReportRuntimeLexicalError(cx
, errorNumber
, script
, iter
.pc());
1522 bool ThrowBadDerivedReturnOrUninitializedThis(JSContext
* cx
, HandleValue v
) {
1523 MOZ_ASSERT(!v
.isObject());
1524 if (v
.isUndefined()) {
1525 return js::ThrowUninitializedThis(cx
);
1528 ReportValueError(cx
, JSMSG_BAD_DERIVED_RETURN
, JSDVG_IGNORE_STACK
, v
,
1533 bool BaselineGetFunctionThis(JSContext
* cx
, BaselineFrame
* frame
,
1534 MutableHandleValue res
) {
1535 return GetFunctionThis(cx
, frame
, res
);
1538 bool CallNativeGetter(JSContext
* cx
, HandleFunction callee
,
1539 HandleValue receiver
, MutableHandleValue result
) {
1540 AutoRealm
ar(cx
, callee
);
1542 MOZ_ASSERT(callee
->isNativeFun());
1543 JSNative natfun
= callee
->native();
1545 JS::RootedValueArray
<2> vp(cx
);
1546 vp
[0].setObject(*callee
.get());
1547 vp
[1].set(receiver
);
1549 if (!natfun(cx
, 0, vp
.begin())) {
1557 bool CallDOMGetter(JSContext
* cx
, const JSJitInfo
* info
, HandleObject obj
,
1558 MutableHandleValue result
) {
1559 MOZ_ASSERT(info
->type() == JSJitInfo::Getter
);
1560 MOZ_ASSERT(obj
->is
<NativeObject
>());
1561 MOZ_ASSERT(obj
->getClass()->isDOMClass());
1562 MOZ_ASSERT(obj
->as
<NativeObject
>().numFixedSlots() > 0);
1565 DOMInstanceClassHasProtoAtDepth instanceChecker
=
1566 cx
->runtime()->DOMcallbacks
->instanceClassMatchesProto
;
1567 MOZ_ASSERT(instanceChecker(obj
->getClass(), info
->protoID
, info
->depth
));
1570 // Loading DOM_OBJECT_SLOT, which must be the first slot.
1571 JS::Value val
= JS::GetReservedSlot(obj
, 0);
1572 JSJitGetterOp getter
= info
->getter
;
1573 return getter(cx
, obj
, val
.toPrivate(), JSJitGetterCallArgs(result
));
1576 bool CallNativeSetter(JSContext
* cx
, HandleFunction callee
, HandleObject obj
,
1578 AutoRealm
ar(cx
, callee
);
1580 MOZ_ASSERT(callee
->isNativeFun());
1581 JSNative natfun
= callee
->native();
1583 JS::RootedValueArray
<3> vp(cx
);
1584 vp
[0].setObject(*callee
.get());
1585 vp
[1].setObject(*obj
.get());
1588 return natfun(cx
, 1, vp
.begin());
1591 bool CallDOMSetter(JSContext
* cx
, const JSJitInfo
* info
, HandleObject obj
,
1592 HandleValue value
) {
1593 MOZ_ASSERT(info
->type() == JSJitInfo::Setter
);
1594 MOZ_ASSERT(obj
->is
<NativeObject
>());
1595 MOZ_ASSERT(obj
->getClass()->isDOMClass());
1596 MOZ_ASSERT(obj
->as
<NativeObject
>().numFixedSlots() > 0);
1599 DOMInstanceClassHasProtoAtDepth instanceChecker
=
1600 cx
->runtime()->DOMcallbacks
->instanceClassMatchesProto
;
1601 MOZ_ASSERT(instanceChecker(obj
->getClass(), info
->protoID
, info
->depth
));
1604 // Loading DOM_OBJECT_SLOT, which must be the first slot.
1605 JS::Value val
= JS::GetReservedSlot(obj
, 0);
1606 JSJitSetterOp setter
= info
->setter
;
1608 RootedValue
v(cx
, value
);
1609 return setter(cx
, obj
, val
.toPrivate(), JSJitSetterCallArgs(&v
));
1612 bool EqualStringsHelperPure(JSString
* str1
, JSString
* str2
) {
1613 // IC code calls this directly so we shouldn't GC.
1614 AutoUnsafeCallWithABI unsafe
;
1616 MOZ_ASSERT(str1
->isAtom());
1617 MOZ_ASSERT(!str2
->isAtom());
1618 MOZ_ASSERT(str1
->length() == str2
->length());
1620 // ensureLinear is intentionally called with a nullptr to avoid OOM
1621 // reporting; if it fails, we will continue to the next stub.
1622 JSLinearString
* str2Linear
= str2
->ensureLinear(nullptr);
1627 return EqualChars(&str1
->asLinear(), str2Linear
);
1630 static bool MaybeTypedArrayIndexString(jsid id
) {
1631 MOZ_ASSERT(id
.isAtom() || id
.isSymbol());
1633 if (MOZ_LIKELY(id
.isAtom())) {
1634 JSAtom
* str
= id
.toAtom();
1635 if (str
->length() > 0) {
1636 // Only check the first character because we want this function to be
1638 return CanStartTypedArrayIndex(str
->latin1OrTwoByteChar(0));
1644 static void VerifyCacheEntry(JSContext
* cx
, NativeObject
* obj
, PropertyKey key
,
1645 const MegamorphicCacheEntry
& entry
) {
1647 if (entry
.isMissingProperty()) {
1649 PropertyResult prop
;
1650 MOZ_ASSERT(LookupPropertyPure(cx
, obj
, key
, &pobj
, &prop
));
1651 MOZ_ASSERT(prop
.isNotFound());
1654 if (entry
.isMissingOwnProperty()) {
1655 MOZ_ASSERT(!obj
->containsPure(key
));
1658 MOZ_ASSERT(entry
.isDataProperty());
1659 for (size_t i
= 0, numHops
= entry
.numHops(); i
< numHops
; i
++) {
1660 MOZ_ASSERT(!obj
->containsPure(key
));
1661 obj
= &obj
->staticPrototype()->as
<NativeObject
>();
1663 mozilla::Maybe
<PropertyInfo
> prop
= obj
->lookupPure(key
);
1664 MOZ_ASSERT(prop
.isSome());
1665 MOZ_ASSERT(prop
->isDataProperty());
1666 MOZ_ASSERT(obj
->getTaggedSlotOffset(prop
->slot()) == entry
.slotOffset());
1670 static MOZ_ALWAYS_INLINE
bool GetNativeDataPropertyPureImpl(
1671 JSContext
* cx
, JSObject
* obj
, jsid id
, MegamorphicCacheEntry
* entry
,
1673 MOZ_ASSERT(obj
->is
<NativeObject
>());
1674 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
1675 Shape
* receiverShape
= obj
->shape();
1676 MegamorphicCache
& cache
= cx
->caches().megamorphicCache
;
1682 MOZ_ASSERT(!nobj
->getOpsLookupProperty());
1685 if (PropMap
* map
= nobj
->shape()->lookup(cx
, id
, &index
)) {
1686 PropertyInfo prop
= map
->getPropertyInfo(index
);
1687 if (!prop
.isDataProperty()) {
1690 TaggedSlotOffset offset
= nobj
->getTaggedSlotOffset(prop
.slot());
1691 cache
.initEntryForDataProperty(entry
, receiverShape
, id
, numHops
, offset
);
1692 *vp
= nobj
->getSlot(prop
.slot());
1696 // Property not found. Watch out for Class hooks and TypedArrays.
1697 if (MOZ_UNLIKELY(!nobj
->is
<PlainObject
>())) {
1698 if (ClassMayResolveId(cx
->names(), nobj
->getClass(), id
, nobj
)) {
1702 // Don't skip past TypedArrayObjects if the id can be a TypedArray index.
1703 if (nobj
->is
<TypedArrayObject
>()) {
1704 if (MaybeTypedArrayIndexString(id
)) {
1710 JSObject
* proto
= nobj
->staticPrototype();
1712 cache
.initEntryForMissingProperty(entry
, receiverShape
, id
);
1717 if (!proto
->is
<NativeObject
>()) {
1720 nobj
= &proto
->as
<NativeObject
>();
1725 bool GetNativeDataPropertyPureWithCacheLookup(JSContext
* cx
, JSObject
* obj
,
1727 MegamorphicCacheEntry
* entry
,
1729 AutoUnsafeCallWithABI unsafe
;
1731 // If we're on x86, we didn't have enough registers to populate this
1732 // directly in Baseline JITted code, so we do the lookup here.
1733 Shape
* receiverShape
= obj
->shape();
1734 MegamorphicCache
& cache
= cx
->caches().megamorphicCache
;
1736 if (cache
.lookup(receiverShape
, id
, &entry
)) {
1737 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
1738 VerifyCacheEntry(cx
, nobj
, id
, *entry
);
1739 if (entry
->isDataProperty()) {
1740 for (size_t i
= 0, numHops
= entry
->numHops(); i
< numHops
; i
++) {
1741 nobj
= &nobj
->staticPrototype()->as
<NativeObject
>();
1743 uint32_t offset
= entry
->slotOffset().offset();
1744 if (entry
->slotOffset().isFixedSlot()) {
1745 size_t index
= NativeObject::getFixedSlotIndexFromOffset(offset
);
1746 *vp
= nobj
->getFixedSlot(index
);
1748 size_t index
= NativeObject::getDynamicSlotIndexFromOffset(offset
);
1749 *vp
= nobj
->getDynamicSlot(index
);
1753 if (entry
->isMissingProperty()) {
1757 MOZ_ASSERT(entry
->isMissingOwnProperty());
1760 return GetNativeDataPropertyPureImpl(cx
, obj
, id
, entry
, vp
);
1763 bool CheckProxyGetByValueResult(JSContext
* cx
, HandleObject obj
,
1764 HandleValue idVal
, HandleValue value
,
1765 MutableHandleValue result
) {
1766 MOZ_ASSERT(idVal
.isString() || idVal
.isSymbol());
1767 RootedId
rootedId(cx
);
1768 if (!PrimitiveValueToId
<CanGC
>(cx
, idVal
, &rootedId
)) {
1773 ScriptedProxyHandler::checkGetTrapResult(cx
, obj
, rootedId
, value
);
1774 if (validation
!= ScriptedProxyHandler::GetTrapValidationResult::OK
) {
1775 ScriptedProxyHandler::reportGetTrapValidationError(cx
, rootedId
,
1783 bool GetNativeDataPropertyPure(JSContext
* cx
, JSObject
* obj
, PropertyKey id
,
1784 MegamorphicCacheEntry
* entry
, Value
* vp
) {
1785 AutoUnsafeCallWithABI unsafe
;
1786 return GetNativeDataPropertyPureImpl(cx
, obj
, id
, entry
, vp
);
1789 static MOZ_ALWAYS_INLINE
bool ValueToAtomOrSymbolPure(JSContext
* cx
,
1792 if (MOZ_LIKELY(idVal
.isString())) {
1793 JSString
* s
= idVal
.toString();
1796 atom
= &s
->asAtom();
1798 atom
= AtomizeString(cx
, s
);
1800 cx
->recoverFromOutOfMemory();
1805 // Watch out for integer ids because they may be stored in dense elements.
1806 static_assert(PropertyKey::IntMin
== 0);
1807 static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT
< PropertyKey::IntMax
,
1808 "All dense elements must have integer jsids");
1810 if (MOZ_UNLIKELY(atom
->isIndex(&index
) && index
<= PropertyKey::IntMax
)) {
1814 *id
= PropertyKey::NonIntAtom(atom
);
1818 if (idVal
.isSymbol()) {
1819 *id
= PropertyKey::Symbol(idVal
.toSymbol());
1823 if (idVal
.isNull()) {
1824 *id
= PropertyKey::NonIntAtom(cx
->names().null
);
1828 if (idVal
.isUndefined()) {
1829 *id
= PropertyKey::NonIntAtom(cx
->names().undefined
);
1836 bool GetNativeDataPropertyByValuePure(JSContext
* cx
, JSObject
* obj
,
1837 MegamorphicCacheEntry
* entry
, Value
* vp
) {
1838 AutoUnsafeCallWithABI unsafe
;
1840 // vp[0] contains the id, result will be stored in vp[1].
1841 Value idVal
= vp
[0];
1843 if (!ValueToAtomOrSymbolPure(cx
, idVal
, &id
)) {
1847 Shape
* receiverShape
= obj
->shape();
1848 MegamorphicCache
& cache
= cx
->caches().megamorphicCache
;
1850 cache
.lookup(receiverShape
, id
, &entry
);
1853 Value
* res
= vp
+ 1;
1854 return GetNativeDataPropertyPureImpl(cx
, obj
, id
, entry
, res
);
1857 bool ObjectHasGetterSetterPure(JSContext
* cx
, JSObject
* objArg
, jsid id
,
1858 GetterSetter
* getterSetter
) {
1859 AutoUnsafeCallWithABI unsafe
;
1861 // Window objects may require outerizing (passing the WindowProxy to the
1862 // getter/setter), so we don't support them here.
1863 if (MOZ_UNLIKELY(!objArg
->is
<NativeObject
>() || IsWindow(objArg
))) {
1867 NativeObject
* nobj
= &objArg
->as
<NativeObject
>();
1871 if (PropMap
* map
= nobj
->shape()->lookup(cx
, id
, &index
)) {
1872 PropertyInfo prop
= map
->getPropertyInfo(index
);
1873 if (!prop
.isAccessorProperty()) {
1876 GetterSetter
* actualGetterSetter
= nobj
->getGetterSetter(prop
);
1877 if (actualGetterSetter
== getterSetter
) {
1880 return (actualGetterSetter
->getter() == getterSetter
->getter() &&
1881 actualGetterSetter
->setter() == getterSetter
->setter());
1884 // Property not found. Watch out for Class hooks.
1885 if (!nobj
->is
<PlainObject
>()) {
1886 if (ClassMayResolveId(cx
->names(), nobj
->getClass(), id
, nobj
)) {
1891 JSObject
* proto
= nobj
->staticPrototype();
1896 if (!proto
->is
<NativeObject
>()) {
1899 nobj
= &proto
->as
<NativeObject
>();
1903 template <bool HasOwn
>
1904 bool HasNativeDataPropertyPure(JSContext
* cx
, JSObject
* obj
,
1905 MegamorphicCacheEntry
* entry
, Value
* vp
) {
1906 AutoUnsafeCallWithABI unsafe
;
1908 // vp[0] contains the id, result will be stored in vp[1].
1909 Value idVal
= vp
[0];
1911 if (!ValueToAtomOrSymbolPure(cx
, idVal
, &id
)) {
1915 MegamorphicCache
& cache
= cx
->caches().megamorphicCache
;
1916 Shape
* receiverShape
= obj
->shape();
1918 if (cache
.lookup(receiverShape
, id
, &entry
)) {
1919 VerifyCacheEntry(cx
, &obj
->as
<NativeObject
>(), id
, *entry
);
1925 if (MOZ_UNLIKELY(!obj
->is
<NativeObject
>())) {
1929 MOZ_ASSERT(!obj
->getOpsLookupProperty());
1931 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
1933 if (PropMap
* map
= nobj
->shape()->lookup(cx
, id
, &index
)) {
1934 PropertyInfo prop
= map
->getPropertyInfo(index
);
1935 if (prop
.isDataProperty()) {
1936 TaggedSlotOffset offset
= nobj
->getTaggedSlotOffset(prop
.slot());
1937 cache
.initEntryForDataProperty(entry
, receiverShape
, id
, numHops
,
1940 vp
[1].setBoolean(true);
1944 // Property not found. Watch out for Class hooks and TypedArrays.
1945 if (MOZ_UNLIKELY(!obj
->is
<PlainObject
>())) {
1946 // Fail if there's a resolve hook, unless the mayResolve hook tells us
1947 // the resolve hook won't define a property with this id.
1948 if (ClassMayResolveId(cx
->names(), obj
->getClass(), id
, obj
)) {
1952 // Don't skip past TypedArrayObjects if the id can be a TypedArray
1954 if (obj
->is
<TypedArrayObject
>()) {
1955 if (MaybeTypedArrayIndexString(id
)) {
1961 // If implementing Object.hasOwnProperty, don't follow protochain.
1962 if constexpr (HasOwn
) {
1966 // Get prototype. Objects that may allow dynamic prototypes are already
1967 // filtered out above.
1968 obj
= obj
->staticPrototype();
1972 // Missing property.
1974 if constexpr (HasOwn
) {
1975 cache
.initEntryForMissingOwnProperty(entry
, receiverShape
, id
);
1977 cache
.initEntryForMissingProperty(entry
, receiverShape
, id
);
1980 vp
[1].setBoolean(false);
1984 template bool HasNativeDataPropertyPure
<true>(JSContext
* cx
, JSObject
* obj
,
1985 MegamorphicCacheEntry
* entry
,
1988 template bool HasNativeDataPropertyPure
<false>(JSContext
* cx
, JSObject
* obj
,
1989 MegamorphicCacheEntry
* entry
,
1992 bool HasNativeElementPure(JSContext
* cx
, NativeObject
* obj
, int32_t index
,
1994 AutoUnsafeCallWithABI unsafe
;
1996 MOZ_ASSERT(obj
->is
<NativeObject
>());
1997 MOZ_ASSERT(!obj
->getOpsHasProperty());
1998 MOZ_ASSERT(!obj
->getOpsLookupProperty());
1999 MOZ_ASSERT(!obj
->getOpsGetOwnPropertyDescriptor());
2001 if (MOZ_UNLIKELY(index
< 0)) {
2005 if (obj
->containsDenseElement(index
)) {
2006 vp
[0].setBoolean(true);
2010 jsid id
= PropertyKey::Int(index
);
2012 if (obj
->shape()->lookup(cx
, id
, &unused
)) {
2013 vp
[0].setBoolean(true);
2017 // Fail if there's a resolve hook, unless the mayResolve hook tells
2018 // us the resolve hook won't define a property with this id.
2019 if (MOZ_UNLIKELY(ClassMayResolveId(cx
->names(), obj
->getClass(), id
, obj
))) {
2022 // TypedArrayObject are also native and contain indexed properties.
2023 if (MOZ_UNLIKELY(obj
->is
<TypedArrayObject
>())) {
2024 size_t length
= obj
->as
<TypedArrayObject
>().length().valueOr(0);
2025 vp
[0].setBoolean(uint32_t(index
) < length
);
2029 vp
[0].setBoolean(false);
2033 // Fast path for setting/adding a plain object property. This is the common case
2034 // for megamorphic SetProp/SetElem.
2035 template <bool UseCache
>
2036 static bool TryAddOrSetPlainObjectProperty(JSContext
* cx
,
2037 Handle
<PlainObject
*> obj
,
2038 PropertyKey key
, HandleValue value
,
2040 MOZ_ASSERT(!*optimized
);
2042 Shape
* receiverShape
= obj
->shape();
2043 MegamorphicSetPropCache
& cache
= *cx
->caches().megamorphicSetPropCache
;
2046 if constexpr (UseCache
) {
2047 MegamorphicSetPropCache::Entry
* entry
;
2048 if (cache
.lookup(receiverShape
, key
, &entry
)) {
2049 if (entry
->afterShape() != nullptr) { // AddProp
2050 NativeObject
* holder
= nullptr;
2051 PropertyResult prop
;
2052 MOZ_ASSERT(LookupPropertyPure(cx
, obj
, key
, &holder
, &prop
));
2053 MOZ_ASSERT(obj
!= holder
);
2054 MOZ_ASSERT_IF(prop
.isFound(),
2055 prop
.isNativeProperty() &&
2056 prop
.propertyInfo().isDataProperty() &&
2057 prop
.propertyInfo().writable());
2059 mozilla::Maybe
<PropertyInfo
> prop
= obj
->lookupPure(key
);
2060 MOZ_ASSERT(prop
.isSome());
2061 MOZ_ASSERT(prop
->isDataProperty());
2062 MOZ_ASSERT(obj
->getTaggedSlotOffset(prop
->slot()) ==
2063 entry
->slotOffset());
2069 // Fast path for changing a data property.
2071 if (PropMap
* map
= obj
->shape()->lookup(cx
, key
, &index
)) {
2072 PropertyInfo prop
= map
->getPropertyInfo(index
);
2073 if (!prop
.isDataProperty() || !prop
.writable()) {
2076 obj
->setSlot(prop
.slot(), value
);
2077 if (!Watchtower::watchPropertyModification
<AllowGC::NoGC
>(cx
, obj
, key
)) {
2082 if constexpr (UseCache
) {
2083 TaggedSlotOffset offset
= obj
->getTaggedSlotOffset(prop
.slot());
2084 cache
.set(receiverShape
, nullptr, key
, offset
, 0);
2089 // Don't support "__proto__". This lets us take advantage of the
2090 // hasNonWritableOrAccessorPropExclProto optimization below.
2091 if (MOZ_UNLIKELY(!obj
->isExtensible() || key
.isAtom(cx
->names().proto_
))) {
2095 // Ensure the proto chain contains only plain objects. Deoptimize for accessor
2096 // properties and non-writable data properties (we can't shadow non-writable
2098 JSObject
* proto
= obj
->staticPrototype();
2100 if (!proto
->is
<PlainObject
>()) {
2103 PlainObject
* plainProto
= &proto
->as
<PlainObject
>();
2104 if (plainProto
->hasNonWritableOrAccessorPropExclProto()) {
2106 if (PropMap
* map
= plainProto
->shape()->lookup(cx
, key
, &index
)) {
2107 PropertyInfo prop
= map
->getPropertyInfo(index
);
2108 if (!prop
.isDataProperty() || !prop
.writable()) {
2114 proto
= plainProto
->staticPrototype();
2118 // At this point either the property is missing or it's a writable data
2119 // property on the proto chain that we can shadow.
2121 NativeObject
* holder
= nullptr;
2122 PropertyResult prop
;
2123 MOZ_ASSERT(LookupPropertyPure(cx
, obj
, key
, &holder
, &prop
));
2124 MOZ_ASSERT(obj
!= holder
);
2125 MOZ_ASSERT_IF(prop
.isFound(), prop
.isNativeProperty() &&
2126 prop
.propertyInfo().isDataProperty() &&
2127 prop
.propertyInfo().writable());
2132 Rooted
<PropertyKey
> keyRoot(cx
, key
);
2133 Rooted
<Shape
*> receiverShapeRoot(cx
, receiverShape
);
2134 uint32_t resultSlot
= 0;
2135 size_t numDynamic
= obj
->numDynamicSlots();
2136 bool res
= AddDataPropertyToPlainObject(cx
, obj
, keyRoot
, value
, &resultSlot
);
2138 if constexpr (UseCache
) {
2139 if (res
&& obj
->shape()->isShared() &&
2140 resultSlot
< SharedPropMap::MaxPropsForNonDictionary
&&
2141 !Watchtower::watchesPropertyAdd(obj
)) {
2142 TaggedSlotOffset offset
= obj
->getTaggedSlotOffset(resultSlot
);
2143 uint32_t newCapacity
= 0;
2144 if (!(resultSlot
< obj
->numFixedSlots() ||
2145 (resultSlot
- obj
->numFixedSlots()) < numDynamic
)) {
2146 newCapacity
= obj
->numDynamicSlots();
2148 cache
.set(receiverShapeRoot
, obj
->shape(), keyRoot
, offset
, newCapacity
);
2155 template <bool Cached
>
2156 bool SetElementMegamorphic(JSContext
* cx
, HandleObject obj
, HandleValue index
,
2157 HandleValue value
, bool strict
) {
2158 if (obj
->is
<PlainObject
>()) {
2160 if (ValueToAtomOrSymbolPure(cx
, index
, &key
)) {
2161 bool optimized
= false;
2162 if (!TryAddOrSetPlainObjectProperty
<Cached
>(cx
, obj
.as
<PlainObject
>(),
2163 key
, value
, &optimized
)) {
2171 Rooted
<Value
> receiver(cx
, ObjectValue(*obj
));
2172 return SetObjectElementWithReceiver(cx
, obj
, index
, value
, receiver
, strict
);
2175 template bool SetElementMegamorphic
<false>(JSContext
* cx
, HandleObject obj
,
2176 HandleValue index
, HandleValue value
,
2178 template bool SetElementMegamorphic
<true>(JSContext
* cx
, HandleObject obj
,
2179 HandleValue index
, HandleValue value
,
2182 template <bool Cached
>
2183 bool SetPropertyMegamorphic(JSContext
* cx
, HandleObject obj
, HandleId id
,
2184 HandleValue value
, bool strict
) {
2185 if (obj
->is
<PlainObject
>()) {
2186 bool optimized
= false;
2187 if (!TryAddOrSetPlainObjectProperty
<Cached
>(cx
, obj
.as
<PlainObject
>(), id
,
2188 value
, &optimized
)) {
2195 Rooted
<Value
> receiver(cx
, ObjectValue(*obj
));
2196 ObjectOpResult result
;
2197 return SetProperty(cx
, obj
, id
, value
, receiver
, result
) &&
2198 result
.checkStrictModeError(cx
, obj
, id
, strict
);
2201 template bool SetPropertyMegamorphic
<false>(JSContext
* cx
, HandleObject obj
,
2202 HandleId id
, HandleValue value
,
2204 template bool SetPropertyMegamorphic
<true>(JSContext
* cx
, HandleObject obj
,
2205 HandleId id
, HandleValue value
,
2208 void HandleCodeCoverageAtPC(BaselineFrame
* frame
, jsbytecode
* pc
) {
2209 AutoUnsafeCallWithABI
unsafe(UnsafeABIStrictness::AllowPendingExceptions
);
2211 MOZ_ASSERT(frame
->runningInInterpreter());
2213 JSScript
* script
= frame
->script();
2214 MOZ_ASSERT(pc
== script
->main() || BytecodeIsJumpTarget(JSOp(*pc
)));
2216 if (!script
->hasScriptCounts()) {
2217 if (!script
->realm()->collectCoverageForDebug()) {
2220 JSContext
* cx
= script
->runtimeFromMainThread()->mainContextFromOwnThread();
2221 AutoEnterOOMUnsafeRegion oomUnsafe
;
2222 if (!script
->initScriptCounts(cx
)) {
2223 oomUnsafe
.crash("initScriptCounts");
2227 PCCounts
* counts
= script
->maybeGetPCCounts(pc
);
2229 counts
->numExec()++;
2232 void HandleCodeCoverageAtPrologue(BaselineFrame
* frame
) {
2233 AutoUnsafeCallWithABI unsafe
;
2235 MOZ_ASSERT(frame
->runningInInterpreter());
2237 JSScript
* script
= frame
->script();
2238 jsbytecode
* main
= script
->main();
2239 if (!BytecodeIsJumpTarget(JSOp(*main
))) {
2240 HandleCodeCoverageAtPC(frame
, main
);
2244 JSString
* TypeOfNameObject(JSObject
* obj
, JSRuntime
* rt
) {
2245 AutoUnsafeCallWithABI unsafe
;
2246 JSType type
= js::TypeOfObject(obj
);
2247 return TypeName(type
, *rt
->commonNames
);
2250 bool GetPrototypeOf(JSContext
* cx
, HandleObject target
,
2251 MutableHandleValue rval
) {
2252 MOZ_ASSERT(target
->hasDynamicPrototype());
2254 RootedObject
proto(cx
);
2255 if (!GetPrototype(cx
, target
, &proto
)) {
2258 rval
.setObjectOrNull(proto
);
2262 static JSString
* ConvertObjectToStringForConcat(JSContext
* cx
,
2264 MOZ_ASSERT(obj
.isObject());
2265 RootedValue
rootedObj(cx
, obj
);
2266 if (!ToPrimitive(cx
, &rootedObj
)) {
2269 return ToString
<CanGC
>(cx
, rootedObj
);
2272 bool DoConcatStringObject(JSContext
* cx
, HandleValue lhs
, HandleValue rhs
,
2273 MutableHandleValue res
) {
2274 JSString
* lstr
= nullptr;
2275 JSString
* rstr
= nullptr;
2277 if (lhs
.isString()) {
2278 // Convert rhs first.
2279 MOZ_ASSERT(lhs
.isString() && rhs
.isObject());
2280 rstr
= ConvertObjectToStringForConcat(cx
, rhs
);
2285 // lhs is already string.
2286 lstr
= lhs
.toString();
2288 MOZ_ASSERT(rhs
.isString() && lhs
.isObject());
2289 // Convert lhs first.
2290 lstr
= ConvertObjectToStringForConcat(cx
, lhs
);
2295 // rhs is already string.
2296 rstr
= rhs
.toString();
2299 JSString
* str
= ConcatStrings
<NoGC
>(cx
, lstr
, rstr
);
2301 RootedString
nlstr(cx
, lstr
), nrstr(cx
, rstr
);
2302 str
= ConcatStrings
<CanGC
>(cx
, nlstr
, nrstr
);
2312 bool IsPossiblyWrappedTypedArray(JSContext
* cx
, JSObject
* obj
, bool* result
) {
2313 JSObject
* unwrapped
= CheckedUnwrapDynamic(obj
, cx
);
2315 ReportAccessDenied(cx
);
2319 *result
= unwrapped
->is
<TypedArrayObject
>();
2323 // Called from CreateDependentString::generateFallback.
2324 void* AllocateDependentString(JSContext
* cx
) {
2325 AutoUnsafeCallWithABI unsafe
;
2326 return cx
->newCell
<JSDependentString
, NoGC
>(js::gc::Heap::Default
);
2328 void* AllocateFatInlineString(JSContext
* cx
) {
2329 AutoUnsafeCallWithABI unsafe
;
2330 return cx
->newCell
<JSFatInlineString
, NoGC
>(js::gc::Heap::Default
);
2333 // Called to allocate a BigInt if inline allocation failed.
2334 void* AllocateBigIntNoGC(JSContext
* cx
, bool requestMinorGC
) {
2335 AutoUnsafeCallWithABI unsafe
;
2337 if (requestMinorGC
&& cx
->nursery().isEnabled()) {
2338 cx
->nursery().requestMinorGC(JS::GCReason::OUT_OF_NURSERY
);
2341 return cx
->newCell
<JS::BigInt
, NoGC
>(js::gc::Heap::Tenured
);
2344 void AllocateAndInitTypedArrayBuffer(JSContext
* cx
, TypedArrayObject
* obj
,
2346 AutoUnsafeCallWithABI unsafe
;
2348 // Initialize the data slot to UndefinedValue to signal to our JIT caller that
2349 // the allocation failed if the slot isn't overwritten below.
2350 obj
->initFixedSlot(TypedArrayObject::DATA_SLOT
, UndefinedValue());
2352 // Negative numbers or zero will bail out to the slow path, which in turn will
2353 // raise an invalid argument exception or create a correct object with zero
2355 constexpr size_t byteLengthLimit
= TypedArrayObject::ByteLengthLimit
;
2356 if (count
<= 0 || size_t(count
) > byteLengthLimit
/ obj
->bytesPerElement()) {
2357 obj
->setFixedSlot(TypedArrayObject::LENGTH_SLOT
, PrivateValue(size_t(0)));
2361 obj
->setFixedSlot(TypedArrayObject::LENGTH_SLOT
, PrivateValue(count
));
2363 size_t nbytes
= size_t(count
) * obj
->bytesPerElement();
2364 MOZ_ASSERT(nbytes
<= byteLengthLimit
);
2365 nbytes
= RoundUp(nbytes
, sizeof(Value
));
2367 MOZ_ASSERT(!obj
->isTenured());
2368 void* buf
= cx
->nursery().allocateZeroedBuffer(obj
, nbytes
,
2369 js::ArrayBufferContentsArena
);
2371 InitReservedSlot(obj
, TypedArrayObject::DATA_SLOT
, buf
, nbytes
,
2372 MemoryUse::TypedArrayElements
);
2377 void TraceCreateObject(JSObject
* obj
) {
2378 AutoUnsafeCallWithABI unsafe
;
2379 js::gc::gcprobes::CreateObject(obj
);
2383 #if JS_BITS_PER_WORD == 32
2384 BigInt
* CreateBigIntFromInt64(JSContext
* cx
, uint32_t low
, uint32_t high
) {
2385 uint64_t n
= (static_cast<uint64_t>(high
) << 32) + low
;
2386 return js::BigInt::createFromInt64(cx
, n
);
2389 BigInt
* CreateBigIntFromUint64(JSContext
* cx
, uint32_t low
, uint32_t high
) {
2390 uint64_t n
= (static_cast<uint64_t>(high
) << 32) + low
;
2391 return js::BigInt::createFromUint64(cx
, n
);
2394 BigInt
* CreateBigIntFromInt64(JSContext
* cx
, uint64_t i64
) {
2395 return js::BigInt::createFromInt64(cx
, i64
);
2398 BigInt
* CreateBigIntFromUint64(JSContext
* cx
, uint64_t i64
) {
2399 return js::BigInt::createFromUint64(cx
, i64
);
2403 bool DoStringToInt64(JSContext
* cx
, HandleString str
, uint64_t* res
) {
2405 JS_TRY_VAR_OR_RETURN_FALSE(cx
, bi
, js::StringToBigInt(cx
, str
));
2408 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2409 JSMSG_BIGINT_INVALID_SYNTAX
);
2413 *res
= js::BigInt::toUint64(bi
);
2417 template <EqualityKind Kind
>
2418 bool BigIntEqual(BigInt
* x
, BigInt
* y
) {
2419 AutoUnsafeCallWithABI unsafe
;
2420 bool res
= BigInt::equal(x
, y
);
2421 if (Kind
!= EqualityKind::Equal
) {
2427 template bool BigIntEqual
<EqualityKind::Equal
>(BigInt
* x
, BigInt
* y
);
2428 template bool BigIntEqual
<EqualityKind::NotEqual
>(BigInt
* x
, BigInt
* y
);
2430 template <ComparisonKind Kind
>
2431 bool BigIntCompare(BigInt
* x
, BigInt
* y
) {
2432 AutoUnsafeCallWithABI unsafe
;
2433 bool res
= BigInt::lessThan(x
, y
);
2434 if (Kind
!= ComparisonKind::LessThan
) {
2440 template bool BigIntCompare
<ComparisonKind::LessThan
>(BigInt
* x
, BigInt
* y
);
2441 template bool BigIntCompare
<ComparisonKind::GreaterThanOrEqual
>(BigInt
* x
,
2444 template <EqualityKind Kind
>
2445 bool BigIntNumberEqual(BigInt
* x
, double y
) {
2446 AutoUnsafeCallWithABI unsafe
;
2447 bool res
= BigInt::equal(x
, y
);
2448 if (Kind
!= EqualityKind::Equal
) {
2454 template bool BigIntNumberEqual
<EqualityKind::Equal
>(BigInt
* x
, double y
);
2455 template bool BigIntNumberEqual
<EqualityKind::NotEqual
>(BigInt
* x
, double y
);
2457 template <ComparisonKind Kind
>
2458 bool BigIntNumberCompare(BigInt
* x
, double y
) {
2459 AutoUnsafeCallWithABI unsafe
;
2460 mozilla::Maybe
<bool> res
= BigInt::lessThan(x
, y
);
2461 if (Kind
== ComparisonKind::LessThan
) {
2462 return res
.valueOr(false);
2464 return !res
.valueOr(true);
2467 template bool BigIntNumberCompare
<ComparisonKind::LessThan
>(BigInt
* x
,
2469 template bool BigIntNumberCompare
<ComparisonKind::GreaterThanOrEqual
>(BigInt
* x
,
2472 template <ComparisonKind Kind
>
2473 bool NumberBigIntCompare(double x
, BigInt
* y
) {
2474 AutoUnsafeCallWithABI unsafe
;
2475 mozilla::Maybe
<bool> res
= BigInt::lessThan(x
, y
);
2476 if (Kind
== ComparisonKind::LessThan
) {
2477 return res
.valueOr(false);
2479 return !res
.valueOr(true);
2482 template bool NumberBigIntCompare
<ComparisonKind::LessThan
>(double x
,
2484 template bool NumberBigIntCompare
<ComparisonKind::GreaterThanOrEqual
>(
2485 double x
, BigInt
* y
);
2487 template <EqualityKind Kind
>
2488 bool BigIntStringEqual(JSContext
* cx
, HandleBigInt x
, HandleString y
,
2490 JS_TRY_VAR_OR_RETURN_FALSE(cx
, *res
, BigInt::equal(cx
, x
, y
));
2491 if (Kind
!= EqualityKind::Equal
) {
2497 template bool BigIntStringEqual
<EqualityKind::Equal
>(JSContext
* cx
,
2499 HandleString y
, bool* res
);
2500 template bool BigIntStringEqual
<EqualityKind::NotEqual
>(JSContext
* cx
,
2505 template <ComparisonKind Kind
>
2506 bool BigIntStringCompare(JSContext
* cx
, HandleBigInt x
, HandleString y
,
2508 mozilla::Maybe
<bool> result
;
2509 if (!BigInt::lessThan(cx
, x
, y
, result
)) {
2512 if (Kind
== ComparisonKind::LessThan
) {
2513 *res
= result
.valueOr(false);
2515 *res
= !result
.valueOr(true);
2520 template bool BigIntStringCompare
<ComparisonKind::LessThan
>(JSContext
* cx
,
2524 template bool BigIntStringCompare
<ComparisonKind::GreaterThanOrEqual
>(
2525 JSContext
* cx
, HandleBigInt x
, HandleString y
, bool* res
);
2527 template <ComparisonKind Kind
>
2528 bool StringBigIntCompare(JSContext
* cx
, HandleString x
, HandleBigInt y
,
2530 mozilla::Maybe
<bool> result
;
2531 if (!BigInt::lessThan(cx
, x
, y
, result
)) {
2534 if (Kind
== ComparisonKind::LessThan
) {
2535 *res
= result
.valueOr(false);
2537 *res
= !result
.valueOr(true);
2542 template bool StringBigIntCompare
<ComparisonKind::LessThan
>(JSContext
* cx
,
2546 template bool StringBigIntCompare
<ComparisonKind::GreaterThanOrEqual
>(
2547 JSContext
* cx
, HandleString x
, HandleBigInt y
, bool* res
);
2549 BigInt
* BigIntAsIntN(JSContext
* cx
, HandleBigInt x
, int32_t bits
) {
2550 MOZ_ASSERT(bits
>= 0);
2551 return BigInt::asIntN(cx
, x
, uint64_t(bits
));
2554 BigInt
* BigIntAsUintN(JSContext
* cx
, HandleBigInt x
, int32_t bits
) {
2555 MOZ_ASSERT(bits
>= 0);
2556 return BigInt::asUintN(cx
, x
, uint64_t(bits
));
2559 template <typename T
>
2560 static int32_t AtomicsCompareExchange(TypedArrayObject
* typedArray
,
2561 size_t index
, int32_t expected
,
2562 int32_t replacement
) {
2563 AutoUnsafeCallWithABI unsafe
;
2565 MOZ_ASSERT(!typedArray
->hasDetachedBuffer());
2566 MOZ_ASSERT_IF(typedArray
->hasResizableBuffer(), !typedArray
->isOutOfBounds());
2567 MOZ_ASSERT(index
< typedArray
->length().valueOr(0));
2569 SharedMem
<T
*> addr
= typedArray
->dataPointerEither().cast
<T
*>();
2570 return jit::AtomicOperations::compareExchangeSeqCst(addr
+ index
, T(expected
),
2574 AtomicsCompareExchangeFn
AtomicsCompareExchange(Scalar::Type elementType
) {
2575 switch (elementType
) {
2577 return AtomicsCompareExchange
<int8_t>;
2579 return AtomicsCompareExchange
<uint8_t>;
2581 return AtomicsCompareExchange
<int16_t>;
2582 case Scalar::Uint16
:
2583 return AtomicsCompareExchange
<uint16_t>;
2585 return AtomicsCompareExchange
<int32_t>;
2586 case Scalar::Uint32
:
2587 return AtomicsCompareExchange
<uint32_t>;
2589 MOZ_CRASH("Unexpected TypedArray type");
2593 template <typename T
>
2594 static int32_t AtomicsExchange(TypedArrayObject
* typedArray
, size_t index
,
2596 AutoUnsafeCallWithABI unsafe
;
2598 MOZ_ASSERT(!typedArray
->hasDetachedBuffer());
2599 MOZ_ASSERT_IF(typedArray
->hasResizableBuffer(), !typedArray
->isOutOfBounds());
2600 MOZ_ASSERT(index
< typedArray
->length().valueOr(0));
2602 SharedMem
<T
*> addr
= typedArray
->dataPointerEither().cast
<T
*>();
2603 return jit::AtomicOperations::exchangeSeqCst(addr
+ index
, T(value
));
2606 AtomicsReadWriteModifyFn
AtomicsExchange(Scalar::Type elementType
) {
2607 switch (elementType
) {
2609 return AtomicsExchange
<int8_t>;
2611 return AtomicsExchange
<uint8_t>;
2613 return AtomicsExchange
<int16_t>;
2614 case Scalar::Uint16
:
2615 return AtomicsExchange
<uint16_t>;
2617 return AtomicsExchange
<int32_t>;
2618 case Scalar::Uint32
:
2619 return AtomicsExchange
<uint32_t>;
2621 MOZ_CRASH("Unexpected TypedArray type");
2625 template <typename T
>
2626 static int32_t AtomicsAdd(TypedArrayObject
* typedArray
, size_t index
,
2628 AutoUnsafeCallWithABI unsafe
;
2630 MOZ_ASSERT(!typedArray
->hasDetachedBuffer());
2631 MOZ_ASSERT_IF(typedArray
->hasResizableBuffer(), !typedArray
->isOutOfBounds());
2632 MOZ_ASSERT(index
< typedArray
->length().valueOr(0));
2634 SharedMem
<T
*> addr
= typedArray
->dataPointerEither().cast
<T
*>();
2635 return jit::AtomicOperations::fetchAddSeqCst(addr
+ index
, T(value
));
2638 AtomicsReadWriteModifyFn
AtomicsAdd(Scalar::Type elementType
) {
2639 switch (elementType
) {
2641 return AtomicsAdd
<int8_t>;
2643 return AtomicsAdd
<uint8_t>;
2645 return AtomicsAdd
<int16_t>;
2646 case Scalar::Uint16
:
2647 return AtomicsAdd
<uint16_t>;
2649 return AtomicsAdd
<int32_t>;
2650 case Scalar::Uint32
:
2651 return AtomicsAdd
<uint32_t>;
2653 MOZ_CRASH("Unexpected TypedArray type");
2657 template <typename T
>
2658 static int32_t AtomicsSub(TypedArrayObject
* typedArray
, size_t index
,
2660 AutoUnsafeCallWithABI unsafe
;
2662 MOZ_ASSERT(!typedArray
->hasDetachedBuffer());
2663 MOZ_ASSERT_IF(typedArray
->hasResizableBuffer(), !typedArray
->isOutOfBounds());
2664 MOZ_ASSERT(index
< typedArray
->length().valueOr(0));
2666 SharedMem
<T
*> addr
= typedArray
->dataPointerEither().cast
<T
*>();
2667 return jit::AtomicOperations::fetchSubSeqCst(addr
+ index
, T(value
));
2670 AtomicsReadWriteModifyFn
AtomicsSub(Scalar::Type elementType
) {
2671 switch (elementType
) {
2673 return AtomicsSub
<int8_t>;
2675 return AtomicsSub
<uint8_t>;
2677 return AtomicsSub
<int16_t>;
2678 case Scalar::Uint16
:
2679 return AtomicsSub
<uint16_t>;
2681 return AtomicsSub
<int32_t>;
2682 case Scalar::Uint32
:
2683 return AtomicsSub
<uint32_t>;
2685 MOZ_CRASH("Unexpected TypedArray type");
2689 template <typename T
>
2690 static int32_t AtomicsAnd(TypedArrayObject
* typedArray
, size_t index
,
2692 AutoUnsafeCallWithABI unsafe
;
2694 MOZ_ASSERT(!typedArray
->hasDetachedBuffer());
2695 MOZ_ASSERT_IF(typedArray
->hasResizableBuffer(), !typedArray
->isOutOfBounds());
2696 MOZ_ASSERT(index
< typedArray
->length().valueOr(0));
2698 SharedMem
<T
*> addr
= typedArray
->dataPointerEither().cast
<T
*>();
2699 return jit::AtomicOperations::fetchAndSeqCst(addr
+ index
, T(value
));
2702 AtomicsReadWriteModifyFn
AtomicsAnd(Scalar::Type elementType
) {
2703 switch (elementType
) {
2705 return AtomicsAnd
<int8_t>;
2707 return AtomicsAnd
<uint8_t>;
2709 return AtomicsAnd
<int16_t>;
2710 case Scalar::Uint16
:
2711 return AtomicsAnd
<uint16_t>;
2713 return AtomicsAnd
<int32_t>;
2714 case Scalar::Uint32
:
2715 return AtomicsAnd
<uint32_t>;
2717 MOZ_CRASH("Unexpected TypedArray type");
2721 template <typename T
>
2722 static int32_t AtomicsOr(TypedArrayObject
* typedArray
, size_t index
,
2724 AutoUnsafeCallWithABI unsafe
;
2726 MOZ_ASSERT(!typedArray
->hasDetachedBuffer());
2727 MOZ_ASSERT_IF(typedArray
->hasResizableBuffer(), !typedArray
->isOutOfBounds());
2728 MOZ_ASSERT(index
< typedArray
->length().valueOr(0));
2730 SharedMem
<T
*> addr
= typedArray
->dataPointerEither().cast
<T
*>();
2731 return jit::AtomicOperations::fetchOrSeqCst(addr
+ index
, T(value
));
2734 AtomicsReadWriteModifyFn
AtomicsOr(Scalar::Type elementType
) {
2735 switch (elementType
) {
2737 return AtomicsOr
<int8_t>;
2739 return AtomicsOr
<uint8_t>;
2741 return AtomicsOr
<int16_t>;
2742 case Scalar::Uint16
:
2743 return AtomicsOr
<uint16_t>;
2745 return AtomicsOr
<int32_t>;
2746 case Scalar::Uint32
:
2747 return AtomicsOr
<uint32_t>;
2749 MOZ_CRASH("Unexpected TypedArray type");
2753 template <typename T
>
2754 static int32_t AtomicsXor(TypedArrayObject
* typedArray
, size_t index
,
2756 AutoUnsafeCallWithABI unsafe
;
2758 MOZ_ASSERT(!typedArray
->hasDetachedBuffer());
2759 MOZ_ASSERT_IF(typedArray
->hasResizableBuffer(), !typedArray
->isOutOfBounds());
2760 MOZ_ASSERT(index
< typedArray
->length().valueOr(0));
2762 SharedMem
<T
*> addr
= typedArray
->dataPointerEither().cast
<T
*>();
2763 return jit::AtomicOperations::fetchXorSeqCst(addr
+ index
, T(value
));
2766 AtomicsReadWriteModifyFn
AtomicsXor(Scalar::Type elementType
) {
2767 switch (elementType
) {
2769 return AtomicsXor
<int8_t>;
2771 return AtomicsXor
<uint8_t>;
2773 return AtomicsXor
<int16_t>;
2774 case Scalar::Uint16
:
2775 return AtomicsXor
<uint16_t>;
2777 return AtomicsXor
<int32_t>;
2778 case Scalar::Uint32
:
2779 return AtomicsXor
<uint32_t>;
2781 MOZ_CRASH("Unexpected TypedArray type");
2785 template <typename AtomicOp
, typename
... Args
>
2786 static BigInt
* AtomicAccess64(JSContext
* cx
, TypedArrayObject
* typedArray
,
2787 size_t index
, AtomicOp op
, Args
... args
) {
2788 MOZ_ASSERT(Scalar::isBigIntType(typedArray
->type()));
2789 MOZ_ASSERT(!typedArray
->hasDetachedBuffer());
2790 MOZ_ASSERT_IF(typedArray
->hasResizableBuffer(), !typedArray
->isOutOfBounds());
2791 MOZ_ASSERT(index
< typedArray
->length().valueOr(0));
2793 if (typedArray
->type() == Scalar::BigInt64
) {
2794 SharedMem
<int64_t*> addr
= typedArray
->dataPointerEither().cast
<int64_t*>();
2795 int64_t v
= op(addr
+ index
, BigInt::toInt64(args
)...);
2796 return BigInt::createFromInt64(cx
, v
);
2799 SharedMem
<uint64_t*> addr
= typedArray
->dataPointerEither().cast
<uint64_t*>();
2800 uint64_t v
= op(addr
+ index
, BigInt::toUint64(args
)...);
2801 return BigInt::createFromUint64(cx
, v
);
2804 template <typename AtomicOp
, typename
... Args
>
2805 static auto AtomicAccess64(TypedArrayObject
* typedArray
, size_t index
,
2806 AtomicOp op
, Args
... args
) {
2807 MOZ_ASSERT(Scalar::isBigIntType(typedArray
->type()));
2808 MOZ_ASSERT(!typedArray
->hasDetachedBuffer());
2809 MOZ_ASSERT_IF(typedArray
->hasResizableBuffer(), !typedArray
->isOutOfBounds());
2810 MOZ_ASSERT(index
< typedArray
->length().valueOr(0));
2812 if (typedArray
->type() == Scalar::BigInt64
) {
2813 SharedMem
<int64_t*> addr
= typedArray
->dataPointerEither().cast
<int64_t*>();
2814 return op(addr
+ index
, BigInt::toInt64(args
)...);
2817 SharedMem
<uint64_t*> addr
= typedArray
->dataPointerEither().cast
<uint64_t*>();
2818 return op(addr
+ index
, BigInt::toUint64(args
)...);
2821 BigInt
* AtomicsLoad64(JSContext
* cx
, TypedArrayObject
* typedArray
,
2823 return AtomicAccess64(cx
, typedArray
, index
, [](auto addr
) {
2824 return jit::AtomicOperations::loadSeqCst(addr
);
2828 void AtomicsStore64(TypedArrayObject
* typedArray
, size_t index
,
2829 const BigInt
* value
) {
2830 AutoUnsafeCallWithABI unsafe
;
2834 [](auto addr
, auto val
) {
2835 jit::AtomicOperations::storeSeqCst(addr
, val
);
2840 BigInt
* AtomicsCompareExchange64(JSContext
* cx
, TypedArrayObject
* typedArray
,
2841 size_t index
, const BigInt
* expected
,
2842 const BigInt
* replacement
) {
2843 return AtomicAccess64(
2844 cx
, typedArray
, index
,
2845 [](auto addr
, auto oldval
, auto newval
) {
2846 return jit::AtomicOperations::compareExchangeSeqCst(addr
, oldval
,
2849 expected
, replacement
);
2852 BigInt
* AtomicsExchange64(JSContext
* cx
, TypedArrayObject
* typedArray
,
2853 size_t index
, const BigInt
* value
) {
2854 return AtomicAccess64(
2855 cx
, typedArray
, index
,
2856 [](auto addr
, auto val
) {
2857 return jit::AtomicOperations::exchangeSeqCst(addr
, val
);
2862 BigInt
* AtomicsAdd64(JSContext
* cx
, TypedArrayObject
* typedArray
, size_t index
,
2863 const BigInt
* value
) {
2864 return AtomicAccess64(
2865 cx
, typedArray
, index
,
2866 [](auto addr
, auto val
) {
2867 return jit::AtomicOperations::fetchAddSeqCst(addr
, val
);
2872 BigInt
* AtomicsAnd64(JSContext
* cx
, TypedArrayObject
* typedArray
, size_t index
,
2873 const BigInt
* value
) {
2874 return AtomicAccess64(
2875 cx
, typedArray
, index
,
2876 [](auto addr
, auto val
) {
2877 return jit::AtomicOperations::fetchAndSeqCst(addr
, val
);
2882 BigInt
* AtomicsOr64(JSContext
* cx
, TypedArrayObject
* typedArray
, size_t index
,
2883 const BigInt
* value
) {
2884 return AtomicAccess64(
2885 cx
, typedArray
, index
,
2886 [](auto addr
, auto val
) {
2887 return jit::AtomicOperations::fetchOrSeqCst(addr
, val
);
2892 BigInt
* AtomicsSub64(JSContext
* cx
, TypedArrayObject
* typedArray
, size_t index
,
2893 const BigInt
* value
) {
2894 return AtomicAccess64(
2895 cx
, typedArray
, index
,
2896 [](auto addr
, auto val
) {
2897 return jit::AtomicOperations::fetchSubSeqCst(addr
, val
);
2902 BigInt
* AtomicsXor64(JSContext
* cx
, TypedArrayObject
* typedArray
, size_t index
,
2903 const BigInt
* value
) {
2904 return AtomicAccess64(
2905 cx
, typedArray
, index
,
2906 [](auto addr
, auto val
) {
2907 return jit::AtomicOperations::fetchXorSeqCst(addr
, val
);
2912 JSAtom
* AtomizeStringNoGC(JSContext
* cx
, JSString
* str
) {
2913 // IC code calls this directly so we shouldn't GC.
2914 AutoUnsafeCallWithABI unsafe
;
2916 JSAtom
* atom
= AtomizeString(cx
, str
);
2918 cx
->recoverFromOutOfMemory();
2925 bool SetObjectHas(JSContext
* cx
, HandleObject obj
, HandleValue key
,
2927 return SetObject::has(cx
, obj
, key
, rval
);
2930 bool MapObjectHas(JSContext
* cx
, HandleObject obj
, HandleValue key
,
2932 return MapObject::has(cx
, obj
, key
, rval
);
2935 bool MapObjectGet(JSContext
* cx
, HandleObject obj
, HandleValue key
,
2936 MutableHandleValue rval
) {
2937 return MapObject::get(cx
, obj
, key
, rval
);
2941 template <class OrderedHashTable
>
2942 static mozilla::HashNumber
HashValue(JSContext
* cx
, OrderedHashTable
* hashTable
,
2943 const Value
* value
) {
2944 RootedValue
rootedValue(cx
, *value
);
2945 HashableValue hashable
;
2946 MOZ_ALWAYS_TRUE(hashable
.setValue(cx
, rootedValue
));
2948 return hashTable
->hash(hashable
);
2952 void AssertSetObjectHash(JSContext
* cx
, SetObject
* obj
, const Value
* value
,
2953 mozilla::HashNumber actualHash
) {
2954 AutoUnsafeCallWithABI unsafe
;
2956 MOZ_ASSERT(actualHash
== HashValue(cx
, obj
->getData(), value
));
2959 void AssertMapObjectHash(JSContext
* cx
, MapObject
* obj
, const Value
* value
,
2960 mozilla::HashNumber actualHash
) {
2961 AutoUnsafeCallWithABI unsafe
;
2963 MOZ_ASSERT(actualHash
== HashValue(cx
, obj
->getData(), value
));
2966 void AssertPropertyLookup(NativeObject
* obj
, PropertyKey id
, uint32_t slot
) {
2967 AutoUnsafeCallWithABI unsafe
;
2969 mozilla::Maybe
<PropertyInfo
> prop
= obj
->lookupPure(id
);
2970 MOZ_ASSERT(prop
.isSome());
2971 MOZ_ASSERT(prop
->slot() == slot
);
2973 MOZ_CRASH("This should only be called in debug builds.");
2977 void AssumeUnreachable(const char* output
) {
2978 MOZ_ReportAssertionFailure(output
, __FILE__
, __LINE__
);
2981 void Printf0(const char* output
) {
2982 AutoUnsafeCallWithABI unsafe
;
2984 // Use stderr instead of stdout because this is only used for debug
2985 // output. stderr is less likely to interfere with the program's normal
2986 // output, and it's always unbuffered.
2987 fprintf(stderr
, "%s", output
);
2990 void Printf1(const char* output
, uintptr_t value
) {
2991 AutoUnsafeCallWithABI unsafe
;
2992 AutoEnterOOMUnsafeRegion oomUnsafe
;
2993 js::UniqueChars line
= JS_sprintf_append(nullptr, output
, value
);
2995 oomUnsafe
.crash("OOM at masm.printf");
2997 fprintf(stderr
, "%s", line
.get());