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