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 "builtin/TestingFunctions.h"
8 
9 #include "mozilla/Atomics.h"
10 #include "mozilla/Casting.h"
11 #include "mozilla/FloatingPoint.h"
12 #include "mozilla/Maybe.h"
13 #include "mozilla/Span.h"
14 #include "mozilla/Sprintf.h"
15 #include "mozilla/TextUtils.h"
16 #include "mozilla/Tuple.h"
17 #include "mozilla/Unused.h"
18 
19 #include <algorithm>
20 #include <cfloat>
21 #include <cmath>
22 #include <cstdlib>
23 #include <ctime>
24 #include <utility>
25 
26 #if defined(XP_UNIX) && !defined(XP_DARWIN)
27 #  include <time.h>
28 #else
29 #  include <chrono>
30 #endif
31 
32 #include "jsapi.h"
33 #include "jsfriendapi.h"
34 
35 #include "builtin/Promise.h"
36 #include "builtin/SelfHostingDefines.h"
37 #ifdef DEBUG
38 #  include "frontend/TokenStream.h"
39 #  ifndef ENABLE_NEW_REGEP
40 #    include "irregexp/RegExpAST.h"
41 #    include "irregexp/RegExpEngine.h"
42 #    include "irregexp/RegExpParser.h"
43 #  endif
44 #endif
45 #include "gc/Allocator.h"
46 #include "gc/Zone.h"
47 #include "jit/BaselineJIT.h"
48 #include "jit/InlinableNatives.h"
49 #include "jit/Ion.h"
50 #include "jit/JitRealm.h"
51 #include "js/Array.h"        // JS::NewArrayObject
52 #include "js/ArrayBuffer.h"  // JS::{DetachArrayBuffer,GetArrayBufferLengthAndData,NewArrayBufferWithContents}
53 #include "js/CharacterEncoding.h"
54 #include "js/CompilationAndEvaluation.h"
55 #include "js/CompileOptions.h"
56 #include "js/Date.h"
57 #include "js/Debug.h"
58 #include "js/HashTable.h"
59 #include "js/LocaleSensitive.h"
60 #include "js/PropertySpec.h"
61 #include "js/RegExpFlags.h"  // JS::RegExpFlag, JS::RegExpFlags
62 #include "js/SourceText.h"
63 #include "js/StableStringChars.h"
64 #include "js/StructuredClone.h"
65 #include "js/UbiNode.h"
66 #include "js/UbiNodeBreadthFirst.h"
67 #include "js/UbiNodeShortestPaths.h"
68 #include "js/UniquePtr.h"
69 #include "js/Vector.h"
70 #include "js/Wrapper.h"
71 #include "threading/CpuCount.h"
72 #include "util/StringBuffer.h"
73 #include "util/Text.h"
74 #include "vm/AsyncFunction.h"
75 #include "vm/AsyncIteration.h"
76 #include "vm/ErrorObject.h"
77 #include "vm/GlobalObject.h"
78 #include "vm/Interpreter.h"
79 #include "vm/Iteration.h"
80 #include "vm/JSContext.h"
81 #include "vm/JSObject.h"
82 #include "vm/PlainObject.h"    // js::PlainObject
83 #include "vm/PromiseObject.h"  // js::PromiseObject, js::PromiseSlot_*
84 #include "vm/ProxyObject.h"
85 #include "vm/SavedStacks.h"
86 #include "vm/Stack.h"
87 #include "vm/StringType.h"
88 #include "vm/TraceLogging.h"
89 #include "wasm/AsmJS.h"
90 #include "wasm/WasmBaselineCompile.h"
91 #include "wasm/WasmCraneliftCompile.h"
92 #include "wasm/WasmInstance.h"
93 #include "wasm/WasmIonCompile.h"
94 #include "wasm/WasmJS.h"
95 #include "wasm/WasmModule.h"
96 #include "wasm/WasmSignalHandlers.h"
97 #include "wasm/WasmTypes.h"
98 
99 #include "debugger/DebugAPI-inl.h"
100 #include "vm/Compartment-inl.h"
101 #include "vm/EnvironmentObject-inl.h"
102 #include "vm/JSContext-inl.h"
103 #include "vm/JSObject-inl.h"
104 #include "vm/NativeObject-inl.h"
105 #include "vm/StringType-inl.h"
106 
107 using namespace js;
108 
109 using mozilla::ArrayLength;
110 using mozilla::AssertedCast;
111 using mozilla::AsWritableChars;
112 using mozilla::MakeSpan;
113 using mozilla::Maybe;
114 using mozilla::Tie;
115 using mozilla::Tuple;
116 
117 using JS::AutoStableStringChars;
118 using JS::CompileOptions;
119 using JS::RegExpFlag;
120 using JS::RegExpFlags;
121 using JS::SourceOwnership;
122 using JS::SourceText;
123 
124 // If fuzzingSafe is set, remove functionality that could cause problems with
125 // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
126 mozilla::Atomic<bool> fuzzingSafe(false);
127 
128 // If disableOOMFunctions is set, disable functionality that causes artificial
129 // OOM conditions.
130 static mozilla::Atomic<bool> disableOOMFunctions(false);
131 
EnvVarIsDefined(const char * name)132 static bool EnvVarIsDefined(const char* name) {
133   const char* value = getenv(name);
134   return value && *value;
135 }
136 
137 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
EnvVarAsInt(const char * name,int * valueOut)138 static bool EnvVarAsInt(const char* name, int* valueOut) {
139   if (!EnvVarIsDefined(name)) {
140     return false;
141   }
142 
143   *valueOut = atoi(getenv(name));
144   return true;
145 }
146 #endif
147 
GetBuildConfiguration(JSContext * cx,unsigned argc,Value * vp)148 static bool GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) {
149   CallArgs args = CallArgsFromVp(argc, vp);
150   RootedObject info(cx, JS_NewPlainObject(cx));
151   if (!info) {
152     return false;
153   }
154 
155   if (!JS_SetProperty(cx, info, "rooting-analysis", FalseHandleValue)) {
156     return false;
157   }
158 
159   if (!JS_SetProperty(cx, info, "exact-rooting", TrueHandleValue)) {
160     return false;
161   }
162 
163   if (!JS_SetProperty(cx, info, "trace-jscalls-api", FalseHandleValue)) {
164     return false;
165   }
166 
167   if (!JS_SetProperty(cx, info, "incremental-gc", TrueHandleValue)) {
168     return false;
169   }
170 
171   if (!JS_SetProperty(cx, info, "generational-gc", TrueHandleValue)) {
172     return false;
173   }
174 
175   if (!JS_SetProperty(cx, info, "oom-backtraces", FalseHandleValue)) {
176     return false;
177   }
178 
179   RootedValue value(cx);
180 #ifdef DEBUG
181   value = BooleanValue(true);
182 #else
183   value = BooleanValue(false);
184 #endif
185   if (!JS_SetProperty(cx, info, "debug", value)) {
186     return false;
187   }
188 
189 #ifdef RELEASE_OR_BETA
190   value = BooleanValue(true);
191 #else
192   value = BooleanValue(false);
193 #endif
194   if (!JS_SetProperty(cx, info, "release_or_beta", value)) {
195     return false;
196   }
197 
198 #ifdef MOZ_CODE_COVERAGE
199   value = BooleanValue(true);
200 #else
201   value = BooleanValue(false);
202 #endif
203   if (!JS_SetProperty(cx, info, "coverage", value)) {
204     return false;
205   }
206 
207 #ifdef JS_HAS_CTYPES
208   value = BooleanValue(true);
209 #else
210   value = BooleanValue(false);
211 #endif
212   if (!JS_SetProperty(cx, info, "has-ctypes", value)) {
213     return false;
214   }
215 
216 #if defined(_M_IX86) || defined(__i386__)
217   value = BooleanValue(true);
218 #else
219   value = BooleanValue(false);
220 #endif
221   if (!JS_SetProperty(cx, info, "x86", value)) {
222     return false;
223   }
224 
225 #if defined(_M_X64) || defined(__x86_64__)
226   value = BooleanValue(true);
227 #else
228   value = BooleanValue(false);
229 #endif
230   if (!JS_SetProperty(cx, info, "x64", value)) {
231     return false;
232   }
233 
234 #ifdef JS_CODEGEN_ARM
235   value = BooleanValue(true);
236 #else
237   value = BooleanValue(false);
238 #endif
239   if (!JS_SetProperty(cx, info, "arm", value)) {
240     return false;
241   }
242 
243 #ifdef JS_SIMULATOR_ARM
244   value = BooleanValue(true);
245 #else
246   value = BooleanValue(false);
247 #endif
248   if (!JS_SetProperty(cx, info, "arm-simulator", value)) {
249     return false;
250   }
251 
252 #ifdef ANDROID
253   value = BooleanValue(true);
254 #else
255   value = BooleanValue(false);
256 #endif
257   if (!JS_SetProperty(cx, info, "android", value)) {
258     return false;
259   }
260 
261 #ifdef XP_WIN
262   value = BooleanValue(true);
263 #else
264   value = BooleanValue(false);
265 #endif
266   if (!JS_SetProperty(cx, info, "windows", value)) {
267     return false;
268   }
269 
270 #ifdef JS_CODEGEN_ARM64
271   value = BooleanValue(true);
272 #else
273   value = BooleanValue(false);
274 #endif
275   if (!JS_SetProperty(cx, info, "arm64", value)) {
276     return false;
277   }
278 
279 #ifdef JS_SIMULATOR_ARM64
280   value = BooleanValue(true);
281 #else
282   value = BooleanValue(false);
283 #endif
284   if (!JS_SetProperty(cx, info, "arm64-simulator", value)) {
285     return false;
286   }
287 
288 #ifdef JS_CODEGEN_MIPS32
289   value = BooleanValue(true);
290 #else
291   value = BooleanValue(false);
292 #endif
293   if (!JS_SetProperty(cx, info, "mips32", value)) {
294     return false;
295   }
296 
297 #ifdef JS_CODEGEN_MIPS64
298   value = BooleanValue(true);
299 #else
300   value = BooleanValue(false);
301 #endif
302   if (!JS_SetProperty(cx, info, "mips64", value)) {
303     return false;
304   }
305 
306 #ifdef JS_SIMULATOR_MIPS32
307   value = BooleanValue(true);
308 #else
309   value = BooleanValue(false);
310 #endif
311   if (!JS_SetProperty(cx, info, "mips32-simulator", value)) {
312     return false;
313   }
314 
315 #ifdef JS_SIMULATOR_MIPS64
316   value = BooleanValue(true);
317 #else
318   value = BooleanValue(false);
319 #endif
320   if (!JS_SetProperty(cx, info, "mips64-simulator", value)) {
321     return false;
322   }
323 
324 #ifdef MOZ_ASAN
325   value = BooleanValue(true);
326 #else
327   value = BooleanValue(false);
328 #endif
329   if (!JS_SetProperty(cx, info, "asan", value)) {
330     return false;
331   }
332 
333 #ifdef MOZ_TSAN
334   value = BooleanValue(true);
335 #else
336   value = BooleanValue(false);
337 #endif
338   if (!JS_SetProperty(cx, info, "tsan", value)) {
339     return false;
340   }
341 
342 #ifdef MOZ_UBSAN
343   value = BooleanValue(true);
344 #else
345   value = BooleanValue(false);
346 #endif
347   if (!JS_SetProperty(cx, info, "ubsan", value)) {
348     return false;
349   }
350 
351 #ifdef JS_GC_ZEAL
352   value = BooleanValue(true);
353 #else
354   value = BooleanValue(false);
355 #endif
356   if (!JS_SetProperty(cx, info, "has-gczeal", value)) {
357     return false;
358   }
359 
360 #ifdef JS_MORE_DETERMINISTIC
361   value = BooleanValue(true);
362 #else
363   value = BooleanValue(false);
364 #endif
365   if (!JS_SetProperty(cx, info, "more-deterministic", value)) {
366     return false;
367   }
368 
369 #ifdef MOZ_PROFILING
370   value = BooleanValue(true);
371 #else
372   value = BooleanValue(false);
373 #endif
374   if (!JS_SetProperty(cx, info, "profiling", value)) {
375     return false;
376   }
377 
378 #ifdef INCLUDE_MOZILLA_DTRACE
379   value = BooleanValue(true);
380 #else
381   value = BooleanValue(false);
382 #endif
383   if (!JS_SetProperty(cx, info, "dtrace", value)) {
384     return false;
385   }
386 
387 #ifdef MOZ_VALGRIND
388   value = BooleanValue(true);
389 #else
390   value = BooleanValue(false);
391 #endif
392   if (!JS_SetProperty(cx, info, "valgrind", value)) {
393     return false;
394   }
395 
396 #ifdef JS_HAS_TYPED_OBJECTS
397   value = BooleanValue(true);
398 #else
399   value = BooleanValue(false);
400 #endif
401   if (!JS_SetProperty(cx, info, "typed-objects", value)) {
402     return false;
403   }
404 
405 #ifdef JS_HAS_INTL_API
406   value = BooleanValue(true);
407 #else
408   value = BooleanValue(false);
409 #endif
410   if (!JS_SetProperty(cx, info, "intl-api", value)) {
411     return false;
412   }
413 
414 #if defined(SOLARIS)
415   value = BooleanValue(false);
416 #else
417   value = BooleanValue(true);
418 #endif
419   if (!JS_SetProperty(cx, info, "mapped-array-buffer", value)) {
420     return false;
421   }
422 
423 #ifdef MOZ_MEMORY
424   value = BooleanValue(true);
425 #else
426   value = BooleanValue(false);
427 #endif
428   if (!JS_SetProperty(cx, info, "moz-memory", value)) {
429     return false;
430   }
431 
432 #if defined(JS_BUILD_BINAST)
433   value = BooleanValue(true);
434 #else
435   value = BooleanValue(false);
436 #endif
437   if (!JS_SetProperty(cx, info, "binast", value)) {
438     return false;
439   }
440 
441   value.setInt32(sizeof(void*));
442   if (!JS_SetProperty(cx, info, "pointer-byte-size", value)) {
443     return false;
444   }
445 
446   args.rval().setObject(*info);
447   return true;
448 }
449 
IsLCovEnabled(JSContext * cx,unsigned argc,Value * vp)450 static bool IsLCovEnabled(JSContext* cx, unsigned argc, Value* vp) {
451   CallArgs args = CallArgsFromVp(argc, vp);
452   args.rval().setBoolean(coverage::IsLCovEnabled());
453   return true;
454 }
455 
IsTypeInferenceEnabled(JSContext * cx,unsigned argc,Value * vp)456 static bool IsTypeInferenceEnabled(JSContext* cx, unsigned argc, Value* vp) {
457   CallArgs args = CallArgsFromVp(argc, vp);
458   args.rval().setBoolean(js::IsTypeInferenceEnabled());
459   return true;
460 }
461 
ReturnStringCopy(JSContext * cx,CallArgs & args,const char * message)462 static bool ReturnStringCopy(JSContext* cx, CallArgs& args,
463                              const char* message) {
464   JSString* str = JS_NewStringCopyZ(cx, message);
465   if (!str) {
466     return false;
467   }
468 
469   args.rval().setString(str);
470   return true;
471 }
472 
MaybeGC(JSContext * cx,unsigned argc,Value * vp)473 static bool MaybeGC(JSContext* cx, unsigned argc, Value* vp) {
474   CallArgs args = CallArgsFromVp(argc, vp);
475   JS_MaybeGC(cx);
476   args.rval().setUndefined();
477   return true;
478 }
479 
GC(JSContext * cx,unsigned argc,Value * vp)480 static bool GC(JSContext* cx, unsigned argc, Value* vp) {
481   CallArgs args = CallArgsFromVp(argc, vp);
482 
483   /*
484    * If the first argument is 'zone', we collect any zones previously
485    * scheduled for GC via schedulegc. If the first argument is an object, we
486    * collect the object's zone (and any other zones scheduled for
487    * GC). Otherwise, we collect all zones.
488    */
489   bool zone = false;
490   if (args.length() >= 1) {
491     Value arg = args[0];
492     if (arg.isString()) {
493       if (!JS_StringEqualsLiteral(cx, arg.toString(), "zone", &zone)) {
494         return false;
495       }
496     } else if (arg.isObject()) {
497       PrepareZoneForGC(cx, UncheckedUnwrap(&arg.toObject())->zone());
498       zone = true;
499     }
500   }
501 
502   JSGCInvocationKind gckind = GC_NORMAL;
503   JS::GCReason reason = JS::GCReason::API;
504   if (args.length() >= 2) {
505     Value arg = args[1];
506     if (arg.isString()) {
507       bool shrinking = false;
508       bool last_ditch = false;
509       if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking",
510                                   &shrinking)) {
511         return false;
512       }
513       if (!JS_StringEqualsLiteral(cx, arg.toString(), "last-ditch",
514                                   &last_ditch)) {
515         return false;
516       }
517       if (shrinking) {
518         gckind = GC_SHRINK;
519       } else if (last_ditch) {
520         gckind = GC_SHRINK;
521         reason = JS::GCReason::LAST_DITCH;
522       }
523     }
524   }
525 
526 #ifndef JS_MORE_DETERMINISTIC
527   size_t preBytes = cx->runtime()->gc.heapSize.bytes();
528 #endif
529 
530   if (zone) {
531     PrepareForDebugGC(cx->runtime());
532   } else {
533     JS::PrepareForFullGC(cx);
534   }
535 
536   JS::NonIncrementalGC(cx, gckind, reason);
537 
538   char buf[256] = {'\0'};
539 #ifndef JS_MORE_DETERMINISTIC
540   SprintfLiteral(buf, "before %zu, after %zu\n", preBytes,
541                  cx->runtime()->gc.heapSize.bytes());
542 #endif
543   return ReturnStringCopy(cx, args, buf);
544 }
545 
MinorGC(JSContext * cx,unsigned argc,Value * vp)546 static bool MinorGC(JSContext* cx, unsigned argc, Value* vp) {
547   CallArgs args = CallArgsFromVp(argc, vp);
548   if (args.get(0) == BooleanValue(true)) {
549     cx->runtime()->gc.storeBuffer().setAboutToOverflow(
550         JS::GCReason::FULL_GENERIC_BUFFER);
551   }
552 
553   cx->minorGC(JS::GCReason::API);
554   args.rval().setUndefined();
555   return true;
556 }
557 
558 #define FOR_EACH_GC_PARAM(_)                                               \
559   _("maxBytes", JSGC_MAX_BYTES, true)                                      \
560   _("minNurseryBytes", JSGC_MIN_NURSERY_BYTES, true)                       \
561   _("maxNurseryBytes", JSGC_MAX_NURSERY_BYTES, true)                       \
562   _("gcBytes", JSGC_BYTES, false)                                          \
563   _("nurseryBytes", JSGC_NURSERY_BYTES, false)                             \
564   _("gcNumber", JSGC_NUMBER, false)                                        \
565   _("mode", JSGC_MODE, true)                                               \
566   _("unusedChunks", JSGC_UNUSED_CHUNKS, false)                             \
567   _("totalChunks", JSGC_TOTAL_CHUNKS, false)                               \
568   _("sliceTimeBudgetMS", JSGC_SLICE_TIME_BUDGET_MS, true)                  \
569   _("markStackLimit", JSGC_MARK_STACK_LIMIT, true)                         \
570   _("highFrequencyTimeLimit", JSGC_HIGH_FREQUENCY_TIME_LIMIT, true)        \
571   _("smallHeapSizeMax", JSGC_SMALL_HEAP_SIZE_MAX, true)                    \
572   _("largeHeapSizeMin", JSGC_LARGE_HEAP_SIZE_MIN, true)                    \
573   _("highFrequencySmallHeapGrowth", JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH, \
574     true)                                                                  \
575   _("highFrequencyLargeHeapGrowth", JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH, \
576     true)                                                                  \
577   _("lowFrequencyHeapGrowth", JSGC_LOW_FREQUENCY_HEAP_GROWTH, true)        \
578   _("allocationThreshold", JSGC_ALLOCATION_THRESHOLD, true)                \
579   _("smallHeapIncrementalLimit", JSGC_SMALL_HEAP_INCREMENTAL_LIMIT, true)  \
580   _("largeHeapIncrementalLimit", JSGC_LARGE_HEAP_INCREMENTAL_LIMIT, true)  \
581   _("minEmptyChunkCount", JSGC_MIN_EMPTY_CHUNK_COUNT, true)                \
582   _("maxEmptyChunkCount", JSGC_MAX_EMPTY_CHUNK_COUNT, true)                \
583   _("compactingEnabled", JSGC_COMPACTING_ENABLED, true)                    \
584   _("minLastDitchGCPeriod", JSGC_MIN_LAST_DITCH_GC_PERIOD, true)           \
585   _("nurseryFreeThresholdForIdleCollection",                               \
586     JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION, true)                 \
587   _("nurseryFreeThresholdForIdleCollectionPercent",                        \
588     JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT, true)         \
589   _("pretenureThreshold", JSGC_PRETENURE_THRESHOLD, true)                  \
590   _("pretenureGroupThreshold", JSGC_PRETENURE_GROUP_THRESHOLD, true)       \
591   _("zoneAllocDelayKB", JSGC_ZONE_ALLOC_DELAY_KB, true)                    \
592   _("mallocThresholdBase", JSGC_MALLOC_THRESHOLD_BASE, true)               \
593   _("mallocGrowthFactor", JSGC_MALLOC_GROWTH_FACTOR, true)
594 
595 static const struct ParamInfo {
596   const char* name;
597   JSGCParamKey param;
598   bool writable;
599 } paramMap[] = {
600 #define DEFINE_PARAM_INFO(name, key, writable) {name, key, writable},
601     FOR_EACH_GC_PARAM(DEFINE_PARAM_INFO)
602 #undef DEFINE_PARAM_INFO
603 };
604 
605 #define PARAM_NAME_LIST_ENTRY(name, key, writable) " " name
606 #define GC_PARAMETER_ARGS_LIST FOR_EACH_GC_PARAM(PARAM_NAME_LIST_ENTRY)
607 
GCParameter(JSContext * cx,unsigned argc,Value * vp)608 static bool GCParameter(JSContext* cx, unsigned argc, Value* vp) {
609   CallArgs args = CallArgsFromVp(argc, vp);
610 
611   JSString* str = ToString(cx, args.get(0));
612   if (!str) {
613     return false;
614   }
615 
616   JSLinearString* linearStr = JS_EnsureLinearString(cx, str);
617   if (!linearStr) {
618     return false;
619   }
620 
621   size_t paramIndex = 0;
622   for (;; paramIndex++) {
623     if (paramIndex == ArrayLength(paramMap)) {
624       JS_ReportErrorASCII(
625           cx, "the first argument must be one of:" GC_PARAMETER_ARGS_LIST);
626       return false;
627     }
628     if (JS_LinearStringEqualsAscii(linearStr, paramMap[paramIndex].name)) {
629       break;
630     }
631   }
632   const ParamInfo& info = paramMap[paramIndex];
633   JSGCParamKey param = info.param;
634 
635   // Request mode.
636   if (args.length() == 1) {
637     uint32_t value = JS_GetGCParameter(cx, param);
638     args.rval().setNumber(value);
639     return true;
640   }
641 
642   if (!info.writable) {
643     JS_ReportErrorASCII(cx, "Attempt to change read-only parameter %s",
644                         info.name);
645     return false;
646   }
647 
648   if (disableOOMFunctions) {
649     switch (param) {
650       case JSGC_MAX_BYTES:
651       case JSGC_MAX_NURSERY_BYTES:
652         args.rval().setUndefined();
653         return true;
654       default:
655         break;
656     }
657   }
658 
659   double d;
660   if (!ToNumber(cx, args[1], &d)) {
661     return false;
662   }
663 
664   if (d < 0 || d > UINT32_MAX) {
665     JS_ReportErrorASCII(cx, "Parameter value out of range");
666     return false;
667   }
668 
669   uint32_t value = floor(d);
670   if (param == JSGC_MARK_STACK_LIMIT && JS::IsIncrementalGCInProgress(cx)) {
671     JS_ReportErrorASCII(
672         cx, "attempt to set markStackLimit while a GC is in progress");
673     return false;
674   }
675 
676   bool ok = cx->runtime()->gc.setParameter(param, value);
677   if (!ok) {
678     JS_ReportErrorASCII(cx, "Parameter value out of range");
679     return false;
680   }
681 
682   args.rval().setUndefined();
683   return true;
684 }
685 
RelazifyFunctions(JSContext * cx,unsigned argc,Value * vp)686 static bool RelazifyFunctions(JSContext* cx, unsigned argc, Value* vp) {
687   // Relazifying functions on GC is usually only done for compartments that are
688   // not active. To aid fuzzing, this testing function allows us to relazify
689   // even if the compartment is active.
690 
691   CallArgs args = CallArgsFromVp(argc, vp);
692 
693   // Disable relazification of all scripts on stack. It is a pervasive
694   // assumption in the engine that running scripts still have bytecode.
695   for (AllScriptFramesIter i(cx); !i.done(); ++i) {
696     i.script()->clearAllowRelazify();
697   }
698 
699   cx->runtime()->allowRelazificationForTesting = true;
700 
701   JS::PrepareForFullGC(cx);
702   JS::NonIncrementalGC(cx, GC_SHRINK, JS::GCReason::API);
703 
704   cx->runtime()->allowRelazificationForTesting = false;
705 
706   args.rval().setUndefined();
707   return true;
708 }
709 
IsProxy(JSContext * cx,unsigned argc,Value * vp)710 static bool IsProxy(JSContext* cx, unsigned argc, Value* vp) {
711   CallArgs args = CallArgsFromVp(argc, vp);
712   if (args.length() != 1) {
713     JS_ReportErrorASCII(cx, "the function takes exactly one argument");
714     return false;
715   }
716   if (!args[0].isObject()) {
717     args.rval().setBoolean(false);
718     return true;
719   }
720   args.rval().setBoolean(args[0].toObject().is<ProxyObject>());
721   return true;
722 }
723 
WasmIsSupported(JSContext * cx,unsigned argc,Value * vp)724 static bool WasmIsSupported(JSContext* cx, unsigned argc, Value* vp) {
725   CallArgs args = CallArgsFromVp(argc, vp);
726   args.rval().setBoolean(wasm::HasSupport(cx));
727   return true;
728 }
729 
WasmIsSupportedByHardware(JSContext * cx,unsigned argc,Value * vp)730 static bool WasmIsSupportedByHardware(JSContext* cx, unsigned argc, Value* vp) {
731   CallArgs args = CallArgsFromVp(argc, vp);
732   args.rval().setBoolean(wasm::HasPlatformSupport(cx));
733   return true;
734 }
735 
WasmDebuggingIsSupported(JSContext * cx,unsigned argc,Value * vp)736 static bool WasmDebuggingIsSupported(JSContext* cx, unsigned argc, Value* vp) {
737   CallArgs args = CallArgsFromVp(argc, vp);
738   args.rval().setBoolean(wasm::HasSupport(cx) && wasm::BaselineAvailable(cx));
739   return true;
740 }
741 
WasmStreamingIsSupported(JSContext * cx,unsigned argc,Value * vp)742 static bool WasmStreamingIsSupported(JSContext* cx, unsigned argc, Value* vp) {
743   CallArgs args = CallArgsFromVp(argc, vp);
744   args.rval().setBoolean(wasm::StreamingCompilationAvailable(cx));
745   return true;
746 }
747 
WasmCachingIsSupported(JSContext * cx,unsigned argc,Value * vp)748 static bool WasmCachingIsSupported(JSContext* cx, unsigned argc, Value* vp) {
749   CallArgs args = CallArgsFromVp(argc, vp);
750   args.rval().setBoolean(wasm::CodeCachingAvailable(cx));
751   return true;
752 }
753 
WasmHugeMemoryIsSupported(JSContext * cx,unsigned argc,Value * vp)754 static bool WasmHugeMemoryIsSupported(JSContext* cx, unsigned argc, Value* vp) {
755   CallArgs args = CallArgsFromVp(argc, vp);
756 #ifdef WASM_SUPPORTS_HUGE_MEMORY
757   args.rval().setBoolean(true);
758 #else
759   args.rval().setBoolean(false);
760 #endif
761   return true;
762 }
763 
WasmThreadsSupported(JSContext * cx,unsigned argc,Value * vp)764 static bool WasmThreadsSupported(JSContext* cx, unsigned argc, Value* vp) {
765   CallArgs args = CallArgsFromVp(argc, vp);
766   args.rval().setBoolean(wasm::ThreadsAvailable(cx));
767   return true;
768 }
769 
WasmBulkMemSupported(JSContext * cx,unsigned argc,Value * vp)770 static bool WasmBulkMemSupported(JSContext* cx, unsigned argc, Value* vp) {
771   CallArgs args = CallArgsFromVp(argc, vp);
772 #ifdef ENABLE_WASM_BULKMEM_OPS
773   bool isSupported = true;
774 #else
775   bool isSupported =
776       cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
777 #endif
778   args.rval().setBoolean(isSupported);
779   return true;
780 }
781 
WasmReftypesEnabled(JSContext * cx,unsigned argc,Value * vp)782 static bool WasmReftypesEnabled(JSContext* cx, unsigned argc, Value* vp) {
783   CallArgs args = CallArgsFromVp(argc, vp);
784   args.rval().setBoolean(wasm::ReftypesAvailable(cx));
785   return true;
786 }
787 
WasmGcEnabled(JSContext * cx,unsigned argc,Value * vp)788 static bool WasmGcEnabled(JSContext* cx, unsigned argc, Value* vp) {
789   CallArgs args = CallArgsFromVp(argc, vp);
790   args.rval().setBoolean(wasm::GcTypesAvailable(cx));
791   return true;
792 }
793 
WasmMultiValueEnabled(JSContext * cx,unsigned argc,Value * vp)794 static bool WasmMultiValueEnabled(JSContext* cx, unsigned argc, Value* vp) {
795   CallArgs args = CallArgsFromVp(argc, vp);
796   args.rval().setBoolean(wasm::MultiValuesAvailable(cx));
797   return true;
798 }
799 
WasmSimdSupported(JSContext * cx,unsigned argc,Value * vp)800 static bool WasmSimdSupported(JSContext* cx, unsigned argc, Value* vp) {
801   CallArgs args = CallArgsFromVp(argc, vp);
802   args.rval().setBoolean(wasm::SimdAvailable(cx));
803   return true;
804 }
805 
WasmCompilersPresent(JSContext * cx,unsigned argc,Value * vp)806 static bool WasmCompilersPresent(JSContext* cx, unsigned argc, Value* vp) {
807   CallArgs args = CallArgsFromVp(argc, vp);
808 
809   char buf[256];
810   *buf = 0;
811   if (wasm::BaselinePlatformSupport()) {
812     strcat(buf, "baseline");
813   }
814   if (wasm::IonPlatformSupport()) {
815     if (*buf) {
816       strcat(buf, ",");
817     }
818     strcat(buf, "ion");
819   }
820   if (wasm::CraneliftPlatformSupport()) {
821     if (*buf) {
822       strcat(buf, ",");
823     }
824     strcat(buf, "cranelift");
825   }
826 
827   JSString* result = JS_NewStringCopyZ(cx, buf);
828   if (!result) {
829     return false;
830   }
831 
832   args.rval().setString(result);
833   return true;
834 }
835 
WasmCompileMode(JSContext * cx,unsigned argc,Value * vp)836 static bool WasmCompileMode(JSContext* cx, unsigned argc, Value* vp) {
837   CallArgs args = CallArgsFromVp(argc, vp);
838 
839   // This triplet of predicates will select zero or one baseline compiler and
840   // zero or one optimizing compiler.
841   bool baseline = wasm::BaselineAvailable(cx);
842   bool ion = wasm::IonAvailable(cx);
843   bool cranelift = wasm::CraneliftAvailable(cx);
844 
845   MOZ_ASSERT(!(ion && cranelift));
846 
847   JSString* result;
848   if (!wasm::HasSupport(cx)) {
849     result = JS_NewStringCopyZ(cx, "none");
850   } else if (baseline && ion) {
851     result = JS_NewStringCopyZ(cx, "baseline+ion");
852   } else if (baseline && cranelift) {
853     result = JS_NewStringCopyZ(cx, "baseline+cranelift");
854   } else if (baseline) {
855     result = JS_NewStringCopyZ(cx, "baseline");
856   } else if (cranelift) {
857     result = JS_NewStringCopyZ(cx, "cranelift");
858   } else {
859     result = JS_NewStringCopyZ(cx, "ion");
860   }
861 
862   if (!result) {
863     return false;
864   }
865 
866   args.rval().setString(result);
867   return true;
868 }
869 
WasmCraneliftDisabledByFeatures(JSContext * cx,unsigned argc,Value * vp)870 static bool WasmCraneliftDisabledByFeatures(JSContext* cx, unsigned argc,
871                                             Value* vp) {
872   CallArgs args = CallArgsFromVp(argc, vp);
873   bool isDisabled = false;
874   JSStringBuilder reason(cx);
875   if (!wasm::CraneliftDisabledByFeatures(cx, &isDisabled, &reason)) {
876     return false;
877   }
878   if (isDisabled) {
879     JSString* result = reason.finishString();
880     if (!result) {
881       return false;
882     }
883     args.rval().setString(result);
884   } else {
885     args.rval().setBoolean(false);
886   }
887   return true;
888 }
889 
WasmIonDisabledByFeatures(JSContext * cx,unsigned argc,Value * vp)890 static bool WasmIonDisabledByFeatures(JSContext* cx, unsigned argc, Value* vp) {
891   CallArgs args = CallArgsFromVp(argc, vp);
892   bool isDisabled = false;
893   JSStringBuilder reason(cx);
894   if (!wasm::IonDisabledByFeatures(cx, &isDisabled, &reason)) {
895     return false;
896   }
897   if (isDisabled) {
898     JSString* result = reason.finishString();
899     if (!result) {
900       return false;
901     }
902     args.rval().setString(result);
903   } else {
904     args.rval().setBoolean(false);
905   }
906   return true;
907 }
908 
909 #ifdef ENABLE_WASM_SIMD
910 #  ifdef DEBUG
911 static char lastAnalysisResult[1024];
912 
913 namespace js {
914 namespace wasm {
ReportSimdAnalysis(const char * v)915 void ReportSimdAnalysis(const char* v) {
916   strncpy(lastAnalysisResult, v, sizeof(lastAnalysisResult));
917   lastAnalysisResult[sizeof(lastAnalysisResult) - 1] = 0;
918 }
919 }  // namespace wasm
920 }  // namespace js
921 
922 // Unstable API for white-box testing of SIMD optimizations.
923 //
924 // Current API: takes no arguments, returns a string describing the last Simd
925 // simplification applied.
926 
WasmSimdAnalysis(JSContext * cx,unsigned argc,Value * vp)927 static bool WasmSimdAnalysis(JSContext* cx, unsigned argc, Value* vp) {
928   CallArgs args = CallArgsFromVp(argc, vp);
929   JSString* result =
930       JS_NewStringCopyZ(cx, *lastAnalysisResult ? lastAnalysisResult : "none");
931   if (!result) {
932     return false;
933   }
934   args.rval().setString(result);
935   *lastAnalysisResult = (char)0;
936   return true;
937 }
938 #  endif
939 #endif
940 
ConvertToTier(JSContext * cx,HandleValue value,const wasm::Code & code,wasm::Tier * tier)941 static bool ConvertToTier(JSContext* cx, HandleValue value,
942                           const wasm::Code& code, wasm::Tier* tier) {
943   RootedString option(cx, JS::ToString(cx, value));
944 
945   if (!option) {
946     return false;
947   }
948 
949   bool stableTier = false;
950   bool bestTier = false;
951   bool baselineTier = false;
952   bool ionTier = false;
953 
954   if (!JS_StringEqualsLiteral(cx, option, "stable", &stableTier) ||
955       !JS_StringEqualsLiteral(cx, option, "best", &bestTier) ||
956       !JS_StringEqualsLiteral(cx, option, "baseline", &baselineTier) ||
957       !JS_StringEqualsLiteral(cx, option, "ion", &ionTier)) {
958     return false;
959   }
960 
961   if (stableTier) {
962     *tier = code.stableTier();
963   } else if (bestTier) {
964     *tier = code.bestTier();
965   } else if (baselineTier) {
966     *tier = wasm::Tier::Baseline;
967   } else if (ionTier) {
968     *tier = wasm::Tier::Optimized;
969   } else {
970     // You can omit the argument but you can't pass just anything you like
971     return false;
972   }
973 
974   return true;
975 }
976 
WasmExtractCode(JSContext * cx,unsigned argc,Value * vp)977 static bool WasmExtractCode(JSContext* cx, unsigned argc, Value* vp) {
978   if (!cx->options().wasm()) {
979     JS_ReportErrorASCII(cx, "wasm support unavailable");
980     return false;
981   }
982 
983   CallArgs args = CallArgsFromVp(argc, vp);
984 
985   if (!args.get(0).isObject()) {
986     JS_ReportErrorASCII(cx, "argument is not an object");
987     return false;
988   }
989 
990   Rooted<WasmModuleObject*> module(
991       cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>());
992   if (!module) {
993     JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
994     return false;
995   }
996 
997   wasm::Tier tier = module->module().code().stableTier();
998   ;
999   if (args.length() > 1 &&
1000       !ConvertToTier(cx, args[1], module->module().code(), &tier)) {
1001     args.rval().setNull();
1002     return false;
1003   }
1004 
1005   RootedValue result(cx);
1006   if (!module->module().extractCode(cx, tier, &result)) {
1007     return false;
1008   }
1009 
1010   args.rval().set(result);
1011   return true;
1012 }
1013 
WasmDisassemble(JSContext * cx,unsigned argc,Value * vp)1014 static bool WasmDisassemble(JSContext* cx, unsigned argc, Value* vp) {
1015   if (!cx->options().wasm()) {
1016     JS_ReportErrorASCII(cx, "wasm support unavailable");
1017     return false;
1018   }
1019 
1020   CallArgs args = CallArgsFromVp(argc, vp);
1021 
1022   args.rval().set(UndefinedValue());
1023 
1024   if (!args.get(0).isObject()) {
1025     JS_ReportErrorASCII(cx, "argument is not an object");
1026     return false;
1027   }
1028 
1029   RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>());
1030 
1031   if (!func || !wasm::IsWasmExportedFunction(func)) {
1032     JS_ReportErrorASCII(cx, "argument is not an exported wasm function");
1033     return false;
1034   }
1035 
1036   wasm::Instance& instance = wasm::ExportedFunctionToInstance(func);
1037   uint32_t funcIndex = wasm::ExportedFunctionToFuncIndex(func);
1038 
1039   wasm::Tier tier = instance.code().stableTier();
1040 
1041   if (args.length() > 1 &&
1042       !ConvertToTier(cx, args[1], instance.code(), &tier)) {
1043     JS_ReportErrorASCII(cx, "invalid tier");
1044     return false;
1045   }
1046 
1047   if (!instance.code().hasTier(tier)) {
1048     JS_ReportErrorASCII(cx, "function missing selected tier");
1049     return false;
1050   }
1051 
1052   instance.disassembleExport(cx, funcIndex, tier, [](const char* text) {
1053     fprintf(stderr, "%s\n", text);
1054   });
1055 
1056   return true;
1057 }
1058 
1059 enum class Flag { Tier2Complete, Deserialized };
1060 
WasmReturnFlag(JSContext * cx,unsigned argc,Value * vp,Flag flag)1061 static bool WasmReturnFlag(JSContext* cx, unsigned argc, Value* vp, Flag flag) {
1062   CallArgs args = CallArgsFromVp(argc, vp);
1063 
1064   if (!args.get(0).isObject()) {
1065     JS_ReportErrorASCII(cx, "argument is not an object");
1066     return false;
1067   }
1068 
1069   Rooted<WasmModuleObject*> module(
1070       cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>());
1071   if (!module) {
1072     JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
1073     return false;
1074   }
1075 
1076   bool b;
1077   switch (flag) {
1078     case Flag::Tier2Complete:
1079       b = !module->module().testingTier2Active();
1080       break;
1081     case Flag::Deserialized:
1082       b = module->module().loggingDeserialized();
1083       break;
1084   }
1085 
1086   args.rval().set(BooleanValue(b));
1087   return true;
1088 }
1089 
WasmHasTier2CompilationCompleted(JSContext * cx,unsigned argc,Value * vp)1090 static bool WasmHasTier2CompilationCompleted(JSContext* cx, unsigned argc,
1091                                              Value* vp) {
1092   return WasmReturnFlag(cx, argc, vp, Flag::Tier2Complete);
1093 }
1094 
WasmLoadedFromCache(JSContext * cx,unsigned argc,Value * vp)1095 static bool WasmLoadedFromCache(JSContext* cx, unsigned argc, Value* vp) {
1096   return WasmReturnFlag(cx, argc, vp, Flag::Deserialized);
1097 }
1098 
IsLazyFunction(JSContext * cx,unsigned argc,Value * vp)1099 static bool IsLazyFunction(JSContext* cx, unsigned argc, Value* vp) {
1100   CallArgs args = CallArgsFromVp(argc, vp);
1101   if (args.length() != 1) {
1102     JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
1103     return false;
1104   }
1105   if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
1106     JS_ReportErrorASCII(cx, "The first argument should be a function.");
1107     return false;
1108   }
1109   JSFunction* fun = &args[0].toObject().as<JSFunction>();
1110   args.rval().setBoolean(fun->isInterpreted() && !fun->hasBytecode());
1111   return true;
1112 }
1113 
IsRelazifiableFunction(JSContext * cx,unsigned argc,Value * vp)1114 static bool IsRelazifiableFunction(JSContext* cx, unsigned argc, Value* vp) {
1115   CallArgs args = CallArgsFromVp(argc, vp);
1116   if (args.length() != 1) {
1117     JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
1118     return false;
1119   }
1120   if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
1121     JS_ReportErrorASCII(cx, "The first argument should be a function.");
1122     return false;
1123   }
1124 
1125   JSFunction* fun = &args[0].toObject().as<JSFunction>();
1126   args.rval().setBoolean(fun->hasBytecode() &&
1127                          fun->nonLazyScript()->allowRelazify());
1128   return true;
1129 }
1130 
InternalConst(JSContext * cx,unsigned argc,Value * vp)1131 static bool InternalConst(JSContext* cx, unsigned argc, Value* vp) {
1132   CallArgs args = CallArgsFromVp(argc, vp);
1133   if (args.length() == 0) {
1134     JS_ReportErrorASCII(cx, "the function takes exactly one argument");
1135     return false;
1136   }
1137 
1138   JSString* str = ToString(cx, args[0]);
1139   if (!str) {
1140     return false;
1141   }
1142   JSLinearString* linear = JS_EnsureLinearString(cx, str);
1143   if (!linear) {
1144     return false;
1145   }
1146 
1147   if (JS_LinearStringEqualsLiteral(linear,
1148                                    "INCREMENTAL_MARK_STACK_BASE_CAPACITY")) {
1149     args.rval().setNumber(uint32_t(js::INCREMENTAL_MARK_STACK_BASE_CAPACITY));
1150   } else {
1151     JS_ReportErrorASCII(cx, "unknown const name");
1152     return false;
1153   }
1154   return true;
1155 }
1156 
GCPreserveCode(JSContext * cx,unsigned argc,Value * vp)1157 static bool GCPreserveCode(JSContext* cx, unsigned argc, Value* vp) {
1158   CallArgs args = CallArgsFromVp(argc, vp);
1159 
1160   if (args.length() != 0) {
1161     RootedObject callee(cx, &args.callee());
1162     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1163     return false;
1164   }
1165 
1166   cx->runtime()->gc.setAlwaysPreserveCode();
1167 
1168   args.rval().setUndefined();
1169   return true;
1170 }
1171 
1172 #ifdef JS_GC_ZEAL
1173 
ParseGCZealMode(JSContext * cx,const CallArgs & args,uint8_t * zeal)1174 static bool ParseGCZealMode(JSContext* cx, const CallArgs& args,
1175                             uint8_t* zeal) {
1176   uint32_t value;
1177   if (!ToUint32(cx, args.get(0), &value)) {
1178     return false;
1179   }
1180 
1181   if (value > uint32_t(gc::ZealMode::Limit)) {
1182     JS_ReportErrorASCII(cx, "gczeal argument out of range");
1183     return false;
1184   }
1185 
1186   *zeal = static_cast<uint8_t>(value);
1187   return true;
1188 }
1189 
GCZeal(JSContext * cx,unsigned argc,Value * vp)1190 static bool GCZeal(JSContext* cx, unsigned argc, Value* vp) {
1191   CallArgs args = CallArgsFromVp(argc, vp);
1192 
1193   if (args.length() > 2) {
1194     RootedObject callee(cx, &args.callee());
1195     ReportUsageErrorASCII(cx, callee, "Too many arguments");
1196     return false;
1197   }
1198 
1199   uint8_t zeal;
1200   if (!ParseGCZealMode(cx, args, &zeal)) {
1201     return false;
1202   }
1203 
1204   uint32_t frequency = JS_DEFAULT_ZEAL_FREQ;
1205   if (args.length() >= 2) {
1206     if (!ToUint32(cx, args.get(1), &frequency)) {
1207       return false;
1208     }
1209   }
1210 
1211   JS_SetGCZeal(cx, zeal, frequency);
1212   args.rval().setUndefined();
1213   return true;
1214 }
1215 
UnsetGCZeal(JSContext * cx,unsigned argc,Value * vp)1216 static bool UnsetGCZeal(JSContext* cx, unsigned argc, Value* vp) {
1217   CallArgs args = CallArgsFromVp(argc, vp);
1218 
1219   if (args.length() > 1) {
1220     RootedObject callee(cx, &args.callee());
1221     ReportUsageErrorASCII(cx, callee, "Too many arguments");
1222     return false;
1223   }
1224 
1225   uint8_t zeal;
1226   if (!ParseGCZealMode(cx, args, &zeal)) {
1227     return false;
1228   }
1229 
1230   JS_UnsetGCZeal(cx, zeal);
1231   args.rval().setUndefined();
1232   return true;
1233 }
1234 
ScheduleGC(JSContext * cx,unsigned argc,Value * vp)1235 static bool ScheduleGC(JSContext* cx, unsigned argc, Value* vp) {
1236   CallArgs args = CallArgsFromVp(argc, vp);
1237 
1238   if (args.length() > 1) {
1239     RootedObject callee(cx, &args.callee());
1240     ReportUsageErrorASCII(cx, callee, "Too many arguments");
1241     return false;
1242   }
1243 
1244   if (args.length() == 0) {
1245     /* Fetch next zeal trigger only. */
1246   } else if (args[0].isNumber()) {
1247     /* Schedule a GC to happen after |arg| allocations. */
1248     JS_ScheduleGC(cx, std::max(int(args[0].toNumber()), 0));
1249   } else {
1250     RootedObject callee(cx, &args.callee());
1251     ReportUsageErrorASCII(cx, callee, "Bad argument - expecting number");
1252     return false;
1253   }
1254 
1255   uint32_t zealBits;
1256   uint32_t freq;
1257   uint32_t next;
1258   JS_GetGCZealBits(cx, &zealBits, &freq, &next);
1259   args.rval().setInt32(next);
1260   return true;
1261 }
1262 
SelectForGC(JSContext * cx,unsigned argc,Value * vp)1263 static bool SelectForGC(JSContext* cx, unsigned argc, Value* vp) {
1264   CallArgs args = CallArgsFromVp(argc, vp);
1265 
1266   /*
1267    * The selectedForMarking set is intended to be manually marked at slice
1268    * start to detect missing pre-barriers. It is invalid for nursery things
1269    * to be in the set, so evict the nursery before adding items.
1270    */
1271   cx->runtime()->gc.evictNursery();
1272 
1273   for (unsigned i = 0; i < args.length(); i++) {
1274     if (args[i].isObject()) {
1275       if (!cx->runtime()->gc.selectForMarking(&args[i].toObject())) {
1276         return false;
1277       }
1278     }
1279   }
1280 
1281   args.rval().setUndefined();
1282   return true;
1283 }
1284 
VerifyPreBarriers(JSContext * cx,unsigned argc,Value * vp)1285 static bool VerifyPreBarriers(JSContext* cx, unsigned argc, Value* vp) {
1286   CallArgs args = CallArgsFromVp(argc, vp);
1287 
1288   if (args.length() > 0) {
1289     RootedObject callee(cx, &args.callee());
1290     ReportUsageErrorASCII(cx, callee, "Too many arguments");
1291     return false;
1292   }
1293 
1294   gc::VerifyBarriers(cx->runtime(), gc::PreBarrierVerifier);
1295   args.rval().setUndefined();
1296   return true;
1297 }
1298 
VerifyPostBarriers(JSContext * cx,unsigned argc,Value * vp)1299 static bool VerifyPostBarriers(JSContext* cx, unsigned argc, Value* vp) {
1300   // This is a no-op since the post barrier verifier was removed.
1301   CallArgs args = CallArgsFromVp(argc, vp);
1302   if (args.length()) {
1303     RootedObject callee(cx, &args.callee());
1304     ReportUsageErrorASCII(cx, callee, "Too many arguments");
1305     return false;
1306   }
1307   args.rval().setUndefined();
1308   return true;
1309 }
1310 
CurrentGC(JSContext * cx,unsigned argc,Value * vp)1311 static bool CurrentGC(JSContext* cx, unsigned argc, Value* vp) {
1312   CallArgs args = CallArgsFromVp(argc, vp);
1313 
1314   if (args.length() != 0) {
1315     RootedObject callee(cx, &args.callee());
1316     ReportUsageErrorASCII(cx, callee, "Too many arguments");
1317     return false;
1318   }
1319 
1320   RootedObject result(cx, JS_NewPlainObject(cx));
1321   if (!result) {
1322     return false;
1323   }
1324 
1325   js::gc::GCRuntime& gc = cx->runtime()->gc;
1326   const char* state = StateName(gc.state());
1327 
1328   RootedString str(cx, JS_NewStringCopyZ(cx, state));
1329   if (!str) {
1330     return false;
1331   }
1332   RootedValue val(cx, StringValue(str));
1333   if (!JS_DefineProperty(cx, result, "incrementalState", val,
1334                          JSPROP_ENUMERATE)) {
1335     return false;
1336   }
1337 
1338   if (gc.state() == js::gc::State::Sweep) {
1339     val = Int32Value(gc.getCurrentSweepGroupIndex());
1340     if (!JS_DefineProperty(cx, result, "sweepGroup", val, JSPROP_ENUMERATE)) {
1341       return false;
1342     }
1343   }
1344 
1345   val = BooleanValue(gc.isShrinkingGC());
1346   if (!JS_DefineProperty(cx, result, "isShrinking", val, JSPROP_ENUMERATE)) {
1347     return false;
1348   }
1349 
1350   val = Int32Value(gc.gcNumber());
1351   if (!JS_DefineProperty(cx, result, "number", val, JSPROP_ENUMERATE)) {
1352     return false;
1353   }
1354 
1355   val = Int32Value(gc.minorGCCount());
1356   if (!JS_DefineProperty(cx, result, "minorCount", val, JSPROP_ENUMERATE)) {
1357     return false;
1358   }
1359 
1360   val = Int32Value(gc.majorGCCount());
1361   if (!JS_DefineProperty(cx, result, "majorCount", val, JSPROP_ENUMERATE)) {
1362     return false;
1363   }
1364 
1365   val = BooleanValue(gc.isFullGc());
1366   if (!JS_DefineProperty(cx, result, "isFull", val, JSPROP_ENUMERATE)) {
1367     return false;
1368   }
1369 
1370   val = BooleanValue(gc.isCompactingGc());
1371   if (!JS_DefineProperty(cx, result, "isCompacting", val, JSPROP_ENUMERATE)) {
1372     return false;
1373   }
1374 
1375 #  ifdef DEBUG
1376   val = Int32Value(gc.marker.queuePos);
1377   if (!JS_DefineProperty(cx, result, "queuePos", val, JSPROP_ENUMERATE)) {
1378     return false;
1379   }
1380 #  endif
1381 
1382   args.rval().setObject(*result);
1383   return true;
1384 }
1385 
DeterministicGC(JSContext * cx,unsigned argc,Value * vp)1386 static bool DeterministicGC(JSContext* cx, unsigned argc, Value* vp) {
1387   CallArgs args = CallArgsFromVp(argc, vp);
1388 
1389   if (args.length() != 1) {
1390     RootedObject callee(cx, &args.callee());
1391     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1392     return false;
1393   }
1394 
1395   cx->runtime()->gc.setDeterministic(ToBoolean(args[0]));
1396   args.rval().setUndefined();
1397   return true;
1398 }
1399 
DumpGCArenaInfo(JSContext * cx,unsigned argc,Value * vp)1400 static bool DumpGCArenaInfo(JSContext* cx, unsigned argc, Value* vp) {
1401   CallArgs args = CallArgsFromVp(argc, vp);
1402   js::gc::DumpArenaInfo();
1403   args.rval().setUndefined();
1404   return true;
1405 }
1406 
1407 #endif /* JS_GC_ZEAL */
1408 
GCState(JSContext * cx,unsigned argc,Value * vp)1409 static bool GCState(JSContext* cx, unsigned argc, Value* vp) {
1410   CallArgs args = CallArgsFromVp(argc, vp);
1411 
1412   if (args.length() > 1) {
1413     RootedObject callee(cx, &args.callee());
1414     ReportUsageErrorASCII(cx, callee, "Too many arguments");
1415     return false;
1416   }
1417 
1418   const char* state;
1419 
1420   if (args.length() == 1) {
1421     if (!args[0].isObject()) {
1422       RootedObject callee(cx, &args.callee());
1423       ReportUsageErrorASCII(cx, callee, "Expected object");
1424       return false;
1425     }
1426 
1427     JSObject* obj = UncheckedUnwrap(&args[0].toObject());
1428     state = gc::StateName(obj->zone()->gcState());
1429   } else {
1430     state = gc::StateName(cx->runtime()->gc.state());
1431   }
1432 
1433   return ReturnStringCopy(cx, args, state);
1434 }
1435 
ScheduleZoneForGC(JSContext * cx,unsigned argc,Value * vp)1436 static bool ScheduleZoneForGC(JSContext* cx, unsigned argc, Value* vp) {
1437   CallArgs args = CallArgsFromVp(argc, vp);
1438 
1439   if (args.length() != 1) {
1440     RootedObject callee(cx, &args.callee());
1441     ReportUsageErrorASCII(cx, callee, "Expecting a single argument");
1442     return false;
1443   }
1444 
1445   if (args[0].isObject()) {
1446     // Ensure that |zone| is collected during the next GC.
1447     Zone* zone = UncheckedUnwrap(&args[0].toObject())->zone();
1448     PrepareZoneForGC(cx, zone);
1449   } else if (args[0].isString()) {
1450     // This allows us to schedule the atoms zone for GC.
1451     Zone* zone = args[0].toString()->zoneFromAnyThread();
1452     if (!CurrentThreadCanAccessZone(zone)) {
1453       RootedObject callee(cx, &args.callee());
1454       ReportUsageErrorASCII(cx, callee, "Specified zone not accessible for GC");
1455       return false;
1456     }
1457     PrepareZoneForGC(cx, zone);
1458   } else {
1459     RootedObject callee(cx, &args.callee());
1460     ReportUsageErrorASCII(cx, callee,
1461                           "Bad argument - expecting object or string");
1462     return false;
1463   }
1464 
1465   args.rval().setUndefined();
1466   return true;
1467 }
1468 
StartGC(JSContext * cx,unsigned argc,Value * vp)1469 static bool StartGC(JSContext* cx, unsigned argc, Value* vp) {
1470   CallArgs args = CallArgsFromVp(argc, vp);
1471 
1472   if (args.length() > 2) {
1473     RootedObject callee(cx, &args.callee());
1474     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1475     return false;
1476   }
1477 
1478   auto budget = SliceBudget::unlimited();
1479   if (args.length() >= 1) {
1480     uint32_t work = 0;
1481     if (!ToUint32(cx, args[0], &work)) {
1482       return false;
1483     }
1484     budget = SliceBudget(WorkBudget(work));
1485   }
1486 
1487   bool shrinking = false;
1488   if (args.length() >= 2) {
1489     Value arg = args[1];
1490     if (arg.isString()) {
1491       if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking",
1492                                   &shrinking)) {
1493         return false;
1494       }
1495     }
1496   }
1497 
1498   JSRuntime* rt = cx->runtime();
1499   if (rt->gc.isIncrementalGCInProgress()) {
1500     RootedObject callee(cx, &args.callee());
1501     JS_ReportErrorASCII(cx, "Incremental GC already in progress");
1502     return false;
1503   }
1504 
1505   JSGCInvocationKind gckind = shrinking ? GC_SHRINK : GC_NORMAL;
1506   rt->gc.startDebugGC(gckind, budget);
1507 
1508   args.rval().setUndefined();
1509   return true;
1510 }
1511 
FinishGC(JSContext * cx,unsigned argc,Value * vp)1512 static bool FinishGC(JSContext* cx, unsigned argc, Value* vp) {
1513   CallArgs args = CallArgsFromVp(argc, vp);
1514 
1515   if (args.length() > 0) {
1516     RootedObject callee(cx, &args.callee());
1517     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1518     return false;
1519   }
1520 
1521   JSRuntime* rt = cx->runtime();
1522   if (rt->gc.isIncrementalGCInProgress()) {
1523     rt->gc.finishGC(JS::GCReason::DEBUG_GC);
1524   }
1525 
1526   args.rval().setUndefined();
1527   return true;
1528 }
1529 
GCSlice(JSContext * cx,unsigned argc,Value * vp)1530 static bool GCSlice(JSContext* cx, unsigned argc, Value* vp) {
1531   CallArgs args = CallArgsFromVp(argc, vp);
1532 
1533   if (args.length() > 1) {
1534     RootedObject callee(cx, &args.callee());
1535     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1536     return false;
1537   }
1538 
1539   auto budget = SliceBudget::unlimited();
1540   if (args.length() == 1) {
1541     uint32_t work = 0;
1542     if (!ToUint32(cx, args[0], &work)) {
1543       return false;
1544     }
1545     budget = SliceBudget(WorkBudget(work));
1546   }
1547 
1548   JSRuntime* rt = cx->runtime();
1549   if (!rt->gc.isIncrementalGCInProgress()) {
1550     rt->gc.startDebugGC(GC_NORMAL, budget);
1551   } else {
1552     rt->gc.debugGCSlice(budget);
1553   }
1554 
1555   args.rval().setUndefined();
1556   return true;
1557 }
1558 
AbortGC(JSContext * cx,unsigned argc,Value * vp)1559 static bool AbortGC(JSContext* cx, unsigned argc, Value* vp) {
1560   CallArgs args = CallArgsFromVp(argc, vp);
1561 
1562   if (args.length() != 0) {
1563     RootedObject callee(cx, &args.callee());
1564     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1565     return false;
1566   }
1567 
1568   JS::AbortIncrementalGC(cx);
1569   args.rval().setUndefined();
1570   return true;
1571 }
1572 
FullCompartmentChecks(JSContext * cx,unsigned argc,Value * vp)1573 static bool FullCompartmentChecks(JSContext* cx, unsigned argc, Value* vp) {
1574   CallArgs args = CallArgsFromVp(argc, vp);
1575 
1576   if (args.length() != 1) {
1577     RootedObject callee(cx, &args.callee());
1578     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1579     return false;
1580   }
1581 
1582   cx->runtime()->gc.setFullCompartmentChecks(ToBoolean(args[0]));
1583   args.rval().setUndefined();
1584   return true;
1585 }
1586 
NondeterministicGetWeakMapKeys(JSContext * cx,unsigned argc,Value * vp)1587 static bool NondeterministicGetWeakMapKeys(JSContext* cx, unsigned argc,
1588                                            Value* vp) {
1589   CallArgs args = CallArgsFromVp(argc, vp);
1590 
1591   if (args.length() != 1) {
1592     RootedObject callee(cx, &args.callee());
1593     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1594     return false;
1595   }
1596   if (!args[0].isObject()) {
1597     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1598                               JSMSG_NOT_EXPECTED_TYPE,
1599                               "nondeterministicGetWeakMapKeys", "WeakMap",
1600                               InformalValueTypeName(args[0]));
1601     return false;
1602   }
1603   RootedObject arr(cx);
1604   RootedObject mapObj(cx, &args[0].toObject());
1605   if (!JS_NondeterministicGetWeakMapKeys(cx, mapObj, &arr)) {
1606     return false;
1607   }
1608   if (!arr) {
1609     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1610                               JSMSG_NOT_EXPECTED_TYPE,
1611                               "nondeterministicGetWeakMapKeys", "WeakMap",
1612                               args[0].toObject().getClass()->name);
1613     return false;
1614   }
1615   args.rval().setObject(*arr);
1616   return true;
1617 }
1618 
1619 class HasChildTracer final : public JS::CallbackTracer {
1620   RootedValue child_;
1621   bool found_;
1622 
onChild(const JS::GCCellPtr & thing)1623   bool onChild(const JS::GCCellPtr& thing) override {
1624     if (thing.asCell() == child_.toGCThing()) {
1625       found_ = true;
1626     }
1627     return true;
1628   }
1629 
1630  public:
HasChildTracer(JSContext * cx,HandleValue child)1631   HasChildTracer(JSContext* cx, HandleValue child)
1632       : JS::CallbackTracer(cx, TraceWeakMapKeysValues),
1633         child_(cx, child),
1634         found_(false) {}
1635 
found() const1636   bool found() const { return found_; }
1637 };
1638 
HasChild(JSContext * cx,unsigned argc,Value * vp)1639 static bool HasChild(JSContext* cx, unsigned argc, Value* vp) {
1640   CallArgs args = CallArgsFromVp(argc, vp);
1641   RootedValue parent(cx, args.get(0));
1642   RootedValue child(cx, args.get(1));
1643 
1644   if (!parent.isGCThing() || !child.isGCThing()) {
1645     args.rval().setBoolean(false);
1646     return true;
1647   }
1648 
1649   HasChildTracer trc(cx, child);
1650   TraceChildren(&trc, parent.toGCThing(), parent.traceKind());
1651   args.rval().setBoolean(trc.found());
1652   return true;
1653 }
1654 
SetSavedStacksRNGState(JSContext * cx,unsigned argc,Value * vp)1655 static bool SetSavedStacksRNGState(JSContext* cx, unsigned argc, Value* vp) {
1656   CallArgs args = CallArgsFromVp(argc, vp);
1657   if (!args.requireAtLeast(cx, "setSavedStacksRNGState", 1)) {
1658     return false;
1659   }
1660 
1661   int32_t seed;
1662   if (!ToInt32(cx, args[0], &seed)) {
1663     return false;
1664   }
1665 
1666   // Either one or the other of the seed arguments must be non-zero;
1667   // make this true no matter what value 'seed' has.
1668   cx->realm()->savedStacks().setRNGState(seed, (seed + 1) * 33);
1669   return true;
1670 }
1671 
GetSavedFrameCount(JSContext * cx,unsigned argc,Value * vp)1672 static bool GetSavedFrameCount(JSContext* cx, unsigned argc, Value* vp) {
1673   CallArgs args = CallArgsFromVp(argc, vp);
1674   args.rval().setNumber(cx->realm()->savedStacks().count());
1675   return true;
1676 }
1677 
ClearSavedFrames(JSContext * cx,unsigned argc,Value * vp)1678 static bool ClearSavedFrames(JSContext* cx, unsigned argc, Value* vp) {
1679   CallArgs args = CallArgsFromVp(argc, vp);
1680 
1681   js::SavedStacks& savedStacks = cx->realm()->savedStacks();
1682   savedStacks.clear();
1683 
1684   for (ActivationIterator iter(cx); !iter.done(); ++iter) {
1685     iter->clearLiveSavedFrameCache();
1686   }
1687 
1688   args.rval().setUndefined();
1689   return true;
1690 }
1691 
SaveStack(JSContext * cx,unsigned argc,Value * vp)1692 static bool SaveStack(JSContext* cx, unsigned argc, Value* vp) {
1693   CallArgs args = CallArgsFromVp(argc, vp);
1694 
1695   JS::StackCapture capture((JS::AllFrames()));
1696   if (args.length() >= 1) {
1697     double maxDouble;
1698     if (!ToNumber(cx, args[0], &maxDouble)) {
1699       return false;
1700     }
1701     if (mozilla::IsNaN(maxDouble) || maxDouble < 0 || maxDouble > UINT32_MAX) {
1702       ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
1703                        nullptr, "not a valid maximum frame count");
1704       return false;
1705     }
1706     uint32_t max = uint32_t(maxDouble);
1707     if (max > 0) {
1708       capture = JS::StackCapture(JS::MaxFrames(max));
1709     }
1710   }
1711 
1712   RootedObject compartmentObject(cx);
1713   if (args.length() >= 2) {
1714     if (!args[1].isObject()) {
1715       ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
1716                        nullptr, "not an object");
1717       return false;
1718     }
1719     compartmentObject = UncheckedUnwrap(&args[1].toObject());
1720     if (!compartmentObject) {
1721       return false;
1722     }
1723   }
1724 
1725   RootedObject stack(cx);
1726   {
1727     Maybe<AutoRealm> ar;
1728     if (compartmentObject) {
1729       ar.emplace(cx, compartmentObject);
1730     }
1731     if (!JS::CaptureCurrentStack(cx, &stack, std::move(capture))) {
1732       return false;
1733     }
1734   }
1735 
1736   if (stack && !cx->compartment()->wrap(cx, &stack)) {
1737     return false;
1738   }
1739 
1740   args.rval().setObjectOrNull(stack);
1741   return true;
1742 }
1743 
CaptureFirstSubsumedFrame(JSContext * cx,unsigned argc,JS::Value * vp)1744 static bool CaptureFirstSubsumedFrame(JSContext* cx, unsigned argc,
1745                                       JS::Value* vp) {
1746   CallArgs args = CallArgsFromVp(argc, vp);
1747   if (!args.requireAtLeast(cx, "captureFirstSubsumedFrame", 1)) {
1748     return false;
1749   }
1750 
1751   if (!args[0].isObject()) {
1752     JS_ReportErrorASCII(cx, "The argument must be an object");
1753     return false;
1754   }
1755 
1756   RootedObject obj(cx, &args[0].toObject());
1757   obj = CheckedUnwrapStatic(obj);
1758   if (!obj) {
1759     JS_ReportErrorASCII(cx, "Denied permission to object.");
1760     return false;
1761   }
1762 
1763   JS::StackCapture capture(
1764       JS::FirstSubsumedFrame(cx, obj->nonCCWRealm()->principals()));
1765   if (args.length() > 1) {
1766     capture.as<JS::FirstSubsumedFrame>().ignoreSelfHosted =
1767         JS::ToBoolean(args[1]);
1768   }
1769 
1770   JS::RootedObject capturedStack(cx);
1771   if (!JS::CaptureCurrentStack(cx, &capturedStack, std::move(capture))) {
1772     return false;
1773   }
1774 
1775   args.rval().setObjectOrNull(capturedStack);
1776   return true;
1777 }
1778 
CallFunctionFromNativeFrame(JSContext * cx,unsigned argc,Value * vp)1779 static bool CallFunctionFromNativeFrame(JSContext* cx, unsigned argc,
1780                                         Value* vp) {
1781   CallArgs args = CallArgsFromVp(argc, vp);
1782 
1783   if (args.length() != 1) {
1784     JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
1785     return false;
1786   }
1787   if (!args[0].isObject() || !IsCallable(args[0])) {
1788     JS_ReportErrorASCII(cx, "The first argument should be a function.");
1789     return false;
1790   }
1791 
1792   RootedObject function(cx, &args[0].toObject());
1793   return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(),
1794               args.rval());
1795 }
1796 
CallFunctionWithAsyncStack(JSContext * cx,unsigned argc,Value * vp)1797 static bool CallFunctionWithAsyncStack(JSContext* cx, unsigned argc,
1798                                        Value* vp) {
1799   CallArgs args = CallArgsFromVp(argc, vp);
1800 
1801   if (args.length() != 3) {
1802     JS_ReportErrorASCII(cx, "The function takes exactly three arguments.");
1803     return false;
1804   }
1805   if (!args[0].isObject() || !IsCallable(args[0])) {
1806     JS_ReportErrorASCII(cx, "The first argument should be a function.");
1807     return false;
1808   }
1809   if (!args[1].isObject() || !args[1].toObject().is<SavedFrame>()) {
1810     JS_ReportErrorASCII(cx, "The second argument should be a SavedFrame.");
1811     return false;
1812   }
1813   if (!args[2].isString() || args[2].toString()->empty()) {
1814     JS_ReportErrorASCII(cx, "The third argument should be a non-empty string.");
1815     return false;
1816   }
1817 
1818   RootedObject function(cx, &args[0].toObject());
1819   RootedObject stack(cx, &args[1].toObject());
1820   RootedString asyncCause(cx, args[2].toString());
1821   UniqueChars utf8Cause = JS_EncodeStringToUTF8(cx, asyncCause);
1822   if (!utf8Cause) {
1823     MOZ_ASSERT(cx->isExceptionPending());
1824     return false;
1825   }
1826 
1827   JS::AutoSetAsyncStackForNewCalls sas(
1828       cx, stack, utf8Cause.get(),
1829       JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
1830   return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(),
1831               args.rval());
1832 }
1833 
EnableTrackAllocations(JSContext * cx,unsigned argc,Value * vp)1834 static bool EnableTrackAllocations(JSContext* cx, unsigned argc, Value* vp) {
1835   SetAllocationMetadataBuilder(cx, &SavedStacks::metadataBuilder);
1836   return true;
1837 }
1838 
DisableTrackAllocations(JSContext * cx,unsigned argc,Value * vp)1839 static bool DisableTrackAllocations(JSContext* cx, unsigned argc, Value* vp) {
1840   SetAllocationMetadataBuilder(cx, nullptr);
1841   return true;
1842 }
1843 
SetTestFilenameValidationCallback(JSContext * cx,unsigned argc,Value * vp)1844 static bool SetTestFilenameValidationCallback(JSContext* cx, unsigned argc,
1845                                               Value* vp) {
1846   CallArgs args = CallArgsFromVp(argc, vp);
1847 
1848   // Accept all filenames that start with "safe". In system code also accept
1849   // filenames starting with "system".
1850   auto testCb = [](const char* filename, bool isSystemRealm) -> bool {
1851     if (strstr(filename, "safe") == filename) {
1852       return true;
1853     }
1854     if (isSystemRealm && strstr(filename, "system") == filename) {
1855       return true;
1856     }
1857     return false;
1858   };
1859   JS::SetFilenameValidationCallback(testCb);
1860 
1861   args.rval().setUndefined();
1862   return true;
1863 }
1864 
1865 struct TestExternalString : public JSExternalStringCallbacks {
finalizeTestExternalString1866   void finalize(char16_t* chars) const override { js_free(chars); }
sizeOfBufferTestExternalString1867   size_t sizeOfBuffer(const char16_t* chars,
1868                       mozilla::MallocSizeOf mallocSizeOf) const override {
1869     return mallocSizeOf(chars);
1870   }
1871 };
1872 
1873 static constexpr TestExternalString TestExternalStringCallbacks;
1874 
NewExternalString(JSContext * cx,unsigned argc,Value * vp)1875 static bool NewExternalString(JSContext* cx, unsigned argc, Value* vp) {
1876   CallArgs args = CallArgsFromVp(argc, vp);
1877 
1878   if (args.length() != 1 || !args[0].isString()) {
1879     JS_ReportErrorASCII(cx,
1880                         "newExternalString takes exactly one string argument.");
1881     return false;
1882   }
1883 
1884   RootedString str(cx, args[0].toString());
1885   size_t len = str->length();
1886 
1887   auto buf = cx->make_pod_array<char16_t>(len);
1888   if (!buf) {
1889     return false;
1890   }
1891 
1892   if (!JS_CopyStringChars(cx, mozilla::Range<char16_t>(buf.get(), len), str)) {
1893     return false;
1894   }
1895 
1896   JSString* res =
1897       JS_NewExternalString(cx, buf.get(), len, &TestExternalStringCallbacks);
1898   if (!res) {
1899     return false;
1900   }
1901 
1902   mozilla::Unused << buf.release();
1903   args.rval().setString(res);
1904   return true;
1905 }
1906 
NewMaybeExternalString(JSContext * cx,unsigned argc,Value * vp)1907 static bool NewMaybeExternalString(JSContext* cx, unsigned argc, Value* vp) {
1908   CallArgs args = CallArgsFromVp(argc, vp);
1909 
1910   if (args.length() != 1 || !args[0].isString()) {
1911     JS_ReportErrorASCII(
1912         cx, "newMaybeExternalString takes exactly one string argument.");
1913     return false;
1914   }
1915 
1916   RootedString str(cx, args[0].toString());
1917   size_t len = str->length();
1918 
1919   auto buf = cx->make_pod_array<char16_t>(len);
1920   if (!buf) {
1921     return false;
1922   }
1923 
1924   if (!JS_CopyStringChars(cx, mozilla::Range<char16_t>(buf.get(), len), str)) {
1925     return false;
1926   }
1927 
1928   bool allocatedExternal;
1929   JSString* res = JS_NewMaybeExternalString(
1930       cx, buf.get(), len, &TestExternalStringCallbacks, &allocatedExternal);
1931   if (!res) {
1932     return false;
1933   }
1934 
1935   if (allocatedExternal) {
1936     mozilla::Unused << buf.release();
1937   }
1938   args.rval().setString(res);
1939   return true;
1940 }
1941 
1942 // Warning! This will let you create ropes that I'm not sure would be possible
1943 // otherwise, specifically:
1944 //
1945 //   - a rope with a zero-length child
1946 //   - a rope that would fit into an inline string
1947 //
NewRope(JSContext * cx,unsigned argc,Value * vp)1948 static bool NewRope(JSContext* cx, unsigned argc, Value* vp) {
1949   CallArgs args = CallArgsFromVp(argc, vp);
1950 
1951   if (!args.get(0).isString() || !args.get(1).isString()) {
1952     JS_ReportErrorASCII(cx, "newRope requires two string arguments.");
1953     return false;
1954   }
1955 
1956   gc::InitialHeap heap = js::gc::DefaultHeap;
1957   if (args.get(2).isObject()) {
1958     RootedObject options(cx, &args[2].toObject());
1959     RootedValue v(cx);
1960     if (!JS_GetProperty(cx, options, "nursery", &v)) {
1961       return false;
1962     }
1963     if (!v.isUndefined() && !ToBoolean(v)) {
1964       heap = js::gc::TenuredHeap;
1965     }
1966   }
1967 
1968   RootedString left(cx, args[0].toString());
1969   RootedString right(cx, args[1].toString());
1970   size_t length = JS_GetStringLength(left) + JS_GetStringLength(right);
1971   if (length > JSString::MAX_LENGTH) {
1972     JS_ReportErrorASCII(cx, "rope length exceeds maximum string length");
1973     return false;
1974   }
1975 
1976   Rooted<JSRope*> str(cx, JSRope::new_<CanGC>(cx, left, right, length, heap));
1977   if (!str) {
1978     return false;
1979   }
1980 
1981   args.rval().setString(str);
1982   return true;
1983 }
1984 
IsRope(JSContext * cx,unsigned argc,Value * vp)1985 static bool IsRope(JSContext* cx, unsigned argc, Value* vp) {
1986   CallArgs args = CallArgsFromVp(argc, vp);
1987 
1988   if (!args.get(0).isString()) {
1989     JS_ReportErrorASCII(cx, "isRope requires a string argument.");
1990     return false;
1991   }
1992 
1993   JSString* str = args[0].toString();
1994   args.rval().setBoolean(str->isRope());
1995   return true;
1996 }
1997 
EnsureLinearString(JSContext * cx,unsigned argc,Value * vp)1998 static bool EnsureLinearString(JSContext* cx, unsigned argc, Value* vp) {
1999   CallArgs args = CallArgsFromVp(argc, vp);
2000 
2001   if (args.length() != 1 || !args[0].isString()) {
2002     JS_ReportErrorASCII(
2003         cx, "ensureLinearString takes exactly one string argument.");
2004     return false;
2005   }
2006 
2007   JSLinearString* linear = args[0].toString()->ensureLinear(cx);
2008   if (!linear) {
2009     return false;
2010   }
2011 
2012   args.rval().setString(linear);
2013   return true;
2014 }
2015 
RepresentativeStringArray(JSContext * cx,unsigned argc,Value * vp)2016 static bool RepresentativeStringArray(JSContext* cx, unsigned argc, Value* vp) {
2017   CallArgs args = CallArgsFromVp(argc, vp);
2018 
2019   RootedObject array(cx, JS::NewArrayObject(cx, 0));
2020   if (!array) {
2021     return false;
2022   }
2023 
2024   if (!JSString::fillWithRepresentatives(cx, array.as<ArrayObject>())) {
2025     return false;
2026   }
2027 
2028   args.rval().setObject(*array);
2029   return true;
2030 }
2031 
2032 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
2033 
OOMThreadTypes(JSContext * cx,unsigned argc,Value * vp)2034 static bool OOMThreadTypes(JSContext* cx, unsigned argc, Value* vp) {
2035   CallArgs args = CallArgsFromVp(argc, vp);
2036   args.rval().setInt32(js::THREAD_TYPE_MAX);
2037   return true;
2038 }
2039 
CheckCanSimulateOOM(JSContext * cx)2040 static bool CheckCanSimulateOOM(JSContext* cx) {
2041   if (js::oom::GetThreadType() != js::THREAD_TYPE_MAIN) {
2042     JS_ReportErrorASCII(
2043         cx, "Simulated OOM failure is only supported on the main thread");
2044     return false;
2045   }
2046 
2047   return true;
2048 }
2049 
SetupOOMFailure(JSContext * cx,bool failAlways,unsigned argc,Value * vp)2050 static bool SetupOOMFailure(JSContext* cx, bool failAlways, unsigned argc,
2051                             Value* vp) {
2052   CallArgs args = CallArgsFromVp(argc, vp);
2053 
2054   if (disableOOMFunctions) {
2055     args.rval().setUndefined();
2056     return true;
2057   }
2058 
2059   if (args.length() < 1) {
2060     JS_ReportErrorASCII(cx, "Count argument required");
2061     return false;
2062   }
2063 
2064   if (args.length() > 2) {
2065     JS_ReportErrorASCII(cx, "Too many arguments");
2066     return false;
2067   }
2068 
2069   int32_t count;
2070   if (!JS::ToInt32(cx, args.get(0), &count)) {
2071     return false;
2072   }
2073 
2074   if (count <= 0) {
2075     JS_ReportErrorASCII(cx, "OOM cutoff should be positive");
2076     return false;
2077   }
2078 
2079   uint32_t targetThread = js::THREAD_TYPE_MAIN;
2080   if (args.length() > 1 && !ToUint32(cx, args[1], &targetThread)) {
2081     return false;
2082   }
2083 
2084   if (targetThread == js::THREAD_TYPE_NONE ||
2085       targetThread == js::THREAD_TYPE_WORKER ||
2086       targetThread >= js::THREAD_TYPE_MAX) {
2087     JS_ReportErrorASCII(cx, "Invalid thread type specified");
2088     return false;
2089   }
2090 
2091   if (!CheckCanSimulateOOM(cx)) {
2092     return false;
2093   }
2094 
2095   js::oom::simulator.simulateFailureAfter(js::oom::FailureSimulator::Kind::OOM,
2096                                           count, targetThread, failAlways);
2097   args.rval().setUndefined();
2098   return true;
2099 }
2100 
OOMAfterAllocations(JSContext * cx,unsigned argc,Value * vp)2101 static bool OOMAfterAllocations(JSContext* cx, unsigned argc, Value* vp) {
2102   return SetupOOMFailure(cx, true, argc, vp);
2103 }
2104 
OOMAtAllocation(JSContext * cx,unsigned argc,Value * vp)2105 static bool OOMAtAllocation(JSContext* cx, unsigned argc, Value* vp) {
2106   return SetupOOMFailure(cx, false, argc, vp);
2107 }
2108 
ResetOOMFailure(JSContext * cx,unsigned argc,Value * vp)2109 static bool ResetOOMFailure(JSContext* cx, unsigned argc, Value* vp) {
2110   CallArgs args = CallArgsFromVp(argc, vp);
2111 
2112   if (!CheckCanSimulateOOM(cx)) {
2113     return false;
2114   }
2115 
2116   args.rval().setBoolean(js::oom::HadSimulatedOOM());
2117   js::oom::simulator.reset();
2118   return true;
2119 }
2120 
CountCompartments(JSContext * cx)2121 static size_t CountCompartments(JSContext* cx) {
2122   size_t count = 0;
2123   for (auto zone : cx->runtime()->gc.zones()) {
2124     count += zone->compartments().length();
2125   }
2126   return count;
2127 }
2128 
2129 // Iterative failure testing: test a function by simulating failures at indexed
2130 // locations throughout the normal execution path and checking that the
2131 // resulting state of the environment is consistent with the error result.
2132 //
2133 // For example, trigger OOM at every allocation point and test that the function
2134 // either recovers and succeeds or raises an exception and fails.
2135 
2136 struct MOZ_STACK_CLASS IterativeFailureTestParams {
IterativeFailureTestParamsIterativeFailureTestParams2137   explicit IterativeFailureTestParams(JSContext* cx) : testFunction(cx) {}
2138 
2139   RootedFunction testFunction;
2140   unsigned threadStart = 0;
2141   unsigned threadEnd = 0;
2142   bool expectExceptionOnFailure = true;
2143   bool keepFailing = false;
2144   bool verbose = false;
2145 };
2146 
2147 struct IterativeFailureSimulator {
setupIterativeFailureSimulator2148   virtual void setup(JSContext* cx) {}
teardownIterativeFailureSimulator2149   virtual void teardown(JSContext* cx) {}
2150   virtual void startSimulating(JSContext* cx, unsigned iteration,
2151                                unsigned thread, bool keepFailing) = 0;
2152   virtual bool stopSimulating() = 0;
cleanupIterativeFailureSimulator2153   virtual void cleanup(JSContext* cx) {}
2154 };
2155 
RunIterativeFailureTest(JSContext * cx,const IterativeFailureTestParams & params,IterativeFailureSimulator & simulator)2156 bool RunIterativeFailureTest(JSContext* cx,
2157                              const IterativeFailureTestParams& params,
2158                              IterativeFailureSimulator& simulator) {
2159   if (disableOOMFunctions) {
2160     return true;
2161   }
2162 
2163   if (!CheckCanSimulateOOM(cx)) {
2164     return false;
2165   }
2166 
2167   // Disallow nested tests.
2168   if (cx->runningOOMTest) {
2169     JS_ReportErrorASCII(
2170         cx, "Nested call to iterative failure test is not allowed.");
2171     return false;
2172   }
2173   cx->runningOOMTest = true;
2174 
2175   MOZ_ASSERT(!cx->isExceptionPending());
2176 
2177 #  ifdef JS_GC_ZEAL
2178   JS_SetGCZeal(cx, 0, JS_DEFAULT_ZEAL_FREQ);
2179 #  endif
2180 
2181   // Delazify the function here if necessary so we don't end up testing that.
2182   if (params.testFunction->isInterpreted() &&
2183       !JSFunction::getOrCreateScript(cx, params.testFunction)) {
2184     return false;
2185   }
2186 
2187   size_t compartmentCount = CountCompartments(cx);
2188 
2189   RootedValue exception(cx);
2190 
2191   simulator.setup(cx);
2192 
2193   for (unsigned thread = params.threadStart; thread <= params.threadEnd;
2194        thread++) {
2195     if (params.verbose) {
2196       fprintf(stderr, "thread %u\n", thread);
2197     }
2198 
2199     unsigned iteration = 1;
2200     bool failureWasSimulated;
2201     do {
2202       if (params.verbose) {
2203         fprintf(stderr, "  iteration %u\n", iteration);
2204       }
2205 
2206       MOZ_ASSERT(!cx->isExceptionPending());
2207 
2208       simulator.startSimulating(cx, iteration, thread, params.keepFailing);
2209 
2210       RootedValue result(cx);
2211       bool ok = JS_CallFunction(cx, cx->global(), params.testFunction,
2212                                 HandleValueArray::empty(), &result);
2213 
2214       failureWasSimulated = simulator.stopSimulating();
2215 
2216       if (ok) {
2217         MOZ_ASSERT(!cx->isExceptionPending(),
2218                    "Thunk execution succeeded but an exception was raised - "
2219                    "missing error check?");
2220       } else if (params.expectExceptionOnFailure) {
2221         MOZ_ASSERT(cx->isExceptionPending(),
2222                    "Thunk execution failed but no exception was raised - "
2223                    "missing call to js::ReportOutOfMemory()?");
2224       }
2225 
2226       // Note that it is possible that the function throws an exception
2227       // unconnected to the simulated failure, in which case we ignore
2228       // it. More correct would be to have the caller pass some kind of
2229       // exception specification and to check the exception against it.
2230 
2231       if (!failureWasSimulated && cx->isExceptionPending()) {
2232         if (!cx->getPendingException(&exception)) {
2233           return false;
2234         }
2235       }
2236       cx->clearPendingException();
2237       simulator.cleanup(cx);
2238 
2239       gc::FinishGC(cx);
2240 
2241       // Some tests create a new compartment or zone on every
2242       // iteration. Our GC is triggered by GC allocations and not by
2243       // number of compartments or zones, so these won't normally get
2244       // cleaned up. The check here stops some tests running out of
2245       // memory. ("Gentlemen, you can't fight in here! This is the
2246       // War oom!")
2247       if (CountCompartments(cx) > compartmentCount + 100) {
2248         JS_GC(cx);
2249         compartmentCount = CountCompartments(cx);
2250       }
2251 
2252 #  ifdef JS_TRACE_LOGGING
2253       // Reset the TraceLogger state if enabled.
2254       TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
2255       if (logger && logger->enabled()) {
2256         while (logger->enabled()) {
2257           logger->disable();
2258         }
2259         logger->enable(cx);
2260       }
2261 #  endif
2262 
2263       iteration++;
2264     } while (failureWasSimulated);
2265 
2266     if (params.verbose) {
2267       fprintf(stderr, "  finished after %u iterations\n", iteration - 1);
2268       if (!exception.isUndefined()) {
2269         RootedString str(cx, JS::ToString(cx, exception));
2270         if (!str) {
2271           fprintf(stderr,
2272                   "  error while trying to print exception, giving up\n");
2273           return false;
2274         }
2275         UniqueChars bytes(JS_EncodeStringToLatin1(cx, str));
2276         if (!bytes) {
2277           return false;
2278         }
2279         fprintf(stderr, "  threw %s\n", bytes.get());
2280       }
2281     }
2282   }
2283 
2284   simulator.teardown(cx);
2285 
2286   cx->runningOOMTest = false;
2287   return true;
2288 }
2289 
ParseIterativeFailureTestParams(JSContext * cx,const CallArgs & args,IterativeFailureTestParams * params)2290 bool ParseIterativeFailureTestParams(JSContext* cx, const CallArgs& args,
2291                                      IterativeFailureTestParams* params) {
2292   MOZ_ASSERT(params);
2293 
2294   if (args.length() < 1 || args.length() > 2) {
2295     JS_ReportErrorASCII(cx, "function takes between 1 and 2 arguments.");
2296     return false;
2297   }
2298 
2299   if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
2300     JS_ReportErrorASCII(cx, "The first argument must be the function to test.");
2301     return false;
2302   }
2303   params->testFunction = &args[0].toObject().as<JSFunction>();
2304 
2305   if (args.length() == 2) {
2306     if (args[1].isBoolean()) {
2307       params->expectExceptionOnFailure = args[1].toBoolean();
2308     } else if (args[1].isObject()) {
2309       RootedObject options(cx, &args[1].toObject());
2310       RootedValue value(cx);
2311 
2312       if (!JS_GetProperty(cx, options, "expectExceptionOnFailure", &value)) {
2313         return false;
2314       }
2315       if (!value.isUndefined()) {
2316         params->expectExceptionOnFailure = ToBoolean(value);
2317       }
2318 
2319       if (!JS_GetProperty(cx, options, "keepFailing", &value)) {
2320         return false;
2321       }
2322       if (!value.isUndefined()) {
2323         params->keepFailing = ToBoolean(value);
2324       }
2325     } else {
2326       JS_ReportErrorASCII(
2327           cx, "The optional second argument must be an object or a boolean.");
2328       return false;
2329     }
2330   }
2331 
2332   // There are some places where we do fail without raising an exception, so
2333   // we can't expose this to the fuzzers by default.
2334   if (fuzzingSafe) {
2335     params->expectExceptionOnFailure = false;
2336   }
2337 
2338   // Test all threads by default except worker threads.
2339   params->threadStart = oom::FirstThreadTypeToTest;
2340   params->threadEnd = oom::LastThreadTypeToTest;
2341 
2342   // Test a single thread type if specified by the OOM_THREAD environment
2343   // variable.
2344   int threadOption = 0;
2345   if (EnvVarAsInt("OOM_THREAD", &threadOption)) {
2346     if (threadOption < oom::FirstThreadTypeToTest ||
2347         threadOption > oom::LastThreadTypeToTest) {
2348       JS_ReportErrorASCII(cx, "OOM_THREAD value out of range.");
2349       return false;
2350     }
2351 
2352     params->threadStart = threadOption;
2353     params->threadEnd = threadOption;
2354   }
2355 
2356   params->verbose = EnvVarIsDefined("OOM_VERBOSE");
2357 
2358   return true;
2359 }
2360 
2361 struct OOMSimulator : public IterativeFailureSimulator {
setupOOMSimulator2362   void setup(JSContext* cx) override { cx->runtime()->hadOutOfMemory = false; }
2363 
startSimulatingOOMSimulator2364   void startSimulating(JSContext* cx, unsigned i, unsigned thread,
2365                        bool keepFailing) override {
2366     MOZ_ASSERT(!cx->runtime()->hadOutOfMemory);
2367     js::oom::simulator.simulateFailureAfter(
2368         js::oom::FailureSimulator::Kind::OOM, i, thread, keepFailing);
2369   }
2370 
stopSimulatingOOMSimulator2371   bool stopSimulating() override {
2372     bool handledOOM = js::oom::HadSimulatedOOM();
2373     js::oom::simulator.reset();
2374     return handledOOM;
2375   }
2376 
cleanupOOMSimulator2377   void cleanup(JSContext* cx) override {
2378     cx->runtime()->hadOutOfMemory = false;
2379   }
2380 };
2381 
OOMTest(JSContext * cx,unsigned argc,Value * vp)2382 static bool OOMTest(JSContext* cx, unsigned argc, Value* vp) {
2383   CallArgs args = CallArgsFromVp(argc, vp);
2384 
2385   IterativeFailureTestParams params(cx);
2386   if (!ParseIterativeFailureTestParams(cx, args, &params)) {
2387     return false;
2388   }
2389 
2390   OOMSimulator simulator;
2391   if (!RunIterativeFailureTest(cx, params, simulator)) {
2392     return false;
2393   }
2394 
2395   args.rval().setUndefined();
2396   return true;
2397 }
2398 
2399 struct StackOOMSimulator : public IterativeFailureSimulator {
startSimulatingStackOOMSimulator2400   void startSimulating(JSContext* cx, unsigned i, unsigned thread,
2401                        bool keepFailing) override {
2402     js::oom::simulator.simulateFailureAfter(
2403         js::oom::FailureSimulator::Kind::StackOOM, i, thread, keepFailing);
2404   }
2405 
stopSimulatingStackOOMSimulator2406   bool stopSimulating() override {
2407     bool handledOOM = js::oom::HadSimulatedStackOOM();
2408     js::oom::simulator.reset();
2409     return handledOOM;
2410   }
2411 };
2412 
StackTest(JSContext * cx,unsigned argc,Value * vp)2413 static bool StackTest(JSContext* cx, unsigned argc, Value* vp) {
2414   CallArgs args = CallArgsFromVp(argc, vp);
2415 
2416   IterativeFailureTestParams params(cx);
2417   if (!ParseIterativeFailureTestParams(cx, args, &params)) {
2418     return false;
2419   }
2420 
2421   StackOOMSimulator simulator;
2422   if (!RunIterativeFailureTest(cx, params, simulator)) {
2423     return false;
2424   }
2425 
2426   args.rval().setUndefined();
2427   return true;
2428 }
2429 
2430 struct FailingIterruptSimulator : public IterativeFailureSimulator {
2431   JSInterruptCallback* prevEnd = nullptr;
2432 
failingInterruptCallbackFailingIterruptSimulator2433   static bool failingInterruptCallback(JSContext* cx) { return false; }
2434 
setupFailingIterruptSimulator2435   void setup(JSContext* cx) override {
2436     prevEnd = cx->interruptCallbacks().end();
2437     JS_AddInterruptCallback(cx, failingInterruptCallback);
2438   }
2439 
teardownFailingIterruptSimulator2440   void teardown(JSContext* cx) override {
2441     cx->interruptCallbacks().erase(prevEnd, cx->interruptCallbacks().end());
2442   }
2443 
startSimulatingFailingIterruptSimulator2444   void startSimulating(JSContext* cx, unsigned i, unsigned thread,
2445                        bool keepFailing) override {
2446     js::oom::simulator.simulateFailureAfter(
2447         js::oom::FailureSimulator::Kind::Interrupt, i, thread, keepFailing);
2448   }
2449 
stopSimulatingFailingIterruptSimulator2450   bool stopSimulating() override {
2451     bool handledInterrupt = js::oom::HadSimulatedInterrupt();
2452     js::oom::simulator.reset();
2453     return handledInterrupt;
2454   }
2455 };
2456 
InterruptTest(JSContext * cx,unsigned argc,Value * vp)2457 static bool InterruptTest(JSContext* cx, unsigned argc, Value* vp) {
2458   CallArgs args = CallArgsFromVp(argc, vp);
2459 
2460   IterativeFailureTestParams params(cx);
2461   if (!ParseIterativeFailureTestParams(cx, args, &params)) {
2462     return false;
2463   }
2464 
2465   FailingIterruptSimulator simulator;
2466   if (!RunIterativeFailureTest(cx, params, simulator)) {
2467     return false;
2468   }
2469 
2470   args.rval().setUndefined();
2471   return true;
2472 }
2473 
2474 #endif  // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
2475 
SettlePromiseNow(JSContext * cx,unsigned argc,Value * vp)2476 static bool SettlePromiseNow(JSContext* cx, unsigned argc, Value* vp) {
2477   CallArgs args = CallArgsFromVp(argc, vp);
2478   if (!args.requireAtLeast(cx, "settlePromiseNow", 1)) {
2479     return false;
2480   }
2481   if (!args[0].isObject() || !args[0].toObject().is<PromiseObject>()) {
2482     JS_ReportErrorASCII(cx, "first argument must be a Promise object");
2483     return false;
2484   }
2485 
2486   Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
2487   if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
2488     JS_ReportErrorASCII(
2489         cx, "async function/generator's promise shouldn't be manually settled");
2490     return false;
2491   }
2492 
2493   if (promise->state() != JS::PromiseState::Pending) {
2494     JS_ReportErrorASCII(cx, "cannot settle an already-resolved promise");
2495     return false;
2496   }
2497 
2498   int32_t flags = promise->flags();
2499   promise->setFixedSlot(
2500       PromiseSlot_Flags,
2501       Int32Value(flags | PROMISE_FLAG_RESOLVED | PROMISE_FLAG_FULFILLED));
2502   promise->setFixedSlot(PromiseSlot_ReactionsOrResult, UndefinedValue());
2503 
2504   DebugAPI::onPromiseSettled(cx, promise);
2505   return true;
2506 }
2507 
GetWaitForAllPromise(JSContext * cx,unsigned argc,Value * vp)2508 static bool GetWaitForAllPromise(JSContext* cx, unsigned argc, Value* vp) {
2509   CallArgs args = CallArgsFromVp(argc, vp);
2510   if (!args.requireAtLeast(cx, "getWaitForAllPromise", 1)) {
2511     return false;
2512   }
2513   if (!args[0].isObject() || !args[0].toObject().is<ArrayObject>() ||
2514       args[0].toObject().as<NativeObject>().isIndexed()) {
2515     JS_ReportErrorASCII(
2516         cx, "first argument must be a dense Array of Promise objects");
2517     return false;
2518   }
2519   RootedNativeObject list(cx, &args[0].toObject().as<NativeObject>());
2520   RootedObjectVector promises(cx);
2521   uint32_t count = list->getDenseInitializedLength();
2522   if (!promises.resize(count)) {
2523     return false;
2524   }
2525 
2526   for (uint32_t i = 0; i < count; i++) {
2527     RootedValue elem(cx, list->getDenseElement(i));
2528     if (!elem.isObject() || !elem.toObject().is<PromiseObject>()) {
2529       JS_ReportErrorASCII(
2530           cx, "Each entry in the passed-in Array must be a Promise");
2531       return false;
2532     }
2533     promises[i].set(&elem.toObject());
2534   }
2535 
2536   RootedObject resultPromise(cx, JS::GetWaitForAllPromise(cx, promises));
2537   if (!resultPromise) {
2538     return false;
2539   }
2540 
2541   args.rval().set(ObjectValue(*resultPromise));
2542   return true;
2543 }
2544 
ResolvePromise(JSContext * cx,unsigned argc,Value * vp)2545 static bool ResolvePromise(JSContext* cx, unsigned argc, Value* vp) {
2546   CallArgs args = CallArgsFromVp(argc, vp);
2547   if (!args.requireAtLeast(cx, "resolvePromise", 2)) {
2548     return false;
2549   }
2550   if (!args[0].isObject() ||
2551       !UncheckedUnwrap(&args[0].toObject())->is<PromiseObject>()) {
2552     JS_ReportErrorASCII(
2553         cx, "first argument must be a maybe-wrapped Promise object");
2554     return false;
2555   }
2556 
2557   RootedObject promise(cx, &args[0].toObject());
2558   RootedValue resolution(cx, args[1]);
2559   mozilla::Maybe<AutoRealm> ar;
2560   if (IsWrapper(promise)) {
2561     promise = UncheckedUnwrap(promise);
2562     ar.emplace(cx, promise);
2563     if (!cx->compartment()->wrap(cx, &resolution)) {
2564       return false;
2565     }
2566   }
2567 
2568   if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
2569     JS_ReportErrorASCII(
2570         cx,
2571         "async function/generator's promise shouldn't be manually resolved");
2572     return false;
2573   }
2574 
2575   bool result = JS::ResolvePromise(cx, promise, resolution);
2576   if (result) {
2577     args.rval().setUndefined();
2578   }
2579   return result;
2580 }
2581 
RejectPromise(JSContext * cx,unsigned argc,Value * vp)2582 static bool RejectPromise(JSContext* cx, unsigned argc, Value* vp) {
2583   CallArgs args = CallArgsFromVp(argc, vp);
2584   if (!args.requireAtLeast(cx, "rejectPromise", 2)) {
2585     return false;
2586   }
2587   if (!args[0].isObject() ||
2588       !UncheckedUnwrap(&args[0].toObject())->is<PromiseObject>()) {
2589     JS_ReportErrorASCII(
2590         cx, "first argument must be a maybe-wrapped Promise object");
2591     return false;
2592   }
2593 
2594   RootedObject promise(cx, &args[0].toObject());
2595   RootedValue reason(cx, args[1]);
2596   mozilla::Maybe<AutoRealm> ar;
2597   if (IsWrapper(promise)) {
2598     promise = UncheckedUnwrap(promise);
2599     ar.emplace(cx, promise);
2600     if (!cx->compartment()->wrap(cx, &reason)) {
2601       return false;
2602     }
2603   }
2604 
2605   if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
2606     JS_ReportErrorASCII(
2607         cx,
2608         "async function/generator's promise shouldn't be manually rejected");
2609     return false;
2610   }
2611 
2612   bool result = JS::RejectPromise(cx, promise, reason);
2613   if (result) {
2614     args.rval().setUndefined();
2615   }
2616   return result;
2617 }
2618 
StreamsAreEnabled(JSContext * cx,unsigned argc,Value * vp)2619 static bool StreamsAreEnabled(JSContext* cx, unsigned argc, Value* vp) {
2620   CallArgs args = CallArgsFromVp(argc, vp);
2621   args.rval().setBoolean(cx->realm()->creationOptions().getStreamsEnabled());
2622   return true;
2623 }
2624 
2625 static unsigned finalizeCount = 0;
2626 
finalize_counter_finalize(JSFreeOp * fop,JSObject * obj)2627 static void finalize_counter_finalize(JSFreeOp* fop, JSObject* obj) {
2628   ++finalizeCount;
2629 }
2630 
2631 static const JSClassOps FinalizeCounterClassOps = {
2632     nullptr,                    // addProperty
2633     nullptr,                    // delProperty
2634     nullptr,                    // enumerate
2635     nullptr,                    // newEnumerate
2636     nullptr,                    // resolve
2637     nullptr,                    // mayResolve
2638     finalize_counter_finalize,  // finalize
2639     nullptr,                    // call
2640     nullptr,                    // hasInstance
2641     nullptr,                    // construct
2642     nullptr,                    // trace
2643 };
2644 
2645 static const JSClass FinalizeCounterClass = {
2646     "FinalizeCounter", JSCLASS_FOREGROUND_FINALIZE, &FinalizeCounterClassOps};
2647 
MakeFinalizeObserver(JSContext * cx,unsigned argc,Value * vp)2648 static bool MakeFinalizeObserver(JSContext* cx, unsigned argc, Value* vp) {
2649   CallArgs args = CallArgsFromVp(argc, vp);
2650 
2651   JSObject* obj =
2652       JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, nullptr);
2653   if (!obj) {
2654     return false;
2655   }
2656 
2657   args.rval().setObject(*obj);
2658   return true;
2659 }
2660 
FinalizeCount(JSContext * cx,unsigned argc,Value * vp)2661 static bool FinalizeCount(JSContext* cx, unsigned argc, Value* vp) {
2662   CallArgs args = CallArgsFromVp(argc, vp);
2663   args.rval().setInt32(finalizeCount);
2664   return true;
2665 }
2666 
ResetFinalizeCount(JSContext * cx,unsigned argc,Value * vp)2667 static bool ResetFinalizeCount(JSContext* cx, unsigned argc, Value* vp) {
2668   CallArgs args = CallArgsFromVp(argc, vp);
2669   finalizeCount = 0;
2670   args.rval().setUndefined();
2671   return true;
2672 }
2673 
DumpHeap(JSContext * cx,unsigned argc,Value * vp)2674 static bool DumpHeap(JSContext* cx, unsigned argc, Value* vp) {
2675   CallArgs args = CallArgsFromVp(argc, vp);
2676 
2677   FILE* dumpFile = stdout;
2678 
2679   if (args.length() > 1) {
2680     RootedObject callee(cx, &args.callee());
2681     ReportUsageErrorASCII(cx, callee, "Too many arguments");
2682     return false;
2683   }
2684 
2685   if (!args.get(0).isUndefined()) {
2686     RootedString str(cx, ToString(cx, args[0]));
2687     if (!str) {
2688       return false;
2689     }
2690     if (!fuzzingSafe) {
2691       UniqueChars fileNameBytes = JS_EncodeStringToLatin1(cx, str);
2692       if (!fileNameBytes) {
2693         return false;
2694       }
2695       dumpFile = fopen(fileNameBytes.get(), "w");
2696       if (!dumpFile) {
2697         fileNameBytes = JS_EncodeStringToLatin1(cx, str);
2698         if (!fileNameBytes) {
2699           return false;
2700         }
2701         JS_ReportErrorLatin1(cx, "can't open %s", fileNameBytes.get());
2702         return false;
2703       }
2704     }
2705   }
2706 
2707   js::DumpHeap(cx, dumpFile, js::IgnoreNurseryObjects);
2708 
2709   if (dumpFile != stdout) {
2710     fclose(dumpFile);
2711   }
2712 
2713   args.rval().setUndefined();
2714   return true;
2715 }
2716 
Terminate(JSContext * cx,unsigned arg,Value * vp)2717 static bool Terminate(JSContext* cx, unsigned arg, Value* vp) {
2718 #ifdef JS_MORE_DETERMINISTIC
2719   // Print a message to stderr in more-deterministic builds to help jsfunfuzz
2720   // find uncatchable-exception bugs.
2721   fprintf(stderr, "terminate called\n");
2722 #endif
2723 
2724   JS_ClearPendingException(cx);
2725   return false;
2726 }
2727 
ReadGeckoProfilingStack(JSContext * cx,unsigned argc,Value * vp)2728 static bool ReadGeckoProfilingStack(JSContext* cx, unsigned argc, Value* vp) {
2729   CallArgs args = CallArgsFromVp(argc, vp);
2730   args.rval().setUndefined();
2731 
2732   // Return boolean 'false' if profiler is not enabled.
2733   if (!cx->runtime()->geckoProfiler().enabled()) {
2734     args.rval().setBoolean(false);
2735     return true;
2736   }
2737 
2738   // Array holding physical jit stack frames.
2739   RootedObject stack(cx, NewDenseEmptyArray(cx));
2740   if (!stack) {
2741     return false;
2742   }
2743 
2744   // If profiler sampling has been suppressed, return an empty
2745   // stack.
2746   if (!cx->isProfilerSamplingEnabled()) {
2747     args.rval().setObject(*stack);
2748     return true;
2749   }
2750 
2751   struct InlineFrameInfo {
2752     InlineFrameInfo(const char* kind, UniqueChars label)
2753         : kind(kind), label(std::move(label)) {}
2754     const char* kind;
2755     UniqueChars label;
2756   };
2757 
2758   Vector<Vector<InlineFrameInfo, 0, TempAllocPolicy>, 0, TempAllocPolicy>
2759       frameInfo(cx);
2760 
2761   JS::ProfilingFrameIterator::RegisterState state;
2762   for (JS::ProfilingFrameIterator i(cx, state); !i.done(); ++i) {
2763     MOZ_ASSERT(i.stackAddress() != nullptr);
2764 
2765     if (!frameInfo.emplaceBack(cx)) {
2766       return false;
2767     }
2768 
2769     const size_t MaxInlineFrames = 16;
2770     JS::ProfilingFrameIterator::Frame frames[MaxInlineFrames];
2771     uint32_t nframes = i.extractStack(frames, 0, MaxInlineFrames);
2772     MOZ_ASSERT(nframes <= MaxInlineFrames);
2773     for (uint32_t i = 0; i < nframes; i++) {
2774       const char* frameKindStr = nullptr;
2775       switch (frames[i].kind) {
2776         case JS::ProfilingFrameIterator::Frame_BaselineInterpreter:
2777           frameKindStr = "baseline-interpreter";
2778           break;
2779         case JS::ProfilingFrameIterator::Frame_Baseline:
2780           frameKindStr = "baseline-jit";
2781           break;
2782         case JS::ProfilingFrameIterator::Frame_Ion:
2783           frameKindStr = "ion";
2784           break;
2785         case JS::ProfilingFrameIterator::Frame_Wasm:
2786           frameKindStr = "wasm";
2787           break;
2788         default:
2789           frameKindStr = "unknown";
2790       }
2791 
2792       UniqueChars label =
2793           DuplicateStringToArena(js::StringBufferArena, cx, frames[i].label);
2794       if (!label) {
2795         return false;
2796       }
2797 
2798       if (!frameInfo.back().emplaceBack(frameKindStr, std::move(label))) {
2799         return false;
2800       }
2801     }
2802   }
2803 
2804   RootedObject inlineFrameInfo(cx);
2805   RootedString frameKind(cx);
2806   RootedString frameLabel(cx);
2807   RootedId idx(cx);
2808 
2809   const unsigned propAttrs = JSPROP_ENUMERATE;
2810 
2811   uint32_t physicalFrameNo = 0;
2812   for (auto& frame : frameInfo) {
2813     // Array holding all inline frames in a single physical jit stack frame.
2814     RootedObject inlineStack(cx, NewDenseEmptyArray(cx));
2815     if (!inlineStack) {
2816       return false;
2817     }
2818 
2819     uint32_t inlineFrameNo = 0;
2820     for (auto& inlineFrame : frame) {
2821       // Object holding frame info.
2822       RootedObject inlineFrameInfo(cx,
2823                                    NewBuiltinClassInstance<PlainObject>(cx));
2824       if (!inlineFrameInfo) {
2825         return false;
2826       }
2827 
2828       frameKind = NewStringCopyZ<CanGC>(cx, inlineFrame.kind);
2829       if (!frameKind) {
2830         return false;
2831       }
2832 
2833       if (!JS_DefineProperty(cx, inlineFrameInfo, "kind", frameKind,
2834                              propAttrs)) {
2835         return false;
2836       }
2837 
2838       frameLabel = NewLatin1StringZ(cx, std::move(inlineFrame.label));
2839       if (!frameLabel) {
2840         return false;
2841       }
2842 
2843       if (!JS_DefineProperty(cx, inlineFrameInfo, "label", frameLabel,
2844                              propAttrs)) {
2845         return false;
2846       }
2847 
2848       idx = INT_TO_JSID(inlineFrameNo);
2849       if (!JS_DefinePropertyById(cx, inlineStack, idx, inlineFrameInfo, 0)) {
2850         return false;
2851       }
2852 
2853       ++inlineFrameNo;
2854     }
2855 
2856     // Push inline array into main array.
2857     idx = INT_TO_JSID(physicalFrameNo);
2858     if (!JS_DefinePropertyById(cx, stack, idx, inlineStack, 0)) {
2859       return false;
2860     }
2861 
2862     ++physicalFrameNo;
2863   }
2864 
2865   args.rval().setObject(*stack);
2866   return true;
2867 }
2868 
EnableOsiPointRegisterChecks(JSContext *,unsigned argc,Value * vp)2869 static bool EnableOsiPointRegisterChecks(JSContext*, unsigned argc, Value* vp) {
2870   CallArgs args = CallArgsFromVp(argc, vp);
2871 #ifdef CHECK_OSIPOINT_REGISTERS
2872   jit::JitOptions.checkOsiPointRegisters = true;
2873 #endif
2874   args.rval().setUndefined();
2875   return true;
2876 }
2877 
DisplayName(JSContext * cx,unsigned argc,Value * vp)2878 static bool DisplayName(JSContext* cx, unsigned argc, Value* vp) {
2879   CallArgs args = CallArgsFromVp(argc, vp);
2880   if (!args.get(0).isObject() || !args[0].toObject().is<JSFunction>()) {
2881     RootedObject arg(cx, &args.callee());
2882     ReportUsageErrorASCII(cx, arg, "Must have one function argument");
2883     return false;
2884   }
2885 
2886   JSFunction* fun = &args[0].toObject().as<JSFunction>();
2887   JSString* str = fun->displayAtom();
2888   args.rval().setString(str ? str : cx->runtime()->emptyString.ref());
2889   return true;
2890 }
2891 
2892 class ShellAllocationMetadataBuilder : public AllocationMetadataBuilder {
2893  public:
ShellAllocationMetadataBuilder()2894   ShellAllocationMetadataBuilder() : AllocationMetadataBuilder() {}
2895 
2896   virtual JSObject* build(JSContext* cx, HandleObject,
2897                           AutoEnterOOMUnsafeRegion& oomUnsafe) const override;
2898 
2899   static const ShellAllocationMetadataBuilder metadataBuilder;
2900 };
2901 
build(JSContext * cx,HandleObject,AutoEnterOOMUnsafeRegion & oomUnsafe) const2902 JSObject* ShellAllocationMetadataBuilder::build(
2903     JSContext* cx, HandleObject, AutoEnterOOMUnsafeRegion& oomUnsafe) const {
2904   RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
2905   if (!obj) {
2906     oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2907   }
2908 
2909   RootedObject stack(cx, NewDenseEmptyArray(cx));
2910   if (!stack) {
2911     oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2912   }
2913 
2914   static int createdIndex = 0;
2915   createdIndex++;
2916 
2917   if (!JS_DefineProperty(cx, obj, "index", createdIndex, 0)) {
2918     oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2919   }
2920 
2921   if (!JS_DefineProperty(cx, obj, "stack", stack, 0)) {
2922     oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2923   }
2924 
2925   int stackIndex = 0;
2926   RootedId id(cx);
2927   RootedValue callee(cx);
2928   for (NonBuiltinScriptFrameIter iter(cx); !iter.done(); ++iter) {
2929     if (iter.isFunctionFrame() && iter.compartment() == cx->compartment()) {
2930       id = INT_TO_JSID(stackIndex);
2931       RootedObject callee(cx, iter.callee(cx));
2932       if (!JS_DefinePropertyById(cx, stack, id, callee, 0)) {
2933         oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2934       }
2935       stackIndex++;
2936     }
2937   }
2938 
2939   return obj;
2940 }
2941 
2942 const ShellAllocationMetadataBuilder
2943     ShellAllocationMetadataBuilder::metadataBuilder;
2944 
EnableShellAllocationMetadataBuilder(JSContext * cx,unsigned argc,Value * vp)2945 static bool EnableShellAllocationMetadataBuilder(JSContext* cx, unsigned argc,
2946                                                  Value* vp) {
2947   CallArgs args = CallArgsFromVp(argc, vp);
2948 
2949   SetAllocationMetadataBuilder(
2950       cx, &ShellAllocationMetadataBuilder::metadataBuilder);
2951 
2952   args.rval().setUndefined();
2953   return true;
2954 }
2955 
GetAllocationMetadata(JSContext * cx,unsigned argc,Value * vp)2956 static bool GetAllocationMetadata(JSContext* cx, unsigned argc, Value* vp) {
2957   CallArgs args = CallArgsFromVp(argc, vp);
2958   if (args.length() != 1 || !args[0].isObject()) {
2959     JS_ReportErrorASCII(cx, "Argument must be an object");
2960     return false;
2961   }
2962 
2963   args.rval().setObjectOrNull(GetAllocationMetadata(&args[0].toObject()));
2964   return true;
2965 }
2966 
testingFunc_bailout(JSContext * cx,unsigned argc,Value * vp)2967 static bool testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp) {
2968   CallArgs args = CallArgsFromVp(argc, vp);
2969 
2970   // NOP when not in IonMonkey
2971   args.rval().setUndefined();
2972   return true;
2973 }
2974 
testingFunc_bailAfter(JSContext * cx,unsigned argc,Value * vp)2975 static bool testingFunc_bailAfter(JSContext* cx, unsigned argc, Value* vp) {
2976   CallArgs args = CallArgsFromVp(argc, vp);
2977   if (args.length() != 1 || !args[0].isInt32() || args[0].toInt32() < 0) {
2978     JS_ReportErrorASCII(
2979         cx, "Argument must be a positive number that fits in an int32");
2980     return false;
2981   }
2982 
2983 #ifdef DEBUG
2984   if (auto* jitRuntime = cx->runtime()->jitRuntime()) {
2985     uint32_t bailAfter = args[0].toInt32();
2986     bool enableBailAfter = bailAfter > 0;
2987     if (jitRuntime->ionBailAfterEnabled() != enableBailAfter) {
2988       // Force JIT code to be recompiled with (or without) instrumentation.
2989       ReleaseAllJITCode(cx->defaultFreeOp());
2990       jitRuntime->setIonBailAfterEnabled(enableBailAfter);
2991     }
2992     jitRuntime->setIonBailAfterCounter(bailAfter);
2993   }
2994 #endif
2995 
2996   args.rval().setUndefined();
2997   return true;
2998 }
2999 
3000 static constexpr unsigned JitWarmupResetLimit = 20;
3001 static_assert(JitWarmupResetLimit <=
3002                   unsigned(JSScript::MutableFlags::WarmupResets_MASK),
3003               "JitWarmupResetLimit exceeds max value");
3004 
testingFunc_inJit(JSContext * cx,unsigned argc,Value * vp)3005 static bool testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp) {
3006   CallArgs args = CallArgsFromVp(argc, vp);
3007 
3008   if (!jit::IsBaselineJitEnabled(cx)) {
3009     return ReturnStringCopy(cx, args, "Baseline is disabled.");
3010   }
3011 
3012   // Use frame iterator to inspect caller.
3013   FrameIter iter(cx);
3014 
3015   // We may be invoked directly, not in a JS context, e.g. if inJit is added as
3016   // a callback on the event queue.
3017   if (iter.done()) {
3018     args.rval().setBoolean(false);
3019     return true;
3020   }
3021 
3022   if (iter.hasScript()) {
3023     // Detect repeated attempts to compile, resetting the counter if inJit
3024     // succeeds. Note: This script may have be inlined into its caller.
3025     if (iter.isJSJit()) {
3026       iter.script()->resetWarmUpResetCounter();
3027     } else if (iter.script()->getWarmUpResetCount() >= JitWarmupResetLimit) {
3028       return ReturnStringCopy(
3029           cx, args, "Compilation is being repeatedly prevented. Giving up.");
3030     }
3031   }
3032 
3033   // Returns true for any JIT (including WASM).
3034   MOZ_ASSERT_IF(iter.isJSJit(), cx->currentlyRunningInJit());
3035   args.rval().setBoolean(cx->currentlyRunningInJit());
3036   return true;
3037 }
3038 
testingFunc_inIon(JSContext * cx,unsigned argc,Value * vp)3039 static bool testingFunc_inIon(JSContext* cx, unsigned argc, Value* vp) {
3040   CallArgs args = CallArgsFromVp(argc, vp);
3041 
3042   if (!jit::IsIonEnabled(cx)) {
3043     return ReturnStringCopy(cx, args, "Ion is disabled.");
3044   }
3045 
3046   // Use frame iterator to inspect caller.
3047   FrameIter iter(cx);
3048 
3049   // We may be invoked directly, not in a JS context, e.g. if inJson is added as
3050   // a callback on the event queue.
3051   if (iter.done()) {
3052     args.rval().setBoolean(false);
3053     return true;
3054   }
3055 
3056   if (iter.hasScript()) {
3057     // Detect repeated attempts to compile, resetting the counter if inIon
3058     // succeeds. Note: This script may have be inlined into its caller.
3059     if (iter.isIon()) {
3060       iter.script()->resetWarmUpResetCounter();
3061     } else if (!iter.script()->canIonCompile()) {
3062       return ReturnStringCopy(cx, args, "Unable to Ion-compile this script.");
3063     } else if (iter.script()->getWarmUpResetCount() >= JitWarmupResetLimit) {
3064       return ReturnStringCopy(
3065           cx, args, "Compilation is being repeatedly prevented. Giving up.");
3066     }
3067   }
3068 
3069   args.rval().setBoolean(iter.isIon());
3070   return true;
3071 }
3072 
testingFunc_assertFloat32(JSContext * cx,unsigned argc,Value * vp)3073 bool js::testingFunc_assertFloat32(JSContext* cx, unsigned argc, Value* vp) {
3074   CallArgs args = CallArgsFromVp(argc, vp);
3075   if (args.length() != 2) {
3076     JS_ReportErrorASCII(cx, "Expects only 2 arguments");
3077     return false;
3078   }
3079 
3080   // NOP when not in IonMonkey
3081   args.rval().setUndefined();
3082   return true;
3083 }
3084 
TestingFunc_assertJitStackInvariants(JSContext * cx,unsigned argc,Value * vp)3085 static bool TestingFunc_assertJitStackInvariants(JSContext* cx, unsigned argc,
3086                                                  Value* vp) {
3087   CallArgs args = CallArgsFromVp(argc, vp);
3088 
3089   jit::AssertJitStackInvariants(cx);
3090   args.rval().setUndefined();
3091   return true;
3092 }
3093 
testingFunc_assertRecoveredOnBailout(JSContext * cx,unsigned argc,Value * vp)3094 bool js::testingFunc_assertRecoveredOnBailout(JSContext* cx, unsigned argc,
3095                                               Value* vp) {
3096   CallArgs args = CallArgsFromVp(argc, vp);
3097   if (args.length() != 2) {
3098     JS_ReportErrorASCII(cx, "Expects only 2 arguments");
3099     return false;
3100   }
3101 
3102   // NOP when not in IonMonkey
3103   args.rval().setUndefined();
3104   return true;
3105 }
3106 
GetJitCompilerOptions(JSContext * cx,unsigned argc,Value * vp)3107 static bool GetJitCompilerOptions(JSContext* cx, unsigned argc, Value* vp) {
3108   CallArgs args = CallArgsFromVp(argc, vp);
3109   RootedObject info(cx, JS_NewPlainObject(cx));
3110   if (!info) {
3111     return false;
3112   }
3113 
3114   uint32_t intValue = 0;
3115   RootedValue value(cx);
3116 
3117 #define JIT_COMPILER_MATCH(key, string)                         \
3118   opt = JSJITCOMPILER_##key;                                    \
3119   if (JS_GetGlobalJitCompilerOption(cx, opt, &intValue)) {      \
3120     value.setInt32(intValue);                                   \
3121     if (!JS_SetProperty(cx, info, string, value)) return false; \
3122   }
3123 
3124   JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION;
3125   JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH);
3126 #undef JIT_COMPILER_MATCH
3127 
3128   args.rval().setObject(*info);
3129   return true;
3130 }
3131 
SetIonCheckGraphCoherency(JSContext * cx,unsigned argc,Value * vp)3132 static bool SetIonCheckGraphCoherency(JSContext* cx, unsigned argc, Value* vp) {
3133   CallArgs args = CallArgsFromVp(argc, vp);
3134   jit::JitOptions.checkGraphConsistency = ToBoolean(args.get(0));
3135   args.rval().setUndefined();
3136   return true;
3137 }
3138 
3139 // A JSObject that holds structured clone data, similar to the C++ class
3140 // JSAutoStructuredCloneBuffer.
3141 class CloneBufferObject : public NativeObject {
3142   static const JSPropertySpec props_[3];
3143 
3144   static const size_t DATA_SLOT = 0;
3145   static const size_t SYNTHETIC_SLOT = 1;
3146   static const size_t NUM_SLOTS = 2;
3147 
3148  public:
3149   static const JSClass class_;
3150 
Create(JSContext * cx)3151   static CloneBufferObject* Create(JSContext* cx) {
3152     RootedObject obj(cx, JS_NewObject(cx, &class_));
3153     if (!obj) {
3154       return nullptr;
3155     }
3156     obj->as<CloneBufferObject>().setReservedSlot(DATA_SLOT,
3157                                                  PrivateValue(nullptr));
3158     obj->as<CloneBufferObject>().setReservedSlot(SYNTHETIC_SLOT,
3159                                                  BooleanValue(false));
3160 
3161     if (!JS_DefineProperties(cx, obj, props_)) {
3162       return nullptr;
3163     }
3164 
3165     return &obj->as<CloneBufferObject>();
3166   }
3167 
Create(JSContext * cx,JSAutoStructuredCloneBuffer * buffer)3168   static CloneBufferObject* Create(JSContext* cx,
3169                                    JSAutoStructuredCloneBuffer* buffer) {
3170     Rooted<CloneBufferObject*> obj(cx, Create(cx));
3171     if (!obj) {
3172       return nullptr;
3173     }
3174     auto data = js::MakeUnique<JSStructuredCloneData>(buffer->scope());
3175     if (!data) {
3176       ReportOutOfMemory(cx);
3177       return nullptr;
3178     }
3179     buffer->steal(data.get());
3180     obj->setData(data.release(), false);
3181     return obj;
3182   }
3183 
data() const3184   JSStructuredCloneData* data() const {
3185     return static_cast<JSStructuredCloneData*>(
3186         getReservedSlot(DATA_SLOT).toPrivate());
3187   }
3188 
isSynthetic() const3189   bool isSynthetic() const {
3190     return getReservedSlot(SYNTHETIC_SLOT).toBoolean();
3191   }
3192 
setData(JSStructuredCloneData * aData,bool synthetic)3193   void setData(JSStructuredCloneData* aData, bool synthetic) {
3194     MOZ_ASSERT(!data());
3195     setReservedSlot(DATA_SLOT, PrivateValue(aData));
3196     setReservedSlot(SYNTHETIC_SLOT, BooleanValue(synthetic));
3197   }
3198 
3199   // Discard an owned clone buffer.
discard()3200   void discard() {
3201     js_delete(data());
3202     setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
3203   }
3204 
setCloneBuffer_impl(JSContext * cx,const CallArgs & args)3205   static bool setCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
3206     Rooted<CloneBufferObject*> obj(
3207         cx, &args.thisv().toObject().as<CloneBufferObject>());
3208 
3209     const char* data = nullptr;
3210     UniqueChars dataOwner;
3211     uint32_t nbytes;
3212 
3213     if (args.get(0).isObject() && args[0].toObject().is<ArrayBufferObject>()) {
3214       ArrayBufferObject* buffer = &args[0].toObject().as<ArrayBufferObject>();
3215       bool isSharedMemory;
3216       uint8_t* dataBytes = nullptr;
3217       JS::GetArrayBufferLengthAndData(buffer, &nbytes, &isSharedMemory,
3218                                       &dataBytes);
3219       MOZ_ASSERT(!isSharedMemory);
3220       data = reinterpret_cast<char*>(dataBytes);
3221     } else {
3222       JSString* str = JS::ToString(cx, args.get(0));
3223       if (!str) {
3224         return false;
3225       }
3226       dataOwner = JS_EncodeStringToLatin1(cx, str);
3227       if (!dataOwner) {
3228         return false;
3229       }
3230       data = dataOwner.get();
3231       nbytes = JS_GetStringLength(str);
3232     }
3233 
3234     if (nbytes == 0 || (nbytes % sizeof(uint64_t) != 0)) {
3235       JS_ReportErrorASCII(cx, "Invalid length for clonebuffer data");
3236       return false;
3237     }
3238 
3239     auto buf = js::MakeUnique<JSStructuredCloneData>(
3240         JS::StructuredCloneScope::DifferentProcess);
3241     if (!buf || !buf->Init(nbytes)) {
3242       ReportOutOfMemory(cx);
3243       return false;
3244     }
3245 
3246     MOZ_ALWAYS_TRUE(buf->AppendBytes(data, nbytes));
3247     obj->discard();
3248     obj->setData(buf.release(), true);
3249 
3250     args.rval().setUndefined();
3251     return true;
3252   }
3253 
is(HandleValue v)3254   static bool is(HandleValue v) {
3255     return v.isObject() && v.toObject().is<CloneBufferObject>();
3256   }
3257 
setCloneBuffer(JSContext * cx,unsigned int argc,JS::Value * vp)3258   static bool setCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
3259     CallArgs args = CallArgsFromVp(argc, vp);
3260     return CallNonGenericMethod<is, setCloneBuffer_impl>(cx, args);
3261   }
3262 
getData(JSContext * cx,Handle<CloneBufferObject * > obj,JSStructuredCloneData ** data)3263   static bool getData(JSContext* cx, Handle<CloneBufferObject*> obj,
3264                       JSStructuredCloneData** data) {
3265     if (!obj->data()) {
3266       *data = nullptr;
3267       return true;
3268     }
3269 
3270     bool hasTransferable;
3271     if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable)) {
3272       return false;
3273     }
3274 
3275     if (hasTransferable) {
3276       JS_ReportErrorASCII(
3277           cx, "cannot retrieve structured clone buffer with transferables");
3278       return false;
3279     }
3280 
3281     *data = obj->data();
3282     return true;
3283   }
3284 
getCloneBuffer_impl(JSContext * cx,const CallArgs & args)3285   static bool getCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
3286     Rooted<CloneBufferObject*> obj(
3287         cx, &args.thisv().toObject().as<CloneBufferObject>());
3288     MOZ_ASSERT(args.length() == 0);
3289 
3290     JSStructuredCloneData* data;
3291     if (!getData(cx, obj, &data)) {
3292       return false;
3293     }
3294 
3295     size_t size = data->Size();
3296     UniqueChars buffer(js_pod_malloc<char>(size));
3297     if (!buffer) {
3298       ReportOutOfMemory(cx);
3299       return false;
3300     }
3301     auto iter = data->Start();
3302     if (!data->ReadBytes(iter, buffer.get(), size)) {
3303       ReportOutOfMemory(cx);
3304       return false;
3305     }
3306     JSString* str = JS_NewStringCopyN(cx, buffer.get(), size);
3307     if (!str) {
3308       return false;
3309     }
3310     args.rval().setString(str);
3311     return true;
3312   }
3313 
getCloneBuffer(JSContext * cx,unsigned int argc,JS::Value * vp)3314   static bool getCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
3315     CallArgs args = CallArgsFromVp(argc, vp);
3316     return CallNonGenericMethod<is, getCloneBuffer_impl>(cx, args);
3317   }
3318 
getCloneBufferAsArrayBuffer_impl(JSContext * cx,const CallArgs & args)3319   static bool getCloneBufferAsArrayBuffer_impl(JSContext* cx,
3320                                                const CallArgs& args) {
3321     Rooted<CloneBufferObject*> obj(
3322         cx, &args.thisv().toObject().as<CloneBufferObject>());
3323     MOZ_ASSERT(args.length() == 0);
3324 
3325     JSStructuredCloneData* data;
3326     if (!getData(cx, obj, &data)) {
3327       return false;
3328     }
3329 
3330     size_t size = data->Size();
3331     UniqueChars buffer(js_pod_malloc<char>(size));
3332     if (!buffer) {
3333       ReportOutOfMemory(cx);
3334       return false;
3335     }
3336     auto iter = data->Start();
3337     if (!data->ReadBytes(iter, buffer.get(), size)) {
3338       ReportOutOfMemory(cx);
3339       return false;
3340     }
3341 
3342     auto* rawBuffer = buffer.release();
3343     JSObject* arrayBuffer = JS::NewArrayBufferWithContents(cx, size, rawBuffer);
3344     if (!arrayBuffer) {
3345       js_free(rawBuffer);
3346       return false;
3347     }
3348 
3349     args.rval().setObject(*arrayBuffer);
3350     return true;
3351   }
3352 
getCloneBufferAsArrayBuffer(JSContext * cx,unsigned int argc,JS::Value * vp)3353   static bool getCloneBufferAsArrayBuffer(JSContext* cx, unsigned int argc,
3354                                           JS::Value* vp) {
3355     CallArgs args = CallArgsFromVp(argc, vp);
3356     return CallNonGenericMethod<is, getCloneBufferAsArrayBuffer_impl>(cx, args);
3357   }
3358 
Finalize(JSFreeOp * fop,JSObject * obj)3359   static void Finalize(JSFreeOp* fop, JSObject* obj) {
3360     obj->as<CloneBufferObject>().discard();
3361   }
3362 };
3363 
3364 static const JSClassOps CloneBufferObjectClassOps = {
3365     nullptr,                      // addProperty
3366     nullptr,                      // delProperty
3367     nullptr,                      // enumerate
3368     nullptr,                      // newEnumerate
3369     nullptr,                      // resolve
3370     nullptr,                      // mayResolve
3371     CloneBufferObject::Finalize,  // finalize
3372     nullptr,                      // call
3373     nullptr,                      // hasInstance
3374     nullptr,                      // construct
3375     nullptr,                      // trace
3376 };
3377 
3378 const JSClass CloneBufferObject::class_ = {
3379     "CloneBuffer",
3380     JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS) |
3381         JSCLASS_FOREGROUND_FINALIZE,
3382     &CloneBufferObjectClassOps};
3383 
3384 const JSPropertySpec CloneBufferObject::props_[] = {
3385     JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0),
3386     JS_PSGS("arraybuffer", getCloneBufferAsArrayBuffer, setCloneBuffer, 0),
3387     JS_PS_END};
3388 
ParseCloneScope(JSContext * cx,HandleString str)3389 static mozilla::Maybe<JS::StructuredCloneScope> ParseCloneScope(
3390     JSContext* cx, HandleString str) {
3391   mozilla::Maybe<JS::StructuredCloneScope> scope;
3392 
3393   JSLinearString* scopeStr = str->ensureLinear(cx);
3394   if (!scopeStr) {
3395     return scope;
3396   }
3397 
3398   if (StringEqualsLiteral(scopeStr, "SameProcess")) {
3399     scope.emplace(JS::StructuredCloneScope::SameProcess);
3400   } else if (StringEqualsLiteral(scopeStr, "DifferentProcess")) {
3401     scope.emplace(JS::StructuredCloneScope::DifferentProcess);
3402   } else if (StringEqualsLiteral(scopeStr, "DifferentProcessForIndexedDB")) {
3403     scope.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB);
3404   }
3405 
3406   return scope;
3407 }
3408 
testingFunc_serialize(JSContext * cx,unsigned argc,Value * vp)3409 bool js::testingFunc_serialize(JSContext* cx, unsigned argc, Value* vp) {
3410   CallArgs args = CallArgsFromVp(argc, vp);
3411 
3412   mozilla::Maybe<JSAutoStructuredCloneBuffer> clonebuf;
3413   JS::CloneDataPolicy policy;
3414 
3415   if (!args.get(2).isUndefined()) {
3416     RootedObject opts(cx, ToObject(cx, args.get(2)));
3417     if (!opts) {
3418       return false;
3419     }
3420 
3421     RootedValue v(cx);
3422     if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v)) {
3423       return false;
3424     }
3425 
3426     if (!v.isUndefined()) {
3427       JSString* str = JS::ToString(cx, v);
3428       if (!str) {
3429         return false;
3430       }
3431       JSLinearString* poli = str->ensureLinear(cx);
3432       if (!poli) {
3433         return false;
3434       }
3435 
3436       if (StringEqualsLiteral(poli, "allow")) {
3437         policy.allowSharedMemoryObjects();
3438         policy.allowIntraClusterClonableSharedObjects();
3439       } else if (StringEqualsLiteral(poli, "deny")) {
3440         // default
3441       } else {
3442         JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'");
3443         return false;
3444       }
3445     }
3446 
3447     if (!JS_GetProperty(cx, opts, "scope", &v)) {
3448       return false;
3449     }
3450 
3451     if (!v.isUndefined()) {
3452       RootedString str(cx, JS::ToString(cx, v));
3453       if (!str) {
3454         return false;
3455       }
3456       auto scope = ParseCloneScope(cx, str);
3457       if (!scope) {
3458         JS_ReportErrorASCII(cx, "Invalid structured clone scope");
3459         return false;
3460       }
3461       clonebuf.emplace(*scope, nullptr, nullptr);
3462     }
3463   }
3464 
3465   if (!clonebuf) {
3466     clonebuf.emplace(JS::StructuredCloneScope::SameProcess, nullptr, nullptr);
3467   }
3468 
3469   if (!clonebuf->write(cx, args.get(0), args.get(1), policy)) {
3470     return false;
3471   }
3472 
3473   RootedObject obj(cx, CloneBufferObject::Create(cx, clonebuf.ptr()));
3474   if (!obj) {
3475     return false;
3476   }
3477 
3478   args.rval().setObject(*obj);
3479   return true;
3480 }
3481 
Deserialize(JSContext * cx,unsigned argc,Value * vp)3482 static bool Deserialize(JSContext* cx, unsigned argc, Value* vp) {
3483   CallArgs args = CallArgsFromVp(argc, vp);
3484 
3485   if (!args.get(0).isObject() || !args[0].toObject().is<CloneBufferObject>()) {
3486     JS_ReportErrorASCII(cx, "deserialize requires a clonebuffer argument");
3487     return false;
3488   }
3489   Rooted<CloneBufferObject*> obj(cx,
3490                                  &args[0].toObject().as<CloneBufferObject>());
3491 
3492   JS::CloneDataPolicy policy;
3493   JS::StructuredCloneScope scope =
3494       obj->isSynthetic() ? JS::StructuredCloneScope::DifferentProcess
3495                          : JS::StructuredCloneScope::SameProcess;
3496   if (args.get(1).isObject()) {
3497     RootedObject opts(cx, &args[1].toObject());
3498     if (!opts) {
3499       return false;
3500     }
3501 
3502     RootedValue v(cx);
3503     if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v)) {
3504       return false;
3505     }
3506 
3507     if (!v.isUndefined()) {
3508       JSString* str = JS::ToString(cx, v);
3509       if (!str) {
3510         return false;
3511       }
3512       JSLinearString* poli = str->ensureLinear(cx);
3513       if (!poli) {
3514         return false;
3515       }
3516 
3517       if (StringEqualsLiteral(poli, "allow")) {
3518         policy.allowSharedMemoryObjects();
3519         policy.allowIntraClusterClonableSharedObjects();
3520       } else if (StringEqualsLiteral(poli, "deny")) {
3521         // default
3522       } else {
3523         JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'");
3524         return false;
3525       }
3526     }
3527 
3528     if (!JS_GetProperty(cx, opts, "scope", &v)) {
3529       return false;
3530     }
3531 
3532     if (!v.isUndefined()) {
3533       RootedString str(cx, JS::ToString(cx, v));
3534       if (!str) {
3535         return false;
3536       }
3537       auto maybeScope = ParseCloneScope(cx, str);
3538       if (!maybeScope) {
3539         JS_ReportErrorASCII(cx, "Invalid structured clone scope");
3540         return false;
3541       }
3542 
3543       if (*maybeScope < scope) {
3544         JS_ReportErrorASCII(cx,
3545                             "Cannot use less restrictive scope "
3546                             "than the deserialized clone buffer's scope");
3547         return false;
3548       }
3549 
3550       scope = *maybeScope;
3551     }
3552   }
3553 
3554   // Clone buffer was already consumed?
3555   if (!obj->data()) {
3556     JS_ReportErrorASCII(cx,
3557                         "deserialize given invalid clone buffer "
3558                         "(transferables already consumed?)");
3559     return false;
3560   }
3561 
3562   bool hasTransferable;
3563   if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable)) {
3564     return false;
3565   }
3566 
3567   RootedValue deserialized(cx);
3568   if (!JS_ReadStructuredClone(cx, *obj->data(), JS_STRUCTURED_CLONE_VERSION,
3569                               scope, &deserialized, policy, nullptr, nullptr)) {
3570     return false;
3571   }
3572   args.rval().set(deserialized);
3573 
3574   // Consume any clone buffer with transferables; throw an error if it is
3575   // deserialized again.
3576   if (hasTransferable) {
3577     obj->discard();
3578   }
3579 
3580   return true;
3581 }
3582 
DetachArrayBuffer(JSContext * cx,unsigned argc,Value * vp)3583 static bool DetachArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
3584   CallArgs args = CallArgsFromVp(argc, vp);
3585 
3586   if (args.length() != 1) {
3587     JS_ReportErrorASCII(cx, "detachArrayBuffer() requires a single argument");
3588     return false;
3589   }
3590 
3591   if (!args[0].isObject()) {
3592     JS_ReportErrorASCII(cx, "detachArrayBuffer must be passed an object");
3593     return false;
3594   }
3595 
3596   RootedObject obj(cx, &args[0].toObject());
3597   if (!JS::DetachArrayBuffer(cx, obj)) {
3598     return false;
3599   }
3600 
3601   args.rval().setUndefined();
3602   return true;
3603 }
3604 
HelperThreadCount(JSContext * cx,unsigned argc,Value * vp)3605 static bool HelperThreadCount(JSContext* cx, unsigned argc, Value* vp) {
3606   CallArgs args = CallArgsFromVp(argc, vp);
3607 #ifdef JS_MORE_DETERMINISTIC
3608   // Always return 0 to get consistent output with and without --no-threads.
3609   args.rval().setInt32(0);
3610 #else
3611   if (CanUseExtraThreads()) {
3612     args.rval().setInt32(HelperThreadState().threadCount);
3613   } else {
3614     args.rval().setInt32(0);
3615   }
3616 #endif
3617   return true;
3618 }
3619 
EnableShapeConsistencyChecks(JSContext * cx,unsigned argc,Value * vp)3620 static bool EnableShapeConsistencyChecks(JSContext* cx, unsigned argc,
3621                                          Value* vp) {
3622   CallArgs args = CallArgsFromVp(argc, vp);
3623 #ifdef DEBUG
3624   NativeObject::enableShapeConsistencyChecks();
3625 #endif
3626   args.rval().setUndefined();
3627   return true;
3628 }
3629 
3630 #ifdef JS_TRACE_LOGGING
EnableTraceLogger(JSContext * cx,unsigned argc,Value * vp)3631 static bool EnableTraceLogger(JSContext* cx, unsigned argc, Value* vp) {
3632   CallArgs args = CallArgsFromVp(argc, vp);
3633   TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
3634   if (!TraceLoggerEnable(logger, cx)) {
3635     return false;
3636   }
3637 
3638   args.rval().setUndefined();
3639   return true;
3640 }
3641 
DisableTraceLogger(JSContext * cx,unsigned argc,Value * vp)3642 static bool DisableTraceLogger(JSContext* cx, unsigned argc, Value* vp) {
3643   CallArgs args = CallArgsFromVp(argc, vp);
3644   TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
3645   args.rval().setBoolean(TraceLoggerDisable(logger));
3646 
3647   return true;
3648 }
3649 #endif
3650 
3651 #if defined(DEBUG) || defined(JS_JITSPEW)
DumpObject(JSContext * cx,unsigned argc,Value * vp)3652 static bool DumpObject(JSContext* cx, unsigned argc, Value* vp) {
3653   CallArgs args = CallArgsFromVp(argc, vp);
3654   RootedObject obj(cx, ToObject(cx, args.get(0)));
3655   if (!obj) {
3656     return false;
3657   }
3658 
3659   DumpObject(obj);
3660 
3661   args.rval().setUndefined();
3662   return true;
3663 }
3664 #endif
3665 
SharedMemoryEnabled(JSContext * cx,unsigned argc,Value * vp)3666 static bool SharedMemoryEnabled(JSContext* cx, unsigned argc, Value* vp) {
3667   CallArgs args = CallArgsFromVp(argc, vp);
3668   args.rval().setBoolean(
3669       cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
3670   return true;
3671 }
3672 
SharedArrayRawBufferCount(JSContext * cx,unsigned argc,Value * vp)3673 static bool SharedArrayRawBufferCount(JSContext* cx, unsigned argc, Value* vp) {
3674   CallArgs args = CallArgsFromVp(argc, vp);
3675   args.rval().setInt32(LiveMappedBufferCount());
3676   return true;
3677 }
3678 
SharedArrayRawBufferRefcount(JSContext * cx,unsigned argc,Value * vp)3679 static bool SharedArrayRawBufferRefcount(JSContext* cx, unsigned argc,
3680                                          Value* vp) {
3681   CallArgs args = CallArgsFromVp(argc, vp);
3682   if (args.length() != 1 || !args[0].isObject()) {
3683     JS_ReportErrorASCII(cx, "Expected SharedArrayBuffer object");
3684     return false;
3685   }
3686   RootedObject obj(cx, &args[0].toObject());
3687   if (!obj->is<SharedArrayBufferObject>()) {
3688     JS_ReportErrorASCII(cx, "Expected SharedArrayBuffer object");
3689     return false;
3690   }
3691   args.rval().setInt32(
3692       obj->as<SharedArrayBufferObject>().rawBufferObject()->refcount());
3693   return true;
3694 }
3695 
3696 #ifdef NIGHTLY_BUILD
ObjectAddress(JSContext * cx,unsigned argc,Value * vp)3697 static bool ObjectAddress(JSContext* cx, unsigned argc, Value* vp) {
3698   CallArgs args = CallArgsFromVp(argc, vp);
3699   if (args.length() != 1) {
3700     RootedObject callee(cx, &args.callee());
3701     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
3702     return false;
3703   }
3704   if (!args[0].isObject()) {
3705     RootedObject callee(cx, &args.callee());
3706     ReportUsageErrorASCII(cx, callee, "Expected object");
3707     return false;
3708   }
3709 
3710 #  ifdef JS_MORE_DETERMINISTIC
3711   args.rval().setInt32(0);
3712   return true;
3713 #  else
3714   void* ptr = js::UncheckedUnwrap(&args[0].toObject(), true);
3715   char buffer[64];
3716   SprintfLiteral(buffer, "%p", ptr);
3717 
3718   return ReturnStringCopy(cx, args, buffer);
3719 #  endif
3720 }
3721 
SharedAddress(JSContext * cx,unsigned argc,Value * vp)3722 static bool SharedAddress(JSContext* cx, unsigned argc, Value* vp) {
3723   CallArgs args = CallArgsFromVp(argc, vp);
3724   if (args.length() != 1) {
3725     RootedObject callee(cx, &args.callee());
3726     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
3727     return false;
3728   }
3729   if (!args[0].isObject()) {
3730     RootedObject callee(cx, &args.callee());
3731     ReportUsageErrorASCII(cx, callee, "Expected object");
3732     return false;
3733   }
3734 
3735 #  ifdef JS_MORE_DETERMINISTIC
3736   args.rval().setString(cx->staticStrings().getUint(0));
3737 #  else
3738   RootedObject obj(cx, CheckedUnwrapStatic(&args[0].toObject()));
3739   if (!obj) {
3740     ReportAccessDenied(cx);
3741     return false;
3742   }
3743   if (!obj->is<SharedArrayBufferObject>()) {
3744     JS_ReportErrorASCII(cx, "Argument must be a SharedArrayBuffer");
3745     return false;
3746   }
3747   char buffer[64];
3748   uint32_t nchar = SprintfLiteral(
3749       buffer, "%p",
3750       obj->as<SharedArrayBufferObject>().dataPointerShared().unwrap(
3751           /*safeish*/));
3752 
3753   JSString* str = JS_NewStringCopyN(cx, buffer, nchar);
3754   if (!str) {
3755     return false;
3756   }
3757 
3758   args.rval().setString(str);
3759 #  endif
3760 
3761   return true;
3762 }
3763 #endif
3764 
DumpBacktrace(JSContext * cx,unsigned argc,Value * vp)3765 static bool DumpBacktrace(JSContext* cx, unsigned argc, Value* vp) {
3766   CallArgs args = CallArgsFromVp(argc, vp);
3767   DumpBacktrace(cx);
3768   args.rval().setUndefined();
3769   return true;
3770 }
3771 
GetBacktrace(JSContext * cx,unsigned argc,Value * vp)3772 static bool GetBacktrace(JSContext* cx, unsigned argc, Value* vp) {
3773   CallArgs args = CallArgsFromVp(argc, vp);
3774 
3775   bool showArgs = false;
3776   bool showLocals = false;
3777   bool showThisProps = false;
3778 
3779   if (args.length() > 1) {
3780     RootedObject callee(cx, &args.callee());
3781     ReportUsageErrorASCII(cx, callee, "Too many arguments");
3782     return false;
3783   }
3784 
3785   if (args.length() == 1) {
3786     RootedObject cfg(cx, ToObject(cx, args[0]));
3787     if (!cfg) {
3788       return false;
3789     }
3790     RootedValue v(cx);
3791 
3792     if (!JS_GetProperty(cx, cfg, "args", &v)) {
3793       return false;
3794     }
3795     showArgs = ToBoolean(v);
3796 
3797     if (!JS_GetProperty(cx, cfg, "locals", &v)) {
3798       return false;
3799     }
3800     showLocals = ToBoolean(v);
3801 
3802     if (!JS_GetProperty(cx, cfg, "thisprops", &v)) {
3803       return false;
3804     }
3805     showThisProps = ToBoolean(v);
3806   }
3807 
3808   JS::UniqueChars buf =
3809       JS::FormatStackDump(cx, showArgs, showLocals, showThisProps);
3810   if (!buf) {
3811     return false;
3812   }
3813 
3814   JS::ConstUTF8CharsZ utf8chars(buf.get(), strlen(buf.get()));
3815   JSString* str = NewStringCopyUTF8Z<CanGC>(cx, utf8chars);
3816   if (!str) {
3817     return false;
3818   }
3819 
3820   args.rval().setString(str);
3821   return true;
3822 }
3823 
ReportOutOfMemory(JSContext * cx,unsigned argc,Value * vp)3824 static bool ReportOutOfMemory(JSContext* cx, unsigned argc, Value* vp) {
3825   CallArgs args = CallArgsFromVp(argc, vp);
3826   JS_ReportOutOfMemory(cx);
3827   cx->clearPendingException();
3828   args.rval().setUndefined();
3829   return true;
3830 }
3831 
ThrowOutOfMemory(JSContext * cx,unsigned argc,Value * vp)3832 static bool ThrowOutOfMemory(JSContext* cx, unsigned argc, Value* vp) {
3833   JS_ReportOutOfMemory(cx);
3834   return false;
3835 }
3836 
ReportLargeAllocationFailure(JSContext * cx,unsigned argc,Value * vp)3837 static bool ReportLargeAllocationFailure(JSContext* cx, unsigned argc,
3838                                          Value* vp) {
3839   CallArgs args = CallArgsFromVp(argc, vp);
3840   void* buf = cx->runtime()->onOutOfMemoryCanGC(
3841       AllocFunction::Malloc, js::MallocArena, JSRuntime::LARGE_ALLOCATION);
3842   js_free(buf);
3843   args.rval().setUndefined();
3844   return true;
3845 }
3846 
3847 namespace heaptools {
3848 
3849 using EdgeName = UniqueTwoByteChars;
3850 
3851 // An edge to a node from its predecessor in a path through the graph.
3852 class BackEdge {
3853   // The node from which this edge starts.
3854   JS::ubi::Node predecessor_;
3855 
3856   // The name of this edge.
3857   EdgeName name_;
3858 
3859  public:
BackEdge()3860   BackEdge() : name_(nullptr) {}
3861   // Construct an initialized back edge, taking ownership of |name|.
BackEdge(JS::ubi::Node predecessor,EdgeName name)3862   BackEdge(JS::ubi::Node predecessor, EdgeName name)
3863       : predecessor_(predecessor), name_(std::move(name)) {}
BackEdge(BackEdge && rhs)3864   BackEdge(BackEdge&& rhs)
3865       : predecessor_(rhs.predecessor_), name_(std::move(rhs.name_)) {}
operator =(BackEdge && rhs)3866   BackEdge& operator=(BackEdge&& rhs) {
3867     MOZ_ASSERT(&rhs != this);
3868     this->~BackEdge();
3869     new (this) BackEdge(std::move(rhs));
3870     return *this;
3871   }
3872 
forgetName()3873   EdgeName forgetName() { return std::move(name_); }
predecessor() const3874   JS::ubi::Node predecessor() const { return predecessor_; }
3875 
3876  private:
3877   // No copy constructor or copying assignment.
3878   BackEdge(const BackEdge&) = delete;
3879   BackEdge& operator=(const BackEdge&) = delete;
3880 };
3881 
3882 // A path-finding handler class for use with JS::ubi::BreadthFirst.
3883 struct FindPathHandler {
3884   using NodeData = BackEdge;
3885   using Traversal = JS::ubi::BreadthFirst<FindPathHandler>;
3886 
FindPathHandlerheaptools::FindPathHandler3887   FindPathHandler(JSContext* cx, JS::ubi::Node start, JS::ubi::Node target,
3888                   MutableHandle<GCVector<Value>> nodes, Vector<EdgeName>& edges)
3889       : cx(cx),
3890         start(start),
3891         target(target),
3892         foundPath(false),
3893         nodes(nodes),
3894         edges(edges) {}
3895 
operator ()heaptools::FindPathHandler3896   bool operator()(Traversal& traversal, JS::ubi::Node origin,
3897                   const JS::ubi::Edge& edge, BackEdge* backEdge, bool first) {
3898     // We take care of each node the first time we visit it, so there's
3899     // nothing to be done on subsequent visits.
3900     if (!first) {
3901       return true;
3902     }
3903 
3904     // Record how we reached this node. This is the last edge on a
3905     // shortest path to this node.
3906     EdgeName edgeName =
3907         DuplicateStringToArena(js::StringBufferArena, cx, edge.name.get());
3908     if (!edgeName) {
3909       return false;
3910     }
3911     *backEdge = BackEdge(origin, std::move(edgeName));
3912 
3913     // Have we reached our final target node?
3914     if (edge.referent == target) {
3915       // Record the path that got us here, which must be a shortest path.
3916       if (!recordPath(traversal, backEdge)) {
3917         return false;
3918       }
3919       foundPath = true;
3920       traversal.stop();
3921     }
3922 
3923     return true;
3924   }
3925 
3926   // We've found a path to our target. Walk the backlinks to produce the
3927   // (reversed) path, saving the path in |nodes| and |edges|. |nodes| is
3928   // rooted, so it can hold the path's nodes as we leave the scope of
3929   // the AutoCheckCannotGC. Note that nodes are added to |visited| after we
3930   // return from operator() so we have to pass the target BackEdge* to this
3931   // function.
recordPathheaptools::FindPathHandler3932   bool recordPath(Traversal& traversal, BackEdge* targetBackEdge) {
3933     JS::ubi::Node here = target;
3934 
3935     do {
3936       BackEdge* backEdge = targetBackEdge;
3937       if (here != target) {
3938         Traversal::NodeMap::Ptr p = traversal.visited.lookup(here);
3939         MOZ_ASSERT(p);
3940         backEdge = &p->value();
3941       }
3942       JS::ubi::Node predecessor = backEdge->predecessor();
3943       if (!nodes.append(predecessor.exposeToJS()) ||
3944           !edges.append(backEdge->forgetName())) {
3945         return false;
3946       }
3947       here = predecessor;
3948     } while (here != start);
3949 
3950     return true;
3951   }
3952 
3953   JSContext* cx;
3954 
3955   // The node we're starting from.
3956   JS::ubi::Node start;
3957 
3958   // The node we're looking for.
3959   JS::ubi::Node target;
3960 
3961   // True if we found a path to target, false if we didn't.
3962   bool foundPath;
3963 
3964   // The nodes and edges of the path --- should we find one. The path is
3965   // stored in reverse order, because that's how it's easiest for us to
3966   // construct it:
3967   // - edges[i] is the name of the edge from nodes[i] to nodes[i-1].
3968   // - edges[0] is the name of the edge from nodes[0] to the target.
3969   // - The last node, nodes[n-1], is the start node.
3970   MutableHandle<GCVector<Value>> nodes;
3971   Vector<EdgeName>& edges;
3972 };
3973 
3974 }  // namespace heaptools
3975 
FindPath(JSContext * cx,unsigned argc,Value * vp)3976 static bool FindPath(JSContext* cx, unsigned argc, Value* vp) {
3977   CallArgs args = CallArgsFromVp(argc, vp);
3978   if (!args.requireAtLeast(cx, "findPath", 2)) {
3979     return false;
3980   }
3981 
3982   // We don't ToString non-objects given as 'start' or 'target', because this
3983   // test is all about object identity, and ToString doesn't preserve that.
3984   // Non-GCThing endpoints don't make much sense.
3985   if (!args[0].isObject() && !args[0].isString() && !args[0].isSymbol()) {
3986     ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
3987                      nullptr, "not an object, string, or symbol");
3988     return false;
3989   }
3990 
3991   if (!args[1].isObject() && !args[1].isString() && !args[1].isSymbol()) {
3992     ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
3993                      nullptr, "not an object, string, or symbol");
3994     return false;
3995   }
3996 
3997   Rooted<GCVector<Value>> nodes(cx, GCVector<Value>(cx));
3998   Vector<heaptools::EdgeName> edges(cx);
3999 
4000   {
4001     // We can't tolerate the GC moving things around while we're searching
4002     // the heap. Check that nothing we do causes a GC.
4003     JS::AutoCheckCannotGC autoCannotGC;
4004 
4005     JS::ubi::Node start(args[0]), target(args[1]);
4006 
4007     heaptools::FindPathHandler handler(cx, start, target, &nodes, edges);
4008     heaptools::FindPathHandler::Traversal traversal(cx, handler, autoCannotGC);
4009     if (!traversal.addStart(start)) {
4010       ReportOutOfMemory(cx);
4011       return false;
4012     }
4013 
4014     if (!traversal.traverse()) {
4015       if (!cx->isExceptionPending()) {
4016         ReportOutOfMemory(cx);
4017       }
4018       return false;
4019     }
4020 
4021     if (!handler.foundPath) {
4022       // We didn't find any paths from the start to the target.
4023       args.rval().setUndefined();
4024       return true;
4025     }
4026   }
4027 
4028   // |nodes| and |edges| contain the path from |start| to |target|, reversed.
4029   // Construct a JavaScript array describing the path from the start to the
4030   // target. Each element has the form:
4031   //
4032   //   {
4033   //     node: <object or string or symbol>,
4034   //     edge: <string describing outgoing edge from node>
4035   //   }
4036   //
4037   // or, if the node is some internal thing that isn't a proper JavaScript
4038   // value:
4039   //
4040   //   { node: undefined, edge: <string> }
4041   size_t length = nodes.length();
4042   RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length));
4043   if (!result) {
4044     return false;
4045   }
4046   result->ensureDenseInitializedLength(cx, 0, length);
4047 
4048   // Walk |nodes| and |edges| in the stored order, and construct the result
4049   // array in start-to-target order.
4050   for (size_t i = 0; i < length; i++) {
4051     // Build an object describing the node and edge.
4052     RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
4053     if (!obj) {
4054       return false;
4055     }
4056 
4057     RootedValue wrapped(cx, nodes[i]);
4058     if (!cx->compartment()->wrap(cx, &wrapped)) {
4059       return false;
4060     }
4061 
4062     if (!JS_DefineProperty(cx, obj, "node", wrapped, JSPROP_ENUMERATE)) {
4063       return false;
4064     }
4065 
4066     heaptools::EdgeName edgeName = std::move(edges[i]);
4067 
4068     size_t edgeNameLength = js_strlen(edgeName.get());
4069     RootedString edgeStr(
4070         cx, NewString<CanGC>(cx, std::move(edgeName), edgeNameLength));
4071     if (!edgeStr) {
4072       return false;
4073     }
4074 
4075     if (!JS_DefineProperty(cx, obj, "edge", edgeStr, JSPROP_ENUMERATE)) {
4076       return false;
4077     }
4078 
4079     result->setDenseElement(length - i - 1, ObjectValue(*obj));
4080   }
4081 
4082   args.rval().setObject(*result);
4083   return true;
4084 }
4085 
ShortestPaths(JSContext * cx,unsigned argc,Value * vp)4086 static bool ShortestPaths(JSContext* cx, unsigned argc, Value* vp) {
4087   CallArgs args = CallArgsFromVp(argc, vp);
4088   if (!args.requireAtLeast(cx, "shortestPaths", 3)) {
4089     return false;
4090   }
4091 
4092   // We don't ToString non-objects given as 'start' or 'target', because this
4093   // test is all about object identity, and ToString doesn't preserve that.
4094   // Non-GCThing endpoints don't make much sense.
4095   if (!args[0].isObject() && !args[0].isString() && !args[0].isSymbol()) {
4096     ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
4097                      nullptr, "not an object, string, or symbol");
4098     return false;
4099   }
4100 
4101   if (!args[1].isObject() || !args[1].toObject().is<ArrayObject>()) {
4102     ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[1],
4103                      nullptr, "not an array object");
4104     return false;
4105   }
4106 
4107   RootedArrayObject objs(cx, &args[1].toObject().as<ArrayObject>());
4108   size_t length = objs->getDenseInitializedLength();
4109   if (length == 0) {
4110     ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[1],
4111                      nullptr,
4112                      "not a dense array object with one or more elements");
4113     return false;
4114   }
4115 
4116   for (size_t i = 0; i < length; i++) {
4117     RootedValue el(cx, objs->getDenseElement(i));
4118     if (!el.isObject() && !el.isString() && !el.isSymbol()) {
4119       JS_ReportErrorASCII(cx,
4120                           "Each target must be an object, string, or symbol");
4121       return false;
4122     }
4123   }
4124 
4125   int32_t maxNumPaths;
4126   if (!JS::ToInt32(cx, args[2], &maxNumPaths)) {
4127     return false;
4128   }
4129   if (maxNumPaths <= 0) {
4130     ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[2],
4131                      nullptr, "not greater than 0");
4132     return false;
4133   }
4134 
4135   // We accumulate the results into a GC-stable form, due to the fact that the
4136   // JS::ubi::ShortestPaths lifetime (when operating on the live heap graph)
4137   // is bounded within an AutoCheckCannotGC.
4138   Rooted<GCVector<GCVector<GCVector<Value>>>> values(
4139       cx, GCVector<GCVector<GCVector<Value>>>(cx));
4140   Vector<Vector<Vector<JS::ubi::EdgeName>>> names(cx);
4141 
4142   {
4143     JS::AutoCheckCannotGC noGC(cx);
4144 
4145     JS::ubi::NodeSet targets;
4146 
4147     for (size_t i = 0; i < length; i++) {
4148       RootedValue val(cx, objs->getDenseElement(i));
4149       JS::ubi::Node node(val);
4150       if (!targets.put(node)) {
4151         ReportOutOfMemory(cx);
4152         return false;
4153       }
4154     }
4155 
4156     JS::ubi::Node root(args[0]);
4157     auto maybeShortestPaths = JS::ubi::ShortestPaths::Create(
4158         cx, noGC, maxNumPaths, root, std::move(targets));
4159     if (maybeShortestPaths.isNothing()) {
4160       ReportOutOfMemory(cx);
4161       return false;
4162     }
4163     auto& shortestPaths = *maybeShortestPaths;
4164 
4165     for (size_t i = 0; i < length; i++) {
4166       if (!values.append(GCVector<GCVector<Value>>(cx)) ||
4167           !names.append(Vector<Vector<JS::ubi::EdgeName>>(cx))) {
4168         return false;
4169       }
4170 
4171       RootedValue val(cx, objs->getDenseElement(i));
4172       JS::ubi::Node target(val);
4173 
4174       bool ok = shortestPaths.forEachPath(target, [&](JS::ubi::Path& path) {
4175         Rooted<GCVector<Value>> pathVals(cx, GCVector<Value>(cx));
4176         Vector<JS::ubi::EdgeName> pathNames(cx);
4177 
4178         for (auto& part : path) {
4179           if (!pathVals.append(part->predecessor().exposeToJS()) ||
4180               !pathNames.append(std::move(part->name()))) {
4181             return false;
4182           }
4183         }
4184 
4185         return values.back().append(std::move(pathVals.get())) &&
4186                names.back().append(std::move(pathNames));
4187       });
4188 
4189       if (!ok) {
4190         return false;
4191       }
4192     }
4193   }
4194 
4195   MOZ_ASSERT(values.length() == names.length());
4196   MOZ_ASSERT(values.length() == length);
4197 
4198   RootedArrayObject results(cx, NewDenseFullyAllocatedArray(cx, length));
4199   if (!results) {
4200     return false;
4201   }
4202   results->ensureDenseInitializedLength(cx, 0, length);
4203 
4204   for (size_t i = 0; i < length; i++) {
4205     size_t numPaths = values[i].length();
4206     MOZ_ASSERT(names[i].length() == numPaths);
4207 
4208     RootedArrayObject pathsArray(cx, NewDenseFullyAllocatedArray(cx, numPaths));
4209     if (!pathsArray) {
4210       return false;
4211     }
4212     pathsArray->ensureDenseInitializedLength(cx, 0, numPaths);
4213 
4214     for (size_t j = 0; j < numPaths; j++) {
4215       size_t pathLength = values[i][j].length();
4216       MOZ_ASSERT(names[i][j].length() == pathLength);
4217 
4218       RootedArrayObject path(cx, NewDenseFullyAllocatedArray(cx, pathLength));
4219       if (!path) {
4220         return false;
4221       }
4222       path->ensureDenseInitializedLength(cx, 0, pathLength);
4223 
4224       for (size_t k = 0; k < pathLength; k++) {
4225         RootedPlainObject part(cx, NewBuiltinClassInstance<PlainObject>(cx));
4226         if (!part) {
4227           return false;
4228         }
4229 
4230         RootedValue predecessor(cx, values[i][j][k]);
4231         if (!cx->compartment()->wrap(cx, &predecessor) ||
4232             !JS_DefineProperty(cx, part, "predecessor", predecessor,
4233                                JSPROP_ENUMERATE)) {
4234           return false;
4235         }
4236 
4237         if (names[i][j][k]) {
4238           RootedString edge(cx,
4239                             NewStringCopyZ<CanGC>(cx, names[i][j][k].get()));
4240           if (!edge ||
4241               !JS_DefineProperty(cx, part, "edge", edge, JSPROP_ENUMERATE)) {
4242             return false;
4243           }
4244         }
4245 
4246         path->setDenseElement(k, ObjectValue(*part));
4247       }
4248 
4249       pathsArray->setDenseElement(j, ObjectValue(*path));
4250     }
4251 
4252     results->setDenseElement(i, ObjectValue(*pathsArray));
4253   }
4254 
4255   args.rval().setObject(*results);
4256   return true;
4257 }
4258 
EvalReturningScope(JSContext * cx,unsigned argc,Value * vp)4259 static bool EvalReturningScope(JSContext* cx, unsigned argc, Value* vp) {
4260   CallArgs args = CallArgsFromVp(argc, vp);
4261   if (!args.requireAtLeast(cx, "evalReturningScope", 1)) {
4262     return false;
4263   }
4264 
4265   RootedString str(cx, ToString(cx, args[0]));
4266   if (!str) {
4267     return false;
4268   }
4269 
4270   RootedObject global(cx);
4271   if (args.hasDefined(1)) {
4272     global = ToObject(cx, args[1]);
4273     if (!global) {
4274       return false;
4275     }
4276   }
4277 
4278   AutoStableStringChars strChars(cx);
4279   if (!strChars.initTwoByte(cx, str)) {
4280     return false;
4281   }
4282 
4283   mozilla::Range<const char16_t> chars = strChars.twoByteRange();
4284   size_t srclen = chars.length();
4285   const char16_t* src = chars.begin().get();
4286 
4287   JS::AutoFilename filename;
4288   unsigned lineno;
4289 
4290   JS::DescribeScriptedCaller(cx, &filename, &lineno);
4291 
4292   JS::CompileOptions options(cx);
4293   options.setFileAndLine(filename.get(), lineno);
4294   options.setNoScriptRval(true);
4295 
4296   JS::SourceText<char16_t> srcBuf;
4297   if (!srcBuf.init(cx, src, srclen, SourceOwnership::Borrowed)) {
4298     return false;
4299   }
4300 
4301   RootedScript script(cx, JS::CompileForNonSyntacticScope(cx, options, srcBuf));
4302   if (!script) {
4303     return false;
4304   }
4305 
4306   if (global) {
4307     global = CheckedUnwrapDynamic(global, cx, /* stopAtWindowProxy = */ false);
4308     if (!global) {
4309       JS_ReportErrorASCII(cx, "Permission denied to access global");
4310       return false;
4311     }
4312     if (!global->is<GlobalObject>()) {
4313       JS_ReportErrorASCII(cx, "Argument must be a global object");
4314       return false;
4315     }
4316   } else {
4317     global = JS::CurrentGlobalOrNull(cx);
4318   }
4319 
4320   RootedObject varObj(cx);
4321   RootedObject lexicalScope(cx);
4322 
4323   {
4324     // If we're switching globals here, ExecuteInFrameScriptEnvironment will
4325     // take care of cloning the script into that compartment before
4326     // executing it.
4327     AutoRealm ar(cx, global);
4328     JS::RootedObject obj(cx, JS_NewPlainObject(cx));
4329     if (!obj) {
4330       return false;
4331     }
4332 
4333     if (!js::ExecuteInFrameScriptEnvironment(cx, obj, script, &lexicalScope)) {
4334       return false;
4335     }
4336 
4337     varObj = lexicalScope->enclosingEnvironment()->enclosingEnvironment();
4338   }
4339 
4340   RootedObject rv(cx, JS_NewPlainObject(cx));
4341   if (!rv) {
4342     return false;
4343   }
4344 
4345   RootedValue varObjVal(cx, ObjectValue(*varObj));
4346   if (!cx->compartment()->wrap(cx, &varObjVal)) {
4347     return false;
4348   }
4349   if (!JS_SetProperty(cx, rv, "vars", varObjVal)) {
4350     return false;
4351   }
4352 
4353   RootedValue lexicalScopeVal(cx, ObjectValue(*lexicalScope));
4354   if (!cx->compartment()->wrap(cx, &lexicalScopeVal)) {
4355     return false;
4356   }
4357   if (!JS_SetProperty(cx, rv, "lexicals", lexicalScopeVal)) {
4358     return false;
4359   }
4360 
4361   args.rval().setObject(*rv);
4362   return true;
4363 }
4364 
ShellCloneAndExecuteScript(JSContext * cx,unsigned argc,Value * vp)4365 static bool ShellCloneAndExecuteScript(JSContext* cx, unsigned argc,
4366                                        Value* vp) {
4367   CallArgs args = CallArgsFromVp(argc, vp);
4368   if (!args.requireAtLeast(cx, "cloneAndExecuteScript", 2)) {
4369     return false;
4370   }
4371 
4372   RootedString str(cx, ToString(cx, args[0]));
4373   if (!str) {
4374     return false;
4375   }
4376 
4377   RootedObject global(cx, ToObject(cx, args[1]));
4378   if (!global) {
4379     return false;
4380   }
4381 
4382   AutoStableStringChars strChars(cx);
4383   if (!strChars.initTwoByte(cx, str)) {
4384     return false;
4385   }
4386 
4387   mozilla::Range<const char16_t> chars = strChars.twoByteRange();
4388   size_t srclen = chars.length();
4389   const char16_t* src = chars.begin().get();
4390 
4391   JS::AutoFilename filename;
4392   unsigned lineno;
4393 
4394   JS::DescribeScriptedCaller(cx, &filename, &lineno);
4395 
4396   JS::CompileOptions options(cx);
4397   options.setFileAndLine(filename.get(), lineno);
4398 
4399   JS::SourceText<char16_t> srcBuf;
4400   if (!srcBuf.init(cx, src, srclen, SourceOwnership::Borrowed)) {
4401     return false;
4402   }
4403 
4404   RootedScript script(cx, JS::Compile(cx, options, srcBuf));
4405   if (!script) {
4406     return false;
4407   }
4408 
4409   global = CheckedUnwrapDynamic(global, cx, /* stopAtWindowProxy = */ false);
4410   if (!global) {
4411     JS_ReportErrorASCII(cx, "Permission denied to access global");
4412     return false;
4413   }
4414   if (!global->is<GlobalObject>()) {
4415     JS_ReportErrorASCII(cx, "Argument must be a global object");
4416     return false;
4417   }
4418 
4419   JS::RootedValue rval(cx);
4420   {
4421     AutoRealm ar(cx, global);
4422     if (!JS::CloneAndExecuteScript(cx, script, &rval)) {
4423       return false;
4424     }
4425   }
4426 
4427   if (!cx->compartment()->wrap(cx, &rval)) {
4428     return false;
4429   }
4430 
4431   args.rval().set(rval);
4432   return true;
4433 }
4434 
ByteSize(JSContext * cx,unsigned argc,Value * vp)4435 static bool ByteSize(JSContext* cx, unsigned argc, Value* vp) {
4436   CallArgs args = CallArgsFromVp(argc, vp);
4437   mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf;
4438 
4439   {
4440     // We can't tolerate the GC moving things around while we're using a
4441     // ubi::Node. Check that nothing we do causes a GC.
4442     JS::AutoCheckCannotGC autoCannotGC;
4443 
4444     JS::ubi::Node node = args.get(0);
4445     if (node) {
4446       args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
4447     } else {
4448       args.rval().setUndefined();
4449     }
4450   }
4451   return true;
4452 }
4453 
ByteSizeOfScript(JSContext * cx,unsigned argc,Value * vp)4454 static bool ByteSizeOfScript(JSContext* cx, unsigned argc, Value* vp) {
4455   CallArgs args = CallArgsFromVp(argc, vp);
4456   if (!args.requireAtLeast(cx, "byteSizeOfScript", 1)) {
4457     return false;
4458   }
4459   if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
4460     JS_ReportErrorASCII(cx, "Argument must be a Function object");
4461     return false;
4462   }
4463 
4464   RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
4465   if (fun->isNative()) {
4466     JS_ReportErrorASCII(cx, "Argument must be a scripted function");
4467     return false;
4468   }
4469 
4470   RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
4471   if (!script) {
4472     return false;
4473   }
4474 
4475   mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf;
4476 
4477   {
4478     // We can't tolerate the GC moving things around while we're using a
4479     // ubi::Node. Check that nothing we do causes a GC.
4480     JS::AutoCheckCannotGC autoCannotGC;
4481 
4482     JS::ubi::Node node = script;
4483     if (node) {
4484       args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
4485     } else {
4486       args.rval().setUndefined();
4487     }
4488   }
4489   return true;
4490 }
4491 
SetImmutablePrototype(JSContext * cx,unsigned argc,Value * vp)4492 static bool SetImmutablePrototype(JSContext* cx, unsigned argc, Value* vp) {
4493   CallArgs args = CallArgsFromVp(argc, vp);
4494   if (!args.get(0).isObject()) {
4495     JS_ReportErrorASCII(cx, "setImmutablePrototype: object expected");
4496     return false;
4497   }
4498 
4499   RootedObject obj(cx, &args[0].toObject());
4500 
4501   bool succeeded;
4502   if (!js::SetImmutablePrototype(cx, obj, &succeeded)) {
4503     return false;
4504   }
4505 
4506   args.rval().setBoolean(succeeded);
4507   return true;
4508 }
4509 
4510 #ifdef DEBUG
DumpStringRepresentation(JSContext * cx,unsigned argc,Value * vp)4511 static bool DumpStringRepresentation(JSContext* cx, unsigned argc, Value* vp) {
4512   CallArgs args = CallArgsFromVp(argc, vp);
4513 
4514   RootedString str(cx, ToString(cx, args.get(0)));
4515   if (!str) {
4516     return false;
4517   }
4518 
4519   Fprinter out(stderr);
4520   str->dumpRepresentation(out, 0);
4521 
4522   args.rval().setUndefined();
4523   return true;
4524 }
4525 #endif
4526 
SetLazyParsingDisabled(JSContext * cx,unsigned argc,Value * vp)4527 static bool SetLazyParsingDisabled(JSContext* cx, unsigned argc, Value* vp) {
4528   CallArgs args = CallArgsFromVp(argc, vp);
4529 
4530   bool disable = !args.hasDefined(0) || ToBoolean(args[0]);
4531   cx->realm()->behaviors().setDisableLazyParsing(disable);
4532 
4533   args.rval().setUndefined();
4534   return true;
4535 }
4536 
SetDiscardSource(JSContext * cx,unsigned argc,Value * vp)4537 static bool SetDiscardSource(JSContext* cx, unsigned argc, Value* vp) {
4538   CallArgs args = CallArgsFromVp(argc, vp);
4539 
4540   bool discard = !args.hasDefined(0) || ToBoolean(args[0]);
4541   cx->realm()->behaviors().setDiscardSource(discard);
4542 
4543   args.rval().setUndefined();
4544   return true;
4545 }
4546 
GetConstructorName(JSContext * cx,unsigned argc,Value * vp)4547 static bool GetConstructorName(JSContext* cx, unsigned argc, Value* vp) {
4548   CallArgs args = CallArgsFromVp(argc, vp);
4549   if (!args.requireAtLeast(cx, "getConstructorName", 1)) {
4550     return false;
4551   }
4552 
4553   if (!args[0].isObject()) {
4554     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4555                               JSMSG_NOT_EXPECTED_TYPE, "getConstructorName",
4556                               "Object", InformalValueTypeName(args[0]));
4557     return false;
4558   }
4559 
4560   RootedAtom name(cx);
4561   RootedObject obj(cx, &args[0].toObject());
4562   if (!JSObject::constructorDisplayAtom(cx, obj, &name)) {
4563     return false;
4564   }
4565 
4566   if (name) {
4567     args.rval().setString(name);
4568   } else {
4569     args.rval().setNull();
4570   }
4571   return true;
4572 }
4573 
4574 class AllocationMarkerObject : public NativeObject {
4575  public:
4576   static const JSClass class_;
4577 };
4578 
4579 const JSClass AllocationMarkerObject::class_ = {"AllocationMarker"};
4580 
AllocationMarker(JSContext * cx,unsigned argc,Value * vp)4581 static bool AllocationMarker(JSContext* cx, unsigned argc, Value* vp) {
4582   CallArgs args = CallArgsFromVp(argc, vp);
4583 
4584   bool allocateInsideNursery = true;
4585   if (args.length() > 0 && args[0].isObject()) {
4586     RootedObject options(cx, &args[0].toObject());
4587 
4588     RootedValue nurseryVal(cx);
4589     if (!JS_GetProperty(cx, options, "nursery", &nurseryVal)) {
4590       return false;
4591     }
4592     allocateInsideNursery = ToBoolean(nurseryVal);
4593   }
4594 
4595   JSObject* obj =
4596       allocateInsideNursery
4597           ? NewObjectWithGivenProto<AllocationMarkerObject>(cx, nullptr)
4598           : NewTenuredObjectWithGivenProto<AllocationMarkerObject>(cx, nullptr);
4599   if (!obj) {
4600     return false;
4601   }
4602 
4603   args.rval().setObject(*obj);
4604   return true;
4605 }
4606 
4607 namespace gcCallback {
4608 
4609 struct MajorGC {
4610   int32_t depth;
4611   int32_t phases;
4612 };
4613 
majorGC(JSContext * cx,JSGCStatus status,JS::GCReason reason,void * data)4614 static void majorGC(JSContext* cx, JSGCStatus status, JS::GCReason reason,
4615                     void* data) {
4616   auto info = static_cast<MajorGC*>(data);
4617   if (!(info->phases & (1 << status))) {
4618     return;
4619   }
4620 
4621   if (info->depth > 0) {
4622     info->depth--;
4623     JS::PrepareForFullGC(cx);
4624     JS::NonIncrementalGC(cx, GC_NORMAL, JS::GCReason::API);
4625     info->depth++;
4626   }
4627 }
4628 
4629 struct MinorGC {
4630   int32_t phases;
4631   bool active;
4632 };
4633 
minorGC(JSContext * cx,JSGCStatus status,JS::GCReason reason,void * data)4634 static void minorGC(JSContext* cx, JSGCStatus status, JS::GCReason reason,
4635                     void* data) {
4636   auto info = static_cast<MinorGC*>(data);
4637   if (!(info->phases & (1 << status))) {
4638     return;
4639   }
4640 
4641   if (info->active) {
4642     info->active = false;
4643     if (cx->zone() && !cx->zone()->isAtomsZone()) {
4644       cx->runtime()->gc.evictNursery(JS::GCReason::DEBUG_GC);
4645     }
4646     info->active = true;
4647   }
4648 }
4649 
4650 // Process global, should really be runtime-local.
4651 static MajorGC majorGCInfo;
4652 static MinorGC minorGCInfo;
4653 
enterNullRealm(JSContext * cx,JSGCStatus status,JS::GCReason reason,void * data)4654 static void enterNullRealm(JSContext* cx, JSGCStatus status,
4655                            JS::GCReason reason, void* data) {
4656   JSAutoNullableRealm enterRealm(cx, nullptr);
4657 }
4658 
4659 } /* namespace gcCallback */
4660 
SetGCCallback(JSContext * cx,unsigned argc,Value * vp)4661 static bool SetGCCallback(JSContext* cx, unsigned argc, Value* vp) {
4662   CallArgs args = CallArgsFromVp(argc, vp);
4663 
4664   if (args.length() != 1) {
4665     JS_ReportErrorASCII(cx, "Wrong number of arguments");
4666     return false;
4667   }
4668 
4669   RootedObject opts(cx, ToObject(cx, args[0]));
4670   if (!opts) {
4671     return false;
4672   }
4673 
4674   RootedValue v(cx);
4675   if (!JS_GetProperty(cx, opts, "action", &v)) {
4676     return false;
4677   }
4678 
4679   JSString* str = JS::ToString(cx, v);
4680   if (!str) {
4681     return false;
4682   }
4683   RootedLinearString action(cx, str->ensureLinear(cx));
4684   if (!action) {
4685     return false;
4686   }
4687 
4688   int32_t phases = 0;
4689   if (StringEqualsLiteral(action, "minorGC") ||
4690       StringEqualsLiteral(action, "majorGC")) {
4691     if (!JS_GetProperty(cx, opts, "phases", &v)) {
4692       return false;
4693     }
4694     if (v.isUndefined()) {
4695       phases = (1 << JSGC_END);
4696     } else {
4697       JSString* str = JS::ToString(cx, v);
4698       if (!str) {
4699         return false;
4700       }
4701       JSLinearString* phasesStr = str->ensureLinear(cx);
4702       if (!phasesStr) {
4703         return false;
4704       }
4705 
4706       if (StringEqualsLiteral(phasesStr, "begin")) {
4707         phases = (1 << JSGC_BEGIN);
4708       } else if (StringEqualsLiteral(phasesStr, "end")) {
4709         phases = (1 << JSGC_END);
4710       } else if (StringEqualsLiteral(phasesStr, "both")) {
4711         phases = (1 << JSGC_BEGIN) | (1 << JSGC_END);
4712       } else {
4713         JS_ReportErrorASCII(cx, "Invalid callback phase");
4714         return false;
4715       }
4716     }
4717   }
4718 
4719   if (StringEqualsLiteral(action, "minorGC")) {
4720     gcCallback::minorGCInfo.phases = phases;
4721     gcCallback::minorGCInfo.active = true;
4722     JS_SetGCCallback(cx, gcCallback::minorGC, &gcCallback::minorGCInfo);
4723   } else if (StringEqualsLiteral(action, "majorGC")) {
4724     if (!JS_GetProperty(cx, opts, "depth", &v)) {
4725       return false;
4726     }
4727     int32_t depth = 1;
4728     if (!v.isUndefined()) {
4729       if (!ToInt32(cx, v, &depth)) {
4730         return false;
4731       }
4732     }
4733     if (depth < 0) {
4734       JS_ReportErrorASCII(cx, "Nesting depth cannot be negative");
4735       return false;
4736     }
4737     if (depth + gcstats::MAX_PHASE_NESTING >
4738         gcstats::Statistics::MAX_SUSPENDED_PHASES) {
4739       JS_ReportErrorASCII(cx, "Nesting depth too large, would overflow");
4740       return false;
4741     }
4742 
4743     gcCallback::majorGCInfo.phases = phases;
4744     gcCallback::majorGCInfo.depth = depth;
4745     JS_SetGCCallback(cx, gcCallback::majorGC, &gcCallback::majorGCInfo);
4746   } else if (StringEqualsLiteral(action, "enterNullRealm")) {
4747     JS_SetGCCallback(cx, gcCallback::enterNullRealm, nullptr);
4748   } else {
4749     JS_ReportErrorASCII(cx, "Unknown GC callback action");
4750     return false;
4751   }
4752 
4753   args.rval().setUndefined();
4754   return true;
4755 }
4756 
4757 #ifdef DEBUG
EnqueueMark(JSContext * cx,unsigned argc,Value * vp)4758 static bool EnqueueMark(JSContext* cx, unsigned argc, Value* vp) {
4759   CallArgs args = CallArgsFromVp(argc, vp);
4760 
4761   auto& queue = cx->runtime()->gc.marker.markQueue;
4762 
4763   if (args.get(0).isString()) {
4764     RootedString val(cx, args[0].toString());
4765     if (!val->ensureLinear(cx)) {
4766       return false;
4767     }
4768     if (!queue.append(StringValue(val))) {
4769       JS_ReportOutOfMemory(cx);
4770       return false;
4771     }
4772   } else if (args.get(0).isObject()) {
4773     if (!queue.append(args[0])) {
4774       JS_ReportOutOfMemory(cx);
4775       return false;
4776     }
4777   } else {
4778     JS_ReportErrorASCII(cx, "Argument must be a string or object");
4779     return false;
4780   }
4781 
4782   args.rval().setUndefined();
4783   return true;
4784 }
4785 
GetMarkQueue(JSContext * cx,unsigned argc,Value * vp)4786 static bool GetMarkQueue(JSContext* cx, unsigned argc, Value* vp) {
4787   CallArgs args = CallArgsFromVp(argc, vp);
4788 
4789   auto& queue = cx->runtime()->gc.marker.markQueue.get();
4790 
4791   RootedObject result(cx, JS::NewArrayObject(cx, queue.length()));
4792   if (!result) {
4793     return false;
4794   }
4795   for (size_t i = 0; i < queue.length(); i++) {
4796     RootedValue val(cx, queue[i]);
4797     if (!JS_WrapValue(cx, &val)) {
4798       return false;
4799     }
4800     if (!JS_SetElement(cx, result, i, val)) {
4801       return false;
4802     }
4803   }
4804 
4805   args.rval().setObject(*result);
4806   return true;
4807 }
4808 
ClearMarkQueue(JSContext * cx,unsigned argc,Value * vp)4809 static bool ClearMarkQueue(JSContext* cx, unsigned argc, Value* vp) {
4810   CallArgs args = CallArgsFromVp(argc, vp);
4811 
4812   cx->runtime()->gc.marker.markQueue.clear();
4813   args.rval().setUndefined();
4814   return true;
4815 }
4816 #endif  // DEBUG
4817 
GetLcovInfo(JSContext * cx,unsigned argc,Value * vp)4818 static bool GetLcovInfo(JSContext* cx, unsigned argc, Value* vp) {
4819   CallArgs args = CallArgsFromVp(argc, vp);
4820 
4821   if (args.length() > 1) {
4822     JS_ReportErrorASCII(cx, "Wrong number of arguments");
4823     return false;
4824   }
4825 
4826   if (!coverage::IsLCovEnabled()) {
4827     JS_ReportErrorASCII(cx, "Coverage not enabled for process.");
4828     return false;
4829   }
4830 
4831   RootedObject global(cx);
4832   if (args.hasDefined(0)) {
4833     global = ToObject(cx, args[0]);
4834     if (!global) {
4835       JS_ReportErrorASCII(cx, "Permission denied to access global");
4836       return false;
4837     }
4838     global = CheckedUnwrapDynamic(global, cx, /* stopAtWindowProxy = */ false);
4839     if (!global) {
4840       ReportAccessDenied(cx);
4841       return false;
4842     }
4843     if (!global->is<GlobalObject>()) {
4844       JS_ReportErrorASCII(cx, "Argument must be a global object");
4845       return false;
4846     }
4847   } else {
4848     global = JS::CurrentGlobalOrNull(cx);
4849   }
4850 
4851   size_t length = 0;
4852   UniqueChars content;
4853   {
4854     AutoRealm ar(cx, global);
4855     content = js::GetCodeCoverageSummary(cx, &length);
4856   }
4857 
4858   if (!content) {
4859     return false;
4860   }
4861 
4862   JSString* str = JS_NewStringCopyN(cx, content.get(), length);
4863   if (!str) {
4864     return false;
4865   }
4866 
4867   args.rval().setString(str);
4868   return true;
4869 }
4870 
4871 #ifdef DEBUG
SetRNGState(JSContext * cx,unsigned argc,Value * vp)4872 static bool SetRNGState(JSContext* cx, unsigned argc, Value* vp) {
4873   CallArgs args = CallArgsFromVp(argc, vp);
4874   if (!args.requireAtLeast(cx, "SetRNGState", 2)) {
4875     return false;
4876   }
4877 
4878   double d0;
4879   if (!ToNumber(cx, args[0], &d0)) {
4880     return false;
4881   }
4882 
4883   double d1;
4884   if (!ToNumber(cx, args[1], &d1)) {
4885     return false;
4886   }
4887 
4888   uint64_t seed0 = static_cast<uint64_t>(d0);
4889   uint64_t seed1 = static_cast<uint64_t>(d1);
4890 
4891   if (seed0 == 0 && seed1 == 0) {
4892     JS_ReportErrorASCII(cx, "RNG requires non-zero seed");
4893     return false;
4894   }
4895 
4896   cx->realm()->getOrCreateRandomNumberGenerator().setState(seed0, seed1);
4897 
4898   args.rval().setUndefined();
4899   return true;
4900 }
4901 #endif
4902 
GetModuleEnvironment(JSContext * cx,HandleModuleObject module)4903 static ModuleEnvironmentObject* GetModuleEnvironment(
4904     JSContext* cx, HandleModuleObject module) {
4905   // Use the initial environment so that tests can check bindings exists
4906   // before they have been instantiated.
4907   RootedModuleEnvironmentObject env(cx, &module->initialEnvironment());
4908   MOZ_ASSERT(env);
4909   return env;
4910 }
4911 
GetModuleEnvironmentNames(JSContext * cx,unsigned argc,Value * vp)4912 static bool GetModuleEnvironmentNames(JSContext* cx, unsigned argc, Value* vp) {
4913   CallArgs args = CallArgsFromVp(argc, vp);
4914   if (args.length() != 1) {
4915     JS_ReportErrorASCII(cx, "Wrong number of arguments");
4916     return false;
4917   }
4918 
4919   if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
4920     JS_ReportErrorASCII(cx, "First argument should be a ModuleObject");
4921     return false;
4922   }
4923 
4924   RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
4925   if (module->hadEvaluationError()) {
4926     JS_ReportErrorASCII(cx, "Module environment unavailable");
4927     return false;
4928   }
4929 
4930   RootedModuleEnvironmentObject env(cx, GetModuleEnvironment(cx, module));
4931   Rooted<IdVector> ids(cx, IdVector(cx));
4932   if (!JS_Enumerate(cx, env, &ids)) {
4933     return false;
4934   }
4935 
4936   uint32_t length = ids.length();
4937   RootedArrayObject array(cx, NewDenseFullyAllocatedArray(cx, length));
4938   if (!array) {
4939     return false;
4940   }
4941 
4942   array->setDenseInitializedLength(length);
4943   for (uint32_t i = 0; i < length; i++) {
4944     array->initDenseElement(i, StringValue(JSID_TO_STRING(ids[i])));
4945   }
4946 
4947   args.rval().setObject(*array);
4948   return true;
4949 }
4950 
GetModuleEnvironmentValue(JSContext * cx,unsigned argc,Value * vp)4951 static bool GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp) {
4952   CallArgs args = CallArgsFromVp(argc, vp);
4953   if (args.length() != 2) {
4954     JS_ReportErrorASCII(cx, "Wrong number of arguments");
4955     return false;
4956   }
4957 
4958   if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
4959     JS_ReportErrorASCII(cx, "First argument should be a ModuleObject");
4960     return false;
4961   }
4962 
4963   if (!args[1].isString()) {
4964     JS_ReportErrorASCII(cx, "Second argument should be a string");
4965     return false;
4966   }
4967 
4968   RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
4969   if (module->hadEvaluationError()) {
4970     JS_ReportErrorASCII(cx, "Module environment unavailable");
4971     return false;
4972   }
4973 
4974   RootedModuleEnvironmentObject env(cx, GetModuleEnvironment(cx, module));
4975   RootedString name(cx, args[1].toString());
4976   RootedId id(cx);
4977   if (!JS_StringToId(cx, name, &id)) {
4978     return false;
4979   }
4980 
4981   if (!GetProperty(cx, env, env, id, args.rval())) {
4982     return false;
4983   }
4984 
4985   if (args.rval().isMagic(JS_UNINITIALIZED_LEXICAL)) {
4986     ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, id);
4987     return false;
4988   }
4989 
4990   return true;
4991 }
4992 
4993 #if defined(DEBUG) && !defined(ENABLE_NEW_REGEXP)
AssertionTypeToString(irregexp::RegExpAssertion::AssertionType type)4994 static const char* AssertionTypeToString(
4995     irregexp::RegExpAssertion::AssertionType type) {
4996   switch (type) {
4997     case irregexp::RegExpAssertion::START_OF_LINE:
4998       return "START_OF_LINE";
4999     case irregexp::RegExpAssertion::START_OF_INPUT:
5000       return "START_OF_INPUT";
5001     case irregexp::RegExpAssertion::END_OF_LINE:
5002       return "END_OF_LINE";
5003     case irregexp::RegExpAssertion::END_OF_INPUT:
5004       return "END_OF_INPUT";
5005     case irregexp::RegExpAssertion::BOUNDARY:
5006       return "BOUNDARY";
5007     case irregexp::RegExpAssertion::NON_BOUNDARY:
5008       return "NON_BOUNDARY";
5009     case irregexp::RegExpAssertion::NOT_AFTER_LEAD_SURROGATE:
5010       return "NOT_AFTER_LEAD_SURROGATE";
5011     case irregexp::RegExpAssertion::NOT_IN_SURROGATE_PAIR:
5012       return "NOT_IN_SURROGATE_PAIR";
5013   }
5014   MOZ_CRASH("unexpected AssertionType");
5015 }
5016 
ConvertRegExpTreeToObject(JSContext * cx,LifoAlloc & alloc,irregexp::RegExpTree * tree)5017 static JSObject* ConvertRegExpTreeToObject(JSContext* cx, LifoAlloc& alloc,
5018                                            irregexp::RegExpTree* tree) {
5019   RootedObject obj(cx, JS_NewPlainObject(cx));
5020   if (!obj) {
5021     return nullptr;
5022   }
5023 
5024   auto IntProp = [](JSContext* cx, HandleObject obj, const char* name,
5025                     int32_t value) {
5026     RootedValue val(cx, Int32Value(value));
5027     return JS_SetProperty(cx, obj, name, val);
5028   };
5029 
5030   auto BooleanProp = [](JSContext* cx, HandleObject obj, const char* name,
5031                         bool value) {
5032     RootedValue val(cx, BooleanValue(value));
5033     return JS_SetProperty(cx, obj, name, val);
5034   };
5035 
5036   auto StringProp = [](JSContext* cx, HandleObject obj, const char* name,
5037                        const char* value) {
5038     RootedString valueStr(cx, JS_NewStringCopyZ(cx, value));
5039     if (!valueStr) {
5040       return false;
5041     }
5042 
5043     RootedValue val(cx, StringValue(valueStr));
5044     return JS_SetProperty(cx, obj, name, val);
5045   };
5046 
5047   auto ObjectProp = [](JSContext* cx, HandleObject obj, const char* name,
5048                        HandleObject value) {
5049     RootedValue val(cx, ObjectValue(*value));
5050     return JS_SetProperty(cx, obj, name, val);
5051   };
5052 
5053   auto CharVectorProp = [](JSContext* cx, HandleObject obj, const char* name,
5054                            const irregexp::CharacterVector& data) {
5055     RootedString valueStr(cx,
5056                           JS_NewUCStringCopyN(cx, data.begin(), data.length()));
5057     if (!valueStr) {
5058       return false;
5059     }
5060 
5061     RootedValue val(cx, StringValue(valueStr));
5062     return JS_SetProperty(cx, obj, name, val);
5063   };
5064 
5065   auto TreeProp = [&ObjectProp, &alloc](JSContext* cx, HandleObject obj,
5066                                         const char* name,
5067                                         irregexp::RegExpTree* tree) {
5068     RootedObject treeObj(cx, ConvertRegExpTreeToObject(cx, alloc, tree));
5069     if (!treeObj) {
5070       return false;
5071     }
5072     return ObjectProp(cx, obj, name, treeObj);
5073   };
5074 
5075   auto TreeVectorProp = [&ObjectProp, &alloc](
5076                             JSContext* cx, HandleObject obj, const char* name,
5077                             const irregexp::RegExpTreeVector& nodes) {
5078     size_t len = nodes.length();
5079     RootedObject array(cx, JS::NewArrayObject(cx, len));
5080     if (!array) {
5081       return false;
5082     }
5083 
5084     for (size_t i = 0; i < len; i++) {
5085       RootedObject child(cx, ConvertRegExpTreeToObject(cx, alloc, nodes[i]));
5086       if (!child) {
5087         return false;
5088       }
5089 
5090       RootedValue childVal(cx, ObjectValue(*child));
5091       if (!JS_SetElement(cx, array, i, childVal)) {
5092         return false;
5093       }
5094     }
5095     return ObjectProp(cx, obj, name, array);
5096   };
5097 
5098   auto CharRangesProp = [&ObjectProp](
5099                             JSContext* cx, HandleObject obj, const char* name,
5100                             const irregexp::CharacterRangeVector& ranges) {
5101     size_t len = ranges.length();
5102     RootedObject array(cx, JS::NewArrayObject(cx, len));
5103     if (!array) {
5104       return false;
5105     }
5106 
5107     for (size_t i = 0; i < len; i++) {
5108       const irregexp::CharacterRange& range = ranges[i];
5109       RootedObject rangeObj(cx, JS_NewPlainObject(cx));
5110       if (!rangeObj) {
5111         return false;
5112       }
5113 
5114       auto CharProp = [](JSContext* cx, HandleObject obj, const char* name,
5115                          char16_t c) {
5116         RootedString valueStr(cx, JS_NewUCStringCopyN(cx, &c, 1));
5117         if (!valueStr) {
5118           return false;
5119         }
5120         RootedValue val(cx, StringValue(valueStr));
5121         return JS_SetProperty(cx, obj, name, val);
5122       };
5123 
5124       if (!CharProp(cx, rangeObj, "from", range.from())) {
5125         return false;
5126       }
5127       if (!CharProp(cx, rangeObj, "to", range.to())) {
5128         return false;
5129       }
5130 
5131       RootedValue rangeVal(cx, ObjectValue(*rangeObj));
5132       if (!JS_SetElement(cx, array, i, rangeVal)) {
5133         return false;
5134       }
5135     }
5136     return ObjectProp(cx, obj, name, array);
5137   };
5138 
5139   auto ElemProp = [&ObjectProp, &alloc](
5140                       JSContext* cx, HandleObject obj, const char* name,
5141                       const irregexp::TextElementVector& elements) {
5142     size_t len = elements.length();
5143     RootedObject array(cx, JS::NewArrayObject(cx, len));
5144     if (!array) {
5145       return false;
5146     }
5147 
5148     for (size_t i = 0; i < len; i++) {
5149       const irregexp::TextElement& element = elements[i];
5150       RootedObject elemTree(
5151           cx, ConvertRegExpTreeToObject(cx, alloc, element.tree()));
5152       if (!elemTree) {
5153         return false;
5154       }
5155 
5156       RootedValue elemTreeVal(cx, ObjectValue(*elemTree));
5157       if (!JS_SetElement(cx, array, i, elemTreeVal)) {
5158         return false;
5159       }
5160     }
5161     return ObjectProp(cx, obj, name, array);
5162   };
5163 
5164   if (tree->IsDisjunction()) {
5165     if (!StringProp(cx, obj, "type", "Disjunction")) {
5166       return nullptr;
5167     }
5168     irregexp::RegExpDisjunction* t = tree->AsDisjunction();
5169     if (!TreeVectorProp(cx, obj, "alternatives", t->alternatives())) {
5170       return nullptr;
5171     }
5172     return obj;
5173   }
5174   if (tree->IsAlternative()) {
5175     if (!StringProp(cx, obj, "type", "Alternative")) {
5176       return nullptr;
5177     }
5178     irregexp::RegExpAlternative* t = tree->AsAlternative();
5179     if (!TreeVectorProp(cx, obj, "nodes", t->nodes())) {
5180       return nullptr;
5181     }
5182     return obj;
5183   }
5184   if (tree->IsAssertion()) {
5185     if (!StringProp(cx, obj, "type", "Assertion")) {
5186       return nullptr;
5187     }
5188     irregexp::RegExpAssertion* t = tree->AsAssertion();
5189     if (!StringProp(cx, obj, "assertion_type",
5190                     AssertionTypeToString(t->assertion_type()))) {
5191       return nullptr;
5192     }
5193     return obj;
5194   }
5195   if (tree->IsCharacterClass()) {
5196     if (!StringProp(cx, obj, "type", "CharacterClass")) {
5197       return nullptr;
5198     }
5199     irregexp::RegExpCharacterClass* t = tree->AsCharacterClass();
5200     if (!BooleanProp(cx, obj, "is_negated", t->is_negated())) {
5201       return nullptr;
5202     }
5203     if (!CharRangesProp(cx, obj, "ranges", t->ranges(&alloc))) {
5204       return nullptr;
5205     }
5206     return obj;
5207   }
5208   if (tree->IsAtom()) {
5209     if (!StringProp(cx, obj, "type", "Atom")) {
5210       return nullptr;
5211     }
5212     irregexp::RegExpAtom* t = tree->AsAtom();
5213     if (!CharVectorProp(cx, obj, "data", t->data())) {
5214       return nullptr;
5215     }
5216     return obj;
5217   }
5218   if (tree->IsText()) {
5219     if (!StringProp(cx, obj, "type", "Text")) {
5220       return nullptr;
5221     }
5222     irregexp::RegExpText* t = tree->AsText();
5223     if (!ElemProp(cx, obj, "elements", t->elements())) {
5224       return nullptr;
5225     }
5226     return obj;
5227   }
5228   if (tree->IsQuantifier()) {
5229     if (!StringProp(cx, obj, "type", "Quantifier")) {
5230       return nullptr;
5231     }
5232     irregexp::RegExpQuantifier* t = tree->AsQuantifier();
5233     if (!IntProp(cx, obj, "min", t->min())) {
5234       return nullptr;
5235     }
5236     if (!IntProp(cx, obj, "max", t->max())) {
5237       return nullptr;
5238     }
5239     if (!StringProp(cx, obj, "quantifier_type",
5240                     t->is_possessive()
5241                         ? "POSSESSIVE"
5242                         : t->is_non_greedy() ? "NON_GREEDY" : "GREEDY"))
5243       return nullptr;
5244     if (!TreeProp(cx, obj, "body", t->body())) {
5245       return nullptr;
5246     }
5247     return obj;
5248   }
5249   if (tree->IsCapture()) {
5250     if (!StringProp(cx, obj, "type", "Capture")) {
5251       return nullptr;
5252     }
5253     irregexp::RegExpCapture* t = tree->AsCapture();
5254     if (!IntProp(cx, obj, "index", t->index())) {
5255       return nullptr;
5256     }
5257     if (!TreeProp(cx, obj, "body", t->body())) {
5258       return nullptr;
5259     }
5260     return obj;
5261   }
5262   if (tree->IsLookahead()) {
5263     if (!StringProp(cx, obj, "type", "Lookahead")) {
5264       return nullptr;
5265     }
5266     irregexp::RegExpLookahead* t = tree->AsLookahead();
5267     if (!BooleanProp(cx, obj, "is_positive", t->is_positive())) {
5268       return nullptr;
5269     }
5270     if (!TreeProp(cx, obj, "body", t->body())) {
5271       return nullptr;
5272     }
5273     return obj;
5274   }
5275   if (tree->IsBackReference()) {
5276     if (!StringProp(cx, obj, "type", "BackReference")) {
5277       return nullptr;
5278     }
5279     irregexp::RegExpBackReference* t = tree->AsBackReference();
5280     if (!IntProp(cx, obj, "index", t->index())) {
5281       return nullptr;
5282     }
5283     return obj;
5284   }
5285   if (tree->IsEmpty()) {
5286     if (!StringProp(cx, obj, "type", "Empty")) {
5287       return nullptr;
5288     }
5289     return obj;
5290   }
5291 
5292   MOZ_CRASH("unexpected RegExpTree type");
5293 }
5294 
ParseRegExp(JSContext * cx,unsigned argc,Value * vp)5295 static bool ParseRegExp(JSContext* cx, unsigned argc, Value* vp) {
5296   CallArgs args = CallArgsFromVp(argc, vp);
5297   RootedObject callee(cx, &args.callee());
5298 
5299   if (args.length() == 0) {
5300     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
5301     return false;
5302   }
5303 
5304   if (!args[0].isString()) {
5305     ReportUsageErrorASCII(cx, callee, "First argument must be a String");
5306     return false;
5307   }
5308 
5309   RegExpFlags flags = RegExpFlag::NoFlags;
5310   if (!args.get(1).isUndefined()) {
5311     if (!args.get(1).isString()) {
5312       ReportUsageErrorASCII(cx, callee,
5313                             "Second argument, if present, must be a String");
5314       return false;
5315     }
5316     RootedString flagStr(cx, args[1].toString());
5317     if (!ParseRegExpFlags(cx, flagStr, &flags)) {
5318       return false;
5319     }
5320   }
5321 
5322   bool match_only = false;
5323   if (!args.get(2).isUndefined()) {
5324     if (!args.get(2).isBoolean()) {
5325       ReportUsageErrorASCII(cx, callee,
5326                             "Third argument, if present, must be a Boolean");
5327       return false;
5328     }
5329     match_only = args[2].toBoolean();
5330   }
5331 
5332   RootedAtom pattern(cx, AtomizeString(cx, args[0].toString()));
5333   if (!pattern) {
5334     return false;
5335   }
5336 
5337   CompileOptions options(cx);
5338   frontend::DummyTokenStream dummyTokenStream(cx, options);
5339 
5340   // Data lifetime is controlled by LifoAllocScope.
5341   LifoAllocScope allocScope(&cx->tempLifoAlloc());
5342   irregexp::RegExpCompileData data;
5343   if (!irregexp::ParsePattern(dummyTokenStream, allocScope.alloc(), pattern,
5344                               match_only, flags, &data)) {
5345     return false;
5346   }
5347 
5348   RootedObject obj(
5349       cx, ConvertRegExpTreeToObject(cx, allocScope.alloc(), data.tree));
5350   if (!obj) {
5351     return false;
5352   }
5353 
5354   args.rval().setObject(*obj);
5355   return true;
5356 }
5357 
DisRegExp(JSContext * cx,unsigned argc,Value * vp)5358 static bool DisRegExp(JSContext* cx, unsigned argc, Value* vp) {
5359   CallArgs args = CallArgsFromVp(argc, vp);
5360   RootedObject callee(cx, &args.callee());
5361 
5362   if (args.length() == 0) {
5363     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
5364     return false;
5365   }
5366 
5367   if (!args[0].isObject() || !args[0].toObject().is<RegExpObject>()) {
5368     ReportUsageErrorASCII(cx, callee, "First argument must be a RegExp");
5369     return false;
5370   }
5371 
5372   Rooted<RegExpObject*> reobj(cx, &args[0].toObject().as<RegExpObject>());
5373 
5374   RootedLinearString input(cx, cx->runtime()->emptyString);
5375   if (!args.get(1).isUndefined()) {
5376     if (!args.get(1).isString()) {
5377       ReportUsageErrorASCII(cx, callee,
5378                             "Second argument, if present, must be a String");
5379       return false;
5380     }
5381     RootedString inputStr(cx, args[1].toString());
5382     input = inputStr->ensureLinear(cx);
5383     if (!input) {
5384       return false;
5385     }
5386   }
5387 
5388   if (!RegExpObject::dumpBytecode(cx, reobj, input)) {
5389     return false;
5390   }
5391 
5392   args.rval().setUndefined();
5393   return true;
5394 }
5395 #endif  // DEBUG && !ENABLE_NEW_REGEXP
5396 
GetTimeZone(JSContext * cx,unsigned argc,Value * vp)5397 static bool GetTimeZone(JSContext* cx, unsigned argc, Value* vp) {
5398   CallArgs args = CallArgsFromVp(argc, vp);
5399   RootedObject callee(cx, &args.callee());
5400 
5401   if (args.length() != 0) {
5402     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
5403     return false;
5404   }
5405 
5406   auto getTimeZone = [](std::time_t* now) -> const char* {
5407     std::tm local{};
5408 #if defined(_WIN32)
5409     _tzset();
5410     if (localtime_s(&local, now) == 0) {
5411       return _tzname[local.tm_isdst > 0];
5412     }
5413 #else
5414     tzset();
5415 #  if defined(HAVE_LOCALTIME_R)
5416     if (localtime_r(now, &local)) {
5417 #  else
5418     std::tm* localtm = std::localtime(now);
5419     if (localtm) {
5420       *local = *localtm;
5421 #  endif /* HAVE_LOCALTIME_R */
5422 
5423 #  if defined(HAVE_TM_ZONE_TM_GMTOFF)
5424       return local.tm_zone;
5425 #  else
5426       return tzname[local.tm_isdst > 0];
5427 #  endif /* HAVE_TM_ZONE_TM_GMTOFF */
5428     }
5429 #endif   /* _WIN32 */
5430     return nullptr;
5431   };
5432 
5433   std::time_t now = std::time(nullptr);
5434   if (now != static_cast<std::time_t>(-1)) {
5435     if (const char* tz = getTimeZone(&now)) {
5436       return ReturnStringCopy(cx, args, tz);
5437     }
5438   }
5439 
5440   args.rval().setUndefined();
5441   return true;
5442 }
5443 
5444 static bool SetTimeZone(JSContext* cx, unsigned argc, Value* vp) {
5445   CallArgs args = CallArgsFromVp(argc, vp);
5446   RootedObject callee(cx, &args.callee());
5447 
5448   if (args.length() != 1) {
5449     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
5450     return false;
5451   }
5452 
5453   if (!args[0].isString() && !args[0].isUndefined()) {
5454     ReportUsageErrorASCII(cx, callee,
5455                           "First argument should be a string or undefined");
5456     return false;
5457   }
5458 
5459   auto setTimeZone = [](const char* value) {
5460 #if defined(_WIN32)
5461     return _putenv_s("TZ", value) == 0;
5462 #else
5463     return setenv("TZ", value, true) == 0;
5464 #endif /* _WIN32 */
5465   };
5466 
5467   auto unsetTimeZone = []() {
5468 #if defined(_WIN32)
5469     return _putenv_s("TZ", "") == 0;
5470 #else
5471     return unsetenv("TZ") == 0;
5472 #endif /* _WIN32 */
5473   };
5474 
5475   if (args[0].isString() && !args[0].toString()->empty()) {
5476     RootedLinearString str(cx, args[0].toString()->ensureLinear(cx));
5477     if (!str) {
5478       return false;
5479     }
5480 
5481     if (!StringIsAscii(str)) {
5482       ReportUsageErrorASCII(cx, callee,
5483                             "First argument contains non-ASCII characters");
5484       return false;
5485     }
5486 
5487     UniqueChars timeZone = JS_EncodeStringToASCII(cx, str);
5488     if (!timeZone) {
5489       return false;
5490     }
5491 
5492     if (!setTimeZone(timeZone.get())) {
5493       JS_ReportErrorASCII(cx, "Failed to set 'TZ' environment variable");
5494       return false;
5495     }
5496   } else {
5497     if (!unsetTimeZone()) {
5498       JS_ReportErrorASCII(cx, "Failed to unset 'TZ' environment variable");
5499       return false;
5500     }
5501   }
5502 
5503 #if defined(_WIN32)
5504   _tzset();
5505 #else
5506   tzset();
5507 #endif /* _WIN32 */
5508 
5509   JS::ResetTimeZone();
5510 
5511   args.rval().setUndefined();
5512   return true;
5513 }
5514 
5515 static bool GetCoreCount(JSContext* cx, unsigned argc, Value* vp) {
5516   CallArgs args = CallArgsFromVp(argc, vp);
5517   RootedObject callee(cx, &args.callee());
5518 
5519   if (args.length() != 0) {
5520     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
5521     return false;
5522   }
5523 
5524   args.rval().setInt32(GetCPUCount());
5525   return true;
5526 }
5527 
5528 static bool GetDefaultLocale(JSContext* cx, unsigned argc, Value* vp) {
5529   CallArgs args = CallArgsFromVp(argc, vp);
5530   RootedObject callee(cx, &args.callee());
5531 
5532   if (args.length() != 0) {
5533     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
5534     return false;
5535   }
5536 
5537   UniqueChars locale = JS_GetDefaultLocale(cx);
5538   if (!locale) {
5539     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5540                               JSMSG_DEFAULT_LOCALE_ERROR);
5541     return false;
5542   }
5543 
5544   return ReturnStringCopy(cx, args, locale.get());
5545 }
5546 
5547 static bool SetDefaultLocale(JSContext* cx, unsigned argc, Value* vp) {
5548   CallArgs args = CallArgsFromVp(argc, vp);
5549   RootedObject callee(cx, &args.callee());
5550 
5551   if (args.length() != 1) {
5552     ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
5553     return false;
5554   }
5555 
5556   if (!args[0].isString() && !args[0].isUndefined()) {
5557     ReportUsageErrorASCII(cx, callee,
5558                           "First argument should be a string or undefined");
5559     return false;
5560   }
5561 
5562   if (args[0].isString() && !args[0].toString()->empty()) {
5563     RootedLinearString str(cx, args[0].toString()->ensureLinear(cx));
5564     if (!str) {
5565       return false;
5566     }
5567 
5568     if (!StringIsAscii(str)) {
5569       ReportUsageErrorASCII(cx, callee,
5570                             "First argument contains non-ASCII characters");
5571       return false;
5572     }
5573 
5574     UniqueChars locale = JS_EncodeStringToASCII(cx, str);
5575     if (!locale) {
5576       return false;
5577     }
5578 
5579     bool containsOnlyValidBCP47Characters =
5580         mozilla::IsAsciiAlpha(locale[0]) &&
5581         std::all_of(locale.get(), locale.get() + str->length(), [](auto c) {
5582           return mozilla::IsAsciiAlphanumeric(c) || c == '-';
5583         });
5584 
5585     if (!containsOnlyValidBCP47Characters) {
5586       ReportUsageErrorASCII(cx, callee,
5587                             "First argument should be a BCP47 language tag");
5588       return false;
5589     }
5590 
5591     if (!JS_SetDefaultLocale(cx->runtime(), locale.get())) {
5592       ReportOutOfMemory(cx);
5593       return false;
5594     }
5595   } else {
5596     JS_ResetDefaultLocale(cx->runtime());
5597   }
5598 
5599   args.rval().setUndefined();
5600   return true;
5601 }
5602 
5603 #if defined(FUZZING) && defined(__AFL_COMPILER)
5604 static bool AflLoop(JSContext* cx, unsigned argc, Value* vp) {
5605   CallArgs args = CallArgsFromVp(argc, vp);
5606 
5607   uint32_t max_cnt;
5608   if (!ToUint32(cx, args.get(0), &max_cnt)) {
5609     return false;
5610   }
5611 
5612   args.rval().setBoolean(!!__AFL_LOOP(max_cnt));
5613   return true;
5614 }
5615 #endif
5616 
5617 static bool MonotonicNow(JSContext* cx, unsigned argc, Value* vp) {
5618   CallArgs args = CallArgsFromVp(argc, vp);
5619   double now;
5620 
5621 // The std::chrono symbols are too new to be present in STL on all platforms we
5622 // care about, so use raw POSIX clock APIs when it might be necessary.
5623 #if defined(XP_UNIX) && !defined(XP_DARWIN)
5624   auto ComputeNow = [](const timespec& ts) {
5625     return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
5626   };
5627 
5628   timespec ts;
5629   if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
5630     // Use a monotonic clock if available.
5631     now = ComputeNow(ts);
5632   } else {
5633     // Use a realtime clock as fallback.
5634     if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
5635       // Fail if no clock is available.
5636       JS_ReportErrorASCII(cx, "can't retrieve system clock");
5637       return false;
5638     }
5639 
5640     now = ComputeNow(ts);
5641 
5642     // Manually enforce atomicity on a non-monotonic clock.
5643     {
5644       static mozilla::Atomic<bool, mozilla::ReleaseAcquire> spinLock;
5645       while (!spinLock.compareExchange(false, true)) {
5646         continue;
5647       }
5648 
5649       static double lastNow = -FLT_MAX;
5650       now = lastNow = std::max(now, lastNow);
5651 
5652       spinLock = false;
5653     }
5654   }
5655 #else
5656   using std::chrono::duration_cast;
5657   using std::chrono::milliseconds;
5658   using std::chrono::steady_clock;
5659   now = duration_cast<milliseconds>(steady_clock::now().time_since_epoch())
5660             .count();
5661 #endif  // XP_UNIX && !XP_DARWIN
5662 
5663   args.rval().setNumber(now);
5664   return true;
5665 }
5666 
5667 static bool TimeSinceCreation(JSContext* cx, unsigned argc, Value* vp) {
5668   CallArgs args = CallArgsFromVp(argc, vp);
5669   double when =
5670       (mozilla::TimeStamp::Now() - mozilla::TimeStamp::ProcessCreation())
5671           .ToMilliseconds();
5672   args.rval().setNumber(when);
5673   return true;
5674 }
5675 
5676 static bool GetErrorNotes(JSContext* cx, unsigned argc, Value* vp) {
5677   CallArgs args = CallArgsFromVp(argc, vp);
5678   if (!args.requireAtLeast(cx, "getErrorNotes", 1)) {
5679     return false;
5680   }
5681 
5682   if (!args[0].isObject() || !args[0].toObject().is<ErrorObject>()) {
5683     args.rval().setNull();
5684     return true;
5685   }
5686 
5687   JSErrorReport* report = args[0].toObject().as<ErrorObject>().getErrorReport();
5688   if (!report) {
5689     args.rval().setNull();
5690     return true;
5691   }
5692 
5693   RootedObject notesArray(cx, CreateErrorNotesArray(cx, report));
5694   if (!notesArray) {
5695     return false;
5696   }
5697 
5698   args.rval().setObject(*notesArray);
5699   return true;
5700 }
5701 
5702 static bool IsConstructor(JSContext* cx, unsigned argc, Value* vp) {
5703   CallArgs args = CallArgsFromVp(argc, vp);
5704   if (args.length() < 1) {
5705     args.rval().setBoolean(false);
5706   } else {
5707     args.rval().setBoolean(IsConstructor(args[0]));
5708   }
5709   return true;
5710 }
5711 
5712 static bool SetTimeResolution(JSContext* cx, unsigned argc, Value* vp) {
5713   CallArgs args = CallArgsFromVp(argc, vp);
5714   RootedObject callee(cx, &args.callee());
5715 
5716   if (!args.requireAtLeast(cx, "setTimeResolution", 2)) {
5717     return false;
5718   }
5719 
5720   if (!args[0].isInt32()) {
5721     ReportUsageErrorASCII(cx, callee, "First argument must be an Int32.");
5722     return false;
5723   }
5724   int32_t resolution = args[0].toInt32();
5725 
5726   if (!args[1].isBoolean()) {
5727     ReportUsageErrorASCII(cx, callee, "Second argument must be a Boolean");
5728     return false;
5729   }
5730   bool jitter = args[1].toBoolean();
5731 
5732   JS::SetTimeResolutionUsec(resolution, jitter);
5733 
5734   args.rval().setUndefined();
5735   return true;
5736 }
5737 
5738 static bool ScriptedCallerGlobal(JSContext* cx, unsigned argc, Value* vp) {
5739   CallArgs args = CallArgsFromVp(argc, vp);
5740 
5741   RootedObject obj(cx, JS::GetScriptedCallerGlobal(cx));
5742   if (!obj) {
5743     args.rval().setNull();
5744     return true;
5745   }
5746 
5747   obj = ToWindowProxyIfWindow(obj);
5748 
5749   if (!cx->compartment()->wrap(cx, &obj)) {
5750     return false;
5751   }
5752 
5753   args.rval().setObject(*obj);
5754   return true;
5755 }
5756 
5757 static bool ObjectGlobal(JSContext* cx, unsigned argc, Value* vp) {
5758   CallArgs args = CallArgsFromVp(argc, vp);
5759   RootedObject callee(cx, &args.callee());
5760 
5761   if (!args.get(0).isObject()) {
5762     ReportUsageErrorASCII(cx, callee, "Argument must be an object");
5763     return false;
5764   }
5765 
5766   RootedObject obj(cx, &args[0].toObject());
5767   if (IsCrossCompartmentWrapper(obj)) {
5768     args.rval().setNull();
5769     return true;
5770   }
5771 
5772   obj = ToWindowProxyIfWindow(&obj->nonCCWGlobal());
5773 
5774   args.rval().setObject(*obj);
5775   return true;
5776 }
5777 
5778 static bool IsSameCompartment(JSContext* cx, unsigned argc, Value* vp) {
5779   CallArgs args = CallArgsFromVp(argc, vp);
5780   RootedObject callee(cx, &args.callee());
5781 
5782   if (!args.get(0).isObject() || !args.get(1).isObject()) {
5783     ReportUsageErrorASCII(cx, callee, "Both arguments must be objects");
5784     return false;
5785   }
5786 
5787   RootedObject obj1(cx, UncheckedUnwrap(&args[0].toObject()));
5788   RootedObject obj2(cx, UncheckedUnwrap(&args[1].toObject()));
5789 
5790   args.rval().setBoolean(obj1->compartment() == obj2->compartment());
5791   return true;
5792 }
5793 
5794 static bool FirstGlobalInCompartment(JSContext* cx, unsigned argc, Value* vp) {
5795   CallArgs args = CallArgsFromVp(argc, vp);
5796   RootedObject callee(cx, &args.callee());
5797 
5798   if (!args.get(0).isObject()) {
5799     ReportUsageErrorASCII(cx, callee, "Argument must be an object");
5800     return false;
5801   }
5802 
5803   RootedObject obj(cx, UncheckedUnwrap(&args[0].toObject()));
5804   obj = ToWindowProxyIfWindow(GetFirstGlobalInCompartment(obj->compartment()));
5805 
5806   if (!cx->compartment()->wrap(cx, &obj)) {
5807     return false;
5808   }
5809 
5810   args.rval().setObject(*obj);
5811   return true;
5812 }
5813 
5814 static bool AssertCorrectRealm(JSContext* cx, unsigned argc, Value* vp) {
5815   CallArgs args = CallArgsFromVp(argc, vp);
5816   MOZ_RELEASE_ASSERT(cx->realm() == args.callee().as<JSFunction>().realm());
5817   args.rval().setUndefined();
5818   return true;
5819 }
5820 
5821 static bool GlobalLexicals(JSContext* cx, unsigned argc, Value* vp) {
5822   CallArgs args = CallArgsFromVp(argc, vp);
5823 
5824   Rooted<LexicalEnvironmentObject*> globalLexical(
5825       cx, &cx->global()->lexicalEnvironment());
5826 
5827   RootedIdVector props(cx);
5828   if (!GetPropertyKeys(cx, globalLexical, JSITER_HIDDEN, &props)) {
5829     return false;
5830   }
5831 
5832   RootedObject res(cx, JS_NewPlainObject(cx));
5833   if (!res) {
5834     return false;
5835   }
5836 
5837   RootedValue val(cx);
5838   for (size_t i = 0; i < props.length(); i++) {
5839     HandleId id = props[i];
5840     if (!JS_GetPropertyById(cx, globalLexical, id, &val)) {
5841       return false;
5842     }
5843     if (val.isMagic(JS_UNINITIALIZED_LEXICAL)) {
5844       continue;
5845     }
5846     if (!JS_DefinePropertyById(cx, res, id, val, JSPROP_ENUMERATE)) {
5847       return false;
5848     }
5849   }
5850 
5851   args.rval().setObject(*res);
5852   return true;
5853 }
5854 
5855 static bool MonitorType(JSContext* cx, unsigned argc, Value* vp) {
5856   CallArgs args = CallArgsFromVp(argc, vp);
5857   RootedObject callee(cx, &args.callee());
5858 
5859   // First argument must be either a scripted function or null/undefined (in
5860   // this case we use the caller's script).
5861   RootedFunction fun(cx);
5862   RootedScript script(cx);
5863   if (args.get(0).isNullOrUndefined()) {
5864     script = cx->currentScript();
5865     if (!script) {
5866       ReportUsageErrorASCII(cx, callee, "No scripted caller");
5867       return false;
5868     }
5869   } else {
5870     if (!IsFunctionObject(args.get(0), fun.address()) ||
5871         !fun->isInterpreted()) {
5872       ReportUsageErrorASCII(
5873           cx, callee,
5874           "First argument must be a scripted function or null/undefined");
5875       return false;
5876     }
5877 
5878     script = JSFunction::getOrCreateScript(cx, fun);
5879     if (!script) {
5880       return false;
5881     }
5882   }
5883 
5884   int32_t index;
5885   if (!ToInt32(cx, args.get(1), &index)) {
5886     return false;
5887   }
5888 
5889   if (index < 0 || uint32_t(index) >= jit::JitScript::NumTypeSets(script)) {
5890     ReportUsageErrorASCII(cx, callee, "Index out of range");
5891     return false;
5892   }
5893 
5894   RootedValue val(cx, args.get(2));
5895 
5896   // If val is "unknown" or "unknownObject" we mark the TypeSet as unknown or
5897   // unknownObject, respectively.
5898   bool unknown = false;
5899   bool unknownObject = false;
5900   if (val.isString()) {
5901     if (!JS_StringEqualsLiteral(cx, val.toString(), "unknown", &unknown)) {
5902       return false;
5903     }
5904     if (!JS_StringEqualsLiteral(cx, val.toString(), "unknownObject",
5905                                 &unknownObject)) {
5906       return false;
5907     }
5908   }
5909 
5910   // Avoid assertion failures if TI or Baseline Interpreter is disabled or if we
5911   // can't Baseline Interpret this script.
5912   if (!IsTypeInferenceEnabled() || !jit::IsBaselineInterpreterEnabled() ||
5913       !jit::CanBaselineInterpretScript(script)) {
5914     args.rval().setUndefined();
5915     return true;
5916   }
5917 
5918   AutoRealm ar(cx, script);
5919 
5920   if (!cx->realm()->ensureJitRealmExists(cx)) {
5921     return false;
5922   }
5923 
5924   jit::AutoKeepJitScripts keepJitScript(cx);
5925   if (!script->ensureHasJitScript(cx, keepJitScript)) {
5926     return false;
5927   }
5928 
5929   AutoEnterAnalysis enter(cx);
5930   AutoSweepJitScript sweep(script);
5931   StackTypeSet* typeSet = script->jitScript()->typeArray(sweep) + index;
5932   if (unknown) {
5933     typeSet->addType(sweep, cx, TypeSet::UnknownType());
5934   } else if (unknownObject) {
5935     typeSet->addType(sweep, cx, TypeSet::AnyObjectType());
5936   } else {
5937     typeSet->addType(sweep, cx, TypeSet::GetValueType(val));
5938   }
5939 
5940   args.rval().setUndefined();
5941   return true;
5942 }
5943 
5944 static bool MarkObjectPropertiesUnknown(JSContext* cx, unsigned argc,
5945                                         Value* vp) {
5946   CallArgs args = CallArgsFromVp(argc, vp);
5947   RootedObject callee(cx, &args.callee());
5948 
5949   if (!args.get(0).isObject()) {
5950     ReportUsageErrorASCII(cx, callee, "Argument must be an object");
5951     return false;
5952   }
5953 
5954   RootedObject obj(cx, &args[0].toObject());
5955   RootedObjectGroup group(cx, JSObject::getGroup(cx, obj));
5956   if (!group) {
5957     return false;
5958   }
5959 
5960   MarkObjectGroupUnknownProperties(cx, group);
5961 
5962   args.rval().setUndefined();
5963   return true;
5964 }
5965 
5966 static bool EncodeAsUtf8InBuffer(JSContext* cx, unsigned argc, Value* vp) {
5967   CallArgs args = CallArgsFromVp(argc, vp);
5968   if (!args.requireAtLeast(cx, "encodeAsUtf8InBuffer", 2)) {
5969     return false;
5970   }
5971 
5972   RootedObject callee(cx, &args.callee());
5973 
5974   if (!args[0].isString()) {
5975     ReportUsageErrorASCII(cx, callee, "First argument must be a String");
5976     return false;
5977   }
5978 
5979   // Create the amounts array early so that the raw pointer into Uint8Array
5980   // data has as short a lifetime as possible
5981   RootedArrayObject array(cx, NewDenseFullyAllocatedArray(cx, 2));
5982   if (!array) {
5983     return false;
5984   }
5985   array->ensureDenseInitializedLength(cx, 0, 2);
5986 
5987   uint32_t length;
5988   bool isSharedMemory;
5989   uint8_t* data;
5990   if (!args[1].isObject() ||
5991       !JS_GetObjectAsUint8Array(&args[1].toObject(), &length, &isSharedMemory,
5992                                 &data) ||
5993       isSharedMemory ||  // excluded views of SharedArrayBuffers
5994       !data) {           // exclude views of detached ArrayBuffers
5995     ReportUsageErrorASCII(cx, callee, "Second argument must be a Uint8Array");
5996     return false;
5997   }
5998 
5999   Maybe<Tuple<size_t, size_t>> amounts = JS_EncodeStringToUTF8BufferPartial(
6000       cx, args[0].toString(), AsWritableChars(MakeSpan(data, length)));
6001   if (!amounts) {
6002     ReportOutOfMemory(cx);
6003     return false;
6004   }
6005 
6006   size_t unitsRead, bytesWritten;
6007   Tie(unitsRead, bytesWritten) = *amounts;
6008 
6009   array->initDenseElement(0, Int32Value(AssertedCast<int32_t>(unitsRead)));
6010   array->initDenseElement(1, Int32Value(AssertedCast<int32_t>(bytesWritten)));
6011 
6012   args.rval().setObject(*array);
6013   return true;
6014 }
6015 
6016 JSScript* js::TestingFunctionArgumentToScript(
6017     JSContext* cx, HandleValue v, JSFunction** funp /* = nullptr */) {
6018   if (v.isString()) {
6019     // To convert a string to a script, compile it. Parse it as an ES6 Program.
6020     RootedLinearString linearStr(cx, StringToLinearString(cx, v.toString()));
6021     if (!linearStr) {
6022       return nullptr;
6023     }
6024     size_t len = GetLinearStringLength(linearStr);
6025     AutoStableStringChars linearChars(cx);
6026     if (!linearChars.initTwoByte(cx, linearStr)) {
6027       return nullptr;
6028     }
6029     const char16_t* chars = linearChars.twoByteRange().begin().get();
6030 
6031     SourceText<char16_t> source;
6032     if (!source.init(cx, chars, len, SourceOwnership::Borrowed)) {
6033       return nullptr;
6034     }
6035 
6036     CompileOptions options(cx);
6037     return JS::Compile(cx, options, source);
6038   }
6039 
6040   RootedFunction fun(cx, JS_ValueToFunction(cx, v));
6041   if (!fun) {
6042     return nullptr;
6043   }
6044 
6045   // Unwrap bound functions.
6046   while (fun->isBoundFunction()) {
6047     JSObject* target = fun->getBoundFunctionTarget();
6048     if (target && target->is<JSFunction>()) {
6049       fun = &target->as<JSFunction>();
6050     } else {
6051       break;
6052     }
6053   }
6054 
6055   if (!fun->isInterpreted()) {
6056     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
6057                               JSMSG_TESTING_SCRIPTS_ONLY);
6058     return nullptr;
6059   }
6060 
6061   JSScript* script = JSFunction::getOrCreateScript(cx, fun);
6062   if (!script) {
6063     return nullptr;
6064   }
6065 
6066   if (funp) {
6067     *funp = fun;
6068   }
6069 
6070   return script;
6071 }
6072 
6073 static bool BaselineCompile(JSContext* cx, unsigned argc, Value* vp) {
6074   CallArgs args = CallArgsFromVp(argc, vp);
6075   RootedObject callee(cx, &args.callee());
6076 
6077   RootedScript script(cx);
6078   if (args.length() == 0) {
6079     NonBuiltinScriptFrameIter iter(cx);
6080     if (iter.done()) {
6081       ReportUsageErrorASCII(cx, callee,
6082                             "no script argument and no script caller");
6083       return false;
6084     }
6085     script = iter.script();
6086   } else {
6087     script = TestingFunctionArgumentToScript(cx, args[0]);
6088     if (!script) {
6089       return false;
6090     }
6091   }
6092 
6093   bool forceDebug = false;
6094   if (args.length() > 1) {
6095     if (args.length() > 2) {
6096       ReportUsageErrorASCII(cx, callee, "too many arguments");
6097       return false;
6098     }
6099     if (!args[1].isBoolean() && !args[1].isUndefined()) {
6100       ReportUsageErrorASCII(
6101           cx, callee, "forceDebugInstrumentation argument should be boolean");
6102       return false;
6103     }
6104     forceDebug = ToBoolean(args[1]);
6105   }
6106 
6107   const char* returnedStr = nullptr;
6108   do {
6109 #ifdef JS_MORE_DETERMINISTIC
6110     // In order to check for differential behaviour, baselineCompile should have
6111     // the same output whether --no-baseline is used or not.
6112     if (fuzzingSafe) {
6113       returnedStr = "skipped (fuzzing-safe)";
6114       break;
6115     }
6116 #endif
6117 
6118     AutoRealm ar(cx, script);
6119     if (script->hasBaselineScript()) {
6120       if (forceDebug && !script->baselineScript()->hasDebugInstrumentation()) {
6121         // There isn't an easy way to do this for a script that might be on
6122         // stack right now. See
6123         // js::jit::RecompileOnStackBaselineScriptsForDebugMode.
6124         ReportUsageErrorASCII(
6125             cx, callee, "unsupported case: recompiling script for debug mode");
6126         return false;
6127       }
6128 
6129       args.rval().setUndefined();
6130       return true;
6131     }
6132 
6133     if (!jit::IsBaselineJitEnabled(cx)) {
6134       returnedStr = "baseline disabled";
6135       break;
6136     }
6137     if (!script->canBaselineCompile()) {
6138       returnedStr = "can't compile";
6139       break;
6140     }
6141     if (!cx->realm()->ensureJitRealmExists(cx)) {
6142       return false;
6143     }
6144 
6145     jit::MethodStatus status = jit::BaselineCompile(cx, script, forceDebug);
6146     switch (status) {
6147       case jit::Method_Error:
6148         return false;
6149       case jit::Method_CantCompile:
6150         returnedStr = "can't compile";
6151         break;
6152       case jit::Method_Skipped:
6153         returnedStr = "skipped";
6154         break;
6155       case jit::Method_Compiled:
6156         args.rval().setUndefined();
6157     }
6158   } while (false);
6159 
6160   if (returnedStr) {
6161     return ReturnStringCopy(cx, args, returnedStr);
6162   }
6163 
6164   return true;
6165 }
6166 
6167 static bool ClearKeptObjects(JSContext* cx, unsigned argc, Value* vp) {
6168   CallArgs args = CallArgsFromVp(argc, vp);
6169   JS::ClearKeptObjects(cx);
6170   args.rval().setUndefined();
6171   return true;
6172 }
6173 
6174 static bool PCCountProfiling_Start(JSContext* cx, unsigned argc, Value* vp) {
6175   CallArgs args = CallArgsFromVp(argc, vp);
6176 
6177   js::StartPCCountProfiling(cx);
6178 
6179   args.rval().setUndefined();
6180   return true;
6181 }
6182 
6183 static bool PCCountProfiling_Stop(JSContext* cx, unsigned argc, Value* vp) {
6184   CallArgs args = CallArgsFromVp(argc, vp);
6185 
6186   js::StopPCCountProfiling(cx);
6187 
6188   args.rval().setUndefined();
6189   return true;
6190 }
6191 
6192 static bool PCCountProfiling_Purge(JSContext* cx, unsigned argc, Value* vp) {
6193   CallArgs args = CallArgsFromVp(argc, vp);
6194 
6195   js::PurgePCCounts(cx);
6196 
6197   args.rval().setUndefined();
6198   return true;
6199 }
6200 
6201 static bool PCCountProfiling_ScriptCount(JSContext* cx, unsigned argc,
6202                                          Value* vp) {
6203   CallArgs args = CallArgsFromVp(argc, vp);
6204 
6205   size_t length = js::GetPCCountScriptCount(cx);
6206 
6207   args.rval().setNumber(double(length));
6208   return true;
6209 }
6210 
6211 static bool PCCountProfiling_ScriptSummary(JSContext* cx, unsigned argc,
6212                                            Value* vp) {
6213   CallArgs args = CallArgsFromVp(argc, vp);
6214   if (!args.requireAtLeast(cx, "summary", 1)) {
6215     return false;
6216   }
6217 
6218   uint32_t index;
6219   if (!JS::ToUint32(cx, args[0], &index)) {
6220     return false;
6221   }
6222 
6223   JSString* str = js::GetPCCountScriptSummary(cx, index);
6224   if (!str) {
6225     return false;
6226   }
6227 
6228   args.rval().setString(str);
6229   return true;
6230 }
6231 
6232 static bool PCCountProfiling_ScriptContents(JSContext* cx, unsigned argc,
6233                                             Value* vp) {
6234   CallArgs args = CallArgsFromVp(argc, vp);
6235   if (!args.requireAtLeast(cx, "contents", 1)) {
6236     return false;
6237   }
6238 
6239   uint32_t index;
6240   if (!JS::ToUint32(cx, args[0], &index)) {
6241     return false;
6242   }
6243 
6244   JSString* str = js::GetPCCountScriptContents(cx, index);
6245   if (!str) {
6246     return false;
6247   }
6248 
6249   args.rval().setString(str);
6250   return true;
6251 }
6252 
6253 // clang-format off
6254 static const JSFunctionSpecWithHelp TestingFunctions[] = {
6255     JS_FN_HELP("gc", ::GC, 0, 0,
6256 "gc([obj] | 'zone' [, ('shrinking' | 'last-ditch') ])",
6257 "  Run the garbage collector.\n"
6258 "  The first parameter describes which zones to collect: if an object is\n"
6259 "  given, GC only its zone. If 'zone' is given, GC any zones that were\n"
6260 "  scheduled via schedulegc.\n"
6261 "  The second parameter is optional and may be 'shrinking' to perform a\n"
6262 "  shrinking GC or 'last-ditch' for a shrinking, last-ditch GC."),
6263 
6264     JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
6265 "minorgc([aboutToOverflow])",
6266 "  Run a minor collector on the Nursery. When aboutToOverflow is true, marks\n"
6267 "  the store buffer as about-to-overflow before collecting."),
6268 
6269     JS_FN_HELP("maybegc", ::MaybeGC, 0, 0,
6270 "maybegc()",
6271 "  Hint to the engine that now is an ok time to run the garbage collector.\n"),
6272 
6273     JS_FN_HELP("gcparam", GCParameter, 2, 0,
6274 "gcparam(name [, value])",
6275 "  Wrapper for JS_[GS]etGCParameter. The name is one of:" GC_PARAMETER_ARGS_LIST),
6276 
6277     JS_FN_HELP("relazifyFunctions", RelazifyFunctions, 0, 0,
6278 "relazifyFunctions(...)",
6279 "  Perform a GC and allow relazification of functions. Accepts the same\n"
6280 "  arguments as gc()."),
6281 
6282     JS_FN_HELP("getBuildConfiguration", GetBuildConfiguration, 0, 0,
6283 "getBuildConfiguration()",
6284 "  Return an object describing some of the configuration options SpiderMonkey\n"
6285 "  was built with."),
6286 
6287     JS_FN_HELP("isLcovEnabled", ::IsLCovEnabled, 0, 0,
6288 "isLcovEnabled()",
6289 "  Return true if JS LCov support is enabled."),
6290 
6291     JS_FN_HELP("isTypeInferenceEnabled", ::IsTypeInferenceEnabled, 0, 0,
6292 "isTypeInferenceEnabled()",
6293 "  Return true if Type Inference is enabled."),
6294 
6295     JS_FN_HELP("hasChild", HasChild, 0, 0,
6296 "hasChild(parent, child)",
6297 "  Return true if |child| is a child of |parent|, as determined by a call to\n"
6298 "  TraceChildren"),
6299 
6300     JS_FN_HELP("setSavedStacksRNGState", SetSavedStacksRNGState, 1, 0,
6301 "setSavedStacksRNGState(seed)",
6302 "  Set this compartment's SavedStacks' RNG state.\n"),
6303 
6304     JS_FN_HELP("getSavedFrameCount", GetSavedFrameCount, 0, 0,
6305 "getSavedFrameCount()",
6306 "  Return the number of SavedFrame instances stored in this compartment's\n"
6307 "  SavedStacks cache."),
6308 
6309     JS_FN_HELP("clearSavedFrames", ClearSavedFrames, 0, 0,
6310 "clearSavedFrames()",
6311 "  Empty the current compartment's cache of SavedFrame objects, so that\n"
6312 "  subsequent stack captures allocate fresh objects to represent frames.\n"
6313 "  Clear the current stack's LiveSavedFrameCaches."),
6314 
6315     JS_FN_HELP("saveStack", SaveStack, 0, 0,
6316 "saveStack([maxDepth [, compartment]])",
6317 "  Capture a stack. If 'maxDepth' is given, capture at most 'maxDepth' number\n"
6318 "  of frames. If 'compartment' is given, allocate the js::SavedFrame instances\n"
6319 "  with the given object's compartment."),
6320 
6321     JS_FN_HELP("captureFirstSubsumedFrame", CaptureFirstSubsumedFrame, 1, 0,
6322 "saveStack(object [, shouldIgnoreSelfHosted = true]])",
6323 "  Capture a stack back to the first frame whose principals are subsumed by the\n"
6324 "  object's compartment's principals. If 'shouldIgnoreSelfHosted' is given,\n"
6325 "  control whether self-hosted frames are considered when checking principals."),
6326 
6327     JS_FN_HELP("callFunctionFromNativeFrame", CallFunctionFromNativeFrame, 1, 0,
6328 "callFunctionFromNativeFrame(function)",
6329 "  Call 'function' with a (C++-)native frame on stack.\n"
6330 "  Required for testing that SaveStack properly handles native frames."),
6331 
6332     JS_FN_HELP("callFunctionWithAsyncStack", CallFunctionWithAsyncStack, 0, 0,
6333 "callFunctionWithAsyncStack(function, stack, asyncCause)",
6334 "  Call 'function', using the provided stack as the async stack responsible\n"
6335 "  for the call, and propagate its return value or the exception it throws.\n"
6336 "  The function is called with no arguments, and 'this' is 'undefined'. The\n"
6337 "  specified |asyncCause| is attached to the provided stack frame."),
6338 
6339     JS_FN_HELP("enableTrackAllocations", EnableTrackAllocations, 0, 0,
6340 "enableTrackAllocations()",
6341 "  Start capturing the JS stack at every allocation. Note that this sets an\n"
6342 "  object metadata callback that will override any other object metadata\n"
6343 "  callback that may be set."),
6344 
6345     JS_FN_HELP("disableTrackAllocations", DisableTrackAllocations, 0, 0,
6346 "disableTrackAllocations()",
6347 "  Stop capturing the JS stack at every allocation."),
6348 
6349     JS_FN_HELP("setTestFilenameValidationCallback", SetTestFilenameValidationCallback, 0, 0,
6350 "setTestFilenameValidationCallback()",
6351 "  Set the filename validation callback to a callback that accepts only\n"
6352 "  filenames starting with 'safe' or (only in system realms) 'system'."),
6353 
6354     JS_FN_HELP("newExternalString", NewExternalString, 1, 0,
6355 "newExternalString(str)",
6356 "  Copies str's chars and returns a new external string."),
6357 
6358     JS_FN_HELP("newMaybeExternalString", NewMaybeExternalString, 1, 0,
6359 "newMaybeExternalString(str)",
6360 "  Like newExternalString but uses the JS_NewMaybeExternalString API."),
6361 
6362     JS_FN_HELP("ensureLinearString", EnsureLinearString, 1, 0,
6363 "ensureLinearString(str)",
6364 "  Ensures str is a linear (non-rope) string and returns it."),
6365 
6366     JS_FN_HELP("representativeStringArray", RepresentativeStringArray, 0, 0,
6367 "representativeStringArray()",
6368 "  Returns an array of strings that represent the various internal string\n"
6369 "  types and character encodings."),
6370 
6371 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
6372 
6373     JS_FN_HELP("oomThreadTypes", OOMThreadTypes, 0, 0,
6374 "oomThreadTypes()",
6375 "  Get the number of thread types that can be used as an argument for\n"
6376 "  oomAfterAllocations() and oomAtAllocation()."),
6377 
6378     JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 2, 0,
6379 "oomAfterAllocations(count [,threadType])",
6380 "  After 'count' js_malloc memory allocations, fail every following allocation\n"
6381 "  (return nullptr). The optional thread type limits the effect to the\n"
6382 "  specified type of helper thread."),
6383 
6384     JS_FN_HELP("oomAtAllocation", OOMAtAllocation, 2, 0,
6385 "oomAtAllocation(count [,threadType])",
6386 "  After 'count' js_malloc memory allocations, fail the next allocation\n"
6387 "  (return nullptr). The optional thread type limits the effect to the\n"
6388 "  specified type of helper thread."),
6389 
6390     JS_FN_HELP("resetOOMFailure", ResetOOMFailure, 0, 0,
6391 "resetOOMFailure()",
6392 "  Remove the allocation failure scheduled by either oomAfterAllocations() or\n"
6393 "  oomAtAllocation() and return whether any allocation had been caused to fail."),
6394 
6395     JS_FN_HELP("oomTest", OOMTest, 0, 0,
6396 "oomTest(function, [expectExceptionOnFailure = true | options])",
6397 "  Test that the passed function behaves correctly under OOM conditions by\n"
6398 "  repeatedly executing it and simulating allocation failure at successive\n"
6399 "  allocations until the function completes without seeing a failure.\n"
6400 "  By default this tests that an exception is raised if execution fails, but\n"
6401 "  this can be disabled by passing false as the optional second parameter.\n"
6402 "  This is also disabled when --fuzzing-safe is specified.\n"
6403 "  Alternatively an object can be passed to set the following options:\n"
6404 "    expectExceptionOnFailure: bool - as described above.\n"
6405 "    keepFailing: bool - continue to fail after first simulated failure.\n"
6406 "\n"
6407 "  WARNING: By design, oomTest assumes the test-function follows the same\n"
6408 "  code path each time it is called, right up to the point where OOM occurs.\n"
6409 "  If on iteration 70 it finishes and caches a unit of work that saves 65\n"
6410 "  allocations the next time we run, then the subsequent 65 allocation\n"
6411 "  points will go untested.\n"
6412 "\n"
6413 "  Things in this category include lazy parsing and baseline compilation,\n"
6414 "  so it is very easy to accidentally write an oomTest that only tests one\n"
6415 "  or the other of those, and not the functionality you meant to test!\n"
6416 "  To avoid lazy parsing, call the test function once first before passing\n"
6417 "  it to oomTest. The jits can be disabled via the test harness.\n"),
6418 
6419     JS_FN_HELP("stackTest", StackTest, 0, 0,
6420 "stackTest(function, [expectExceptionOnFailure = true])",
6421 "  This function behaves exactly like oomTest with the difference that\n"
6422 "  instead of simulating regular OOM conditions, it simulates the engine\n"
6423 "  running out of stack space (failing recursion check).\n"
6424 "\n"
6425 "  See the WARNING in help('oomTest').\n"),
6426 
6427     JS_FN_HELP("interruptTest", InterruptTest, 0, 0,
6428 "interruptTest(function)",
6429 "  This function simulates interrupts similar to how oomTest simulates OOM conditions."
6430 "\n"
6431 "  See the WARNING in help('oomTest').\n"),
6432 
6433 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
6434 
6435     JS_FN_HELP("newRope", NewRope, 3, 0,
6436 "newRope(left, right[, options])",
6437 "  Creates a rope with the given left/right strings.\n"
6438 "  Available options:\n"
6439 "    nursery: bool - force the string to be created in/out of the nursery, if possible.\n"),
6440 
6441     JS_FN_HELP("isRope", IsRope, 1, 0,
6442 "isRope(str)",
6443 "  Returns true if the parameter is a rope"),
6444 
6445     JS_FN_HELP("settlePromiseNow", SettlePromiseNow, 1, 0,
6446 "settlePromiseNow(promise)",
6447 "  'Settle' a 'promise' immediately. This just marks the promise as resolved\n"
6448 "  with a value of `undefined` and causes the firing of any onPromiseSettled\n"
6449 "  hooks set on Debugger instances that are observing the given promise's\n"
6450 "  global as a debuggee."),
6451     JS_FN_HELP("getWaitForAllPromise", GetWaitForAllPromise, 1, 0,
6452 "getWaitForAllPromise(densePromisesArray)",
6453 "  Calls the 'GetWaitForAllPromise' JSAPI function and returns the result\n"
6454 "  Promise."),
6455 JS_FN_HELP("resolvePromise", ResolvePromise, 2, 0,
6456 "resolvePromise(promise, resolution)",
6457 "  Resolve a Promise by calling the JSAPI function JS::ResolvePromise."),
6458 JS_FN_HELP("rejectPromise", RejectPromise, 2, 0,
6459 "rejectPromise(promise, reason)",
6460 "  Reject a Promise by calling the JSAPI function JS::RejectPromise."),
6461 
6462 JS_FN_HELP("streamsAreEnabled", StreamsAreEnabled, 0, 0,
6463 "streamsAreEnabled()",
6464 "  Returns a boolean indicating whether WHATWG Streams are enabled for the current realm."),
6465 
6466     JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
6467 "makeFinalizeObserver()",
6468 "  Get a special object whose finalization increases the counter returned\n"
6469 "  by the finalizeCount function."),
6470 
6471     JS_FN_HELP("finalizeCount", FinalizeCount, 0, 0,
6472 "finalizeCount()",
6473 "  Return the current value of the finalization counter that is incremented\n"
6474 "  each time an object returned by the makeFinalizeObserver is finalized."),
6475 
6476     JS_FN_HELP("resetFinalizeCount", ResetFinalizeCount, 0, 0,
6477 "resetFinalizeCount()",
6478 "  Reset the value returned by finalizeCount()."),
6479 
6480     JS_FN_HELP("gcPreserveCode", GCPreserveCode, 0, 0,
6481 "gcPreserveCode()",
6482 "  Preserve JIT code during garbage collections."),
6483 
6484 #ifdef JS_GC_ZEAL
6485     JS_FN_HELP("gczeal", GCZeal, 2, 0,
6486 "gczeal(mode, [frequency])",
6487 gc::ZealModeHelpText),
6488 
6489     JS_FN_HELP("unsetgczeal", UnsetGCZeal, 2, 0,
6490 "unsetgczeal(mode)",
6491 "  Turn off a single zeal mode set with gczeal() and don't finish any ongoing\n"
6492 "  collection that may be happening."),
6493 
6494     JS_FN_HELP("schedulegc", ScheduleGC, 1, 0,
6495 "schedulegc([num])",
6496 "  If num is given, schedule a GC after num allocations.\n"
6497 "  Returns the number of allocations before the next trigger."),
6498 
6499     JS_FN_HELP("selectforgc", SelectForGC, 0, 0,
6500 "selectforgc(obj1, obj2, ...)",
6501 "  Schedule the given objects to be marked in the next GC slice."),
6502 
6503     JS_FN_HELP("verifyprebarriers", VerifyPreBarriers, 0, 0,
6504 "verifyprebarriers()",
6505 "  Start or end a run of the pre-write barrier verifier."),
6506 
6507     JS_FN_HELP("verifypostbarriers", VerifyPostBarriers, 0, 0,
6508 "verifypostbarriers()",
6509 "  Does nothing (the post-write barrier verifier has been remove)."),
6510 
6511     JS_FN_HELP("currentgc", CurrentGC, 0, 0,
6512 "currentgc()",
6513 "  Report various information about the currently running incremental GC,\n"
6514 "  if one is running."),
6515 
6516     JS_FN_HELP("deterministicgc", DeterministicGC, 1, 0,
6517 "deterministicgc(true|false)",
6518 "  If true, only allow determinstic GCs to run."),
6519 
6520     JS_FN_HELP("dumpGCArenaInfo", DumpGCArenaInfo, 0, 0,
6521 "dumpGCArenaInfo()",
6522 "  Prints information about the different GC things and how they are arranged\n"
6523 "  in arenas.\n"),
6524 #endif
6525 
6526     JS_FN_HELP("gcstate", GCState, 0, 0,
6527 "gcstate([obj])",
6528 "  Report the global GC state, or the GC state for the zone containing |obj|."),
6529 
6530     JS_FN_HELP("schedulezone", ScheduleZoneForGC, 1, 0,
6531 "schedulezone([obj | string])",
6532 "  If obj is given, schedule a GC of obj's zone.\n"
6533 "  If string is given, schedule a GC of the string's zone if possible."),
6534 
6535     JS_FN_HELP("startgc", StartGC, 1, 0,
6536 "startgc([n [, 'shrinking']])",
6537 "  Start an incremental GC and run a slice that processes about n objects.\n"
6538 "  If 'shrinking' is passesd as the optional second argument, perform a\n"
6539 "  shrinking GC rather than a normal GC. If no zones have been selected with\n"
6540 "  schedulegc(), a full GC will be performed."),
6541 
6542     JS_FN_HELP("finishgc", FinishGC, 0, 0,
6543 "finishgc()",
6544 "   Finish an in-progress incremental GC, if none is running then do nothing."),
6545 
6546     JS_FN_HELP("gcslice", GCSlice, 1, 0,
6547 "gcslice([n])",
6548 "  Start or continue an an incremental GC, running a slice that processes about n objects."),
6549 
6550     JS_FN_HELP("abortgc", AbortGC, 1, 0,
6551 "abortgc()",
6552 "  Abort the current incremental GC."),
6553 
6554     JS_FN_HELP("fullcompartmentchecks", FullCompartmentChecks, 1, 0,
6555 "fullcompartmentchecks(true|false)",
6556 "  If true, check for compartment mismatches before every GC."),
6557 
6558     JS_FN_HELP("nondeterministicGetWeakMapKeys", NondeterministicGetWeakMapKeys, 1, 0,
6559 "nondeterministicGetWeakMapKeys(weakmap)",
6560 "  Return an array of the keys in the given WeakMap."),
6561 
6562     JS_FN_HELP("internalConst", InternalConst, 1, 0,
6563 "internalConst(name)",
6564 "  Query an internal constant for the engine. See InternalConst source for\n"
6565 "  the list of constant names."),
6566 
6567     JS_FN_HELP("isProxy", IsProxy, 1, 0,
6568 "isProxy(obj)",
6569 "  If true, obj is a proxy of some sort"),
6570 
6571     JS_FN_HELP("dumpHeap", DumpHeap, 1, 0,
6572 "dumpHeap([filename])",
6573 "  Dump reachable and unreachable objects to the named file, or to stdout. Objects\n"
6574 "  in the nursery are ignored, so if you wish to include them, consider calling\n"
6575 "  minorgc() first."),
6576 
6577     JS_FN_HELP("terminate", Terminate, 0, 0,
6578 "terminate()",
6579 "  Terminate JavaScript execution, as if we had run out of\n"
6580 "  memory or been terminated by the slow script dialog."),
6581 
6582     JS_FN_HELP("readGeckoProfilingStack", ReadGeckoProfilingStack, 0, 0,
6583 "readGeckoProfilingStack()",
6584 "  Reads the jit stack using ProfilingFrameIterator."),
6585 
6586     JS_FN_HELP("enableOsiPointRegisterChecks", EnableOsiPointRegisterChecks, 0, 0,
6587 "enableOsiPointRegisterChecks()",
6588 "  Emit extra code to verify live regs at the start of a VM call are not\n"
6589 "  modified before its OsiPoint."),
6590 
6591     JS_FN_HELP("displayName", DisplayName, 1, 0,
6592 "displayName(fn)",
6593 "  Gets the display name for a function, which can possibly be a guessed or\n"
6594 "  inferred name based on where the function was defined. This can be\n"
6595 "  different from the 'name' property on the function."),
6596 
6597     JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable, 0, 0,
6598 "isAsmJSCompilationAvailable",
6599 "  Returns whether asm.js compilation is currently available or whether it is disabled\n"
6600 "  (e.g., by the debugger)."),
6601 
6602     JS_FN_HELP("getJitCompilerOptions", GetJitCompilerOptions, 0, 0,
6603 "getJitCompilerOptions()",
6604 "  Return an object describing some of the JIT compiler options.\n"),
6605 
6606     JS_FN_HELP("isAsmJSModule", IsAsmJSModule, 1, 0,
6607 "isAsmJSModule(fn)",
6608 "  Returns whether the given value is a function containing \"use asm\" that has been\n"
6609 "  validated according to the asm.js spec."),
6610 
6611     JS_FN_HELP("isAsmJSFunction", IsAsmJSFunction, 1, 0,
6612 "isAsmJSFunction(fn)",
6613 "  Returns whether the given value is a nested function in an asm.js module that has been\n"
6614 "  both compile- and link-time validated."),
6615 
6616     JS_FN_HELP("wasmIsSupported", WasmIsSupported, 0, 0,
6617 "wasmIsSupported()",
6618 "  Returns a boolean indicating whether WebAssembly is supported on the current device."),
6619 
6620     JS_FN_HELP("wasmIsSupportedByHardware", WasmIsSupportedByHardware, 0, 0,
6621 "wasmIsSupportedByHardware()",
6622 "  Returns a boolean indicating whether WebAssembly is supported on the current hardware (regardless of whether we've enabled support)."),
6623 
6624     JS_FN_HELP("wasmDebuggingIsSupported", WasmDebuggingIsSupported, 0, 0,
6625 "wasmDebuggingIsSupported()",
6626 "  Returns a boolean indicating whether WebAssembly debugging is supported on the current device;\n"
6627 "  returns false also if WebAssembly is not supported"),
6628 
6629     JS_FN_HELP("wasmStreamingIsSupported", WasmStreamingIsSupported, 0, 0,
6630 "wasmStreamingIsSupported()",
6631 "  Returns a boolean indicating whether WebAssembly caching is supported by the runtime."),
6632 
6633     JS_FN_HELP("wasmCachingIsSupported", WasmCachingIsSupported, 0, 0,
6634 "wasmCachingIsSupported()",
6635 "  Returns a boolean indicating whether WebAssembly caching is supported by the runtime."),
6636 
6637     JS_FN_HELP("wasmHugeMemoryIsSupported", WasmHugeMemoryIsSupported, 0, 0,
6638 "wasmHugeMemoryIsSupported()",
6639 "  Returns a boolean indicating whether WebAssembly supports using a large"
6640 "  virtual memory reservation in order to elide bounds checks on this platform."),
6641 
6642     JS_FN_HELP("wasmThreadsSupported", WasmThreadsSupported, 0, 0,
6643 "wasmThreadsSupported()",
6644 "  Returns a boolean indicating whether the WebAssembly threads proposal is\n"
6645 "  supported on the current device."),
6646 
6647     JS_FN_HELP("wasmBulkMemSupported", WasmBulkMemSupported, 0, 0,
6648 "wasmBulkMemSupported()",
6649 "  Returns a boolean indicating whether the WebAssembly bulk memory proposal is\n"
6650 "  supported on the current device."),
6651 
6652     JS_FN_HELP("wasmSimdSupported", WasmSimdSupported, 0, 0,
6653 "wasmSimdSupported()",
6654 "  Returns a boolean indicating whether WebAssembly SIMD is supported by the\n"
6655 "  compilers and runtime."),
6656 
6657     JS_FN_HELP("wasmCompilersPresent", WasmCompilersPresent, 0, 0,
6658 "wasmCompilersPresent()",
6659 "  Returns a string indicating the present wasm compilers: a comma-separated list\n"
6660 "  of 'baseline', 'ion', and 'cranelift'.  A compiler is present in the executable\n"
6661 "  if it is compiled in and can generate code for the current architecture."),
6662 
6663     JS_FN_HELP("wasmCompileMode", WasmCompileMode, 0, 0,
6664 "wasmCompileMode()",
6665 "  Returns a string indicating the available wasm compilers: 'baseline', 'ion',\n"
6666 "  'cranelift', 'baseline+ion', 'baseline+cranelift', or 'none'.  A compiler is\n"
6667 "  available if it is present in the executable and not disabled by switches\n"
6668 "  or runtime conditions.  At most one baseline and one optimizing compiler can\n"
6669 "  be available."),
6670 
6671     JS_FN_HELP("wasmCraneliftDisabledByFeatures", WasmCraneliftDisabledByFeatures, 0, 0,
6672 "wasmCraneliftDisabledByFeatures()",
6673 "  If some feature is enabled at compile-time or run-time that prevents Cranelift\n"
6674 "  from being used then this returns a truthy string describing the features that\n."
6675 "  are disabling it.  Otherwise it returns false."),
6676 
6677     JS_FN_HELP("wasmIonDisabledByFeatures", WasmIonDisabledByFeatures, 0, 0,
6678 "wasmIonDisabledByFeatures()",
6679 "  If some feature is enabled at compile-time or run-time that prevents Ion\n"
6680 "  from being used then this returns a truthy string describing the features that\n."
6681 "  are disabling it.  Otherwise it returns false."),
6682 
6683     JS_FN_HELP("wasmExtractCode", WasmExtractCode, 1, 0,
6684 "wasmExtractCode(module[, tier])",
6685 "  Extracts generated machine code from WebAssembly.Module.  The tier is a string,\n"
6686 "  'stable', 'best', 'baseline', or 'ion'; the default is 'stable'.  If the request\n"
6687 "  cannot be satisfied then null is returned.  If the request is 'ion' then block\n"
6688 "  until background compilation is complete."),
6689 
6690     JS_FN_HELP("wasmDis", WasmDisassemble, 1, 0,
6691 "wasmDis(function[, tier])",
6692 "  Disassembles generated machine code from an exported WebAssembly function.\n"
6693 "  The tier is a string, 'stable', 'best', 'baseline', or 'ion'; the default is\n"
6694 "  'stable'."),
6695 
6696     JS_FN_HELP("wasmHasTier2CompilationCompleted", WasmHasTier2CompilationCompleted, 1, 0,
6697 "wasmHasTier2CompilationCompleted(module)",
6698 "  Returns a boolean indicating whether a given module has finished compiled code for tier2. \n"
6699 "This will return true early if compilation isn't two-tiered. "),
6700 
6701     JS_FN_HELP("wasmLoadedFromCache", WasmLoadedFromCache, 1, 0,
6702 "wasmLoadedFromCache(module)",
6703 "  Returns a boolean indicating whether a given module was deserialized directly from a\n"
6704 "  cache (as opposed to compiled from bytecode)."),
6705 
6706     JS_FN_HELP("wasmReftypesEnabled", WasmReftypesEnabled, 1, 0,
6707 "wasmReftypesEnabled()",
6708 "  Returns a boolean indicating whether the WebAssembly reftypes proposal is enabled."),
6709 
6710     JS_FN_HELP("wasmGcEnabled", WasmGcEnabled, 1, 0,
6711 "wasmGcEnabled()",
6712 "  Returns a boolean indicating whether the WebAssembly GC types proposal is enabled."),
6713 
6714     JS_FN_HELP("wasmMultiValueEnabled", WasmMultiValueEnabled, 1, 0,
6715 "wasmMultiValueEnabled()",
6716 "  Returns a boolean indicating whether the WebAssembly multi-value proposal is enabled."),
6717 
6718 #if defined(ENABLE_WASM_SIMD) && defined(DEBUG)
6719     JS_FN_HELP("wasmSimdAnalysis", WasmSimdAnalysis, 1, 0,
6720 "wasmSimdAnalysis(...)",
6721 "  Unstable API for white-box testing.\n"),
6722 #endif
6723 
6724     JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0,
6725 "isLazyFunction(fun)",
6726 "  True if fun is a lazy JSFunction."),
6727 
6728     JS_FN_HELP("isRelazifiableFunction", IsRelazifiableFunction, 1, 0,
6729 "isRelazifiableFunction(fun)",
6730 "  True if fun is a JSFunction with a relazifiable JSScript."),
6731 
6732     JS_FN_HELP("enableShellAllocationMetadataBuilder", EnableShellAllocationMetadataBuilder, 0, 0,
6733 "enableShellAllocationMetadataBuilder()",
6734 "  Use ShellAllocationMetadataBuilder to supply metadata for all newly created objects."),
6735 
6736     JS_FN_HELP("getAllocationMetadata", GetAllocationMetadata, 1, 0,
6737 "getAllocationMetadata(obj)",
6738 "  Get the metadata for an object."),
6739 
6740     JS_INLINABLE_FN_HELP("bailout", testingFunc_bailout, 0, 0, TestBailout,
6741 "bailout()",
6742 "  Force a bailout out of ionmonkey (if running in ionmonkey)."),
6743 
6744     JS_FN_HELP("bailAfter", testingFunc_bailAfter, 1, 0,
6745 "bailAfter(number)",
6746 "  Start a counter to bail once after passing the given amount of possible bailout positions in\n"
6747 "  ionmonkey.\n"),
6748 
6749 
6750     JS_FN_HELP("inJit", testingFunc_inJit, 0, 0,
6751 "inJit()",
6752 "  Returns true when called within (jit-)compiled code. When jit compilation is disabled this\n"
6753 "  function returns an error string. This function returns false in all other cases.\n"
6754 "  Depending on truthiness, you should continue to wait for compilation to happen or stop execution.\n"),
6755 
6756     JS_FN_HELP("inIon", testingFunc_inIon, 0, 0,
6757 "inIon()",
6758 "  Returns true when called within ion. When ion is disabled or when compilation is abnormally\n"
6759 "  slow to start, this function returns an error string. Otherwise, this function returns false.\n"
6760 "  This behaviour ensures that a falsy value means that we are not in ion, but expect a\n"
6761 "  compilation to occur in the future. Conversely, a truthy value means that we are either in\n"
6762 "  ion or that there is litle or no chance of ion ever compiling the current script."),
6763 
6764     JS_FN_HELP("assertJitStackInvariants", TestingFunc_assertJitStackInvariants, 0, 0,
6765 "assertJitStackInvariants()",
6766 "  Iterates the Jit stack and check that stack invariants hold."),
6767 
6768     JS_FN_HELP("setIonCheckGraphCoherency", SetIonCheckGraphCoherency, 1, 0,
6769 "setIonCheckGraphCoherency(bool)",
6770 "  Set whether Ion should perform graph consistency (DEBUG-only) assertions. These assertions\n"
6771 "  are valuable and should be generally enabled, however they can be very expensive for large\n"
6772 "  (wasm) programs."),
6773 
6774     JS_FN_HELP("serialize", testingFunc_serialize, 1, 0,
6775 "serialize(data, [transferables, [policy]])",
6776 "  Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
6777 "  clone buffer object. 'policy' may be an options hash. Valid keys:\n"
6778 "    'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n"
6779 "      to specify whether SharedArrayBuffers may be serialized.\n"
6780 "    'scope' - SameProcess, DifferentProcess, or\n"
6781 "      DifferentProcessForIndexedDB. Determines how some values will be\n"
6782 "      serialized. Clone buffers may only be deserialized with a compatible\n"
6783 "      scope. NOTE - For DifferentProcess/DifferentProcessForIndexedDB,\n"
6784 "      must also set SharedArrayBuffer:'deny' if data contains any shared memory\n"
6785 "      object."),
6786 
6787     JS_FN_HELP("deserialize", Deserialize, 1, 0,
6788 "deserialize(clonebuffer[, opts])",
6789 "  Deserialize data generated by serialize. 'opts' may be an options hash.\n"
6790 "  Valid keys:\n"
6791 "    'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n"
6792 "      to specify whether SharedArrayBuffers may be serialized.\n"
6793 "    'scope', which limits the clone buffers that are considered\n"
6794 "  valid. Allowed values: ''SameProcess', 'DifferentProcess',\n"
6795 "  and 'DifferentProcessForIndexedDB'. So for example, a\n"
6796 "  DifferentProcessForIndexedDB clone buffer may be deserialized in any scope, but\n"
6797 "  a SameProcess clone buffer cannot be deserialized in a\n"
6798 "  DifferentProcess scope."),
6799 
6800     JS_FN_HELP("detachArrayBuffer", DetachArrayBuffer, 1, 0,
6801 "detachArrayBuffer(buffer)",
6802 "  Detach the given ArrayBuffer object from its memory, i.e. as if it\n"
6803 "  had been transferred to a WebWorker."),
6804 
6805     JS_FN_HELP("helperThreadCount", HelperThreadCount, 0, 0,
6806 "helperThreadCount()",
6807 "  Returns the number of helper threads available for off-thread tasks."),
6808 
6809     JS_FN_HELP("enableShapeConsistencyChecks", EnableShapeConsistencyChecks, 0, 0,
6810 "enableShapeConsistencyChecks()",
6811 "  Enable some slow Shape assertions.\n"),
6812 
6813 #ifdef JS_TRACE_LOGGING
6814     JS_FN_HELP("startTraceLogger", EnableTraceLogger, 0, 0,
6815 "startTraceLogger()",
6816 "  Start logging this thread.\n"),
6817 
6818     JS_FN_HELP("stopTraceLogger", DisableTraceLogger, 0, 0,
6819 "stopTraceLogger()",
6820 "  Stop logging this thread."),
6821 #endif
6822 
6823     JS_FN_HELP("reportOutOfMemory", ReportOutOfMemory, 0, 0,
6824 "reportOutOfMemory()",
6825 "  Report OOM, then clear the exception and return undefined. For crash testing."),
6826 
6827     JS_FN_HELP("throwOutOfMemory", ThrowOutOfMemory, 0, 0,
6828 "throwOutOfMemory()",
6829 "  Throw out of memory exception, for OOM handling testing."),
6830 
6831     JS_FN_HELP("reportLargeAllocationFailure", ReportLargeAllocationFailure, 0, 0,
6832 "reportLargeAllocationFailure()",
6833 "  Call the large allocation failure callback, as though a large malloc call failed,\n"
6834 "  then return undefined. In Gecko, this sends a memory pressure notification, which\n"
6835 "  can free up some memory."),
6836 
6837     JS_FN_HELP("findPath", FindPath, 2, 0,
6838 "findPath(start, target)",
6839 "  Return an array describing one of the shortest paths of GC heap edges from\n"
6840 "  |start| to |target|, or |undefined| if |target| is unreachable from |start|.\n"
6841 "  Each element of the array is either of the form:\n"
6842 "    { node: <object or string>, edge: <string describing edge from node> }\n"
6843 "  if the node is a JavaScript object or value; or of the form:\n"
6844 "    { type: <string describing node>, edge: <string describing edge> }\n"
6845 "  if the node is some internal thing that is not a proper JavaScript value\n"
6846 "  (like a shape or a scope chain element). The destination of the i'th array\n"
6847 "  element's edge is the node of the i+1'th array element; the destination of\n"
6848 "  the last array element is implicitly |target|.\n"),
6849 
6850     JS_FN_HELP("shortestPaths", ShortestPaths, 3, 0,
6851 "shortestPaths(start, targets, maxNumPaths)",
6852 "  Return an array of arrays of shortest retaining paths. There is an array of\n"
6853 "  shortest retaining paths for each object in |targets|. The maximum number of\n"
6854 "  paths in each of those arrays is bounded by |maxNumPaths|. Each element in a\n"
6855 "  path is of the form |{ predecessor, edge }|."),
6856 
6857 #if defined(DEBUG) || defined(JS_JITSPEW)
6858     JS_FN_HELP("dumpObject", DumpObject, 1, 0,
6859 "dumpObject()",
6860 "  Dump an internal representation of an object."),
6861 #endif
6862 
6863     JS_FN_HELP("sharedMemoryEnabled", SharedMemoryEnabled, 0, 0,
6864 "sharedMemoryEnabled()",
6865 "  Return true if SharedArrayBuffer and Atomics are enabled"),
6866 
6867     JS_FN_HELP("sharedArrayRawBufferCount", SharedArrayRawBufferCount, 0, 0,
6868 "sharedArrayRawBufferCount()",
6869 "  Return the number of live SharedArrayRawBuffer objects"),
6870 
6871     JS_FN_HELP("sharedArrayRawBufferRefcount", SharedArrayRawBufferRefcount, 0, 0,
6872 "sharedArrayRawBufferRefcount(sab)",
6873 "  Return the reference count of the SharedArrayRawBuffer object held by sab"),
6874 
6875 #ifdef NIGHTLY_BUILD
6876     JS_FN_HELP("objectAddress", ObjectAddress, 1, 0,
6877 "objectAddress(obj)",
6878 "  Return the current address of the object. For debugging only--this\n"
6879 "  address may change during a moving GC."),
6880 
6881     JS_FN_HELP("sharedAddress", SharedAddress, 1, 0,
6882 "sharedAddress(obj)",
6883 "  Return the address of the shared storage of a SharedArrayBuffer."),
6884 #endif
6885 
6886     JS_FN_HELP("evalReturningScope", EvalReturningScope, 1, 0,
6887 "evalReturningScope(scriptStr, [global])",
6888 "  Evaluate the script in a new scope and return the scope.\n"
6889 "  If |global| is present, clone the script to |global| before executing."),
6890 
6891     JS_FN_HELP("cloneAndExecuteScript", ShellCloneAndExecuteScript, 2, 0,
6892 "cloneAndExecuteScript(source, global)",
6893 "  Compile |source| in the current compartment, clone it into |global|'s\n"
6894 "  compartment, and run it there."),
6895 
6896     JS_FN_HELP("backtrace", DumpBacktrace, 1, 0,
6897 "backtrace()",
6898 "  Dump out a brief backtrace."),
6899 
6900     JS_FN_HELP("getBacktrace", GetBacktrace, 1, 0,
6901 "getBacktrace([options])",
6902 "  Return the current stack as a string. Takes an optional options object,\n"
6903 "  which may contain any or all of the boolean properties\n"
6904 "    options.args - show arguments to each function\n"
6905 "    options.locals - show local variables in each frame\n"
6906 "    options.thisprops - show the properties of the 'this' object of each frame\n"),
6907 
6908     JS_FN_HELP("byteSize", ByteSize, 1, 0,
6909 "byteSize(value)",
6910 "  Return the size in bytes occupied by |value|, or |undefined| if value\n"
6911 "  is not allocated in memory.\n"),
6912 
6913     JS_FN_HELP("byteSizeOfScript", ByteSizeOfScript, 1, 0,
6914 "byteSizeOfScript(f)",
6915 "  Return the size in bytes occupied by the function |f|'s JSScript.\n"),
6916 
6917     JS_FN_HELP("setImmutablePrototype", SetImmutablePrototype, 1, 0,
6918 "setImmutablePrototype(obj)",
6919 "  Try to make obj's [[Prototype]] immutable, such that subsequent attempts to\n"
6920 "  change it will fail.  Return true if obj's [[Prototype]] was successfully made\n"
6921 "  immutable (or if it already was immutable), false otherwise.  Throws in case\n"
6922 "  of internal error, or if the operation doesn't even make sense (for example,\n"
6923 "  because the object is a revoked proxy)."),
6924 
6925 #ifdef DEBUG
6926     JS_FN_HELP("dumpStringRepresentation", DumpStringRepresentation, 1, 0,
6927 "dumpStringRepresentation(str)",
6928 "  Print a human-readable description of how the string |str| is represented.\n"),
6929 #endif
6930 
6931     JS_FN_HELP("setLazyParsingDisabled", SetLazyParsingDisabled, 1, 0,
6932 "setLazyParsingDisabled(bool)",
6933 "  Explicitly disable lazy parsing in the current compartment.  The default is that lazy "
6934 "  parsing is not explicitly disabled."),
6935 
6936     JS_FN_HELP("setDiscardSource", SetDiscardSource, 1, 0,
6937 "setDiscardSource(bool)",
6938 "  Explicitly enable source discarding in the current compartment.  The default is that "
6939 "  source discarding is not explicitly enabled."),
6940 
6941     JS_FN_HELP("getConstructorName", GetConstructorName, 1, 0,
6942 "getConstructorName(object)",
6943 "  If the given object was created with `new Ctor`, return the constructor's display name. "
6944 "  Otherwise, return null."),
6945 
6946     JS_FN_HELP("allocationMarker", AllocationMarker, 0, 0,
6947 "allocationMarker([options])",
6948 "  Return a freshly allocated object whose [[Class]] name is\n"
6949 "  \"AllocationMarker\". Such objects are allocated only by calls\n"
6950 "  to this function, never implicitly by the system, making them\n"
6951 "  suitable for use in allocation tooling tests. Takes an optional\n"
6952 "  options object which may contain the following properties:\n"
6953 "    * nursery: bool, whether to allocate the object in the nursery\n"),
6954 
6955     JS_FN_HELP("setGCCallback", SetGCCallback, 1, 0,
6956 "setGCCallback({action:\"...\", options...})",
6957 "  Set the GC callback. action may be:\n"
6958 "    'minorGC' - run a nursery collection\n"
6959 "    'majorGC' - run a major collection, nesting up to a given 'depth'\n"),
6960 
6961 #ifdef DEBUG
6962     JS_FN_HELP("enqueueMark", EnqueueMark, 1, 0,
6963 "enqueueMark(obj|string)",
6964 "  Add an object to the queue of objects to mark at the beginning every GC. (Note\n"
6965 "  that the objects will actually be marked at the beginning of every slice, but\n"
6966 "  after the first slice they will already be marked so nothing will happen.)\n"
6967 "  \n"
6968 "  Instead of an object, a few magic strings may be used:\n"
6969 "    'yield' - cause the current marking slice to end, as if the mark budget were\n"
6970 "      exceeded.\n"
6971 "    'enter-weak-marking-mode' - divide the list into two segments. The items after\n"
6972 "      this string will not be marked until we enter weak marking mode. Note that weak\n"
6973 "      marking mode may be entered zero or multiple times for one GC.\n"
6974 "    'abort-weak-marking-mode' - same as above, but then abort weak marking to fall back\n"
6975 "      on the old iterative marking code path.\n"
6976 "    'drain' - fully drain the mark stack before continuing.\n"
6977 "    'set-color-black' - force everything following in the mark queue to be marked black.\n"
6978 "    'set-color-gray' - continue with the regular GC until gray marking is possible, then force\n"
6979 "       everything following in the mark queue to be marked gray.\n"
6980 "    'unset-color' - stop forcing the mark color."),
6981 
6982     JS_FN_HELP("clearMarkQueue", ClearMarkQueue, 0, 0,
6983 "clearMarkQueue()",
6984 "  Cancel the special marking of all objects enqueue with enqueueMark()."),
6985 
6986     JS_FN_HELP("getMarkQueue", GetMarkQueue, 0, 0,
6987 "getMarkQueue()",
6988 "  Return the current mark queue set up via enqueueMark calls. Note that all\n"
6989 "  returned values will be wrapped into the current compartment, so this loses\n"
6990 "  some fidelity."),
6991 #endif // DEBUG
6992 
6993     JS_FN_HELP("getLcovInfo", GetLcovInfo, 1, 0,
6994 "getLcovInfo(global)",
6995 "  Generate LCOV tracefile for the given compartment.  If no global are provided then\n"
6996 "  the current global is used as the default one.\n"),
6997 
6998 #ifdef DEBUG
6999     JS_FN_HELP("setRNGState", SetRNGState, 2, 0,
7000 "setRNGState(seed0, seed1)",
7001 "  Set this compartment's RNG state.\n"),
7002 #endif
7003 
7004     JS_FN_HELP("getModuleEnvironmentNames", GetModuleEnvironmentNames, 1, 0,
7005 "getModuleEnvironmentNames(module)",
7006 "  Get the list of a module environment's bound names for a specified module.\n"),
7007 
7008     JS_FN_HELP("getModuleEnvironmentValue", GetModuleEnvironmentValue, 2, 0,
7009 "getModuleEnvironmentValue(module, name)",
7010 "  Get the value of a bound name in a module environment.\n"),
7011 
7012 #if defined(FUZZING) && defined(__AFL_COMPILER)
7013     JS_FN_HELP("aflloop", AflLoop, 1, 0,
7014 "aflloop(max_cnt)",
7015 "  Call the __AFL_LOOP() runtime function (see AFL docs)\n"),
7016 #endif
7017 
7018     JS_FN_HELP("monotonicNow", MonotonicNow, 0, 0,
7019 "monotonicNow()",
7020 "  Return a timestamp reflecting the current elapsed system time.\n"
7021 "  This is monotonically increasing.\n"),
7022 
7023     JS_FN_HELP("timeSinceCreation", TimeSinceCreation, 0, 0,
7024 "TimeSinceCreation()",
7025 "  Returns the time in milliseconds since process creation.\n"
7026 "  This uses a clock compatible with the profiler.\n"),
7027 
7028     JS_FN_HELP("isConstructor", IsConstructor, 1, 0,
7029 "isConstructor(value)",
7030 "  Returns whether the value is considered IsConstructor.\n"),
7031 
7032     JS_FN_HELP("getTimeZone", GetTimeZone, 0, 0,
7033 "getTimeZone()",
7034 "  Get the current time zone.\n"),
7035 
7036     JS_FN_HELP("getDefaultLocale", GetDefaultLocale, 0, 0,
7037 "getDefaultLocale()",
7038 "  Get the current default locale.\n"),
7039 
7040     JS_FN_HELP("getCoreCount", GetCoreCount, 0, 0,
7041 "getCoreCount()",
7042 "  Get the number of CPU cores from the platform layer.  Typically this\n"
7043 "  means the number of hyperthreads on systems where that makes sense.\n"),
7044 
7045     JS_FN_HELP("setTimeResolution", SetTimeResolution, 2, 0,
7046 "setTimeResolution(resolution, jitter)",
7047 "  Enables time clamping and jittering. Specify a time resolution in\n"
7048 "  microseconds and whether or not to jitter\n"),
7049 
7050     JS_FN_HELP("scriptedCallerGlobal", ScriptedCallerGlobal, 0, 0,
7051 "scriptedCallerGlobal()",
7052 "  Get the caller's global (or null). See JS::GetScriptedCallerGlobal.\n"),
7053 
7054     JS_FN_HELP("objectGlobal", ObjectGlobal, 1, 0,
7055 "objectGlobal(obj)",
7056 "  Returns the object's global object or null if the object is a wrapper.\n"),
7057 
7058     JS_FN_HELP("isSameCompartment", IsSameCompartment, 2, 0,
7059 "isSameCompartment(obj1, obj2)",
7060 "  Unwraps obj1 and obj2 and returns whether the unwrapped objects are\n"
7061 "  same-compartment.\n"),
7062 
7063     JS_FN_HELP("firstGlobalInCompartment", FirstGlobalInCompartment, 1, 0,
7064 "firstGlobalInCompartment(obj)",
7065 "  Returns the first global in obj's compartment.\n"),
7066 
7067     JS_FN_HELP("assertCorrectRealm", AssertCorrectRealm, 0, 0,
7068 "assertCorrectRealm()",
7069 "  Asserts cx->realm matches callee->realm.\n"),
7070 
7071     JS_FN_HELP("globalLexicals", GlobalLexicals, 0, 0,
7072 "globalLexicals()",
7073 "  Returns an object containing a copy of all global lexical bindings.\n"
7074 "  Example use: let x = 1; assertEq(globalLexicals().x, 1);\n"),
7075 
7076     JS_FN_HELP("monitorType", MonitorType, 3, 0,
7077 "monitorType(fun, index, val)",
7078 "  Adds val's type to the index'th StackTypeSet of the function's\n"
7079 "  JitScript.\n"
7080 "  If fun is null or undefined, the caller's script is used.\n"
7081 "  If the value is the string 'unknown' or 'unknownObject'\n"
7082 "  the TypeSet is marked as unknown or unknownObject, respectively.\n"),
7083 
7084     JS_FN_HELP("baselineCompile", BaselineCompile, 2, 0,
7085 "baselineCompile([fun/code], forceDebugInstrumentation=false)",
7086 "  Baseline-compiles the given JS function or script.\n"
7087 "  Without arguments, baseline-compiles the caller's script; but note\n"
7088 "  that extra boilerplate is needed afterwards to cause the VM to start\n"
7089 "  running the jitcode rather than staying in the interpreter:\n"
7090 "    baselineCompile();  for (var i=0; i<1; i++) {} ...\n"
7091 "  The interpreter will enter the new jitcode at the loop header unless\n"
7092 "  baselineCompile returned a string or threw an error.\n"),
7093 
7094     JS_FN_HELP("markObjectPropertiesUnknown", MarkObjectPropertiesUnknown, 1, 0,
7095 "markObjectPropertiesUnknown(obj)",
7096 "  Mark all objects in obj's object group as having unknown properties.\n"),
7097 
7098     JS_FN_HELP("encodeAsUtf8InBuffer", EncodeAsUtf8InBuffer, 2, 0,
7099 "encodeAsUtf8InBuffer(str, uint8Array)",
7100 "  Encode as many whole code points from the string str into the provided\n"
7101 "  Uint8Array as will completely fit in it, converting lone surrogates to\n"
7102 "  REPLACEMENT CHARACTER.  Return an array [r, w] where |r| is the\n"
7103 "  number of 16-bit units read and |w| is the number of bytes of UTF-8\n"
7104 "  written."),
7105 
7106    JS_FN_HELP("clearKeptObjects", ClearKeptObjects, 0, 0,
7107 "clearKeptObjects()",
7108 "Perform the ECMAScript ClearKeptObjects operation, clearing the list of\n"
7109 "observed WeakRef targets that are kept alive until the next synchronous\n"
7110 "sequence of ECMAScript execution completes. This is used for testing\n"
7111 "WeakRefs.\n"),
7112 
7113     JS_FS_HELP_END
7114 };
7115 // clang-format on
7116 
7117 // clang-format off
7118 static const JSFunctionSpecWithHelp FuzzingUnsafeTestingFunctions[] = {
7119 #if defined(DEBUG) && !defined(ENABLE_NEW_REGEXP)
7120     JS_FN_HELP("parseRegExp", ParseRegExp, 3, 0,
7121 "parseRegExp(pattern[, flags[, match_only])",
7122 "  Parses a RegExp pattern and returns a tree, potentially throwing."),
7123 
7124     JS_FN_HELP("disRegExp", DisRegExp, 3, 0,
7125 "disRegExp(regexp[, input])",
7126 "  Dumps RegExp bytecode."),
7127 #endif
7128 
7129     JS_FN_HELP("getErrorNotes", GetErrorNotes, 1, 0,
7130 "getErrorNotes(error)",
7131 "  Returns an array of error notes."),
7132 
7133     JS_FN_HELP("setTimeZone", SetTimeZone, 1, 0,
7134 "setTimeZone(tzname)",
7135 "  Set the 'TZ' environment variable to the given time zone and applies the new time zone.\n"
7136 "  An empty string or undefined resets the time zone to its default value.\n"
7137 "  NOTE: The input string is not validated and will be passed verbatim to setenv()."),
7138 
7139 JS_FN_HELP("setDefaultLocale", SetDefaultLocale, 1, 0,
7140 "setDefaultLocale(locale)",
7141 "  Set the runtime default locale to the given value.\n"
7142 "  An empty string or undefined resets the runtime locale to its default value.\n"
7143 "  NOTE: The input string is not fully validated, it must be a valid BCP-47 language tag."),
7144 
7145     JS_FS_HELP_END
7146 };
7147 // clang-format on
7148 
7149 // clang-format off
7150 static const JSFunctionSpecWithHelp PCCountProfilingTestingFunctions[] = {
7151     JS_FN_HELP("start", PCCountProfiling_Start, 0, 0,
7152     "start()",
7153     "  Start PC count profiling."),
7154 
7155     JS_FN_HELP("stop", PCCountProfiling_Stop, 0, 0,
7156     "stop()",
7157     "  Stop PC count profiling."),
7158 
7159     JS_FN_HELP("purge", PCCountProfiling_Purge, 0, 0,
7160     "purge()",
7161     "  Purge the collected PC count profiling data."),
7162 
7163     JS_FN_HELP("count", PCCountProfiling_ScriptCount, 0, 0,
7164     "count()",
7165     "  Return the number of profiled scripts."),
7166 
7167     JS_FN_HELP("summary", PCCountProfiling_ScriptSummary, 1, 0,
7168     "summary(index)",
7169     "  Return the PC count profiling summary for the given script index.\n"
7170     "  The script index must be in the range [0, pc.count())."),
7171 
7172     JS_FN_HELP("contents", PCCountProfiling_ScriptContents, 1, 0,
7173     "contents(index)",
7174     "  Return the complete profiling contents for the given script index.\n"
7175     "  The script index must be in the range [0, pc.count())."),
7176 
7177     JS_FS_HELP_END
7178 };
7179 // clang-format on
7180 
7181 bool js::DefineTestingFunctions(JSContext* cx, HandleObject obj,
7182                                 bool fuzzingSafe_, bool disableOOMFunctions_) {
7183   fuzzingSafe = fuzzingSafe_;
7184   if (EnvVarIsDefined("MOZ_FUZZING_SAFE")) {
7185     fuzzingSafe = true;
7186   }
7187 
7188   disableOOMFunctions = disableOOMFunctions_;
7189 
7190   if (!fuzzingSafe) {
7191     if (!JS_DefineFunctionsWithHelp(cx, obj, FuzzingUnsafeTestingFunctions)) {
7192       return false;
7193     }
7194 
7195     RootedObject pccount(cx, JS_NewPlainObject(cx));
7196     if (!pccount) {
7197       return false;
7198     }
7199 
7200     if (!JS_DefineProperty(cx, obj, "pccount", pccount, 0)) {
7201       return false;
7202     }
7203 
7204     if (!JS_DefineFunctionsWithHelp(cx, pccount,
7205                                     PCCountProfilingTestingFunctions)) {
7206       return false;
7207     }
7208   }
7209 
7210   return JS_DefineFunctionsWithHelp(cx, obj, TestingFunctions);
7211 }
7212