Bug 1890513: Directly invoke variadic native functions. r=jandem
[gecko.git] / js / src / jit / VMFunctions.cpp
blob9999960532c79267ef15944bfed548709f774fb9
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"
14 #include "gc/Cell.h"
15 #include "gc/GC.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"
55 using namespace js;
56 using namespace js::jit;
58 namespace js {
60 class ArgumentsObject;
61 class NamedLambdaObject;
62 class AsyncFunctionGeneratorObject;
63 class RegExpObject;
65 namespace jit {
67 struct IonOsrTempData;
69 struct PopValues {
70 uint8_t numValues;
71 explicit constexpr PopValues(uint8_t numValues = 0) : numValues(numValues) {}
74 template <class>
75 struct ReturnTypeToDataType { /* Unexpected return type for a VMFunction. */
77 template <>
78 struct ReturnTypeToDataType<void> {
79 static const DataType result = Type_Void;
81 template <>
82 struct ReturnTypeToDataType<bool> {
83 static const DataType result = Type_Bool;
85 template <class T>
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.
94 template <class T>
95 struct TypeToArgProperties {
96 static const uint32_t result =
97 (sizeof(T) <= sizeof(void*) ? VMFunctionData::Word
98 : VMFunctionData::Double);
100 template <>
101 struct TypeToArgProperties<const Value&> {
102 static const uint32_t result =
103 TypeToArgProperties<Value>::result | VMFunctionData::ByRef;
105 template <>
106 struct TypeToArgProperties<HandleValue> {
107 static const uint32_t result =
108 TypeToArgProperties<Value>::result | VMFunctionData::ByRef;
110 template <>
111 struct TypeToArgProperties<MutableHandleValue> {
112 static const uint32_t result =
113 TypeToArgProperties<Value>::result | VMFunctionData::ByRef;
115 template <>
116 struct TypeToArgProperties<HandleId> {
117 static const uint32_t result =
118 TypeToArgProperties<jsid>::result | VMFunctionData::ByRef;
120 template <class T>
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;
128 template <class T>
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.
135 template <class T>
136 struct TypeToPassInFloatReg {
137 static const uint32_t result = 0;
139 template <>
140 struct TypeToPassInFloatReg<double> {
141 static const uint32_t result = 1;
144 // Convert argument types to root types used by the gc, see TraceJitExitFrame.
145 template <class T>
146 struct TypeToRootType {
147 static const uint32_t result = VMFunctionData::RootNone;
149 template <>
150 struct TypeToRootType<HandleValue> {
151 static const uint32_t result = VMFunctionData::RootValue;
153 template <>
154 struct TypeToRootType<MutableHandleValue> {
155 static const uint32_t result = VMFunctionData::RootValue;
157 template <>
158 struct TypeToRootType<HandleId> {
159 static const uint32_t result = VMFunctionData::RootId;
161 template <class T>
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() {
167 using JS::TraceKind;
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();
193 template <class T>
194 struct TypeToRootType<Handle<T>> {
195 // Fail for Handle types that aren't specialized above.
198 template <class>
199 struct OutParamToDataType {
200 static const DataType result = Type_Void;
202 template <class T>
203 struct OutParamToDataType<const T*> {
204 // Const pointers can't be output parameters.
205 static const DataType result = Type_Void;
207 template <>
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;
212 template <>
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;
217 template <>
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;
222 template <>
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;
227 template <>
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;
232 template <>
233 struct OutParamToDataType<Value*> {
234 static const DataType result = Type_Value;
236 template <>
237 struct OutParamToDataType<int*> {
238 static const DataType result = Type_Int32;
240 template <>
241 struct OutParamToDataType<uint32_t*> {
242 static const DataType result = Type_Int32;
244 template <>
245 struct OutParamToDataType<bool*> {
246 static const DataType result = Type_Bool;
248 template <>
249 struct OutParamToDataType<double*> {
250 static const DataType result = Type_Double;
252 template <class T>
253 struct OutParamToDataType<T*> {
254 // Fail for pointer types that aren't specialized above.
256 template <class T>
257 struct OutParamToDataType<T**> {
258 static const DataType result = Type_Pointer;
260 template <class T>
261 struct OutParamToDataType<MutableHandle<T>> {
262 static const DataType result = Type_Handle;
265 template <class>
266 struct OutParamToRootType {
267 static const VMFunctionData::RootType result = VMFunctionData::RootNone;
269 template <>
270 struct OutParamToRootType<MutableHandleValue> {
271 static const VMFunctionData::RootType result = VMFunctionData::RootValue;
273 template <>
274 struct OutParamToRootType<MutableHandleObject> {
275 static const VMFunctionData::RootType result = VMFunctionData::RootObject;
277 template <>
278 struct OutParamToRootType<MutableHandleString> {
279 static const VMFunctionData::RootType result = VMFunctionData::RootString;
281 template <>
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,
292 typename... Args>
293 struct BitMask;
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.
359 #if MOZ_IS_GCC
360 # pragma GCC diagnostic push
361 # pragma GCC diagnostic ignored "-Wignored-attributes"
362 #endif
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
372 #if MOZ_IS_GCC
373 # pragma GCC diagnostic pop
374 #endif
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
379 // constexpr.
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 {
395 switch (outParam) {
396 case Type_Value:
397 return sizeof(Value);
399 case Type_Pointer:
400 case Type_Int32:
401 case Type_Bool:
402 return sizeof(uintptr_t);
404 case Type_Double:
405 return sizeof(double);
407 case Type_Handle:
408 switch (outParamRootType) {
409 case RootNone:
410 MOZ_CRASH("Handle must have root type");
411 case RootObject:
412 case RootString:
413 case RootCell:
414 case RootBigInt:
415 case RootId:
416 return sizeof(uintptr_t);
417 case RootValue:
418 return sizeof(Value);
420 MOZ_CRASH("Invalid type");
422 case Type_Void:
423 return 0;
425 case Type_Cell:
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)) {
439 return false;
442 #ifdef DEBUG
443 const char* lastName = nullptr;
444 #endif
446 for (size_t i = 0; i < NumVMFunctions; i++) {
447 VMFunctionId id = VMFunctionId(i);
448 const VMFunctionData& fun = GetVMFunction(id);
450 #ifdef DEBUG
451 // Assert the list is sorted by name.
452 if (lastName) {
453 MOZ_ASSERT(strcmp(lastName, fun.name()) < 0,
454 "VM function list must be sorted by name");
456 lastName = fun.name();
457 #endif
459 JitSpew(JitSpew_Codegen, "# VM function wrapper (%s)", fun.name());
461 uint32_t offset;
462 if (!generateVMWrapper(cx, masm, id, fun, GetVMFunctionTarget(id),
463 &offset)) {
464 return false;
466 #if defined(JS_ION_PERF)
467 rangeRecorder.recordVMWrapperOffset(fun.name());
468 #else
469 rangeRecorder.recordOffset("Trampoline: VMWrapper");
470 #endif
472 MOZ_ASSERT(functionWrapperOffsets_.length() == size_t(id));
473 functionWrapperOffsets_.infallibleAppend(offset);
476 return true;
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));
489 if (constructing) {
490 if (!IsConstructor(fval)) {
491 ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval,
492 nullptr);
493 return false;
496 ConstructArgs cargs(cx);
497 if (!cargs.init(cx, argc)) {
498 return false;
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
514 // object.
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)) {
521 return false;
524 rval.setObject(*obj);
525 return true;
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,
533 rval);
536 InvokeArgsMaybeIgnoresReturnValue args(cx);
537 if (!args.init(cx, argc, ignoresReturnValue)) {
538 return false;
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,
571 &rval)) {
572 return false;
575 // Overwrite |this| with the return value.
576 argv[0] = rval;
577 return true;
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.
587 // This handles 1).
588 #ifdef JS_SIMULATOR
589 if (cx->simulator()->overRecursedWithExtra(extra)) {
590 ReportOverRecursed(cx);
591 return false;
593 #else
594 AutoCheckRecursionLimit recursion(cx);
595 if (!recursion.checkWithExtra(cx, extra)) {
596 return false;
598 #endif
600 // This handles 2).
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,
615 HandleValue value) {
616 if (!value.isObjectOrNull()) {
617 return true;
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,
626 bool* res) {
627 JSLinearString* linearLhs = lhs->ensureLinear(cx);
628 if (!linearLhs) {
629 return false;
631 JSLinearString* linearRhs = rhs->ensureLinear(cx);
632 if (!linearRhs) {
633 return false;
636 *res = EqualChars(linearLhs, linearRhs);
638 if constexpr (Kind == EqualityKind::NotEqual) {
639 *res = !*res;
641 return true;
644 template bool StringsEqual<EqualityKind::Equal>(JSContext* cx, HandleString lhs,
645 HandleString rhs, bool* res);
646 template bool StringsEqual<EqualityKind::NotEqual>(JSContext* cx,
647 HandleString lhs,
648 HandleString rhs, bool* res);
650 template <ComparisonKind Kind>
651 bool StringsCompare(JSContext* cx, HandleString lhs, HandleString rhs,
652 bool* res) {
653 int32_t result;
654 if (!js::CompareStrings(cx, lhs, rhs, &result)) {
655 return false;
657 if (Kind == ComparisonKind::LessThan) {
658 *res = result < 0;
659 } else {
660 *res = result >= 0;
662 return true;
665 template bool StringsCompare<ComparisonKind::LessThan>(JSContext* cx,
666 HandleString lhs,
667 HandleString rhs,
668 bool* res);
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())) {
678 return nullptr;
680 return argv[0].toString();
683 bool SetArrayLength(JSContext* cx, HandleObject obj, HandleValue value,
684 bool strict) {
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)) {
704 return false;
706 } else {
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,
714 uint32_t* code) {
715 char16_t c;
716 if (!str->getChar(cx, index, &c)) {
717 return false;
719 *code = c;
720 return true;
723 bool CodePointAt(JSContext* cx, HandleString str, int32_t index,
724 uint32_t* code) {
725 char32_t codePoint;
726 if (!str->getCodePoint(cx, size_t(index), &codePoint)) {
727 return false;
729 *code = codePoint;
730 return true;
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) {
764 size_t begin = 0;
765 while (begin < chars.length() && unicode::IsSpace(chars[begin])) {
766 ++begin;
768 return 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])) {
775 --end;
777 return end;
780 int32_t StringTrimStartIndex(const JSString* str) {
781 AutoUnsafeCallWithABI unsafe;
783 MOZ_ASSERT(str->isLinear());
785 const auto* linear = &str->asLinear();
787 size_t begin;
788 if (linear->hasLatin1Chars()) {
789 JS::AutoCheckCannotGC nogc;
790 begin = StringTrimStartIndex(linear->latin1Range(nogc));
791 } else {
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();
806 size_t end;
807 if (linear->hasLatin1Chars()) {
808 JS::AutoCheckCannotGC nogc;
809 end = StringTrimEndIndex(linear->latin1Range(nogc), size_t(start));
810 } else {
811 JS::AutoCheckCannotGC nogc;
812 end = StringTrimEndIndex(linear->twoByteRange(nogc), size_t(start));
814 return int32_t(end);
817 JSString* CharCodeToLowerCase(JSContext* cx, int32_t code) {
818 RootedString str(cx, StringFromCharCode(cx, code));
819 if (!str) {
820 return nullptr;
822 return js::StringToLowerCase(cx, str);
825 JSString* CharCodeToUpperCase(JSContext* cx, int32_t code) {
826 RootedString str(cx, StringFromCharCode(cx, code));
827 if (!str) {
828 return nullptr;
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())) {
840 JSOp op = JSOp(*pc);
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,
844 receiver, result)) {
845 return false;
847 } else {
848 if (!NativeSetProperty<Qualified>(cx, obj.as<NativeObject>(), id, value,
849 receiver, result)) {
850 return false;
853 } else {
854 if (!SetProperty(cx, obj, id, value, receiver, result)) {
855 return false;
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) {
872 RootedId id(cx);
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)) {
893 return false;
896 MOZ_ASSERT_IF(rval.isObject(), fun->realm() == rval.toObject().nonCCWRealm());
897 return true;
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>()) {
906 return true;
909 HandleFunction fun = callee.as<JSFunction>();
910 if (!fun->isInterpreted() || !fun->isConstructor()) {
911 return true;
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>()) {
922 rval.setNull();
923 return true;
925 JSFunction* newTargetFun = &newTarget->as<JSFunction>();
926 if (!newTargetFun->hasNonConfigurablePrototypeDataProperty()) {
927 rval.setNull();
928 return true;
932 AutoRealm ar(cx, fun);
933 if (!js::CreateThis(cx, fun, newTarget, GenericObject, rval)) {
934 return false;
937 MOZ_ASSERT_IF(rval.isObject(), fun->realm() == rval.toObject().nonCCWRealm());
938 return true;
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()) {
959 return;
962 if (nobj->getDenseInitializedLength() > MAX_WHOLE_CELL_BUFFER_SIZE
963 #ifdef JS_GC_ZEAL
964 || rt->hasZealMode(gc::ZealMode::ElementsBarrier)
965 #endif
967 rt->gc.storeBuffer().putSlot(nobj, HeapSlot::Element,
968 nobj->unshiftedIndex(index), 1);
969 return;
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;
989 double d;
990 if (!StringToNumberPure(cx, str, &d)) {
991 return false;
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()) {
1002 return -1;
1005 uint32_t index = UINT32_MAX; // Initialize this to appease Valgrind.
1006 if (!str->asLinear().isIndex(&index) || index > INT32_MAX) {
1007 return -1;
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;
1017 MOZ_ASSERT(obj);
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);
1030 return 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);
1040 return wrapped;
1043 return nullptr;
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)) {
1053 return false;
1056 return true;
1059 bool DebugEpilogue(JSContext* cx, BaselineFrame* frame, const jsbytecode* pc,
1060 bool ok) {
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);
1070 if (!ok) {
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);
1075 return false;
1078 return true;
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,
1098 argsObj);
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);
1115 return true;
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);
1153 return true;
1156 bool GeneratorThrowOrReturn(JSContext* cx, BaselineFrame* frame,
1157 Handle<AbstractGeneratorObject*> genObj,
1158 HandleValue arg, int32_t resumeKindArg) {
1159 GeneratorResumeKind resumeKind = IntToResumeKind(resumeKindArg);
1160 MOZ_ALWAYS_FALSE(
1161 js::GeneratorThrowOrReturn(cx, frame, genObj, arg, resumeKind));
1162 return false;
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);
1182 if (!obj) {
1183 return false;
1185 res.setObject(*obj);
1186 return true;
1189 ArrayObject* NewArrayObjectEnsureDenseInitLength(JSContext* cx, int32_t count) {
1190 MOZ_ASSERT(count >= 0);
1192 auto* array = NewDenseFullyAllocatedArray(cx, count);
1193 if (!array) {
1194 return nullptr;
1196 array->ensureDenseInitializedLength(0, count);
1198 return array;
1201 ArrayObject* InitRestParameter(JSContext* cx, uint32_t length, Value* rest,
1202 Handle<ArrayObject*> arrRes) {
1203 if (arrRes) {
1204 // Fast path: we managed to allocate the array inline; initialize the
1205 // elements.
1206 MOZ_ASSERT(arrRes->getDenseInitializedLength() == 0);
1208 // We don't call this function if we can initialize the elements in JIT
1209 // code.
1210 MOZ_ASSERT(length > arrRes->getDenseCapacity());
1212 if (!arrRes->growElements(cx, length)) {
1213 return nullptr;
1215 arrRes->initDenseElements(rest, length);
1216 arrRes->setLength(length);
1217 return arrRes;
1220 return NewDenseCopiedArray(cx, length, rest);
1223 bool HandleDebugTrap(JSContext* cx, BaselineFrame* frame,
1224 const uint8_t* retAddr) {
1225 RootedScript script(cx, frame->script());
1226 jsbytecode* pc;
1227 if (frame->runningInInterpreter()) {
1228 pc = frame->interpreterPC();
1229 } else {
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));
1239 } else {
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
1247 // it now.
1248 MOZ_ASSERT(!frame->isDebuggee());
1250 if (!DebugAfterYield(cx, frame)) {
1251 return false;
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()) {
1257 return true;
1261 MOZ_ASSERT(frame->isDebuggee());
1263 if (DebugAPI::stepModeEnabled(script) && !DebugAPI::onSingleStep(cx)) {
1264 return false;
1267 if (DebugAPI::hasBreakpointsAt(script, pc) && !DebugAPI::onTrap(cx)) {
1268 return false;
1271 return true;
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>();
1293 return true;
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);
1321 return true;
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>();
1343 return true;
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) {
1354 MOZ_ASSERT(string);
1355 MOZ_ASSERT(pattern);
1356 MOZ_ASSERT(repl);
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;
1371 #ifdef DEBUG
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));
1383 #endif
1386 void AssertValidStringPtr(JSContext* cx, JSString* str) {
1387 AutoUnsafeCallWithABI unsafe;
1388 #ifdef DEBUG
1389 // We can't closely inspect strings from another runtime.
1390 if (str->runtimeFromAnyThread() != cx->runtime()) {
1391 MOZ_ASSERT(str->isPermanentAtom());
1392 return;
1395 if (str->isAtom()) {
1396 MOZ_ASSERT(str->zone()->isAtomsZone());
1397 } else {
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);
1408 } else {
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);
1418 } else {
1419 MOZ_ASSERT(kind == gc::AllocKind::STRING);
1421 #endif
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());
1430 return;
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())) {
1471 return nullptr;
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;
1497 MOZ_ASSERT(*objp);
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());
1519 return false;
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,
1529 nullptr);
1530 return false;
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())) {
1550 return false;
1553 result.set(vp[0]);
1554 return true;
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);
1564 #ifdef DEBUG
1565 DOMInstanceClassHasProtoAtDepth instanceChecker =
1566 cx->runtime()->DOMcallbacks->instanceClassMatchesProto;
1567 MOZ_ASSERT(instanceChecker(obj->getClass(), info->protoID, info->depth));
1568 #endif
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,
1577 HandleValue rhs) {
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());
1586 vp[2].set(rhs);
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);
1598 #ifdef DEBUG
1599 DOMInstanceClassHasProtoAtDepth instanceChecker =
1600 cx->runtime()->DOMcallbacks->instanceClassMatchesProto;
1601 MOZ_ASSERT(instanceChecker(obj->getClass(), info->protoID, info->depth));
1602 #endif
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);
1623 if (!str2Linear) {
1624 return false;
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
1637 // fast.
1638 return CanStartTypedArrayIndex(str->latin1OrTwoByteChar(0));
1641 return false;
1644 static void VerifyCacheEntry(JSContext* cx, NativeObject* obj, PropertyKey key,
1645 const MegamorphicCacheEntry& entry) {
1646 #ifdef DEBUG
1647 if (entry.isMissingProperty()) {
1648 NativeObject* pobj;
1649 PropertyResult prop;
1650 MOZ_ASSERT(LookupPropertyPure(cx, obj, key, &pobj, &prop));
1651 MOZ_ASSERT(prop.isNotFound());
1652 return;
1654 if (entry.isMissingOwnProperty()) {
1655 MOZ_ASSERT(!obj->containsPure(key));
1656 return;
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());
1667 #endif
1670 static MOZ_ALWAYS_INLINE bool GetNativeDataPropertyPureImpl(
1671 JSContext* cx, JSObject* obj, jsid id, MegamorphicCacheEntry* entry,
1672 Value* vp) {
1673 MOZ_ASSERT(obj->is<NativeObject>());
1674 NativeObject* nobj = &obj->as<NativeObject>();
1675 Shape* receiverShape = obj->shape();
1676 MegamorphicCache& cache = cx->caches().megamorphicCache;
1678 MOZ_ASSERT(entry);
1680 size_t numHops = 0;
1681 while (true) {
1682 MOZ_ASSERT(!nobj->getOpsLookupProperty());
1684 uint32_t index;
1685 if (PropMap* map = nobj->shape()->lookup(cx, id, &index)) {
1686 PropertyInfo prop = map->getPropertyInfo(index);
1687 if (!prop.isDataProperty()) {
1688 return false;
1690 TaggedSlotOffset offset = nobj->getTaggedSlotOffset(prop.slot());
1691 cache.initEntryForDataProperty(entry, receiverShape, id, numHops, offset);
1692 *vp = nobj->getSlot(prop.slot());
1693 return true;
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)) {
1699 return false;
1702 // Don't skip past TypedArrayObjects if the id can be a TypedArray index.
1703 if (nobj->is<TypedArrayObject>()) {
1704 if (MaybeTypedArrayIndexString(id)) {
1705 return false;
1710 JSObject* proto = nobj->staticPrototype();
1711 if (!proto) {
1712 cache.initEntryForMissingProperty(entry, receiverShape, id);
1713 vp->setUndefined();
1714 return true;
1717 if (!proto->is<NativeObject>()) {
1718 return false;
1720 nobj = &proto->as<NativeObject>();
1721 numHops++;
1725 bool GetNativeDataPropertyPureWithCacheLookup(JSContext* cx, JSObject* obj,
1726 PropertyKey id,
1727 MegamorphicCacheEntry* entry,
1728 Value* vp) {
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);
1747 } else {
1748 size_t index = NativeObject::getDynamicSlotIndexFromOffset(offset);
1749 *vp = nobj->getDynamicSlot(index);
1751 return true;
1753 if (entry->isMissingProperty()) {
1754 vp->setUndefined();
1755 return true;
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)) {
1769 return false;
1772 auto validation =
1773 ScriptedProxyHandler::checkGetTrapResult(cx, obj, rootedId, value);
1774 if (validation != ScriptedProxyHandler::GetTrapValidationResult::OK) {
1775 ScriptedProxyHandler::reportGetTrapValidationError(cx, rootedId,
1776 validation);
1777 return false;
1779 result.set(value);
1780 return true;
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,
1790 const Value& idVal,
1791 jsid* id) {
1792 if (MOZ_LIKELY(idVal.isString())) {
1793 JSString* s = idVal.toString();
1794 JSAtom* atom;
1795 if (s->isAtom()) {
1796 atom = &s->asAtom();
1797 } else {
1798 atom = AtomizeString(cx, s);
1799 if (!atom) {
1800 cx->recoverFromOutOfMemory();
1801 return false;
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");
1809 uint32_t index;
1810 if (MOZ_UNLIKELY(atom->isIndex(&index) && index <= PropertyKey::IntMax)) {
1811 return false;
1814 *id = PropertyKey::NonIntAtom(atom);
1815 return true;
1818 if (idVal.isSymbol()) {
1819 *id = PropertyKey::Symbol(idVal.toSymbol());
1820 return true;
1823 if (idVal.isNull()) {
1824 *id = PropertyKey::NonIntAtom(cx->names().null);
1825 return true;
1828 if (idVal.isUndefined()) {
1829 *id = PropertyKey::NonIntAtom(cx->names().undefined);
1830 return true;
1833 return false;
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];
1842 jsid id;
1843 if (!ValueToAtomOrSymbolPure(cx, idVal, &id)) {
1844 return false;
1847 Shape* receiverShape = obj->shape();
1848 MegamorphicCache& cache = cx->caches().megamorphicCache;
1849 if (!entry) {
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))) {
1864 return false;
1867 NativeObject* nobj = &objArg->as<NativeObject>();
1869 while (true) {
1870 uint32_t index;
1871 if (PropMap* map = nobj->shape()->lookup(cx, id, &index)) {
1872 PropertyInfo prop = map->getPropertyInfo(index);
1873 if (!prop.isAccessorProperty()) {
1874 return false;
1876 GetterSetter* actualGetterSetter = nobj->getGetterSetter(prop);
1877 if (actualGetterSetter == getterSetter) {
1878 return true;
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)) {
1887 return false;
1891 JSObject* proto = nobj->staticPrototype();
1892 if (!proto) {
1893 return false;
1896 if (!proto->is<NativeObject>()) {
1897 return false;
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];
1910 jsid id;
1911 if (!ValueToAtomOrSymbolPure(cx, idVal, &id)) {
1912 return false;
1915 MegamorphicCache& cache = cx->caches().megamorphicCache;
1916 Shape* receiverShape = obj->shape();
1917 if (!entry) {
1918 if (cache.lookup(receiverShape, id, &entry)) {
1919 VerifyCacheEntry(cx, &obj->as<NativeObject>(), id, *entry);
1923 size_t numHops = 0;
1924 do {
1925 if (MOZ_UNLIKELY(!obj->is<NativeObject>())) {
1926 return false;
1929 MOZ_ASSERT(!obj->getOpsLookupProperty());
1931 NativeObject* nobj = &obj->as<NativeObject>();
1932 uint32_t index;
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,
1938 offset);
1940 vp[1].setBoolean(true);
1941 return 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)) {
1949 return false;
1952 // Don't skip past TypedArrayObjects if the id can be a TypedArray
1953 // index.
1954 if (obj->is<TypedArrayObject>()) {
1955 if (MaybeTypedArrayIndexString(id)) {
1956 return false;
1961 // If implementing Object.hasOwnProperty, don't follow protochain.
1962 if constexpr (HasOwn) {
1963 break;
1966 // Get prototype. Objects that may allow dynamic prototypes are already
1967 // filtered out above.
1968 obj = obj->staticPrototype();
1969 numHops++;
1970 } while (obj);
1972 // Missing property.
1973 if (entry) {
1974 if constexpr (HasOwn) {
1975 cache.initEntryForMissingOwnProperty(entry, receiverShape, id);
1976 } else {
1977 cache.initEntryForMissingProperty(entry, receiverShape, id);
1980 vp[1].setBoolean(false);
1981 return true;
1984 template bool HasNativeDataPropertyPure<true>(JSContext* cx, JSObject* obj,
1985 MegamorphicCacheEntry* entry,
1986 Value* vp);
1988 template bool HasNativeDataPropertyPure<false>(JSContext* cx, JSObject* obj,
1989 MegamorphicCacheEntry* entry,
1990 Value* vp);
1992 bool HasNativeElementPure(JSContext* cx, NativeObject* obj, int32_t index,
1993 Value* vp) {
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)) {
2002 return false;
2005 if (obj->containsDenseElement(index)) {
2006 vp[0].setBoolean(true);
2007 return true;
2010 jsid id = PropertyKey::Int(index);
2011 uint32_t unused;
2012 if (obj->shape()->lookup(cx, id, &unused)) {
2013 vp[0].setBoolean(true);
2014 return 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))) {
2020 return false;
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);
2026 return true;
2029 vp[0].setBoolean(false);
2030 return true;
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,
2039 bool* optimized) {
2040 MOZ_ASSERT(!*optimized);
2042 Shape* receiverShape = obj->shape();
2043 MegamorphicSetPropCache& cache = *cx->caches().megamorphicSetPropCache;
2045 #ifdef DEBUG
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());
2058 } else { // SetProp
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());
2067 #endif
2069 // Fast path for changing a data property.
2070 uint32_t index;
2071 if (PropMap* map = obj->shape()->lookup(cx, key, &index)) {
2072 PropertyInfo prop = map->getPropertyInfo(index);
2073 if (!prop.isDataProperty() || !prop.writable()) {
2074 return true;
2076 obj->setSlot(prop.slot(), value);
2077 if (!Watchtower::watchPropertyModification<AllowGC::NoGC>(cx, obj, key)) {
2078 return false;
2080 *optimized = true;
2082 if constexpr (UseCache) {
2083 TaggedSlotOffset offset = obj->getTaggedSlotOffset(prop.slot());
2084 cache.set(receiverShape, nullptr, key, offset, 0);
2086 return true;
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_))) {
2092 return true;
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
2097 // properties).
2098 JSObject* proto = obj->staticPrototype();
2099 while (proto) {
2100 if (!proto->is<PlainObject>()) {
2101 return true;
2103 PlainObject* plainProto = &proto->as<PlainObject>();
2104 if (plainProto->hasNonWritableOrAccessorPropExclProto()) {
2105 uint32_t index;
2106 if (PropMap* map = plainProto->shape()->lookup(cx, key, &index)) {
2107 PropertyInfo prop = map->getPropertyInfo(index);
2108 if (!prop.isDataProperty() || !prop.writable()) {
2109 return true;
2111 break;
2114 proto = plainProto->staticPrototype();
2117 #ifdef DEBUG
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());
2129 #endif
2131 *optimized = true;
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);
2152 return res;
2155 template <bool Cached>
2156 bool SetElementMegamorphic(JSContext* cx, HandleObject obj, HandleValue index,
2157 HandleValue value, bool strict) {
2158 if (obj->is<PlainObject>()) {
2159 PropertyKey key;
2160 if (ValueToAtomOrSymbolPure(cx, index, &key)) {
2161 bool optimized = false;
2162 if (!TryAddOrSetPlainObjectProperty<Cached>(cx, obj.as<PlainObject>(),
2163 key, value, &optimized)) {
2164 return false;
2166 if (optimized) {
2167 return true;
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,
2177 bool strict);
2178 template bool SetElementMegamorphic<true>(JSContext* cx, HandleObject obj,
2179 HandleValue index, HandleValue value,
2180 bool strict);
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)) {
2189 return false;
2191 if (optimized) {
2192 return true;
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,
2203 bool strict);
2204 template bool SetPropertyMegamorphic<true>(JSContext* cx, HandleObject obj,
2205 HandleId id, HandleValue value,
2206 bool strict);
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()) {
2218 return;
2220 JSContext* cx = script->runtimeFromMainThread()->mainContextFromOwnThread();
2221 AutoEnterOOMUnsafeRegion oomUnsafe;
2222 if (!script->initScriptCounts(cx)) {
2223 oomUnsafe.crash("initScriptCounts");
2227 PCCounts* counts = script->maybeGetPCCounts(pc);
2228 MOZ_ASSERT(counts);
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)) {
2256 return false;
2258 rval.setObjectOrNull(proto);
2259 return true;
2262 static JSString* ConvertObjectToStringForConcat(JSContext* cx,
2263 HandleValue obj) {
2264 MOZ_ASSERT(obj.isObject());
2265 RootedValue rootedObj(cx, obj);
2266 if (!ToPrimitive(cx, &rootedObj)) {
2267 return nullptr;
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);
2281 if (!rstr) {
2282 return false;
2285 // lhs is already string.
2286 lstr = lhs.toString();
2287 } else {
2288 MOZ_ASSERT(rhs.isString() && lhs.isObject());
2289 // Convert lhs first.
2290 lstr = ConvertObjectToStringForConcat(cx, lhs);
2291 if (!lstr) {
2292 return false;
2295 // rhs is already string.
2296 rstr = rhs.toString();
2299 JSString* str = ConcatStrings<NoGC>(cx, lstr, rstr);
2300 if (!str) {
2301 RootedString nlstr(cx, lstr), nrstr(cx, rstr);
2302 str = ConcatStrings<CanGC>(cx, nlstr, nrstr);
2303 if (!str) {
2304 return false;
2308 res.setString(str);
2309 return true;
2312 bool IsPossiblyWrappedTypedArray(JSContext* cx, JSObject* obj, bool* result) {
2313 JSObject* unwrapped = CheckedUnwrapDynamic(obj, cx);
2314 if (!unwrapped) {
2315 ReportAccessDenied(cx);
2316 return false;
2319 *result = unwrapped->is<TypedArrayObject>();
2320 return true;
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,
2345 int32_t count) {
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
2354 // elements.
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)));
2358 return;
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);
2370 if (buf) {
2371 InitReservedSlot(obj, TypedArrayObject::DATA_SLOT, buf, nbytes,
2372 MemoryUse::TypedArrayElements);
2376 #ifdef JS_GC_PROBES
2377 void TraceCreateObject(JSObject* obj) {
2378 AutoUnsafeCallWithABI unsafe;
2379 js::gc::gcprobes::CreateObject(obj);
2381 #endif
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);
2393 #else
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);
2401 #endif
2403 bool DoStringToInt64(JSContext* cx, HandleString str, uint64_t* res) {
2404 BigInt* bi;
2405 JS_TRY_VAR_OR_RETURN_FALSE(cx, bi, js::StringToBigInt(cx, str));
2407 if (!bi) {
2408 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2409 JSMSG_BIGINT_INVALID_SYNTAX);
2410 return false;
2413 *res = js::BigInt::toUint64(bi);
2414 return true;
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) {
2422 res = !res;
2424 return res;
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) {
2435 res = !res;
2437 return res;
2440 template bool BigIntCompare<ComparisonKind::LessThan>(BigInt* x, BigInt* y);
2441 template bool BigIntCompare<ComparisonKind::GreaterThanOrEqual>(BigInt* x,
2442 BigInt* y);
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) {
2449 res = !res;
2451 return res;
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,
2468 double y);
2469 template bool BigIntNumberCompare<ComparisonKind::GreaterThanOrEqual>(BigInt* x,
2470 double y);
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,
2483 BigInt* y);
2484 template bool NumberBigIntCompare<ComparisonKind::GreaterThanOrEqual>(
2485 double x, BigInt* y);
2487 template <EqualityKind Kind>
2488 bool BigIntStringEqual(JSContext* cx, HandleBigInt x, HandleString y,
2489 bool* res) {
2490 JS_TRY_VAR_OR_RETURN_FALSE(cx, *res, BigInt::equal(cx, x, y));
2491 if (Kind != EqualityKind::Equal) {
2492 *res = !*res;
2494 return true;
2497 template bool BigIntStringEqual<EqualityKind::Equal>(JSContext* cx,
2498 HandleBigInt x,
2499 HandleString y, bool* res);
2500 template bool BigIntStringEqual<EqualityKind::NotEqual>(JSContext* cx,
2501 HandleBigInt x,
2502 HandleString y,
2503 bool* res);
2505 template <ComparisonKind Kind>
2506 bool BigIntStringCompare(JSContext* cx, HandleBigInt x, HandleString y,
2507 bool* res) {
2508 mozilla::Maybe<bool> result;
2509 if (!BigInt::lessThan(cx, x, y, result)) {
2510 return false;
2512 if (Kind == ComparisonKind::LessThan) {
2513 *res = result.valueOr(false);
2514 } else {
2515 *res = !result.valueOr(true);
2517 return true;
2520 template bool BigIntStringCompare<ComparisonKind::LessThan>(JSContext* cx,
2521 HandleBigInt x,
2522 HandleString y,
2523 bool* res);
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,
2529 bool* res) {
2530 mozilla::Maybe<bool> result;
2531 if (!BigInt::lessThan(cx, x, y, result)) {
2532 return false;
2534 if (Kind == ComparisonKind::LessThan) {
2535 *res = result.valueOr(false);
2536 } else {
2537 *res = !result.valueOr(true);
2539 return true;
2542 template bool StringBigIntCompare<ComparisonKind::LessThan>(JSContext* cx,
2543 HandleString x,
2544 HandleBigInt y,
2545 bool* res);
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),
2571 T(replacement));
2574 AtomicsCompareExchangeFn AtomicsCompareExchange(Scalar::Type elementType) {
2575 switch (elementType) {
2576 case Scalar::Int8:
2577 return AtomicsCompareExchange<int8_t>;
2578 case Scalar::Uint8:
2579 return AtomicsCompareExchange<uint8_t>;
2580 case Scalar::Int16:
2581 return AtomicsCompareExchange<int16_t>;
2582 case Scalar::Uint16:
2583 return AtomicsCompareExchange<uint16_t>;
2584 case Scalar::Int32:
2585 return AtomicsCompareExchange<int32_t>;
2586 case Scalar::Uint32:
2587 return AtomicsCompareExchange<uint32_t>;
2588 default:
2589 MOZ_CRASH("Unexpected TypedArray type");
2593 template <typename T>
2594 static int32_t AtomicsExchange(TypedArrayObject* typedArray, size_t index,
2595 int32_t value) {
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) {
2608 case Scalar::Int8:
2609 return AtomicsExchange<int8_t>;
2610 case Scalar::Uint8:
2611 return AtomicsExchange<uint8_t>;
2612 case Scalar::Int16:
2613 return AtomicsExchange<int16_t>;
2614 case Scalar::Uint16:
2615 return AtomicsExchange<uint16_t>;
2616 case Scalar::Int32:
2617 return AtomicsExchange<int32_t>;
2618 case Scalar::Uint32:
2619 return AtomicsExchange<uint32_t>;
2620 default:
2621 MOZ_CRASH("Unexpected TypedArray type");
2625 template <typename T>
2626 static int32_t AtomicsAdd(TypedArrayObject* typedArray, size_t index,
2627 int32_t value) {
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) {
2640 case Scalar::Int8:
2641 return AtomicsAdd<int8_t>;
2642 case Scalar::Uint8:
2643 return AtomicsAdd<uint8_t>;
2644 case Scalar::Int16:
2645 return AtomicsAdd<int16_t>;
2646 case Scalar::Uint16:
2647 return AtomicsAdd<uint16_t>;
2648 case Scalar::Int32:
2649 return AtomicsAdd<int32_t>;
2650 case Scalar::Uint32:
2651 return AtomicsAdd<uint32_t>;
2652 default:
2653 MOZ_CRASH("Unexpected TypedArray type");
2657 template <typename T>
2658 static int32_t AtomicsSub(TypedArrayObject* typedArray, size_t index,
2659 int32_t value) {
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) {
2672 case Scalar::Int8:
2673 return AtomicsSub<int8_t>;
2674 case Scalar::Uint8:
2675 return AtomicsSub<uint8_t>;
2676 case Scalar::Int16:
2677 return AtomicsSub<int16_t>;
2678 case Scalar::Uint16:
2679 return AtomicsSub<uint16_t>;
2680 case Scalar::Int32:
2681 return AtomicsSub<int32_t>;
2682 case Scalar::Uint32:
2683 return AtomicsSub<uint32_t>;
2684 default:
2685 MOZ_CRASH("Unexpected TypedArray type");
2689 template <typename T>
2690 static int32_t AtomicsAnd(TypedArrayObject* typedArray, size_t index,
2691 int32_t value) {
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) {
2704 case Scalar::Int8:
2705 return AtomicsAnd<int8_t>;
2706 case Scalar::Uint8:
2707 return AtomicsAnd<uint8_t>;
2708 case Scalar::Int16:
2709 return AtomicsAnd<int16_t>;
2710 case Scalar::Uint16:
2711 return AtomicsAnd<uint16_t>;
2712 case Scalar::Int32:
2713 return AtomicsAnd<int32_t>;
2714 case Scalar::Uint32:
2715 return AtomicsAnd<uint32_t>;
2716 default:
2717 MOZ_CRASH("Unexpected TypedArray type");
2721 template <typename T>
2722 static int32_t AtomicsOr(TypedArrayObject* typedArray, size_t index,
2723 int32_t value) {
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) {
2736 case Scalar::Int8:
2737 return AtomicsOr<int8_t>;
2738 case Scalar::Uint8:
2739 return AtomicsOr<uint8_t>;
2740 case Scalar::Int16:
2741 return AtomicsOr<int16_t>;
2742 case Scalar::Uint16:
2743 return AtomicsOr<uint16_t>;
2744 case Scalar::Int32:
2745 return AtomicsOr<int32_t>;
2746 case Scalar::Uint32:
2747 return AtomicsOr<uint32_t>;
2748 default:
2749 MOZ_CRASH("Unexpected TypedArray type");
2753 template <typename T>
2754 static int32_t AtomicsXor(TypedArrayObject* typedArray, size_t index,
2755 int32_t value) {
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) {
2768 case Scalar::Int8:
2769 return AtomicsXor<int8_t>;
2770 case Scalar::Uint8:
2771 return AtomicsXor<uint8_t>;
2772 case Scalar::Int16:
2773 return AtomicsXor<int16_t>;
2774 case Scalar::Uint16:
2775 return AtomicsXor<uint16_t>;
2776 case Scalar::Int32:
2777 return AtomicsXor<int32_t>;
2778 case Scalar::Uint32:
2779 return AtomicsXor<uint32_t>;
2780 default:
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,
2822 size_t index) {
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;
2832 AtomicAccess64(
2833 typedArray, index,
2834 [](auto addr, auto val) {
2835 jit::AtomicOperations::storeSeqCst(addr, val);
2837 value);
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,
2847 newval);
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);
2859 value);
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);
2869 value);
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);
2879 value);
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);
2889 value);
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);
2899 value);
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);
2909 value);
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);
2917 if (!atom) {
2918 cx->recoverFromOutOfMemory();
2919 return nullptr;
2922 return atom;
2925 bool SetObjectHas(JSContext* cx, HandleObject obj, HandleValue key,
2926 bool* rval) {
2927 return SetObject::has(cx, obj, key, rval);
2930 bool MapObjectHas(JSContext* cx, HandleObject obj, HandleValue key,
2931 bool* rval) {
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);
2940 #ifdef DEBUG
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);
2950 #endif
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;
2968 #ifdef DEBUG
2969 mozilla::Maybe<PropertyInfo> prop = obj->lookupPure(id);
2970 MOZ_ASSERT(prop.isSome());
2971 MOZ_ASSERT(prop->slot() == slot);
2972 #else
2973 MOZ_CRASH("This should only be called in debug builds.");
2974 #endif
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);
2994 if (!line) {
2995 oomUnsafe.crash("OOM at masm.printf");
2997 fprintf(stderr, "%s", line.get());
3000 } // namespace jit
3001 } // namespace js