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/. */
6
7 #include "jit/VMFunctions.h"
8
9 #include "mozilla/FloatingPoint.h"
10
11 #include "builtin/MapObject.h"
12 #include "builtin/String.h"
13 #include "ds/OrderedHashTable.h"
14 #include "gc/Cell.h"
15 #include "jit/arm/Simulator-arm.h"
16 #include "jit/AtomicOperations.h"
17 #include "jit/BaselineIC.h"
18 #include "jit/CalleeToken.h"
19 #include "jit/JitFrames.h"
20 #include "jit/JitRuntime.h"
21 #include "jit/mips32/Simulator-mips32.h"
22 #include "jit/mips64/Simulator-mips64.h"
23 #include "jit/Simulator.h"
24 #include "js/experimental/JitInfo.h"
25 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
26 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
27 #include "js/friend/WindowProxy.h" // js::IsWindow
28 #include "js/Printf.h"
29 #include "js/TraceKind.h"
30 #include "vm/ArrayObject.h"
31 #include "vm/Interpreter.h"
32 #include "vm/JSAtom.h"
33 #include "vm/PlainObject.h" // js::PlainObject
34 #include "vm/SelfHosting.h"
35 #include "vm/StaticStrings.h"
36 #include "vm/TraceLogging.h"
37 #include "vm/TypedArrayObject.h"
38 #include "wasm/TypedObject.h"
39
40 #include "debugger/DebugAPI-inl.h"
41 #include "jit/BaselineFrame-inl.h"
42 #include "jit/VMFunctionList-inl.h"
43 #include "vm/Interpreter-inl.h"
44 #include "vm/JSScript-inl.h"
45 #include "vm/NativeObject-inl.h"
46 #include "vm/PlainObject-inl.h" // js::CreateThis
47 #include "vm/StringObject-inl.h"
48
49 using namespace js;
50 using namespace js::jit;
51
52 namespace js {
53
54 class ArgumentsObject;
55 class NamedLambdaObject;
56 class AsyncFunctionGeneratorObject;
57 class RegExpObject;
58
59 namespace jit {
60
61 struct IonOsrTempData;
62
63 struct PopValues {
64 uint8_t numValues;
65
PopValuesjs::jit::PopValues66 explicit constexpr PopValues(uint8_t numValues) : numValues(numValues) {}
67 };
68
69 template <class>
70 struct ReturnTypeToDataType { /* Unexpected return type for a VMFunction. */
71 };
72 template <>
73 struct ReturnTypeToDataType<void> {
74 static const DataType result = Type_Void;
75 };
76 template <>
77 struct ReturnTypeToDataType<bool> {
78 static const DataType result = Type_Bool;
79 };
80 template <class T>
81 struct ReturnTypeToDataType<T*> {
82 // Assume by default that any pointer return types are cells.
83 static_assert(std::is_base_of_v<gc::Cell, T>);
84
85 static const DataType result = Type_Cell;
86 };
87
88 // Convert argument types to properties of the argument known by the jit.
89 template <class T>
90 struct TypeToArgProperties {
91 static const uint32_t result =
92 (sizeof(T) <= sizeof(void*) ? VMFunctionData::Word
93 : VMFunctionData::Double);
94 };
95 template <>
96 struct TypeToArgProperties<const Value&> {
97 static const uint32_t result =
98 TypeToArgProperties<Value>::result | VMFunctionData::ByRef;
99 };
100 template <>
101 struct TypeToArgProperties<HandleValue> {
102 static const uint32_t result =
103 TypeToArgProperties<Value>::result | VMFunctionData::ByRef;
104 };
105 template <>
106 struct TypeToArgProperties<MutableHandleValue> {
107 static const uint32_t result =
108 TypeToArgProperties<Value>::result | VMFunctionData::ByRef;
109 };
110 template <>
111 struct TypeToArgProperties<HandleId> {
112 static const uint32_t result =
113 TypeToArgProperties<jsid>::result | VMFunctionData::ByRef;
114 };
115 template <class T>
116 struct TypeToArgProperties<Handle<T*>> {
117 // Assume by default that any pointer handle types are cells.
118 static_assert(std::is_base_of_v<gc::Cell, T>);
119
120 static const uint32_t result =
121 TypeToArgProperties<T*>::result | VMFunctionData::ByRef;
122 };
123 template <class T>
124 struct TypeToArgProperties<Handle<T>> {
125 // Fail for Handle types that aren't specialized above.
126 };
127
128 // Convert argument type to whether or not it should be passed in a float
129 // register on platforms that have them, like x64.
130 template <class T>
131 struct TypeToPassInFloatReg {
132 static const uint32_t result = 0;
133 };
134 template <>
135 struct TypeToPassInFloatReg<double> {
136 static const uint32_t result = 1;
137 };
138
139 // Convert argument types to root types used by the gc, see TraceJitExitFrame.
140 template <class T>
141 struct TypeToRootType {
142 static const uint32_t result = VMFunctionData::RootNone;
143 };
144 template <>
145 struct TypeToRootType<HandleValue> {
146 static const uint32_t result = VMFunctionData::RootValue;
147 };
148 template <>
149 struct TypeToRootType<MutableHandleValue> {
150 static const uint32_t result = VMFunctionData::RootValue;
151 };
152 template <>
153 struct TypeToRootType<HandleId> {
154 static const uint32_t result = VMFunctionData::RootId;
155 };
156 template <class T>
157 struct TypeToRootType<Handle<T*>> {
158 // Assume by default that any pointer types are cells.
159 static_assert(std::is_base_of_v<gc::Cell, T>);
160
rootTypejs::jit::TypeToRootType161 static constexpr uint32_t rootType() {
162 using JS::TraceKind;
163
164 switch (JS::MapTypeToTraceKind<T>::kind) {
165 case TraceKind::Object:
166 return VMFunctionData::RootObject;
167 case TraceKind::BigInt:
168 return VMFunctionData::RootBigInt;
169 case TraceKind::String:
170 return VMFunctionData::RootString;
171 case TraceKind::Shape:
172 case TraceKind::Script:
173 case TraceKind::Scope:
174 return VMFunctionData::RootCell;
175 case TraceKind::Symbol:
176 case TraceKind::BaseShape:
177 case TraceKind::Null:
178 case TraceKind::JitCode:
179 case TraceKind::RegExpShared:
180 case TraceKind::GetterSetter:
181 case TraceKind::PropMap:
182 MOZ_CRASH("Unexpected trace kind");
183 }
184 }
185
186 static constexpr uint32_t result = rootType();
187 };
188 template <class T>
189 struct TypeToRootType<Handle<T>> {
190 // Fail for Handle types that aren't specialized above.
191 };
192
193 template <class>
194 struct OutParamToDataType {
195 static const DataType result = Type_Void;
196 };
197 template <class T>
198 struct OutParamToDataType<const T*> {
199 // Const pointers can't be output parameters.
200 static const DataType result = Type_Void;
201 };
202 template <>
203 struct OutParamToDataType<uint64_t*> {
204 // Already used as an input type, so it can't be used as an output param.
205 static const DataType result = Type_Void;
206 };
207 template <>
208 struct OutParamToDataType<JSObject*> {
209 // Already used as an input type, so it can't be used as an output param.
210 static const DataType result = Type_Void;
211 };
212 template <>
213 struct OutParamToDataType<JSString*> {
214 // Already used as an input type, so it can't be used as an output param.
215 static const DataType result = Type_Void;
216 };
217 template <>
218 struct OutParamToDataType<BaselineFrame*> {
219 // Already used as an input type, so it can't be used as an output param.
220 static const DataType result = Type_Void;
221 };
222 template <>
223 struct OutParamToDataType<gc::AllocSite*> {
224 // Already used as an input type, so it can't be used as an output param.
225 static const DataType result = Type_Void;
226 };
227 template <>
228 struct OutParamToDataType<Value*> {
229 static const DataType result = Type_Value;
230 };
231 template <>
232 struct OutParamToDataType<int*> {
233 static const DataType result = Type_Int32;
234 };
235 template <>
236 struct OutParamToDataType<uint32_t*> {
237 static const DataType result = Type_Int32;
238 };
239 template <>
240 struct OutParamToDataType<bool*> {
241 static const DataType result = Type_Bool;
242 };
243 template <>
244 struct OutParamToDataType<double*> {
245 static const DataType result = Type_Double;
246 };
247 template <class T>
248 struct OutParamToDataType<T*> {
249 // Fail for pointer types that aren't specialized above.
250 };
251 template <class T>
252 struct OutParamToDataType<T**> {
253 static const DataType result = Type_Pointer;
254 };
255 template <class T>
256 struct OutParamToDataType<MutableHandle<T>> {
257 static const DataType result = Type_Handle;
258 };
259
260 template <class>
261 struct OutParamToRootType {
262 static const VMFunctionData::RootType result = VMFunctionData::RootNone;
263 };
264 template <>
265 struct OutParamToRootType<MutableHandleValue> {
266 static const VMFunctionData::RootType result = VMFunctionData::RootValue;
267 };
268 template <>
269 struct OutParamToRootType<MutableHandleObject> {
270 static const VMFunctionData::RootType result = VMFunctionData::RootObject;
271 };
272 template <>
273 struct OutParamToRootType<MutableHandleString> {
274 static const VMFunctionData::RootType result = VMFunctionData::RootString;
275 };
276 template <>
277 struct OutParamToRootType<MutableHandleBigInt> {
278 static const VMFunctionData::RootType result = VMFunctionData::RootBigInt;
279 };
280
281 // Construct a bit mask from a list of types. The mask is constructed as an OR
282 // of the mask produced for each argument. The result of each argument is
283 // shifted by its index, such that the result of the first argument is on the
284 // low bits of the mask, and the result of the last argument in part of the
285 // high bits of the mask.
286 template <template <typename> class Each, typename ResultType, size_t Shift,
287 typename... Args>
288 struct BitMask;
289
290 template <template <typename> class Each, typename ResultType, size_t Shift>
291 struct BitMask<Each, ResultType, Shift> {
292 static constexpr ResultType result = ResultType();
293 };
294
295 template <template <typename> class Each, typename ResultType, size_t Shift,
296 typename HeadType, typename... TailTypes>
297 struct BitMask<Each, ResultType, Shift, HeadType, TailTypes...> {
298 static_assert(ResultType(Each<HeadType>::result) < (1 << Shift),
299 "not enough bits reserved by the shift for individual results");
300 static_assert(sizeof...(TailTypes) < (8 * sizeof(ResultType) / Shift),
301 "not enough bits in the result type to store all bit masks");
302
303 static constexpr ResultType result =
304 ResultType(Each<HeadType>::result) |
305 (BitMask<Each, ResultType, Shift, TailTypes...>::result << Shift);
306 };
307
308 // Helper template to build the VMFunctionData for a function.
309 template <typename... Args>
310 struct VMFunctionDataHelper;
311
312 template <class R, typename... Args>
313 struct VMFunctionDataHelper<R (*)(JSContext*, Args...)>
314 : public VMFunctionData {
315 using Fun = R (*)(JSContext*, Args...);
316
returnTypejs::jit::VMFunctionDataHelper317 static constexpr DataType returnType() {
318 return ReturnTypeToDataType<R>::result;
319 }
outParamjs::jit::VMFunctionDataHelper320 static constexpr DataType outParam() {
321 return OutParamToDataType<typename LastArg<Args...>::Type>::result;
322 }
outParamRootTypejs::jit::VMFunctionDataHelper323 static constexpr RootType outParamRootType() {
324 return OutParamToRootType<typename LastArg<Args...>::Type>::result;
325 }
NbArgsjs::jit::VMFunctionDataHelper326 static constexpr size_t NbArgs() { return sizeof...(Args); }
explicitArgsjs::jit::VMFunctionDataHelper327 static constexpr size_t explicitArgs() {
328 return NbArgs() - (outParam() != Type_Void ? 1 : 0);
329 }
argumentPropertiesjs::jit::VMFunctionDataHelper330 static constexpr uint32_t argumentProperties() {
331 return BitMask<TypeToArgProperties, uint32_t, 2, Args...>::result;
332 }
argumentPassedInFloatRegsjs::jit::VMFunctionDataHelper333 static constexpr uint32_t argumentPassedInFloatRegs() {
334 return BitMask<TypeToPassInFloatReg, uint32_t, 2, Args...>::result;
335 }
argumentRootTypesjs::jit::VMFunctionDataHelper336 static constexpr uint64_t argumentRootTypes() {
337 return BitMask<TypeToRootType, uint64_t, 3, Args...>::result;
338 }
VMFunctionDataHelperjs::jit::VMFunctionDataHelper339 constexpr explicit VMFunctionDataHelper(const char* name)
340 : VMFunctionData(name, explicitArgs(), argumentProperties(),
341 argumentPassedInFloatRegs(), argumentRootTypes(),
342 outParam(), outParamRootType(), returnType(),
343 /* extraValuesToPop = */ 0, NonTailCall) {}
VMFunctionDataHelperjs::jit::VMFunctionDataHelper344 constexpr explicit VMFunctionDataHelper(const char* name,
345 MaybeTailCall expectTailCall,
346 PopValues extraValuesToPop)
347 : VMFunctionData(name, explicitArgs(), argumentProperties(),
348 argumentPassedInFloatRegs(), argumentRootTypes(),
349 outParam(), outParamRootType(), returnType(),
350 extraValuesToPop.numValues, expectTailCall) {}
351 };
352
353 // GCC warns when the signature does not have matching attributes (for example
354 // [[nodiscard]]). Squelch this warning to avoid a GCC-only footgun.
355 #if MOZ_IS_GCC
356 # pragma GCC diagnostic push
357 # pragma GCC diagnostic ignored "-Wignored-attributes"
358 #endif
359
360 // Generate VMFunctionData array.
361 static constexpr VMFunctionData vmFunctions[] = {
362 #define DEF_VMFUNCTION(name, fp) VMFunctionDataHelper<decltype(&(::fp))>(#name),
363 VMFUNCTION_LIST(DEF_VMFUNCTION)
364 #undef DEF_VMFUNCTION
365 };
366 static constexpr VMFunctionData tailCallVMFunctions[] = {
367 #define DEF_VMFUNCTION(name, fp, valuesToPop) \
368 VMFunctionDataHelper<decltype(&(::fp))>(#name, TailCall, \
369 PopValues(valuesToPop)),
370 TAIL_CALL_VMFUNCTION_LIST(DEF_VMFUNCTION)
371 #undef DEF_VMFUNCTION
372 };
373
374 #if MOZ_IS_GCC
375 # pragma GCC diagnostic pop
376 #endif
377
378 // Generate arrays storing C++ function pointers. These pointers are not stored
379 // in VMFunctionData because there's no good way to cast them to void* in
380 // constexpr code. Compilers are smart enough to treat the const array below as
381 // constexpr.
382 #define DEF_VMFUNCTION(name, fp, ...) (void*)(::fp),
383 static void* const vmFunctionTargets[] = {VMFUNCTION_LIST(DEF_VMFUNCTION)};
384 static void* const tailCallVMFunctionTargets[] = {
385 TAIL_CALL_VMFUNCTION_LIST(DEF_VMFUNCTION)};
386 #undef DEF_VMFUNCTION
387
GetVMFunction(VMFunctionId id)388 const VMFunctionData& GetVMFunction(VMFunctionId id) {
389 return vmFunctions[size_t(id)];
390 }
GetVMFunction(TailCallVMFunctionId id)391 const VMFunctionData& GetVMFunction(TailCallVMFunctionId id) {
392 return tailCallVMFunctions[size_t(id)];
393 }
394
GetVMFunctionTarget(VMFunctionId id)395 static DynFn GetVMFunctionTarget(VMFunctionId id) {
396 return DynFn{vmFunctionTargets[size_t(id)]};
397 }
398
GetVMFunctionTarget(TailCallVMFunctionId id)399 static DynFn GetVMFunctionTarget(TailCallVMFunctionId id) {
400 return DynFn{tailCallVMFunctionTargets[size_t(id)]};
401 }
402
403 template <typename IdT>
generateVMWrappers(JSContext * cx,MacroAssembler & masm,VMWrapperOffsets & offsets)404 bool JitRuntime::generateVMWrappers(JSContext* cx, MacroAssembler& masm,
405 VMWrapperOffsets& offsets) {
406 // Generate all VM function wrappers.
407
408 static constexpr size_t NumVMFunctions = size_t(IdT::Count);
409
410 if (!offsets.reserve(NumVMFunctions)) {
411 return false;
412 }
413
414 #ifdef DEBUG
415 const char* lastName = nullptr;
416 #endif
417
418 for (size_t i = 0; i < NumVMFunctions; i++) {
419 IdT id = IdT(i);
420 const VMFunctionData& fun = GetVMFunction(id);
421
422 #ifdef DEBUG
423 // Assert the list is sorted by name.
424 if (lastName) {
425 MOZ_ASSERT(strcmp(lastName, fun.name()) < 0,
426 "VM function list must be sorted by name");
427 }
428 lastName = fun.name();
429 #endif
430
431 JitSpew(JitSpew_Codegen, "# VM function wrapper (%s)", fun.name());
432
433 uint32_t offset;
434 if (!generateVMWrapper(cx, masm, fun, GetVMFunctionTarget(id), &offset)) {
435 return false;
436 }
437
438 MOZ_ASSERT(offsets.length() == size_t(id));
439 offsets.infallibleAppend(offset);
440 }
441
442 return true;
443 };
444
generateVMWrappers(JSContext * cx,MacroAssembler & masm)445 bool JitRuntime::generateVMWrappers(JSContext* cx, MacroAssembler& masm) {
446 if (!generateVMWrappers<VMFunctionId>(cx, masm, functionWrapperOffsets_)) {
447 return false;
448 }
449
450 if (!generateVMWrappers<TailCallVMFunctionId>(
451 cx, masm, tailCallFunctionWrapperOffsets_)) {
452 return false;
453 }
454
455 return true;
456 }
457
InvokeFunction(JSContext * cx,HandleObject obj,bool constructing,bool ignoresReturnValue,uint32_t argc,Value * argv,MutableHandleValue rval)458 bool InvokeFunction(JSContext* cx, HandleObject obj, bool constructing,
459 bool ignoresReturnValue, uint32_t argc, Value* argv,
460 MutableHandleValue rval) {
461 TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
462 TraceLogStartEvent(logger, TraceLogger_Call);
463
464 RootedExternalValueArray argvRoot(cx, argc + 1 + constructing, argv);
465
466 // Data in the argument vector is arranged for a JIT -> JIT call.
467 RootedValue thisv(cx, argv[0]);
468 Value* argvWithoutThis = argv + 1;
469
470 RootedValue fval(cx, ObjectValue(*obj));
471 if (constructing) {
472 if (!IsConstructor(fval)) {
473 ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval,
474 nullptr);
475 return false;
476 }
477
478 ConstructArgs cargs(cx);
479 if (!cargs.init(cx, argc)) {
480 return false;
481 }
482
483 for (uint32_t i = 0; i < argc; i++) {
484 cargs[i].set(argvWithoutThis[i]);
485 }
486
487 RootedValue newTarget(cx, argvWithoutThis[argc]);
488
489 // See CreateThisFromIon for why this can be NullValue.
490 if (thisv.isNull()) {
491 thisv.setMagic(JS_IS_CONSTRUCTING);
492 }
493
494 // If |this| hasn't been created, or is JS_UNINITIALIZED_LEXICAL,
495 // we can use normal construction code without creating an extraneous
496 // object.
497 if (thisv.isMagic()) {
498 MOZ_ASSERT(thisv.whyMagic() == JS_IS_CONSTRUCTING ||
499 thisv.whyMagic() == JS_UNINITIALIZED_LEXICAL);
500
501 RootedObject obj(cx);
502 if (!Construct(cx, fval, cargs, newTarget, &obj)) {
503 return false;
504 }
505
506 rval.setObject(*obj);
507 return true;
508 }
509
510 // Otherwise the default |this| has already been created. We could
511 // almost perform a *call* at this point, but we'd break |new.target|
512 // in the function. So in this one weird case we call a one-off
513 // construction path that *won't* set |this| to JS_IS_CONSTRUCTING.
514 return InternalConstructWithProvidedThis(cx, fval, thisv, cargs, newTarget,
515 rval);
516 }
517
518 InvokeArgsMaybeIgnoresReturnValue args(cx);
519 if (!args.init(cx, argc, ignoresReturnValue)) {
520 return false;
521 }
522
523 for (size_t i = 0; i < argc; i++) {
524 args[i].set(argvWithoutThis[i]);
525 }
526
527 return Call(cx, fval, thisv, args, rval);
528 }
529
GetContextSensitiveInterpreterStub()530 void* GetContextSensitiveInterpreterStub() {
531 return TlsContext.get()->runtime()->jitRuntime()->interpreterStub().value;
532 }
533
InvokeFromInterpreterStub(JSContext * cx,InterpreterStubExitFrameLayout * frame)534 bool InvokeFromInterpreterStub(JSContext* cx,
535 InterpreterStubExitFrameLayout* frame) {
536 JitFrameLayout* jsFrame = frame->jsFrame();
537 CalleeToken token = jsFrame->calleeToken();
538
539 Value* argv = jsFrame->argv();
540 uint32_t numActualArgs = jsFrame->numActualArgs();
541 bool constructing = CalleeTokenIsConstructing(token);
542 RootedFunction fun(cx, CalleeTokenToFunction(token));
543
544 // Ensure new.target immediately follows the actual arguments (the arguments
545 // rectifier added padding).
546 if (constructing && numActualArgs < fun->nargs()) {
547 argv[1 + numActualArgs] = argv[1 + fun->nargs()];
548 }
549
550 RootedValue rval(cx);
551 if (!InvokeFunction(cx, fun, constructing,
552 /* ignoresReturnValue = */ false, numActualArgs, argv,
553 &rval)) {
554 return false;
555 }
556
557 // Overwrite |this| with the return value.
558 argv[0] = rval;
559 return true;
560 }
561
CheckOverRecursedImpl(JSContext * cx,size_t extra)562 static bool CheckOverRecursedImpl(JSContext* cx, size_t extra) {
563 // We just failed the jitStackLimit check. There are two possible reasons:
564 // 1) jitStackLimit was the real stack limit and we're over-recursed
565 // 2) jitStackLimit was set to UINTPTR_MAX by JSContext::requestInterrupt
566 // and we need to call JSContext::handleInterrupt.
567
568 // This handles 1).
569 #ifdef JS_SIMULATOR
570 if (cx->simulator()->overRecursedWithExtra(extra)) {
571 ReportOverRecursed(cx);
572 return false;
573 }
574 #else
575 AutoCheckRecursionLimit recursion(cx);
576 if (!recursion.checkWithExtra(cx, extra)) {
577 return false;
578 }
579 #endif
580
581 // This handles 2).
582 gc::MaybeVerifyBarriers(cx);
583 return cx->handleInterrupt();
584 }
585
CheckOverRecursed(JSContext * cx)586 bool CheckOverRecursed(JSContext* cx) { return CheckOverRecursedImpl(cx, 0); }
587
CheckOverRecursedBaseline(JSContext * cx,BaselineFrame * frame)588 bool CheckOverRecursedBaseline(JSContext* cx, BaselineFrame* frame) {
589 // The stack check in Baseline happens before pushing locals so we have to
590 // account for that by including script->nslots() in the C++ recursion check.
591 size_t extra = frame->script()->nslots() * sizeof(Value);
592 return CheckOverRecursedImpl(cx, extra);
593 }
594
MutatePrototype(JSContext * cx,HandlePlainObject obj,HandleValue value)595 bool MutatePrototype(JSContext* cx, HandlePlainObject obj, HandleValue value) {
596 if (!value.isObjectOrNull()) {
597 return true;
598 }
599
600 RootedObject newProto(cx, value.toObjectOrNull());
601 return SetPrototype(cx, obj, newProto);
602 }
603
604 template <EqualityKind Kind>
StringsEqual(JSContext * cx,HandleString lhs,HandleString rhs,bool * res)605 bool StringsEqual(JSContext* cx, HandleString lhs, HandleString rhs,
606 bool* res) {
607 if (!js::EqualStrings(cx, lhs, rhs, res)) {
608 return false;
609 }
610 if (Kind != EqualityKind::Equal) {
611 *res = !*res;
612 }
613 return true;
614 }
615
616 template bool StringsEqual<EqualityKind::Equal>(JSContext* cx, HandleString lhs,
617 HandleString rhs, bool* res);
618 template bool StringsEqual<EqualityKind::NotEqual>(JSContext* cx,
619 HandleString lhs,
620 HandleString rhs, bool* res);
621
622 template <ComparisonKind Kind>
StringsCompare(JSContext * cx,HandleString lhs,HandleString rhs,bool * res)623 bool StringsCompare(JSContext* cx, HandleString lhs, HandleString rhs,
624 bool* res) {
625 int32_t result;
626 if (!js::CompareStrings(cx, lhs, rhs, &result)) {
627 return false;
628 }
629 if (Kind == ComparisonKind::LessThan) {
630 *res = result < 0;
631 } else {
632 *res = result >= 0;
633 }
634 return true;
635 }
636
637 template bool StringsCompare<ComparisonKind::LessThan>(JSContext* cx,
638 HandleString lhs,
639 HandleString rhs,
640 bool* res);
641 template bool StringsCompare<ComparisonKind::GreaterThanOrEqual>(
642 JSContext* cx, HandleString lhs, HandleString rhs, bool* res);
643
ArrayPushDense(JSContext * cx,HandleArrayObject arr,HandleValue v,uint32_t * length)644 bool ArrayPushDense(JSContext* cx, HandleArrayObject arr, HandleValue v,
645 uint32_t* length) {
646 *length = arr->length();
647 DenseElementResult result =
648 arr->setOrExtendDenseElements(cx, *length, v.address(), 1);
649 if (result != DenseElementResult::Incomplete) {
650 (*length)++;
651 return result == DenseElementResult::Success;
652 }
653
654 JS::RootedValueArray<3> argv(cx);
655 argv[0].setUndefined();
656 argv[1].setObject(*arr);
657 argv[2].set(v);
658 if (!js::array_push(cx, 1, argv.begin())) {
659 return false;
660 }
661
662 // Length must fit in an int32 because we guard against overflow before
663 // calling this VM function.
664 *length = argv[0].toInt32();
665 return true;
666 }
667
ArrayJoin(JSContext * cx,HandleObject array,HandleString sep)668 JSString* ArrayJoin(JSContext* cx, HandleObject array, HandleString sep) {
669 JS::RootedValueArray<3> argv(cx);
670 argv[0].setUndefined();
671 argv[1].setObject(*array);
672 argv[2].setString(sep);
673 if (!js::array_join(cx, 1, argv.begin())) {
674 return nullptr;
675 }
676 return argv[0].toString();
677 }
678
SetArrayLength(JSContext * cx,HandleObject obj,HandleValue value,bool strict)679 bool SetArrayLength(JSContext* cx, HandleObject obj, HandleValue value,
680 bool strict) {
681 Handle<ArrayObject*> array = obj.as<ArrayObject>();
682
683 RootedId id(cx, NameToId(cx->names().length));
684 ObjectOpResult result;
685
686 // SetArrayLength is called by IC stubs for SetProp and SetElem on arrays'
687 // "length" property.
688 //
689 // ArraySetLength below coerces |value| before checking for length being
690 // writable, and in the case of illegal values, will throw RangeError even
691 // when "length" is not writable. This is incorrect observable behavior,
692 // as a regular [[Set]] operation will check for "length" being
693 // writable before attempting any assignment.
694 //
695 // So, perform ArraySetLength if and only if "length" is writable.
696 if (array->lengthIsWritable()) {
697 Rooted<PropertyDescriptor> desc(
698 cx, PropertyDescriptor::Data(value, JS::PropertyAttribute::Writable));
699 if (!ArraySetLength(cx, array, id, desc, result)) {
700 return false;
701 }
702 } else {
703 MOZ_ALWAYS_TRUE(result.fail(JSMSG_READ_ONLY));
704 }
705
706 return result.checkStrictModeError(cx, obj, id, strict);
707 }
708
CharCodeAt(JSContext * cx,HandleString str,int32_t index,uint32_t * code)709 bool CharCodeAt(JSContext* cx, HandleString str, int32_t index,
710 uint32_t* code) {
711 char16_t c;
712 if (!str->getChar(cx, index, &c)) {
713 return false;
714 }
715 *code = c;
716 return true;
717 }
718
StringFromCharCode(JSContext * cx,int32_t code)719 JSLinearString* StringFromCharCode(JSContext* cx, int32_t code) {
720 char16_t c = char16_t(code);
721
722 if (StaticStrings::hasUnit(c)) {
723 return cx->staticStrings().getUnit(c);
724 }
725
726 return NewStringCopyNDontDeflate<CanGC>(cx, &c, 1);
727 }
728
StringFromCharCodeNoGC(JSContext * cx,int32_t code)729 JSLinearString* StringFromCharCodeNoGC(JSContext* cx, int32_t code) {
730 AutoUnsafeCallWithABI unsafe;
731
732 char16_t c = char16_t(code);
733
734 if (StaticStrings::hasUnit(c)) {
735 return cx->staticStrings().getUnit(c);
736 }
737
738 return NewStringCopyNDontDeflate<NoGC>(cx, &c, 1);
739 }
740
StringFromCodePoint(JSContext * cx,int32_t codePoint)741 JSString* StringFromCodePoint(JSContext* cx, int32_t codePoint) {
742 RootedValue rval(cx, Int32Value(codePoint));
743 if (!str_fromCodePoint_one_arg(cx, rval, &rval)) {
744 return nullptr;
745 }
746
747 return rval.toString();
748 }
749
SetProperty(JSContext * cx,HandleObject obj,HandlePropertyName name,HandleValue value,bool strict,jsbytecode * pc)750 bool SetProperty(JSContext* cx, HandleObject obj, HandlePropertyName name,
751 HandleValue value, bool strict, jsbytecode* pc) {
752 RootedId id(cx, NameToId(name));
753
754 RootedValue receiver(cx, ObjectValue(*obj));
755 ObjectOpResult result;
756 if (MOZ_LIKELY(!obj->getOpsSetProperty())) {
757 JSOp op = JSOp(*pc);
758 if (op == JSOp::SetName || op == JSOp::StrictSetName ||
759 op == JSOp::SetGName || op == JSOp::StrictSetGName) {
760 if (!NativeSetProperty<Unqualified>(cx, obj.as<NativeObject>(), id, value,
761 receiver, result)) {
762 return false;
763 }
764 } else {
765 if (!NativeSetProperty<Qualified>(cx, obj.as<NativeObject>(), id, value,
766 receiver, result)) {
767 return false;
768 }
769 }
770 } else {
771 if (!SetProperty(cx, obj, id, value, receiver, result)) {
772 return false;
773 }
774 }
775 return result.checkStrictModeError(cx, obj, id, strict);
776 }
777
InterruptCheck(JSContext * cx)778 bool InterruptCheck(JSContext* cx) {
779 gc::MaybeVerifyBarriers(cx);
780
781 return CheckForInterrupt(cx);
782 }
783
NewCallObject(JSContext * cx,HandleShape shape)784 JSObject* NewCallObject(JSContext* cx, HandleShape shape) {
785 JSObject* obj = CallObject::create(cx, shape);
786 if (!obj) {
787 return nullptr;
788 }
789
790 // The JIT creates call objects in the nursery, so elides barriers for
791 // the initializing writes. The interpreter, however, may have allocated
792 // the call object tenured, so barrier as needed before re-entering.
793 if (!IsInsideNursery(obj)) {
794 cx->runtime()->gc.storeBuffer().putWholeCell(obj);
795 }
796
797 return obj;
798 }
799
NewStringObject(JSContext * cx,HandleString str)800 JSObject* NewStringObject(JSContext* cx, HandleString str) {
801 return StringObject::create(cx, str);
802 }
803
OperatorIn(JSContext * cx,HandleValue key,HandleObject obj,bool * out)804 bool OperatorIn(JSContext* cx, HandleValue key, HandleObject obj, bool* out) {
805 RootedId id(cx);
806 return ToPropertyKey(cx, key, &id) && HasProperty(cx, obj, id, out);
807 }
808
GetIntrinsicValue(JSContext * cx,HandlePropertyName name,MutableHandleValue rval)809 bool GetIntrinsicValue(JSContext* cx, HandlePropertyName name,
810 MutableHandleValue rval) {
811 return GlobalObject::getIntrinsicValue(cx, cx->global(), name, rval);
812 }
813
CreateThisFromIC(JSContext * cx,HandleObject callee,HandleObject newTarget,MutableHandleValue rval)814 bool CreateThisFromIC(JSContext* cx, HandleObject callee,
815 HandleObject newTarget, MutableHandleValue rval) {
816 HandleFunction fun = callee.as<JSFunction>();
817 MOZ_ASSERT(fun->isInterpreted());
818 MOZ_ASSERT(fun->isConstructor());
819 MOZ_ASSERT(cx->realm() == fun->realm(),
820 "Realm switching happens before creating this");
821
822 // CreateThis expects rval to be this magic value.
823 rval.set(MagicValue(JS_IS_CONSTRUCTING));
824
825 if (!js::CreateThis(cx, fun, newTarget, GenericObject, rval)) {
826 return false;
827 }
828
829 MOZ_ASSERT_IF(rval.isObject(), fun->realm() == rval.toObject().nonCCWRealm());
830 return true;
831 }
832
CreateThisFromIon(JSContext * cx,HandleObject callee,HandleObject newTarget,MutableHandleValue rval)833 bool CreateThisFromIon(JSContext* cx, HandleObject callee,
834 HandleObject newTarget, MutableHandleValue rval) {
835 // Return JS_IS_CONSTRUCTING for cases not supported by the inline call path.
836 rval.set(MagicValue(JS_IS_CONSTRUCTING));
837
838 if (!callee->is<JSFunction>()) {
839 return true;
840 }
841
842 HandleFunction fun = callee.as<JSFunction>();
843 if (!fun->isInterpreted() || !fun->isConstructor()) {
844 return true;
845 }
846
847 // If newTarget is not a function or is a function with a possibly-getter
848 // .prototype property, return NullValue to signal to LCallGeneric that it has
849 // to take the slow path. Note that we return NullValue instead of a
850 // MagicValue only because it's easier and faster to check for in JIT code
851 // (if we returned a MagicValue, JIT code would have to check both the type
852 // tag and the JSWhyMagic payload).
853 if (!fun->constructorNeedsUninitializedThis()) {
854 if (!newTarget->is<JSFunction>()) {
855 rval.setNull();
856 return true;
857 }
858 JSFunction* newTargetFun = &newTarget->as<JSFunction>();
859 if (!newTargetFun->hasNonConfigurablePrototypeDataProperty()) {
860 rval.setNull();
861 return true;
862 }
863 }
864
865 AutoRealm ar(cx, fun);
866 if (!js::CreateThis(cx, fun, newTarget, GenericObject, rval)) {
867 return false;
868 }
869
870 MOZ_ASSERT_IF(rval.isObject(), fun->realm() == rval.toObject().nonCCWRealm());
871 return true;
872 }
873
PostWriteBarrier(JSRuntime * rt,js::gc::Cell * cell)874 void PostWriteBarrier(JSRuntime* rt, js::gc::Cell* cell) {
875 AutoUnsafeCallWithABI unsafe;
876 MOZ_ASSERT(!IsInsideNursery(cell));
877 rt->gc.storeBuffer().putWholeCell(cell);
878 }
879
880 static const size_t MAX_WHOLE_CELL_BUFFER_SIZE = 4096;
881
882 template <IndexInBounds InBounds>
PostWriteElementBarrier(JSRuntime * rt,JSObject * obj,int32_t index)883 void PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, int32_t index) {
884 AutoUnsafeCallWithABI unsafe;
885
886 MOZ_ASSERT(!IsInsideNursery(obj));
887
888 if (InBounds == IndexInBounds::Yes) {
889 MOZ_ASSERT(uint32_t(index) <
890 obj->as<NativeObject>().getDenseInitializedLength());
891 } else {
892 if (MOZ_UNLIKELY(!obj->is<NativeObject>() || index < 0 ||
893 uint32_t(index) >=
894 NativeObject::MAX_DENSE_ELEMENTS_COUNT)) {
895 rt->gc.storeBuffer().putWholeCell(obj);
896 return;
897 }
898 }
899
900 NativeObject* nobj = &obj->as<NativeObject>();
901 if (nobj->isInWholeCellBuffer()) {
902 return;
903 }
904
905 if (nobj->getDenseInitializedLength() > MAX_WHOLE_CELL_BUFFER_SIZE
906 #ifdef JS_GC_ZEAL
907 || rt->hasZealMode(gc::ZealMode::ElementsBarrier)
908 #endif
909 ) {
910 rt->gc.storeBuffer().putSlot(nobj, HeapSlot::Element,
911 nobj->unshiftedIndex(index), 1);
912 return;
913 }
914
915 rt->gc.storeBuffer().putWholeCell(obj);
916 }
917
918 template void PostWriteElementBarrier<IndexInBounds::Yes>(JSRuntime* rt,
919 JSObject* obj,
920 int32_t index);
921
922 template void PostWriteElementBarrier<IndexInBounds::Maybe>(JSRuntime* rt,
923 JSObject* obj,
924 int32_t index);
925
PostGlobalWriteBarrier(JSRuntime * rt,GlobalObject * obj)926 void PostGlobalWriteBarrier(JSRuntime* rt, GlobalObject* obj) {
927 MOZ_ASSERT(obj->JSObject::is<GlobalObject>());
928
929 if (!obj->realm()->globalWriteBarriered) {
930 PostWriteBarrier(rt, obj);
931 obj->realm()->globalWriteBarriered = 1;
932 }
933 }
934
GetInt32FromStringPure(JSContext * cx,JSString * str,int32_t * result)935 bool GetInt32FromStringPure(JSContext* cx, JSString* str, int32_t* result) {
936 // We shouldn't GC here as this is called directly from IC code.
937 AutoUnsafeCallWithABI unsafe;
938
939 double d;
940 if (!StringToNumberPure(cx, str, &d)) {
941 return false;
942 }
943
944 return mozilla::NumberIsInt32(d, result);
945 }
946
GetIndexFromString(JSString * str)947 int32_t GetIndexFromString(JSString* str) {
948 // We shouldn't GC here as this is called directly from IC code.
949 AutoUnsafeCallWithABI unsafe;
950
951 if (!str->isLinear()) {
952 return -1;
953 }
954
955 uint32_t index = UINT32_MAX; // Initialize this to appease Valgrind.
956 if (!str->asLinear().isIndex(&index) || index > INT32_MAX) {
957 return -1;
958 }
959
960 return int32_t(index);
961 }
962
WrapObjectPure(JSContext * cx,JSObject * obj)963 JSObject* WrapObjectPure(JSContext* cx, JSObject* obj) {
964 // IC code calls this directly so we shouldn't GC.
965 AutoUnsafeCallWithABI unsafe;
966
967 MOZ_ASSERT(obj);
968 MOZ_ASSERT(cx->compartment() != obj->compartment());
969
970 // From: Compartment::getNonWrapperObjectForCurrentCompartment
971 // Note that if the object is same-compartment, but has been wrapped into a
972 // different compartment, we need to unwrap it and return the bare same-
973 // compartment object. Note again that windows are always wrapped by a
974 // WindowProxy even when same-compartment so take care not to strip this
975 // particular wrapper.
976 obj = UncheckedUnwrap(obj, /* stopAtWindowProxy = */ true);
977 if (cx->compartment() == obj->compartment()) {
978 MOZ_ASSERT(!IsWindow(obj));
979 JS::ExposeObjectToActiveJS(obj);
980 return obj;
981 }
982
983 // Try to Lookup an existing wrapper for this object. We assume that
984 // if we can find such a wrapper, not calling preWrap is correct.
985 if (ObjectWrapperMap::Ptr p = cx->compartment()->lookupWrapper(obj)) {
986 JSObject* wrapped = p->value().get();
987
988 // Ensure the wrapper is still exposed.
989 JS::ExposeObjectToActiveJS(wrapped);
990 return wrapped;
991 }
992
993 return nullptr;
994 }
995
DebugPrologue(JSContext * cx,BaselineFrame * frame)996 bool DebugPrologue(JSContext* cx, BaselineFrame* frame) {
997 return DebugAPI::onEnterFrame(cx, frame);
998 }
999
DebugEpilogueOnBaselineReturn(JSContext * cx,BaselineFrame * frame,const jsbytecode * pc)1000 bool DebugEpilogueOnBaselineReturn(JSContext* cx, BaselineFrame* frame,
1001 const jsbytecode* pc) {
1002 if (!DebugEpilogue(cx, frame, pc, true)) {
1003 // DebugEpilogue popped the frame by updating packedExitFP, so run the
1004 // stop event here before we enter the exception handler.
1005 TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
1006 TraceLogStopEvent(logger, TraceLogger_Baseline);
1007 TraceLogStopEvent(logger, TraceLogger_Scripts);
1008 return false;
1009 }
1010
1011 return true;
1012 }
1013
DebugEpilogue(JSContext * cx,BaselineFrame * frame,const jsbytecode * pc,bool ok)1014 bool DebugEpilogue(JSContext* cx, BaselineFrame* frame, const jsbytecode* pc,
1015 bool ok) {
1016 // If DebugAPI::onLeaveFrame returns |true| we have to return the frame's
1017 // return value. If it returns |false|, the debugger threw an exception.
1018 // In both cases we have to pop debug scopes.
1019 ok = DebugAPI::onLeaveFrame(cx, frame, pc, ok);
1020
1021 // Unwind to the outermost environment.
1022 EnvironmentIter ei(cx, frame, pc);
1023 UnwindAllEnvironmentsInFrame(cx, ei);
1024
1025 if (!ok) {
1026 // Pop this frame by updating packedExitFP, so that the exception
1027 // handling code will start at the previous frame.
1028 JitFrameLayout* prefix = frame->framePrefix();
1029 EnsureBareExitFrame(cx->activation()->asJit(), prefix);
1030 return false;
1031 }
1032
1033 return true;
1034 }
1035
FrameIsDebuggeeCheck(BaselineFrame * frame)1036 void FrameIsDebuggeeCheck(BaselineFrame* frame) {
1037 AutoUnsafeCallWithABI unsafe;
1038 if (frame->script()->isDebuggee()) {
1039 frame->setIsDebuggee();
1040 }
1041 }
1042
CreateGeneratorFromFrame(JSContext * cx,BaselineFrame * frame)1043 JSObject* CreateGeneratorFromFrame(JSContext* cx, BaselineFrame* frame) {
1044 return AbstractGeneratorObject::createFromFrame(cx, frame);
1045 }
1046
CreateGenerator(JSContext * cx,HandleFunction callee,HandleScript script,HandleObject environmentChain,HandleObject args)1047 JSObject* CreateGenerator(JSContext* cx, HandleFunction callee,
1048 HandleScript script, HandleObject environmentChain,
1049 HandleObject args) {
1050 Rooted<ArgumentsObject*> argsObj(
1051 cx, args ? &args->as<ArgumentsObject>() : nullptr);
1052 return AbstractGeneratorObject::create(cx, callee, script, environmentChain,
1053 argsObj);
1054 }
1055
NormalSuspend(JSContext * cx,HandleObject obj,BaselineFrame * frame,uint32_t frameSize,const jsbytecode * pc)1056 bool NormalSuspend(JSContext* cx, HandleObject obj, BaselineFrame* frame,
1057 uint32_t frameSize, const jsbytecode* pc) {
1058 MOZ_ASSERT(JSOp(*pc) == JSOp::InitialYield || JSOp(*pc) == JSOp::Yield ||
1059 JSOp(*pc) == JSOp::Await);
1060
1061 // Minus one because we don't want to include the return value.
1062 uint32_t numSlots = frame->numValueSlots(frameSize) - 1;
1063 MOZ_ASSERT(numSlots >= frame->script()->nfixed());
1064 return AbstractGeneratorObject::suspend(cx, obj, frame, pc, numSlots);
1065 }
1066
FinalSuspend(JSContext * cx,HandleObject obj,const jsbytecode * pc)1067 bool FinalSuspend(JSContext* cx, HandleObject obj, const jsbytecode* pc) {
1068 MOZ_ASSERT(JSOp(*pc) == JSOp::FinalYieldRval);
1069 AbstractGeneratorObject::finalSuspend(obj);
1070 return true;
1071 }
1072
InterpretResume(JSContext * cx,HandleObject obj,Value * stackValues,MutableHandleValue rval)1073 bool InterpretResume(JSContext* cx, HandleObject obj, Value* stackValues,
1074 MutableHandleValue rval) {
1075 MOZ_ASSERT(obj->is<AbstractGeneratorObject>());
1076
1077 // The |stackValues| argument points to the JSOp::Resume operands on the
1078 // native stack. Because the stack grows down, these values are:
1079 //
1080 // [resumeKind, argument, generator, ..]
1081
1082 MOZ_ASSERT(stackValues[2].toObject() == *obj);
1083
1084 GeneratorResumeKind resumeKind = IntToResumeKind(stackValues[0].toInt32());
1085 JSAtom* kind = ResumeKindToAtom(cx, resumeKind);
1086
1087 FixedInvokeArgs<3> args(cx);
1088
1089 args[0].setObject(*obj);
1090 args[1].set(stackValues[1]);
1091 args[2].setString(kind);
1092
1093 return CallSelfHostedFunction(cx, cx->names().InterpretGeneratorResume,
1094 UndefinedHandleValue, args, rval);
1095 }
1096
DebugAfterYield(JSContext * cx,BaselineFrame * frame)1097 bool DebugAfterYield(JSContext* cx, BaselineFrame* frame) {
1098 // The BaselineFrame has just been constructed by JSOp::Resume in the
1099 // caller. We need to set its debuggee flag as necessary.
1100 //
1101 // If a breakpoint is set on JSOp::AfterYield, or stepping is enabled,
1102 // we may already have done this work. Don't fire onEnterFrame again.
1103 if (frame->script()->isDebuggee() && !frame->isDebuggee()) {
1104 frame->setIsDebuggee();
1105 return DebugAPI::onResumeFrame(cx, frame);
1106 }
1107
1108 return true;
1109 }
1110
GeneratorThrowOrReturn(JSContext * cx,BaselineFrame * frame,Handle<AbstractGeneratorObject * > genObj,HandleValue arg,int32_t resumeKindArg)1111 bool GeneratorThrowOrReturn(JSContext* cx, BaselineFrame* frame,
1112 Handle<AbstractGeneratorObject*> genObj,
1113 HandleValue arg, int32_t resumeKindArg) {
1114 GeneratorResumeKind resumeKind = IntToResumeKind(resumeKindArg);
1115 MOZ_ALWAYS_FALSE(
1116 js::GeneratorThrowOrReturn(cx, frame, genObj, arg, resumeKind));
1117 return false;
1118 }
1119
GlobalDeclInstantiationFromIon(JSContext * cx,HandleScript script,const jsbytecode * pc)1120 bool GlobalDeclInstantiationFromIon(JSContext* cx, HandleScript script,
1121 const jsbytecode* pc) {
1122 MOZ_ASSERT(!script->hasNonSyntacticScope());
1123
1124 RootedObject envChain(cx, &cx->global()->lexicalEnvironment());
1125 GCThingIndex lastFun = GET_GCTHING_INDEX(pc);
1126
1127 return GlobalOrEvalDeclInstantiation(cx, envChain, script, lastFun);
1128 }
1129
InitFunctionEnvironmentObjects(JSContext * cx,BaselineFrame * frame)1130 bool InitFunctionEnvironmentObjects(JSContext* cx, BaselineFrame* frame) {
1131 return frame->initFunctionEnvironmentObjects(cx);
1132 }
1133
NewArgumentsObject(JSContext * cx,BaselineFrame * frame,MutableHandleValue res)1134 bool NewArgumentsObject(JSContext* cx, BaselineFrame* frame,
1135 MutableHandleValue res) {
1136 ArgumentsObject* obj = ArgumentsObject::createExpected(cx, frame);
1137 if (!obj) {
1138 return false;
1139 }
1140 res.setObject(*obj);
1141 return true;
1142 }
1143
CopyLexicalEnvironmentObject(JSContext * cx,HandleObject env,bool copySlots)1144 JSObject* CopyLexicalEnvironmentObject(JSContext* cx, HandleObject env,
1145 bool copySlots) {
1146 Handle<BlockLexicalEnvironmentObject*> lexicalEnv =
1147 env.as<BlockLexicalEnvironmentObject>();
1148
1149 if (copySlots) {
1150 return BlockLexicalEnvironmentObject::clone(cx, lexicalEnv);
1151 }
1152
1153 return BlockLexicalEnvironmentObject::recreate(cx, lexicalEnv);
1154 }
1155
InitRestParameter(JSContext * cx,uint32_t length,Value * rest,HandleObject objRes)1156 JSObject* InitRestParameter(JSContext* cx, uint32_t length, Value* rest,
1157 HandleObject objRes) {
1158 if (objRes) {
1159 Handle<ArrayObject*> arrRes = objRes.as<ArrayObject>();
1160 MOZ_ASSERT(arrRes->getDenseInitializedLength() == 0);
1161
1162 // Fast path: we managed to allocate the array inline; initialize the
1163 // slots.
1164 if (length > 0) {
1165 if (!arrRes->ensureElements(cx, length)) {
1166 return nullptr;
1167 }
1168 arrRes->initDenseElements(rest, length);
1169 arrRes->setLength(length);
1170 }
1171 return arrRes;
1172 }
1173
1174 return NewDenseCopiedArray(cx, length, rest);
1175 }
1176
HandleDebugTrap(JSContext * cx,BaselineFrame * frame,const uint8_t * retAddr)1177 bool HandleDebugTrap(JSContext* cx, BaselineFrame* frame,
1178 const uint8_t* retAddr) {
1179 RootedScript script(cx, frame->script());
1180 jsbytecode* pc;
1181 if (frame->runningInInterpreter()) {
1182 pc = frame->interpreterPC();
1183 } else {
1184 BaselineScript* blScript = script->baselineScript();
1185 pc = blScript->retAddrEntryFromReturnAddress(retAddr).pc(script);
1186 }
1187
1188 // The Baseline Interpreter calls HandleDebugTrap for every op when the script
1189 // is in step mode or has breakpoints. The Baseline Compiler can toggle
1190 // breakpoints more granularly for specific bytecode PCs.
1191 if (frame->runningInInterpreter()) {
1192 MOZ_ASSERT(DebugAPI::hasAnyBreakpointsOrStepMode(script));
1193 } else {
1194 MOZ_ASSERT(DebugAPI::stepModeEnabled(script) ||
1195 DebugAPI::hasBreakpointsAt(script, pc));
1196 }
1197
1198 if (JSOp(*pc) == JSOp::AfterYield) {
1199 // JSOp::AfterYield will set the frame's debuggee flag and call the
1200 // onEnterFrame handler, but if we set a breakpoint there we have to do
1201 // it now.
1202 MOZ_ASSERT(!frame->isDebuggee());
1203
1204 if (!DebugAfterYield(cx, frame)) {
1205 return false;
1206 }
1207
1208 // If the frame is not a debuggee we're done. This can happen, for instance,
1209 // if the onEnterFrame hook called removeDebuggee.
1210 if (!frame->isDebuggee()) {
1211 return true;
1212 }
1213 }
1214
1215 MOZ_ASSERT(frame->isDebuggee());
1216
1217 if (DebugAPI::stepModeEnabled(script) && !DebugAPI::onSingleStep(cx)) {
1218 return false;
1219 }
1220
1221 if (DebugAPI::hasBreakpointsAt(script, pc) && !DebugAPI::onTrap(cx)) {
1222 return false;
1223 }
1224
1225 return true;
1226 }
1227
OnDebuggerStatement(JSContext * cx,BaselineFrame * frame)1228 bool OnDebuggerStatement(JSContext* cx, BaselineFrame* frame) {
1229 return DebugAPI::onDebuggerStatement(cx, frame);
1230 }
1231
GlobalHasLiveOnDebuggerStatement(JSContext * cx)1232 bool GlobalHasLiveOnDebuggerStatement(JSContext* cx) {
1233 AutoUnsafeCallWithABI unsafe;
1234 return cx->realm()->isDebuggee() &&
1235 DebugAPI::hasDebuggerStatementHook(cx->global());
1236 }
1237
PushLexicalEnv(JSContext * cx,BaselineFrame * frame,Handle<LexicalScope * > scope)1238 bool PushLexicalEnv(JSContext* cx, BaselineFrame* frame,
1239 Handle<LexicalScope*> scope) {
1240 return frame->pushLexicalEnvironment(cx, scope);
1241 }
1242
DebugLeaveThenPopLexicalEnv(JSContext * cx,BaselineFrame * frame,const jsbytecode * pc)1243 bool DebugLeaveThenPopLexicalEnv(JSContext* cx, BaselineFrame* frame,
1244 const jsbytecode* pc) {
1245 MOZ_ALWAYS_TRUE(DebugLeaveLexicalEnv(cx, frame, pc));
1246 frame->popOffEnvironmentChain<ScopedLexicalEnvironmentObject>();
1247 return true;
1248 }
1249
FreshenLexicalEnv(JSContext * cx,BaselineFrame * frame)1250 bool FreshenLexicalEnv(JSContext* cx, BaselineFrame* frame) {
1251 return frame->freshenLexicalEnvironment(cx);
1252 }
1253
DebugLeaveThenFreshenLexicalEnv(JSContext * cx,BaselineFrame * frame,const jsbytecode * pc)1254 bool DebugLeaveThenFreshenLexicalEnv(JSContext* cx, BaselineFrame* frame,
1255 const jsbytecode* pc) {
1256 MOZ_ALWAYS_TRUE(DebugLeaveLexicalEnv(cx, frame, pc));
1257 return frame->freshenLexicalEnvironment(cx);
1258 }
1259
RecreateLexicalEnv(JSContext * cx,BaselineFrame * frame)1260 bool RecreateLexicalEnv(JSContext* cx, BaselineFrame* frame) {
1261 return frame->recreateLexicalEnvironment(cx);
1262 }
1263
DebugLeaveThenRecreateLexicalEnv(JSContext * cx,BaselineFrame * frame,const jsbytecode * pc)1264 bool DebugLeaveThenRecreateLexicalEnv(JSContext* cx, BaselineFrame* frame,
1265 const jsbytecode* pc) {
1266 MOZ_ALWAYS_TRUE(DebugLeaveLexicalEnv(cx, frame, pc));
1267 return frame->recreateLexicalEnvironment(cx);
1268 }
1269
DebugLeaveLexicalEnv(JSContext * cx,BaselineFrame * frame,const jsbytecode * pc)1270 bool DebugLeaveLexicalEnv(JSContext* cx, BaselineFrame* frame,
1271 const jsbytecode* pc) {
1272 MOZ_ASSERT_IF(!frame->runningInInterpreter(),
1273 frame->script()->baselineScript()->hasDebugInstrumentation());
1274 if (cx->realm()->isDebuggee()) {
1275 DebugEnvironments::onPopLexical(cx, frame, pc);
1276 }
1277 return true;
1278 }
1279
PushClassBodyEnv(JSContext * cx,BaselineFrame * frame,Handle<ClassBodyScope * > scope)1280 bool PushClassBodyEnv(JSContext* cx, BaselineFrame* frame,
1281 Handle<ClassBodyScope*> scope) {
1282 return frame->pushClassBodyEnvironment(cx, scope);
1283 }
1284
PushVarEnv(JSContext * cx,BaselineFrame * frame,HandleScope scope)1285 bool PushVarEnv(JSContext* cx, BaselineFrame* frame, HandleScope scope) {
1286 return frame->pushVarEnvironment(cx, scope);
1287 }
1288
EnterWith(JSContext * cx,BaselineFrame * frame,HandleValue val,Handle<WithScope * > templ)1289 bool EnterWith(JSContext* cx, BaselineFrame* frame, HandleValue val,
1290 Handle<WithScope*> templ) {
1291 return EnterWithOperation(cx, frame, val, templ);
1292 }
1293
LeaveWith(JSContext * cx,BaselineFrame * frame)1294 bool LeaveWith(JSContext* cx, BaselineFrame* frame) {
1295 if (MOZ_UNLIKELY(frame->isDebuggee())) {
1296 DebugEnvironments::onPopWith(frame);
1297 }
1298 frame->popOffEnvironmentChain<WithEnvironmentObject>();
1299 return true;
1300 }
1301
InitBaselineFrameForOsr(BaselineFrame * frame,InterpreterFrame * interpFrame,uint32_t numStackValues)1302 bool InitBaselineFrameForOsr(BaselineFrame* frame,
1303 InterpreterFrame* interpFrame,
1304 uint32_t numStackValues) {
1305 return frame->initForOsr(interpFrame, numStackValues);
1306 }
1307
StringReplace(JSContext * cx,HandleString string,HandleString pattern,HandleString repl)1308 JSString* StringReplace(JSContext* cx, HandleString string,
1309 HandleString pattern, HandleString repl) {
1310 MOZ_ASSERT(string);
1311 MOZ_ASSERT(pattern);
1312 MOZ_ASSERT(repl);
1313
1314 return str_replace_string_raw(cx, string, pattern, repl);
1315 }
1316
SetDenseElement(JSContext * cx,HandleNativeObject obj,int32_t index,HandleValue value,bool strict)1317 bool SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index,
1318 HandleValue value, bool strict) {
1319 // This function is called from Ion code for StoreElementHole's OOL path.
1320 // In this case we know the object is native.
1321
1322 DenseElementResult result =
1323 obj->setOrExtendDenseElements(cx, index, value.address(), 1);
1324 if (result != DenseElementResult::Incomplete) {
1325 return result == DenseElementResult::Success;
1326 }
1327
1328 RootedValue indexVal(cx, Int32Value(index));
1329 return SetObjectElement(cx, obj, indexVal, value, strict);
1330 }
1331
AssertValidBigIntPtr(JSContext * cx,JS::BigInt * bi)1332 void AssertValidBigIntPtr(JSContext* cx, JS::BigInt* bi) {
1333 AutoUnsafeCallWithABI unsafe;
1334 // FIXME: check runtime?
1335 MOZ_ASSERT(cx->zone() == bi->zone());
1336 MOZ_ASSERT(bi->isAligned());
1337 MOZ_ASSERT(bi->getAllocKind() == gc::AllocKind::BIGINT);
1338 }
1339
AssertValidObjectPtr(JSContext * cx,JSObject * obj)1340 void AssertValidObjectPtr(JSContext* cx, JSObject* obj) {
1341 AutoUnsafeCallWithABI unsafe;
1342 #ifdef DEBUG
1343 // Check what we can, so that we'll hopefully assert/crash if we get a
1344 // bogus object (pointer).
1345 MOZ_ASSERT(obj->compartment() == cx->compartment());
1346 MOZ_ASSERT(obj->zoneFromAnyThread() == cx->zone());
1347 MOZ_ASSERT(obj->runtimeFromMainThread() == cx->runtime());
1348
1349 if (obj->isTenured()) {
1350 MOZ_ASSERT(obj->isAligned());
1351 gc::AllocKind kind = obj->asTenured().getAllocKind();
1352 MOZ_ASSERT(gc::IsObjectAllocKind(kind));
1353 }
1354 #endif
1355 }
1356
AssertValidStringPtr(JSContext * cx,JSString * str)1357 void AssertValidStringPtr(JSContext* cx, JSString* str) {
1358 AutoUnsafeCallWithABI unsafe;
1359 #ifdef DEBUG
1360 // We can't closely inspect strings from another runtime.
1361 if (str->runtimeFromAnyThread() != cx->runtime()) {
1362 MOZ_ASSERT(str->isPermanentAtom());
1363 return;
1364 }
1365
1366 if (str->isAtom()) {
1367 MOZ_ASSERT(str->zone()->isAtomsZone());
1368 } else {
1369 MOZ_ASSERT(str->zone() == cx->zone());
1370 }
1371
1372 MOZ_ASSERT(str->isAligned());
1373 MOZ_ASSERT(str->length() <= JSString::MAX_LENGTH);
1374
1375 gc::AllocKind kind = str->getAllocKind();
1376 if (str->isFatInline()) {
1377 MOZ_ASSERT(kind == gc::AllocKind::FAT_INLINE_STRING ||
1378 kind == gc::AllocKind::FAT_INLINE_ATOM);
1379 } else if (str->isExternal()) {
1380 MOZ_ASSERT(kind == gc::AllocKind::EXTERNAL_STRING);
1381 } else if (str->isAtom()) {
1382 MOZ_ASSERT(kind == gc::AllocKind::ATOM);
1383 } else if (str->isLinear()) {
1384 MOZ_ASSERT(kind == gc::AllocKind::STRING ||
1385 kind == gc::AllocKind::FAT_INLINE_STRING);
1386 } else {
1387 MOZ_ASSERT(kind == gc::AllocKind::STRING);
1388 }
1389 #endif
1390 }
1391
AssertValidSymbolPtr(JSContext * cx,JS::Symbol * sym)1392 void AssertValidSymbolPtr(JSContext* cx, JS::Symbol* sym) {
1393 AutoUnsafeCallWithABI unsafe;
1394
1395 // We can't closely inspect symbols from another runtime.
1396 if (sym->runtimeFromAnyThread() != cx->runtime()) {
1397 MOZ_ASSERT(sym->isWellKnownSymbol());
1398 return;
1399 }
1400
1401 MOZ_ASSERT(sym->zone()->isAtomsZone());
1402 MOZ_ASSERT(sym->isAligned());
1403 if (JSAtom* desc = sym->description()) {
1404 AssertValidStringPtr(cx, desc);
1405 }
1406
1407 MOZ_ASSERT(sym->getAllocKind() == gc::AllocKind::SYMBOL);
1408 }
1409
AssertValidValue(JSContext * cx,Value * v)1410 void AssertValidValue(JSContext* cx, Value* v) {
1411 AutoUnsafeCallWithABI unsafe;
1412 if (v->isObject()) {
1413 AssertValidObjectPtr(cx, &v->toObject());
1414 } else if (v->isString()) {
1415 AssertValidStringPtr(cx, v->toString());
1416 } else if (v->isSymbol()) {
1417 AssertValidSymbolPtr(cx, v->toSymbol());
1418 } else if (v->isBigInt()) {
1419 AssertValidBigIntPtr(cx, v->toBigInt());
1420 }
1421 }
1422
ObjectIsCallable(JSObject * obj)1423 bool ObjectIsCallable(JSObject* obj) {
1424 AutoUnsafeCallWithABI unsafe;
1425 return obj->isCallable();
1426 }
1427
ObjectIsConstructor(JSObject * obj)1428 bool ObjectIsConstructor(JSObject* obj) {
1429 AutoUnsafeCallWithABI unsafe;
1430 return obj->isConstructor();
1431 }
1432
JitValuePreWriteBarrier(JSRuntime * rt,Value * vp)1433 void JitValuePreWriteBarrier(JSRuntime* rt, Value* vp) {
1434 AutoUnsafeCallWithABI unsafe;
1435 MOZ_ASSERT(vp->isGCThing());
1436 MOZ_ASSERT(!vp->toGCThing()->isMarkedBlack());
1437 gc::ValuePreWriteBarrier(*vp);
1438 }
1439
JitStringPreWriteBarrier(JSRuntime * rt,JSString ** stringp)1440 void JitStringPreWriteBarrier(JSRuntime* rt, JSString** stringp) {
1441 AutoUnsafeCallWithABI unsafe;
1442 MOZ_ASSERT(*stringp);
1443 MOZ_ASSERT(!(*stringp)->isMarkedBlack());
1444 gc::PreWriteBarrier(*stringp);
1445 }
1446
JitObjectPreWriteBarrier(JSRuntime * rt,JSObject ** objp)1447 void JitObjectPreWriteBarrier(JSRuntime* rt, JSObject** objp) {
1448 AutoUnsafeCallWithABI unsafe;
1449 MOZ_ASSERT(*objp);
1450 MOZ_ASSERT(!(*objp)->isMarkedBlack());
1451 gc::PreWriteBarrier(*objp);
1452 }
1453
JitShapePreWriteBarrier(JSRuntime * rt,Shape ** shapep)1454 void JitShapePreWriteBarrier(JSRuntime* rt, Shape** shapep) {
1455 AutoUnsafeCallWithABI unsafe;
1456 MOZ_ASSERT(!(*shapep)->isMarkedBlack());
1457 gc::PreWriteBarrier(*shapep);
1458 }
1459
ThrowRuntimeLexicalError(JSContext * cx,unsigned errorNumber)1460 bool ThrowRuntimeLexicalError(JSContext* cx, unsigned errorNumber) {
1461 ScriptFrameIter iter(cx);
1462 RootedScript script(cx, iter.script());
1463 ReportRuntimeLexicalError(cx, errorNumber, script, iter.pc());
1464 return false;
1465 }
1466
ThrowBadDerivedReturnOrUninitializedThis(JSContext * cx,HandleValue v)1467 bool ThrowBadDerivedReturnOrUninitializedThis(JSContext* cx, HandleValue v) {
1468 MOZ_ASSERT(!v.isObject());
1469 if (v.isUndefined()) {
1470 return js::ThrowUninitializedThis(cx);
1471 }
1472
1473 ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, v,
1474 nullptr);
1475 return false;
1476 }
1477
BaselineGetFunctionThis(JSContext * cx,BaselineFrame * frame,MutableHandleValue res)1478 bool BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame,
1479 MutableHandleValue res) {
1480 return GetFunctionThis(cx, frame, res);
1481 }
1482
CallNativeGetter(JSContext * cx,HandleFunction callee,HandleValue receiver,MutableHandleValue result)1483 bool CallNativeGetter(JSContext* cx, HandleFunction callee,
1484 HandleValue receiver, MutableHandleValue result) {
1485 AutoRealm ar(cx, callee);
1486
1487 MOZ_ASSERT(callee->isNativeFun());
1488 JSNative natfun = callee->native();
1489
1490 JS::RootedValueArray<2> vp(cx);
1491 vp[0].setObject(*callee.get());
1492 vp[1].set(receiver);
1493
1494 if (!natfun(cx, 0, vp.begin())) {
1495 return false;
1496 }
1497
1498 result.set(vp[0]);
1499 return true;
1500 }
1501
CallDOMGetter(JSContext * cx,const JSJitInfo * info,HandleObject obj,MutableHandleValue result)1502 bool CallDOMGetter(JSContext* cx, const JSJitInfo* info, HandleObject obj,
1503 MutableHandleValue result) {
1504 MOZ_ASSERT(info->type() == JSJitInfo::Getter);
1505 MOZ_ASSERT(obj->is<NativeObject>());
1506 MOZ_ASSERT(obj->getClass()->isDOMClass());
1507
1508 #ifdef DEBUG
1509 DOMInstanceClassHasProtoAtDepth instanceChecker =
1510 cx->runtime()->DOMcallbacks->instanceClassMatchesProto;
1511 MOZ_ASSERT(instanceChecker(obj->getClass(), info->protoID, info->depth));
1512 #endif
1513
1514 // Loading DOM_OBJECT_SLOT, which must be the first slot.
1515 JS::Value val = JS::GetReservedSlot(obj, 0);
1516 JSJitGetterOp getter = info->getter;
1517 return getter(cx, obj, val.toPrivate(), JSJitGetterCallArgs(result));
1518 }
1519
CallNativeSetter(JSContext * cx,HandleFunction callee,HandleObject obj,HandleValue rhs)1520 bool CallNativeSetter(JSContext* cx, HandleFunction callee, HandleObject obj,
1521 HandleValue rhs) {
1522 AutoRealm ar(cx, callee);
1523
1524 MOZ_ASSERT(callee->isNativeFun());
1525 JSNative natfun = callee->native();
1526
1527 JS::RootedValueArray<3> vp(cx);
1528 vp[0].setObject(*callee.get());
1529 vp[1].setObject(*obj.get());
1530 vp[2].set(rhs);
1531
1532 return natfun(cx, 1, vp.begin());
1533 }
1534
CallDOMSetter(JSContext * cx,const JSJitInfo * info,HandleObject obj,HandleValue value)1535 bool CallDOMSetter(JSContext* cx, const JSJitInfo* info, HandleObject obj,
1536 HandleValue value) {
1537 MOZ_ASSERT(info->type() == JSJitInfo::Setter);
1538 MOZ_ASSERT(obj->is<NativeObject>());
1539 MOZ_ASSERT(obj->getClass()->isDOMClass());
1540
1541 #ifdef DEBUG
1542 DOMInstanceClassHasProtoAtDepth instanceChecker =
1543 cx->runtime()->DOMcallbacks->instanceClassMatchesProto;
1544 MOZ_ASSERT(instanceChecker(obj->getClass(), info->protoID, info->depth));
1545 #endif
1546
1547 // Loading DOM_OBJECT_SLOT, which must be the first slot.
1548 JS::Value val = JS::GetReservedSlot(obj, 0);
1549 JSJitSetterOp setter = info->setter;
1550
1551 RootedValue v(cx, value);
1552 return setter(cx, obj, val.toPrivate(), JSJitSetterCallArgs(&v));
1553 }
1554
EqualStringsHelperPure(JSString * str1,JSString * str2)1555 bool EqualStringsHelperPure(JSString* str1, JSString* str2) {
1556 // IC code calls this directly so we shouldn't GC.
1557 AutoUnsafeCallWithABI unsafe;
1558
1559 MOZ_ASSERT(str1->isAtom());
1560 MOZ_ASSERT(!str2->isAtom());
1561 MOZ_ASSERT(str1->length() == str2->length());
1562
1563 // ensureLinear is intentionally called with a nullptr to avoid OOM
1564 // reporting; if it fails, we will continue to the next stub.
1565 JSLinearString* str2Linear = str2->ensureLinear(nullptr);
1566 if (!str2Linear) {
1567 return false;
1568 }
1569
1570 return EqualChars(&str1->asLinear(), str2Linear);
1571 }
1572
MaybeTypedArrayIndexString(jsid id)1573 static bool MaybeTypedArrayIndexString(jsid id) {
1574 MOZ_ASSERT(id.isAtom() || id.isSymbol());
1575
1576 if (MOZ_LIKELY(id.isAtom())) {
1577 JSAtom* str = id.toAtom();
1578 if (str->length() > 0) {
1579 // Only check the first character because we want this function to be
1580 // fast.
1581 return CanStartTypedArrayIndex(str->latin1OrTwoByteChar(0));
1582 }
1583 }
1584 return false;
1585 }
1586
VerifyCacheEntry(JSContext * cx,NativeObject * obj,PropertyKey key,const MegamorphicCache::Entry & entry)1587 static void VerifyCacheEntry(JSContext* cx, NativeObject* obj, PropertyKey key,
1588 const MegamorphicCache::Entry& entry) {
1589 #ifdef DEBUG
1590 if (entry.isMissingProperty()) {
1591 NativeObject* pobj;
1592 PropertyResult prop;
1593 MOZ_ASSERT(LookupPropertyPure(cx, obj, key, &pobj, &prop));
1594 MOZ_ASSERT(prop.isNotFound());
1595 return;
1596 }
1597 if (entry.isMissingOwnProperty()) {
1598 MOZ_ASSERT(!obj->containsPure(key));
1599 return;
1600 }
1601 MOZ_ASSERT(entry.isDataProperty());
1602 for (size_t i = 0, numHops = entry.numHops(); i < numHops; i++) {
1603 MOZ_ASSERT(!obj->containsPure(key));
1604 obj = &obj->staticPrototype()->as<NativeObject>();
1605 }
1606 mozilla::Maybe<PropertyInfo> prop = obj->lookupPure(key);
1607 MOZ_ASSERT(prop.isSome());
1608 MOZ_ASSERT(prop->isDataProperty());
1609 MOZ_ASSERT(prop->slot() == entry.slot());
1610 #endif
1611 }
1612
GetNativeDataPropertyPure(JSContext * cx,NativeObject * obj,jsid id,Value * vp)1613 static MOZ_ALWAYS_INLINE bool GetNativeDataPropertyPure(JSContext* cx,
1614 NativeObject* obj,
1615 jsid id, Value* vp) {
1616 // Fast path used by megamorphic IC stubs. Unlike our other property
1617 // lookup paths, this is optimized to be as fast as possible for simple
1618 // data property lookups.
1619
1620 AutoUnsafeCallWithABI unsafe;
1621
1622 MOZ_ASSERT(id.isAtom() || id.isSymbol());
1623
1624 Shape* receiverShape = obj->shape();
1625 MegamorphicCache& cache = cx->caches().megamorphicCache;
1626 MegamorphicCache::Entry* entry;
1627 if (JitOptions.enableWatchtowerMegamorphic &&
1628 cache.lookup(receiverShape, id, &entry)) {
1629 VerifyCacheEntry(cx, obj, id, *entry);
1630 if (entry->isDataProperty()) {
1631 for (size_t i = 0, numHops = entry->numHops(); i < numHops; i++) {
1632 obj = &obj->staticPrototype()->as<NativeObject>();
1633 }
1634 *vp = obj->getSlot(entry->slot());
1635 return true;
1636 }
1637 if (entry->isMissingProperty()) {
1638 vp->setUndefined();
1639 return true;
1640 }
1641 MOZ_ASSERT(entry->isMissingOwnProperty());
1642 }
1643
1644 size_t numHops = 0;
1645 while (true) {
1646 uint32_t index;
1647 if (PropMap* map = obj->shape()->lookup(cx, id, &index)) {
1648 PropertyInfo prop = map->getPropertyInfo(index);
1649 if (!prop.isDataProperty()) {
1650 return false;
1651 }
1652 if (JitOptions.enableWatchtowerMegamorphic) {
1653 cache.initEntryForDataProperty(entry, receiverShape, id, numHops,
1654 prop.slot());
1655 }
1656 *vp = obj->getSlot(prop.slot());
1657 return true;
1658 }
1659
1660 // Property not found. Watch out for Class hooks and TypedArrays.
1661 if (MOZ_UNLIKELY(!obj->is<PlainObject>())) {
1662 if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj)) {
1663 return false;
1664 }
1665
1666 // Don't skip past TypedArrayObjects if the id can be a TypedArray index.
1667 if (obj->is<TypedArrayObject>()) {
1668 if (MaybeTypedArrayIndexString(id)) {
1669 return false;
1670 }
1671 }
1672 }
1673
1674 JSObject* proto = obj->staticPrototype();
1675 if (!proto) {
1676 if (JitOptions.enableWatchtowerMegamorphic) {
1677 cache.initEntryForMissingProperty(entry, receiverShape, id);
1678 }
1679 vp->setUndefined();
1680 return true;
1681 }
1682
1683 if (!proto->is<NativeObject>()) {
1684 return false;
1685 }
1686 obj = &proto->as<NativeObject>();
1687 numHops++;
1688 }
1689 }
1690
GetNativeDataPropertyPure(JSContext * cx,JSObject * obj,PropertyName * name,Value * vp)1691 bool GetNativeDataPropertyPure(JSContext* cx, JSObject* obj, PropertyName* name,
1692 Value* vp) {
1693 // Condition checked by caller.
1694 MOZ_ASSERT(obj->is<NativeObject>());
1695 return GetNativeDataPropertyPure(cx, &obj->as<NativeObject>(), NameToId(name),
1696 vp);
1697 }
1698
ValueToAtomOrSymbolPure(JSContext * cx,Value & idVal,jsid * id)1699 static MOZ_ALWAYS_INLINE bool ValueToAtomOrSymbolPure(JSContext* cx,
1700 Value& idVal, jsid* id) {
1701 if (MOZ_LIKELY(idVal.isString())) {
1702 JSString* s = idVal.toString();
1703 JSAtom* atom;
1704 if (s->isAtom()) {
1705 atom = &s->asAtom();
1706 } else {
1707 atom = AtomizeString(cx, s);
1708 if (!atom) {
1709 cx->recoverFromOutOfMemory();
1710 return false;
1711 }
1712 }
1713 *id = AtomToId(atom);
1714 } else if (idVal.isSymbol()) {
1715 *id = PropertyKey::Symbol(idVal.toSymbol());
1716 } else {
1717 if (!ValueToIdPure(idVal, id)) {
1718 return false;
1719 }
1720 }
1721
1722 // Watch out for ids that may be stored in dense elements.
1723 static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT < PropertyKey::IntMax,
1724 "All dense elements must have integer jsids");
1725 if (MOZ_UNLIKELY(id->isInt())) {
1726 return false;
1727 }
1728
1729 return true;
1730 }
1731
GetNativeDataPropertyByValuePure(JSContext * cx,JSObject * obj,Value * vp)1732 bool GetNativeDataPropertyByValuePure(JSContext* cx, JSObject* obj, Value* vp) {
1733 AutoUnsafeCallWithABI unsafe;
1734
1735 // Condition checked by caller.
1736 MOZ_ASSERT(obj->is<NativeObject>());
1737
1738 // vp[0] contains the id, result will be stored in vp[1].
1739 Value idVal = vp[0];
1740 jsid id;
1741 if (!ValueToAtomOrSymbolPure(cx, idVal, &id)) {
1742 return false;
1743 }
1744
1745 Value* res = vp + 1;
1746 return GetNativeDataPropertyPure(cx, &obj->as<NativeObject>(), id, res);
1747 }
1748
SetNativeDataPropertyPure(JSContext * cx,JSObject * obj,PropertyName * name,Value * val)1749 bool SetNativeDataPropertyPure(JSContext* cx, JSObject* obj, PropertyName* name,
1750 Value* val) {
1751 AutoUnsafeCallWithABI unsafe;
1752
1753 if (MOZ_UNLIKELY(!obj->is<NativeObject>())) {
1754 return false;
1755 }
1756
1757 NativeObject* nobj = &obj->as<NativeObject>();
1758 uint32_t index;
1759 PropMap* map = nobj->shape()->lookup(cx, NameToId(name), &index);
1760 if (!map) {
1761 return false;
1762 }
1763
1764 PropertyInfo prop = map->getPropertyInfo(index);
1765 if (!prop.isDataProperty() || !prop.writable()) {
1766 return false;
1767 }
1768
1769 nobj->setSlot(prop.slot(), *val);
1770 return true;
1771 }
1772
ObjectHasGetterSetterPure(JSContext * cx,JSObject * objArg,jsid id,GetterSetter * getterSetter)1773 bool ObjectHasGetterSetterPure(JSContext* cx, JSObject* objArg, jsid id,
1774 GetterSetter* getterSetter) {
1775 AutoUnsafeCallWithABI unsafe;
1776
1777 // Window objects may require outerizing (passing the WindowProxy to the
1778 // getter/setter), so we don't support them here.
1779 if (MOZ_UNLIKELY(!objArg->is<NativeObject>() || IsWindow(objArg))) {
1780 return false;
1781 }
1782
1783 NativeObject* nobj = &objArg->as<NativeObject>();
1784
1785 while (true) {
1786 uint32_t index;
1787 if (PropMap* map = nobj->shape()->lookup(cx, id, &index)) {
1788 PropertyInfo prop = map->getPropertyInfo(index);
1789 if (!prop.isAccessorProperty()) {
1790 return false;
1791 }
1792 GetterSetter* actualGetterSetter = nobj->getGetterSetter(prop);
1793 if (actualGetterSetter == getterSetter) {
1794 return true;
1795 }
1796 return (actualGetterSetter->getter() == getterSetter->getter() &&
1797 actualGetterSetter->setter() == getterSetter->setter());
1798 }
1799
1800 // Property not found. Watch out for Class hooks.
1801 if (!nobj->is<PlainObject>()) {
1802 if (ClassMayResolveId(cx->names(), nobj->getClass(), id, nobj)) {
1803 return false;
1804 }
1805 }
1806
1807 JSObject* proto = nobj->staticPrototype();
1808 if (!proto) {
1809 return false;
1810 }
1811
1812 if (!proto->is<NativeObject>()) {
1813 return false;
1814 }
1815 nobj = &proto->as<NativeObject>();
1816 }
1817 }
1818
1819 template <bool HasOwn>
HasNativeDataPropertyPure(JSContext * cx,JSObject * obj,Value * vp)1820 bool HasNativeDataPropertyPure(JSContext* cx, JSObject* obj, Value* vp) {
1821 AutoUnsafeCallWithABI unsafe;
1822
1823 // vp[0] contains the id, result will be stored in vp[1].
1824 Value idVal = vp[0];
1825 jsid id;
1826 if (!ValueToAtomOrSymbolPure(cx, idVal, &id)) {
1827 return false;
1828 }
1829
1830 Shape* receiverShape = obj->shape();
1831 MegamorphicCache& cache = cx->caches().megamorphicCache;
1832 MegamorphicCache::Entry* entry;
1833 if (JitOptions.enableWatchtowerMegamorphic &&
1834 cache.lookup(receiverShape, id, &entry)) {
1835 VerifyCacheEntry(cx, &obj->as<NativeObject>(), id, *entry);
1836 if (entry->isDataProperty()) {
1837 vp[1].setBoolean(HasOwn ? entry->numHops() == 0 : true);
1838 return true;
1839 }
1840 if (HasOwn || entry->isMissingProperty()) {
1841 vp[1].setBoolean(false);
1842 return true;
1843 }
1844 MOZ_ASSERT(!HasOwn);
1845 MOZ_ASSERT(entry->isMissingOwnProperty());
1846 }
1847
1848 size_t numHops = 0;
1849 do {
1850 if (MOZ_UNLIKELY(!obj->is<NativeObject>())) {
1851 return false;
1852 }
1853
1854 uint32_t index;
1855 if (PropMap* map = obj->shape()->lookup(cx, id, &index)) {
1856 if (JitOptions.enableWatchtowerMegamorphic) {
1857 PropertyInfo prop = map->getPropertyInfo(index);
1858 if (prop.isDataProperty()) {
1859 cache.initEntryForDataProperty(entry, receiverShape, id, numHops,
1860 prop.slot());
1861 }
1862 }
1863 vp[1].setBoolean(true);
1864 return true;
1865 }
1866
1867 // Property not found. Watch out for Class hooks and TypedArrays.
1868 if (MOZ_UNLIKELY(!obj->is<PlainObject>())) {
1869 // Fail if there's a resolve hook, unless the mayResolve hook tells us
1870 // the resolve hook won't define a property with this id.
1871 if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj)) {
1872 return false;
1873 }
1874
1875 // Don't skip past TypedArrayObjects if the id can be a TypedArray
1876 // index.
1877 if (obj->is<TypedArrayObject>()) {
1878 if (MaybeTypedArrayIndexString(id)) {
1879 return false;
1880 }
1881 }
1882 }
1883
1884 // If implementing Object.hasOwnProperty, don't follow protochain.
1885 if constexpr (HasOwn) {
1886 break;
1887 }
1888
1889 // Get prototype. Objects that may allow dynamic prototypes are already
1890 // filtered out above.
1891 obj = obj->staticPrototype();
1892 numHops++;
1893 } while (obj);
1894
1895 // Missing property.
1896 if (JitOptions.enableWatchtowerMegamorphic) {
1897 if constexpr (HasOwn) {
1898 cache.initEntryForMissingOwnProperty(entry, receiverShape, id);
1899 } else {
1900 cache.initEntryForMissingProperty(entry, receiverShape, id);
1901 }
1902 }
1903 vp[1].setBoolean(false);
1904 return true;
1905 }
1906
1907 template bool HasNativeDataPropertyPure<true>(JSContext* cx, JSObject* obj,
1908 Value* vp);
1909
1910 template bool HasNativeDataPropertyPure<false>(JSContext* cx, JSObject* obj,
1911 Value* vp);
1912
HasNativeElementPure(JSContext * cx,NativeObject * obj,int32_t index,Value * vp)1913 bool HasNativeElementPure(JSContext* cx, NativeObject* obj, int32_t index,
1914 Value* vp) {
1915 AutoUnsafeCallWithABI unsafe;
1916
1917 MOZ_ASSERT(obj->is<NativeObject>());
1918 MOZ_ASSERT(!obj->getOpsHasProperty());
1919 MOZ_ASSERT(!obj->getOpsLookupProperty());
1920 MOZ_ASSERT(!obj->getOpsGetOwnPropertyDescriptor());
1921
1922 if (MOZ_UNLIKELY(index < 0)) {
1923 return false;
1924 }
1925
1926 if (obj->containsDenseElement(index)) {
1927 vp[0].setBoolean(true);
1928 return true;
1929 }
1930
1931 jsid id = PropertyKey::Int(index);
1932 uint32_t unused;
1933 if (obj->shape()->lookup(cx, id, &unused)) {
1934 vp[0].setBoolean(true);
1935 return true;
1936 }
1937
1938 // Fail if there's a resolve hook, unless the mayResolve hook tells
1939 // us the resolve hook won't define a property with this id.
1940 if (MOZ_UNLIKELY(ClassMayResolveId(cx->names(), obj->getClass(), id, obj))) {
1941 return false;
1942 }
1943 // TypedArrayObject are also native and contain indexed properties.
1944 if (MOZ_UNLIKELY(obj->is<TypedArrayObject>())) {
1945 size_t length = obj->as<TypedArrayObject>().length();
1946 vp[0].setBoolean(uint32_t(index) < length);
1947 return true;
1948 }
1949
1950 vp[0].setBoolean(false);
1951 return true;
1952 }
1953
HandleCodeCoverageAtPC(BaselineFrame * frame,jsbytecode * pc)1954 void HandleCodeCoverageAtPC(BaselineFrame* frame, jsbytecode* pc) {
1955 AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
1956
1957 MOZ_ASSERT(frame->runningInInterpreter());
1958
1959 JSScript* script = frame->script();
1960 MOZ_ASSERT(pc == script->main() || BytecodeIsJumpTarget(JSOp(*pc)));
1961
1962 if (!script->hasScriptCounts()) {
1963 if (!script->realm()->collectCoverageForDebug()) {
1964 return;
1965 }
1966 JSContext* cx = script->runtimeFromMainThread()->mainContextFromOwnThread();
1967 AutoEnterOOMUnsafeRegion oomUnsafe;
1968 if (!script->initScriptCounts(cx)) {
1969 oomUnsafe.crash("initScriptCounts");
1970 }
1971 }
1972
1973 PCCounts* counts = script->maybeGetPCCounts(pc);
1974 MOZ_ASSERT(counts);
1975 counts->numExec()++;
1976 }
1977
HandleCodeCoverageAtPrologue(BaselineFrame * frame)1978 void HandleCodeCoverageAtPrologue(BaselineFrame* frame) {
1979 AutoUnsafeCallWithABI unsafe;
1980
1981 MOZ_ASSERT(frame->runningInInterpreter());
1982
1983 JSScript* script = frame->script();
1984 jsbytecode* main = script->main();
1985 if (!BytecodeIsJumpTarget(JSOp(*main))) {
1986 HandleCodeCoverageAtPC(frame, main);
1987 }
1988 }
1989
TypeOfNameObject(JSObject * obj,JSRuntime * rt)1990 JSString* TypeOfNameObject(JSObject* obj, JSRuntime* rt) {
1991 AutoUnsafeCallWithABI unsafe;
1992 JSType type = js::TypeOfObject(obj);
1993 return TypeName(type, *rt->commonNames);
1994 }
1995
GetPrototypeOf(JSContext * cx,HandleObject target,MutableHandleValue rval)1996 bool GetPrototypeOf(JSContext* cx, HandleObject target,
1997 MutableHandleValue rval) {
1998 MOZ_ASSERT(target->hasDynamicPrototype());
1999
2000 RootedObject proto(cx);
2001 if (!GetPrototype(cx, target, &proto)) {
2002 return false;
2003 }
2004 rval.setObjectOrNull(proto);
2005 return true;
2006 }
2007
ConvertObjectToStringForConcat(JSContext * cx,HandleValue obj)2008 static JSString* ConvertObjectToStringForConcat(JSContext* cx,
2009 HandleValue obj) {
2010 MOZ_ASSERT(obj.isObject());
2011 RootedValue rootedObj(cx, obj);
2012 if (!ToPrimitive(cx, &rootedObj)) {
2013 return nullptr;
2014 }
2015 return ToString<CanGC>(cx, rootedObj);
2016 }
2017
DoConcatStringObject(JSContext * cx,HandleValue lhs,HandleValue rhs,MutableHandleValue res)2018 bool DoConcatStringObject(JSContext* cx, HandleValue lhs, HandleValue rhs,
2019 MutableHandleValue res) {
2020 JSString* lstr = nullptr;
2021 JSString* rstr = nullptr;
2022
2023 if (lhs.isString()) {
2024 // Convert rhs first.
2025 MOZ_ASSERT(lhs.isString() && rhs.isObject());
2026 rstr = ConvertObjectToStringForConcat(cx, rhs);
2027 if (!rstr) {
2028 return false;
2029 }
2030
2031 // lhs is already string.
2032 lstr = lhs.toString();
2033 } else {
2034 MOZ_ASSERT(rhs.isString() && lhs.isObject());
2035 // Convert lhs first.
2036 lstr = ConvertObjectToStringForConcat(cx, lhs);
2037 if (!lstr) {
2038 return false;
2039 }
2040
2041 // rhs is already string.
2042 rstr = rhs.toString();
2043 }
2044
2045 JSString* str = ConcatStrings<NoGC>(cx, lstr, rstr);
2046 if (!str) {
2047 RootedString nlstr(cx, lstr), nrstr(cx, rstr);
2048 str = ConcatStrings<CanGC>(cx, nlstr, nrstr);
2049 if (!str) {
2050 return false;
2051 }
2052 }
2053
2054 res.setString(str);
2055 return true;
2056 }
2057
IsPossiblyWrappedTypedArray(JSContext * cx,JSObject * obj,bool * result)2058 bool IsPossiblyWrappedTypedArray(JSContext* cx, JSObject* obj, bool* result) {
2059 JSObject* unwrapped = CheckedUnwrapDynamic(obj, cx);
2060 if (!unwrapped) {
2061 ReportAccessDenied(cx);
2062 return false;
2063 }
2064
2065 *result = unwrapped->is<TypedArrayObject>();
2066 return true;
2067 }
2068
2069 // Called from CreateDependentString::generateFallback.
AllocateString(JSContext * cx)2070 void* AllocateString(JSContext* cx) {
2071 AutoUnsafeCallWithABI unsafe;
2072 return js::AllocateString<JSString, NoGC>(cx, js::gc::DefaultHeap);
2073 }
AllocateFatInlineString(JSContext * cx)2074 void* AllocateFatInlineString(JSContext* cx) {
2075 AutoUnsafeCallWithABI unsafe;
2076 return js::AllocateString<JSFatInlineString, NoGC>(cx, js::gc::DefaultHeap);
2077 }
2078
2079 // Called to allocate a BigInt if inline allocation failed.
AllocateBigIntNoGC(JSContext * cx,bool requestMinorGC)2080 void* AllocateBigIntNoGC(JSContext* cx, bool requestMinorGC) {
2081 AutoUnsafeCallWithABI unsafe;
2082
2083 if (requestMinorGC) {
2084 cx->nursery().requestMinorGC(JS::GCReason::OUT_OF_NURSERY);
2085 }
2086
2087 return js::AllocateBigInt<NoGC>(cx, gc::TenuredHeap);
2088 }
2089
AllocateAndInitTypedArrayBuffer(JSContext * cx,TypedArrayObject * obj,int32_t count)2090 void AllocateAndInitTypedArrayBuffer(JSContext* cx, TypedArrayObject* obj,
2091 int32_t count) {
2092 AutoUnsafeCallWithABI unsafe;
2093
2094 // Initialize the data slot to UndefinedValue to signal to our JIT caller that
2095 // the allocation failed if the slot isn't overwritten below.
2096 obj->initFixedSlot(TypedArrayObject::DATA_SLOT, UndefinedValue());
2097
2098 // Negative numbers or zero will bail out to the slow path, which in turn will
2099 // raise an invalid argument exception or create a correct object with zero
2100 // elements.
2101 const size_t maxByteLength = TypedArrayObject::maxByteLength();
2102 if (count <= 0 || size_t(count) > maxByteLength / obj->bytesPerElement()) {
2103 obj->setFixedSlot(TypedArrayObject::LENGTH_SLOT, PrivateValue(size_t(0)));
2104 return;
2105 }
2106
2107 obj->setFixedSlot(TypedArrayObject::LENGTH_SLOT, PrivateValue(count));
2108
2109 size_t nbytes = size_t(count) * obj->bytesPerElement();
2110 MOZ_ASSERT(nbytes <= maxByteLength);
2111 nbytes = RoundUp(nbytes, sizeof(Value));
2112
2113 void* buf = cx->nursery().allocateZeroedBuffer(obj, nbytes,
2114 js::ArrayBufferContentsArena);
2115 if (buf) {
2116 InitReservedSlot(obj, TypedArrayObject::DATA_SLOT, buf, nbytes,
2117 MemoryUse::TypedArrayElements);
2118 }
2119 }
2120
CreateMatchResultFallbackFunc(JSContext * cx,gc::AllocKind kind,size_t nDynamicSlots)2121 void* CreateMatchResultFallbackFunc(JSContext* cx, gc::AllocKind kind,
2122 size_t nDynamicSlots) {
2123 AutoUnsafeCallWithABI unsafe;
2124 return js::AllocateObject<NoGC>(cx, kind, nDynamicSlots, gc::DefaultHeap,
2125 &ArrayObject::class_);
2126 }
2127
2128 #ifdef JS_GC_PROBES
TraceCreateObject(JSObject * obj)2129 void TraceCreateObject(JSObject* obj) {
2130 AutoUnsafeCallWithABI unsafe;
2131 js::gc::gcprobes::CreateObject(obj);
2132 }
2133 #endif
2134
2135 #if JS_BITS_PER_WORD == 32
CreateBigIntFromInt64(JSContext * cx,uint32_t low,uint32_t high)2136 BigInt* CreateBigIntFromInt64(JSContext* cx, uint32_t low, uint32_t high) {
2137 uint64_t n = (static_cast<uint64_t>(high) << 32) + low;
2138 return js::BigInt::createFromInt64(cx, n);
2139 }
2140
CreateBigIntFromUint64(JSContext * cx,uint32_t low,uint32_t high)2141 BigInt* CreateBigIntFromUint64(JSContext* cx, uint32_t low, uint32_t high) {
2142 uint64_t n = (static_cast<uint64_t>(high) << 32) + low;
2143 return js::BigInt::createFromUint64(cx, n);
2144 }
2145 #else
CreateBigIntFromInt64(JSContext * cx,uint64_t i64)2146 BigInt* CreateBigIntFromInt64(JSContext* cx, uint64_t i64) {
2147 return js::BigInt::createFromInt64(cx, i64);
2148 }
2149
CreateBigIntFromUint64(JSContext * cx,uint64_t i64)2150 BigInt* CreateBigIntFromUint64(JSContext* cx, uint64_t i64) {
2151 return js::BigInt::createFromUint64(cx, i64);
2152 }
2153 #endif
2154
DoStringToInt64(JSContext * cx,HandleString str,uint64_t * res)2155 bool DoStringToInt64(JSContext* cx, HandleString str, uint64_t* res) {
2156 BigInt* bi;
2157 JS_TRY_VAR_OR_RETURN_FALSE(cx, bi, js::StringToBigInt(cx, str));
2158
2159 if (!bi) {
2160 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2161 JSMSG_BIGINT_INVALID_SYNTAX);
2162 return false;
2163 }
2164
2165 *res = js::BigInt::toUint64(bi);
2166 return true;
2167 }
2168
2169 template <EqualityKind Kind>
BigIntEqual(BigInt * x,BigInt * y)2170 bool BigIntEqual(BigInt* x, BigInt* y) {
2171 AutoUnsafeCallWithABI unsafe;
2172 bool res = BigInt::equal(x, y);
2173 if (Kind != EqualityKind::Equal) {
2174 res = !res;
2175 }
2176 return res;
2177 }
2178
2179 template bool BigIntEqual<EqualityKind::Equal>(BigInt* x, BigInt* y);
2180 template bool BigIntEqual<EqualityKind::NotEqual>(BigInt* x, BigInt* y);
2181
2182 template <ComparisonKind Kind>
BigIntCompare(BigInt * x,BigInt * y)2183 bool BigIntCompare(BigInt* x, BigInt* y) {
2184 AutoUnsafeCallWithABI unsafe;
2185 bool res = BigInt::lessThan(x, y);
2186 if (Kind != ComparisonKind::LessThan) {
2187 res = !res;
2188 }
2189 return res;
2190 }
2191
2192 template bool BigIntCompare<ComparisonKind::LessThan>(BigInt* x, BigInt* y);
2193 template bool BigIntCompare<ComparisonKind::GreaterThanOrEqual>(BigInt* x,
2194 BigInt* y);
2195
2196 template <EqualityKind Kind>
BigIntNumberEqual(BigInt * x,double y)2197 bool BigIntNumberEqual(BigInt* x, double y) {
2198 AutoUnsafeCallWithABI unsafe;
2199 bool res = BigInt::equal(x, y);
2200 if (Kind != EqualityKind::Equal) {
2201 res = !res;
2202 }
2203 return res;
2204 }
2205
2206 template bool BigIntNumberEqual<EqualityKind::Equal>(BigInt* x, double y);
2207 template bool BigIntNumberEqual<EqualityKind::NotEqual>(BigInt* x, double y);
2208
2209 template <ComparisonKind Kind>
BigIntNumberCompare(BigInt * x,double y)2210 bool BigIntNumberCompare(BigInt* x, double y) {
2211 AutoUnsafeCallWithABI unsafe;
2212 mozilla::Maybe<bool> res = BigInt::lessThan(x, y);
2213 if (Kind == ComparisonKind::LessThan) {
2214 return res.valueOr(false);
2215 }
2216 return !res.valueOr(true);
2217 }
2218
2219 template bool BigIntNumberCompare<ComparisonKind::LessThan>(BigInt* x,
2220 double y);
2221 template bool BigIntNumberCompare<ComparisonKind::GreaterThanOrEqual>(BigInt* x,
2222 double y);
2223
2224 template <ComparisonKind Kind>
NumberBigIntCompare(double x,BigInt * y)2225 bool NumberBigIntCompare(double x, BigInt* y) {
2226 AutoUnsafeCallWithABI unsafe;
2227 mozilla::Maybe<bool> res = BigInt::lessThan(x, y);
2228 if (Kind == ComparisonKind::LessThan) {
2229 return res.valueOr(false);
2230 }
2231 return !res.valueOr(true);
2232 }
2233
2234 template bool NumberBigIntCompare<ComparisonKind::LessThan>(double x,
2235 BigInt* y);
2236 template bool NumberBigIntCompare<ComparisonKind::GreaterThanOrEqual>(
2237 double x, BigInt* y);
2238
2239 template <EqualityKind Kind>
BigIntStringEqual(JSContext * cx,HandleBigInt x,HandleString y,bool * res)2240 bool BigIntStringEqual(JSContext* cx, HandleBigInt x, HandleString y,
2241 bool* res) {
2242 JS_TRY_VAR_OR_RETURN_FALSE(cx, *res, BigInt::equal(cx, x, y));
2243 if (Kind != EqualityKind::Equal) {
2244 *res = !*res;
2245 }
2246 return true;
2247 }
2248
2249 template bool BigIntStringEqual<EqualityKind::Equal>(JSContext* cx,
2250 HandleBigInt x,
2251 HandleString y, bool* res);
2252 template bool BigIntStringEqual<EqualityKind::NotEqual>(JSContext* cx,
2253 HandleBigInt x,
2254 HandleString y,
2255 bool* res);
2256
2257 template <ComparisonKind Kind>
BigIntStringCompare(JSContext * cx,HandleBigInt x,HandleString y,bool * res)2258 bool BigIntStringCompare(JSContext* cx, HandleBigInt x, HandleString y,
2259 bool* res) {
2260 mozilla::Maybe<bool> result;
2261 if (!BigInt::lessThan(cx, x, y, result)) {
2262 return false;
2263 }
2264 if (Kind == ComparisonKind::LessThan) {
2265 *res = result.valueOr(false);
2266 } else {
2267 *res = !result.valueOr(true);
2268 }
2269 return true;
2270 }
2271
2272 template bool BigIntStringCompare<ComparisonKind::LessThan>(JSContext* cx,
2273 HandleBigInt x,
2274 HandleString y,
2275 bool* res);
2276 template bool BigIntStringCompare<ComparisonKind::GreaterThanOrEqual>(
2277 JSContext* cx, HandleBigInt x, HandleString y, bool* res);
2278
2279 template <ComparisonKind Kind>
StringBigIntCompare(JSContext * cx,HandleString x,HandleBigInt y,bool * res)2280 bool StringBigIntCompare(JSContext* cx, HandleString x, HandleBigInt y,
2281 bool* res) {
2282 mozilla::Maybe<bool> result;
2283 if (!BigInt::lessThan(cx, x, y, result)) {
2284 return false;
2285 }
2286 if (Kind == ComparisonKind::LessThan) {
2287 *res = result.valueOr(false);
2288 } else {
2289 *res = !result.valueOr(true);
2290 }
2291 return true;
2292 }
2293
2294 template bool StringBigIntCompare<ComparisonKind::LessThan>(JSContext* cx,
2295 HandleString x,
2296 HandleBigInt y,
2297 bool* res);
2298 template bool StringBigIntCompare<ComparisonKind::GreaterThanOrEqual>(
2299 JSContext* cx, HandleString x, HandleBigInt y, bool* res);
2300
BigIntAsIntN(JSContext * cx,HandleBigInt x,int32_t bits)2301 BigInt* BigIntAsIntN(JSContext* cx, HandleBigInt x, int32_t bits) {
2302 MOZ_ASSERT(bits >= 0);
2303 return BigInt::asIntN(cx, x, uint64_t(bits));
2304 }
2305
BigIntAsUintN(JSContext * cx,HandleBigInt x,int32_t bits)2306 BigInt* BigIntAsUintN(JSContext* cx, HandleBigInt x, int32_t bits) {
2307 MOZ_ASSERT(bits >= 0);
2308 return BigInt::asUintN(cx, x, uint64_t(bits));
2309 }
2310
2311 template <typename T>
AtomicsCompareExchange(TypedArrayObject * typedArray,size_t index,int32_t expected,int32_t replacement)2312 static int32_t AtomicsCompareExchange(TypedArrayObject* typedArray,
2313 size_t index, int32_t expected,
2314 int32_t replacement) {
2315 AutoUnsafeCallWithABI unsafe;
2316
2317 MOZ_ASSERT(!typedArray->hasDetachedBuffer());
2318 MOZ_ASSERT(index < typedArray->length());
2319
2320 SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>();
2321 return jit::AtomicOperations::compareExchangeSeqCst(addr + index, T(expected),
2322 T(replacement));
2323 }
2324
AtomicsCompareExchange(Scalar::Type elementType)2325 AtomicsCompareExchangeFn AtomicsCompareExchange(Scalar::Type elementType) {
2326 switch (elementType) {
2327 case Scalar::Int8:
2328 return AtomicsCompareExchange<int8_t>;
2329 case Scalar::Uint8:
2330 return AtomicsCompareExchange<uint8_t>;
2331 case Scalar::Int16:
2332 return AtomicsCompareExchange<int16_t>;
2333 case Scalar::Uint16:
2334 return AtomicsCompareExchange<uint16_t>;
2335 case Scalar::Int32:
2336 return AtomicsCompareExchange<int32_t>;
2337 case Scalar::Uint32:
2338 return AtomicsCompareExchange<uint32_t>;
2339 default:
2340 MOZ_CRASH("Unexpected TypedArray type");
2341 }
2342 }
2343
2344 template <typename T>
AtomicsExchange(TypedArrayObject * typedArray,size_t index,int32_t value)2345 static int32_t AtomicsExchange(TypedArrayObject* typedArray, size_t index,
2346 int32_t value) {
2347 AutoUnsafeCallWithABI unsafe;
2348
2349 MOZ_ASSERT(!typedArray->hasDetachedBuffer());
2350 MOZ_ASSERT(index < typedArray->length());
2351
2352 SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>();
2353 return jit::AtomicOperations::exchangeSeqCst(addr + index, T(value));
2354 }
2355
AtomicsExchange(Scalar::Type elementType)2356 AtomicsReadWriteModifyFn AtomicsExchange(Scalar::Type elementType) {
2357 switch (elementType) {
2358 case Scalar::Int8:
2359 return AtomicsExchange<int8_t>;
2360 case Scalar::Uint8:
2361 return AtomicsExchange<uint8_t>;
2362 case Scalar::Int16:
2363 return AtomicsExchange<int16_t>;
2364 case Scalar::Uint16:
2365 return AtomicsExchange<uint16_t>;
2366 case Scalar::Int32:
2367 return AtomicsExchange<int32_t>;
2368 case Scalar::Uint32:
2369 return AtomicsExchange<uint32_t>;
2370 default:
2371 MOZ_CRASH("Unexpected TypedArray type");
2372 }
2373 }
2374
2375 template <typename T>
AtomicsAdd(TypedArrayObject * typedArray,size_t index,int32_t value)2376 static int32_t AtomicsAdd(TypedArrayObject* typedArray, size_t index,
2377 int32_t value) {
2378 AutoUnsafeCallWithABI unsafe;
2379
2380 MOZ_ASSERT(!typedArray->hasDetachedBuffer());
2381 MOZ_ASSERT(index < typedArray->length());
2382
2383 SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>();
2384 return jit::AtomicOperations::fetchAddSeqCst(addr + index, T(value));
2385 }
2386
AtomicsAdd(Scalar::Type elementType)2387 AtomicsReadWriteModifyFn AtomicsAdd(Scalar::Type elementType) {
2388 switch (elementType) {
2389 case Scalar::Int8:
2390 return AtomicsAdd<int8_t>;
2391 case Scalar::Uint8:
2392 return AtomicsAdd<uint8_t>;
2393 case Scalar::Int16:
2394 return AtomicsAdd<int16_t>;
2395 case Scalar::Uint16:
2396 return AtomicsAdd<uint16_t>;
2397 case Scalar::Int32:
2398 return AtomicsAdd<int32_t>;
2399 case Scalar::Uint32:
2400 return AtomicsAdd<uint32_t>;
2401 default:
2402 MOZ_CRASH("Unexpected TypedArray type");
2403 }
2404 }
2405
2406 template <typename T>
AtomicsSub(TypedArrayObject * typedArray,size_t index,int32_t value)2407 static int32_t AtomicsSub(TypedArrayObject* typedArray, size_t index,
2408 int32_t value) {
2409 AutoUnsafeCallWithABI unsafe;
2410
2411 MOZ_ASSERT(!typedArray->hasDetachedBuffer());
2412 MOZ_ASSERT(index < typedArray->length());
2413
2414 SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>();
2415 return jit::AtomicOperations::fetchSubSeqCst(addr + index, T(value));
2416 }
2417
AtomicsSub(Scalar::Type elementType)2418 AtomicsReadWriteModifyFn AtomicsSub(Scalar::Type elementType) {
2419 switch (elementType) {
2420 case Scalar::Int8:
2421 return AtomicsSub<int8_t>;
2422 case Scalar::Uint8:
2423 return AtomicsSub<uint8_t>;
2424 case Scalar::Int16:
2425 return AtomicsSub<int16_t>;
2426 case Scalar::Uint16:
2427 return AtomicsSub<uint16_t>;
2428 case Scalar::Int32:
2429 return AtomicsSub<int32_t>;
2430 case Scalar::Uint32:
2431 return AtomicsSub<uint32_t>;
2432 default:
2433 MOZ_CRASH("Unexpected TypedArray type");
2434 }
2435 }
2436
2437 template <typename T>
AtomicsAnd(TypedArrayObject * typedArray,size_t index,int32_t value)2438 static int32_t AtomicsAnd(TypedArrayObject* typedArray, size_t index,
2439 int32_t value) {
2440 AutoUnsafeCallWithABI unsafe;
2441
2442 MOZ_ASSERT(!typedArray->hasDetachedBuffer());
2443 MOZ_ASSERT(index < typedArray->length());
2444
2445 SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>();
2446 return jit::AtomicOperations::fetchAndSeqCst(addr + index, T(value));
2447 }
2448
AtomicsAnd(Scalar::Type elementType)2449 AtomicsReadWriteModifyFn AtomicsAnd(Scalar::Type elementType) {
2450 switch (elementType) {
2451 case Scalar::Int8:
2452 return AtomicsAnd<int8_t>;
2453 case Scalar::Uint8:
2454 return AtomicsAnd<uint8_t>;
2455 case Scalar::Int16:
2456 return AtomicsAnd<int16_t>;
2457 case Scalar::Uint16:
2458 return AtomicsAnd<uint16_t>;
2459 case Scalar::Int32:
2460 return AtomicsAnd<int32_t>;
2461 case Scalar::Uint32:
2462 return AtomicsAnd<uint32_t>;
2463 default:
2464 MOZ_CRASH("Unexpected TypedArray type");
2465 }
2466 }
2467
2468 template <typename T>
AtomicsOr(TypedArrayObject * typedArray,size_t index,int32_t value)2469 static int32_t AtomicsOr(TypedArrayObject* typedArray, size_t index,
2470 int32_t value) {
2471 AutoUnsafeCallWithABI unsafe;
2472
2473 MOZ_ASSERT(!typedArray->hasDetachedBuffer());
2474 MOZ_ASSERT(index < typedArray->length());
2475
2476 SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>();
2477 return jit::AtomicOperations::fetchOrSeqCst(addr + index, T(value));
2478 }
2479
AtomicsOr(Scalar::Type elementType)2480 AtomicsReadWriteModifyFn AtomicsOr(Scalar::Type elementType) {
2481 switch (elementType) {
2482 case Scalar::Int8:
2483 return AtomicsOr<int8_t>;
2484 case Scalar::Uint8:
2485 return AtomicsOr<uint8_t>;
2486 case Scalar::Int16:
2487 return AtomicsOr<int16_t>;
2488 case Scalar::Uint16:
2489 return AtomicsOr<uint16_t>;
2490 case Scalar::Int32:
2491 return AtomicsOr<int32_t>;
2492 case Scalar::Uint32:
2493 return AtomicsOr<uint32_t>;
2494 default:
2495 MOZ_CRASH("Unexpected TypedArray type");
2496 }
2497 }
2498
2499 template <typename T>
AtomicsXor(TypedArrayObject * typedArray,size_t index,int32_t value)2500 static int32_t AtomicsXor(TypedArrayObject* typedArray, size_t index,
2501 int32_t value) {
2502 AutoUnsafeCallWithABI unsafe;
2503
2504 MOZ_ASSERT(!typedArray->hasDetachedBuffer());
2505 MOZ_ASSERT(index < typedArray->length());
2506
2507 SharedMem<T*> addr = typedArray->dataPointerEither().cast<T*>();
2508 return jit::AtomicOperations::fetchXorSeqCst(addr + index, T(value));
2509 }
2510
AtomicsXor(Scalar::Type elementType)2511 AtomicsReadWriteModifyFn AtomicsXor(Scalar::Type elementType) {
2512 switch (elementType) {
2513 case Scalar::Int8:
2514 return AtomicsXor<int8_t>;
2515 case Scalar::Uint8:
2516 return AtomicsXor<uint8_t>;
2517 case Scalar::Int16:
2518 return AtomicsXor<int16_t>;
2519 case Scalar::Uint16:
2520 return AtomicsXor<uint16_t>;
2521 case Scalar::Int32:
2522 return AtomicsXor<int32_t>;
2523 case Scalar::Uint32:
2524 return AtomicsXor<uint32_t>;
2525 default:
2526 MOZ_CRASH("Unexpected TypedArray type");
2527 }
2528 }
2529
2530 template <typename AtomicOp, typename... Args>
AtomicAccess64(JSContext * cx,TypedArrayObject * typedArray,size_t index,AtomicOp op,Args...args)2531 static BigInt* AtomicAccess64(JSContext* cx, TypedArrayObject* typedArray,
2532 size_t index, AtomicOp op, Args... args) {
2533 MOZ_ASSERT(Scalar::isBigIntType(typedArray->type()));
2534 MOZ_ASSERT(!typedArray->hasDetachedBuffer());
2535 MOZ_ASSERT(index < typedArray->length());
2536
2537 if (typedArray->type() == Scalar::BigInt64) {
2538 SharedMem<int64_t*> addr = typedArray->dataPointerEither().cast<int64_t*>();
2539 int64_t v = op(addr + index, BigInt::toInt64(args)...);
2540 return BigInt::createFromInt64(cx, v);
2541 }
2542
2543 SharedMem<uint64_t*> addr = typedArray->dataPointerEither().cast<uint64_t*>();
2544 uint64_t v = op(addr + index, BigInt::toUint64(args)...);
2545 return BigInt::createFromUint64(cx, v);
2546 }
2547
2548 template <typename AtomicOp, typename... Args>
AtomicAccess64(TypedArrayObject * typedArray,size_t index,AtomicOp op,Args...args)2549 static auto AtomicAccess64(TypedArrayObject* typedArray, size_t index,
2550 AtomicOp op, Args... args) {
2551 MOZ_ASSERT(Scalar::isBigIntType(typedArray->type()));
2552 MOZ_ASSERT(!typedArray->hasDetachedBuffer());
2553 MOZ_ASSERT(index < typedArray->length());
2554
2555 if (typedArray->type() == Scalar::BigInt64) {
2556 SharedMem<int64_t*> addr = typedArray->dataPointerEither().cast<int64_t*>();
2557 return op(addr + index, BigInt::toInt64(args)...);
2558 }
2559
2560 SharedMem<uint64_t*> addr = typedArray->dataPointerEither().cast<uint64_t*>();
2561 return op(addr + index, BigInt::toUint64(args)...);
2562 }
2563
AtomicsLoad64(JSContext * cx,TypedArrayObject * typedArray,size_t index)2564 BigInt* AtomicsLoad64(JSContext* cx, TypedArrayObject* typedArray,
2565 size_t index) {
2566 return AtomicAccess64(cx, typedArray, index, [](auto addr) {
2567 return jit::AtomicOperations::loadSeqCst(addr);
2568 });
2569 }
2570
AtomicsStore64(TypedArrayObject * typedArray,size_t index,const BigInt * value)2571 void AtomicsStore64(TypedArrayObject* typedArray, size_t index,
2572 const BigInt* value) {
2573 AutoUnsafeCallWithABI unsafe;
2574
2575 AtomicAccess64(
2576 typedArray, index,
2577 [](auto addr, auto val) {
2578 jit::AtomicOperations::storeSeqCst(addr, val);
2579 },
2580 value);
2581 }
2582
AtomicsCompareExchange64(JSContext * cx,TypedArrayObject * typedArray,size_t index,const BigInt * expected,const BigInt * replacement)2583 BigInt* AtomicsCompareExchange64(JSContext* cx, TypedArrayObject* typedArray,
2584 size_t index, const BigInt* expected,
2585 const BigInt* replacement) {
2586 return AtomicAccess64(
2587 cx, typedArray, index,
2588 [](auto addr, auto oldval, auto newval) {
2589 return jit::AtomicOperations::compareExchangeSeqCst(addr, oldval,
2590 newval);
2591 },
2592 expected, replacement);
2593 }
2594
AtomicsExchange64(JSContext * cx,TypedArrayObject * typedArray,size_t index,const BigInt * value)2595 BigInt* AtomicsExchange64(JSContext* cx, TypedArrayObject* typedArray,
2596 size_t index, const BigInt* value) {
2597 return AtomicAccess64(
2598 cx, typedArray, index,
2599 [](auto addr, auto val) {
2600 return jit::AtomicOperations::exchangeSeqCst(addr, val);
2601 },
2602 value);
2603 }
2604
AtomicsAdd64(JSContext * cx,TypedArrayObject * typedArray,size_t index,const BigInt * value)2605 BigInt* AtomicsAdd64(JSContext* cx, TypedArrayObject* typedArray, size_t index,
2606 const BigInt* value) {
2607 return AtomicAccess64(
2608 cx, typedArray, index,
2609 [](auto addr, auto val) {
2610 return jit::AtomicOperations::fetchAddSeqCst(addr, val);
2611 },
2612 value);
2613 }
2614
AtomicsAnd64(JSContext * cx,TypedArrayObject * typedArray,size_t index,const BigInt * value)2615 BigInt* AtomicsAnd64(JSContext* cx, TypedArrayObject* typedArray, size_t index,
2616 const BigInt* value) {
2617 return AtomicAccess64(
2618 cx, typedArray, index,
2619 [](auto addr, auto val) {
2620 return jit::AtomicOperations::fetchAndSeqCst(addr, val);
2621 },
2622 value);
2623 }
2624
AtomicsOr64(JSContext * cx,TypedArrayObject * typedArray,size_t index,const BigInt * value)2625 BigInt* AtomicsOr64(JSContext* cx, TypedArrayObject* typedArray, size_t index,
2626 const BigInt* value) {
2627 return AtomicAccess64(
2628 cx, typedArray, index,
2629 [](auto addr, auto val) {
2630 return jit::AtomicOperations::fetchOrSeqCst(addr, val);
2631 },
2632 value);
2633 }
2634
AtomicsSub64(JSContext * cx,TypedArrayObject * typedArray,size_t index,const BigInt * value)2635 BigInt* AtomicsSub64(JSContext* cx, TypedArrayObject* typedArray, size_t index,
2636 const BigInt* value) {
2637 return AtomicAccess64(
2638 cx, typedArray, index,
2639 [](auto addr, auto val) {
2640 return jit::AtomicOperations::fetchSubSeqCst(addr, val);
2641 },
2642 value);
2643 }
2644
AtomicsXor64(JSContext * cx,TypedArrayObject * typedArray,size_t index,const BigInt * value)2645 BigInt* AtomicsXor64(JSContext* cx, TypedArrayObject* typedArray, size_t index,
2646 const BigInt* value) {
2647 return AtomicAccess64(
2648 cx, typedArray, index,
2649 [](auto addr, auto val) {
2650 return jit::AtomicOperations::fetchXorSeqCst(addr, val);
2651 },
2652 value);
2653 }
2654
AtomizeStringNoGC(JSContext * cx,JSString * str)2655 JSAtom* AtomizeStringNoGC(JSContext* cx, JSString* str) {
2656 // IC code calls this directly so we shouldn't GC.
2657 AutoUnsafeCallWithABI unsafe;
2658
2659 JSAtom* atom = AtomizeString(cx, str);
2660 if (!atom) {
2661 cx->recoverFromOutOfMemory();
2662 return nullptr;
2663 }
2664
2665 return atom;
2666 }
2667
SetObjectHas(JSContext * cx,HandleObject obj,HandleValue key,bool * rval)2668 bool SetObjectHas(JSContext* cx, HandleObject obj, HandleValue key,
2669 bool* rval) {
2670 return SetObject::has(cx, obj, key, rval);
2671 }
2672
MapObjectHas(JSContext * cx,HandleObject obj,HandleValue key,bool * rval)2673 bool MapObjectHas(JSContext* cx, HandleObject obj, HandleValue key,
2674 bool* rval) {
2675 return MapObject::has(cx, obj, key, rval);
2676 }
2677
MapObjectGet(JSContext * cx,HandleObject obj,HandleValue key,MutableHandleValue rval)2678 bool MapObjectGet(JSContext* cx, HandleObject obj, HandleValue key,
2679 MutableHandleValue rval) {
2680 return MapObject::get(cx, obj, key, rval);
2681 }
2682
2683 #ifdef DEBUG
2684 template <class OrderedHashTable>
HashValue(JSContext * cx,OrderedHashTable * hashTable,const Value * value)2685 static mozilla::HashNumber HashValue(JSContext* cx, OrderedHashTable* hashTable,
2686 const Value* value) {
2687 RootedValue rootedValue(cx, *value);
2688 HashableValue hashable;
2689 MOZ_ALWAYS_TRUE(hashable.setValue(cx, rootedValue));
2690
2691 return hashTable->hash(hashable);
2692 }
2693 #endif
2694
AssertSetObjectHash(JSContext * cx,SetObject * obj,const Value * value,mozilla::HashNumber actualHash)2695 void AssertSetObjectHash(JSContext* cx, SetObject* obj, const Value* value,
2696 mozilla::HashNumber actualHash) {
2697 AutoUnsafeCallWithABI unsafe;
2698
2699 MOZ_ASSERT(actualHash == HashValue(cx, obj->getData(), value));
2700 }
2701
AssertMapObjectHash(JSContext * cx,MapObject * obj,const Value * value,mozilla::HashNumber actualHash)2702 void AssertMapObjectHash(JSContext* cx, MapObject* obj, const Value* value,
2703 mozilla::HashNumber actualHash) {
2704 AutoUnsafeCallWithABI unsafe;
2705
2706 MOZ_ASSERT(actualHash == HashValue(cx, obj->getData(), value));
2707 }
2708
AssumeUnreachable(const char * output)2709 void AssumeUnreachable(const char* output) {
2710 MOZ_ReportAssertionFailure(output, __FILE__, __LINE__);
2711 }
2712
Printf0(const char * output)2713 void Printf0(const char* output) {
2714 AutoUnsafeCallWithABI unsafe;
2715
2716 // Use stderr instead of stdout because this is only used for debug
2717 // output. stderr is less likely to interfere with the program's normal
2718 // output, and it's always unbuffered.
2719 fprintf(stderr, "%s", output);
2720 }
2721
Printf1(const char * output,uintptr_t value)2722 void Printf1(const char* output, uintptr_t value) {
2723 AutoUnsafeCallWithABI unsafe;
2724 AutoEnterOOMUnsafeRegion oomUnsafe;
2725 js::UniqueChars line = JS_sprintf_append(nullptr, output, value);
2726 if (!line) {
2727 oomUnsafe.crash("OOM at masm.printf");
2728 }
2729 fprintf(stderr, "%s", line.get());
2730 }
2731
2732 } // namespace jit
2733 } // namespace js
2734