tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestingFunctions.cpp (350106B)


      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 #ifdef JS_HAS_INTL_API
     12 #  include "mozilla/intl/ICU4CLibrary.h"
     13 #  include "mozilla/intl/Locale.h"
     14 #  include "mozilla/intl/String.h"
     15 #  include "mozilla/intl/TimeZone.h"
     16 #endif
     17 #include "mozilla/Maybe.h"
     18 #include "mozilla/RefPtr.h"
     19 #include "mozilla/ScopeExit.h"
     20 #include "mozilla/Span.h"
     21 #include "mozilla/Sprintf.h"
     22 #include "mozilla/StringBuffer.h"
     23 #include "mozilla/ThreadLocal.h"
     24 
     25 #include <algorithm>
     26 #include <cfloat>
     27 #include <cinttypes>
     28 #include <cmath>
     29 #include <cstdlib>
     30 #include <ctime>
     31 #include <functional>
     32 #include <utility>
     33 
     34 #if defined(XP_UNIX) && !defined(XP_DARWIN)
     35 #  include <time.h>
     36 #else
     37 #  include <chrono>
     38 #endif
     39 
     40 #include "fdlibm.h"
     41 #include "jsapi.h"
     42 #include "jsfriendapi.h"
     43 
     44 #ifdef JS_HAS_INTL_API
     45 #  include "builtin/intl/CommonFunctions.h"
     46 #  include "builtin/intl/FormatBuffer.h"
     47 #  include "builtin/intl/SharedIntlData.h"
     48 #endif
     49 #include "builtin/BigInt.h"
     50 #include "builtin/JSON.h"
     51 #include "builtin/MapObject.h"
     52 #include "builtin/Promise.h"
     53 #include "builtin/TestingUtility.h"  // js::ParseCompileOptions, js::ParseDebugMetadata
     54 #include "builtin/WeakMapObject.h"
     55 #include "ds/IdValuePair.h"               // js::IdValuePair
     56 #include "frontend/CompilationStencil.h"  // frontend::CompilationStencil
     57 #include "frontend/FrontendContext.h"     // AutoReportFrontendContext
     58 #include "gc/GC.h"
     59 #include "gc/GCEnum.h"
     60 #include "gc/GCLock.h"
     61 #include "gc/Zone.h"
     62 #include "jit/BaselineJIT.h"
     63 #include "jit/CacheIRSpewer.h"
     64 #include "jit/Disassemble.h"
     65 #include "jit/FlushICache.h"
     66 #include "jit/InlinableNatives.h"
     67 #include "jit/Invalidation.h"
     68 #include "jit/Ion.h"
     69 #include "jit/JitOptions.h"
     70 #include "jit/JitRuntime.h"
     71 #include "jit/JitScript.h"
     72 #include "jit/TrialInlining.h"
     73 #include "js/Array.h"        // JS::NewArrayObject
     74 #include "js/ArrayBuffer.h"  // JS::{DetachArrayBuffer,GetArrayBufferLengthAndData,NewArrayBufferWithContents}
     75 #include "js/CallAndConstruct.h"  // JS::Call, JS::IsCallable, JS::IsConstructor, JS_CallFunction
     76 #include "js/CharacterEncoding.h"
     77 #include "js/CompilationAndEvaluation.h"
     78 #include "js/CompileOptions.h"  // JS::CompileOptions, JS::DecodeOptions, JS::InstantiateOptions
     79 #include "js/Conversions.h"
     80 #include "js/Date.h"
     81 #include "js/experimental/CodeCoverage.h"   // js::GetCodeCoverageSummary
     82 #include "js/experimental/CompileScript.h"  // JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil, JS::PrepareForInstantiate
     83 #include "js/experimental/JSStencil.h"  // JS::Stencil, JS::EncodeStencil, JS::DecodeStencil, JS::InstantiateGlobalStencil
     84 #include "js/experimental/PCCountProfiling.h"  // JS::{Start,Stop}PCCountProfiling, JS::PurgePCCounts, JS::GetPCCountScript{Count,Summary,Contents}
     85 #include "js/experimental/TypedData.h"         // JS_GetObjectAsUint8Array
     86 #include "js/friend/DumpFunctions.h"  // js::Dump{Backtrace,Heap,Object}, JS::FormatStackDump, js::IgnoreNurseryObjects
     87 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
     88 #include "js/friend/WindowProxy.h"    // js::ToWindowProxyIfWindow
     89 #include "js/GlobalObject.h"
     90 #include "js/HashTable.h"
     91 #include "js/Interrupt.h"
     92 #include "js/LocaleSensitive.h"
     93 #include "js/Prefs.h"
     94 #include "js/Printf.h"
     95 #include "js/PropertyAndElement.h"  // JS_DefineProperties, JS_DefineProperty, JS_DefinePropertyById, JS_Enumerate, JS_GetProperty, JS_GetPropertyById, JS_HasProperty, JS_SetElement, JS_SetProperty
     96 #include "js/PropertySpec.h"
     97 #include "js/SourceText.h"
     98 #include "js/StableStringChars.h"
     99 #include "js/Stack.h"
    100 #include "js/String.h"  // JS::GetLinearStringLength, JS::StringToLinearString
    101 #include "js/StructuredClone.h"
    102 #include "js/Transcoding.h"  // JS::TranscodeResult, JS::TranscodeRange, JS::TranscodeBuffer, JS::IsTranscodeFailureResult
    103 #include "js/UbiNode.h"
    104 #include "js/UbiNodeBreadthFirst.h"
    105 #include "js/UbiNodeShortestPaths.h"
    106 #include "js/UniquePtr.h"
    107 #include "js/Vector.h"
    108 #include "js/Wrapper.h"
    109 #include "threading/CpuCount.h"
    110 #include "util/DifferentialTesting.h"
    111 #include "util/StringBuilder.h"
    112 #include "util/Text.h"
    113 #include "vm/BooleanObject.h"
    114 #include "vm/DateObject.h"
    115 #include "vm/DateTime.h"
    116 #include "vm/ErrorObject.h"
    117 #include "vm/GlobalObject.h"
    118 #include "vm/HelperThreads.h"
    119 #include "vm/HelperThreadState.h"
    120 #include "vm/Interpreter.h"
    121 #include "vm/JSContext.h"
    122 #include "vm/JSObject.h"
    123 #include "vm/NumberObject.h"
    124 #include "vm/PlainObject.h"    // js::PlainObject
    125 #include "vm/PromiseObject.h"  // js::PromiseObject, js::PromiseSlot_*
    126 #include "vm/ProxyObject.h"
    127 #include "vm/RealmFuses.h"
    128 #include "vm/RuntimeFuses.h"
    129 #include "vm/SavedStacks.h"
    130 #include "vm/ScopeKind.h"
    131 #include "vm/Stack.h"
    132 #include "vm/StencilObject.h"  // StencilObject, StencilXDRBufferObject
    133 #include "vm/StringObject.h"
    134 #include "vm/StringType.h"
    135 #include "wasm/AsmJS.h"
    136 #include "wasm/WasmBaselineCompile.h"
    137 #include "wasm/WasmBuiltinModule.h"
    138 #include "wasm/WasmDump.h"
    139 #include "wasm/WasmFeatures.h"
    140 #include "wasm/WasmGcObject.h"
    141 #include "wasm/WasmInstance.h"
    142 #include "wasm/WasmIonCompile.h"
    143 #include "wasm/WasmJS.h"
    144 #include "wasm/WasmModule.h"
    145 #include "wasm/WasmValType.h"
    146 #include "wasm/WasmValue.h"
    147 
    148 #include "debugger/DebugAPI-inl.h"
    149 #include "gc/AtomMarking-inl.h"
    150 #include "jit/JitHints-inl.h"
    151 #include "vm/Compartment-inl.h"
    152 #include "vm/EnvironmentObject-inl.h"
    153 #include "vm/JSContext-inl.h"
    154 #include "vm/JSObject-inl.h"
    155 #include "vm/NativeObject-inl.h"
    156 #include "vm/ObjectFlags-inl.h"
    157 #include "vm/StringType-inl.h"
    158 #include "wasm/WasmInstance-inl.h"
    159 
    160 using namespace js;
    161 
    162 using mozilla::AssertedCast;
    163 using mozilla::AsWritableChars;
    164 using mozilla::Maybe;
    165 using mozilla::Some;
    166 using mozilla::Span;
    167 
    168 using JS::AutoStableStringChars;
    169 using JS::CompileOptions;
    170 using JS::SliceBudget;
    171 using JS::SourceText;
    172 using JS::WorkBudget;
    173 
    174 // If fuzzingSafe is set, remove functionality that could cause problems with
    175 // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
    176 mozilla::Atomic<bool> js::fuzzingSafe(false);
    177 
    178 // If disableOOMFunctions is set, disable functionality that causes artificial
    179 // OOM conditions.
    180 static mozilla::Atomic<bool> disableOOMFunctions(false);
    181 
    182 static bool EnvVarIsDefined(const char* name) {
    183  const char* value = getenv(name);
    184  return value && *value;
    185 }
    186 
    187 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
    188 static bool EnvVarAsInt(const char* name, int* valueOut) {
    189  if (!EnvVarIsDefined(name)) {
    190    return false;
    191  }
    192 
    193  *valueOut = atoi(getenv(name));
    194  return true;
    195 }
    196 #endif
    197 
    198 static bool GetRealmConfiguration(JSContext* cx, unsigned argc, Value* vp) {
    199  CallArgs args = CallArgsFromVp(argc, vp);
    200  RootedObject callee(cx, &args.callee());
    201  RootedObject info(cx, JS_NewPlainObject(cx));
    202  if (!info) {
    203    return false;
    204  }
    205  if (args.length() > 1) {
    206    ReportUsageErrorASCII(cx, callee, "Must have zero or one arguments");
    207    return false;
    208  }
    209  if (args.length() == 1 && !args[0].isString()) {
    210    ReportUsageErrorASCII(cx, callee, "Argument must be a string");
    211    return false;
    212  }
    213 
    214  if (args.length() == 1) {
    215    RootedString str(cx, ToString(cx, args[0]));
    216    if (!str) {
    217      return false;
    218    }
    219    RootedId id(cx);
    220    if (!JS_StringToId(cx, str, &id)) {
    221      return false;
    222    }
    223 
    224    bool hasProperty;
    225    if (JS_HasPropertyById(cx, info, id, &hasProperty) && hasProperty) {
    226      // Returning a true/false from GetProperty
    227      return GetProperty(cx, info, info, id, args.rval());
    228    }
    229 
    230    ReportUsageErrorASCII(cx, callee, "Invalid option name");
    231    return false;
    232  }
    233 
    234  args.rval().setObject(*info);
    235  return true;
    236 }
    237 
    238 static bool GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) {
    239  CallArgs args = CallArgsFromVp(argc, vp);
    240  RootedObject callee(cx, &args.callee());
    241  RootedObject info(cx, JS_NewPlainObject(cx));
    242  if (!info) {
    243    return false;
    244  }
    245  if (args.length() > 1) {
    246    ReportUsageErrorASCII(cx, callee, "Must have zero or one arguments");
    247    return false;
    248  }
    249  if (args.length() == 1 && !args[0].isString()) {
    250    ReportUsageErrorASCII(cx, callee, "Argument must be a string");
    251    return false;
    252  }
    253 
    254  if (!JS_SetProperty(cx, info, "rooting-analysis", FalseHandleValue)) {
    255    return false;
    256  }
    257 
    258  if (!JS_SetProperty(cx, info, "exact-rooting", TrueHandleValue)) {
    259    return false;
    260  }
    261 
    262  if (!JS_SetProperty(cx, info, "trace-jscalls-api", FalseHandleValue)) {
    263    return false;
    264  }
    265 
    266  if (!JS_SetProperty(cx, info, "incremental-gc", TrueHandleValue)) {
    267    return false;
    268  }
    269 
    270  if (!JS_SetProperty(cx, info, "generational-gc", TrueHandleValue)) {
    271    return false;
    272  }
    273 
    274  if (!JS_SetProperty(cx, info, "oom-backtraces", FalseHandleValue)) {
    275    return false;
    276  }
    277 
    278  RootedValue value(cx);
    279 #ifdef DEBUG
    280  value = BooleanValue(true);
    281 #else
    282  value = BooleanValue(false);
    283 #endif
    284  if (!JS_SetProperty(cx, info, "debug", value)) {
    285    return false;
    286  }
    287 
    288 #ifdef RELEASE_OR_BETA
    289  value = BooleanValue(true);
    290 #else
    291  value = BooleanValue(false);
    292 #endif
    293  if (!JS_SetProperty(cx, info, "release_or_beta", value)) {
    294    return false;
    295  }
    296 
    297 #ifdef EARLY_BETA_OR_EARLIER
    298  value = BooleanValue(true);
    299 #else
    300  value = BooleanValue(false);
    301 #endif
    302  if (!JS_SetProperty(cx, info, "early_beta_or_earlier", value)) {
    303    return false;
    304  }
    305 
    306 #ifdef MOZ_CODE_COVERAGE
    307  value = BooleanValue(true);
    308 #else
    309  value = BooleanValue(false);
    310 #endif
    311  if (!JS_SetProperty(cx, info, "coverage", value)) {
    312    return false;
    313  }
    314 
    315 #ifdef JS_HAS_CTYPES
    316  value = BooleanValue(true);
    317 #else
    318  value = BooleanValue(false);
    319 #endif
    320  if (!JS_SetProperty(cx, info, "has-ctypes", value)) {
    321    return false;
    322  }
    323 
    324 #if defined(JS_CODEGEN_X86)
    325  value = BooleanValue(true);
    326 #else
    327  value = BooleanValue(false);
    328 #endif
    329  if (!JS_SetProperty(cx, info, "x86", value)) {
    330    return false;
    331  }
    332 
    333 #if defined(JS_CODEGEN_X64)
    334  value = BooleanValue(true);
    335 #else
    336  value = BooleanValue(false);
    337 #endif
    338  if (!JS_SetProperty(cx, info, "x64", value)) {
    339    return false;
    340  }
    341 
    342 #ifdef JS_CODEGEN_ARM
    343  value = BooleanValue(true);
    344 #else
    345  value = BooleanValue(false);
    346 #endif
    347  if (!JS_SetProperty(cx, info, "arm", value)) {
    348    return false;
    349  }
    350 
    351 #ifdef JS_SIMULATOR_ARM
    352  value = BooleanValue(true);
    353 #else
    354  value = BooleanValue(false);
    355 #endif
    356  if (!JS_SetProperty(cx, info, "arm-simulator", value)) {
    357    return false;
    358  }
    359 
    360 #ifdef ANDROID
    361  value = BooleanValue(true);
    362 #else
    363  value = BooleanValue(false);
    364 #endif
    365  if (!JS_SetProperty(cx, info, "android", value)) {
    366    return false;
    367  }
    368 
    369 #ifdef XP_WIN
    370  value = BooleanValue(true);
    371 #else
    372  value = BooleanValue(false);
    373 #endif
    374  if (!JS_SetProperty(cx, info, "windows", value)) {
    375    return false;
    376  }
    377 
    378 #ifdef XP_MACOSX
    379  value = BooleanValue(true);
    380 #else
    381  value = BooleanValue(false);
    382 #endif
    383  if (!JS_SetProperty(cx, info, "osx", value)) {
    384    return false;
    385  }
    386 
    387 #ifdef JS_CODEGEN_ARM64
    388  value = BooleanValue(true);
    389 #else
    390  value = BooleanValue(false);
    391 #endif
    392  if (!JS_SetProperty(cx, info, "arm64", value)) {
    393    return false;
    394  }
    395 
    396 #ifdef JS_SIMULATOR_ARM64
    397  value = BooleanValue(true);
    398 #else
    399  value = BooleanValue(false);
    400 #endif
    401  if (!JS_SetProperty(cx, info, "arm64-simulator", value)) {
    402    return false;
    403  }
    404 
    405 #ifdef JS_CODEGEN_MIPS64
    406  value = BooleanValue(true);
    407 #else
    408  value = BooleanValue(false);
    409 #endif
    410  if (!JS_SetProperty(cx, info, "mips64", value)) {
    411    return false;
    412  }
    413 
    414 #ifdef JS_SIMULATOR_MIPS64
    415  value = BooleanValue(true);
    416 #else
    417  value = BooleanValue(false);
    418 #endif
    419  if (!JS_SetProperty(cx, info, "mips64-simulator", value)) {
    420    return false;
    421  }
    422 
    423 #ifdef JS_SIMULATOR
    424  value = BooleanValue(true);
    425 #else
    426  value = BooleanValue(false);
    427 #endif
    428  if (!JS_SetProperty(cx, info, "simulator", value)) {
    429    return false;
    430  }
    431 
    432 #ifdef __wasi__
    433  value = BooleanValue(true);
    434 #else
    435  value = BooleanValue(false);
    436 #endif
    437  if (!JS_SetProperty(cx, info, "wasi", value)) {
    438    return false;
    439  }
    440 
    441 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
    442  value = BooleanValue(true);
    443 #else
    444  value = BooleanValue(false);
    445 #endif
    446  if (!JS_SetProperty(cx, info, "pbl", value)) {
    447    return false;
    448  }
    449 
    450 #ifdef JS_CODEGEN_LOONG64
    451  value = BooleanValue(true);
    452 #else
    453  value = BooleanValue(false);
    454 #endif
    455  if (!JS_SetProperty(cx, info, "loong64", value)) {
    456    return false;
    457  }
    458 
    459 #ifdef JS_SIMULATOR_LOONG64
    460  value = BooleanValue(true);
    461 #else
    462  value = BooleanValue(false);
    463 #endif
    464  if (!JS_SetProperty(cx, info, "loong64-simulator", value)) {
    465    return false;
    466  }
    467 
    468 #ifdef JS_CODEGEN_RISCV64
    469  value = BooleanValue(true);
    470 #else
    471  value = BooleanValue(false);
    472 #endif
    473  if (!JS_SetProperty(cx, info, "riscv64", value)) {
    474    return false;
    475  }
    476 
    477 #ifdef JS_SIMULATOR_RISCV64
    478  value = BooleanValue(true);
    479 #else
    480  value = BooleanValue(false);
    481 #endif
    482  if (!JS_SetProperty(cx, info, "riscv64-simulator", value)) {
    483    return false;
    484  }
    485 
    486 #ifdef MOZ_ASAN
    487  value = BooleanValue(true);
    488 #else
    489  value = BooleanValue(false);
    490 #endif
    491  if (!JS_SetProperty(cx, info, "asan", value)) {
    492    return false;
    493  }
    494 
    495 #ifdef MOZ_TSAN
    496  value = BooleanValue(true);
    497 #else
    498  value = BooleanValue(false);
    499 #endif
    500  if (!JS_SetProperty(cx, info, "tsan", value)) {
    501    return false;
    502  }
    503 
    504 #ifdef MOZ_UBSAN
    505  value = BooleanValue(true);
    506 #else
    507  value = BooleanValue(false);
    508 #endif
    509  if (!JS_SetProperty(cx, info, "ubsan", value)) {
    510    return false;
    511  }
    512 
    513 #ifdef JS_GC_ZEAL
    514  value = BooleanValue(true);
    515 #else
    516  value = BooleanValue(false);
    517 #endif
    518  if (!JS_SetProperty(cx, info, "has-gczeal", value)) {
    519    return false;
    520  }
    521 
    522 #ifdef MOZ_PROFILING
    523  value = BooleanValue(true);
    524 #else
    525  value = BooleanValue(false);
    526 #endif
    527  if (!JS_SetProperty(cx, info, "profiling", value)) {
    528    return false;
    529  }
    530 
    531 #ifdef MOZ_VALGRIND
    532  value = BooleanValue(true);
    533 #else
    534  value = BooleanValue(false);
    535 #endif
    536  if (!JS_SetProperty(cx, info, "valgrind", value)) {
    537    return false;
    538  }
    539 
    540 #ifdef JS_HAS_INTL_API
    541  value = BooleanValue(true);
    542 #else
    543  value = BooleanValue(false);
    544 #endif
    545  if (!JS_SetProperty(cx, info, "intl-api", value)) {
    546    return false;
    547  }
    548 
    549 #if defined(SOLARIS)
    550  value = BooleanValue(false);
    551 #else
    552  value = BooleanValue(true);
    553 #endif
    554  if (!JS_SetProperty(cx, info, "mapped-array-buffer", value)) {
    555    return false;
    556  }
    557 
    558 #ifdef MOZ_MEMORY
    559  value = BooleanValue(true);
    560 #else
    561  value = BooleanValue(false);
    562 #endif
    563  if (!JS_SetProperty(cx, info, "moz-memory", value)) {
    564    return false;
    565  }
    566 
    567  value.setInt32(sizeof(void*));
    568  if (!JS_SetProperty(cx, info, "pointer-byte-size", value)) {
    569    return false;
    570  }
    571 
    572 #ifdef ENABLE_DECORATORS
    573  value = BooleanValue(true);
    574 #else
    575  value = BooleanValue(false);
    576 #endif
    577  if (!JS_SetProperty(cx, info, "decorators", value)) {
    578    return false;
    579  }
    580 
    581 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    582  value = BooleanValue(true);
    583 #else
    584  value = BooleanValue(false);
    585 #endif
    586  if (!JS_SetProperty(cx, info, "explicit-resource-management", value)) {
    587    return false;
    588  }
    589 
    590 #ifdef FUZZING
    591  value = BooleanValue(true);
    592 #else
    593  value = BooleanValue(false);
    594 #endif
    595  if (!JS_SetProperty(cx, info, "fuzzing-defined", value)) {
    596    return false;
    597  }
    598 
    599 #if (defined(__GNUC__) && defined(__SSE__) && defined(__x86_64__)) || \
    600    defined(__arm__) || defined(__aarch64__)
    601  // See js.cpp "disable-main-thread-denormals" command line option.
    602  value = BooleanValue(true);
    603 #else
    604  value = BooleanValue(false);
    605 #endif
    606  if (!JS_SetProperty(cx, info, "can-disable-main-thread-denormals", value)) {
    607    return false;
    608  }
    609 
    610  value = Int32Value(JSFatInlineString::MAX_LENGTH_LATIN1);
    611  if (!JS_SetProperty(cx, info, "inline-latin1-chars", value)) {
    612    return false;
    613  }
    614 
    615  value = Int32Value(JSFatInlineString::MAX_LENGTH_TWO_BYTE);
    616  if (!JS_SetProperty(cx, info, "inline-two-byte-chars", value)) {
    617    return false;
    618  }
    619 
    620  value = Int32Value(JSThinInlineString::MAX_LENGTH_LATIN1);
    621  if (!JS_SetProperty(cx, info, "thin-inline-latin1-chars", value)) {
    622    return false;
    623  }
    624 
    625  value = Int32Value(JSThinInlineString::MAX_LENGTH_TWO_BYTE);
    626  if (!JS_SetProperty(cx, info, "thin-inline-two-byte-chars", value)) {
    627    return false;
    628  }
    629 
    630  if (js::ThinInlineAtom::EverInstantiated) {
    631    value = Int32Value(js::ThinInlineAtom::MAX_LENGTH_LATIN1);
    632    if (!JS_SetProperty(cx, info, "thin-inline-atom-latin1-chars", value)) {
    633      return false;
    634    }
    635 
    636    value = Int32Value(js::ThinInlineAtom::MAX_LENGTH_TWO_BYTE);
    637    if (!JS_SetProperty(cx, info, "thin-inline-atom-two-byte-chars", value)) {
    638      return false;
    639    }
    640  }
    641 
    642  value = Int32Value(js::FatInlineAtom::MAX_LENGTH_LATIN1);
    643  if (!JS_SetProperty(cx, info, "fat-inline-atom-latin1-chars", value)) {
    644    return false;
    645  }
    646 
    647  value = Int32Value(js::FatInlineAtom::MAX_LENGTH_TWO_BYTE);
    648  if (!JS_SetProperty(cx, info, "fat-inline-atom-two-byte-chars", value)) {
    649    return false;
    650  }
    651 
    652  if (args.length() == 1) {
    653    RootedString str(cx, ToString(cx, args[0]));
    654    if (!str) {
    655      return false;
    656    }
    657    RootedId id(cx);
    658    if (!JS_StringToId(cx, str, &id)) {
    659      return false;
    660    }
    661 
    662    bool hasProperty;
    663    if (JS_HasPropertyById(cx, info, id, &hasProperty) && hasProperty) {
    664      // Returning a true/false from GetProperty
    665      return GetProperty(cx, info, info, id, args.rval());
    666    }
    667 
    668    ReportUsageErrorASCII(cx, callee, "Invalid option name");
    669    return false;
    670  }
    671 
    672  args.rval().setObject(*info);
    673  return true;
    674 }
    675 
    676 static bool IsLCovEnabled(JSContext* cx, unsigned argc, Value* vp) {
    677  CallArgs args = CallArgsFromVp(argc, vp);
    678  args.rval().setBoolean(coverage::IsLCovEnabled());
    679  return true;
    680 }
    681 
    682 static bool TrialInline(JSContext* cx, unsigned argc, Value* vp) {
    683  CallArgs args = CallArgsFromVp(argc, vp);
    684  args.rval().setUndefined();
    685 
    686  FrameIter iter(cx);
    687  if (iter.done() || !iter.isBaseline() || iter.realm() != cx->realm()) {
    688    return true;
    689  }
    690 
    691  jit::BaselineFrame* frame = iter.abstractFramePtr().asBaselineFrame();
    692  if (!jit::CanIonCompileScript(cx, frame->script())) {
    693    return true;
    694  }
    695 
    696  return jit::DoTrialInlining(cx, frame);
    697 }
    698 
    699 static bool ReturnStringCopy(JSContext* cx, CallArgs& args,
    700                             const char* message) {
    701  JSString* str = JS_NewStringCopyZ(cx, message);
    702  if (!str) {
    703    return false;
    704  }
    705 
    706  args.rval().setString(str);
    707  return true;
    708 }
    709 
    710 static bool MaybeGC(JSContext* cx, unsigned argc, Value* vp) {
    711  CallArgs args = CallArgsFromVp(argc, vp);
    712  JS_MaybeGC(cx);
    713  args.rval().setUndefined();
    714  return true;
    715 }
    716 
    717 static bool GC(JSContext* cx, unsigned argc, Value* vp) {
    718  CallArgs args = CallArgsFromVp(argc, vp);
    719 
    720  /*
    721   * If the first argument is 'zone', we collect any zones previously
    722   * scheduled for GC via schedulegc. If the first argument is an object, we
    723   * collect the object's zone (and any other zones scheduled for
    724   * GC). Otherwise, we collect all zones.
    725   */
    726  bool zone = false;
    727  if (args.length() >= 1) {
    728    Value arg = args[0];
    729    if (arg.isString()) {
    730      if (!JS_StringEqualsLiteral(cx, arg.toString(), "zone", &zone)) {
    731        return false;
    732      }
    733    } else if (arg.isObject()) {
    734      PrepareZoneForGC(cx, UncheckedUnwrap(&arg.toObject())->zone());
    735      zone = true;
    736    }
    737  }
    738 
    739  JS::GCOptions options = JS::GCOptions::Normal;
    740  JS::GCReason reason = JS::GCReason::API;
    741  if (args.length() >= 2) {
    742    Value arg = args[1];
    743    if (arg.isString()) {
    744      bool shrinking = false;
    745      bool last_ditch = false;
    746      if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking",
    747                                  &shrinking)) {
    748        return false;
    749      }
    750      if (!JS_StringEqualsLiteral(cx, arg.toString(), "last-ditch",
    751                                  &last_ditch)) {
    752        return false;
    753      }
    754      if (shrinking) {
    755        options = JS::GCOptions::Shrink;
    756      } else if (last_ditch) {
    757        options = JS::GCOptions::Shrink;
    758        reason = JS::GCReason::LAST_DITCH;
    759      }
    760    }
    761  }
    762 
    763  size_t preBytes = cx->runtime()->gc.heapSize.bytes();
    764 
    765  if (zone) {
    766    PrepareForDebugGC(cx->runtime());
    767  } else {
    768    JS::PrepareForFullGC(cx);
    769  }
    770 
    771  JS::NonIncrementalGC(cx, options, reason);
    772 
    773  char buf[256] = {'\0'};
    774  if (!js::SupportDifferentialTesting()) {
    775    SprintfLiteral(buf, "before %zu, after %zu\n", preBytes,
    776                   cx->runtime()->gc.heapSize.bytes());
    777  }
    778  return ReturnStringCopy(cx, args, buf);
    779 }
    780 
    781 static bool MinorGC(JSContext* cx, unsigned argc, Value* vp) {
    782  CallArgs args = CallArgsFromVp(argc, vp);
    783  if (args.get(0) == BooleanValue(true)) {
    784    gc::GCRuntime& gc = cx->runtime()->gc;
    785    if (gc.nursery().isEnabled()) {
    786      gc.storeBuffer().setAboutToOverflow(JS::GCReason::FULL_GENERIC_BUFFER);
    787    }
    788  }
    789 
    790  cx->minorGC(JS::GCReason::API);
    791  args.rval().setUndefined();
    792  return true;
    793 }
    794 
    795 #define PARAM_NAME_LIST_ENTRY(name, key, writable) " " name
    796 #define GC_PARAMETER_ARGS_LIST FOR_EACH_GC_PARAM(PARAM_NAME_LIST_ENTRY)
    797 
    798 static bool GCParameter(JSContext* cx, unsigned argc, Value* vp) {
    799  CallArgs args = CallArgsFromVp(argc, vp);
    800 
    801  JSString* str = ToString(cx, args.get(0));
    802  if (!str) {
    803    return false;
    804  }
    805 
    806  UniqueChars name = EncodeLatin1(cx, str);
    807  if (!name) {
    808    return false;
    809  }
    810 
    811  JSGCParamKey param;
    812  bool writable;
    813  if (!GetGCParameterInfo(name.get(), &param, &writable)) {
    814    JS_ReportErrorASCII(
    815        cx, "the first argument must be one of:" GC_PARAMETER_ARGS_LIST);
    816    return false;
    817  }
    818 
    819  // Request mode.
    820  if (args.length() == 1) {
    821    uint32_t value = JS_GetGCParameter(cx, param);
    822    args.rval().setNumber(value);
    823    return true;
    824  }
    825 
    826  if (!writable) {
    827    JS_ReportErrorASCII(cx, "Attempt to change read-only parameter %s",
    828                        name.get());
    829    return false;
    830  }
    831 
    832  if (fuzzingSafe) {
    833    // Some Params are not yet fuzzing safe and so we silently skip
    834    // changing said parameters.
    835    switch (param) {
    836      case JSGC_SEMISPACE_NURSERY_ENABLED:
    837        args.rval().setUndefined();
    838        return true;
    839      default:
    840        break;
    841    }
    842  }
    843 
    844  if (disableOOMFunctions) {
    845    switch (param) {
    846      case JSGC_MAX_BYTES:
    847      case JSGC_MAX_NURSERY_BYTES:
    848        args.rval().setUndefined();
    849        return true;
    850      default:
    851        break;
    852    }
    853  }
    854 
    855  double d;
    856  if (!ToNumber(cx, args[1], &d)) {
    857    return false;
    858  }
    859 
    860  if (d < 0 || d > UINT32_MAX) {
    861    JS_ReportErrorASCII(cx, "Parameter value out of range");
    862    return false;
    863  }
    864 
    865  uint32_t value = floor(d);
    866  bool ok = cx->runtime()->gc.setParameter(cx, param, value);
    867  if (!ok) {
    868    JS_ReportErrorASCII(cx, "Parameter value out of range");
    869    return false;
    870  }
    871 
    872  args.rval().setUndefined();
    873  return true;
    874 }
    875 
    876 static bool FinishBackgroundFree(JSContext* cx, unsigned argc, Value* vp) {
    877  CallArgs args = CallArgsFromVp(argc, vp);
    878  cx->runtime()->gc.waitBackgroundFreeEnd();
    879  args.rval().setUndefined();
    880  return true;
    881 }
    882 
    883 static bool RelazifyFunctions(JSContext* cx, unsigned argc, Value* vp) {
    884  // Relazifying functions on GC is usually only done for compartments that are
    885  // not active. To aid fuzzing, this testing function allows us to relazify
    886  // even if the compartment is active.
    887 
    888  CallArgs args = CallArgsFromVp(argc, vp);
    889 
    890  // Disable relazification of all scripts on stack. It is a pervasive
    891  // assumption in the engine that running scripts still have bytecode.
    892  for (AllScriptFramesIter i(cx); !i.done(); ++i) {
    893    i.script()->clearAllowRelazify();
    894  }
    895 
    896  cx->runtime()->allowRelazificationForTesting = true;
    897 
    898  JS::PrepareForFullGC(cx);
    899  JS::NonIncrementalGC(cx, JS::GCOptions::Shrink, JS::GCReason::API);
    900 
    901  cx->runtime()->allowRelazificationForTesting = false;
    902 
    903  args.rval().setUndefined();
    904  return true;
    905 }
    906 
    907 static bool IsProxy(JSContext* cx, unsigned argc, Value* vp) {
    908  CallArgs args = CallArgsFromVp(argc, vp);
    909  if (args.length() != 1) {
    910    JS_ReportErrorASCII(cx, "the function takes exactly one argument");
    911    return false;
    912  }
    913  if (!args[0].isObject()) {
    914    args.rval().setBoolean(false);
    915    return true;
    916  }
    917  args.rval().setBoolean(args[0].toObject().is<ProxyObject>());
    918  return true;
    919 }
    920 
    921 static bool WasmIsSupported(JSContext* cx, unsigned argc, Value* vp) {
    922  CallArgs args = CallArgsFromVp(argc, vp);
    923  args.rval().setBoolean(wasm::HasSupport(cx) &&
    924                         wasm::AnyCompilerAvailable(cx));
    925  return true;
    926 }
    927 
    928 static bool WasmIsSupportedByHardware(JSContext* cx, unsigned argc, Value* vp) {
    929  CallArgs args = CallArgsFromVp(argc, vp);
    930  args.rval().setBoolean(wasm::HasPlatformSupport());
    931  return true;
    932 }
    933 
    934 static bool WasmDebuggingEnabled(JSContext* cx, unsigned argc, Value* vp) {
    935  CallArgs args = CallArgsFromVp(argc, vp);
    936  args.rval().setBoolean(wasm::HasSupport(cx) && wasm::BaselineAvailable(cx));
    937  return true;
    938 }
    939 
    940 static bool WasmStreamingEnabled(JSContext* cx, unsigned argc, Value* vp) {
    941  CallArgs args = CallArgsFromVp(argc, vp);
    942  args.rval().setBoolean(wasm::StreamingCompilationAvailable(cx));
    943  return true;
    944 }
    945 
    946 static bool WasmCachingEnabled(JSContext* cx, unsigned argc, Value* vp) {
    947  CallArgs args = CallArgsFromVp(argc, vp);
    948  args.rval().setBoolean(wasm::CodeCachingAvailable(cx));
    949  return true;
    950 }
    951 
    952 static bool WasmHugeMemorySupported(JSContext* cx, unsigned argc, Value* vp) {
    953  CallArgs args = CallArgsFromVp(argc, vp);
    954 #ifdef WASM_SUPPORTS_HUGE_MEMORY
    955  args.rval().setBoolean(true);
    956 #else
    957  args.rval().setBoolean(false);
    958 #endif
    959  return true;
    960 }
    961 
    962 static bool WasmMaxMemoryPages(JSContext* cx, unsigned argc, Value* vp) {
    963  CallArgs args = CallArgsFromVp(argc, vp);
    964  if (args.length() < 1) {
    965    JS_ReportErrorASCII(cx, "not enough arguments");
    966    return false;
    967  }
    968  if (!args.get(0).isString()) {
    969    JS_ReportErrorASCII(cx, "address type must be a string");
    970    return false;
    971  }
    972  RootedString s(cx, args.get(0).toString());
    973  Rooted<JSLinearString*> ls(cx, s->ensureLinear(cx));
    974  if (!ls) {
    975    return false;
    976  }
    977  wasm::PageSize pageSize = wasm::PageSize::Standard;
    978  if (argc > 1 && args.get(1).isInt32()) {
    979    uint32_t pageSizeBytes = args.get(1).toInt32();
    980    if (pageSizeBytes != PageSizeInBytes(wasm::PageSize::Standard)) {
    981      JS_ReportErrorASCII(cx, "bad page size");
    982      return false;
    983    }
    984  }
    985  if (StringEqualsLiteral(ls, "i32")) {
    986    wasm::Pages pages = wasm::MaxMemoryPages(wasm::AddressType::I32, pageSize);
    987    args.rval().setInt32(pages.pageCount());
    988    return true;
    989  }
    990  if (StringEqualsLiteral(ls, "i64")) {
    991    wasm::Pages pages = wasm::MaxMemoryPages(wasm::AddressType::I64, pageSize);
    992    args.rval().setNumber(pages.pageCount());
    993    return true;
    994  }
    995  JS_ReportErrorASCII(cx, "bad address type");
    996  return false;
    997 }
    998 
    999 static bool WasmThreadsEnabled(JSContext* cx, unsigned argc, Value* vp) {
   1000  CallArgs args = CallArgsFromVp(argc, vp);
   1001  args.rval().setBoolean(wasm::ThreadsAvailable(cx));
   1002  return true;
   1003 }
   1004 
   1005 #define WASM_FEATURE(NAME, ...)                                              \
   1006  static bool Wasm##NAME##Enabled(JSContext* cx, unsigned argc, Value* vp) { \
   1007    CallArgs args = CallArgsFromVp(argc, vp);                                \
   1008    args.rval().setBoolean(wasm::NAME##Available(cx));                       \
   1009    return true;                                                             \
   1010  }
   1011 JS_FOR_WASM_FEATURES(WASM_FEATURE);
   1012 #undef WASM_FEATURE
   1013 
   1014 static bool WasmSimdEnabled(JSContext* cx, unsigned argc, Value* vp) {
   1015  CallArgs args = CallArgsFromVp(argc, vp);
   1016  args.rval().setBoolean(wasm::SimdAvailable(cx));
   1017  return true;
   1018 }
   1019 
   1020 static bool WasmCompilersPresent(JSContext* cx, unsigned argc, Value* vp) {
   1021  CallArgs args = CallArgsFromVp(argc, vp);
   1022 
   1023  char buf[256];
   1024  *buf = 0;
   1025  if (wasm::BaselinePlatformSupport()) {
   1026    strcat(buf, "baseline");
   1027  }
   1028  if (wasm::IonPlatformSupport()) {
   1029    if (*buf) {
   1030      strcat(buf, ",");
   1031    }
   1032    strcat(buf, "ion");
   1033  }
   1034 
   1035  return ReturnStringCopy(cx, args, buf);
   1036 }
   1037 
   1038 static bool WasmCompileMode(JSContext* cx, unsigned argc, Value* vp) {
   1039  CallArgs args = CallArgsFromVp(argc, vp);
   1040 
   1041  // This triplet of predicates will select zero or one baseline compiler and
   1042  // zero or one optimizing compiler.
   1043  bool baseline = wasm::BaselineAvailable(cx);
   1044  bool ion = wasm::IonAvailable(cx);
   1045  bool none = !baseline && !ion;
   1046  bool tiered = baseline && ion;
   1047 
   1048  JSStringBuilder result(cx);
   1049  if (none && !result.append("none")) {
   1050    return false;
   1051  }
   1052  if (baseline && !result.append("baseline")) {
   1053    return false;
   1054  }
   1055  if (tiered && !result.append("+")) {
   1056    return false;
   1057  }
   1058  if (ion && !result.append("ion")) {
   1059    return false;
   1060  }
   1061  if (JSString* str = result.finishString()) {
   1062    args.rval().setString(str);
   1063    return true;
   1064  }
   1065  return false;
   1066 }
   1067 
   1068 static bool WasmLazyTieringEnabled(JSContext* cx, unsigned argc, Value* vp) {
   1069  // Note: ensure this function stays in sync with `PlatformCanTier()`.
   1070  CallArgs args = CallArgsFromVp(argc, vp);
   1071  bool baseline = wasm::BaselineAvailable(cx);
   1072  bool ion = wasm::IonAvailable(cx);
   1073 
   1074  bool enabled =
   1075      baseline && ion && JS::Prefs::wasm_lazy_tiering() &&
   1076      (JS::Prefs::wasm_lazy_tiering_synchronous() ||
   1077       (CanUseExtraThreads() && jit::CanFlushExecutionContextForAllThreads()));
   1078 
   1079  args.rval().setBoolean(enabled);
   1080  return true;
   1081 }
   1082 
   1083 static bool WasmBaselineDisabledByFeatures(JSContext* cx, unsigned argc,
   1084                                           Value* vp) {
   1085  CallArgs args = CallArgsFromVp(argc, vp);
   1086  bool isDisabled = false;
   1087  JSStringBuilder reason(cx);
   1088  if (!wasm::BaselineDisabledByFeatures(cx, &isDisabled, &reason)) {
   1089    return false;
   1090  }
   1091  if (isDisabled) {
   1092    JSString* result = reason.finishString();
   1093    if (!result) {
   1094      return false;
   1095    }
   1096    args.rval().setString(result);
   1097  } else {
   1098    args.rval().setBoolean(false);
   1099  }
   1100  return true;
   1101 }
   1102 
   1103 static bool WasmIonDisabledByFeatures(JSContext* cx, unsigned argc, Value* vp) {
   1104  CallArgs args = CallArgsFromVp(argc, vp);
   1105  bool isDisabled = false;
   1106  JSStringBuilder reason(cx);
   1107  if (!wasm::IonDisabledByFeatures(cx, &isDisabled, &reason)) {
   1108    return false;
   1109  }
   1110  if (isDisabled) {
   1111    JSString* result = reason.finishString();
   1112    if (!result) {
   1113      return false;
   1114    }
   1115    args.rval().setString(result);
   1116  } else {
   1117    args.rval().setBoolean(false);
   1118  }
   1119  return true;
   1120 }
   1121 
   1122 #ifdef ENABLE_WASM_SIMD
   1123 #  ifdef DEBUG
   1124 static char lastAnalysisResult[1024];
   1125 
   1126 namespace js {
   1127 namespace wasm {
   1128 void ReportSimdAnalysis(const char* data) {
   1129  strncpy(lastAnalysisResult, data, sizeof(lastAnalysisResult));
   1130  lastAnalysisResult[sizeof(lastAnalysisResult) - 1] = 0;
   1131 }
   1132 }  // namespace wasm
   1133 }  // namespace js
   1134 
   1135 // Unstable API for white-box testing of SIMD optimizations.
   1136 //
   1137 // Current API: takes no arguments, returns a string describing the last Simd
   1138 // simplification applied.
   1139 
   1140 static bool WasmSimdAnalysis(JSContext* cx, unsigned argc, Value* vp) {
   1141  CallArgs args = CallArgsFromVp(argc, vp);
   1142  JSString* result =
   1143      JS_NewStringCopyZ(cx, *lastAnalysisResult ? lastAnalysisResult : "none");
   1144  if (!result) {
   1145    return false;
   1146  }
   1147  args.rval().setString(result);
   1148  *lastAnalysisResult = (char)0;
   1149  return true;
   1150 }
   1151 #  endif
   1152 #endif
   1153 
   1154 static bool WasmGlobalFromArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
   1155  if (!wasm::HasSupport(cx)) {
   1156    JS_ReportErrorASCII(cx, "wasm support unavailable");
   1157    return false;
   1158  }
   1159  CallArgs args = CallArgsFromVp(argc, vp);
   1160 
   1161  if (args.length() < 2) {
   1162    JS_ReportErrorASCII(cx, "not enough arguments");
   1163    return false;
   1164  }
   1165 
   1166  // Get the type of the value
   1167  wasm::ValType valType;
   1168  if (!wasm::ToValType(cx, args.get(0), &valType)) {
   1169    return false;
   1170  }
   1171 
   1172  // Get the array buffer for the value
   1173  if (!args.get(1).isObject() ||
   1174      !args.get(1).toObject().is<ArrayBufferObject>()) {
   1175    JS_ReportErrorASCII(cx, "argument is not an array buffer");
   1176    return false;
   1177  }
   1178  Rooted<ArrayBufferObject*> buffer(
   1179      cx, &args.get(1).toObject().as<ArrayBufferObject>());
   1180 
   1181  // Only allow POD to be created from bytes
   1182  switch (valType.kind()) {
   1183    case wasm::ValType::I32:
   1184    case wasm::ValType::I64:
   1185    case wasm::ValType::F32:
   1186    case wasm::ValType::F64:
   1187    case wasm::ValType::V128:
   1188      break;
   1189    default:
   1190      JS_ReportErrorASCII(
   1191          cx, "invalid valtype for creating WebAssembly.Global from bytes");
   1192      return false;
   1193  }
   1194 
   1195  // Check we have all the bytes we need
   1196  if (valType.size() != buffer->byteLength()) {
   1197    JS_ReportErrorASCII(cx, "array buffer has incorrect size");
   1198    return false;
   1199  }
   1200 
   1201  // Copy the bytes from buffer into a tagged val
   1202  wasm::RootedVal val(cx);
   1203  val.get().initFromRootedLocation(valType, buffer->dataPointer());
   1204 
   1205  // Create the global object
   1206  RootedObject proto(
   1207      cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmGlobal));
   1208  if (!proto) {
   1209    return false;
   1210  }
   1211  Rooted<WasmGlobalObject*> result(
   1212      cx, WasmGlobalObject::create(cx, val, false, proto));
   1213  if (!result) {
   1214    return false;
   1215  }
   1216 
   1217  args.rval().setObject(*result.get());
   1218  return true;
   1219 }
   1220 
   1221 enum class LaneInterp {
   1222  I32x4,
   1223  I64x2,
   1224  F32x4,
   1225  F64x2,
   1226 };
   1227 
   1228 size_t LaneInterpLanes(LaneInterp interp) {
   1229  switch (interp) {
   1230    case LaneInterp::I32x4:
   1231      return 4;
   1232    case LaneInterp::I64x2:
   1233      return 2;
   1234    case LaneInterp::F32x4:
   1235      return 4;
   1236    case LaneInterp::F64x2:
   1237      return 2;
   1238    default:
   1239      MOZ_ASSERT_UNREACHABLE();
   1240      return 0;
   1241  }
   1242 }
   1243 
   1244 static bool ToLaneInterp(JSContext* cx, HandleValue v, LaneInterp* out) {
   1245  RootedString interpStr(cx, ToString(cx, v));
   1246  if (!interpStr) {
   1247    return false;
   1248  }
   1249  Rooted<JSLinearString*> interpLinearStr(cx, interpStr->ensureLinear(cx));
   1250  if (!interpLinearStr) {
   1251    return false;
   1252  }
   1253 
   1254  if (StringEqualsLiteral(interpLinearStr, "i32x4")) {
   1255    *out = LaneInterp::I32x4;
   1256    return true;
   1257  } else if (StringEqualsLiteral(interpLinearStr, "i64x2")) {
   1258    *out = LaneInterp::I64x2;
   1259    return true;
   1260  } else if (StringEqualsLiteral(interpLinearStr, "f32x4")) {
   1261    *out = LaneInterp::F32x4;
   1262    return true;
   1263  } else if (StringEqualsLiteral(interpLinearStr, "f64x2")) {
   1264    *out = LaneInterp::F64x2;
   1265    return true;
   1266  }
   1267 
   1268  JS_ReportErrorASCII(cx, "invalid lane interpretation");
   1269  return false;
   1270 }
   1271 
   1272 static bool WasmGlobalExtractLane(JSContext* cx, unsigned argc, Value* vp) {
   1273  if (!wasm::HasSupport(cx)) {
   1274    JS_ReportErrorASCII(cx, "wasm support unavailable");
   1275    return false;
   1276  }
   1277  CallArgs args = CallArgsFromVp(argc, vp);
   1278 
   1279  if (args.length() < 3) {
   1280    JS_ReportErrorASCII(cx, "not enough arguments");
   1281    return false;
   1282  }
   1283 
   1284  // Get the global value
   1285  if (!args.get(0).isObject() ||
   1286      !args.get(0).toObject().is<WasmGlobalObject>()) {
   1287    JS_ReportErrorASCII(cx, "argument is not wasm value");
   1288    return false;
   1289  }
   1290  Rooted<WasmGlobalObject*> global(
   1291      cx, &args.get(0).toObject().as<WasmGlobalObject>());
   1292 
   1293  // Check that we have a v128 value
   1294  if (global->type().kind() != wasm::ValType::V128) {
   1295    JS_ReportErrorASCII(cx, "global is not a v128 value");
   1296    return false;
   1297  }
   1298  wasm::V128 v128 = global->val().get().v128();
   1299 
   1300  // Get the passed interpretation of lanes
   1301  LaneInterp interp;
   1302  if (!ToLaneInterp(cx, args.get(1), &interp)) {
   1303    return false;
   1304  }
   1305 
   1306  // Get the lane to extract
   1307  int32_t lane;
   1308  if (!ToInt32(cx, args.get(2), &lane)) {
   1309    return false;
   1310  }
   1311 
   1312  // Check that the lane interp is valid
   1313  if (lane < 0 || size_t(lane) >= LaneInterpLanes(interp)) {
   1314    JS_ReportErrorASCII(cx, "invalid lane for interp");
   1315    return false;
   1316  }
   1317 
   1318  wasm::RootedVal val(cx);
   1319  switch (interp) {
   1320    case LaneInterp::I32x4: {
   1321      uint32_t i;
   1322      v128.extractLane<uint32_t>(lane, &i);
   1323      val.set(wasm::Val(i));
   1324      break;
   1325    }
   1326    case LaneInterp::I64x2: {
   1327      uint64_t i;
   1328      v128.extractLane<uint64_t>(lane, &i);
   1329      val.set(wasm::Val(i));
   1330      break;
   1331    }
   1332    case LaneInterp::F32x4: {
   1333      float f;
   1334      v128.extractLane<float>(lane, &f);
   1335      val.set(wasm::Val(f));
   1336      break;
   1337    }
   1338    case LaneInterp::F64x2: {
   1339      double d;
   1340      v128.extractLane<double>(lane, &d);
   1341      val.set(wasm::Val(d));
   1342      break;
   1343    }
   1344    default:
   1345      MOZ_ASSERT_UNREACHABLE();
   1346  }
   1347 
   1348  RootedObject proto(
   1349      cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmGlobal));
   1350  Rooted<WasmGlobalObject*> result(
   1351      cx, WasmGlobalObject::create(cx, val, false, proto));
   1352  args.rval().setObject(*result.get());
   1353  return true;
   1354 }
   1355 
   1356 static bool WasmGlobalsEqual(JSContext* cx, unsigned argc, Value* vp) {
   1357  if (!wasm::HasSupport(cx)) {
   1358    JS_ReportErrorASCII(cx, "wasm support unavailable");
   1359    return false;
   1360  }
   1361  CallArgs args = CallArgsFromVp(argc, vp);
   1362 
   1363  if (args.length() < 2) {
   1364    JS_ReportErrorASCII(cx, "not enough arguments");
   1365    return false;
   1366  }
   1367 
   1368  if (!args.get(0).isObject() ||
   1369      !args.get(0).toObject().is<WasmGlobalObject>() ||
   1370      !args.get(1).isObject() ||
   1371      !args.get(1).toObject().is<WasmGlobalObject>()) {
   1372    JS_ReportErrorASCII(cx, "argument is not wasm value");
   1373    return false;
   1374  }
   1375 
   1376  Rooted<WasmGlobalObject*> a(cx,
   1377                              &args.get(0).toObject().as<WasmGlobalObject>());
   1378  Rooted<WasmGlobalObject*> b(cx,
   1379                              &args.get(1).toObject().as<WasmGlobalObject>());
   1380 
   1381  if (a->type().kind() != b->type().kind()) {
   1382    JS_ReportErrorASCII(cx, "globals are of different kind");
   1383    return false;
   1384  }
   1385 
   1386  bool result;
   1387  const wasm::Val& aVal = a->val().get();
   1388  const wasm::Val& bVal = b->val().get();
   1389  switch (a->type().kind()) {
   1390    case wasm::ValType::I32: {
   1391      result = aVal.i32() == bVal.i32();
   1392      break;
   1393    }
   1394    case wasm::ValType::I64: {
   1395      result = aVal.i64() == bVal.i64();
   1396      break;
   1397    }
   1398    case wasm::ValType::F32: {
   1399      result = mozilla::BitwiseCast<uint32_t>(aVal.f32()) ==
   1400               mozilla::BitwiseCast<uint32_t>(bVal.f32());
   1401      break;
   1402    }
   1403    case wasm::ValType::F64: {
   1404      result = mozilla::BitwiseCast<uint64_t>(aVal.f64()) ==
   1405               mozilla::BitwiseCast<uint64_t>(bVal.f64());
   1406      break;
   1407    }
   1408    case wasm::ValType::V128: {
   1409      // Don't know the interpretation of the v128, so we only can do an exact
   1410      // bitwise equality. Testing code can use wasmGlobalExtractLane to
   1411      // workaround this if needed.
   1412      result = aVal.v128() == bVal.v128();
   1413      break;
   1414    }
   1415    case wasm::ValType::Ref: {
   1416      result = aVal.ref() == bVal.ref();
   1417      break;
   1418    }
   1419    default:
   1420      JS_ReportErrorASCII(cx, "unsupported type");
   1421      return false;
   1422  }
   1423  args.rval().setBoolean(result);
   1424  return true;
   1425 }
   1426 
   1427 // Flavors of NaN values for WebAssembly.
   1428 // See
   1429 // https://webassembly.github.io/spec/core/syntax/values.html#floating-point.
   1430 enum class NaNFlavor {
   1431  // A canonical NaN value.
   1432  //  - the sign bit is unspecified,
   1433  //  - the 8-bit exponent is set to all 1s
   1434  //  - the MSB of the payload is set to 1 (a quieted NaN) and all others to 0.
   1435  Canonical,
   1436  // An arithmetic NaN. This is the same as a canonical NaN including that the
   1437  // payload MSB is set to 1, but one or more of the remaining payload bits MAY
   1438  // BE set to 1 (a canonical NaN specifies all 0s).
   1439  Arithmetic,
   1440 };
   1441 
   1442 static bool IsNaNFlavor(uint32_t bits, NaNFlavor flavor) {
   1443  switch (flavor) {
   1444    case NaNFlavor::Canonical: {
   1445      return (bits & 0x7fffffff) == 0x7fc00000;
   1446    }
   1447    case NaNFlavor::Arithmetic: {
   1448      const uint32_t ArithmeticNaN = 0x7f800000;
   1449      const uint32_t ArithmeticPayloadMSB = 0x00400000;
   1450      bool isNaN = (bits & ArithmeticNaN) == ArithmeticNaN;
   1451      bool isMSBSet = (bits & ArithmeticPayloadMSB) == ArithmeticPayloadMSB;
   1452      return isNaN && isMSBSet;
   1453    }
   1454    default:
   1455      MOZ_CRASH();
   1456  }
   1457 }
   1458 
   1459 static bool IsNaNFlavor(uint64_t bits, NaNFlavor flavor) {
   1460  switch (flavor) {
   1461    case NaNFlavor::Canonical: {
   1462      return (bits & 0x7fffffffffffffff) == 0x7ff8000000000000;
   1463    }
   1464    case NaNFlavor::Arithmetic: {
   1465      uint64_t ArithmeticNaN = 0x7ff0000000000000;
   1466      uint64_t ArithmeticPayloadMSB = 0x0008000000000000;
   1467      bool isNaN = (bits & ArithmeticNaN) == ArithmeticNaN;
   1468      bool isMsbSet = (bits & ArithmeticPayloadMSB) == ArithmeticPayloadMSB;
   1469      return isNaN && isMsbSet;
   1470    }
   1471    default:
   1472      MOZ_CRASH();
   1473  }
   1474 }
   1475 
   1476 static bool ToNaNFlavor(JSContext* cx, HandleValue v, NaNFlavor* out) {
   1477  RootedString flavorStr(cx, ToString(cx, v));
   1478  if (!flavorStr) {
   1479    return false;
   1480  }
   1481  Rooted<JSLinearString*> flavorLinearStr(cx, flavorStr->ensureLinear(cx));
   1482  if (!flavorLinearStr) {
   1483    return false;
   1484  }
   1485 
   1486  if (StringEqualsLiteral(flavorLinearStr, "canonical_nan")) {
   1487    *out = NaNFlavor::Canonical;
   1488    return true;
   1489  } else if (StringEqualsLiteral(flavorLinearStr, "arithmetic_nan")) {
   1490    *out = NaNFlavor::Arithmetic;
   1491    return true;
   1492  }
   1493 
   1494  JS_ReportErrorASCII(cx, "invalid nan flavor");
   1495  return false;
   1496 }
   1497 
   1498 static bool WasmGlobalIsNaN(JSContext* cx, unsigned argc, Value* vp) {
   1499  if (!wasm::HasSupport(cx)) {
   1500    JS_ReportErrorASCII(cx, "wasm support unavailable");
   1501    return false;
   1502  }
   1503  CallArgs args = CallArgsFromVp(argc, vp);
   1504 
   1505  if (args.length() < 2) {
   1506    JS_ReportErrorASCII(cx, "not enough arguments");
   1507    return false;
   1508  }
   1509 
   1510  if (!args.get(0).isObject() ||
   1511      !args.get(0).toObject().is<WasmGlobalObject>()) {
   1512    JS_ReportErrorASCII(cx, "argument is not wasm value");
   1513    return false;
   1514  }
   1515  Rooted<WasmGlobalObject*> global(
   1516      cx, &args.get(0).toObject().as<WasmGlobalObject>());
   1517 
   1518  NaNFlavor flavor;
   1519  if (!ToNaNFlavor(cx, args.get(1), &flavor)) {
   1520    return false;
   1521  }
   1522 
   1523  bool result;
   1524  const wasm::Val& val = global->val().get();
   1525  switch (global->type().kind()) {
   1526    case wasm::ValType::F32: {
   1527      result = IsNaNFlavor(mozilla::BitwiseCast<uint32_t>(val.f32()), flavor);
   1528      break;
   1529    }
   1530    case wasm::ValType::F64: {
   1531      result = IsNaNFlavor(mozilla::BitwiseCast<uint64_t>(val.f64()), flavor);
   1532      break;
   1533    }
   1534    default:
   1535      JS_ReportErrorASCII(cx, "global is not a floating point value");
   1536      return false;
   1537  }
   1538  args.rval().setBoolean(result);
   1539  return true;
   1540 }
   1541 
   1542 static bool WasmGlobalToString(JSContext* cx, unsigned argc, Value* vp) {
   1543  if (!wasm::HasSupport(cx)) {
   1544    JS_ReportErrorASCII(cx, "wasm support unavailable");
   1545    return false;
   1546  }
   1547  CallArgs args = CallArgsFromVp(argc, vp);
   1548 
   1549  if (args.length() < 1) {
   1550    JS_ReportErrorASCII(cx, "not enough arguments");
   1551    return false;
   1552  }
   1553  if (!args.get(0).isObject() ||
   1554      !args.get(0).toObject().is<WasmGlobalObject>()) {
   1555    JS_ReportErrorASCII(cx, "argument is not wasm value");
   1556    return false;
   1557  }
   1558  Rooted<WasmGlobalObject*> global(
   1559      cx, &args.get(0).toObject().as<WasmGlobalObject>());
   1560  const wasm::Val& globalVal = global->val().get();
   1561 
   1562  UniqueChars result;
   1563  switch (globalVal.type().kind()) {
   1564    case wasm::ValType::I32: {
   1565      result = JS_smprintf("i32:%" PRIx32, globalVal.i32());
   1566      break;
   1567    }
   1568    case wasm::ValType::I64: {
   1569      result = JS_smprintf("i64:%" PRIx64, globalVal.i64());
   1570      break;
   1571    }
   1572    case wasm::ValType::F32: {
   1573      result = JS_smprintf("f32:%f", globalVal.f32());
   1574      break;
   1575    }
   1576    case wasm::ValType::F64: {
   1577      result = JS_smprintf("f64:%lf", globalVal.f64());
   1578      break;
   1579    }
   1580    case wasm::ValType::V128: {
   1581      wasm::V128 v128 = globalVal.v128();
   1582      result = JS_smprintf(
   1583          "v128:%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x", v128.bytes[0],
   1584          v128.bytes[1], v128.bytes[2], v128.bytes[3], v128.bytes[4],
   1585          v128.bytes[5], v128.bytes[6], v128.bytes[7], v128.bytes[8],
   1586          v128.bytes[9], v128.bytes[10], v128.bytes[11], v128.bytes[12],
   1587          v128.bytes[13], v128.bytes[14], v128.bytes[15]);
   1588      break;
   1589    }
   1590    case wasm::ValType::Ref: {
   1591      result = JS_smprintf("ref:%" PRIxPTR, globalVal.ref().rawValue());
   1592      break;
   1593    }
   1594    default:
   1595      MOZ_ASSERT_UNREACHABLE();
   1596  }
   1597 
   1598  return ReturnStringCopy(cx, args, result.get());
   1599 }
   1600 
   1601 static bool WasmLosslessInvoke(JSContext* cx, unsigned argc, Value* vp) {
   1602  if (!wasm::HasSupport(cx)) {
   1603    JS_ReportErrorASCII(cx, "wasm support unavailable");
   1604    return false;
   1605  }
   1606  CallArgs args = CallArgsFromVp(argc, vp);
   1607 
   1608  if (args.length() < 1) {
   1609    JS_ReportErrorASCII(cx, "not enough arguments");
   1610    return false;
   1611  }
   1612  if (!args.get(0).isObject() || !args.get(0).toObject().is<JSFunction>()) {
   1613    JS_ReportErrorASCII(cx, "argument is not an object");
   1614    return false;
   1615  }
   1616 
   1617  RootedFunction func(cx, &args[0].toObject().as<JSFunction>());
   1618  if (!func || !func->isWasm()) {
   1619    JS_ReportErrorASCII(cx, "argument is not an exported wasm function");
   1620    return false;
   1621  }
   1622 
   1623  // Switch to the function's realm
   1624  AutoRealm ar(cx, func);
   1625 
   1626  // Get the instance and funcIndex for calling the function
   1627  wasm::Instance& instance = func->wasmInstance();
   1628  uint32_t funcIndex = func->wasmFuncIndex();
   1629 
   1630  // Set up a modified call frame following the standard JS
   1631  // [callee, this, arguments...] convention.
   1632  RootedValueVector wasmCallFrame(cx);
   1633  size_t len = 2 + args.length();
   1634  if (!wasmCallFrame.resize(len)) {
   1635    return false;
   1636  }
   1637  wasmCallFrame[0].set(ObjectValue(*func));
   1638  wasmCallFrame[1].set(args.thisv());
   1639  // Copy over the arguments needed to invoke the provided wasm function,
   1640  // skipping the wasm function we're calling that is at `args.get(0)`.
   1641  for (size_t i = 1; i < args.length(); i++) {
   1642    size_t wasmArg = i - 1;
   1643    wasmCallFrame[2 + wasmArg].set(args.get(i));
   1644  }
   1645  size_t wasmArgc = argc - 1;
   1646  CallArgs wasmCallArgs(CallArgsFromVp(wasmArgc, wasmCallFrame.begin()));
   1647 
   1648  // Invoke the function with the new call frame
   1649  bool result = instance.callExport(cx, funcIndex, wasmCallArgs,
   1650                                    wasm::CoercionLevel::Lossless);
   1651  // Assign the wasm rval to our rval
   1652  args.rval().set(wasmCallArgs.rval());
   1653  return result;
   1654 }
   1655 
   1656 static bool ConvertToTier(JSContext* cx, HandleValue value,
   1657                          const wasm::Code& code, wasm::Tier* tier) {
   1658  RootedString option(cx, JS::ToString(cx, value));
   1659 
   1660  if (!option) {
   1661    return false;
   1662  }
   1663 
   1664  bool stableTier = false;
   1665  bool bestTier = false;
   1666  bool baselineTier = false;
   1667  bool ionTier = false;
   1668 
   1669  if (!JS_StringEqualsLiteral(cx, option, "stable", &stableTier) ||
   1670      !JS_StringEqualsLiteral(cx, option, "best", &bestTier) ||
   1671      !JS_StringEqualsLiteral(cx, option, "baseline", &baselineTier) ||
   1672      !JS_StringEqualsLiteral(cx, option, "ion", &ionTier)) {
   1673    return false;
   1674  }
   1675 
   1676  if (stableTier) {
   1677    *tier = code.stableCompleteTier();
   1678  } else if (bestTier) {
   1679    *tier = code.bestCompleteTier();
   1680  } else if (baselineTier) {
   1681    *tier = wasm::Tier::Baseline;
   1682  } else if (ionTier) {
   1683    *tier = wasm::Tier::Optimized;
   1684  } else {
   1685    // You can omit the argument but you can't pass just anything you like
   1686    return false;
   1687  }
   1688 
   1689  return true;
   1690 }
   1691 
   1692 static bool WasmExtractCode(JSContext* cx, unsigned argc, Value* vp) {
   1693  if (!wasm::HasSupport(cx)) {
   1694    JS_ReportErrorASCII(cx, "wasm support unavailable");
   1695    return false;
   1696  }
   1697 
   1698  CallArgs args = CallArgsFromVp(argc, vp);
   1699 
   1700  if (!args.get(0).isObject()) {
   1701    JS_ReportErrorASCII(cx, "argument is not an object");
   1702    return false;
   1703  }
   1704 
   1705  Rooted<WasmModuleObject*> module(
   1706      cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>());
   1707  if (!module) {
   1708    JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
   1709    return false;
   1710  }
   1711 
   1712  wasm::Tier tier = module->module().code().stableCompleteTier();
   1713  if (args.length() > 1 &&
   1714      !ConvertToTier(cx, args[1], module->module().code(), &tier)) {
   1715    args.rval().setNull();
   1716    return false;
   1717  }
   1718 
   1719  RootedValue result(cx);
   1720  if (!module->module().extractCode(cx, tier, &result)) {
   1721    return false;
   1722  }
   1723 
   1724  args.rval().set(result);
   1725  return true;
   1726 }
   1727 
   1728 static bool HasDisassembler(JSContext* cx, unsigned argc, Value* vp) {
   1729  CallArgs args = CallArgsFromVp(argc, vp);
   1730  args.rval().setBoolean(jit::HasDisassembler());
   1731  return true;
   1732 }
   1733 
   1734 MOZ_THREAD_LOCAL(JSSprinter*) disasmPrinter;
   1735 
   1736 static void captureDisasmText(const char* text) {
   1737  JSSprinter* printer = disasmPrinter.get();
   1738  printer->put(text);
   1739  printer->printf("\n");
   1740 }
   1741 
   1742 static bool DisassembleNative(JSContext* cx, unsigned argc, Value* vp) {
   1743  CallArgs args = CallArgsFromVp(argc, vp);
   1744 
   1745  if (args.length() < 1) {
   1746    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   1747                              JSMSG_MORE_ARGS_NEEDED, "disnative", "1", "",
   1748                              "0");
   1749    return false;
   1750  }
   1751 
   1752  if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
   1753    JS_ReportErrorASCII(cx, "The first argument must be a function.");
   1754    return false;
   1755  }
   1756 
   1757  JSSprinter sprinter(cx);
   1758  if (!sprinter.init()) {
   1759    return false;
   1760  }
   1761 
   1762  RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
   1763 
   1764  uint8_t* jit_begin = nullptr;
   1765  uint8_t* jit_end = nullptr;
   1766 
   1767  if (fun->isAsmJSNative() || fun->isWasmWithJitEntry()) {
   1768    if (IsAsmJSModule(fun)) {
   1769      JS_ReportErrorASCII(cx, "Can't disassemble asm.js module function.");
   1770      return false;
   1771    }
   1772    if (fun->isAsmJSNative()) {
   1773      sprinter.printf("; backend=asmjs\n");
   1774    }
   1775    sprinter.printf("; backend=wasm\n");
   1776 
   1777    js::wasm::Instance& inst = fun->wasmInstance();
   1778    const uint32_t funcIndex = fun->wasmFuncIndex();
   1779    const js::wasm::CodeBlock& codeBlock = inst.code().funcCodeBlock(funcIndex);
   1780    const js::wasm::FuncExport& func = codeBlock.lookupFuncExport(funcIndex);
   1781    const js::wasm::CodeRange& codeRange = codeBlock.codeRange(func);
   1782 
   1783    jit_begin = codeBlock.base() + codeRange.begin();
   1784    jit_end = codeBlock.base() + codeRange.end();
   1785  } else if (fun->hasJitScript() && fun->nonLazyScript()->hasIonScript()) {
   1786    sprinter.printf("; backend=ion\n");
   1787    jit_begin = fun->nonLazyScript()->ionScript()->method()->raw();
   1788    jit_end = fun->nonLazyScript()->ionScript()->method()->rawEnd();
   1789  } else if (fun->hasJitScript() && fun->nonLazyScript()->hasBaselineScript()) {
   1790    sprinter.printf("; backend=baseline\n");
   1791    jit_begin = fun->nonLazyScript()->baselineScript()->method()->raw();
   1792    jit_end = fun->nonLazyScript()->baselineScript()->method()->rawEnd();
   1793  } else {
   1794    JS_ReportErrorASCII(cx,
   1795                        "The function hasn't been warmed up, hence no JIT code "
   1796                        "to disassemble.");
   1797    return false;
   1798  }
   1799 
   1800  MOZ_ASSERT(jit_begin);
   1801  MOZ_ASSERT(jit_end);
   1802 
   1803 #ifdef JS_CODEGEN_ARM
   1804  // The ARM32 disassembler is currently not fuzzing-safe because it doesn't
   1805  // handle constant pools correctly (bug 1875363).
   1806  if (fuzzingSafe) {
   1807    JS_ReportErrorASCII(cx, "disnative is not fuzzing-safe on ARM32");
   1808    return false;
   1809  }
   1810 #elif defined(JS_CODEGEN_RISCV64)
   1811  // The riscv64 disassembler is currently not fuzzing-safe because it doesn't
   1812  // handle constant pools correctly (bug 1987559).
   1813  if (fuzzingSafe) {
   1814    JS_ReportErrorASCII(cx, "disnative is not fuzzing-safe on riscv64");
   1815    return false;
   1816  }
   1817 #endif
   1818 
   1819  // Dump the raw code to a file before disassembling in case
   1820  // finishString triggers a GC and discards the jitcode.
   1821  if (!fuzzingSafe && args.length() > 1 && args[1].isString()) {
   1822    RootedString str(cx, args[1].toString());
   1823    JS::UniqueChars fileNameBytes = JS_EncodeStringToUTF8(cx, str);
   1824 
   1825    const char* fileName = fileNameBytes.get();
   1826    if (!fileName) {
   1827      ReportOutOfMemory(cx);
   1828      return false;
   1829    }
   1830 
   1831    FILE* f = fopen(fileName, "w");
   1832    if (!f) {
   1833      JS_ReportErrorASCII(cx, "Could not open file for writing.");
   1834      return false;
   1835    }
   1836 
   1837    uintptr_t expected_length = reinterpret_cast<uintptr_t>(jit_end) -
   1838                                reinterpret_cast<uintptr_t>(jit_begin);
   1839    if (expected_length != fwrite(jit_begin, jit_end - jit_begin, 1, f)) {
   1840      JS_ReportErrorASCII(cx, "Did not write all function bytes to the file.");
   1841      fclose(f);
   1842      return false;
   1843    }
   1844    fclose(f);
   1845  }
   1846 
   1847  disasmPrinter.set(&sprinter);
   1848  auto onFinish = mozilla::MakeScopeExit([&] { disasmPrinter.set(nullptr); });
   1849 
   1850  jit::Disassemble(jit_begin, jit_end - jit_begin, &captureDisasmText);
   1851 
   1852  JSString* str = sprinter.release(cx);
   1853  if (!str) {
   1854    return false;
   1855  }
   1856 
   1857  args.rval().setString(str);
   1858  return true;
   1859 }
   1860 
   1861 static bool DisassembleBaselineICs(JSContext* cx, unsigned argc, Value* vp) {
   1862  CallArgs args = CallArgsFromVp(argc, vp);
   1863  args.rval().setUndefined();
   1864 
   1865  if (!args.requireAtLeast(cx, "disblic", 1)) {
   1866    return false;
   1867  }
   1868 
   1869  if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
   1870    JS_ReportErrorASCII(cx, "The first argument must be a function.");
   1871    return false;
   1872  }
   1873 
   1874  JSSprinter sprinter(cx);
   1875  if (!sprinter.init()) {
   1876    return false;
   1877  }
   1878 
   1879  disasmPrinter.set(&sprinter);
   1880  auto onFinish = mozilla::MakeScopeExit([&] { disasmPrinter.set(nullptr); });
   1881 
   1882  RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
   1883 
   1884  if (!fun->hasJitScript()) {
   1885    args.rval().setUndefined();
   1886    return true;
   1887  }
   1888 
   1889 #ifdef JS_CODEGEN_ARM
   1890  // The ARM32 disassembler is currently not fuzzing-safe because it doesn't
   1891  // handle constant pools correctly (bug 1875363).
   1892  if (fuzzingSafe) {
   1893    JS_ReportErrorASCII(cx, "disblic is not fuzzing-safe on ARM32");
   1894    return false;
   1895  }
   1896 #elif defined(JS_CODEGEN_RISCV64)
   1897  // The riscv64 disassembler is currently not fuzzing-safe because it doesn't
   1898  // handle constant pools correctly (bug 1987559).
   1899  if (fuzzingSafe) {
   1900    JS_ReportErrorASCII(cx, "disblic is not fuzzing-safe on riscv64");
   1901    return false;
   1902  }
   1903 #endif
   1904 
   1905  RootedScript script(cx, fun->nonLazyScript());
   1906  jit::ICScript* icScript = script->jitScript()->icScript();
   1907  for (uint32_t i = 0; i < icScript->numICEntries(); i++) {
   1908    jit::ICEntry& entry = icScript->icEntry(i);
   1909    jit::ICStub* stub = entry.firstStub();
   1910 
   1911    jit::ICStub* fallbackStub = stub;
   1912    while (!fallbackStub->isFallback()) {
   1913      fallbackStub = fallbackStub->toCacheIRStub()->next();
   1914    }
   1915    uint32_t pcOffset = fallbackStub->toFallbackStub()->pcOffset();
   1916    sprinter.printf("; %s (pcOffset %05u)\n",
   1917                    CodeName(JSOp(*script->offsetToPC(pcOffset))), pcOffset);
   1918 
   1919    uint32_t stubNum = 1;
   1920    while (!stub->isFallback()) {
   1921      sprinter.printf(";   Stub #%d (entry count: %d)\n", stubNum,
   1922                      stub->enteredCount());
   1923      jit::ICCacheIRStub* cacheIRStub = stub->toCacheIRStub();
   1924      uint8_t* jit_begin = stub->jitCode()->raw();
   1925      uint8_t* jit_end = stub->jitCode()->rawEnd();
   1926 #ifdef JS_CACHEIR_SPEW
   1927      sprinter.printf(";   IR:\n");
   1928      SpewCacheIROps(sprinter, ";        ", cacheIRStub->stubInfo());
   1929 #endif
   1930 
   1931      jit::Disassemble(jit_begin, jit_end - jit_begin, &captureDisasmText);
   1932      stub = cacheIRStub->next();
   1933      stubNum++;
   1934    }
   1935  }
   1936 
   1937  JSString* str = sprinter.release(cx);
   1938  if (!str) {
   1939    return false;
   1940  }
   1941 
   1942  args.rval().setString(str);
   1943  return true;
   1944 }
   1945 
   1946 static bool ComputeTier(JSContext* cx, const wasm::Code& code,
   1947                        HandleValue tierSelection, wasm::Tier* tier) {
   1948  *tier = code.stableCompleteTier();
   1949  if (!tierSelection.isUndefined() &&
   1950      !ConvertToTier(cx, tierSelection, code, tier)) {
   1951    JS_ReportErrorASCII(cx, "invalid tier");
   1952    return false;
   1953  }
   1954 
   1955  return true;
   1956 }
   1957 
   1958 template <typename DisasmFunction>
   1959 static bool DisassembleIt(JSContext* cx, bool asString, MutableHandleValue rval,
   1960                          DisasmFunction&& disassembleIt) {
   1961  if (asString) {
   1962    JSSprinter sprinter(cx);
   1963    if (!sprinter.init()) {
   1964      return false;
   1965    }
   1966    disasmPrinter.set(&sprinter);
   1967    auto onFinish = mozilla::MakeScopeExit([&] { disasmPrinter.set(nullptr); });
   1968    disassembleIt(captureDisasmText);
   1969 
   1970    JSString* sresult = sprinter.release(cx);
   1971    if (!sresult) {
   1972      return false;
   1973    }
   1974    rval.setString(sresult);
   1975    return true;
   1976  }
   1977 
   1978  disassembleIt([](const char* text) { fprintf(stderr, "%s\n", text); });
   1979  return true;
   1980 }
   1981 
   1982 static bool WasmDisassembleFunction(JSContext* cx, const HandleFunction& func,
   1983                                    HandleValue tierSelection, bool asString,
   1984                                    MutableHandleValue rval) {
   1985  wasm::Instance& instance = func->wasmInstance();
   1986  uint32_t funcIndex = func->wasmFuncIndex();
   1987  wasm::Tier tier;
   1988 
   1989  if (!ComputeTier(cx, instance.code(), tierSelection, &tier)) {
   1990    return false;
   1991  }
   1992 
   1993  if (!instance.code().funcHasTier(funcIndex, tier)) {
   1994    JS_ReportErrorASCII(cx, "function missing selected tier");
   1995    return false;
   1996  }
   1997 
   1998  return DisassembleIt(
   1999      cx, asString, rval, [&](void (*captureText)(const char*)) {
   2000        instance.disassembleExport(cx, funcIndex, tier, captureText);
   2001      });
   2002 }
   2003 
   2004 static bool WasmDisassembleCode(JSContext* cx, const wasm::Code& code,
   2005                                HandleValue tierSelection, int kindSelection,
   2006                                bool asString, MutableHandleValue rval) {
   2007  wasm::Tier tier;
   2008  if (!ComputeTier(cx, code, tierSelection, &tier)) {
   2009    return false;
   2010  }
   2011 
   2012  return DisassembleIt(cx, asString, rval,
   2013                       [&](void (*captureText)(const char*)) {
   2014                         code.disassemble(cx, tier, kindSelection, captureText);
   2015                       });
   2016 }
   2017 
   2018 static bool WasmDisassemble(JSContext* cx, unsigned argc, Value* vp) {
   2019  if (!wasm::HasSupport(cx)) {
   2020    JS_ReportErrorASCII(cx, "wasm support unavailable");
   2021    return false;
   2022  }
   2023 
   2024  CallArgs args = CallArgsFromVp(argc, vp);
   2025 
   2026  args.rval().set(UndefinedValue());
   2027 
   2028  if (!args.get(0).isObject()) {
   2029    JS_ReportErrorASCII(cx, "argument is not an object");
   2030    return false;
   2031  }
   2032 
   2033  bool asString = false;
   2034  RootedValue tierSelection(cx);
   2035  int kindSelection = (1 << wasm::CodeRange::Function);
   2036  if (args.length() > 1 && args[1].isObject()) {
   2037    RootedObject options(cx, &args[1].toObject());
   2038    RootedValue val(cx);
   2039 
   2040    if (!JS_GetProperty(cx, options, "asString", &val)) {
   2041      return false;
   2042    }
   2043    asString = val.isBoolean() && val.toBoolean();
   2044 
   2045    if (!JS_GetProperty(cx, options, "tier", &tierSelection)) {
   2046      return false;
   2047    }
   2048 
   2049    if (!JS_GetProperty(cx, options, "kinds", &val)) {
   2050      return false;
   2051    }
   2052    if (val.isString() && val.toString()->hasLatin1Chars()) {
   2053      AutoStableStringChars stable(cx);
   2054      if (!stable.init(cx, val.toString())) {
   2055        return false;
   2056      }
   2057      const char* p = (const char*)(stable.latin1Chars());
   2058      const char* end = p + val.toString()->length();
   2059      int selection = 0;
   2060      for (;;) {
   2061        if (strncmp(p, "Function", 8) == 0) {
   2062          selection |= (1 << wasm::CodeRange::Function);
   2063          p += 8;
   2064        } else if (strncmp(p, "InterpEntry", 11) == 0) {
   2065          selection |= (1 << wasm::CodeRange::InterpEntry);
   2066          p += 11;
   2067        } else if (strncmp(p, "JitEntry", 8) == 0) {
   2068          selection |= (1 << wasm::CodeRange::JitEntry);
   2069          p += 8;
   2070        } else if (strncmp(p, "ImportInterpExit", 16) == 0) {
   2071          selection |= (1 << wasm::CodeRange::ImportInterpExit);
   2072          p += 16;
   2073        } else if (strncmp(p, "ImportJitExit", 13) == 0) {
   2074          selection |= (1 << wasm::CodeRange::ImportJitExit);
   2075          p += 13;
   2076        } else if (strncmp(p, "all", 3) == 0) {
   2077          selection = ~0;
   2078          p += 3;
   2079        } else {
   2080          break;
   2081        }
   2082        if (p == end || *p != ',') {
   2083          break;
   2084        }
   2085        p++;
   2086      }
   2087      if (p == end) {
   2088        kindSelection = selection;
   2089      } else {
   2090        JS_ReportErrorASCII(cx, "argument object has invalid `kinds`");
   2091        return false;
   2092      }
   2093    }
   2094  }
   2095 
   2096  RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>());
   2097  if (func && func->isWasm()) {
   2098    return WasmDisassembleFunction(cx, func, tierSelection, asString,
   2099                                   args.rval());
   2100  }
   2101  if (args[0].toObject().is<WasmModuleObject>()) {
   2102    return WasmDisassembleCode(
   2103        cx, args[0].toObject().as<WasmModuleObject>().module().code(),
   2104        tierSelection, kindSelection, asString, args.rval());
   2105  }
   2106  if (args[0].toObject().is<WasmInstanceObject>()) {
   2107    return WasmDisassembleCode(
   2108        cx, args[0].toObject().as<WasmInstanceObject>().instance().code(),
   2109        tierSelection, kindSelection, asString, args.rval());
   2110  }
   2111  JS_ReportErrorASCII(
   2112      cx, "argument is not an exported wasm function or a wasm module");
   2113  return false;
   2114 }
   2115 
   2116 static bool WasmModuleToText(JSContext* cx, unsigned argc, Value* vp) {
   2117  if (!wasm::HasSupport(cx)) {
   2118    JS_ReportErrorASCII(cx, "wasm support unavailable");
   2119    return false;
   2120  }
   2121 
   2122  CallArgs args = CallArgsFromVp(argc, vp);
   2123 
   2124  if (!args.get(0).isObject()) {
   2125    JS_ReportErrorASCII(cx, "argument is not an object");
   2126    return false;
   2127  }
   2128 
   2129  if (!args[0].toObject().is<WasmModuleObject>()) {
   2130    JS_ReportErrorASCII(cx, "argument is not a wasm module");
   2131    return false;
   2132  }
   2133 
   2134  const wasm::Module& mod = args[0].toObject().as<WasmModuleObject>().module();
   2135  JSSprinter out(cx);
   2136  if (!out.init()) {
   2137    ReportOutOfMemory(cx);
   2138    return false;
   2139  }
   2140  wasm::DumpModule(mod, out);
   2141 
   2142  JSString* str = out.release(cx);
   2143  if (!str) {
   2144    ReportOutOfMemory(cx);
   2145    return false;
   2146  }
   2147  args.rval().set(StringValue(str));
   2148  return true;
   2149 }
   2150 
   2151 static bool WasmFunctionTier(JSContext* cx, unsigned argc, Value* vp) {
   2152  if (!wasm::HasSupport(cx)) {
   2153    JS_ReportErrorASCII(cx, "wasm support unavailable");
   2154    return false;
   2155  }
   2156 
   2157  CallArgs args = CallArgsFromVp(argc, vp);
   2158 
   2159  args.rval().set(UndefinedValue());
   2160 
   2161  if (!args.get(0).isObject()) {
   2162    JS_ReportErrorASCII(cx, "argument is not an object");
   2163    return false;
   2164  }
   2165 
   2166  RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>());
   2167  if (func && func->isWasm()) {
   2168    uint32_t funcIndex = func->wasmFuncIndex();
   2169    wasm::Instance& instance = func->wasmInstance();
   2170    if (funcIndex < instance.code().funcImports().length()) {
   2171      JS_ReportErrorASCII(cx, "argument is an imported function");
   2172      return false;
   2173    }
   2174    wasm::Tier tier = instance.code().funcTier(funcIndex);
   2175    return ReturnStringCopy(cx, args, wasm::ToString(tier));
   2176  }
   2177  JS_ReportErrorASCII(cx, "argument is not an exported wasm function");
   2178  return false;
   2179 }
   2180 
   2181 static bool WasmDumpIon(JSContext* cx, unsigned argc, Value* vp) {
   2182  if (!wasm::HasSupport(cx)) {
   2183    JS_ReportErrorASCII(cx, "wasm support unavailable");
   2184    return false;
   2185  }
   2186 
   2187  CallArgs args = CallArgsFromVp(argc, vp);
   2188 
   2189  args.rval().set(UndefinedValue());
   2190 
   2191  if (!args.get(0).isObject()) {
   2192    JS_ReportErrorASCII(cx, "argument is not an object");
   2193    return false;
   2194  }
   2195 
   2196  uint32_t targetFuncIndex;
   2197  if (!ToUint32(cx, args.get(1), &targetFuncIndex)) {
   2198    JS_ReportErrorASCII(cx, "argument is not a func index");
   2199    return false;
   2200  }
   2201 
   2202  SharedMem<uint8_t*> dataPointer;
   2203  size_t byteLength;
   2204  if (!IsBufferSource(cx, args.get(0).toObjectOrNull(), /*allowShared*/ false,
   2205                      /*allowResizable*/ false, &dataPointer, &byteLength)) {
   2206    JS_ReportErrorASCII(cx, "argument is not a buffer source");
   2207    return false;
   2208  }
   2209 
   2210  wasm::MutableBytes bytecode = cx->new_<wasm::ShareableBytes>();
   2211  if (!bytecode) {
   2212    return false;
   2213  }
   2214  if (!bytecode->append(dataPointer.unwrap(), byteLength)) {
   2215    ReportOutOfMemory(cx);
   2216    return false;
   2217  }
   2218 
   2219  UniqueChars error;
   2220  JSSprinter out(cx);
   2221  if (!out.init()) {
   2222    ReportOutOfMemory(cx);
   2223    return false;
   2224  }
   2225 
   2226  if (!wasm::DumpIonFunctionInModule(*bytecode, targetFuncIndex, out, &error)) {
   2227    if (error) {
   2228      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
   2229                               JSMSG_WASM_COMPILE_ERROR, error.get());
   2230      return false;
   2231    }
   2232    ReportOutOfMemory(cx);
   2233    return false;
   2234  }
   2235 
   2236  JSString* str = out.release(cx);
   2237  if (!str) {
   2238    ReportOutOfMemory(cx);
   2239    return false;
   2240  }
   2241  args.rval().set(StringValue(str));
   2242  return true;
   2243 }
   2244 
   2245 enum class Flag { Tier2Complete, Deserialized, ParsedBranchHints };
   2246 
   2247 static bool WasmReturnFlag(JSContext* cx, unsigned argc, Value* vp, Flag flag) {
   2248  CallArgs args = CallArgsFromVp(argc, vp);
   2249 
   2250  if (!args.get(0).isObject()) {
   2251    JS_ReportErrorASCII(cx, "argument is not an object");
   2252    return false;
   2253  }
   2254 
   2255  Rooted<WasmModuleObject*> module(
   2256      cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>());
   2257  if (!module) {
   2258    JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
   2259    return false;
   2260  }
   2261 
   2262  bool b;
   2263  switch (flag) {
   2264    case Flag::Tier2Complete:
   2265      b = !module->module().testingTier2Active();
   2266      break;
   2267    case Flag::Deserialized:
   2268      b = module->module().loggingDeserialized();
   2269      break;
   2270    case Flag::ParsedBranchHints:
   2271      b = !module->module().codeMeta().branchHints.failedParse();
   2272      break;
   2273  }
   2274 
   2275  args.rval().set(BooleanValue(b));
   2276  return true;
   2277 }
   2278 
   2279 static bool wasmMetadataAnalysis(JSContext* cx, unsigned argc, Value* vp) {
   2280  CallArgs args = CallArgsFromVp(argc, vp);
   2281 
   2282  if (!args.get(0).isObject()) {
   2283    JS_ReportErrorASCII(cx, "argument is not an object");
   2284    return false;
   2285  }
   2286 
   2287  if (args[0].toObject().is<WasmModuleObject>()) {
   2288    HashMap<const char*, uint32_t, mozilla::CStringHasher, SystemAllocPolicy>
   2289        hashmap = args[0]
   2290                      .toObject()
   2291                      .as<WasmModuleObject>()
   2292                      .module()
   2293                      .code()
   2294                      .metadataAnalysis(cx);
   2295    if (hashmap.empty()) {
   2296      JS_ReportErrorASCII(cx, "Metadata analysis has failed");
   2297      return false;
   2298    }
   2299 
   2300    // metadataAnalysis returned a map of {key, value} with various statistics
   2301    // convert it into a dictionary to be used by JS
   2302    Rooted<IdValueVector> props(cx, IdValueVector(cx));
   2303 
   2304    for (auto iter = hashmap.iter(); !iter.done(); iter.next()) {
   2305      const auto* key = iter.get().key();
   2306      auto value = iter.get().value();
   2307 
   2308      JSString* string = JS_NewStringCopyZ(cx, key);
   2309      if (!string) {
   2310        return false;
   2311      }
   2312 
   2313      if (!props.append(
   2314              IdValuePair(NameToId(string->asLinear().toPropertyName(cx)),
   2315                          NumberValue(value)))) {
   2316        return false;
   2317      }
   2318    }
   2319 
   2320    JSObject* results = NewPlainObjectWithUniqueNames(cx, props);
   2321    if (!results) {
   2322      return false;
   2323    }
   2324    args.rval().setObject(*results);
   2325 
   2326    return true;
   2327  }
   2328 
   2329  JS_ReportErrorASCII(
   2330      cx, "argument is not an exported wasm function or a wasm module");
   2331 
   2332  return false;
   2333 }
   2334 
   2335 static bool WasmHasTier2CompilationCompleted(JSContext* cx, unsigned argc,
   2336                                             Value* vp) {
   2337  return WasmReturnFlag(cx, argc, vp, Flag::Tier2Complete);
   2338 }
   2339 
   2340 static bool WasmLoadedFromCache(JSContext* cx, unsigned argc, Value* vp) {
   2341  return WasmReturnFlag(cx, argc, vp, Flag::Deserialized);
   2342 }
   2343 
   2344 #ifdef ENABLE_WASM_BRANCH_HINTING
   2345 static bool WasmParsedBranchHints(JSContext* cx, unsigned argc, Value* vp) {
   2346  return WasmReturnFlag(cx, argc, vp, Flag::ParsedBranchHints);
   2347 }
   2348 #endif  // ENABLE_WASM_BRANCH_HINTING
   2349 
   2350 static bool WasmBuiltinI8VecMul(JSContext* cx, unsigned argc, Value* vp) {
   2351  if (!wasm::HasSupport(cx)) {
   2352    JS_ReportErrorASCII(cx, "wasm support unavailable");
   2353    return false;
   2354  }
   2355 
   2356  CallArgs args = CallArgsFromVp(argc, vp);
   2357 
   2358  Rooted<WasmModuleObject*> module(cx);
   2359  if (!wasm::CompileBuiltinModule(cx, wasm::BuiltinModuleId::SelfTest, nullptr,
   2360                                  &module)) {
   2361    return false;
   2362  }
   2363  args.rval().set(ObjectValue(*module.get()));
   2364  return true;
   2365 }
   2366 
   2367 static bool WasmGcReadField(JSContext* cx, unsigned argc, Value* vp) {
   2368  CallArgs args = CallArgsFromVp(argc, vp);
   2369  RootedObject callee(cx, &args.callee());
   2370 
   2371  if (!args.requireAtLeast(cx, "wasmGcReadField", 2)) {
   2372    return false;
   2373  }
   2374 
   2375  if (!args[0].isObject() || !args[0].toObject().is<WasmGcObject>()) {
   2376    ReportUsageErrorASCII(cx, callee,
   2377                          "First argument must be a WebAssembly GC object");
   2378    return false;
   2379  }
   2380 
   2381  int32_t fieldIndex;
   2382  if (!JS::ToInt32(cx, args[1], &fieldIndex) || fieldIndex < 0) {
   2383    ReportUsageErrorASCII(cx, callee,
   2384                          "Second argument must be a non-negative integer");
   2385    return false;
   2386  }
   2387 
   2388  Rooted<WasmGcObject*> gcObject(cx, &args[0].toObject().as<WasmGcObject>());
   2389  Rooted<Value> gcValue(cx);
   2390  if (!WasmGcObject::loadValue(cx, gcObject, jsid::Int(int32_t(fieldIndex)),
   2391                               &gcValue)) {
   2392    return false;
   2393  }
   2394 
   2395  args.rval().set(gcValue);
   2396  return true;
   2397 }
   2398 
   2399 static bool WasmGcArrayLength(JSContext* cx, unsigned argc, Value* vp) {
   2400  CallArgs args = CallArgsFromVp(argc, vp);
   2401  RootedObject callee(cx, &args.callee());
   2402 
   2403  if (!args.requireAtLeast(cx, "wasmGcArrayLength", 1)) {
   2404    return false;
   2405  }
   2406 
   2407  if (!args[0].isObject() || !args[0].toObject().is<WasmArrayObject>()) {
   2408    ReportUsageErrorASCII(cx, callee,
   2409                          "First argument must be a WebAssembly GC array");
   2410    return false;
   2411  }
   2412 
   2413  WasmArrayObject& arr = args[0].toObject().as<WasmArrayObject>();
   2414  args.rval().setInt32(int32_t(arr.numElements_));
   2415  return true;
   2416 }
   2417 
   2418 static bool LargeArrayBufferSupported(JSContext* cx, unsigned argc, Value* vp) {
   2419  CallArgs args = CallArgsFromVp(argc, vp);
   2420  args.rval().setBoolean(ArrayBufferObject::ByteLengthLimit >
   2421                         ArrayBufferObject::ByteLengthLimitForSmallBuffer);
   2422  return true;
   2423 }
   2424 
   2425 static bool IsLazyFunction(JSContext* cx, unsigned argc, Value* vp) {
   2426  CallArgs args = CallArgsFromVp(argc, vp);
   2427  if (args.length() != 1) {
   2428    JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
   2429    return false;
   2430  }
   2431  if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
   2432    JS_ReportErrorASCII(cx, "The first argument should be a function.");
   2433    return false;
   2434  }
   2435  JSFunction* fun = &args[0].toObject().as<JSFunction>();
   2436  args.rval().setBoolean(fun->isInterpreted() && !fun->hasBytecode());
   2437  return true;
   2438 }
   2439 
   2440 static bool IsRelazifiableFunction(JSContext* cx, unsigned argc, Value* vp) {
   2441  CallArgs args = CallArgsFromVp(argc, vp);
   2442  if (args.length() != 1) {
   2443    JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
   2444    return false;
   2445  }
   2446  if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
   2447    JS_ReportErrorASCII(cx, "The first argument should be a function.");
   2448    return false;
   2449  }
   2450 
   2451  JSFunction* fun = &args[0].toObject().as<JSFunction>();
   2452  args.rval().setBoolean(fun->hasBytecode() &&
   2453                         fun->nonLazyScript()->allowRelazify());
   2454  return true;
   2455 }
   2456 
   2457 static bool IsCollectingDelazifications(JSContext* cx, unsigned argc,
   2458                                        Value* vp) {
   2459  CallArgs args = CallArgsFromVp(argc, vp);
   2460  if (args.length() != 1) {
   2461    JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
   2462    return false;
   2463  }
   2464 
   2465  if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
   2466    JS_ReportErrorASCII(cx, "The first argument should be a function.");
   2467    return false;
   2468  }
   2469 
   2470  if (fuzzingSafe) {
   2471    // When running code concurrently to fill-up the stencil cache, the content
   2472    // is not garanteed to be present.
   2473    args.rval().setBoolean(false);
   2474    return true;
   2475  }
   2476 
   2477  JSFunction* fun = &args[0].toObject().as<JSFunction>();
   2478  BaseScript* script = fun->baseScript();
   2479  args.rval().setBoolean(
   2480      bool(script->sourceObject()->isCollectingDelazifications()));
   2481  return true;
   2482 }
   2483 
   2484 static bool IsDelazificationsPopulated(JSContext* cx, unsigned argc,
   2485                                       Value* vp) {
   2486  CallArgs args = CallArgsFromVp(argc, vp);
   2487  if (args.length() != 1) {
   2488    JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
   2489    return false;
   2490  }
   2491 
   2492  if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
   2493    JS_ReportErrorASCII(cx, "The first argument should be a function.");
   2494    return false;
   2495  }
   2496 
   2497  if (fuzzingSafe) {
   2498    // When running code concurrently to fill-up the stencil cache, the content
   2499    // is not garanteed to be present.
   2500    args.rval().setBoolean(false);
   2501    return true;
   2502  }
   2503 
   2504  JSFunction* fun = &args[0].toObject().as<JSFunction>();
   2505  BaseScript* script = fun->baseScript();
   2506  RefPtr<frontend::InitialStencilAndDelazifications> stencils =
   2507      script->sourceObject()->maybeGetStencils();
   2508  if (!stencils) {
   2509    args.rval().setBoolean(false);
   2510    return true;
   2511  }
   2512 
   2513  const frontend::CompilationStencil* stencil =
   2514      stencils->getDelazificationFor(script->extent());
   2515  args.rval().setBoolean(bool(stencil));
   2516  return true;
   2517 }
   2518 
   2519 static bool WaitForDelazificationOf(JSContext* cx, unsigned argc, Value* vp) {
   2520  CallArgs args = CallArgsFromVp(argc, vp);
   2521  if (args.length() != 1) {
   2522    JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
   2523    return false;
   2524  }
   2525 
   2526  if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
   2527    JS_ReportErrorASCII(cx, "The first argument should be a function.");
   2528    return false;
   2529  }
   2530  args.rval().setUndefined();
   2531 
   2532  JSFunction* fun = &args[0].toObject().as<JSFunction>();
   2533  BaseScript* script = fun->baseScript();
   2534 
   2535  if (!script->sourceObject()->isSharingDelazifications()) {
   2536    return true;
   2537  }
   2538 
   2539  RefPtr<frontend::InitialStencilAndDelazifications> stencils =
   2540      script->sourceObject()->maybeGetStencils();
   2541 
   2542  AutoLockHelperThreadState lock;
   2543  if (!HelperThreadState().isInitialized(lock)) {
   2544    return true;
   2545  }
   2546 
   2547  while (true) {
   2548    const frontend::CompilationStencil* stencil =
   2549        stencils->getDelazificationFor(script->extent());
   2550    if (stencil) {
   2551      break;
   2552    }
   2553 
   2554    HelperThreadState().wait(lock);
   2555  }
   2556  return true;
   2557 }
   2558 
   2559 static bool HasSameBytecodeData(JSContext* cx, unsigned argc, Value* vp) {
   2560  CallArgs args = CallArgsFromVp(argc, vp);
   2561  if (args.length() != 2) {
   2562    JS_ReportErrorASCII(cx, "The function takes exactly two argument.");
   2563    return false;
   2564  }
   2565 
   2566  auto GetSharedData = [](JSContext* cx,
   2567                          HandleValue v) -> SharedImmutableScriptData* {
   2568    if (!v.isObject()) {
   2569      JS_ReportErrorASCII(cx, "The arguments must be interpreted functions.");
   2570      return nullptr;
   2571    }
   2572 
   2573    RootedObject obj(cx, CheckedUnwrapDynamic(&v.toObject(), cx));
   2574    if (!obj) {
   2575      return nullptr;
   2576    }
   2577 
   2578    if (!obj->is<JSFunction>() || !obj->as<JSFunction>().isInterpreted()) {
   2579      JS_ReportErrorASCII(cx, "The arguments must be interpreted functions.");
   2580      return nullptr;
   2581    }
   2582 
   2583    AutoRealm ar(cx, obj);
   2584    RootedFunction fun(cx, &obj->as<JSFunction>());
   2585    RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
   2586    if (!script) {
   2587      return nullptr;
   2588    }
   2589 
   2590    MOZ_ASSERT(script->sharedData());
   2591    return script->sharedData();
   2592  };
   2593 
   2594  // NOTE: We use RefPtr below to keep the data alive across possible GC since
   2595  //       the functions may be in different Zones.
   2596 
   2597  RefPtr<SharedImmutableScriptData> sharedData1 = GetSharedData(cx, args[0]);
   2598  if (!sharedData1) {
   2599    return false;
   2600  }
   2601 
   2602  RefPtr<SharedImmutableScriptData> sharedData2 = GetSharedData(cx, args[1]);
   2603  if (!sharedData2) {
   2604    return false;
   2605  }
   2606 
   2607  args.rval().setBoolean(sharedData1 == sharedData2);
   2608  return true;
   2609 }
   2610 
   2611 static bool InternalConst(JSContext* cx, unsigned argc, Value* vp) {
   2612  CallArgs args = CallArgsFromVp(argc, vp);
   2613  if (args.length() == 0) {
   2614    JS_ReportErrorASCII(cx, "the function takes exactly one argument");
   2615    return false;
   2616  }
   2617 
   2618  JSString* str = ToString(cx, args[0]);
   2619  if (!str) {
   2620    return false;
   2621  }
   2622  JSLinearString* linear = JS_EnsureLinearString(cx, str);
   2623  if (!linear) {
   2624    return false;
   2625  }
   2626 
   2627  if (JS_LinearStringEqualsLiteral(linear, "MARK_STACK_BASE_CAPACITY")) {
   2628    args.rval().setNumber(uint32_t(js::MARK_STACK_BASE_CAPACITY));
   2629  } else {
   2630    JS_ReportErrorASCII(cx, "unknown const name");
   2631    return false;
   2632  }
   2633  return true;
   2634 }
   2635 
   2636 static bool GCPreserveCode(JSContext* cx, unsigned argc, Value* vp) {
   2637  CallArgs args = CallArgsFromVp(argc, vp);
   2638 
   2639  if (args.length() != 0) {
   2640    RootedObject callee(cx, &args.callee());
   2641    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   2642    return false;
   2643  }
   2644 
   2645  cx->runtime()->gc.setAlwaysPreserveCode();
   2646 
   2647  args.rval().setUndefined();
   2648  return true;
   2649 }
   2650 
   2651 #ifdef JS_GC_ZEAL
   2652 
   2653 static bool ParseGCZealMode(JSContext* cx, const CallArgs& args,
   2654                            js::gc::GCRuntime::ZealSettings* zeal) {
   2655  JSString* modestr = ToString(cx, args.get(0));
   2656  if (!modestr) {
   2657    return false;
   2658  }
   2659 
   2660  UniqueChars mode = EncodeLatin1(cx, modestr);
   2661  if (!mode) {
   2662    return false;
   2663  }
   2664  bool invalid;
   2665  if (!cx->runtime()->gc.parseZeal(mode.get(), strlen(mode.get()), zeal,
   2666                                   &invalid)) {
   2667    js::ReportOutOfMemory(cx);
   2668    return false;
   2669  }
   2670  if (invalid) {
   2671    JS_ReportErrorASCII(cx, "invalid gczeal argument");
   2672    return false;
   2673  }
   2674 
   2675  return true;
   2676 }
   2677 
   2678 static bool GCZeal(JSContext* cx, unsigned argc, Value* vp) {
   2679  CallArgs args = CallArgsFromVp(argc, vp);
   2680 
   2681  if (args.length() > 2) {
   2682    RootedObject callee(cx, &args.callee());
   2683    ReportUsageErrorASCII(cx, callee, "Too many arguments");
   2684    return false;
   2685  }
   2686 
   2687  if (args.length() == 0) {
   2688    uint32_t zealBits, unused1, unused2;
   2689    cx->runtime()->gc.getZealBits(&zealBits, &unused1, &unused2);
   2690    args.rval().setNumber(zealBits);
   2691    return true;
   2692  }
   2693 
   2694  js::gc::GCRuntime::ZealSettings zeal;
   2695  if (!ParseGCZealMode(cx, args, &zeal)) {
   2696    return false;
   2697  }
   2698 
   2699  Maybe<uint32_t> forceFrequency;
   2700  if (args.length() >= 2) {
   2701    uint32_t frequency;
   2702    if (!ToUint32(cx, args.get(1), &frequency)) {
   2703      return false;
   2704    }
   2705    forceFrequency.emplace(frequency);
   2706  }
   2707 
   2708  for (auto [mode, frequency] : zeal) {
   2709    JS::SetGCZeal(cx, mode, forceFrequency.valueOr(frequency));
   2710  }
   2711 
   2712  args.rval().setUndefined();
   2713  return true;
   2714 }
   2715 
   2716 static bool UnsetGCZeal(JSContext* cx, unsigned argc, Value* vp) {
   2717  CallArgs args = CallArgsFromVp(argc, vp);
   2718 
   2719  if (args.length() > 1) {
   2720    RootedObject callee(cx, &args.callee());
   2721    ReportUsageErrorASCII(cx, callee, "Too many arguments");
   2722    return false;
   2723  }
   2724 
   2725  js::gc::GCRuntime::ZealSettings zeal;
   2726  if (!ParseGCZealMode(cx, args, &zeal)) {
   2727    return false;
   2728  }
   2729 
   2730  for (auto [mode, _frequency] : zeal) {
   2731    JS::UnsetGCZeal(cx, mode);
   2732  }
   2733 
   2734  args.rval().setUndefined();
   2735  return true;
   2736 }
   2737 
   2738 static bool ScheduleGC(JSContext* cx, unsigned argc, Value* vp) {
   2739  CallArgs args = CallArgsFromVp(argc, vp);
   2740 
   2741  if (args.length() > 1) {
   2742    RootedObject callee(cx, &args.callee());
   2743    ReportUsageErrorASCII(cx, callee, "Too many arguments");
   2744    return false;
   2745  }
   2746 
   2747  if (args.length() == 0) {
   2748    /* Fetch next zeal trigger only. */
   2749  } else if (args[0].isNumber()) {
   2750    /* Schedule a GC to happen after |arg| allocations. */
   2751    JS::ScheduleGC(cx, std::max(int(args[0].toNumber()), 0));
   2752  } else {
   2753    RootedObject callee(cx, &args.callee());
   2754    ReportUsageErrorASCII(cx, callee, "Bad argument - expecting number");
   2755    return false;
   2756  }
   2757 
   2758  uint32_t zealBits;
   2759  uint32_t freq;
   2760  uint32_t next;
   2761  JS::GetGCZealBits(cx, &zealBits, &freq, &next);
   2762  args.rval().setInt32(next);
   2763  return true;
   2764 }
   2765 
   2766 static bool SelectForGC(JSContext* cx, unsigned argc, Value* vp) {
   2767  CallArgs args = CallArgsFromVp(argc, vp);
   2768 
   2769  /*
   2770   * The selectedForMarking set is intended to be manually marked at slice
   2771   * start to detect missing pre-barriers. It is invalid for nursery things
   2772   * to be in the set, so evict the nursery before adding items.
   2773   */
   2774  cx->runtime()->gc.evictNursery();
   2775 
   2776  for (unsigned i = 0; i < args.length(); i++) {
   2777    if (args[i].isObject()) {
   2778      if (!cx->runtime()->gc.selectForMarking(&args[i].toObject())) {
   2779        ReportOutOfMemory(cx);
   2780        return false;
   2781      }
   2782    }
   2783  }
   2784 
   2785  args.rval().setUndefined();
   2786  return true;
   2787 }
   2788 
   2789 static bool VerifyPreBarriers(JSContext* cx, unsigned argc, Value* vp) {
   2790  CallArgs args = CallArgsFromVp(argc, vp);
   2791 
   2792  if (args.length() > 0) {
   2793    RootedObject callee(cx, &args.callee());
   2794    ReportUsageErrorASCII(cx, callee, "Too many arguments");
   2795    return false;
   2796  }
   2797 
   2798  gc::VerifyBarriers(cx->runtime(), gc::PreBarrierVerifier);
   2799  args.rval().setUndefined();
   2800  return true;
   2801 }
   2802 
   2803 static bool VerifyPostBarriers(JSContext* cx, unsigned argc, Value* vp) {
   2804  CallArgs args = CallArgsFromVp(argc, vp);
   2805 
   2806  if (args.length()) {
   2807    RootedObject callee(cx, &args.callee());
   2808    ReportUsageErrorASCII(cx, callee, "Too many arguments");
   2809    return false;
   2810  }
   2811 
   2812  gc::VerifyBarriers(cx->runtime(), gc::PostBarrierVerifier);
   2813  args.rval().setUndefined();
   2814  return true;
   2815 }
   2816 
   2817 static bool CurrentGC(JSContext* cx, unsigned argc, Value* vp) {
   2818  CallArgs args = CallArgsFromVp(argc, vp);
   2819 
   2820  if (args.length() != 0) {
   2821    RootedObject callee(cx, &args.callee());
   2822    ReportUsageErrorASCII(cx, callee, "Too many arguments");
   2823    return false;
   2824  }
   2825 
   2826  RootedObject result(cx, JS_NewPlainObject(cx));
   2827  if (!result) {
   2828    return false;
   2829  }
   2830 
   2831  js::gc::GCRuntime& gc = cx->runtime()->gc;
   2832  const char* state = StateName(gc.state());
   2833 
   2834  RootedString str(cx, JS_NewStringCopyZ(cx, state));
   2835  if (!str) {
   2836    return false;
   2837  }
   2838  RootedValue val(cx, StringValue(str));
   2839  if (!JS_DefineProperty(cx, result, "incrementalState", val,
   2840                         JSPROP_ENUMERATE)) {
   2841    return false;
   2842  }
   2843 
   2844  if (gc.state() == js::gc::State::Sweep) {
   2845    val = Int32Value(gc.getCurrentSweepGroupIndex());
   2846    if (!JS_DefineProperty(cx, result, "sweepGroup", val, JSPROP_ENUMERATE)) {
   2847      return false;
   2848    }
   2849  }
   2850 
   2851  val = BooleanValue(gc.isIncrementalGCInProgress() && gc.isShrinkingGC());
   2852  if (!JS_DefineProperty(cx, result, "isShrinking", val, JSPROP_ENUMERATE)) {
   2853    return false;
   2854  }
   2855 
   2856  val = Int32Value(gc.gcNumber());
   2857  if (!JS_DefineProperty(cx, result, "number", val, JSPROP_ENUMERATE)) {
   2858    return false;
   2859  }
   2860 
   2861  val = Int32Value(gc.minorGCCount());
   2862  if (!JS_DefineProperty(cx, result, "minorCount", val, JSPROP_ENUMERATE)) {
   2863    return false;
   2864  }
   2865 
   2866  val = Int32Value(gc.majorGCCount());
   2867  if (!JS_DefineProperty(cx, result, "majorCount", val, JSPROP_ENUMERATE)) {
   2868    return false;
   2869  }
   2870 
   2871  val = BooleanValue(gc.isFullGc());
   2872  if (!JS_DefineProperty(cx, result, "isFull", val, JSPROP_ENUMERATE)) {
   2873    return false;
   2874  }
   2875 
   2876  val = BooleanValue(gc.isCompactingGc());
   2877  if (!JS_DefineProperty(cx, result, "isCompacting", val, JSPROP_ENUMERATE)) {
   2878    return false;
   2879  }
   2880 
   2881 #  ifdef DEBUG
   2882  val = Int32Value(gc.testMarkQueuePos());
   2883  if (!JS_DefineProperty(cx, result, "queuePos", val, JSPROP_ENUMERATE)) {
   2884    return false;
   2885  }
   2886 #  endif
   2887 
   2888  val = BooleanValue(gc.finishMarkingDuringSweeping);
   2889  if (!JS_DefineProperty(cx, result, "finishMarkingDuringSweeping", val,
   2890                         JSPROP_ENUMERATE)) {
   2891    return false;
   2892  }
   2893 
   2894  args.rval().setObject(*result);
   2895  return true;
   2896 }
   2897 
   2898 static bool DeterministicGC(JSContext* cx, unsigned argc, Value* vp) {
   2899  CallArgs args = CallArgsFromVp(argc, vp);
   2900 
   2901  if (args.length() != 1) {
   2902    RootedObject callee(cx, &args.callee());
   2903    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   2904    return false;
   2905  }
   2906 
   2907  cx->runtime()->gc.setDeterministic(ToBoolean(args[0]));
   2908  args.rval().setUndefined();
   2909  return true;
   2910 }
   2911 
   2912 static bool DumpGCArenaInfo(JSContext* cx, unsigned argc, Value* vp) {
   2913  CallArgs args = CallArgsFromVp(argc, vp);
   2914  js::gc::DumpArenaInfo();
   2915  args.rval().setUndefined();
   2916  return true;
   2917 }
   2918 
   2919 static bool SetMarkStackLimit(JSContext* cx, unsigned argc, Value* vp) {
   2920  CallArgs args = CallArgsFromVp(argc, vp);
   2921  if (args.length() != 1) {
   2922    RootedObject callee(cx, &args.callee());
   2923    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   2924    return false;
   2925  }
   2926 
   2927  int32_t value;
   2928  if (!ToInt32(cx, args[0], &value) || value <= 0) {
   2929    JS_ReportErrorASCII(cx, "Bad argument to SetMarkStackLimit");
   2930    return false;
   2931  }
   2932 
   2933  if (JS::IsIncrementalGCInProgress(cx)) {
   2934    JS_ReportErrorASCII(
   2935        cx, "Attempt to set markStackLimit while a GC is in progress");
   2936    return false;
   2937  }
   2938 
   2939  JSRuntime* runtime = cx->runtime();
   2940  AutoLockGC lock(runtime);
   2941  runtime->gc.setMarkStackLimit(value, lock);
   2942  args.rval().setUndefined();
   2943  return true;
   2944 }
   2945 
   2946 #endif /* JS_GC_ZEAL */
   2947 
   2948 static bool SetMallocMaxDirtyPageModifier(JSContext* cx, unsigned argc,
   2949                                          Value* vp) {
   2950  CallArgs args = CallArgsFromVp(argc, vp);
   2951  if (args.length() != 1) {
   2952    RootedObject callee(cx, &args.callee());
   2953    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   2954    return false;
   2955  }
   2956 
   2957  constexpr int32_t MinSupportedValue = -5;
   2958  constexpr int32_t MaxSupportedValue = 16;
   2959 
   2960  int32_t value;
   2961  if (!ToInt32(cx, args[0], &value)) {
   2962    return false;
   2963  }
   2964  if (value < MinSupportedValue || value > MaxSupportedValue) {
   2965    JS_ReportErrorASCII(cx, "Bad argument to setMallocMaxDirtyPageModifier");
   2966    return false;
   2967  }
   2968 
   2969 #ifdef MOZ_MEMORY
   2970  moz_set_max_dirty_page_modifier(value);
   2971 #endif
   2972 
   2973  args.rval().setUndefined();
   2974  return true;
   2975 }
   2976 
   2977 static bool GCState(JSContext* cx, unsigned argc, Value* vp) {
   2978  CallArgs args = CallArgsFromVp(argc, vp);
   2979 
   2980  if (args.length() > 1) {
   2981    RootedObject callee(cx, &args.callee());
   2982    ReportUsageErrorASCII(cx, callee, "Too many arguments");
   2983    return false;
   2984  }
   2985 
   2986  const char* state;
   2987 
   2988  if (args.length() == 1) {
   2989    if (!args[0].isObject()) {
   2990      RootedObject callee(cx, &args.callee());
   2991      ReportUsageErrorASCII(cx, callee, "Expected object");
   2992      return false;
   2993    }
   2994 
   2995    JSObject* obj = UncheckedUnwrap(&args[0].toObject());
   2996    state = gc::StateName(obj->zone()->gcState());
   2997  } else {
   2998    state = gc::StateName(cx->runtime()->gc.state());
   2999  }
   3000 
   3001  return ReturnStringCopy(cx, args, state);
   3002 }
   3003 
   3004 static bool ScheduleZoneForGC(JSContext* cx, unsigned argc, Value* vp) {
   3005  CallArgs args = CallArgsFromVp(argc, vp);
   3006 
   3007  if (args.length() != 1) {
   3008    RootedObject callee(cx, &args.callee());
   3009    ReportUsageErrorASCII(cx, callee, "Expecting a single argument");
   3010    return false;
   3011  }
   3012 
   3013  if (args[0].isObject()) {
   3014    // Ensure that |zone| is collected during the next GC.
   3015    Zone* zone = UncheckedUnwrap(&args[0].toObject())->zone();
   3016    PrepareZoneForGC(cx, zone);
   3017  } else if (args[0].isString()) {
   3018    // This allows us to schedule the atoms zone for GC.
   3019    Zone* zone = args[0].toString()->zoneFromAnyThread();
   3020    if (!CurrentThreadCanAccessZone(zone)) {
   3021      RootedObject callee(cx, &args.callee());
   3022      ReportUsageErrorASCII(cx, callee, "Specified zone not accessible for GC");
   3023      return false;
   3024    }
   3025    PrepareZoneForGC(cx, zone);
   3026  } else {
   3027    RootedObject callee(cx, &args.callee());
   3028    ReportUsageErrorASCII(cx, callee,
   3029                          "Bad argument - expecting object or string");
   3030    return false;
   3031  }
   3032 
   3033  args.rval().setUndefined();
   3034  return true;
   3035 }
   3036 
   3037 static bool StartGC(JSContext* cx, unsigned argc, Value* vp) {
   3038  CallArgs args = CallArgsFromVp(argc, vp);
   3039 
   3040  if (args.length() > 2) {
   3041    RootedObject callee(cx, &args.callee());
   3042    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   3043    return false;
   3044  }
   3045 
   3046  auto budget = SliceBudget::unlimited();
   3047  if (args.length() >= 1) {
   3048    uint32_t work = 0;
   3049    if (!ToUint32(cx, args[0], &work)) {
   3050      return false;
   3051    }
   3052    budget = SliceBudget(WorkBudget(work));
   3053  }
   3054 
   3055  bool shrinking = false;
   3056  if (args.length() >= 2) {
   3057    Value arg = args[1];
   3058    if (arg.isString()) {
   3059      if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking",
   3060                                  &shrinking)) {
   3061        return false;
   3062      }
   3063    }
   3064  }
   3065 
   3066  JSRuntime* rt = cx->runtime();
   3067  if (rt->gc.isIncrementalGCInProgress()) {
   3068    RootedObject callee(cx, &args.callee());
   3069    JS_ReportErrorASCII(cx, "Incremental GC already in progress");
   3070    return false;
   3071  }
   3072 
   3073  JS::GCOptions options =
   3074      shrinking ? JS::GCOptions::Shrink : JS::GCOptions::Normal;
   3075  rt->gc.startDebugGC(options, budget);
   3076 
   3077  args.rval().setUndefined();
   3078  return true;
   3079 }
   3080 
   3081 static bool FinishGC(JSContext* cx, unsigned argc, Value* vp) {
   3082  CallArgs args = CallArgsFromVp(argc, vp);
   3083 
   3084  if (args.length() > 0) {
   3085    RootedObject callee(cx, &args.callee());
   3086    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   3087    return false;
   3088  }
   3089 
   3090  JSRuntime* rt = cx->runtime();
   3091  if (rt->gc.isIncrementalGCInProgress()) {
   3092    rt->gc.finishGC(JS::GCReason::DEBUG_GC);
   3093  }
   3094 
   3095  args.rval().setUndefined();
   3096  return true;
   3097 }
   3098 
   3099 static bool GCSlice(JSContext* cx, unsigned argc, Value* vp) {
   3100  CallArgs args = CallArgsFromVp(argc, vp);
   3101 
   3102  if (args.length() > 2) {
   3103    RootedObject callee(cx, &args.callee());
   3104    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   3105    return false;
   3106  }
   3107 
   3108  auto budget = SliceBudget::unlimited();
   3109  if (args.length() >= 1) {
   3110    uint32_t work = 0;
   3111    if (!ToUint32(cx, args[0], &work)) {
   3112      return false;
   3113    }
   3114    budget = SliceBudget(WorkBudget(work));
   3115  }
   3116 
   3117  bool dontStart = false;
   3118  if (args.get(1).isObject()) {
   3119    RootedObject options(cx, &args[1].toObject());
   3120    RootedValue v(cx);
   3121    if (!JS_GetProperty(cx, options, "dontStart", &v)) {
   3122      return false;
   3123    }
   3124    dontStart = ToBoolean(v);
   3125  }
   3126 
   3127  JSRuntime* rt = cx->runtime();
   3128  if (rt->gc.isIncrementalGCInProgress()) {
   3129    rt->gc.debugGCSlice(budget);
   3130  } else if (!dontStart) {
   3131    rt->gc.startDebugGC(JS::GCOptions::Normal, budget);
   3132  }
   3133 
   3134  args.rval().setUndefined();
   3135  return true;
   3136 }
   3137 
   3138 static bool AbortGC(JSContext* cx, unsigned argc, Value* vp) {
   3139  CallArgs args = CallArgsFromVp(argc, vp);
   3140 
   3141  if (args.length() != 0) {
   3142    RootedObject callee(cx, &args.callee());
   3143    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   3144    return false;
   3145  }
   3146 
   3147  JS::AbortIncrementalGC(cx);
   3148  args.rval().setUndefined();
   3149  return true;
   3150 }
   3151 
   3152 static bool FullCompartmentChecks(JSContext* cx, unsigned argc, Value* vp) {
   3153  CallArgs args = CallArgsFromVp(argc, vp);
   3154 
   3155  if (args.length() != 1) {
   3156    RootedObject callee(cx, &args.callee());
   3157    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   3158    return false;
   3159  }
   3160 
   3161  cx->runtime()->gc.setFullCompartmentChecks(ToBoolean(args[0]));
   3162  args.rval().setUndefined();
   3163  return true;
   3164 }
   3165 
   3166 static bool IsAtomMarked(JSContext* cx, unsigned argc, Value* vp) {
   3167  CallArgs args = CallArgsFromVp(argc, vp);
   3168 
   3169  RootedObject callee(cx, &args.callee());
   3170  if (args.length() != 2) {
   3171    ReportUsageErrorASCII(cx, callee, "Expected two arguments");
   3172    return false;
   3173  }
   3174 
   3175  if (!args[0].isObject()) {
   3176    ReportUsageErrorASCII(cx, callee,
   3177                          "Expected an object as the first argument");
   3178    return false;
   3179  }
   3180  Zone* zone = UncheckedUnwrap(&args[0].toObject())->zone();
   3181 
   3182  Maybe<bool> result;
   3183  gc::GCRuntime* gc = &cx->runtime()->gc;
   3184  if (args[1].isSymbol()) {
   3185    result = Some(gc->atomMarking.atomIsMarked(zone, args[1].toSymbol()));
   3186  } else if (args[1].isString()) {
   3187    JSString* str = args[1].toString();
   3188    if (str->isAtom()) {
   3189      result = Some(gc->atomMarking.atomIsMarked(zone, &str->asAtom()));
   3190    }
   3191  }
   3192 
   3193  if (result.isNothing()) {
   3194    ReportUsageErrorASCII(cx, callee,
   3195                          "Expected an atom as the second argument");
   3196    return false;
   3197  }
   3198 
   3199  args.rval().setBoolean(result.value());
   3200  return true;
   3201 }
   3202 
   3203 static bool GetAtomMarkIndex(JSContext* cx, unsigned argc, Value* vp) {
   3204  CallArgs args = CallArgsFromVp(argc, vp);
   3205  RootedObject callee(cx, &args.callee());
   3206 
   3207  if (args.length() != 1 || !args[0].isGCThing() ||
   3208      !args[0].toGCThing()->zone()->isAtomsZone()) {
   3209    ReportUsageErrorASCII(cx, callee,
   3210                          "Expected an atom as the single argument");
   3211    return false;
   3212  }
   3213 
   3214  gc::TenuredCell* atom = &args[0].toGCThing()->asTenured();
   3215  if ((atom->is<JSString>() &&
   3216       atom->as<JSString>()->isPermanentAndMayBeShared()) ||
   3217      (atom->is<JS::Symbol>() &&
   3218       atom->as<JS::Symbol>()->isPermanentAndMayBeShared())) {
   3219    ReportUsageErrorASCII(
   3220        cx, callee, "Atom marking bitmap is not used for permanent atoms");
   3221    return false;
   3222  }
   3223 
   3224  if (atom->is<JSString>() && atom->as<JSString>()->asAtom().isPinned()) {
   3225    ReportUsageErrorASCII(cx, callee,
   3226                          "Atom marking bitmap is not used for pinned atoms");
   3227    return false;
   3228  }
   3229 
   3230  size_t index = gc::AtomMarkingRuntime::getAtomBit(atom);
   3231  MOZ_RELEASE_ASSERT(index <= INT32_MAX);
   3232  args.rval().setInt32(index);
   3233  return true;
   3234 }
   3235 
   3236 static bool GetAtomMarkColor(JSContext* cx, unsigned argc, Value* vp) {
   3237  CallArgs args = CallArgsFromVp(argc, vp);
   3238  RootedObject callee(cx, &args.callee());
   3239 
   3240  if (args.length() != 2) {
   3241    ReportUsageErrorASCII(cx, callee, "Expected two arguments");
   3242    return false;
   3243  }
   3244 
   3245  if (!args[0].isObject()) {
   3246    ReportUsageErrorASCII(cx, callee,
   3247                          "Expected an object as the first argument");
   3248    return false;
   3249  }
   3250  Zone* zone = UncheckedUnwrap(&args[0].toObject())->zone();
   3251 
   3252  if (!args[1].isInt32() || args[1].toInt32() < 0) {
   3253    ReportUsageErrorASCII(cx, callee,
   3254                          "Expected a positive integer as the second argument");
   3255    return false;
   3256  }
   3257  size_t index = args[1].toInt32();
   3258 
   3259  gc::GCRuntime* gc = &cx->runtime()->gc;
   3260  gc::CellColor color = gc->atomMarking.getAtomMarkColorForIndex(zone, index);
   3261  RootedString name(cx, JS_NewStringCopyZ(cx, gc::CellColorName(color)));
   3262  if (!name) {
   3263    return false;
   3264  }
   3265 
   3266  args.rval().setString(name);
   3267  return true;
   3268 }
   3269 
   3270 static WeakMapObject* MaybeWeakMapObject(const Value& value) {
   3271  if (!value.isObject()) {
   3272    return nullptr;
   3273  }
   3274 
   3275  JSObject* obj = UncheckedUnwrap(&value.toObject());
   3276  if (!obj->is<WeakMapObject>()) {
   3277    return nullptr;
   3278  }
   3279 
   3280  return &obj->as<WeakMapObject>();
   3281 }
   3282 
   3283 static bool NondeterministicGetWeakMapSize(JSContext* cx, unsigned argc,
   3284                                           Value* vp) {
   3285  CallArgs args = CallArgsFromVp(argc, vp);
   3286 
   3287  WeakMapObject* weakmap = MaybeWeakMapObject(args.get(0));
   3288  if (args.length() != 1 || !weakmap) {
   3289    RootedObject callee(cx, &args.callee());
   3290    ReportUsageErrorASCII(cx, callee, "Expected a single WeakMap argument ");
   3291    return false;
   3292  }
   3293 
   3294  args.rval().setNumber(weakmap->nondeterministicGetSize());
   3295  return true;
   3296 }
   3297 
   3298 static bool NondeterministicGetWeakMapKeys(JSContext* cx, unsigned argc,
   3299                                           Value* vp) {
   3300  CallArgs args = CallArgsFromVp(argc, vp);
   3301 
   3302  Rooted<WeakMapObject*> weakmap(cx, MaybeWeakMapObject(args.get(0)));
   3303  if (args.length() != 1 || !weakmap) {
   3304    RootedObject callee(cx, &args.callee());
   3305    ReportUsageErrorASCII(cx, callee, "Expected a single WeakMap argument ");
   3306    return false;
   3307  }
   3308 
   3309  RootedObject arr(cx);
   3310  if (!JS_NondeterministicGetWeakMapKeys(cx, weakmap, &arr)) {
   3311    return false;
   3312  }
   3313 
   3314  MOZ_ASSERT(arr);
   3315  args.rval().setObject(*arr);
   3316  return true;
   3317 }
   3318 
   3319 static bool GrayBitsValid(JSContext* cx, unsigned argc, Value* vp) {
   3320  CallArgs args = CallArgsFromVp(argc, vp);
   3321  args.rval().setBoolean(cx->runtime()->gc.areGrayBitsValid());
   3322  return true;
   3323 }
   3324 
   3325 static bool SetGrayBitsInvalid(JSContext* cx, unsigned argc, Value* vp) {
   3326  CallArgs args = CallArgsFromVp(argc, vp);
   3327  cx->runtime()->gc.setGrayBitsInvalid();
   3328  args.rval().setUndefined();
   3329  return true;
   3330 }
   3331 
   3332 class HasChildTracer final : public JS::CallbackTracer {
   3333  RootedValue child_;
   3334  bool found_;
   3335 
   3336  void onChild(JS::GCCellPtr thing, const char* name) override {
   3337    if (thing.asCell() == child_.toGCThing()) {
   3338      found_ = true;
   3339    }
   3340  }
   3341 
   3342 public:
   3343  HasChildTracer(JSContext* cx, HandleValue child)
   3344      : JS::CallbackTracer(cx, JS::TracerKind::Callback,
   3345                           JS::WeakMapTraceAction::TraceKeysAndValues),
   3346        child_(cx, child),
   3347        found_(false) {}
   3348 
   3349  bool found() const { return found_; }
   3350 };
   3351 
   3352 static bool HasChild(JSContext* cx, unsigned argc, Value* vp) {
   3353  CallArgs args = CallArgsFromVp(argc, vp);
   3354  RootedValue parent(cx, args.get(0));
   3355  RootedValue child(cx, args.get(1));
   3356 
   3357  if (!parent.isGCThing() || !child.isGCThing()) {
   3358    args.rval().setBoolean(false);
   3359    return true;
   3360  }
   3361 
   3362  HasChildTracer trc(cx, child);
   3363  TraceChildren(&trc, JS::GCCellPtr(parent.toGCThing(), parent.traceKind()));
   3364  args.rval().setBoolean(trc.found());
   3365  return true;
   3366 }
   3367 
   3368 static bool SetSavedStacksRNGState(JSContext* cx, unsigned argc, Value* vp) {
   3369  CallArgs args = CallArgsFromVp(argc, vp);
   3370  if (!args.requireAtLeast(cx, "setSavedStacksRNGState", 1)) {
   3371    return false;
   3372  }
   3373 
   3374  int32_t seed;
   3375  if (!ToInt32(cx, args[0], &seed)) {
   3376    return false;
   3377  }
   3378 
   3379  // Either one or the other of the seed arguments must be non-zero;
   3380  // make this true no matter what value 'seed' has.
   3381  cx->realm()->savedStacks().setRNGState(seed, (seed + 1) * 33);
   3382  return true;
   3383 }
   3384 
   3385 static bool GetSavedFrameCount(JSContext* cx, unsigned argc, Value* vp) {
   3386  CallArgs args = CallArgsFromVp(argc, vp);
   3387  args.rval().setNumber(cx->realm()->savedStacks().count());
   3388  return true;
   3389 }
   3390 
   3391 static bool ClearSavedFrames(JSContext* cx, unsigned argc, Value* vp) {
   3392  CallArgs args = CallArgsFromVp(argc, vp);
   3393 
   3394  js::SavedStacks& savedStacks = cx->realm()->savedStacks();
   3395  savedStacks.clear();
   3396 
   3397  for (ActivationIterator iter(cx); !iter.done(); ++iter) {
   3398    iter->clearLiveSavedFrameCache();
   3399  }
   3400 
   3401  args.rval().setUndefined();
   3402  return true;
   3403 }
   3404 
   3405 static bool SaveStack(JSContext* cx, unsigned argc, Value* vp) {
   3406  CallArgs args = CallArgsFromVp(argc, vp);
   3407 
   3408  JS::StackCapture capture((JS::AllFrames()));
   3409  if (args.length() >= 1) {
   3410    double maxDouble;
   3411    if (!ToNumber(cx, args[0], &maxDouble)) {
   3412      return false;
   3413    }
   3414    if (std::isnan(maxDouble) || maxDouble < 0 || maxDouble > UINT32_MAX) {
   3415      ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
   3416                       nullptr, "not a valid maximum frame count");
   3417      return false;
   3418    }
   3419    uint32_t max = uint32_t(maxDouble);
   3420    if (max > 0) {
   3421      capture = JS::StackCapture(JS::MaxFrames(max));
   3422    }
   3423  }
   3424 
   3425  RootedObject compartmentObject(cx);
   3426  if (args.length() >= 2) {
   3427    if (!args[1].isObject()) {
   3428      ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
   3429                       nullptr, "not an object");
   3430      return false;
   3431    }
   3432    compartmentObject = UncheckedUnwrap(&args[1].toObject());
   3433    if (!compartmentObject) {
   3434      return false;
   3435    }
   3436  }
   3437 
   3438  RootedObject stack(cx);
   3439  {
   3440    Maybe<AutoRealm> ar;
   3441    if (compartmentObject) {
   3442      ar.emplace(cx, compartmentObject);
   3443    }
   3444    if (!JS::CaptureCurrentStack(cx, &stack, std::move(capture))) {
   3445      return false;
   3446    }
   3447  }
   3448 
   3449  if (stack && !cx->compartment()->wrap(cx, &stack)) {
   3450    return false;
   3451  }
   3452 
   3453  args.rval().setObjectOrNull(stack);
   3454  return true;
   3455 }
   3456 
   3457 static bool CaptureFirstSubsumedFrame(JSContext* cx, unsigned argc,
   3458                                      JS::Value* vp) {
   3459  CallArgs args = CallArgsFromVp(argc, vp);
   3460  if (!args.requireAtLeast(cx, "captureFirstSubsumedFrame", 1)) {
   3461    return false;
   3462  }
   3463 
   3464  if (!args[0].isObject()) {
   3465    JS_ReportErrorASCII(cx, "The argument must be an object");
   3466    return false;
   3467  }
   3468 
   3469  RootedObject obj(cx, &args[0].toObject());
   3470  obj = CheckedUnwrapStatic(obj);
   3471  if (!obj) {
   3472    JS_ReportErrorASCII(cx, "Denied permission to object.");
   3473    return false;
   3474  }
   3475 
   3476  JS::StackCapture capture(
   3477      JS::FirstSubsumedFrame(cx, obj->nonCCWRealm()->principals()));
   3478  if (args.length() > 1) {
   3479    capture.as<JS::FirstSubsumedFrame>().ignoreSelfHosted =
   3480        JS::ToBoolean(args[1]);
   3481  }
   3482 
   3483  JS::RootedObject capturedStack(cx);
   3484  if (!JS::CaptureCurrentStack(cx, &capturedStack, std::move(capture))) {
   3485    return false;
   3486  }
   3487 
   3488  args.rval().setObjectOrNull(capturedStack);
   3489  return true;
   3490 }
   3491 
   3492 static bool CallFunctionFromNativeFrame(JSContext* cx, unsigned argc,
   3493                                        Value* vp) {
   3494  CallArgs args = CallArgsFromVp(argc, vp);
   3495 
   3496  if (args.length() != 1) {
   3497    JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
   3498    return false;
   3499  }
   3500  if (!args[0].isObject() || !IsCallable(args[0])) {
   3501    JS_ReportErrorASCII(cx, "The first argument should be a function.");
   3502    return false;
   3503  }
   3504 
   3505  RootedObject function(cx, &args[0].toObject());
   3506  return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(),
   3507              args.rval());
   3508 }
   3509 
   3510 static bool CallFunctionWithAsyncStack(JSContext* cx, unsigned argc,
   3511                                       Value* vp) {
   3512  CallArgs args = CallArgsFromVp(argc, vp);
   3513 
   3514  if (args.length() != 3) {
   3515    JS_ReportErrorASCII(cx, "The function takes exactly three arguments.");
   3516    return false;
   3517  }
   3518  if (!args[0].isObject() || !IsCallable(args[0])) {
   3519    JS_ReportErrorASCII(cx, "The first argument should be a function.");
   3520    return false;
   3521  }
   3522  if (!args[1].isObject() || !args[1].toObject().is<SavedFrame>()) {
   3523    JS_ReportErrorASCII(cx, "The second argument should be a SavedFrame.");
   3524    return false;
   3525  }
   3526  if (!args[2].isString() || args[2].toString()->empty()) {
   3527    JS_ReportErrorASCII(cx, "The third argument should be a non-empty string.");
   3528    return false;
   3529  }
   3530 
   3531  RootedObject function(cx, &args[0].toObject());
   3532  RootedObject stack(cx, &args[1].toObject());
   3533  RootedString asyncCause(cx, args[2].toString());
   3534  UniqueChars utf8Cause = JS_EncodeStringToUTF8(cx, asyncCause);
   3535  if (!utf8Cause) {
   3536    MOZ_ASSERT(cx->isExceptionPending());
   3537    return false;
   3538  }
   3539 
   3540  JS::AutoSetAsyncStackForNewCalls sas(
   3541      cx, stack, utf8Cause.get(),
   3542      JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
   3543  return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(),
   3544              args.rval());
   3545 }
   3546 
   3547 static bool EnableTrackAllocations(JSContext* cx, unsigned argc, Value* vp) {
   3548  SetAllocationMetadataBuilder(cx, &SavedStacks::metadataBuilder);
   3549  return true;
   3550 }
   3551 
   3552 static bool DisableTrackAllocations(JSContext* cx, unsigned argc, Value* vp) {
   3553  SetAllocationMetadataBuilder(cx, nullptr);
   3554  return true;
   3555 }
   3556 
   3557 static bool SetTestFilenameValidationCallback(JSContext* cx, unsigned argc,
   3558                                              Value* vp) {
   3559  CallArgs args = CallArgsFromVp(argc, vp);
   3560 
   3561  // Accept all filenames that start with "safe". In system code also accept
   3562  // filenames starting with "system".
   3563  auto testCb = [](JSContext* cx, const char* filename) -> bool {
   3564    if (strstr(filename, "safe") == filename) {
   3565      return true;
   3566    }
   3567    if (cx->realm()->isSystem() && strstr(filename, "system") == filename) {
   3568      return true;
   3569    }
   3570 
   3571    return false;
   3572  };
   3573  JS::SetFilenameValidationCallback(testCb);
   3574 
   3575  args.rval().setUndefined();
   3576  return true;
   3577 }
   3578 
   3579 static JSAtom* GetPropertiesAddedName(JSContext* cx) {
   3580  const char* propName = "_propertiesAdded";
   3581  return Atomize(cx, propName, strlen(propName));
   3582 }
   3583 
   3584 static bool NewObjectWithAddPropertyHook(JSContext* cx, unsigned argc,
   3585                                         Value* vp) {
   3586  CallArgs args = CallArgsFromVp(argc, vp);
   3587 
   3588  auto addPropHook = [](JSContext* cx, HandleObject obj, HandleId id,
   3589                        HandleValue v) -> bool {
   3590    Rooted<JSAtom*> propName(cx, GetPropertiesAddedName(cx));
   3591    if (!propName) {
   3592      return false;
   3593    }
   3594    // Don't do anything if we're adding the _propertiesAdded property.
   3595    RootedId propId(cx, AtomToId(propName));
   3596    if (id == propId) {
   3597      return true;
   3598    }
   3599    // Increment _propertiesAdded.
   3600    RootedValue val(cx);
   3601    if (!JS_GetPropertyById(cx, obj, propId, &val)) {
   3602      return false;
   3603    }
   3604    if (!val.isInt32() || val.toInt32() == INT32_MAX) {
   3605      return true;
   3606    }
   3607    val.setInt32(val.toInt32() + 1);
   3608    return JS_DefinePropertyById(cx, obj, propId, val, 0);
   3609  };
   3610 
   3611  static const JSClassOps classOps = {
   3612      addPropHook,  // addProperty
   3613      nullptr,      // delProperty
   3614      nullptr,      // enumerate
   3615      nullptr,      // newEnumerate
   3616      nullptr,      // resolve
   3617      nullptr,      // mayResolve
   3618      nullptr,      // finalize
   3619      nullptr,      // call
   3620      nullptr,      // construct
   3621      nullptr,      // trace
   3622  };
   3623  static const JSClass cls = {
   3624      "ObjectWithAddPropHook",
   3625      0,
   3626      &classOps,
   3627  };
   3628 
   3629  RootedObject obj(cx, JS_NewObject(cx, &cls));
   3630  if (!obj) {
   3631    return false;
   3632  }
   3633 
   3634  // Initialize _propertiesAdded to 0.
   3635  Rooted<JSAtom*> propName(cx, GetPropertiesAddedName(cx));
   3636  if (!propName) {
   3637    return false;
   3638  }
   3639  RootedId propId(cx, AtomToId(propName));
   3640  RootedValue val(cx, Int32Value(0));
   3641  if (!JS_DefinePropertyById(cx, obj, propId, val, 0)) {
   3642    return false;
   3643  }
   3644 
   3645  args.rval().setObject(*obj);
   3646  return true;
   3647 }
   3648 
   3649 static bool NewObjectWithCallHook(JSContext* cx, unsigned argc, Value* vp) {
   3650  CallArgs args = CallArgsFromVp(argc, vp);
   3651 
   3652  static auto hookShared = [](JSContext* cx, CallArgs& args) {
   3653    Rooted<PlainObject*> obj(cx, NewPlainObject(cx));
   3654    if (!obj) {
   3655      return false;
   3656    }
   3657 
   3658    // Define |this|. We can't expose the MagicValue to JS, so we use
   3659    // "<is_constructing>" in that case.
   3660    Rooted<Value> thisv(cx, args.thisv());
   3661    if (thisv.isMagic(JS_IS_CONSTRUCTING)) {
   3662      JSString* str = NewStringCopyZ<CanGC>(cx, "<is_constructing>");
   3663      if (!str) {
   3664        return false;
   3665      }
   3666      thisv.setString(str);
   3667    }
   3668    if (!DefineDataProperty(cx, obj, cx->names().this_, thisv,
   3669                            JSPROP_ENUMERATE)) {
   3670      return false;
   3671    }
   3672 
   3673    // Define |callee|.
   3674    if (!DefineDataProperty(cx, obj, cx->names().callee, args.calleev(),
   3675                            JSPROP_ENUMERATE)) {
   3676      return false;
   3677    }
   3678 
   3679    // Define |arguments| array.
   3680    Rooted<ArrayObject*> arr(
   3681        cx, NewDenseCopiedArray(cx, args.length(), args.array()));
   3682    if (!arr) {
   3683      return false;
   3684    }
   3685    Rooted<Value> arrVal(cx, ObjectValue(*arr));
   3686    if (!DefineDataProperty(cx, obj, cx->names().arguments, arrVal,
   3687                            JSPROP_ENUMERATE)) {
   3688      return false;
   3689    }
   3690 
   3691    // Define |newTarget| if constructing.
   3692    if (args.isConstructing()) {
   3693      const char* propName = "newTarget";
   3694      Rooted<JSAtom*> name(cx, Atomize(cx, propName, strlen(propName)));
   3695      if (!name) {
   3696        return false;
   3697      }
   3698      Rooted<PropertyKey> key(cx, NameToId(name->asPropertyName()));
   3699      if (!DefineDataProperty(cx, obj, key, args.newTarget(),
   3700                              JSPROP_ENUMERATE)) {
   3701        return false;
   3702      }
   3703    }
   3704 
   3705    args.rval().setObject(*obj);
   3706    return true;
   3707  };
   3708 
   3709  static auto callHook = [](JSContext* cx, unsigned argc, Value* vp) {
   3710    CallArgs args = CallArgsFromVp(argc, vp);
   3711    MOZ_ASSERT(!args.isConstructing());
   3712    return hookShared(cx, args);
   3713  };
   3714  static auto constructHook = [](JSContext* cx, unsigned argc, Value* vp) {
   3715    CallArgs args = CallArgsFromVp(argc, vp);
   3716    MOZ_ASSERT(args.isConstructing());
   3717    return hookShared(cx, args);
   3718  };
   3719 
   3720  static const JSClassOps classOps = {
   3721      nullptr,        // addProperty
   3722      nullptr,        // delProperty
   3723      nullptr,        // enumerate
   3724      nullptr,        // newEnumerate
   3725      nullptr,        // resolve
   3726      nullptr,        // mayResolve
   3727      nullptr,        // finalize
   3728      callHook,       // call
   3729      constructHook,  // construct
   3730      nullptr,        // trace
   3731  };
   3732  static const JSClass cls = {
   3733      "ObjectWithCallHook",
   3734      0,
   3735      &classOps,
   3736  };
   3737 
   3738  Rooted<JSObject*> obj(cx, JS_NewObject(cx, &cls));
   3739  if (!obj) {
   3740    return false;
   3741  }
   3742 
   3743  args.rval().setObject(*obj);
   3744  return true;
   3745 }
   3746 
   3747 static constexpr JSClass ObjectWithManyReservedSlotsClass = {
   3748    "ObjectWithManyReservedSlots",
   3749    JSCLASS_HAS_RESERVED_SLOTS(40),
   3750 };
   3751 
   3752 static bool NewObjectWithManyReservedSlots(JSContext* cx, unsigned argc,
   3753                                           Value* vp) {
   3754  CallArgs args = CallArgsFromVp(argc, vp);
   3755 
   3756  static constexpr size_t NumReservedSlots =
   3757      JSCLASS_RESERVED_SLOTS(&ObjectWithManyReservedSlotsClass);
   3758  static_assert(NumReservedSlots > NativeObject::MAX_FIXED_SLOTS);
   3759 
   3760  RootedObject obj(cx, JS_NewObject(cx, &ObjectWithManyReservedSlotsClass));
   3761  if (!obj) {
   3762    return false;
   3763  }
   3764 
   3765  for (size_t i = 0; i < NumReservedSlots; i++) {
   3766    JS_SetReservedSlot(obj, i, Int32Value(i));
   3767  }
   3768 
   3769  args.rval().setObject(*obj);
   3770  return true;
   3771 }
   3772 
   3773 static bool CheckObjectWithManyReservedSlots(JSContext* cx, unsigned argc,
   3774                                             Value* vp) {
   3775  CallArgs args = CallArgsFromVp(argc, vp);
   3776 
   3777  if (args.length() != 1 || !args[0].isObject() ||
   3778      args[0].toObject().getClass() != &ObjectWithManyReservedSlotsClass) {
   3779    JS_ReportErrorASCII(cx,
   3780                        "Expected object from newObjectWithManyReservedSlots");
   3781    return false;
   3782  }
   3783 
   3784  JSObject* obj = &args[0].toObject();
   3785 
   3786  static constexpr size_t NumReservedSlots =
   3787      JSCLASS_RESERVED_SLOTS(&ObjectWithManyReservedSlotsClass);
   3788 
   3789  for (size_t i = 0; i < NumReservedSlots; i++) {
   3790    MOZ_RELEASE_ASSERT(JS::GetReservedSlot(obj, i).toInt32() == int32_t(i));
   3791  }
   3792 
   3793  args.rval().setUndefined();
   3794  return true;
   3795 }
   3796 
   3797 static bool AddObjectFuse(JSContext* cx, unsigned argc, Value* vp) {
   3798  CallArgs args = CallArgsFromVp(argc, vp);
   3799 
   3800  if (args.length() != 1 || !args[0].isObject() ||
   3801      !args[0].toObject().is<NativeObject>()) {
   3802    JS_ReportErrorASCII(cx, "Expected native object argument");
   3803    return false;
   3804  }
   3805 
   3806  Rooted<NativeObject*> obj(cx, &args[0].toObject().as<NativeObject>());
   3807  if (!NativeObject::setHasObjectFuse(cx, obj)) {
   3808    return false;
   3809  }
   3810 
   3811  // The ObjectFuse is normally created lazily the first time it's needed for an
   3812  // IC stub, but here we create it eagerly to simplify tests.
   3813  if (!cx->zone()->objectFuses.getOrCreate(cx, obj)) {
   3814    return false;
   3815  }
   3816 
   3817  args.rval().setUndefined();
   3818  return true;
   3819 }
   3820 
   3821 static bool GetObjectFuseState(JSContext* cx, unsigned argc, Value* vp) {
   3822  CallArgs args = CallArgsFromVp(argc, vp);
   3823 
   3824  if (args.length() != 1 || !args[0].isObject() ||
   3825      !args[0].toObject().hasObjectFuse()) {
   3826    JS_ReportErrorASCII(cx, "Expected object with fuse");
   3827    return false;
   3828  }
   3829 
   3830  Rooted<NativeObject*> obj(cx, &args[0].toObject().as<NativeObject>());
   3831  ObjectFuse* objFuse = cx->zone()->objectFuses.getOrCreate(cx, obj);
   3832  if (!objFuse) {
   3833    return false;
   3834  }
   3835 
   3836  Rooted<PlainObject*> result(cx, NewPlainObject(cx));
   3837  if (!result) {
   3838    return false;
   3839  }
   3840 
   3841  Rooted<Value> val(cx, NumberValue(objFuse->generationMaybeInvalid()));
   3842  if (!JS_DefineProperty(cx, result, "generation", val, JSPROP_ENUMERATE)) {
   3843    return false;
   3844  }
   3845 
   3846  Rooted<PlainObject*> props(cx, NewPlainObjectWithProto(cx, nullptr));
   3847  if (!props) {
   3848    return false;
   3849  }
   3850 
   3851  // Add all properties to a Vector so that we can iterate over them in property
   3852  // definition order.
   3853  Rooted<PropertyInfoWithKeyVector> propsVec(cx, PropertyInfoWithKeyVector(cx));
   3854  for (ShapePropertyIter<CanGC> iter(cx, obj->shape()); !iter.done(); iter++) {
   3855    if (iter->hasSlot() && !propsVec.append(*iter)) {
   3856      return false;
   3857    }
   3858  }
   3859 
   3860  // For each property, define a property on |props| with the value a string
   3861  // describing the property's current state (eg "Constant").
   3862  for (size_t i = propsVec.length(); i > 0; i--) {
   3863    PropertyInfoWithKey prop = propsVec[i - 1];
   3864    Rooted<PropertyKey> key(cx, prop.key());
   3865    JSString* s =
   3866        NewStringCopyZ<CanGC>(cx, objFuse->getPropertyStateString(prop));
   3867    if (!s) {
   3868      return false;
   3869    };
   3870    val.setString(s);
   3871    if (!JS_DefinePropertyById(cx, props, key, val, JSPROP_ENUMERATE)) {
   3872      return false;
   3873    }
   3874  }
   3875  if (!JS_DefineProperty(cx, result, "properties", props, JSPROP_ENUMERATE)) {
   3876    return false;
   3877  }
   3878 
   3879  args.rval().setObject(*result);
   3880  return true;
   3881 }
   3882 
   3883 static bool GetWatchtowerLog(JSContext* cx, unsigned argc, Value* vp) {
   3884  CallArgs args = CallArgsFromVp(argc, vp);
   3885 
   3886  Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
   3887 
   3888  if (auto* log = cx->runtime()->watchtowerTestingLog.ref().get()) {
   3889    Rooted<JSObject*> elem(cx);
   3890    for (PlainObject* obj : *log) {
   3891      elem = obj;
   3892      if (!cx->compartment()->wrap(cx, &elem)) {
   3893        return false;
   3894      }
   3895      if (!values.append(ObjectValue(*elem))) {
   3896        return false;
   3897      }
   3898    }
   3899    log->clearAndFree();
   3900  }
   3901 
   3902  ArrayObject* arr = NewDenseCopiedArray(cx, values.length(), values.begin());
   3903  if (!arr) {
   3904    return false;
   3905  }
   3906 
   3907  args.rval().setObject(*arr);
   3908  return true;
   3909 }
   3910 
   3911 static bool AddWatchtowerTarget(JSContext* cx, unsigned argc, Value* vp) {
   3912  CallArgs args = CallArgsFromVp(argc, vp);
   3913 
   3914  if (args.length() != 1 || !args[0].isObject()) {
   3915    JS_ReportErrorASCII(cx, "Expected a single object argument.");
   3916    return false;
   3917  }
   3918 
   3919  if (!cx->runtime()->watchtowerTestingLog.ref()) {
   3920    auto vec = cx->make_unique<JSRuntime::RootedPlainObjVec>(cx);
   3921    if (!vec) {
   3922      return false;
   3923    }
   3924    cx->runtime()->watchtowerTestingLog = std::move(vec);
   3925  }
   3926 
   3927  RootedObject obj(cx, &args[0].toObject());
   3928  if (!JSObject::setUseWatchtowerTestingLog(cx, obj)) {
   3929    return false;
   3930  }
   3931 
   3932  args.rval().setUndefined();
   3933  return true;
   3934 }
   3935 
   3936 struct TestExternalString : public JSExternalStringCallbacks {
   3937  void finalize(JS::Latin1Char* chars) const override { js_free(chars); }
   3938  void finalize(char16_t* chars) const override { js_free(chars); }
   3939  size_t sizeOfBuffer(const JS::Latin1Char* chars,
   3940                      mozilla::MallocSizeOf mallocSizeOf) const override {
   3941    return mallocSizeOf(chars);
   3942  }
   3943  size_t sizeOfBuffer(const char16_t* chars,
   3944                      mozilla::MallocSizeOf mallocSizeOf) const override {
   3945    return mallocSizeOf(chars);
   3946  }
   3947 };
   3948 
   3949 static constexpr TestExternalString TestExternalStringCallbacks;
   3950 
   3951 static bool NewString(JSContext* cx, unsigned argc, Value* vp) {
   3952  CallArgs args = CallArgsFromVp(argc, vp);
   3953 
   3954  RootedString src(cx, ToString(cx, args.get(0)));
   3955  if (!src) {
   3956    return false;
   3957  }
   3958 
   3959  gc::Heap heap = gc::Heap::Default;
   3960  bool wantTwoByte = false;
   3961  bool forceExternal = false;
   3962  bool maybeExternal = false;
   3963  bool newStringBuffer = false;
   3964  bool shareStringBuffer = false;
   3965  uint32_t capacity = 0;
   3966 
   3967  if (args.get(1).isObject()) {
   3968    RootedObject options(cx, &args[1].toObject());
   3969    RootedValue v(cx);
   3970    bool requestTenured = false;
   3971    struct BoolSetting {
   3972      const char* name;
   3973      bool* value;
   3974    };
   3975    for (auto [name, setting] :
   3976         {BoolSetting{"tenured", &requestTenured},
   3977          BoolSetting{"twoByte", &wantTwoByte},
   3978          BoolSetting{"external", &forceExternal},
   3979          BoolSetting{"maybeExternal", &maybeExternal},
   3980          BoolSetting{"newStringBuffer", &newStringBuffer},
   3981          BoolSetting{"shareStringBuffer", &shareStringBuffer}}) {
   3982      if (!JS_GetProperty(cx, options, name, &v)) {
   3983        return false;
   3984      }
   3985      *setting = ToBoolean(v);  // false if not given (or otherwise undefined)
   3986    }
   3987    struct Uint32Setting {
   3988      const char* name;
   3989      uint32_t* value;
   3990    };
   3991    for (auto [name, setting] : {Uint32Setting{"capacity", &capacity}}) {
   3992      if (!JS_GetProperty(cx, options, name, &v)) {
   3993        return false;
   3994      }
   3995      int32_t i32;
   3996      if (!ToInt32(cx, v, &i32)) {
   3997        return false;
   3998      }
   3999      if (i32 < 0) {
   4000        JS_ReportErrorASCII(cx, "nonnegative value required");
   4001        return false;
   4002      }
   4003      *setting = static_cast<uint32_t>(i32);
   4004    }
   4005 
   4006    heap = requestTenured ? gc::Heap::Tenured : gc::Heap::Default;
   4007    if (forceExternal || maybeExternal) {
   4008      wantTwoByte = true;
   4009    }
   4010    unsigned kinds = forceExternal + maybeExternal + (capacity != 0) +
   4011                     newStringBuffer + shareStringBuffer;
   4012    if (kinds > 1) {
   4013      JS_ReportErrorASCII(cx,
   4014                          "external, capacity, and stringBuffer options can "
   4015                          "not be used at the same time");
   4016      return false;
   4017    }
   4018  }
   4019 
   4020  auto len = src->length();
   4021  RootedString dest(cx);
   4022 
   4023  if (forceExternal || maybeExternal) {
   4024    auto buf = cx->make_pod_array<char16_t>(len);
   4025    if (!buf) {
   4026      return false;
   4027    }
   4028 
   4029    if (!JS_CopyStringChars(cx, mozilla::Range<char16_t>(buf.get(), len),
   4030                            src)) {
   4031      return false;
   4032    }
   4033 
   4034    bool isExternal = true;
   4035    if (forceExternal) {
   4036      dest = JSExternalString::new_(cx, buf.get(), len,
   4037                                    &TestExternalStringCallbacks);
   4038    } else {
   4039      dest = NewMaybeExternalString(
   4040          cx, buf.get(), len, &TestExternalStringCallbacks, &isExternal, heap);
   4041    }
   4042    if (dest && isExternal) {
   4043      (void)buf.release();  // Ownership was transferred.
   4044    }
   4045  } else if (shareStringBuffer) {
   4046    if (!src->isLinear() || !src->asLinear().hasStringBuffer()) {
   4047      JS_ReportErrorASCII(cx, "source string must have a string buffer");
   4048      return false;
   4049    }
   4050    RefPtr<mozilla::StringBuffer> buffer = src->asLinear().stringBuffer();
   4051    if (src->hasLatin1Chars()) {
   4052      Rooted<JSString::OwnedChars<Latin1Char>> owned(cx, std::move(buffer),
   4053                                                     len);
   4054      dest =
   4055          JSLinearString::newValidLength<CanGC, Latin1Char>(cx, &owned, heap);
   4056    } else {
   4057      Rooted<JSString::OwnedChars<char16_t>> owned(cx, std::move(buffer), len);
   4058      dest = JSLinearString::newValidLength<CanGC, char16_t>(cx, &owned, heap);
   4059    }
   4060  } else {
   4061    AutoStableStringChars stable(cx);
   4062    if (!wantTwoByte && src->hasLatin1Chars()) {
   4063      if (!stable.init(cx, src)) {
   4064        return false;
   4065      }
   4066    } else {
   4067      if (!stable.initTwoByte(cx, src)) {
   4068        return false;
   4069      }
   4070    }
   4071    if (newStringBuffer) {
   4072      auto allocString = [&](const auto* chars) -> JSLinearString* {
   4073        using CharT =
   4074            std::remove_const_t<std::remove_pointer_t<decltype(chars)>>;
   4075 
   4076        if (JSInlineString::lengthFits<CharT>(len)) {
   4077          JS_ReportErrorASCII(cx, "Cannot create small non-inline strings");
   4078          return nullptr;
   4079        }
   4080 
   4081        RefPtr<mozilla::StringBuffer> buffer =
   4082            mozilla::StringBuffer::Create(chars, len);
   4083        if (!buffer) {
   4084          ReportOutOfMemory(cx);
   4085          return nullptr;
   4086        }
   4087 
   4088        Rooted<JSString::OwnedChars<CharT>> owned(cx, std::move(buffer), len);
   4089        return JSLinearString::newValidLength<CanGC, CharT>(cx, &owned, heap);
   4090      };
   4091      if (stable.isLatin1()) {
   4092        dest = allocString(stable.latin1Chars());
   4093      } else {
   4094        dest = allocString(stable.twoByteChars());
   4095      }
   4096    } else if (capacity) {
   4097      if (capacity < len) {
   4098        capacity = len;
   4099      }
   4100      if (len == 0) {
   4101        JS_ReportErrorASCII(cx, "Cannot set capacity of empty string");
   4102        return false;
   4103      }
   4104 
   4105      auto createLinearString = [&](const auto* chars) -> JSLinearString* {
   4106        using CharT =
   4107            std::remove_const_t<std::remove_pointer_t<decltype(chars)>>;
   4108 
   4109        if (JSInlineString::lengthFits<CharT>(len)) {
   4110          JS_ReportErrorASCII(cx, "Cannot create small non-inline strings");
   4111          return nullptr;
   4112        }
   4113 
   4114        auto news =
   4115            cx->make_pod_arena_array<CharT>(js::StringBufferArena, capacity);
   4116        if (!news) {
   4117          return nullptr;
   4118        }
   4119        mozilla::PodCopy(news.get(), chars, len);
   4120        Rooted<JSString::OwnedChars<CharT>> owned(cx, std::move(news), len);
   4121        return JSLinearString::newValidLength<CanGC, CharT>(cx, &owned, heap);
   4122      };
   4123 
   4124      if (stable.isLatin1()) {
   4125        dest = createLinearString(stable.latin1Chars());
   4126      } else {
   4127        dest = createLinearString(stable.twoByteChars());
   4128      }
   4129      if (dest) {
   4130        dest->asLinear().makeExtensible(capacity);
   4131      }
   4132    } else if (wantTwoByte) {
   4133      dest = NewStringCopyNDontDeflate<CanGC>(cx, stable.twoByteChars(), len,
   4134                                              heap);
   4135    } else if (stable.isLatin1()) {
   4136      dest = NewStringCopyN<CanGC>(cx, stable.latin1Chars(), len, heap);
   4137    } else {
   4138      // Normal behavior: auto-deflate to latin1 if possible.
   4139      dest = NewStringCopyN<CanGC>(cx, stable.twoByteChars(), len, heap);
   4140    }
   4141  }
   4142 
   4143  if (!dest) {
   4144    return false;
   4145  }
   4146 
   4147  args.rval().setString(dest);
   4148  return true;
   4149 }
   4150 
   4151 static bool NewDependentString(JSContext* cx, unsigned argc, Value* vp) {
   4152  CallArgs args = CallArgsFromVp(argc, vp);
   4153 
   4154  RootedString src(cx, ToString(cx, args.get(0)));
   4155  if (!src) {
   4156    return false;
   4157  }
   4158 
   4159  uint64_t indexStart = 0;
   4160  mozilla::Maybe<uint64_t> indexEnd;
   4161  gc::Heap heap = gc::Heap::Default;
   4162  mozilla::Maybe<gc::Heap> requiredHeap;
   4163  bool suppressContraction = false;
   4164 
   4165  if (!ToIndex(cx, args.get(1), &indexStart)) {
   4166    return false;
   4167  }
   4168 
   4169  Rooted<Value> options(cx);
   4170  if (args.get(2).isObject()) {
   4171    options = args[2];
   4172  } else {
   4173    uint64_t idx;
   4174    if (args.hasDefined(2)) {
   4175      if (!ToIndex(cx, args.get(2), &idx)) {
   4176        return false;
   4177      }
   4178      indexEnd.emplace(idx);
   4179    }
   4180    options = args.get(3);
   4181  }
   4182 
   4183  if (options.isObject()) {
   4184    Rooted<Value> v(cx);
   4185    Rooted<JSObject*> optObj(cx, &options.toObject());
   4186    if (!JS_GetProperty(cx, optObj, "tenured", &v)) {
   4187      return false;
   4188    }
   4189    if (v.isBoolean()) {
   4190      requiredHeap.emplace(v.toBoolean() ? gc::Heap::Tenured
   4191                                         : gc::Heap::Default);
   4192      heap = *requiredHeap;
   4193    }
   4194    if (!JS_GetProperty(cx, optObj, "suppress-contraction", &v)) {
   4195      return false;
   4196    }
   4197    suppressContraction = ToBoolean(v);
   4198  }
   4199 
   4200  if (indexEnd.isNothing()) {
   4201    // Read the length now that no more JS code can run.
   4202    indexEnd.emplace(src->length());
   4203  }
   4204  if (indexStart > src->length() || *indexEnd > src->length() ||
   4205      indexStart >= *indexEnd) {
   4206    JS_ReportErrorASCII(cx, "invalid dependent string bounds");
   4207    return false;
   4208  }
   4209  if (!src->ensureLinear(cx)) {
   4210    return false;
   4211  }
   4212  Rooted<JSString*> result(
   4213      cx, js::NewDependentStringForTesting(
   4214              cx, src, indexStart, *indexEnd - indexStart,
   4215              suppressContraction ? JS::ContractBaseChain::AllowLong
   4216                                  : JS::ContractBaseChain::Contract,
   4217              heap));
   4218  if (!result) {
   4219    return false;
   4220  }
   4221  if (!result->isDependent()) {
   4222    JS_ReportErrorASCII(cx, "resulting string is not dependent (too short?)");
   4223    return false;
   4224  }
   4225 
   4226  if (requiredHeap.isSome()) {
   4227    if ((*requiredHeap == gc::Heap::Tenured) != result->isTenured()) {
   4228      if (result->isTenured()) {
   4229        JS_ReportErrorASCII(cx, "nursery string created in tenured heap");
   4230        return false;
   4231      } else {
   4232        JS_ReportErrorASCII(cx, "tenured string created in nursery heap");
   4233        return false;
   4234      }
   4235    }
   4236  }
   4237 
   4238  args.rval().setString(result);
   4239  return true;
   4240 }
   4241 
   4242 // Warning! This will let you create ropes that I'm not sure would be possible
   4243 // otherwise, specifically:
   4244 //
   4245 //   - a rope with a zero-length child
   4246 //   - a rope that would fit into an inline string
   4247 //
   4248 static bool NewRope(JSContext* cx, unsigned argc, Value* vp) {
   4249  CallArgs args = CallArgsFromVp(argc, vp);
   4250 
   4251  if (!args.get(0).isString() || !args.get(1).isString()) {
   4252    JS_ReportErrorASCII(cx, "newRope requires two string arguments.");
   4253    return false;
   4254  }
   4255 
   4256  gc::Heap heap = js::gc::Heap::Default;
   4257  if (args.get(2).isObject()) {
   4258    RootedObject options(cx, &args[2].toObject());
   4259    RootedValue v(cx);
   4260    if (!JS_GetProperty(cx, options, "nursery", &v)) {
   4261      return false;
   4262    }
   4263    if (!v.isUndefined() && !ToBoolean(v)) {
   4264      heap = js::gc::Heap::Tenured;
   4265    }
   4266  }
   4267 
   4268  RootedString left(cx, args[0].toString());
   4269  RootedString right(cx, args[1].toString());
   4270  size_t length = JS_GetStringLength(left) + JS_GetStringLength(right);
   4271  if (length > JSString::MAX_LENGTH) {
   4272    JS_ReportErrorASCII(cx, "rope length exceeds maximum string length");
   4273    return false;
   4274  }
   4275 
   4276  // Disallow creating ropes where one side is empty.
   4277  if (left->empty() || right->empty()) {
   4278    JS_ReportErrorASCII(cx, "rope child mustn't be the empty string");
   4279    return false;
   4280  }
   4281 
   4282  // Disallow creating ropes which fit into inline strings.
   4283  if (left->hasLatin1Chars() && right->hasLatin1Chars()) {
   4284    if (JSInlineString::lengthFits<JS::Latin1Char>(length)) {
   4285      JS_ReportErrorASCII(cx, "Cannot create small non-inline ropes");
   4286      return false;
   4287    }
   4288  } else {
   4289    if (JSInlineString::lengthFits<char16_t>(length)) {
   4290      JS_ReportErrorASCII(cx, "Cannot create small non-inline ropes");
   4291      return false;
   4292    }
   4293  }
   4294 
   4295  auto* str = JSRope::new_<CanGC>(cx, left, right, length, heap);
   4296  if (!str) {
   4297    return false;
   4298  }
   4299 
   4300  args.rval().setString(str);
   4301  return true;
   4302 }
   4303 
   4304 static bool IsRope(JSContext* cx, unsigned argc, Value* vp) {
   4305  CallArgs args = CallArgsFromVp(argc, vp);
   4306 
   4307  if (!args.get(0).isString()) {
   4308    JS_ReportErrorASCII(cx, "isRope requires a string argument.");
   4309    return false;
   4310  }
   4311 
   4312  JSString* str = args[0].toString();
   4313  args.rval().setBoolean(str->isRope());
   4314  return true;
   4315 }
   4316 
   4317 static bool EnsureLinearString(JSContext* cx, unsigned argc, Value* vp) {
   4318  CallArgs args = CallArgsFromVp(argc, vp);
   4319 
   4320  if (args.length() != 1 || !args[0].isString()) {
   4321    JS_ReportErrorASCII(
   4322        cx, "ensureLinearString takes exactly one string argument.");
   4323    return false;
   4324  }
   4325 
   4326  JSLinearString* linear = args[0].toString()->ensureLinear(cx);
   4327  if (!linear) {
   4328    return false;
   4329  }
   4330 
   4331  args.rval().setString(linear);
   4332  return true;
   4333 }
   4334 
   4335 static bool RepresentativeStringArray(JSContext* cx, unsigned argc, Value* vp) {
   4336  CallArgs args = CallArgsFromVp(argc, vp);
   4337 
   4338  RootedObject array(cx, JS::NewArrayObject(cx, 0));
   4339  if (!array) {
   4340    return false;
   4341  }
   4342 
   4343  if (!JSString::fillWithRepresentatives(cx, array.as<ArrayObject>())) {
   4344    return false;
   4345  }
   4346 
   4347  args.rval().setObject(*array);
   4348  return true;
   4349 }
   4350 
   4351 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
   4352 
   4353 static bool OOMThreadTypes(JSContext* cx, unsigned argc, Value* vp) {
   4354  CallArgs args = CallArgsFromVp(argc, vp);
   4355  args.rval().setInt32(js::THREAD_TYPE_MAX);
   4356  return true;
   4357 }
   4358 
   4359 static bool CheckCanSimulateOOM(JSContext* cx) {
   4360  if (js::oom::GetThreadType() != js::THREAD_TYPE_MAIN) {
   4361    JS_ReportErrorASCII(
   4362        cx, "Simulated OOM failure is only supported on the main thread");
   4363    return false;
   4364  }
   4365 
   4366  return true;
   4367 }
   4368 
   4369 static bool SetupOOMFailure(JSContext* cx, bool failAlways, unsigned argc,
   4370                            Value* vp) {
   4371  CallArgs args = CallArgsFromVp(argc, vp);
   4372 
   4373  if (disableOOMFunctions) {
   4374    args.rval().setUndefined();
   4375    return true;
   4376  }
   4377 
   4378  if (args.length() < 1) {
   4379    JS_ReportErrorASCII(cx, "Count argument required");
   4380    return false;
   4381  }
   4382 
   4383  if (args.length() > 2) {
   4384    JS_ReportErrorASCII(cx, "Too many arguments");
   4385    return false;
   4386  }
   4387 
   4388  int32_t count;
   4389  if (!JS::ToInt32(cx, args.get(0), &count)) {
   4390    return false;
   4391  }
   4392 
   4393  if (count <= 0) {
   4394    JS_ReportErrorASCII(cx, "OOM cutoff should be positive");
   4395    return false;
   4396  }
   4397 
   4398  uint32_t targetThread = js::THREAD_TYPE_MAIN;
   4399  if (args.length() > 1 && !ToUint32(cx, args[1], &targetThread)) {
   4400    return false;
   4401  }
   4402 
   4403  if (targetThread == js::THREAD_TYPE_NONE ||
   4404      targetThread == js::THREAD_TYPE_WORKER ||
   4405      targetThread >= js::THREAD_TYPE_MAX) {
   4406    JS_ReportErrorASCII(cx, "Invalid thread type specified");
   4407    return false;
   4408  }
   4409 
   4410  if (!CheckCanSimulateOOM(cx)) {
   4411    return false;
   4412  }
   4413 
   4414  js::oom::simulator.simulateFailureAfter(js::oom::FailureSimulator::Kind::OOM,
   4415                                          count, targetThread, failAlways);
   4416  args.rval().setUndefined();
   4417  return true;
   4418 }
   4419 
   4420 static bool OOMAfterAllocations(JSContext* cx, unsigned argc, Value* vp) {
   4421  return SetupOOMFailure(cx, true, argc, vp);
   4422 }
   4423 
   4424 static bool OOMAtAllocation(JSContext* cx, unsigned argc, Value* vp) {
   4425  return SetupOOMFailure(cx, false, argc, vp);
   4426 }
   4427 
   4428 static bool ResetOOMFailure(JSContext* cx, unsigned argc, Value* vp) {
   4429  CallArgs args = CallArgsFromVp(argc, vp);
   4430 
   4431  if (!CheckCanSimulateOOM(cx)) {
   4432    return false;
   4433  }
   4434 
   4435  args.rval().setBoolean(js::oom::HadSimulatedOOM());
   4436  js::oom::simulator.reset();
   4437  return true;
   4438 }
   4439 
   4440 static size_t CountCompartments(JSContext* cx) {
   4441  size_t count = 0;
   4442  for (auto zone : cx->runtime()->gc.zones()) {
   4443    count += zone->compartments().length();
   4444  }
   4445  return count;
   4446 }
   4447 
   4448 // Iterative failure testing: test a function by simulating failures at indexed
   4449 // locations throughout the normal execution path and checking that the
   4450 // resulting state of the environment is consistent with the error result.
   4451 //
   4452 // For example, trigger OOM at every allocation point and test that the function
   4453 // either recovers and succeeds or raises an exception and fails.
   4454 
   4455 class MOZ_STACK_CLASS IterativeFailureTest {
   4456 public:
   4457  struct FailureSimulator {
   4458    virtual void setup(JSContext* cx) {}
   4459    virtual void teardown(JSContext* cx) {}
   4460    virtual void startSimulating(JSContext* cx, unsigned iteration,
   4461                                 unsigned thread, bool keepFailing) = 0;
   4462    virtual bool stopSimulating() = 0;
   4463    virtual void cleanup(JSContext* cx) {}
   4464  };
   4465 
   4466  IterativeFailureTest(JSContext* cx, FailureSimulator& simulator);
   4467  bool initParams(const CallArgs& args);
   4468  bool test();
   4469 
   4470 private:
   4471  bool setup();
   4472  bool testThread(unsigned thread);
   4473  bool testIteration(unsigned thread, unsigned iteration,
   4474                     bool& failureWasSimulated, MutableHandleValue exception);
   4475  void cleanup();
   4476  void teardown();
   4477 
   4478  JSContext* const cx;
   4479  FailureSimulator& simulator;
   4480  size_t compartmentCount;
   4481 
   4482  // Test parameters set by initParams.
   4483  RootedFunction testFunction;
   4484  unsigned threadStart = 0;
   4485  unsigned threadEnd = 0;
   4486  bool expectExceptionOnFailure = true;
   4487  bool keepFailing = false;
   4488  bool verbose = false;
   4489 };
   4490 
   4491 bool RunIterativeFailureTest(
   4492    JSContext* cx, const CallArgs& args,
   4493    IterativeFailureTest::FailureSimulator& simulator) {
   4494  IterativeFailureTest test(cx, simulator);
   4495  return test.initParams(args) && test.test();
   4496 }
   4497 
   4498 IterativeFailureTest::IterativeFailureTest(JSContext* cx,
   4499                                           FailureSimulator& simulator)
   4500    : cx(cx), simulator(simulator), testFunction(cx) {}
   4501 
   4502 bool IterativeFailureTest::test() {
   4503  if (disableOOMFunctions) {
   4504    return true;
   4505  }
   4506 
   4507  if (!setup()) {
   4508    return false;
   4509  }
   4510 
   4511  auto onExit = mozilla::MakeScopeExit([this] { teardown(); });
   4512 
   4513  for (unsigned thread = threadStart; thread <= threadEnd; thread++) {
   4514    if (!testThread(thread)) {
   4515      return false;
   4516    }
   4517  }
   4518 
   4519  return true;
   4520 }
   4521 
   4522 bool IterativeFailureTest::setup() {
   4523  if (!CheckCanSimulateOOM(cx)) {
   4524    return false;
   4525  }
   4526 
   4527  // Disallow nested tests.
   4528  if (cx->runningOOMTest) {
   4529    JS_ReportErrorASCII(
   4530        cx, "Nested call to iterative failure test is not allowed.");
   4531    return false;
   4532  }
   4533  cx->runningOOMTest = true;
   4534 
   4535  MOZ_ASSERT(!cx->isExceptionPending());
   4536 
   4537 #  ifdef JS_GC_ZEAL
   4538  JS::SetGCZeal(cx, 0, JS::ShellDefaultGCZealFrequency);
   4539 #  endif
   4540 
   4541  // Delazify the function here if necessary so we don't end up testing that.
   4542  if (testFunction->isInterpreted() &&
   4543      !JSFunction::getOrCreateScript(cx, testFunction)) {
   4544    return false;
   4545  }
   4546 
   4547  compartmentCount = CountCompartments(cx);
   4548 
   4549  simulator.setup(cx);
   4550 
   4551  return true;
   4552 }
   4553 
   4554 bool IterativeFailureTest::testThread(unsigned thread) {
   4555  if (verbose) {
   4556    fprintf(stderr, "thread %u\n", thread);
   4557  }
   4558 
   4559  RootedValue exception(cx);
   4560 
   4561  unsigned iteration = 1;
   4562  bool failureWasSimulated;
   4563  do {
   4564    if (!testIteration(thread, iteration, failureWasSimulated, &exception)) {
   4565      return false;
   4566    }
   4567 
   4568    iteration++;
   4569  } while (failureWasSimulated);
   4570 
   4571  if (verbose) {
   4572    fprintf(stderr, "  finished after %u iterations\n", iteration - 1);
   4573    if (!exception.isUndefined()) {
   4574      RootedString str(cx, JS::ToString(cx, exception));
   4575      if (!str) {
   4576        fprintf(stderr, "  error while trying to print exception, giving up\n");
   4577        return false;
   4578      }
   4579      UniqueChars bytes(JS_EncodeStringToUTF8(cx, str));
   4580      if (!bytes) {
   4581        return false;
   4582      }
   4583      fprintf(stderr, "  threw %s\n", bytes.get());
   4584    }
   4585  }
   4586 
   4587  return true;
   4588 }
   4589 
   4590 bool IterativeFailureTest::testIteration(unsigned thread, unsigned iteration,
   4591                                         bool& failureWasSimulated,
   4592                                         MutableHandleValue exception) {
   4593  if (verbose) {
   4594    fprintf(stderr, "  iteration %u\n", iteration);
   4595  }
   4596 
   4597  MOZ_RELEASE_ASSERT(!cx->isExceptionPending());
   4598 
   4599  simulator.startSimulating(cx, iteration, thread, keepFailing);
   4600 
   4601  RootedValue result(cx);
   4602  bool ok = JS_CallFunction(cx, cx->global(), testFunction,
   4603                            HandleValueArray::empty(), &result);
   4604 
   4605  failureWasSimulated = simulator.stopSimulating();
   4606 
   4607  if (ok && cx->isExceptionPending()) {
   4608    MOZ_CRASH(
   4609        "Thunk execution succeeded but an exception was raised - missing error "
   4610        "check?");
   4611  }
   4612 
   4613  if (!ok && !cx->isExceptionPending() && expectExceptionOnFailure) {
   4614    MOZ_CRASH(
   4615        "Thunk execution failed but no exception was raised - missing call to "
   4616        "js::ReportOutOfMemory()?");
   4617  }
   4618 
   4619  // Note that it is possible that the function throws an exception unconnected
   4620  // to the simulated failure, in which case we ignore it. More correct would be
   4621  // to have the caller pass some kind of exception specification and to check
   4622  // the exception against it.
   4623  if (!failureWasSimulated && cx->isExceptionPending()) {
   4624    if (!cx->getPendingException(exception)) {
   4625      return false;
   4626    }
   4627  }
   4628  cx->clearPendingException();
   4629 
   4630  cleanup();
   4631 
   4632  return true;
   4633 }
   4634 
   4635 void IterativeFailureTest::cleanup() {
   4636  simulator.cleanup(cx);
   4637 
   4638  gc::FinishGC(cx);
   4639 
   4640  // Some tests create a new compartment or zone on every iteration. Our GC is
   4641  // triggered by GC allocations and not by number of compartments or zones, so
   4642  // these won't normally get cleaned up. The check here stops some tests
   4643  // running out of memory. ("Gentlemen, you can't fight in here! This is the
   4644  // War oom!")
   4645  if (CountCompartments(cx) > compartmentCount + 100) {
   4646    JS_GC(cx);
   4647    compartmentCount = CountCompartments(cx);
   4648  }
   4649 }
   4650 
   4651 void IterativeFailureTest::teardown() {
   4652  simulator.teardown(cx);
   4653 
   4654  cx->runningOOMTest = false;
   4655 }
   4656 
   4657 bool IterativeFailureTest::initParams(const CallArgs& args) {
   4658  if (args.length() < 1 || args.length() > 2) {
   4659    JS_ReportErrorASCII(cx, "function takes between 1 and 2 arguments.");
   4660    return false;
   4661  }
   4662 
   4663  if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
   4664    JS_ReportErrorASCII(cx, "The first argument must be the function to test.");
   4665    return false;
   4666  }
   4667  testFunction = &args[0].toObject().as<JSFunction>();
   4668 
   4669  if (args.length() == 2) {
   4670    if (args[1].isBoolean()) {
   4671      expectExceptionOnFailure = args[1].toBoolean();
   4672    } else if (args[1].isObject()) {
   4673      RootedObject options(cx, &args[1].toObject());
   4674      RootedValue value(cx);
   4675 
   4676      if (!JS_GetProperty(cx, options, "expectExceptionOnFailure", &value)) {
   4677        return false;
   4678      }
   4679      if (!value.isUndefined()) {
   4680        expectExceptionOnFailure = ToBoolean(value);
   4681      }
   4682 
   4683      if (!JS_GetProperty(cx, options, "keepFailing", &value)) {
   4684        return false;
   4685      }
   4686      if (!value.isUndefined()) {
   4687        keepFailing = ToBoolean(value);
   4688      }
   4689    } else {
   4690      JS_ReportErrorASCII(
   4691          cx, "The optional second argument must be an object or a boolean.");
   4692      return false;
   4693    }
   4694  }
   4695 
   4696  // There are some places where we do fail without raising an exception, so
   4697  // we can't expose this to the fuzzers by default.
   4698  if (fuzzingSafe) {
   4699    expectExceptionOnFailure = false;
   4700  }
   4701 
   4702  // Test all threads by default except worker threads.
   4703  threadStart = oom::FirstThreadTypeToTest;
   4704  threadEnd = oom::LastThreadTypeToTest;
   4705 
   4706  // Test a single thread type if specified by the OOM_THREAD environment
   4707  // variable.
   4708  int threadOption = 0;
   4709  if (EnvVarAsInt("OOM_THREAD", &threadOption)) {
   4710    if (threadOption < oom::FirstThreadTypeToTest ||
   4711        threadOption > oom::LastThreadTypeToTest) {
   4712      JS_ReportErrorASCII(cx, "OOM_THREAD value out of range.");
   4713      return false;
   4714    }
   4715 
   4716    threadStart = threadOption;
   4717    threadEnd = threadOption;
   4718  }
   4719 
   4720  verbose = EnvVarIsDefined("OOM_VERBOSE");
   4721 
   4722  return true;
   4723 }
   4724 
   4725 struct OOMSimulator : public IterativeFailureTest::FailureSimulator {
   4726  void setup(JSContext* cx) override { cx->runtime()->hadOutOfMemory = false; }
   4727 
   4728  void startSimulating(JSContext* cx, unsigned i, unsigned thread,
   4729                       bool keepFailing) override {
   4730    MOZ_ASSERT(!cx->runtime()->hadOutOfMemory);
   4731    js::oom::simulator.simulateFailureAfter(
   4732        js::oom::FailureSimulator::Kind::OOM, i, thread, keepFailing);
   4733  }
   4734 
   4735  bool stopSimulating() override {
   4736    bool handledOOM = js::oom::HadSimulatedOOM();
   4737    js::oom::simulator.reset();
   4738    return handledOOM;
   4739  }
   4740 
   4741  void cleanup(JSContext* cx) override {
   4742    cx->runtime()->hadOutOfMemory = false;
   4743  }
   4744 };
   4745 
   4746 static bool OOMTest(JSContext* cx, unsigned argc, Value* vp) {
   4747  CallArgs args = CallArgsFromVp(argc, vp);
   4748 
   4749  OOMSimulator simulator;
   4750  if (!RunIterativeFailureTest(cx, args, simulator)) {
   4751    return false;
   4752  }
   4753 
   4754  args.rval().setUndefined();
   4755  return true;
   4756 }
   4757 
   4758 struct StackOOMSimulator : public IterativeFailureTest::FailureSimulator {
   4759  void startSimulating(JSContext* cx, unsigned i, unsigned thread,
   4760                       bool keepFailing) override {
   4761    js::oom::simulator.simulateFailureAfter(
   4762        js::oom::FailureSimulator::Kind::StackOOM, i, thread, keepFailing);
   4763  }
   4764 
   4765  bool stopSimulating() override {
   4766    bool handledOOM = js::oom::HadSimulatedStackOOM();
   4767    js::oom::simulator.reset();
   4768    return handledOOM;
   4769  }
   4770 };
   4771 
   4772 static bool StackTest(JSContext* cx, unsigned argc, Value* vp) {
   4773  CallArgs args = CallArgsFromVp(argc, vp);
   4774 
   4775  StackOOMSimulator simulator;
   4776  if (!RunIterativeFailureTest(cx, args, simulator)) {
   4777    return false;
   4778  }
   4779 
   4780  args.rval().setUndefined();
   4781  return true;
   4782 }
   4783 
   4784 struct FailingIterruptSimulator
   4785    : public IterativeFailureTest::FailureSimulator {
   4786  JSInterruptCallback* prevEnd = nullptr;
   4787 
   4788  static bool failingInterruptCallback(JSContext* cx) { return false; }
   4789 
   4790  void setup(JSContext* cx) override {
   4791    prevEnd = cx->interruptCallbacks().end();
   4792    JS_AddInterruptCallback(cx, failingInterruptCallback);
   4793  }
   4794 
   4795  void teardown(JSContext* cx) override {
   4796    cx->interruptCallbacks().erase(prevEnd, cx->interruptCallbacks().end());
   4797  }
   4798 
   4799  void startSimulating(JSContext* cx, unsigned i, unsigned thread,
   4800                       bool keepFailing) override {
   4801    js::oom::simulator.simulateFailureAfter(
   4802        js::oom::FailureSimulator::Kind::Interrupt, i, thread, keepFailing);
   4803  }
   4804 
   4805  bool stopSimulating() override {
   4806    bool handledInterrupt = js::oom::HadSimulatedInterrupt();
   4807    js::oom::simulator.reset();
   4808    return handledInterrupt;
   4809  }
   4810 };
   4811 
   4812 static bool InterruptTest(JSContext* cx, unsigned argc, Value* vp) {
   4813  CallArgs args = CallArgsFromVp(argc, vp);
   4814 
   4815  FailingIterruptSimulator simulator;
   4816  if (!RunIterativeFailureTest(cx, args, simulator)) {
   4817    return false;
   4818  }
   4819 
   4820  args.rval().setUndefined();
   4821  return true;
   4822 }
   4823 
   4824 #endif  // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
   4825 
   4826 static bool SettlePromiseNow(JSContext* cx, unsigned argc, Value* vp) {
   4827  CallArgs args = CallArgsFromVp(argc, vp);
   4828  if (!args.requireAtLeast(cx, "settlePromiseNow", 1)) {
   4829    return false;
   4830  }
   4831  if (!args[0].isObject() || !args[0].toObject().is<PromiseObject>()) {
   4832    JS_ReportErrorASCII(cx, "first argument must be a Promise object");
   4833    return false;
   4834  }
   4835 
   4836  Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
   4837  if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
   4838    JS_ReportErrorASCII(
   4839        cx, "async function/generator's promise shouldn't be manually settled");
   4840    return false;
   4841  }
   4842 
   4843  if (promise->state() != JS::PromiseState::Pending) {
   4844    JS_ReportErrorASCII(cx, "cannot settle an already-resolved promise");
   4845    return false;
   4846  }
   4847 
   4848  if (IsPromiseWithDefaultResolvingFunction(promise)) {
   4849    SetAlreadyResolvedPromiseWithDefaultResolvingFunction(promise);
   4850  }
   4851 
   4852  int32_t flags = promise->flags();
   4853  promise->setFixedSlot(
   4854      PromiseSlot_Flags,
   4855      Int32Value(flags | PROMISE_FLAG_RESOLVED | PROMISE_FLAG_FULFILLED));
   4856  promise->setFixedSlot(PromiseSlot_ReactionsOrResult, UndefinedValue());
   4857 
   4858  DebugAPI::onPromiseSettled(cx, promise);
   4859  return true;
   4860 }
   4861 
   4862 static bool GetWaitForAllPromise(JSContext* cx, unsigned argc, Value* vp) {
   4863  CallArgs args = CallArgsFromVp(argc, vp);
   4864  if (!args.requireAtLeast(cx, "getWaitForAllPromise", 1)) {
   4865    return false;
   4866  }
   4867  if (!args[0].isObject() || !args[0].toObject().is<ArrayObject>() ||
   4868      args[0].toObject().as<NativeObject>().isIndexed()) {
   4869    JS_ReportErrorASCII(
   4870        cx, "first argument must be a dense Array of Promise objects");
   4871    return false;
   4872  }
   4873  Rooted<NativeObject*> list(cx, &args[0].toObject().as<NativeObject>());
   4874  RootedObjectVector promises(cx);
   4875  uint32_t count = list->getDenseInitializedLength();
   4876  if (!promises.resize(count)) {
   4877    return false;
   4878  }
   4879 
   4880  for (uint32_t i = 0; i < count; i++) {
   4881    RootedValue elem(cx, list->getDenseElement(i));
   4882    if (!elem.isObject() || !elem.toObject().is<PromiseObject>()) {
   4883      JS_ReportErrorASCII(
   4884          cx, "Each entry in the passed-in Array must be a Promise");
   4885      return false;
   4886    }
   4887    promises[i].set(&elem.toObject());
   4888  }
   4889 
   4890  RootedObject resultPromise(cx, JS::GetWaitForAllPromise(cx, promises));
   4891  if (!resultPromise) {
   4892    return false;
   4893  }
   4894 
   4895  args.rval().set(ObjectValue(*resultPromise));
   4896  return true;
   4897 }
   4898 
   4899 static bool ResolvePromise(JSContext* cx, unsigned argc, Value* vp) {
   4900  CallArgs args = CallArgsFromVp(argc, vp);
   4901  if (!args.requireAtLeast(cx, "resolvePromise", 2)) {
   4902    return false;
   4903  }
   4904  if (!args[0].isObject() ||
   4905      !UncheckedUnwrap(&args[0].toObject())->is<PromiseObject>()) {
   4906    JS_ReportErrorASCII(
   4907        cx, "first argument must be a maybe-wrapped Promise object");
   4908    return false;
   4909  }
   4910 
   4911  RootedObject promise(cx, &args[0].toObject());
   4912  RootedValue resolution(cx, args[1]);
   4913  mozilla::Maybe<AutoRealm> ar;
   4914  if (IsWrapper(promise)) {
   4915    promise = UncheckedUnwrap(promise);
   4916    ar.emplace(cx, promise);
   4917    if (!cx->compartment()->wrap(cx, &resolution)) {
   4918      return false;
   4919    }
   4920  }
   4921 
   4922  if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
   4923    JS_ReportErrorASCII(
   4924        cx,
   4925        "async function/generator's promise shouldn't be manually resolved");
   4926    return false;
   4927  }
   4928 
   4929  bool result = JS::ResolvePromise(cx, promise, resolution);
   4930  if (result) {
   4931    args.rval().setUndefined();
   4932  }
   4933  return result;
   4934 }
   4935 
   4936 static bool RejectPromise(JSContext* cx, unsigned argc, Value* vp) {
   4937  CallArgs args = CallArgsFromVp(argc, vp);
   4938  if (!args.requireAtLeast(cx, "rejectPromise", 2)) {
   4939    return false;
   4940  }
   4941  if (!args[0].isObject() ||
   4942      !UncheckedUnwrap(&args[0].toObject())->is<PromiseObject>()) {
   4943    JS_ReportErrorASCII(
   4944        cx, "first argument must be a maybe-wrapped Promise object");
   4945    return false;
   4946  }
   4947 
   4948  RootedObject promise(cx, &args[0].toObject());
   4949  RootedValue reason(cx, args[1]);
   4950  mozilla::Maybe<AutoRealm> ar;
   4951  if (IsWrapper(promise)) {
   4952    promise = UncheckedUnwrap(promise);
   4953    ar.emplace(cx, promise);
   4954    if (!cx->compartment()->wrap(cx, &reason)) {
   4955      return false;
   4956    }
   4957  }
   4958 
   4959  if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
   4960    JS_ReportErrorASCII(
   4961        cx,
   4962        "async function/generator's promise shouldn't be manually rejected");
   4963    return false;
   4964  }
   4965 
   4966  bool result = JS::RejectPromise(cx, promise, reason);
   4967  if (result) {
   4968    args.rval().setUndefined();
   4969  }
   4970  return result;
   4971 }
   4972 
   4973 static unsigned finalizeCount = 0;
   4974 
   4975 static void finalize_counter_finalize(JS::GCContext* gcx, JSObject* obj) {
   4976  ++finalizeCount;
   4977 }
   4978 
   4979 static const JSClassOps FinalizeCounterClassOps = {
   4980    nullptr,                    // addProperty
   4981    nullptr,                    // delProperty
   4982    nullptr,                    // enumerate
   4983    nullptr,                    // newEnumerate
   4984    nullptr,                    // resolve
   4985    nullptr,                    // mayResolve
   4986    finalize_counter_finalize,  // finalize
   4987    nullptr,                    // call
   4988    nullptr,                    // construct
   4989    nullptr,                    // trace
   4990 };
   4991 
   4992 static const JSClass FinalizeCounterClass = {
   4993    "FinalizeCounter",
   4994    JSCLASS_FOREGROUND_FINALIZE,
   4995    &FinalizeCounterClassOps,
   4996 };
   4997 
   4998 static bool MakeFinalizeObserver(JSContext* cx, unsigned argc, Value* vp) {
   4999  CallArgs args = CallArgsFromVp(argc, vp);
   5000 
   5001  JSObject* obj =
   5002      JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, nullptr);
   5003  if (!obj) {
   5004    return false;
   5005  }
   5006 
   5007  args.rval().setObject(*obj);
   5008  return true;
   5009 }
   5010 
   5011 static bool FinalizeCount(JSContext* cx, unsigned argc, Value* vp) {
   5012  CallArgs args = CallArgsFromVp(argc, vp);
   5013  args.rval().setInt32(finalizeCount);
   5014  return true;
   5015 }
   5016 
   5017 static bool ResetFinalizeCount(JSContext* cx, unsigned argc, Value* vp) {
   5018  CallArgs args = CallArgsFromVp(argc, vp);
   5019  finalizeCount = 0;
   5020  args.rval().setUndefined();
   5021  return true;
   5022 }
   5023 
   5024 static bool DumpHeap(JSContext* cx, unsigned argc, Value* vp) {
   5025  CallArgs args = CallArgsFromVp(argc, vp);
   5026 
   5027  FILE* dumpFile = stdout;
   5028  auto closeFile = mozilla::MakeScopeExit([&dumpFile] {
   5029    if (dumpFile && dumpFile != stdout) {
   5030      fclose(dumpFile);
   5031    }
   5032  });
   5033 
   5034  if (args.length() > 1) {
   5035    RootedObject callee(cx, &args.callee());
   5036    ReportUsageErrorASCII(cx, callee, "Too many arguments");
   5037    return false;
   5038  }
   5039 
   5040  if (!args.get(0).isUndefined()) {
   5041    RootedString str(cx, ToString(cx, args[0]));
   5042    if (!str) {
   5043      return false;
   5044    }
   5045    if (!fuzzingSafe) {
   5046      UniqueChars fileNameBytes = JS_EncodeStringToUTF8(cx, str);
   5047      if (!fileNameBytes) {
   5048        return false;
   5049      }
   5050 #ifdef XP_WIN
   5051      UniqueWideChars wideFileNameBytes =
   5052          JS::EncodeUtf8ToWide(cx, fileNameBytes.get());
   5053      if (!wideFileNameBytes) {
   5054        return false;
   5055      }
   5056      dumpFile = _wfopen(wideFileNameBytes.get(), L"w");
   5057 #else
   5058      UniqueChars narrowFileNameBytes =
   5059          JS::EncodeUtf8ToNarrow(cx, fileNameBytes.get());
   5060      if (!narrowFileNameBytes) {
   5061        return false;
   5062      }
   5063      dumpFile = fopen(narrowFileNameBytes.get(), "w");
   5064 #endif
   5065      if (!dumpFile) {
   5066        JS_ReportErrorUTF8(cx, "can't open %s", fileNameBytes.get());
   5067        return false;
   5068      }
   5069    }
   5070  }
   5071 
   5072  js::DumpHeap(cx, dumpFile, js::IgnoreNurseryObjects);
   5073 
   5074  args.rval().setUndefined();
   5075  return true;
   5076 }
   5077 
   5078 static bool Terminate(JSContext* cx, unsigned arg, Value* vp) {
   5079  // Print a message to stderr in differential testing to help jsfunfuzz
   5080  // find uncatchable-exception bugs.
   5081  if (js::SupportDifferentialTesting()) {
   5082    fprintf(stderr, "terminate called\n");
   5083  }
   5084 
   5085  JS::ReportUncatchableException(cx);
   5086  return false;
   5087 }
   5088 
   5089 static bool ReadGeckoProfilingStack(JSContext* cx, unsigned argc, Value* vp) {
   5090  CallArgs args = CallArgsFromVp(argc, vp);
   5091  args.rval().setUndefined();
   5092 
   5093  // Return boolean 'false' if profiler is not enabled.
   5094  if (!cx->runtime()->geckoProfiler().enabled()) {
   5095    args.rval().setBoolean(false);
   5096    return true;
   5097  }
   5098 
   5099  // Array holding physical jit stack frames.
   5100  RootedObject stack(cx, NewDenseEmptyArray(cx));
   5101  if (!stack) {
   5102    return false;
   5103  }
   5104 
   5105  // If profiler sampling has been suppressed, return an empty
   5106  // stack.
   5107  if (!cx->isProfilerSamplingEnabled()) {
   5108    args.rval().setObject(*stack);
   5109    return true;
   5110  }
   5111 
   5112  struct InlineFrameInfo {
   5113    InlineFrameInfo(const char* kind, UniqueChars label)
   5114        : kind(kind), label(std::move(label)) {}
   5115    const char* kind;
   5116    UniqueChars label;
   5117  };
   5118 
   5119  Vector<Vector<InlineFrameInfo, 0, TempAllocPolicy>, 0, TempAllocPolicy>
   5120      frameInfo(cx);
   5121 
   5122  JS::ProfilingFrameIterator::RegisterState state;
   5123  for (JS::ProfilingFrameIterator i(cx, state); !i.done(); ++i) {
   5124    MOZ_ASSERT(i.stackAddress() != nullptr);
   5125 
   5126    if (!frameInfo.emplaceBack(cx)) {
   5127      return false;
   5128    }
   5129 
   5130    const size_t MaxInlineFrames = 16;
   5131    JS::ProfilingFrameIterator::Frame frames[MaxInlineFrames];
   5132    uint32_t nframes = i.extractStack(frames, 0, MaxInlineFrames);
   5133    MOZ_ASSERT(nframes <= MaxInlineFrames);
   5134    for (uint32_t i = 0; i < nframes; i++) {
   5135      const char* frameKindStr = nullptr;
   5136      switch (frames[i].kind) {
   5137        case JS::ProfilingFrameIterator::Frame_BaselineInterpreter:
   5138          frameKindStr = "baseline-interpreter";
   5139          break;
   5140        case JS::ProfilingFrameIterator::Frame_Baseline:
   5141          frameKindStr = "baseline-jit";
   5142          break;
   5143        case JS::ProfilingFrameIterator::Frame_Ion:
   5144          frameKindStr = "ion";
   5145          break;
   5146        case JS::ProfilingFrameIterator::Frame_WasmBaseline:
   5147        case JS::ProfilingFrameIterator::Frame_WasmIon:
   5148        case JS::ProfilingFrameIterator::Frame_WasmOther:
   5149          frameKindStr = "wasm";
   5150          break;
   5151        default:
   5152          frameKindStr = "unknown";
   5153      }
   5154 
   5155      UniqueChars label =
   5156          DuplicateStringToArena(js::StringBufferArena, cx, frames[i].label);
   5157      if (!label) {
   5158        return false;
   5159      }
   5160 
   5161      if (!frameInfo.back().emplaceBack(frameKindStr, std::move(label))) {
   5162        return false;
   5163      }
   5164    }
   5165  }
   5166 
   5167  RootedObject inlineFrameInfo(cx);
   5168  RootedString frameKind(cx);
   5169  RootedString frameLabel(cx);
   5170  RootedId idx(cx);
   5171 
   5172  const unsigned propAttrs = JSPROP_ENUMERATE;
   5173 
   5174  uint32_t physicalFrameNo = 0;
   5175  for (auto& frame : frameInfo) {
   5176    // Array holding all inline frames in a single physical jit stack frame.
   5177    RootedObject inlineStack(cx, NewDenseEmptyArray(cx));
   5178    if (!inlineStack) {
   5179      return false;
   5180    }
   5181 
   5182    uint32_t inlineFrameNo = 0;
   5183    for (auto& inlineFrame : frame) {
   5184      // Object holding frame info.
   5185      RootedObject inlineFrameInfo(cx, NewPlainObject(cx));
   5186      if (!inlineFrameInfo) {
   5187        return false;
   5188      }
   5189 
   5190      frameKind = NewStringCopyZ<CanGC>(cx, inlineFrame.kind);
   5191      if (!frameKind) {
   5192        return false;
   5193      }
   5194 
   5195      if (!JS_DefineProperty(cx, inlineFrameInfo, "kind", frameKind,
   5196                             propAttrs)) {
   5197        return false;
   5198      }
   5199 
   5200      frameLabel = NewLatin1StringZ(cx, std::move(inlineFrame.label));
   5201      if (!frameLabel) {
   5202        return false;
   5203      }
   5204 
   5205      if (!JS_DefineProperty(cx, inlineFrameInfo, "label", frameLabel,
   5206                             propAttrs)) {
   5207        return false;
   5208      }
   5209 
   5210      idx = PropertyKey::Int(inlineFrameNo);
   5211      if (!JS_DefinePropertyById(cx, inlineStack, idx, inlineFrameInfo, 0)) {
   5212        return false;
   5213      }
   5214 
   5215      ++inlineFrameNo;
   5216    }
   5217 
   5218    // Push inline array into main array.
   5219    idx = PropertyKey::Int(physicalFrameNo);
   5220    if (!JS_DefinePropertyById(cx, stack, idx, inlineStack, 0)) {
   5221      return false;
   5222    }
   5223 
   5224    ++physicalFrameNo;
   5225  }
   5226 
   5227  args.rval().setObject(*stack);
   5228  return true;
   5229 }
   5230 
   5231 static bool ReadGeckoInterpProfilingStack(JSContext* cx, unsigned argc,
   5232                                          Value* vp) {
   5233  CallArgs args = CallArgsFromVp(argc, vp);
   5234  args.rval().setUndefined();
   5235 
   5236  // Return boolean 'false' if profiler is not enabled.
   5237  if (!cx->runtime()->geckoProfiler().enabled()) {
   5238    args.rval().setBoolean(false);
   5239    return true;
   5240  }
   5241 
   5242  // Array with information about each frame.
   5243  Rooted<JSObject*> stack(cx, NewDenseEmptyArray(cx));
   5244  if (!stack) {
   5245    return false;
   5246  }
   5247  uint32_t stackIndex = 0;
   5248 
   5249  ProfilingStack* profStack = cx->geckoProfiler().getProfilingStack();
   5250  MOZ_ASSERT(profStack);
   5251 
   5252  for (size_t i = 0; i < profStack->stackSize(); i++) {
   5253    const auto& frame = profStack->frames[i];
   5254    if (!frame.isJsFrame()) {
   5255      continue;
   5256    }
   5257 
   5258    // Skip fake JS frame pushed for js::RunScript by GeckoProfilerEntryMarker.
   5259    const char* dynamicStr = frame.dynamicString();
   5260    if (!dynamicStr) {
   5261      continue;
   5262    }
   5263 
   5264    Rooted<PlainObject*> frameInfo(cx, NewPlainObject(cx));
   5265    if (!frameInfo) {
   5266      return false;
   5267    }
   5268 
   5269    Rooted<JSString*> dynamicString(
   5270        cx, JS_NewStringCopyUTF8Z(
   5271                cx, JS::ConstUTF8CharsZ(dynamicStr, strlen(dynamicStr))));
   5272    if (!dynamicString) {
   5273      return false;
   5274    }
   5275    if (!JS_DefineProperty(cx, frameInfo, "dynamicString", dynamicString,
   5276                           JSPROP_ENUMERATE)) {
   5277      return false;
   5278    }
   5279 
   5280    if (!JS_DefineElement(cx, stack, stackIndex, frameInfo, JSPROP_ENUMERATE)) {
   5281      return false;
   5282    }
   5283    stackIndex++;
   5284  }
   5285 
   5286  args.rval().setObject(*stack);
   5287  return true;
   5288 }
   5289 
   5290 static bool EnableOsiPointRegisterChecks(JSContext*, unsigned argc, Value* vp) {
   5291  CallArgs args = CallArgsFromVp(argc, vp);
   5292 #ifdef CHECK_OSIPOINT_REGISTERS
   5293  jit::JitOptions.checkOsiPointRegisters = true;
   5294 #endif
   5295  args.rval().setUndefined();
   5296  return true;
   5297 }
   5298 
   5299 static bool DisplayName(JSContext* cx, unsigned argc, Value* vp) {
   5300  CallArgs args = CallArgsFromVp(argc, vp);
   5301  if (!args.get(0).isObject() || !args[0].toObject().is<JSFunction>()) {
   5302    RootedObject arg(cx, &args.callee());
   5303    ReportUsageErrorASCII(cx, arg, "Must have one function argument");
   5304    return false;
   5305  }
   5306 
   5307  JSFunction* fun = &args[0].toObject().as<JSFunction>();
   5308  JS::Rooted<JSAtom*> str(cx);
   5309  if (!fun->getDisplayAtom(cx, &str)) {
   5310    return false;
   5311  }
   5312  args.rval().setString(str ? str : cx->runtime()->emptyString.ref());
   5313  return true;
   5314 }
   5315 
   5316 static bool IsAvxPresent(JSContext* cx, unsigned argc, Value* vp) {
   5317  CallArgs args = CallArgsFromVp(argc, vp);
   5318 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
   5319  int minVersion = 1;
   5320  if (argc > 0 && args.get(0).isNumber()) {
   5321    minVersion = std::max(1, int(args[0].toNumber()));
   5322  }
   5323  switch (minVersion) {
   5324    case 1:
   5325      args.rval().setBoolean(jit::Assembler::HasAVX());
   5326      return true;
   5327    case 2:
   5328      args.rval().setBoolean(jit::Assembler::HasAVX2());
   5329      return true;
   5330  }
   5331 #endif
   5332  args.rval().setBoolean(false);
   5333  return true;
   5334 }
   5335 
   5336 class ShellAllocationMetadataBuilder : public AllocationMetadataBuilder {
   5337 public:
   5338  ShellAllocationMetadataBuilder() = default;
   5339 
   5340  virtual JSObject* build(JSContext* cx, HandleObject,
   5341                          AutoEnterOOMUnsafeRegion& oomUnsafe) const override;
   5342 
   5343  static const ShellAllocationMetadataBuilder metadataBuilder;
   5344 };
   5345 
   5346 JSObject* ShellAllocationMetadataBuilder::build(
   5347    JSContext* cx, HandleObject, AutoEnterOOMUnsafeRegion& oomUnsafe) const {
   5348  RootedObject obj(cx, NewPlainObject(cx));
   5349  if (!obj) {
   5350    oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
   5351  }
   5352 
   5353  RootedObject stack(cx, NewDenseEmptyArray(cx));
   5354  if (!stack) {
   5355    oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
   5356  }
   5357 
   5358  static int createdIndex = 0;
   5359  createdIndex++;
   5360 
   5361  if (!JS_DefineProperty(cx, obj, "index", createdIndex, 0)) {
   5362    oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
   5363  }
   5364 
   5365  if (!JS_DefineProperty(cx, obj, "stack", stack, 0)) {
   5366    oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
   5367  }
   5368 
   5369  int stackIndex = 0;
   5370  RootedId id(cx);
   5371  RootedValue callee(cx);
   5372  for (NonBuiltinScriptFrameIter iter(cx); !iter.done(); ++iter) {
   5373    if (iter.isFunctionFrame() && iter.compartment() == cx->compartment()) {
   5374      id = PropertyKey::Int(stackIndex);
   5375      RootedObject callee(cx, iter.callee(cx));
   5376      if (!JS_DefinePropertyById(cx, stack, id, callee, JSPROP_ENUMERATE)) {
   5377        oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
   5378      }
   5379      stackIndex++;
   5380    }
   5381  }
   5382 
   5383  return obj;
   5384 }
   5385 
   5386 const ShellAllocationMetadataBuilder
   5387    ShellAllocationMetadataBuilder::metadataBuilder;
   5388 
   5389 static bool EnableShellAllocationMetadataBuilder(JSContext* cx, unsigned argc,
   5390                                                 Value* vp) {
   5391  CallArgs args = CallArgsFromVp(argc, vp);
   5392 
   5393  SetAllocationMetadataBuilder(
   5394      cx, &ShellAllocationMetadataBuilder::metadataBuilder);
   5395 
   5396  args.rval().setUndefined();
   5397  return true;
   5398 }
   5399 
   5400 static bool GetAllocationMetadata(JSContext* cx, unsigned argc, Value* vp) {
   5401  CallArgs args = CallArgsFromVp(argc, vp);
   5402  if (args.length() != 1 || !args[0].isObject()) {
   5403    JS_ReportErrorASCII(cx, "Argument must be an object");
   5404    return false;
   5405  }
   5406 
   5407  args.rval().setObjectOrNull(GetAllocationMetadata(&args[0].toObject()));
   5408  return true;
   5409 }
   5410 
   5411 static bool testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp) {
   5412  CallArgs args = CallArgsFromVp(argc, vp);
   5413 
   5414  // NOP when not in IonMonkey
   5415  args.rval().setUndefined();
   5416  return true;
   5417 }
   5418 
   5419 static bool testingFunc_bailAfter(JSContext* cx, unsigned argc, Value* vp) {
   5420  CallArgs args = CallArgsFromVp(argc, vp);
   5421  if (args.length() != 1 || !args[0].isInt32() || args[0].toInt32() < 0) {
   5422    JS_ReportErrorASCII(
   5423        cx, "Argument must be a positive number that fits in an int32");
   5424    return false;
   5425  }
   5426 
   5427 #ifdef DEBUG
   5428  if (auto* jitRuntime = cx->runtime()->jitRuntime()) {
   5429    uint32_t bailAfter = args[0].toInt32();
   5430    bool enableBailAfter = bailAfter > 0;
   5431    if (jitRuntime->ionBailAfterEnabled() != enableBailAfter) {
   5432      // Force JIT code to be recompiled with (or without) instrumentation.
   5433      ReleaseAllJITCode(cx->gcContext());
   5434      jitRuntime->setIonBailAfterEnabled(enableBailAfter);
   5435    }
   5436    jitRuntime->setIonBailAfterCounter(bailAfter);
   5437  }
   5438 #endif
   5439 
   5440  args.rval().setUndefined();
   5441  return true;
   5442 }
   5443 
   5444 static bool testingFunc_invalidate(JSContext* cx, unsigned argc, Value* vp) {
   5445  CallArgs args = CallArgsFromVp(argc, vp);
   5446 
   5447  // If the topmost frame is Ion/Warp, find the IonScript and invalidate it.
   5448  FrameIter iter(cx);
   5449  if (!iter.done() && iter.isIon()) {
   5450    while (!iter.isPhysicalJitFrame()) {
   5451      ++iter;
   5452    }
   5453    if (iter.script()->hasIonScript()) {
   5454      js::jit::Invalidate(cx, iter.script());
   5455    }
   5456  }
   5457 
   5458  args.rval().setUndefined();
   5459  return true;
   5460 }
   5461 
   5462 static constexpr unsigned JitWarmupResetLimit = 20;
   5463 static_assert(JitWarmupResetLimit <=
   5464                  unsigned(JSScript::MutableFlags::WarmupResets_MASK),
   5465              "JitWarmupResetLimit exceeds max value");
   5466 
   5467 static bool testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp) {
   5468  CallArgs args = CallArgsFromVp(argc, vp);
   5469 
   5470  if (!jit::IsBaselineJitEnabled(cx)) {
   5471    return ReturnStringCopy(cx, args, "Baseline is disabled.");
   5472  }
   5473 
   5474  // Use frame iterator to inspect caller.
   5475  FrameIter iter(cx);
   5476 
   5477  // We may be invoked directly, not in a JS context, e.g. if inJit is added as
   5478  // a callback on the event queue.
   5479  if (iter.done()) {
   5480    args.rval().setBoolean(false);
   5481    return true;
   5482  }
   5483 
   5484  if (iter.hasScript()) {
   5485    // Detect repeated attempts to compile, resetting the counter if inJit
   5486    // succeeds. Note: This script may have be inlined into its caller.
   5487    if (iter.isJSJit()) {
   5488      iter.script()->resetWarmUpResetCounter();
   5489    } else if (iter.script()->getWarmUpResetCount() >= JitWarmupResetLimit) {
   5490      return ReturnStringCopy(
   5491          cx, args, "Compilation is being repeatedly prevented. Giving up.");
   5492    }
   5493  }
   5494 
   5495  // Returns true for any JIT (including WASM).
   5496  MOZ_ASSERT_IF(iter.isJSJit(), cx->currentlyRunningInJit());
   5497  args.rval().setBoolean(cx->currentlyRunningInJit());
   5498  return true;
   5499 }
   5500 
   5501 static bool testingFunc_inIon(JSContext* cx, unsigned argc, Value* vp) {
   5502  CallArgs args = CallArgsFromVp(argc, vp);
   5503 
   5504  if (!jit::IsIonEnabled(cx)) {
   5505    return ReturnStringCopy(cx, args, "Ion is disabled.");
   5506  }
   5507 
   5508  // Use frame iterator to inspect caller.
   5509  FrameIter iter(cx);
   5510 
   5511  // We may be invoked directly, not in a JS context, e.g. if inIon is added as
   5512  // a callback on the event queue.
   5513  if (iter.done()) {
   5514    args.rval().setBoolean(false);
   5515    return true;
   5516  }
   5517 
   5518  if (iter.hasScript()) {
   5519    // Detect repeated attempts to compile, resetting the counter if inIon
   5520    // succeeds. Note: This script may have be inlined into its caller.
   5521    if (iter.isIon()) {
   5522      iter.script()->resetWarmUpResetCounter();
   5523    } else if (!iter.script()->canIonCompile()) {
   5524      return ReturnStringCopy(cx, args, "Unable to Ion-compile this script.");
   5525    } else if (iter.script()->getWarmUpResetCount() >= JitWarmupResetLimit) {
   5526      return ReturnStringCopy(
   5527          cx, args, "Compilation is being repeatedly prevented. Giving up.");
   5528    }
   5529  }
   5530 
   5531  args.rval().setBoolean(iter.isIon());
   5532  return true;
   5533 }
   5534 
   5535 bool js::testingFunc_assertFloat32(JSContext* cx, unsigned argc, Value* vp) {
   5536  CallArgs args = CallArgsFromVp(argc, vp);
   5537  if (args.length() != 2) {
   5538    JS_ReportErrorASCII(cx, "Expects only 2 arguments");
   5539    return false;
   5540  }
   5541 
   5542  // NOP when not in IonMonkey
   5543  args.rval().setUndefined();
   5544  return true;
   5545 }
   5546 
   5547 static bool TestingFunc_assertJitStackInvariants(JSContext* cx, unsigned argc,
   5548                                                 Value* vp) {
   5549  CallArgs args = CallArgsFromVp(argc, vp);
   5550 
   5551  jit::AssertJitStackInvariants(cx);
   5552  args.rval().setUndefined();
   5553  return true;
   5554 }
   5555 
   5556 bool js::testingFunc_assertRecoveredOnBailout(JSContext* cx, unsigned argc,
   5557                                              Value* vp) {
   5558  CallArgs args = CallArgsFromVp(argc, vp);
   5559  if (args.length() != 2) {
   5560    JS_ReportErrorASCII(cx, "Expects only 2 arguments");
   5561    return false;
   5562  }
   5563 
   5564  // NOP when not in IonMonkey
   5565  args.rval().setUndefined();
   5566  return true;
   5567 }
   5568 
   5569 static bool GetJitCompilerOptions(JSContext* cx, unsigned argc, Value* vp) {
   5570  CallArgs args = CallArgsFromVp(argc, vp);
   5571  RootedObject info(cx, JS_NewPlainObject(cx));
   5572  if (!info) {
   5573    return false;
   5574  }
   5575 
   5576  uint32_t intValue = 0;
   5577  RootedValue value(cx);
   5578 
   5579 #define JIT_COMPILER_MATCH(key, string)                         \
   5580  opt = JSJITCOMPILER_##key;                                    \
   5581  if (JS_GetGlobalJitCompilerOption(cx, opt, &intValue)) {      \
   5582    value.setInt32(intValue);                                   \
   5583    if (!JS_SetProperty(cx, info, string, value)) return false; \
   5584  }
   5585 
   5586  JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION;
   5587  JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH);
   5588 #undef JIT_COMPILER_MATCH
   5589 
   5590  args.rval().setObject(*info);
   5591  return true;
   5592 }
   5593 
   5594 static bool SetIonCheckGraphCoherency(JSContext* cx, unsigned argc, Value* vp) {
   5595  CallArgs args = CallArgsFromVp(argc, vp);
   5596  jit::JitOptions.checkGraphConsistency = ToBoolean(args.get(0));
   5597  args.rval().setUndefined();
   5598  return true;
   5599 }
   5600 
   5601 // A JSObject that holds structured clone data, similar to the C++ class
   5602 // JSAutoStructuredCloneBuffer.
   5603 class CloneBufferObject : public NativeObject {
   5604  static const JSPropertySpec props_[3];
   5605 
   5606  static const size_t DATA_SLOT = 0;
   5607  static const size_t SYNTHETIC_SLOT = 1;
   5608  static const size_t NUM_SLOTS = 2;
   5609 
   5610 public:
   5611  static const JSClass class_;
   5612 
   5613  static CloneBufferObject* Create(JSContext* cx) {
   5614    RootedObject obj(cx, JS_NewObject(cx, &class_));
   5615    if (!obj) {
   5616      return nullptr;
   5617    }
   5618    obj->as<CloneBufferObject>().setReservedSlot(DATA_SLOT,
   5619                                                 PrivateValue(nullptr));
   5620    obj->as<CloneBufferObject>().setReservedSlot(SYNTHETIC_SLOT,
   5621                                                 BooleanValue(false));
   5622 
   5623    if (!JS_DefineProperties(cx, obj, props_)) {
   5624      return nullptr;
   5625    }
   5626 
   5627    return &obj->as<CloneBufferObject>();
   5628  }
   5629 
   5630  static CloneBufferObject* Create(JSContext* cx,
   5631                                   JSAutoStructuredCloneBuffer* buffer) {
   5632    Rooted<CloneBufferObject*> obj(cx, Create(cx));
   5633    if (!obj) {
   5634      return nullptr;
   5635    }
   5636    auto data = js::MakeUnique<JSStructuredCloneData>(buffer->scope());
   5637    if (!data) {
   5638      ReportOutOfMemory(cx);
   5639      return nullptr;
   5640    }
   5641    buffer->giveTo(data.get());
   5642    obj->setData(data.release(), false);
   5643    return obj;
   5644  }
   5645 
   5646  JSStructuredCloneData* data() const {
   5647    return static_cast<JSStructuredCloneData*>(
   5648        getReservedSlot(DATA_SLOT).toPrivate());
   5649  }
   5650 
   5651  bool isSynthetic() const {
   5652    return getReservedSlot(SYNTHETIC_SLOT).toBoolean();
   5653  }
   5654 
   5655  void setData(JSStructuredCloneData* aData, bool synthetic) {
   5656    MOZ_ASSERT(!data());
   5657    setReservedSlot(DATA_SLOT, PrivateValue(aData));
   5658    setReservedSlot(SYNTHETIC_SLOT, BooleanValue(synthetic));
   5659  }
   5660 
   5661  // Discard an owned clone buffer.
   5662  void discard() {
   5663    js_delete(data());
   5664    setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
   5665  }
   5666 
   5667  static bool setCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
   5668    Rooted<CloneBufferObject*> obj(
   5669        cx, &args.thisv().toObject().as<CloneBufferObject>());
   5670 
   5671    const char* data = nullptr;
   5672    UniqueChars dataOwner;
   5673    size_t nbytes;
   5674 
   5675    if (args.get(0).isObject() && args[0].toObject().is<ArrayBufferObject>()) {
   5676      ArrayBufferObject* buffer = &args[0].toObject().as<ArrayBufferObject>();
   5677      bool isSharedMemory;
   5678      uint8_t* dataBytes = nullptr;
   5679      JS::GetArrayBufferLengthAndData(buffer, &nbytes, &isSharedMemory,
   5680                                      &dataBytes);
   5681      MOZ_ASSERT(!isSharedMemory);
   5682      data = reinterpret_cast<char*>(dataBytes);
   5683    } else {
   5684      JSString* str = JS::ToString(cx, args.get(0));
   5685      if (!str) {
   5686        return false;
   5687      }
   5688      dataOwner = JS_EncodeStringToLatin1(cx, str);
   5689      if (!dataOwner) {
   5690        return false;
   5691      }
   5692      data = dataOwner.get();
   5693      nbytes = JS_GetStringLength(str);
   5694    }
   5695 
   5696    if (nbytes == 0 || (nbytes % sizeof(uint64_t) != 0)) {
   5697      JS_ReportErrorASCII(cx, "Invalid length for clonebuffer data");
   5698      return false;
   5699    }
   5700 
   5701    auto buf = js::MakeUnique<JSStructuredCloneData>(
   5702        JS::StructuredCloneScope::DifferentProcess);
   5703    if (!buf || !buf->Init(nbytes)) {
   5704      ReportOutOfMemory(cx);
   5705      return false;
   5706    }
   5707 
   5708    MOZ_ALWAYS_TRUE(buf->AppendBytes(data, nbytes));
   5709    obj->discard();
   5710    obj->setData(buf.release(), true);
   5711 
   5712    args.rval().setUndefined();
   5713    return true;
   5714  }
   5715 
   5716  static bool is(HandleValue v) {
   5717    return v.isObject() && v.toObject().is<CloneBufferObject>();
   5718  }
   5719 
   5720  static bool setCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
   5721    CallArgs args = CallArgsFromVp(argc, vp);
   5722    return CallNonGenericMethod<is, setCloneBuffer_impl>(cx, args);
   5723  }
   5724 
   5725  static bool getData(JSContext* cx, Handle<CloneBufferObject*> obj,
   5726                      JSStructuredCloneData** data) {
   5727    if (!obj->data()) {
   5728      *data = nullptr;
   5729      return true;
   5730    }
   5731 
   5732    bool hasTransferable;
   5733    if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable)) {
   5734      return false;
   5735    }
   5736 
   5737    if (hasTransferable) {
   5738      JS_ReportErrorASCII(
   5739          cx, "cannot retrieve structured clone buffer with transferables");
   5740      return false;
   5741    }
   5742 
   5743    *data = obj->data();
   5744    return true;
   5745  }
   5746 
   5747  static bool getCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
   5748    Rooted<CloneBufferObject*> obj(
   5749        cx, &args.thisv().toObject().as<CloneBufferObject>());
   5750    if (args.length() != 0) {
   5751      RootedObject callee(cx, &args.callee());
   5752      ReportUsageErrorASCII(cx, callee, "Too many arguments");
   5753      return false;
   5754    }
   5755 
   5756    JSStructuredCloneData* data;
   5757    if (!getData(cx, obj, &data)) {
   5758      return false;
   5759    }
   5760 
   5761    if (data == nullptr) {
   5762      args.rval().setUndefined();
   5763      return true;
   5764    }
   5765 
   5766    size_t size = data->Size();
   5767    UniqueChars buffer(js_pod_malloc<char>(size));
   5768    if (!buffer) {
   5769      ReportOutOfMemory(cx);
   5770      return false;
   5771    }
   5772    auto iter = data->Start();
   5773    if (!data->ReadBytes(iter, buffer.get(), size)) {
   5774      ReportOutOfMemory(cx);
   5775      return false;
   5776    }
   5777    JSString* str = JS_NewStringCopyN(cx, buffer.get(), size);
   5778    if (!str) {
   5779      return false;
   5780    }
   5781    args.rval().setString(str);
   5782    return true;
   5783  }
   5784 
   5785  static bool getCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
   5786    CallArgs args = CallArgsFromVp(argc, vp);
   5787    return CallNonGenericMethod<is, getCloneBuffer_impl>(cx, args);
   5788  }
   5789 
   5790  static bool getCloneBufferAsArrayBuffer_impl(JSContext* cx,
   5791                                               const CallArgs& args) {
   5792    Rooted<CloneBufferObject*> obj(
   5793        cx, &args.thisv().toObject().as<CloneBufferObject>());
   5794    if (args.length() != 0) {
   5795      RootedObject callee(cx, &args.callee());
   5796      ReportUsageErrorASCII(cx, callee, "Too many arguments");
   5797      return false;
   5798    }
   5799 
   5800    JSStructuredCloneData* data;
   5801    if (!getData(cx, obj, &data)) {
   5802      return false;
   5803    }
   5804 
   5805    if (data == nullptr) {
   5806      args.rval().setUndefined();
   5807      return true;
   5808    }
   5809 
   5810    size_t size = data->Size();
   5811    UniqueChars buffer(js_pod_malloc<char>(size));
   5812    if (!buffer) {
   5813      ReportOutOfMemory(cx);
   5814      return false;
   5815    }
   5816    auto iter = data->Start();
   5817    if (!data->ReadBytes(iter, buffer.get(), size)) {
   5818      ReportOutOfMemory(cx);
   5819      return false;
   5820    }
   5821 
   5822    JSObject* arrayBuffer =
   5823        JS::NewArrayBufferWithContents(cx, size, std::move(buffer));
   5824    if (!arrayBuffer) {
   5825      return false;
   5826    }
   5827 
   5828    args.rval().setObject(*arrayBuffer);
   5829    return true;
   5830  }
   5831 
   5832  static bool getCloneBufferAsArrayBuffer(JSContext* cx, unsigned int argc,
   5833                                          JS::Value* vp) {
   5834    CallArgs args = CallArgsFromVp(argc, vp);
   5835    return CallNonGenericMethod<is, getCloneBufferAsArrayBuffer_impl>(cx, args);
   5836  }
   5837 
   5838  static void Finalize(JS::GCContext* gcx, JSObject* obj) {
   5839    obj->as<CloneBufferObject>().discard();
   5840  }
   5841 };
   5842 
   5843 static const JSClassOps CloneBufferObjectClassOps = {
   5844    nullptr,                      // addProperty
   5845    nullptr,                      // delProperty
   5846    nullptr,                      // enumerate
   5847    nullptr,                      // newEnumerate
   5848    nullptr,                      // resolve
   5849    nullptr,                      // mayResolve
   5850    CloneBufferObject::Finalize,  // finalize
   5851    nullptr,                      // call
   5852    nullptr,                      // construct
   5853    nullptr,                      // trace
   5854 };
   5855 
   5856 const JSClass CloneBufferObject::class_ = {
   5857    "CloneBuffer",
   5858    JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS) |
   5859        JSCLASS_FOREGROUND_FINALIZE,
   5860    &CloneBufferObjectClassOps,
   5861 };
   5862 
   5863 const JSPropertySpec CloneBufferObject::props_[] = {
   5864    JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0),
   5865    JS_PSGS("arraybuffer", getCloneBufferAsArrayBuffer, setCloneBuffer, 0),
   5866    JS_PS_END,
   5867 };
   5868 
   5869 static mozilla::Maybe<JS::StructuredCloneScope> ParseCloneScope(
   5870    JSContext* cx, HandleString str) {
   5871  mozilla::Maybe<JS::StructuredCloneScope> scope;
   5872 
   5873  JSLinearString* scopeStr = str->ensureLinear(cx);
   5874  if (!scopeStr) {
   5875    return scope;
   5876  }
   5877 
   5878  if (StringEqualsLiteral(scopeStr, "SameProcess")) {
   5879    scope.emplace(JS::StructuredCloneScope::SameProcess);
   5880  } else if (StringEqualsLiteral(scopeStr, "DifferentProcess")) {
   5881    scope.emplace(JS::StructuredCloneScope::DifferentProcess);
   5882  } else if (StringEqualsLiteral(scopeStr, "DifferentProcessForIndexedDB")) {
   5883    scope.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB);
   5884  }
   5885 
   5886  return scope;
   5887 }
   5888 
   5889 // A custom object that is serializable and transferable using
   5890 // the engine's custom hooks. The callbacks log their activity
   5891 // to a JSRuntime-wide log (tagging actions with IDs to distinguish them).
   5892 class CustomSerializableObject : public NativeObject {
   5893  static const size_t ID_SLOT = 0;
   5894  static const size_t DETACHED_SLOT = 1;
   5895  static const size_t BEHAVIOR_SLOT = 2;
   5896  static const size_t NUM_SLOTS = 3;
   5897 
   5898  static constexpr size_t MAX_LOG_LEN = 100;
   5899 
   5900  // The activity log should be specific to a JSRuntime.
   5901  struct ActivityLog {
   5902    uint32_t buffer[MAX_LOG_LEN];
   5903    size_t length = 0;
   5904 
   5905    static MOZ_THREAD_LOCAL(ActivityLog*) self;
   5906    static ActivityLog* getThreadLog() {
   5907      if (!self.initialized() || !self.get()) {
   5908        self.infallibleInit();
   5909        AutoEnterOOMUnsafeRegion oomUnsafe;
   5910        self.set(js_new<ActivityLog>());
   5911        if (!self.get()) {
   5912          oomUnsafe.crash("allocating activity log");
   5913        }
   5914        if (!TlsContext.get()->runtime()->atExit(
   5915                [](void* vpData) {
   5916                  auto* log = static_cast<ActivityLog*>(vpData);
   5917                  js_delete(log);
   5918                },
   5919                self.get())) {
   5920          oomUnsafe.crash("atExit");
   5921        }
   5922      }
   5923      return self.get();
   5924    }
   5925 
   5926    static bool log(int32_t id, char action) {
   5927      return getThreadLog()->logImpl(id, action);
   5928    }
   5929 
   5930    bool logImpl(int32_t id, char action) {
   5931      if (length + 2 > MAX_LOG_LEN) {
   5932        return false;
   5933      }
   5934      buffer[length++] = id;
   5935      buffer[length++] = uint32_t(action);
   5936      return true;
   5937    }
   5938  };
   5939 
   5940 public:
   5941  enum class Behavior {
   5942    Nothing = 0,
   5943    FailDuringReadTransfer = 1,
   5944    FailDuringRead = 2
   5945  };
   5946 
   5947  static constexpr JSClass class_ = {
   5948      "CustomSerializable",
   5949      JSCLASS_HAS_RESERVED_SLOTS(NUM_SLOTS),
   5950  };
   5951 
   5952  static bool is(HandleValue v) {
   5953    return v.isObject() && v.toObject().is<CustomSerializableObject>();
   5954  }
   5955 
   5956  static CustomSerializableObject* Create(JSContext* cx, int32_t id,
   5957                                          Behavior behavior) {
   5958    Rooted<CustomSerializableObject*> obj(
   5959        cx, static_cast<CustomSerializableObject*>(JS_NewObject(cx, &class_)));
   5960    if (!obj) {
   5961      return nullptr;
   5962    }
   5963    obj->setReservedSlot(ID_SLOT, Int32Value(id));
   5964    obj->setReservedSlot(DETACHED_SLOT, BooleanValue(false));
   5965    obj->setReservedSlot(BEHAVIOR_SLOT,
   5966                         Int32Value(static_cast<int32_t>(behavior)));
   5967 
   5968    if (!JS_DefineProperty(cx, obj, "log", getLog, clearLog, 0)) {
   5969      return nullptr;
   5970    }
   5971 
   5972    return obj;
   5973  }
   5974 
   5975 public:
   5976  static uint32_t tag() { return JS_SCTAG_USER_MIN; }
   5977 
   5978  static bool log(int32_t id, char action) {
   5979    return ActivityLog::log(id, action);
   5980  }
   5981  bool log(char action) {
   5982    return log(getReservedSlot(ID_SLOT).toInt32(), action);
   5983  }
   5984 
   5985  void detach() { setReservedSlot(DETACHED_SLOT, BooleanValue(true)); }
   5986  bool isDetached() { return getReservedSlot(DETACHED_SLOT).toBoolean(); }
   5987 
   5988  uint32_t id() const { return getReservedSlot(ID_SLOT).toInt32(); }
   5989  Behavior behavior() {
   5990    return static_cast<Behavior>(getReservedSlot(BEHAVIOR_SLOT).toInt32());
   5991  }
   5992 
   5993  static bool getLog(JSContext* cx, unsigned int argc, JS::Value* vp) {
   5994    CallArgs args = CallArgsFromVp(argc, vp);
   5995    return CallNonGenericMethod<is, getLog_impl>(cx, args);
   5996  }
   5997 
   5998  static bool getLog_impl(JSContext* cx, const CallArgs& args) {
   5999    Rooted<CustomSerializableObject*> obj(
   6000        cx, &args.thisv().toObject().as<CustomSerializableObject>());
   6001 
   6002    size_t len = ActivityLog::getThreadLog()->length;
   6003    uint32_t* logBuffer = ActivityLog::getThreadLog()->buffer;
   6004 
   6005    Rooted<ArrayObject*> result(cx, NewDenseFullyAllocatedArray(cx, len));
   6006    if (!result) {
   6007      return false;
   6008    }
   6009    result->ensureDenseInitializedLength(0, len);
   6010 
   6011    for (size_t p = 0; p < len; p += 2) {
   6012      int32_t id = int32_t(logBuffer[p]);
   6013      char action = char(logBuffer[p + 1]);
   6014      result->setDenseElement(p, Int32Value(id));
   6015      JSString* str = JS_NewStringCopyN(cx, &action, 1);
   6016      if (!str) {
   6017        return false;
   6018      }
   6019      result->setDenseElement(p + 1, StringValue(str));
   6020    }
   6021 
   6022    args.rval().setObject(*result);
   6023    return true;
   6024  }
   6025 
   6026  static bool clearLog(JSContext* cx, unsigned int argc, JS::Value* vp) {
   6027    CallArgs args = CallArgsFromVp(argc, vp);
   6028    if (!args.get(0).isNullOrUndefined()) {
   6029      JS_ReportErrorASCII(cx, "log may only be assigned null/undefined");
   6030      return false;
   6031    }
   6032    ActivityLog::getThreadLog()->length = 0;
   6033    args.rval().setUndefined();
   6034    return true;
   6035  }
   6036 
   6037  static bool Write(JSContext* cx, JSStructuredCloneWriter* w,
   6038                    JS::HandleObject aObj, bool* sameProcessScopeRequired,
   6039                    void* closure) {
   6040    Rooted<CustomSerializableObject*> obj(cx);
   6041 
   6042    if ((obj = aObj->maybeUnwrapIf<CustomSerializableObject>())) {
   6043      obj->log('w');
   6044      // Write a regular clone as a <tag, id> pair, followed by <0, behavior>.
   6045      // Note that transferring will communicate the behavior via a different
   6046      // mechanism.
   6047      return JS_WriteUint32Pair(w, obj->tag(), obj->id()) &&
   6048             JS_WriteUint32Pair(w, 0, static_cast<uint32_t>(obj->behavior()));
   6049    }
   6050 
   6051    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   6052                              JSMSG_SC_UNSUPPORTED_TYPE);
   6053    return false;
   6054  }
   6055 
   6056  static JSObject* Read(JSContext* cx, JSStructuredCloneReader* r,
   6057                        const JS::CloneDataPolicy& cloneDataPolicy,
   6058                        uint32_t tag, uint32_t id, void* closure) {
   6059    uint32_t dummy, behaviorData;
   6060    if (!JS_ReadUint32Pair(r, &dummy, &behaviorData)) {
   6061      return nullptr;
   6062    }
   6063    if (dummy != 0 || id > INT32_MAX) {
   6064      JS_ReportErrorASCII(cx, "out of range");
   6065      return nullptr;
   6066    }
   6067 
   6068    auto b = static_cast<Behavior>(behaviorData);
   6069    Rooted<CustomSerializableObject*> obj(
   6070        cx, Create(cx, static_cast<int32_t>(id), b));
   6071    if (!obj) {
   6072      return nullptr;
   6073    }
   6074 
   6075    obj->log('r');
   6076    if (obj->behavior() == Behavior::FailDuringRead) {
   6077      JS_ReportErrorASCII(cx,
   6078                          "Failed as requested in read during deserialization");
   6079      return nullptr;
   6080    }
   6081    return obj;
   6082  }
   6083 
   6084  static bool CanTransfer(JSContext* cx, JS::Handle<JSObject*> wrapped,
   6085                          bool* sameProcessScopeRequired, void* closure) {
   6086    Rooted<CustomSerializableObject*> obj(cx);
   6087 
   6088    if ((obj = wrapped->maybeUnwrapIf<CustomSerializableObject>())) {
   6089      obj->log('?');
   6090      // For now, all CustomSerializable objects are considered to be
   6091      // transferable.
   6092      return true;
   6093    }
   6094 
   6095    return false;
   6096  }
   6097 
   6098  static bool WriteTransfer(JSContext* cx, JS::Handle<JSObject*> aObj,
   6099                            void* closure, uint32_t* tag,
   6100                            JS::TransferableOwnership* ownership,
   6101                            void** content, uint64_t* extraData) {
   6102    Rooted<CustomSerializableObject*> obj(cx);
   6103 
   6104    if ((obj = aObj->maybeUnwrapIf<CustomSerializableObject>())) {
   6105      if (obj->isDetached()) {
   6106        JS_ReportErrorASCII(cx, "Attempted to transfer detached object");
   6107        return false;
   6108      }
   6109      obj->log('W');
   6110      *content = reinterpret_cast<void*>(obj->id());
   6111      *extraData = static_cast<uint64_t>(obj->behavior());
   6112      *tag = obj->tag();
   6113      *ownership = JS::SCTAG_TMO_CUSTOM;
   6114      obj->detach();
   6115      return true;
   6116    }
   6117 
   6118    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   6119                              JSMSG_SC_NOT_TRANSFERABLE);
   6120    return false;
   6121  }
   6122 
   6123  static bool ReadTransfer(JSContext* cx, JSStructuredCloneReader* r,
   6124                           const JS::CloneDataPolicy& cloneDataPolicy,
   6125                           uint32_t tag, void* content, uint64_t extraData,
   6126                           void* closure,
   6127                           JS::MutableHandleObject returnObject) {
   6128    if (tag == CustomSerializableObject::tag()) {
   6129      int32_t id = int32_t(reinterpret_cast<uintptr_t>(content));
   6130      Rooted<CustomSerializableObject*> obj(
   6131          cx, CustomSerializableObject::Create(
   6132                  cx, id, static_cast<Behavior>(extraData)));
   6133      if (!obj) {
   6134        return false;
   6135      }
   6136      obj->log('R');
   6137      if (obj->behavior() == Behavior::FailDuringReadTransfer) {
   6138        return false;
   6139      }
   6140      returnObject.set(obj);
   6141      return true;
   6142    }
   6143 
   6144    return false;
   6145  }
   6146 
   6147  static void FreeTransfer(uint32_t tag, JS::TransferableOwnership ownership,
   6148                           void* content, uint64_t extraData, void* closure) {
   6149    CustomSerializableObject::log(uint32_t(reinterpret_cast<intptr_t>(content)),
   6150                                  'F');
   6151  }
   6152 };
   6153 
   6154 MOZ_THREAD_LOCAL(CustomSerializableObject::ActivityLog*)
   6155 CustomSerializableObject::ActivityLog::self;
   6156 
   6157 static bool MakeSerializable(JSContext* cx, unsigned argc, Value* vp) {
   6158  CallArgs args = CallArgsFromVp(argc, vp);
   6159 
   6160  int32_t id = 0;
   6161  if (args.get(0).isInt32()) {
   6162    id = args[0].toInt32();
   6163    if (id < 0) {
   6164      JS_ReportErrorASCII(cx, "id out of range");
   6165      return false;
   6166    }
   6167  }
   6168  CustomSerializableObject::Behavior behavior =
   6169      CustomSerializableObject::Behavior::Nothing;
   6170  if (args.get(1).isInt32()) {
   6171    int32_t iv = args[1].toInt32();
   6172    constexpr int32_t min =
   6173        static_cast<int32_t>(CustomSerializableObject::Behavior::Nothing);
   6174    constexpr int32_t max = static_cast<int32_t>(
   6175        CustomSerializableObject::Behavior::FailDuringRead);
   6176    if (iv < min || iv > max) {
   6177      JS_ReportErrorASCII(cx, "behavior out of range");
   6178      return false;
   6179    }
   6180    behavior = static_cast<CustomSerializableObject::Behavior>(iv);
   6181  }
   6182 
   6183  JSObject* obj = CustomSerializableObject::Create(cx, id, behavior);
   6184  if (!obj) {
   6185    return false;
   6186  }
   6187 
   6188  args.rval().setObject(*obj);
   6189  return true;
   6190 }
   6191 
   6192 static JSStructuredCloneCallbacks gCloneCallbacks = {
   6193    .read = CustomSerializableObject::Read,
   6194    .write = CustomSerializableObject::Write,
   6195    .reportError = nullptr,
   6196    .readTransfer = CustomSerializableObject::ReadTransfer,
   6197    .writeTransfer = CustomSerializableObject::WriteTransfer,
   6198    .freeTransfer = CustomSerializableObject::FreeTransfer,
   6199    .canTransfer = CustomSerializableObject::CanTransfer,
   6200    .sabCloned = nullptr};
   6201 
   6202 bool js::testingFunc_serialize(JSContext* cx, unsigned argc, Value* vp) {
   6203  CallArgs args = CallArgsFromVp(argc, vp);
   6204 
   6205  if (js::SupportDifferentialTesting()) {
   6206    RootedObject callee(cx, &args.callee());
   6207    ReportUsageErrorASCII(cx, callee,
   6208                          "Function unavailable in differential testing mode.");
   6209    return false;
   6210  }
   6211 
   6212  mozilla::Maybe<JSAutoStructuredCloneBuffer> clonebuf;
   6213  JS::CloneDataPolicy policy;
   6214 
   6215  if (!args.get(2).isUndefined()) {
   6216    RootedObject opts(cx, ToObject(cx, args.get(2)));
   6217    if (!opts) {
   6218      return false;
   6219    }
   6220 
   6221    RootedValue v(cx);
   6222    if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v)) {
   6223      return false;
   6224    }
   6225 
   6226    if (!v.isUndefined()) {
   6227      JSString* str = JS::ToString(cx, v);
   6228      if (!str) {
   6229        return false;
   6230      }
   6231      JSLinearString* poli = str->ensureLinear(cx);
   6232      if (!poli) {
   6233        return false;
   6234      }
   6235 
   6236      if (StringEqualsLiteral(poli, "allow")) {
   6237        policy.allowSharedMemoryObjects();
   6238        policy.allowIntraClusterClonableSharedObjects();
   6239      } else if (StringEqualsLiteral(poli, "deny")) {
   6240        // default
   6241      } else {
   6242        JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'");
   6243        return false;
   6244      }
   6245    }
   6246 
   6247    if (!JS_GetProperty(cx, opts, "scope", &v)) {
   6248      return false;
   6249    }
   6250 
   6251    if (!v.isUndefined()) {
   6252      RootedString str(cx, JS::ToString(cx, v));
   6253      if (!str) {
   6254        return false;
   6255      }
   6256      auto scope = ParseCloneScope(cx, str);
   6257      if (!scope) {
   6258        JS_ReportErrorASCII(cx, "Invalid structured clone scope");
   6259        return false;
   6260      }
   6261      clonebuf.emplace(*scope, &gCloneCallbacks, nullptr);
   6262    }
   6263  }
   6264 
   6265  if (!clonebuf) {
   6266    clonebuf.emplace(JS::StructuredCloneScope::SameProcess, &gCloneCallbacks,
   6267                     nullptr);
   6268  }
   6269 
   6270  if (!clonebuf->write(cx, args.get(0), args.get(1), policy)) {
   6271    return false;
   6272  }
   6273 
   6274  RootedObject obj(cx, CloneBufferObject::Create(cx, clonebuf.ptr()));
   6275  if (!obj) {
   6276    return false;
   6277  }
   6278 
   6279  args.rval().setObject(*obj);
   6280  return true;
   6281 }
   6282 
   6283 static bool Deserialize(JSContext* cx, unsigned argc, Value* vp) {
   6284  CallArgs args = CallArgsFromVp(argc, vp);
   6285 
   6286  if (js::SupportDifferentialTesting()) {
   6287    RootedObject callee(cx, &args.callee());
   6288    ReportUsageErrorASCII(cx, callee,
   6289                          "Function unavailable in differential testing mode.");
   6290    return false;
   6291  }
   6292 
   6293  if (!args.get(0).isObject() || !args[0].toObject().is<CloneBufferObject>()) {
   6294    JS_ReportErrorASCII(cx, "deserialize requires a clonebuffer argument");
   6295    return false;
   6296  }
   6297  Rooted<CloneBufferObject*> obj(cx,
   6298                                 &args[0].toObject().as<CloneBufferObject>());
   6299 
   6300  JS::CloneDataPolicy policy;
   6301 
   6302  JS::StructuredCloneScope scope =
   6303      obj->isSynthetic() ? JS::StructuredCloneScope::DifferentProcess
   6304                         : JS::StructuredCloneScope::SameProcess;
   6305  if (args.get(1).isObject()) {
   6306    RootedObject opts(cx, &args[1].toObject());
   6307    if (!opts) {
   6308      return false;
   6309    }
   6310 
   6311    RootedValue v(cx);
   6312    if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v)) {
   6313      return false;
   6314    }
   6315 
   6316    if (!v.isUndefined()) {
   6317      JSString* str = JS::ToString(cx, v);
   6318      if (!str) {
   6319        return false;
   6320      }
   6321      JSLinearString* poli = str->ensureLinear(cx);
   6322      if (!poli) {
   6323        return false;
   6324      }
   6325 
   6326      if (StringEqualsLiteral(poli, "allow")) {
   6327        policy.allowSharedMemoryObjects();
   6328        policy.allowIntraClusterClonableSharedObjects();
   6329      } else if (StringEqualsLiteral(poli, "deny")) {
   6330        // default
   6331      } else {
   6332        JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'");
   6333        return false;
   6334      }
   6335    }
   6336 
   6337    if (!JS_GetProperty(cx, opts, "scope", &v)) {
   6338      return false;
   6339    }
   6340 
   6341    if (!v.isUndefined()) {
   6342      RootedString str(cx, JS::ToString(cx, v));
   6343      if (!str) {
   6344        return false;
   6345      }
   6346      auto maybeScope = ParseCloneScope(cx, str);
   6347      if (!maybeScope) {
   6348        JS_ReportErrorASCII(cx, "Invalid structured clone scope");
   6349        return false;
   6350      }
   6351 
   6352      if (*maybeScope < scope) {
   6353        JS_ReportErrorASCII(cx,
   6354                            "Cannot use less restrictive scope "
   6355                            "than the deserialized clone buffer's scope");
   6356        return false;
   6357      }
   6358 
   6359      scope = *maybeScope;
   6360    }
   6361  }
   6362 
   6363  // Clone buffer was already consumed?
   6364  if (!obj->data()) {
   6365    JS_ReportErrorASCII(cx,
   6366                        "deserialize given invalid clone buffer "
   6367                        "(transferables already consumed?)");
   6368    return false;
   6369  }
   6370 
   6371  bool hasTransferable;
   6372  if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable)) {
   6373    return false;
   6374  }
   6375 
   6376  RootedValue deserialized(cx);
   6377  if (!JS_ReadStructuredClone(cx, *obj->data(), JS_STRUCTURED_CLONE_VERSION,
   6378                              scope, &deserialized, policy, &gCloneCallbacks,
   6379                              nullptr)) {
   6380    return false;
   6381  }
   6382  args.rval().set(deserialized);
   6383 
   6384  // Consume any clone buffer with transferables; throw an error if it is
   6385  // deserialized again.
   6386  if (hasTransferable) {
   6387    obj->discard();
   6388  }
   6389 
   6390  return true;
   6391 }
   6392 
   6393 static bool DetachArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
   6394  CallArgs args = CallArgsFromVp(argc, vp);
   6395 
   6396  if (args.length() != 1) {
   6397    JS_ReportErrorASCII(cx, "detachArrayBuffer() requires a single argument");
   6398    return false;
   6399  }
   6400 
   6401  if (!args[0].isObject()) {
   6402    JS_ReportErrorASCII(cx, "detachArrayBuffer must be passed an object");
   6403    return false;
   6404  }
   6405 
   6406  RootedObject obj(cx, &args[0].toObject());
   6407  if (!JS::DetachArrayBuffer(cx, obj)) {
   6408    return false;
   6409  }
   6410 
   6411  args.rval().setUndefined();
   6412  return true;
   6413 }
   6414 
   6415 static bool EnsureNonInline(JSContext* cx, unsigned argc, Value* vp) {
   6416  CallArgs args = CallArgsFromVp(argc, vp);
   6417  Rooted<JSObject*> callee(cx, &args.callee());
   6418 
   6419  if (!args.get(0).isObject()) {
   6420    js::ReportUsageErrorASCII(cx, callee, "Single object argument required");
   6421    return false;
   6422  }
   6423 
   6424  RootedObject obj(cx, &args[0].toObject());
   6425  if (!JS::EnsureNonInlineArrayBufferOrView(cx, obj)) {
   6426    return false;
   6427  }
   6428 
   6429  args.rval().setUndefined();
   6430  return true;
   6431 }
   6432 
   6433 static bool PinArrayBufferOrViewLength(JSContext* cx, unsigned argc,
   6434                                       Value* vp) {
   6435  CallArgs args = CallArgsFromVp(argc, vp);
   6436  Rooted<JSObject*> callee(cx, &args.callee());
   6437 
   6438  if (!args.get(0).isObject()) {
   6439    js::ReportUsageErrorASCII(
   6440        cx, callee, "ArrayBuffer or ArrayBufferView argument required");
   6441    return false;
   6442  }
   6443  RootedObject obj(cx, &args[0].toObject());
   6444  if (!obj->canUnwrapAs<ArrayBufferViewObject>() &&
   6445      !obj->canUnwrapAs<ArrayBufferObjectMaybeShared>()) {
   6446    js::ReportUsageErrorASCII(
   6447        cx, callee, "ArrayBuffer or ArrayBufferView argument required");
   6448    return false;
   6449  }
   6450 
   6451  bool pin = args.get(1).isUndefined() ? true : ToBoolean(args.get(1));
   6452 
   6453  args.rval().setBoolean(JS::PinArrayBufferOrViewLength(obj, pin));
   6454  return true;
   6455 }
   6456 
   6457 static bool JSONStringify(JSContext* cx, unsigned argc, Value* vp) {
   6458  CallArgs args = CallArgsFromVp(argc, vp);
   6459 
   6460  RootedValue value(cx, args.get(0));
   6461  RootedValue behaviorVal(cx, args.get(1));
   6462  StringifyBehavior behavior = StringifyBehavior::Normal;
   6463  if (behaviorVal.isString()) {
   6464    bool matches;
   6465 #define MATCH(name)                                                           \
   6466  if (!JS_StringEqualsLiteral(cx, behaviorVal.toString(), #name, &matches)) { \
   6467    return false;                                                             \
   6468  }                                                                           \
   6469  if (matches) {                                                              \
   6470    behavior = StringifyBehavior::name;                                       \
   6471  }
   6472    MATCH(Normal)
   6473    MATCH(FastOnly)
   6474    MATCH(SlowOnly)
   6475    MATCH(Compare)
   6476 #undef MATCH
   6477  }
   6478 
   6479  JSStringBuilder sb(cx);
   6480  if (!Stringify(cx, &value, nullptr, UndefinedValue(), sb, behavior)) {
   6481    return false;
   6482  }
   6483 
   6484  if (!sb.empty()) {
   6485    JSString* str = sb.finishString();
   6486    if (!str) {
   6487      return false;
   6488    }
   6489    args.rval().setString(str);
   6490  } else {
   6491    args.rval().setUndefined();
   6492  }
   6493 
   6494  return true;
   6495 }
   6496 
   6497 static bool HelperThreadCount(JSContext* cx, unsigned argc, Value* vp) {
   6498  CallArgs args = CallArgsFromVp(argc, vp);
   6499 
   6500  if (js::SupportDifferentialTesting()) {
   6501    // Always return 0 to get consistent output with and without --no-threads.
   6502    args.rval().setInt32(0);
   6503    return true;
   6504  }
   6505 
   6506  if (CanUseExtraThreads()) {
   6507    args.rval().setInt32(GetHelperThreadCount());
   6508  } else {
   6509    args.rval().setInt32(0);
   6510  }
   6511  return true;
   6512 }
   6513 
   6514 static bool EnableShapeConsistencyChecks(JSContext* cx, unsigned argc,
   6515                                         Value* vp) {
   6516  CallArgs args = CallArgsFromVp(argc, vp);
   6517 #ifdef DEBUG
   6518  NativeObject::enableShapeConsistencyChecks();
   6519 #endif
   6520  args.rval().setUndefined();
   6521  return true;
   6522 }
   6523 
   6524 // ShapeSnapshot holds information about an object's properties. This is used
   6525 // for checking object and shape changes between two points in time.
   6526 class ShapeSnapshot {
   6527  HeapPtr<JSObject*> object_;
   6528  HeapPtr<Shape*> shape_;
   6529  HeapPtr<BaseShape*> baseShape_;
   6530  ObjectFlags objectFlags_;
   6531 
   6532  GCVector<HeapPtr<Value>, 8> slots_;
   6533 
   6534  struct PropertySnapshot {
   6535    HeapPtr<PropMap*> propMap;
   6536    uint32_t propMapIndex;
   6537    HeapPtr<PropertyKey> key;
   6538    PropertyInfo prop;
   6539 
   6540    explicit PropertySnapshot(PropMap* map, uint32_t index)
   6541        : propMap(map),
   6542          propMapIndex(index),
   6543          key(map->getKey(index)),
   6544          prop(map->getPropertyInfo(index)) {}
   6545 
   6546    void trace(JSTracer* trc) {
   6547      TraceEdge(trc, &propMap, "propMap");
   6548      TraceEdge(trc, &key, "key");
   6549    }
   6550 
   6551    bool operator==(const PropertySnapshot& other) const {
   6552      return propMap == other.propMap && propMapIndex == other.propMapIndex &&
   6553             key == other.key && prop == other.prop;
   6554    }
   6555    bool operator!=(const PropertySnapshot& other) const {
   6556      return !operator==(other);
   6557    }
   6558  };
   6559  GCVector<PropertySnapshot, 8> properties_;
   6560 
   6561 public:
   6562  explicit ShapeSnapshot(JSContext* cx) : slots_(cx), properties_(cx) {}
   6563  void checkSelf(JSContext* cx) const;
   6564  void check(JSContext* cx, const ShapeSnapshot& other) const;
   6565  bool init(JSObject* obj);
   6566  void trace(JSTracer* trc);
   6567 
   6568  JSObject* object() const { return object_; }
   6569 };
   6570 
   6571 // A JSObject that holds a ShapeSnapshot.
   6572 class ShapeSnapshotObject : public NativeObject {
   6573  static constexpr size_t SnapshotSlot = 0;
   6574  static constexpr size_t ReservedSlots = 1;
   6575 
   6576 public:
   6577  static const JSClassOps classOps_;
   6578  static const JSClass class_;
   6579 
   6580  bool hasSnapshot() const {
   6581    // The snapshot may not be present yet if we GC during initialization.
   6582    return !getReservedSlot(SnapshotSlot).isUndefined();
   6583  }
   6584 
   6585  ShapeSnapshot& snapshot() const {
   6586    void* ptr = getReservedSlot(SnapshotSlot).toPrivate();
   6587    MOZ_ASSERT(ptr);
   6588    return *static_cast<ShapeSnapshot*>(ptr);
   6589  }
   6590 
   6591  static ShapeSnapshotObject* create(JSContext* cx, HandleObject obj);
   6592 
   6593  static void finalize(JS::GCContext* gcx, JSObject* obj) {
   6594    if (obj->as<ShapeSnapshotObject>().hasSnapshot()) {
   6595      js_delete(&obj->as<ShapeSnapshotObject>().snapshot());
   6596    }
   6597  }
   6598  static void trace(JSTracer* trc, JSObject* obj) {
   6599    if (obj->as<ShapeSnapshotObject>().hasSnapshot()) {
   6600      obj->as<ShapeSnapshotObject>().snapshot().trace(trc);
   6601    }
   6602  }
   6603 };
   6604 
   6605 /*static */ const JSClassOps ShapeSnapshotObject::classOps_ = {
   6606    nullptr,                        // addProperty
   6607    nullptr,                        // delProperty
   6608    nullptr,                        // enumerate
   6609    nullptr,                        // newEnumerate
   6610    nullptr,                        // resolve
   6611    nullptr,                        // mayResolve
   6612    ShapeSnapshotObject::finalize,  // finalize
   6613    nullptr,                        // call
   6614    nullptr,                        // construct
   6615    ShapeSnapshotObject::trace,     // trace
   6616 };
   6617 
   6618 /*static */ const JSClass ShapeSnapshotObject::class_ = {
   6619    "ShapeSnapshotObject",
   6620    JSCLASS_HAS_RESERVED_SLOTS(ShapeSnapshotObject::ReservedSlots) |
   6621        JSCLASS_BACKGROUND_FINALIZE,
   6622    &ShapeSnapshotObject::classOps_,
   6623 };
   6624 
   6625 bool ShapeSnapshot::init(JSObject* obj) {
   6626  object_ = obj;
   6627  shape_ = obj->shape();
   6628  baseShape_ = shape_->base();
   6629  objectFlags_ = shape_->objectFlags();
   6630 
   6631  if (obj->is<NativeObject>()) {
   6632    NativeObject* nobj = &obj->as<NativeObject>();
   6633 
   6634    // Snapshot the slot values.
   6635    size_t slotSpan = nobj->slotSpan();
   6636    if (!slots_.growBy(slotSpan)) {
   6637      return false;
   6638    }
   6639    for (size_t i = 0; i < slotSpan; i++) {
   6640      slots_[i] = nobj->getSlot(i);
   6641    }
   6642 
   6643    // Snapshot property information.
   6644    if (uint32_t len = nobj->shape()->propMapLength(); len > 0) {
   6645      PropMap* map = nobj->shape()->propMap();
   6646      while (true) {
   6647        for (uint32_t i = 0; i < len; i++) {
   6648          if (!map->hasKey(i)) {
   6649            continue;
   6650          }
   6651          if (!properties_.append(PropertySnapshot(map, i))) {
   6652            return false;
   6653          }
   6654        }
   6655        if (!map->hasPrevious()) {
   6656          break;
   6657        }
   6658        map = map->asLinked()->previous();
   6659        len = PropMap::Capacity;
   6660      }
   6661    }
   6662  }
   6663 
   6664  return true;
   6665 }
   6666 
   6667 void ShapeSnapshot::trace(JSTracer* trc) {
   6668  TraceEdge(trc, &object_, "object");
   6669  TraceEdge(trc, &shape_, "shape");
   6670  TraceEdge(trc, &baseShape_, "baseShape");
   6671  slots_.trace(trc);
   6672  properties_.trace(trc);
   6673 }
   6674 
   6675 void ShapeSnapshot::checkSelf(JSContext* cx) const {
   6676  // Assertions based on a single snapshot.
   6677 
   6678  // Non-dictionary shapes must not be mutated.
   6679  if (!shape_->isDictionary()) {
   6680    MOZ_RELEASE_ASSERT(shape_->base() == baseShape_);
   6681    MOZ_RELEASE_ASSERT(shape_->objectFlags() == objectFlags_);
   6682  }
   6683 
   6684  for (const PropertySnapshot& propSnapshot : properties_) {
   6685    PropMap* propMap = propSnapshot.propMap;
   6686    uint32_t propMapIndex = propSnapshot.propMapIndex;
   6687    PropertyInfo prop = propSnapshot.prop;
   6688 
   6689    // Skip if the map no longer matches the snapshotted data. This can
   6690    // only happen for dictionary maps because they can be mutated or compacted
   6691    // after a shape change.
   6692    if (!propMap->hasKey(propMapIndex) ||
   6693        PropertySnapshot(propMap, propMapIndex) != propSnapshot) {
   6694      MOZ_RELEASE_ASSERT(propMap->isDictionary());
   6695      MOZ_RELEASE_ASSERT(object_->shape() != shape_);
   6696      continue;
   6697    }
   6698 
   6699    // Ensure ObjectFlags depending on property information are set if needed.
   6700    ObjectFlags expectedFlags = GetObjectFlagsForNewProperty(
   6701        shape_->getObjectClass(), shape_->objectFlags(), propSnapshot.key,
   6702        prop.flags(), cx);
   6703    MOZ_RELEASE_ASSERT(expectedFlags == objectFlags_);
   6704 
   6705    // Accessors must have a PrivateGCThingValue(GetterSetter*) slot value.
   6706    if (prop.isAccessorProperty()) {
   6707      Value slotVal = slots_[prop.slot()];
   6708      MOZ_RELEASE_ASSERT(slotVal.isPrivateGCThing());
   6709      MOZ_RELEASE_ASSERT(slotVal.toGCThing()->is<GetterSetter>());
   6710    }
   6711 
   6712    // Data properties must not have a PrivateGCThingValue slot value.
   6713    if (prop.isDataProperty()) {
   6714      Value slotVal = slots_[prop.slot()];
   6715      MOZ_RELEASE_ASSERT(!slotVal.isPrivateGCThing());
   6716    }
   6717  }
   6718 }
   6719 
   6720 void ShapeSnapshot::check(JSContext* cx, const ShapeSnapshot& later) const {
   6721  checkSelf(cx);
   6722  later.checkSelf(cx);
   6723 
   6724  if (object_ != later.object_) {
   6725    // Snapshots are for different objects. Assert dictionary shapes aren't
   6726    // shared.
   6727    if (object_->is<NativeObject>()) {
   6728      NativeObject* nobj = &object_->as<NativeObject>();
   6729      if (nobj->inDictionaryMode()) {
   6730        MOZ_RELEASE_ASSERT(nobj->shape() != later.shape_);
   6731      }
   6732    }
   6733    return;
   6734  }
   6735 
   6736  // We have two snapshots for the same object. Check the shape information
   6737  // wasn't changed in invalid ways.
   6738 
   6739  // If the Shape is still the same, the object must have the same BaseShape,
   6740  // ObjectFlags and property information.
   6741  if (shape_ == later.shape_) {
   6742    MOZ_RELEASE_ASSERT(objectFlags_ == later.objectFlags_);
   6743    MOZ_RELEASE_ASSERT(baseShape_ == later.baseShape_);
   6744    MOZ_RELEASE_ASSERT(slots_.length() == later.slots_.length());
   6745    MOZ_RELEASE_ASSERT(properties_.length() == later.properties_.length());
   6746 
   6747    for (size_t i = 0; i < properties_.length(); i++) {
   6748      MOZ_RELEASE_ASSERT(properties_[i] == later.properties_[i]);
   6749      // Non-configurable accessor properties and non-configurable, non-writable
   6750      // data properties shouldn't have had their slot mutated.
   6751      PropertyInfo prop = properties_[i].prop;
   6752      if (!prop.configurable()) {
   6753        if (prop.isAccessorProperty() ||
   6754            (prop.isDataProperty() && !prop.writable())) {
   6755          size_t slot = prop.slot();
   6756          MOZ_RELEASE_ASSERT(slots_[slot] == later.slots_[slot]);
   6757        }
   6758      }
   6759    }
   6760  }
   6761 
   6762  // Object flags should not be lost. The exception is the Indexed flag, it
   6763  // can be cleared when densifying elements, so clear that flag first.
   6764  {
   6765    ObjectFlags flags = objectFlags_;
   6766    ObjectFlags flagsLater = later.objectFlags_;
   6767    flags.clearFlag(ObjectFlag::Indexed);
   6768    flagsLater.clearFlag(ObjectFlag::Indexed);
   6769    MOZ_RELEASE_ASSERT((flags.toRaw() & flagsLater.toRaw()) == flags.toRaw());
   6770  }
   6771 
   6772  // If the HadGetterSetterChange flag wasn't set, all GetterSetter slots must
   6773  // be unchanged.
   6774  if (!later.objectFlags_.hasFlag(ObjectFlag::HadGetterSetterChange)) {
   6775    for (size_t i = 0; i < slots_.length(); i++) {
   6776      if (slots_[i].isPrivateGCThing() &&
   6777          slots_[i].toGCThing()->is<GetterSetter>()) {
   6778        MOZ_RELEASE_ASSERT(i < later.slots_.length());
   6779        MOZ_RELEASE_ASSERT(later.slots_[i] == slots_[i]);
   6780      }
   6781    }
   6782  }
   6783 }
   6784 
   6785 // static
   6786 ShapeSnapshotObject* ShapeSnapshotObject::create(JSContext* cx,
   6787                                                 HandleObject obj) {
   6788  Rooted<UniquePtr<ShapeSnapshot>> snapshot(cx,
   6789                                            cx->make_unique<ShapeSnapshot>(cx));
   6790  if (!snapshot || !snapshot->init(obj)) {
   6791    return nullptr;
   6792  }
   6793 
   6794  auto* snapshotObj = NewObjectWithGivenProto<ShapeSnapshotObject>(cx, nullptr);
   6795  if (!snapshotObj) {
   6796    return nullptr;
   6797  }
   6798  snapshotObj->initReservedSlot(SnapshotSlot, PrivateValue(snapshot.release()));
   6799  return snapshotObj;
   6800 }
   6801 
   6802 static bool CreateShapeSnapshot(JSContext* cx, unsigned argc, Value* vp) {
   6803  CallArgs args = CallArgsFromVp(argc, vp);
   6804 
   6805  if (!args.get(0).isObject()) {
   6806    JS_ReportErrorASCII(cx, "createShapeSnapshot requires an object argument");
   6807    return false;
   6808  }
   6809 
   6810  RootedObject obj(cx, &args[0].toObject());
   6811  auto* res = ShapeSnapshotObject::create(cx, obj);
   6812  if (!res) {
   6813    return false;
   6814  }
   6815 
   6816  res->snapshot().check(cx, res->snapshot());
   6817 
   6818  args.rval().setObject(*res);
   6819  return true;
   6820 }
   6821 
   6822 static bool CheckShapeSnapshot(JSContext* cx, unsigned argc, Value* vp) {
   6823  CallArgs args = CallArgsFromVp(argc, vp);
   6824 
   6825  if (!args.get(0).isObject() ||
   6826      !args[0].toObject().is<ShapeSnapshotObject>()) {
   6827    JS_ReportErrorASCII(cx, "checkShapeSnapshot requires a snapshot argument");
   6828    return false;
   6829  }
   6830 
   6831  // Get the object to use from the snapshot if the second argument is not an
   6832  // object.
   6833  RootedObject obj(cx);
   6834  if (args.get(1).isObject()) {
   6835    obj = &args[1].toObject();
   6836  } else {
   6837    auto& snapshot = args[0].toObject().as<ShapeSnapshotObject>().snapshot();
   6838    obj = snapshot.object();
   6839  }
   6840 
   6841  RootedObject otherSnapshot(cx, ShapeSnapshotObject::create(cx, obj));
   6842  if (!otherSnapshot) {
   6843    return false;
   6844  }
   6845 
   6846  auto& snapshot1 = args[0].toObject().as<ShapeSnapshotObject>().snapshot();
   6847  auto& snapshot2 = otherSnapshot->as<ShapeSnapshotObject>().snapshot();
   6848  snapshot1.check(cx, snapshot2);
   6849 
   6850  args.rval().setUndefined();
   6851  return true;
   6852 }
   6853 
   6854 #if defined(DEBUG) || defined(JS_JITSPEW)
   6855 static bool DumpObject(JSContext* cx, unsigned argc, Value* vp) {
   6856  CallArgs args = CallArgsFromVp(argc, vp);
   6857  RootedObject obj(cx, ToObject(cx, args.get(0)));
   6858  if (!obj) {
   6859    return false;
   6860  }
   6861 
   6862  DumpObject(obj);
   6863 
   6864  args.rval().setUndefined();
   6865  return true;
   6866 }
   6867 
   6868 static bool DumpValue(JSContext* cx, unsigned argc, Value* vp) {
   6869  CallArgs args = CallArgsFromVp(argc, vp);
   6870  args.get(0).get().dump();
   6871  args.rval().setUndefined();
   6872  return true;
   6873 }
   6874 
   6875 static bool DumpValueToString(JSContext* cx, unsigned argc, Value* vp) {
   6876  CallArgs args = CallArgsFromVp(argc, vp);
   6877 
   6878  JSSprinter out(cx);
   6879  if (!out.init()) {
   6880    return false;
   6881  }
   6882  args.get(0).get().dump(out);
   6883 
   6884  JSString* rep = out.release(cx);
   6885  if (!rep) {
   6886    return false;
   6887  }
   6888 
   6889  args.rval().setString(rep);
   6890  return true;
   6891 }
   6892 #endif
   6893 
   6894 static bool SharedMemoryEnabled(JSContext* cx, unsigned argc, Value* vp) {
   6895  CallArgs args = CallArgsFromVp(argc, vp);
   6896  args.rval().setBoolean(
   6897      cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
   6898  return true;
   6899 }
   6900 
   6901 static bool SharedArrayRawBufferRefcount(JSContext* cx, unsigned argc,
   6902                                         Value* vp) {
   6903  CallArgs args = CallArgsFromVp(argc, vp);
   6904  if (args.length() != 1 || !args[0].isObject()) {
   6905    JS_ReportErrorASCII(cx, "Expected SharedArrayBuffer object");
   6906    return false;
   6907  }
   6908  RootedObject obj(cx, &args[0].toObject());
   6909  if (!obj->is<SharedArrayBufferObject>()) {
   6910    JS_ReportErrorASCII(cx, "Expected SharedArrayBuffer object");
   6911    return false;
   6912  }
   6913  args.rval().setInt32(
   6914      obj->as<SharedArrayBufferObject>().rawBufferObject()->refcount());
   6915  return true;
   6916 }
   6917 
   6918 #ifdef NIGHTLY_BUILD
   6919 static bool ObjectAddress(JSContext* cx, unsigned argc, Value* vp) {
   6920  CallArgs args = CallArgsFromVp(argc, vp);
   6921 
   6922  if (js::SupportDifferentialTesting()) {
   6923    RootedObject callee(cx, &args.callee());
   6924    ReportUsageErrorASCII(cx, callee,
   6925                          "Function unavailable in differential testing mode.");
   6926    return false;
   6927  }
   6928 
   6929  if (args.length() != 1) {
   6930    RootedObject callee(cx, &args.callee());
   6931    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   6932    return false;
   6933  }
   6934  if (!args[0].isObject()) {
   6935    RootedObject callee(cx, &args.callee());
   6936    ReportUsageErrorASCII(cx, callee, "Expected object");
   6937    return false;
   6938  }
   6939 
   6940  void* ptr = js::UncheckedUnwrap(&args[0].toObject(), true);
   6941  char buffer[64];
   6942  SprintfLiteral(buffer, "%p", ptr);
   6943 
   6944  return ReturnStringCopy(cx, args, buffer);
   6945 }
   6946 
   6947 static bool ScriptAddressForFunction(JSContext* cx, unsigned argc, Value* vp) {
   6948  CallArgs args = CallArgsFromVp(argc, vp);
   6949 
   6950  if (js::SupportDifferentialTesting()) {
   6951    RootedObject callee(cx, &args.callee());
   6952    ReportUsageErrorASCII(cx, callee,
   6953                          "Function unavailable in differential testing mode.");
   6954    return false;
   6955  }
   6956 
   6957  if (args.length() != 1) {
   6958    RootedObject callee(cx, &args.callee());
   6959    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   6960    return false;
   6961  }
   6962  if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
   6963    RootedObject callee(cx, &args.callee());
   6964    ReportUsageErrorASCII(cx, callee, "Expected function");
   6965    return false;
   6966  }
   6967 
   6968  RootedFunction function(cx, &args[0].toObject().as<JSFunction>());
   6969  if (!function->hasBytecode()) {
   6970    RootedObject callee(cx, &args.callee());
   6971    ReportUsageErrorASCII(cx, callee, "Expected non-lazy scripted function");
   6972    return false;
   6973  }
   6974 
   6975  void* ptr = function->nonLazyScript();
   6976  args.rval().setPrivate(ptr);
   6977 
   6978  return true;
   6979 }
   6980 
   6981 static bool SharedAddress(JSContext* cx, unsigned argc, Value* vp) {
   6982  CallArgs args = CallArgsFromVp(argc, vp);
   6983 
   6984  if (js::SupportDifferentialTesting()) {
   6985    RootedObject callee(cx, &args.callee());
   6986    ReportUsageErrorASCII(cx, callee,
   6987                          "Function unavailable in differential testing mode.");
   6988    return false;
   6989  }
   6990 
   6991  if (args.length() != 1) {
   6992    RootedObject callee(cx, &args.callee());
   6993    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   6994    return false;
   6995  }
   6996  if (!args[0].isObject()) {
   6997    RootedObject callee(cx, &args.callee());
   6998    ReportUsageErrorASCII(cx, callee, "Expected object");
   6999    return false;
   7000  }
   7001 
   7002  RootedObject obj(cx, CheckedUnwrapStatic(&args[0].toObject()));
   7003  if (!obj) {
   7004    ReportAccessDenied(cx);
   7005    return false;
   7006  }
   7007  if (!obj->is<SharedArrayBufferObject>()) {
   7008    JS_ReportErrorASCII(cx, "Argument must be a SharedArrayBuffer");
   7009    return false;
   7010  }
   7011  char buffer[64];
   7012  uint32_t nchar = SprintfLiteral(
   7013      buffer, "%p",
   7014      obj->as<SharedArrayBufferObject>().dataPointerShared().unwrap(
   7015          /*safeish*/));
   7016 
   7017  JSString* str = JS_NewStringCopyN(cx, buffer, nchar);
   7018  if (!str) {
   7019    return false;
   7020  }
   7021 
   7022  args.rval().setString(str);
   7023 
   7024  return true;
   7025 }
   7026 #endif
   7027 
   7028 static bool HasInvalidatedTeleporting(JSContext* cx, unsigned argc, Value* vp) {
   7029  CallArgs args = CallArgsFromVp(argc, vp);
   7030 
   7031  if (args.length() != 1 || !args[0].isObject()) {
   7032    RootedObject callee(cx, &args.callee());
   7033    ReportUsageErrorASCII(cx, callee, "Expected single object argument");
   7034    return false;
   7035  }
   7036 
   7037  args.rval().setBoolean(args[0].toObject().hasInvalidatedTeleporting());
   7038  return true;
   7039 }
   7040 
   7041 static bool DumpBacktrace(JSContext* cx, unsigned argc, Value* vp) {
   7042  CallArgs args = CallArgsFromVp(argc, vp);
   7043  DumpBacktrace(cx);
   7044  args.rval().setUndefined();
   7045  return true;
   7046 }
   7047 
   7048 static bool GetBacktrace(JSContext* cx, unsigned argc, Value* vp) {
   7049  CallArgs args = CallArgsFromVp(argc, vp);
   7050 
   7051  bool showArgs = false;
   7052  bool showLocals = false;
   7053  bool showThisProps = false;
   7054 
   7055  if (args.length() > 1) {
   7056    RootedObject callee(cx, &args.callee());
   7057    ReportUsageErrorASCII(cx, callee, "Too many arguments");
   7058    return false;
   7059  }
   7060 
   7061  if (args.length() == 1) {
   7062    RootedObject cfg(cx, ToObject(cx, args[0]));
   7063    if (!cfg) {
   7064      return false;
   7065    }
   7066    RootedValue v(cx);
   7067 
   7068    if (!JS_GetProperty(cx, cfg, "args", &v)) {
   7069      return false;
   7070    }
   7071    showArgs = ToBoolean(v);
   7072 
   7073    if (!JS_GetProperty(cx, cfg, "locals", &v)) {
   7074      return false;
   7075    }
   7076    showLocals = ToBoolean(v);
   7077 
   7078    if (!JS_GetProperty(cx, cfg, "thisprops", &v)) {
   7079      return false;
   7080    }
   7081    showThisProps = ToBoolean(v);
   7082  }
   7083 
   7084  JS::UniqueChars buf =
   7085      JS::FormatStackDump(cx, showArgs, showLocals, showThisProps);
   7086  if (!buf) {
   7087    return false;
   7088  }
   7089 
   7090  size_t len;
   7091  UniqueTwoByteChars ucbuf(JS::LossyUTF8CharsToNewTwoByteCharsZ(
   7092                               cx, JS::UTF8Chars(buf.get(), strlen(buf.get())),
   7093                               &len, js::MallocArena)
   7094                               .get());
   7095  if (!ucbuf) {
   7096    return false;
   7097  }
   7098  JSString* str = JS_NewUCStringCopyN(cx, ucbuf.get(), len);
   7099  if (!str) {
   7100    return false;
   7101  }
   7102 
   7103  args.rval().setString(str);
   7104  return true;
   7105 }
   7106 
   7107 static bool ReportOutOfMemory(JSContext* cx, unsigned argc, Value* vp) {
   7108  CallArgs args = CallArgsFromVp(argc, vp);
   7109  JS_ReportOutOfMemory(cx);
   7110  cx->clearPendingException();
   7111  args.rval().setUndefined();
   7112  return true;
   7113 }
   7114 
   7115 static bool ThrowOutOfMemory(JSContext* cx, unsigned argc, Value* vp) {
   7116  JS_ReportOutOfMemory(cx);
   7117  return false;
   7118 }
   7119 
   7120 static bool ReportLargeAllocationFailure(JSContext* cx, unsigned argc,
   7121                                         Value* vp) {
   7122  CallArgs args = CallArgsFromVp(argc, vp);
   7123 
   7124  size_t bytes = JSRuntime::LARGE_ALLOCATION;
   7125  if (args.length() >= 1) {
   7126    if (!args[0].isInt32()) {
   7127      RootedObject callee(cx, &args.callee());
   7128      ReportUsageErrorASCII(cx, callee,
   7129                            "First argument must be an integer if specified.");
   7130      return false;
   7131    }
   7132    bytes = args[0].toInt32();
   7133  }
   7134 
   7135  void* buf = cx->runtime()->onOutOfMemoryCanGC(AllocFunction::Malloc,
   7136                                                js::MallocArena, bytes);
   7137 
   7138  js_free(buf);
   7139  args.rval().setUndefined();
   7140  return true;
   7141 }
   7142 
   7143 namespace heaptools {
   7144 
   7145 using EdgeName = UniqueTwoByteChars;
   7146 
   7147 // An edge to a node from its predecessor in a path through the graph.
   7148 class BackEdge {
   7149  // The node from which this edge starts.
   7150  JS::ubi::Node predecessor_;
   7151 
   7152  // The name of this edge.
   7153  EdgeName name_;
   7154 
   7155 public:
   7156  BackEdge() : name_(nullptr) {}
   7157  // Construct an initialized back edge, taking ownership of |name|.
   7158  BackEdge(JS::ubi::Node predecessor, EdgeName name)
   7159      : predecessor_(predecessor), name_(std::move(name)) {}
   7160  BackEdge(BackEdge&& rhs)
   7161      : predecessor_(rhs.predecessor_), name_(std::move(rhs.name_)) {}
   7162  BackEdge& operator=(BackEdge&& rhs) {
   7163    MOZ_ASSERT(&rhs != this);
   7164    this->~BackEdge();
   7165    new (this) BackEdge(std::move(rhs));
   7166    return *this;
   7167  }
   7168 
   7169  EdgeName forgetName() { return std::move(name_); }
   7170  JS::ubi::Node predecessor() const { return predecessor_; }
   7171 
   7172 private:
   7173  // No copy constructor or copying assignment.
   7174  BackEdge(const BackEdge&) = delete;
   7175  BackEdge& operator=(const BackEdge&) = delete;
   7176 };
   7177 
   7178 // A path-finding handler class for use with JS::ubi::BreadthFirst.
   7179 struct FindPathHandler {
   7180  using NodeData = BackEdge;
   7181  using Traversal = JS::ubi::BreadthFirst<FindPathHandler>;
   7182 
   7183  FindPathHandler(JSContext* cx, JS::ubi::Node start, JS::ubi::Node target,
   7184                  MutableHandle<GCVector<Value>> nodes, Vector<EdgeName>& edges)
   7185      : cx(cx),
   7186        start(start),
   7187        target(target),
   7188        foundPath(false),
   7189        nodes(nodes),
   7190        edges(edges) {}
   7191 
   7192  bool operator()(Traversal& traversal, JS::ubi::Node origin,
   7193                  const JS::ubi::Edge& edge, BackEdge* backEdge, bool first) {
   7194    // We take care of each node the first time we visit it, so there's
   7195    // nothing to be done on subsequent visits.
   7196    if (!first) {
   7197      return true;
   7198    }
   7199 
   7200    // Record how we reached this node. This is the last edge on a
   7201    // shortest path to this node.
   7202    EdgeName edgeName =
   7203        DuplicateStringToArena(js::StringBufferArena, cx, edge.name.get());
   7204    if (!edgeName) {
   7205      return false;
   7206    }
   7207    *backEdge = BackEdge(origin, std::move(edgeName));
   7208 
   7209    // Have we reached our final target node?
   7210    if (edge.referent == target) {
   7211      // Record the path that got us here, which must be a shortest path.
   7212      if (!recordPath(traversal, backEdge)) {
   7213        return false;
   7214      }
   7215      foundPath = true;
   7216      traversal.stop();
   7217    }
   7218 
   7219    return true;
   7220  }
   7221 
   7222  // We've found a path to our target. Walk the backlinks to produce the
   7223  // (reversed) path, saving the path in |nodes| and |edges|. |nodes| is
   7224  // rooted, so it can hold the path's nodes as we leave the scope of
   7225  // the AutoCheckCannotGC. Note that nodes are added to |visited| after we
   7226  // return from operator() so we have to pass the target BackEdge* to this
   7227  // function.
   7228  bool recordPath(Traversal& traversal, BackEdge* targetBackEdge) {
   7229    JS::ubi::Node here = target;
   7230 
   7231    do {
   7232      BackEdge* backEdge = targetBackEdge;
   7233      if (here != target) {
   7234        Traversal::NodeMap::Ptr p = traversal.visited.lookup(here);
   7235        MOZ_ASSERT(p);
   7236        backEdge = &p->value();
   7237      }
   7238      JS::ubi::Node predecessor = backEdge->predecessor();
   7239      if (!nodes.append(predecessor.exposeToJS()) ||
   7240          !edges.append(backEdge->forgetName())) {
   7241        return false;
   7242      }
   7243      here = predecessor;
   7244    } while (here != start);
   7245 
   7246    return true;
   7247  }
   7248 
   7249  JSContext* cx;
   7250 
   7251  // The node we're starting from.
   7252  JS::ubi::Node start;
   7253 
   7254  // The node we're looking for.
   7255  JS::ubi::Node target;
   7256 
   7257  // True if we found a path to target, false if we didn't.
   7258  bool foundPath;
   7259 
   7260  // The nodes and edges of the path --- should we find one. The path is
   7261  // stored in reverse order, because that's how it's easiest for us to
   7262  // construct it:
   7263  // - edges[i] is the name of the edge from nodes[i] to nodes[i-1].
   7264  // - edges[0] is the name of the edge from nodes[0] to the target.
   7265  // - The last node, nodes[n-1], is the start node.
   7266  MutableHandle<GCVector<Value>> nodes;
   7267  Vector<EdgeName>& edges;
   7268 };
   7269 
   7270 }  // namespace heaptools
   7271 
   7272 static bool FindPath(JSContext* cx, unsigned argc, Value* vp) {
   7273  CallArgs args = CallArgsFromVp(argc, vp);
   7274  if (!args.requireAtLeast(cx, "findPath", 2)) {
   7275    return false;
   7276  }
   7277 
   7278  // We don't ToString non-objects given as 'start' or 'target', because this
   7279  // test is all about object identity, and ToString doesn't preserve that.
   7280  // Non-GCThing endpoints don't make much sense.
   7281  if (!args[0].isObject() && !args[0].isString() && !args[0].isSymbol()) {
   7282    ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
   7283                     nullptr, "not an object, string, or symbol");
   7284    return false;
   7285  }
   7286 
   7287  if (!args[1].isObject() && !args[1].isString() && !args[1].isSymbol()) {
   7288    ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
   7289                     nullptr, "not an object, string, or symbol");
   7290    return false;
   7291  }
   7292 
   7293  Rooted<GCVector<Value>> nodes(cx, GCVector<Value>(cx));
   7294  Vector<heaptools::EdgeName> edges(cx);
   7295 
   7296  {
   7297    // We can't tolerate the GC moving things around while we're searching
   7298    // the heap. Check that nothing we do causes a GC.
   7299    JS::AutoCheckCannotGC autoCannotGC;
   7300 
   7301    JS::ubi::Node start(args[0]), target(args[1]);
   7302 
   7303    heaptools::FindPathHandler handler(cx, start, target, &nodes, edges);
   7304    heaptools::FindPathHandler::Traversal traversal(cx, handler, autoCannotGC);
   7305    if (!traversal.addStart(start)) {
   7306      ReportOutOfMemory(cx);
   7307      return false;
   7308    }
   7309 
   7310    if (!traversal.traverse()) {
   7311      if (!cx->isExceptionPending()) {
   7312        ReportOutOfMemory(cx);
   7313      }
   7314      return false;
   7315    }
   7316 
   7317    if (!handler.foundPath) {
   7318      // We didn't find any paths from the start to the target.
   7319      args.rval().setUndefined();
   7320      return true;
   7321    }
   7322  }
   7323 
   7324  // |nodes| and |edges| contain the path from |start| to |target|, reversed.
   7325  // Construct a JavaScript array describing the path from the start to the
   7326  // target. Each element has the form:
   7327  //
   7328  //   {
   7329  //     node: <object or string or symbol>,
   7330  //     edge: <string describing outgoing edge from node>
   7331  //   }
   7332  //
   7333  // or, if the node is some internal thing that isn't a proper JavaScript
   7334  // value:
   7335  //
   7336  //   { node: undefined, edge: <string> }
   7337  size_t length = nodes.length();
   7338  Rooted<ArrayObject*> result(cx, NewDenseFullyAllocatedArray(cx, length));
   7339  if (!result) {
   7340    return false;
   7341  }
   7342  result->ensureDenseInitializedLength(0, length);
   7343 
   7344  // Walk |nodes| and |edges| in the stored order, and construct the result
   7345  // array in start-to-target order.
   7346  for (size_t i = 0; i < length; i++) {
   7347    // Build an object describing the node and edge.
   7348    RootedObject obj(cx, NewPlainObject(cx));
   7349    if (!obj) {
   7350      return false;
   7351    }
   7352 
   7353    // Only define the "node" property if we're not fuzzing, to prevent the
   7354    // fuzzers from messing with internal objects that we don't want to expose
   7355    // to arbitrary JS.
   7356    if (!fuzzingSafe) {
   7357      RootedValue wrapped(cx, nodes[i]);
   7358      if (!cx->compartment()->wrap(cx, &wrapped)) {
   7359        return false;
   7360      }
   7361 
   7362      if (!JS_DefineProperty(cx, obj, "node", wrapped, JSPROP_ENUMERATE)) {
   7363        return false;
   7364      }
   7365    }
   7366 
   7367    heaptools::EdgeName edgeName = std::move(edges[i]);
   7368 
   7369    size_t edgeNameLength = js_strlen(edgeName.get());
   7370    RootedString edgeStr(
   7371        cx, NewString<CanGC>(cx, std::move(edgeName), edgeNameLength));
   7372    if (!edgeStr) {
   7373      return false;
   7374    }
   7375 
   7376    if (!JS_DefineProperty(cx, obj, "edge", edgeStr, JSPROP_ENUMERATE)) {
   7377      return false;
   7378    }
   7379 
   7380    result->setDenseElement(length - i - 1, ObjectValue(*obj));
   7381  }
   7382 
   7383  args.rval().setObject(*result);
   7384  return true;
   7385 }
   7386 
   7387 static bool ShortestPaths(JSContext* cx, unsigned argc, Value* vp) {
   7388  CallArgs args = CallArgsFromVp(argc, vp);
   7389  if (!args.requireAtLeast(cx, "shortestPaths", 1)) {
   7390    return false;
   7391  }
   7392 
   7393  if (!args[0].isObject() || !args[0].toObject().is<ArrayObject>()) {
   7394    ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
   7395                     nullptr, "not an array object");
   7396    return false;
   7397  }
   7398 
   7399  Rooted<ArrayObject*> objs(cx, &args[0].toObject().as<ArrayObject>());
   7400 
   7401  RootedValue start(cx, NullValue());
   7402  int32_t maxNumPaths = 3;
   7403 
   7404  if (!args.get(1).isUndefined()) {
   7405    if (!args[1].isObject()) {
   7406      ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[1],
   7407                       nullptr, "not an options object");
   7408      return false;
   7409    }
   7410 
   7411    RootedObject options(cx, &args[1].toObject());
   7412    bool exists;
   7413    if (!JS_HasProperty(cx, options, "start", &exists)) {
   7414      return false;
   7415    }
   7416    if (exists) {
   7417      if (!JS_GetProperty(cx, options, "start", &start)) {
   7418        return false;
   7419      }
   7420 
   7421      // Non-GCThing endpoints don't make much sense.
   7422      if (!start.isGCThing()) {
   7423        ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, start,
   7424                         nullptr, "not a GC thing");
   7425        return false;
   7426      }
   7427    }
   7428 
   7429    RootedValue v(cx, Int32Value(maxNumPaths));
   7430    if (!JS_HasProperty(cx, options, "maxNumPaths", &exists)) {
   7431      return false;
   7432    }
   7433    if (exists) {
   7434      if (!JS_GetProperty(cx, options, "maxNumPaths", &v)) {
   7435        return false;
   7436      }
   7437      if (!JS::ToInt32(cx, v, &maxNumPaths)) {
   7438        return false;
   7439      }
   7440    }
   7441    if (maxNumPaths <= 0) {
   7442      ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, v,
   7443                       nullptr, "not greater than 0");
   7444      return false;
   7445    }
   7446  }
   7447 
   7448  // Ensure we have at least one target.
   7449  size_t length = objs->getDenseInitializedLength();
   7450  if (length == 0) {
   7451    ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
   7452                     nullptr,
   7453                     "not a dense array object with one or more elements");
   7454    return false;
   7455  }
   7456 
   7457  for (size_t i = 0; i < length; i++) {
   7458    RootedValue el(cx, objs->getDenseElement(i));
   7459    if (!el.isGCThing()) {
   7460      JS_ReportErrorASCII(cx, "Each target must be a GC thing");
   7461      return false;
   7462    }
   7463  }
   7464 
   7465  // We accumulate the results into a GC-stable form, due to the fact that the
   7466  // JS::ubi::ShortestPaths lifetime (when operating on the live heap graph)
   7467  // is bounded within an AutoCheckCannotGC.
   7468  Rooted<GCVector<GCVector<GCVector<Value>>>> values(
   7469      cx, GCVector<GCVector<GCVector<Value>>>(cx));
   7470  Vector<Vector<Vector<JS::ubi::EdgeName>>> names(cx);
   7471 
   7472  {
   7473    JS::ubi::Node root;
   7474 
   7475    JS::ubi::RootList rootList(cx, true);
   7476    if (start.isNull()) {
   7477      auto [ok, nogc] = rootList.init();
   7478      (void)nogc;  // Old compilers get anxious about nogc being unused.
   7479      if (!ok) {
   7480        ReportOutOfMemory(cx);
   7481        return false;
   7482      }
   7483      root = JS::ubi::Node(&rootList);
   7484    } else {
   7485      root = JS::ubi::Node(start);
   7486    }
   7487    JS::AutoCheckCannotGC noGC(cx);
   7488 
   7489    JS::ubi::NodeSet targets;
   7490 
   7491    for (size_t i = 0; i < length; i++) {
   7492      RootedValue val(cx, objs->getDenseElement(i));
   7493      JS::ubi::Node node(val);
   7494      if (!targets.put(node)) {
   7495        ReportOutOfMemory(cx);
   7496        return false;
   7497      }
   7498    }
   7499 
   7500    auto maybeShortestPaths = JS::ubi::ShortestPaths::Create(
   7501        cx, noGC, maxNumPaths, root, std::move(targets));
   7502    if (maybeShortestPaths.isNothing()) {
   7503      ReportOutOfMemory(cx);
   7504      return false;
   7505    }
   7506    auto& shortestPaths = *maybeShortestPaths;
   7507 
   7508    for (size_t i = 0; i < length; i++) {
   7509      if (!values.append(GCVector<GCVector<Value>>(cx)) ||
   7510          !names.append(Vector<Vector<JS::ubi::EdgeName>>(cx))) {
   7511        return false;
   7512      }
   7513 
   7514      RootedValue val(cx, objs->getDenseElement(i));
   7515      JS::ubi::Node target(val);
   7516 
   7517      bool ok = shortestPaths.forEachPath(target, [&](JS::ubi::Path& path) {
   7518        Rooted<GCVector<Value>> pathVals(cx, GCVector<Value>(cx));
   7519        Vector<JS::ubi::EdgeName> pathNames(cx);
   7520 
   7521        for (auto& part : path) {
   7522          if (!pathVals.append(part->predecessor().exposeToJS()) ||
   7523              !pathNames.append(std::move(part->name()))) {
   7524            return false;
   7525          }
   7526        }
   7527 
   7528        return values.back().append(std::move(pathVals.get())) &&
   7529               names.back().append(std::move(pathNames));
   7530      });
   7531 
   7532      if (!ok) {
   7533        return false;
   7534      }
   7535    }
   7536  }
   7537 
   7538  MOZ_ASSERT(values.length() == names.length());
   7539  MOZ_ASSERT(values.length() == length);
   7540 
   7541  Rooted<ArrayObject*> results(cx, NewDenseFullyAllocatedArray(cx, length));
   7542  if (!results) {
   7543    return false;
   7544  }
   7545  results->ensureDenseInitializedLength(0, length);
   7546 
   7547  for (size_t i = 0; i < length; i++) {
   7548    size_t numPaths = values[i].length();
   7549    MOZ_ASSERT(names[i].length() == numPaths);
   7550 
   7551    Rooted<ArrayObject*> pathsArray(cx,
   7552                                    NewDenseFullyAllocatedArray(cx, numPaths));
   7553    if (!pathsArray) {
   7554      return false;
   7555    }
   7556    pathsArray->ensureDenseInitializedLength(0, numPaths);
   7557 
   7558    for (size_t j = 0; j < numPaths; j++) {
   7559      size_t pathLength = values[i][j].length();
   7560      MOZ_ASSERT(names[i][j].length() == pathLength);
   7561 
   7562      Rooted<ArrayObject*> path(cx,
   7563                                NewDenseFullyAllocatedArray(cx, pathLength));
   7564      if (!path) {
   7565        return false;
   7566      }
   7567      path->ensureDenseInitializedLength(0, pathLength);
   7568 
   7569      for (size_t k = 0; k < pathLength; k++) {
   7570        Rooted<PlainObject*> part(cx, NewPlainObject(cx));
   7571        if (!part) {
   7572          return false;
   7573        }
   7574 
   7575        RootedValue predecessor(cx, values[i][j][k]);
   7576        if (!cx->compartment()->wrap(cx, &predecessor) ||
   7577            !JS_DefineProperty(cx, part, "predecessor", predecessor,
   7578                               JSPROP_ENUMERATE)) {
   7579          return false;
   7580        }
   7581 
   7582        if (names[i][j][k]) {
   7583          RootedString edge(cx,
   7584                            NewStringCopyZ<CanGC>(cx, names[i][j][k].get()));
   7585          if (!edge ||
   7586              !JS_DefineProperty(cx, part, "edge", edge, JSPROP_ENUMERATE)) {
   7587            return false;
   7588          }
   7589        }
   7590 
   7591        path->setDenseElement(k, ObjectValue(*part));
   7592      }
   7593 
   7594      pathsArray->setDenseElement(j, ObjectValue(*path));
   7595    }
   7596 
   7597    results->setDenseElement(i, ObjectValue(*pathsArray));
   7598  }
   7599 
   7600  args.rval().setObject(*results);
   7601  return true;
   7602 }
   7603 
   7604 static bool EvalReturningScope(JSContext* cx, unsigned argc, Value* vp) {
   7605  CallArgs args = CallArgsFromVp(argc, vp);
   7606  if (!args.requireAtLeast(cx, "evalReturningScope", 1)) {
   7607    return false;
   7608  }
   7609 
   7610  RootedString str(cx, ToString(cx, args[0]));
   7611  if (!str) {
   7612    return false;
   7613  }
   7614 
   7615  JS::AutoFilename filename;
   7616  uint32_t lineno;
   7617 
   7618  JS::DescribeScriptedCaller(&filename, cx, &lineno);
   7619 
   7620  // CompileOption should be created in the target global's realm.
   7621  RootedObject global(cx);
   7622  Maybe<JS::CompileOptions> maybeOptions;
   7623  if (args.hasDefined(1)) {
   7624    global = ToObject(cx, args[1]);
   7625    if (!global) {
   7626      return false;
   7627    }
   7628 
   7629    global = CheckedUnwrapDynamic(global, cx, /* stopAtWindowProxy = */ false);
   7630    if (!global) {
   7631      JS_ReportErrorASCII(cx, "Permission denied to access global");
   7632      return false;
   7633    }
   7634    if (!global->is<GlobalObject>()) {
   7635      JS_ReportErrorASCII(cx, "Argument must be a global object");
   7636      return false;
   7637    }
   7638 
   7639    JSAutoRealm ar(cx, global);
   7640    maybeOptions.emplace(cx);
   7641  } else {
   7642    global = JS::CurrentGlobalOrNull(cx);
   7643    maybeOptions.emplace(cx);
   7644  }
   7645 
   7646  CompileOptions& options = maybeOptions.ref();
   7647  options.setFileAndLine(filename.get(), lineno);
   7648  options.setNoScriptRval(true);
   7649  options.setNonSyntacticScope(true);
   7650 
   7651  AutoStableStringChars linearChars(cx);
   7652  if (!linearChars.initTwoByte(cx, str)) {
   7653    return false;
   7654  }
   7655  JS::SourceText<char16_t> srcBuf;
   7656  if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
   7657    return false;
   7658  }
   7659 
   7660  RootedObject varObj(cx);
   7661 
   7662  {
   7663    // ExecuteInFrameScriptEnvironment requires the script be in the same
   7664    // realm as the global. The script compilation should be done after
   7665    // switching globals.
   7666    AutoRealm ar(cx, global);
   7667 
   7668    RootedScript script(cx, JS::Compile(cx, options, srcBuf));
   7669    if (!script) {
   7670      return false;
   7671    }
   7672 
   7673    JS::RootedObject obj(cx, JS_NewPlainObject(cx));
   7674    if (!obj) {
   7675      return false;
   7676    }
   7677 
   7678    RootedObject lexicalScope(cx);
   7679    if (!js::ExecuteInFrameScriptEnvironment(cx, obj, script, &lexicalScope)) {
   7680      return false;
   7681    }
   7682 
   7683    varObj = lexicalScope->enclosingEnvironment()->enclosingEnvironment();
   7684    MOZ_ASSERT(varObj->is<NonSyntacticVariablesObject>());
   7685  }
   7686 
   7687  RootedValue varObjVal(cx, ObjectValue(*varObj));
   7688  if (!cx->compartment()->wrap(cx, &varObjVal)) {
   7689    return false;
   7690  }
   7691 
   7692  args.rval().set(varObjVal);
   7693  return true;
   7694 }
   7695 
   7696 static bool ByteSize(JSContext* cx, unsigned argc, Value* vp) {
   7697  CallArgs args = CallArgsFromVp(argc, vp);
   7698  mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf;
   7699 
   7700  {
   7701    // We can't tolerate the GC moving things around while we're using a
   7702    // ubi::Node. Check that nothing we do causes a GC.
   7703    JS::AutoCheckCannotGC autoCannotGC;
   7704 
   7705    JS::ubi::Node node = args.get(0);
   7706    if (node) {
   7707      args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
   7708    } else {
   7709      args.rval().setUndefined();
   7710    }
   7711  }
   7712  return true;
   7713 }
   7714 
   7715 static bool ByteSizeOfScript(JSContext* cx, unsigned argc, Value* vp) {
   7716  CallArgs args = CallArgsFromVp(argc, vp);
   7717  if (!args.requireAtLeast(cx, "byteSizeOfScript", 1)) {
   7718    return false;
   7719  }
   7720  if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
   7721    JS_ReportErrorASCII(cx, "Argument must be a Function object");
   7722    return false;
   7723  }
   7724 
   7725  RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
   7726  if (fun->isNativeFun()) {
   7727    JS_ReportErrorASCII(cx, "Argument must be a scripted function");
   7728    return false;
   7729  }
   7730 
   7731  RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
   7732  if (!script) {
   7733    return false;
   7734  }
   7735 
   7736  mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf;
   7737 
   7738  {
   7739    // We can't tolerate the GC moving things around while we're using a
   7740    // ubi::Node. Check that nothing we do causes a GC.
   7741    JS::AutoCheckCannotGC autoCannotGC;
   7742 
   7743    JS::ubi::Node node = script;
   7744    if (node) {
   7745      args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
   7746    } else {
   7747      args.rval().setUndefined();
   7748    }
   7749  }
   7750  return true;
   7751 }
   7752 
   7753 static bool SetImmutablePrototype(JSContext* cx, unsigned argc, Value* vp) {
   7754  CallArgs args = CallArgsFromVp(argc, vp);
   7755  if (!args.get(0).isObject()) {
   7756    JS_ReportErrorASCII(cx, "setImmutablePrototype: object expected");
   7757    return false;
   7758  }
   7759 
   7760  RootedObject obj(cx, &args[0].toObject());
   7761 
   7762  bool succeeded;
   7763  if (!js::SetImmutablePrototype(cx, obj, &succeeded)) {
   7764    return false;
   7765  }
   7766 
   7767  args.rval().setBoolean(succeeded);
   7768  return true;
   7769 }
   7770 
   7771 #if defined(DEBUG) || defined(JS_JITSPEW)
   7772 static bool DumpStringRepresentation(JSContext* cx, unsigned argc, Value* vp) {
   7773  CallArgs args = CallArgsFromVp(argc, vp);
   7774 
   7775  RootedString str(cx, ToString(cx, args.get(0)));
   7776  if (!str) {
   7777    return false;
   7778  }
   7779 
   7780  Fprinter out(stderr);
   7781  str->dumpRepresentation(out);
   7782 
   7783  args.rval().setUndefined();
   7784  return true;
   7785 }
   7786 
   7787 static bool GetStringRepresentation(JSContext* cx, unsigned argc, Value* vp) {
   7788  CallArgs args = CallArgsFromVp(argc, vp);
   7789 
   7790  RootedString str(cx, ToString(cx, args.get(0)));
   7791  if (!str) {
   7792    return false;
   7793  }
   7794 
   7795  JSSprinter out(cx);
   7796  if (!out.init()) {
   7797    return false;
   7798  }
   7799  str->dumpRepresentation(out);
   7800 
   7801  JSString* rep = out.release(cx);
   7802  if (!rep) {
   7803    return false;
   7804  }
   7805 
   7806  args.rval().setString(rep);
   7807  return true;
   7808 }
   7809 #endif
   7810 
   7811 static bool ParseCompileOptionsForModule(JSContext* cx,
   7812                                         JS::CompileOptions& options,
   7813                                         JS::Handle<JSObject*> opts,
   7814                                         bool& isModule) {
   7815  JS::Rooted<JS::Value> v(cx);
   7816 
   7817  if (!JS_GetProperty(cx, opts, "module", &v)) {
   7818    return false;
   7819  }
   7820  if (!v.isUndefined() && JS::ToBoolean(v)) {
   7821    options.setModule();
   7822    isModule = true;
   7823 
   7824    if (!ValidateModuleCompileOptions(cx, options)) {
   7825      return false;
   7826    }
   7827  } else {
   7828    isModule = false;
   7829  }
   7830 
   7831  return true;
   7832 }
   7833 
   7834 static bool ParseCompileOptionsForInstantiate(JSContext* cx,
   7835                                              JS::CompileOptions& options,
   7836                                              JS::Handle<JSObject*> opts,
   7837                                              bool& prepareForInstantiate) {
   7838  JS::Rooted<JS::Value> v(cx);
   7839 
   7840  if (!JS_GetProperty(cx, opts, "prepareForInstantiate", &v)) {
   7841    return false;
   7842  }
   7843  if (!v.isUndefined()) {
   7844    prepareForInstantiate = JS::ToBoolean(v);
   7845  } else {
   7846    prepareForInstantiate = false;
   7847  }
   7848 
   7849  return true;
   7850 }
   7851 
   7852 static bool CompileToStencil(JSContext* cx, uint32_t argc, Value* vp) {
   7853  CallArgs args = CallArgsFromVp(argc, vp);
   7854 
   7855  if (!args.requireAtLeast(cx, "compileToStencil", 1)) {
   7856    return false;
   7857  }
   7858  if (!args[0].isString()) {
   7859    const char* typeName = InformalValueTypeName(args[0]);
   7860    JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
   7861    return false;
   7862  }
   7863 
   7864  RootedString src(cx, ToString<CanGC>(cx, args[0]));
   7865  if (!src) {
   7866    return false;
   7867  }
   7868 
   7869  /* Linearize the string to obtain a char16_t* range. */
   7870  AutoStableStringChars linearChars(cx);
   7871  if (!linearChars.initTwoByte(cx, src)) {
   7872    return false;
   7873  }
   7874  JS::SourceText<char16_t> srcBuf;
   7875  if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
   7876    return false;
   7877  }
   7878 
   7879  CompileOptions options(cx);
   7880  options.setFile("<compileToStencil>");
   7881 
   7882  RootedString displayURL(cx);
   7883  RootedString sourceMapURL(cx);
   7884  UniqueChars fileNameBytes;
   7885  bool isModule = false;
   7886  bool prepareForInstantiate = false;
   7887  if (args.length() == 2) {
   7888    if (!args[1].isObject()) {
   7889      JS_ReportErrorASCII(
   7890          cx, "compileToStencil: The 2nd argument must be an object");
   7891      return false;
   7892    }
   7893 
   7894    RootedObject opts(cx, &args[1].toObject());
   7895 
   7896    if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
   7897      return false;
   7898    }
   7899    if (!ParseCompileOptionsForModule(cx, options, opts, isModule)) {
   7900      return false;
   7901    }
   7902    if (!ParseCompileOptionsForInstantiate(cx, options, opts,
   7903                                           prepareForInstantiate)) {
   7904      return false;
   7905    }
   7906    if (!js::ParseSourceOptions(cx, opts, &displayURL, &sourceMapURL)) {
   7907      return false;
   7908    }
   7909  }
   7910 
   7911  RefPtr<JS::Stencil> stencil;
   7912  if (isModule) {
   7913    stencil = JS::CompileModuleScriptToStencil(cx, options, srcBuf);
   7914  } else {
   7915    stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf);
   7916  }
   7917  if (!stencil) {
   7918    return false;
   7919  }
   7920 
   7921  JS::InstantiationStorage storage;
   7922  {
   7923    AutoReportFrontendContext fc(cx);
   7924    if (!SetSourceOptions(cx, &fc, stencil->getInitial()->source, displayURL,
   7925                          sourceMapURL)) {
   7926      return false;
   7927    }
   7928 
   7929    if (prepareForInstantiate) {
   7930      if (!JS::PrepareForInstantiate(&fc, *stencil, storage)) {
   7931        return false;
   7932      }
   7933    }
   7934  }
   7935 
   7936  Rooted<js::StencilObject*> stencilObj(
   7937      cx, js::StencilObject::create(cx, std::move(stencil)));
   7938  if (!stencilObj) {
   7939    return false;
   7940  }
   7941 
   7942  args.rval().setObject(*stencilObj);
   7943  return true;
   7944 }
   7945 
   7946 static bool EvalStencil(JSContext* cx, uint32_t argc, Value* vp) {
   7947  CallArgs args = CallArgsFromVp(argc, vp);
   7948 
   7949  if (!args.requireAtLeast(cx, "evalStencil", 1)) {
   7950    return false;
   7951  }
   7952 
   7953  /* Prepare the input byte array. */
   7954  if (!args[0].isObject()) {
   7955    JS_ReportErrorASCII(cx, "evalStencil: Stencil object expected");
   7956    return false;
   7957  }
   7958  Rooted<js::StencilObject*> stencilObj(
   7959      cx, args[0].toObject().maybeUnwrapIf<js::StencilObject>());
   7960  if (!stencilObj) {
   7961    JS_ReportErrorASCII(cx, "evalStencil: Stencil object expected");
   7962    return false;
   7963  }
   7964 
   7965  if (stencilObj->stencil()->getInitial()->isModule()) {
   7966    JS_ReportErrorASCII(cx,
   7967                        "evalStencil: Module stencil cannot be evaluated. Use "
   7968                        "instantiateModuleStencil instead");
   7969    return false;
   7970  }
   7971 
   7972  CompileOptions options(cx);
   7973  UniqueChars fileNameBytes;
   7974  Rooted<JS::Value> privateValue(cx);
   7975  Rooted<JSString*> elementAttributeName(cx);
   7976  if (args.length() == 2) {
   7977    if (!args[1].isObject()) {
   7978      JS_ReportErrorASCII(cx,
   7979                          "evalStencil: The 2nd argument must be an object");
   7980      return false;
   7981    }
   7982 
   7983    RootedObject opts(cx, &args[1].toObject());
   7984 
   7985    if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
   7986      return false;
   7987    }
   7988    if (!js::ParseDebugMetadata(cx, opts, &privateValue,
   7989                                &elementAttributeName)) {
   7990      return false;
   7991    }
   7992  }
   7993 
   7994  bool useDebugMetadata = !privateValue.isUndefined() || elementAttributeName;
   7995 
   7996  JS::InstantiateOptions instantiateOptions(options);
   7997  if (useDebugMetadata) {
   7998    instantiateOptions.hideScriptFromDebugger = true;
   7999  }
   8000 
   8001  if (!js::ValidateLazinessOfStencilAndGlobal(cx, stencilObj->stencil())) {
   8002    return false;
   8003  }
   8004 
   8005  RootedScript script(cx, JS::InstantiateGlobalStencil(cx, instantiateOptions,
   8006                                                       stencilObj->stencil()));
   8007  if (!script) {
   8008    return false;
   8009  }
   8010 
   8011  if (useDebugMetadata) {
   8012    instantiateOptions.hideScriptFromDebugger = false;
   8013    if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, privateValue,
   8014                                 elementAttributeName, nullptr, nullptr)) {
   8015      return false;
   8016    }
   8017  }
   8018 
   8019  RootedValue retVal(cx);
   8020  if (!JS_ExecuteScript(cx, script, &retVal)) {
   8021    return false;
   8022  }
   8023 
   8024  args.rval().set(retVal);
   8025  return true;
   8026 }
   8027 
   8028 static bool CompileToStencilXDR(JSContext* cx, uint32_t argc, Value* vp) {
   8029  CallArgs args = CallArgsFromVp(argc, vp);
   8030 
   8031  if (!args.requireAtLeast(cx, "compileToStencilXDR", 1)) {
   8032    return false;
   8033  }
   8034 
   8035  RootedString src(cx, ToString<CanGC>(cx, args[0]));
   8036  if (!src) {
   8037    return false;
   8038  }
   8039 
   8040  /* Linearize the string to obtain a char16_t* range. */
   8041  AutoStableStringChars linearChars(cx);
   8042  if (!linearChars.initTwoByte(cx, src)) {
   8043    return false;
   8044  }
   8045  JS::SourceText<char16_t> srcBuf;
   8046  if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
   8047    return false;
   8048  }
   8049 
   8050  CompileOptions options(cx);
   8051  options.setFile("<compileToStencilXDR>");
   8052 
   8053  RootedString displayURL(cx);
   8054  RootedString sourceMapURL(cx);
   8055  UniqueChars fileNameBytes;
   8056  bool isModule = false;
   8057  if (args.length() == 2) {
   8058    if (!args[1].isObject()) {
   8059      JS_ReportErrorASCII(
   8060          cx, "compileToStencilXDR: The 2nd argument must be an object");
   8061      return false;
   8062    }
   8063 
   8064    RootedObject opts(cx, &args[1].toObject());
   8065 
   8066    if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
   8067      return false;
   8068    }
   8069    if (!ParseCompileOptionsForModule(cx, options, opts, isModule)) {
   8070      return false;
   8071    }
   8072    if (!js::ParseSourceOptions(cx, opts, &displayURL, &sourceMapURL)) {
   8073      return false;
   8074    }
   8075  }
   8076 
   8077  RefPtr<JS::Stencil> stencil;
   8078  if (isModule) {
   8079    stencil = JS::CompileModuleScriptToStencil(cx, options, srcBuf);
   8080  } else {
   8081    stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf);
   8082  }
   8083  if (!stencil) {
   8084    return false;
   8085  }
   8086 
   8087  {
   8088    AutoReportFrontendContext fc(cx);
   8089    if (!SetSourceOptions(cx, &fc, stencil->getInitial()->source, displayURL,
   8090                          sourceMapURL)) {
   8091      return false;
   8092    }
   8093  }
   8094 
   8095  /* Serialize the stencil to XDR. */
   8096  JS::TranscodeBuffer xdrBytes;
   8097  auto result = JS::EncodeStencil(cx, stencil, xdrBytes);
   8098  if (result == JS::TranscodeResult::Throw) {
   8099    return false;
   8100  }
   8101  if (JS::IsTranscodeFailureResult(result)) {
   8102    JS_ReportErrorASCII(cx, "Encoding failure");
   8103    return false;
   8104  }
   8105 
   8106  Rooted<StencilXDRBufferObject*> xdrObj(
   8107      cx,
   8108      StencilXDRBufferObject::create(cx, xdrBytes.begin(), xdrBytes.length()));
   8109  if (!xdrObj) {
   8110    return false;
   8111  }
   8112 
   8113  args.rval().setObject(*xdrObj);
   8114  return true;
   8115 }
   8116 
   8117 static bool EvalStencilXDR(JSContext* cx, uint32_t argc, Value* vp) {
   8118  CallArgs args = CallArgsFromVp(argc, vp);
   8119 
   8120  if (!args.requireAtLeast(cx, "evalStencilXDR", 1)) {
   8121    return false;
   8122  }
   8123 
   8124  /* Prepare the input byte array. */
   8125  if (!args[0].isObject()) {
   8126    JS_ReportErrorASCII(cx, "evalStencilXDR: Stencil XDR object expected");
   8127    return false;
   8128  }
   8129  Rooted<StencilXDRBufferObject*> xdrObj(
   8130      cx, args[0].toObject().maybeUnwrapIf<StencilXDRBufferObject>());
   8131  if (!xdrObj) {
   8132    JS_ReportErrorASCII(cx, "evalStencilXDR: Stencil XDR object expected");
   8133    return false;
   8134  }
   8135  MOZ_ASSERT(xdrObj->hasBuffer());
   8136 
   8137  CompileOptions options(cx);
   8138  UniqueChars fileNameBytes;
   8139  Rooted<JS::Value> privateValue(cx);
   8140  Rooted<JSString*> elementAttributeName(cx);
   8141  if (args.length() == 2) {
   8142    if (!args[1].isObject()) {
   8143      JS_ReportErrorASCII(cx,
   8144                          "evalStencilXDR: The 2nd argument must be an object");
   8145      return false;
   8146    }
   8147 
   8148    RootedObject opts(cx, &args[1].toObject());
   8149 
   8150    if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
   8151      return false;
   8152    }
   8153    if (!js::ParseDebugMetadata(cx, opts, &privateValue,
   8154                                &elementAttributeName)) {
   8155      return false;
   8156    }
   8157  }
   8158 
   8159  JS::TranscodeRange xdrRange(xdrObj->buffer(), xdrObj->bufferLength());
   8160  JS::DecodeOptions decodeOptions(options);
   8161  RefPtr<JS::Stencil> stencil;
   8162  auto result =
   8163      JS::DecodeStencil(cx, decodeOptions, xdrRange, getter_AddRefs(stencil));
   8164  if (result == JS::TranscodeResult::Throw) {
   8165    return false;
   8166  }
   8167  if (JS::IsTranscodeFailureResult(result)) {
   8168    JS_ReportErrorASCII(cx, "Decoding failure");
   8169    return false;
   8170  }
   8171 
   8172  if (stencil->getInitial()->isModule()) {
   8173    JS_ReportErrorASCII(cx,
   8174                        "evalStencilXDR: Module stencil cannot be evaluated. "
   8175                        "Use instantiateModuleStencilXDR instead");
   8176    return false;
   8177  }
   8178 
   8179  if (!js::ValidateLazinessOfStencilAndGlobal(cx, stencil.get())) {
   8180    return false;
   8181  }
   8182 
   8183  bool useDebugMetadata = !privateValue.isUndefined() || elementAttributeName;
   8184 
   8185  JS::InstantiateOptions instantiateOptions(options);
   8186  if (useDebugMetadata) {
   8187    instantiateOptions.hideScriptFromDebugger = true;
   8188  }
   8189  RootedScript script(
   8190      cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil.get()));
   8191  if (!script) {
   8192    return false;
   8193  }
   8194 
   8195  if (useDebugMetadata) {
   8196    instantiateOptions.hideScriptFromDebugger = false;
   8197    if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, privateValue,
   8198                                 elementAttributeName, nullptr, nullptr)) {
   8199      return false;
   8200    }
   8201  }
   8202 
   8203  RootedValue retVal(cx);
   8204  if (!JS_ExecuteScript(cx, script, &retVal)) {
   8205    return false;
   8206  }
   8207 
   8208  args.rval().set(retVal);
   8209  return true;
   8210 }
   8211 
   8212 static bool GetExceptionInfo(JSContext* cx, uint32_t argc, Value* vp) {
   8213  CallArgs args = CallArgsFromVp(argc, vp);
   8214 
   8215  if (!args.requireAtLeast(cx, "getExceptionInfo", 1)) {
   8216    return false;
   8217  }
   8218 
   8219  if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
   8220    JS_ReportErrorASCII(cx, "getExceptionInfo: expected function argument");
   8221    return false;
   8222  }
   8223 
   8224  RootedValue rval(cx);
   8225  if (JS_CallFunctionValue(cx, nullptr, args[0], JS::HandleValueArray::empty(),
   8226                           &rval)) {
   8227    // Function didn't throw.
   8228    args.rval().setNull();
   8229    return true;
   8230  }
   8231 
   8232  // We currently don't support interrupts or forced returns.
   8233  if (!cx->isExceptionPending()) {
   8234    JS_ReportErrorASCII(cx, "getExceptionInfo: unsupported exception status");
   8235    return false;
   8236  }
   8237 
   8238  RootedValue excVal(cx);
   8239  Rooted<SavedFrame*> stack(cx);
   8240  if (!GetAndClearExceptionAndStack(cx, &excVal, &stack)) {
   8241    return false;
   8242  }
   8243 
   8244  RootedValue stackVal(cx);
   8245  if (stack) {
   8246    RootedString stackString(cx);
   8247    if (!BuildStackString(cx, cx->realm()->principals(), stack, &stackString)) {
   8248      return false;
   8249    }
   8250    stackVal.setString(stackString);
   8251  } else {
   8252    stackVal.setNull();
   8253  }
   8254 
   8255  RootedObject obj(cx, NewPlainObject(cx));
   8256  if (!obj) {
   8257    return false;
   8258  }
   8259 
   8260  if (!JS_DefineProperty(cx, obj, "exception", excVal, JSPROP_ENUMERATE)) {
   8261    return false;
   8262  }
   8263 
   8264  if (!JS_DefineProperty(cx, obj, "stack", stackVal, JSPROP_ENUMERATE)) {
   8265    return false;
   8266  }
   8267 
   8268  args.rval().setObject(*obj);
   8269  return true;
   8270 }
   8271 
   8272 class AllocationMarkerObject : public NativeObject {
   8273 public:
   8274  static const JSClass class_;
   8275 };
   8276 
   8277 const JSClass AllocationMarkerObject::class_ = {
   8278    "AllocationMarker",
   8279 };
   8280 
   8281 static bool AllocationMarker(JSContext* cx, unsigned argc, Value* vp) {
   8282  CallArgs args = CallArgsFromVp(argc, vp);
   8283 
   8284  bool allocateInsideNursery = true;
   8285  if (args.length() > 0 && args[0].isObject()) {
   8286    RootedObject options(cx, &args[0].toObject());
   8287 
   8288    RootedValue nurseryVal(cx);
   8289    if (!JS_GetProperty(cx, options, "nursery", &nurseryVal)) {
   8290      return false;
   8291    }
   8292    allocateInsideNursery = ToBoolean(nurseryVal);
   8293  }
   8294 
   8295  JSObject* obj =
   8296      allocateInsideNursery
   8297          ? NewObjectWithGivenProto<AllocationMarkerObject>(cx, nullptr)
   8298          : NewTenuredObjectWithGivenProto<AllocationMarkerObject>(cx, nullptr);
   8299  if (!obj) {
   8300    return false;
   8301  }
   8302 
   8303  args.rval().setObject(*obj);
   8304  return true;
   8305 }
   8306 
   8307 namespace gcCallback {
   8308 
   8309 struct MajorGC {
   8310  int32_t depth;
   8311  int32_t phases;
   8312 };
   8313 
   8314 static void majorGC(JSContext* cx, JSGCStatus status, JS::GCReason reason,
   8315                    void* data) {
   8316  auto info = static_cast<MajorGC*>(data);
   8317  if (!(info->phases & (1 << status))) {
   8318    return;
   8319  }
   8320 
   8321  if (info->depth > 0) {
   8322    info->depth--;
   8323    JS::PrepareForFullGC(cx);
   8324    JS::NonIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::API);
   8325    info->depth++;
   8326  }
   8327 }
   8328 
   8329 struct MinorGC {
   8330  int32_t phases;
   8331  bool active;
   8332 };
   8333 
   8334 static void minorGC(JSContext* cx, JSGCStatus status, JS::GCReason reason,
   8335                    void* data) {
   8336  auto info = static_cast<MinorGC*>(data);
   8337  if (!(info->phases & (1 << status))) {
   8338    return;
   8339  }
   8340 
   8341  if (info->active) {
   8342    info->active = false;
   8343    if (cx->zone() && !cx->zone()->isAtomsZone()) {
   8344      cx->runtime()->gc.evictNursery(JS::GCReason::DEBUG_GC);
   8345    }
   8346    info->active = true;
   8347  }
   8348 }
   8349 
   8350 // Process global, should really be runtime-local.
   8351 static MajorGC majorGCInfo;
   8352 static MinorGC minorGCInfo;
   8353 
   8354 static void enterNullRealm(JSContext* cx, JSGCStatus status,
   8355                           JS::GCReason reason, void* data) {
   8356  JSAutoNullableRealm enterRealm(cx, nullptr);
   8357 }
   8358 
   8359 } /* namespace gcCallback */
   8360 
   8361 static bool SetGCCallback(JSContext* cx, unsigned argc, Value* vp) {
   8362  CallArgs args = CallArgsFromVp(argc, vp);
   8363 
   8364  if (args.length() != 1) {
   8365    JS_ReportErrorASCII(cx, "Wrong number of arguments");
   8366    return false;
   8367  }
   8368 
   8369  RootedObject opts(cx, ToObject(cx, args[0]));
   8370  if (!opts) {
   8371    return false;
   8372  }
   8373 
   8374  RootedValue v(cx);
   8375  if (!JS_GetProperty(cx, opts, "action", &v)) {
   8376    return false;
   8377  }
   8378 
   8379  JSString* str = JS::ToString(cx, v);
   8380  if (!str) {
   8381    return false;
   8382  }
   8383  Rooted<JSLinearString*> action(cx, str->ensureLinear(cx));
   8384  if (!action) {
   8385    return false;
   8386  }
   8387 
   8388  int32_t phases = 0;
   8389  if (StringEqualsLiteral(action, "minorGC") ||
   8390      StringEqualsLiteral(action, "majorGC")) {
   8391    if (!JS_GetProperty(cx, opts, "phases", &v)) {
   8392      return false;
   8393    }
   8394    if (v.isUndefined()) {
   8395      phases = (1 << JSGC_END);
   8396    } else {
   8397      JSString* str = JS::ToString(cx, v);
   8398      if (!str) {
   8399        return false;
   8400      }
   8401      JSLinearString* phasesStr = str->ensureLinear(cx);
   8402      if (!phasesStr) {
   8403        return false;
   8404      }
   8405 
   8406      if (StringEqualsLiteral(phasesStr, "begin")) {
   8407        phases = (1 << JSGC_BEGIN);
   8408      } else if (StringEqualsLiteral(phasesStr, "end")) {
   8409        phases = (1 << JSGC_END);
   8410      } else if (StringEqualsLiteral(phasesStr, "both")) {
   8411        phases = (1 << JSGC_BEGIN) | (1 << JSGC_END);
   8412      } else {
   8413        JS_ReportErrorASCII(cx, "Invalid callback phase");
   8414        return false;
   8415      }
   8416    }
   8417  }
   8418 
   8419  if (StringEqualsLiteral(action, "minorGC")) {
   8420    gcCallback::minorGCInfo.phases = phases;
   8421    gcCallback::minorGCInfo.active = true;
   8422    JS_SetGCCallback(cx, gcCallback::minorGC, &gcCallback::minorGCInfo);
   8423  } else if (StringEqualsLiteral(action, "majorGC")) {
   8424    if (!JS_GetProperty(cx, opts, "depth", &v)) {
   8425      return false;
   8426    }
   8427    int32_t depth = 1;
   8428    if (!v.isUndefined()) {
   8429      if (!ToInt32(cx, v, &depth)) {
   8430        return false;
   8431      }
   8432    }
   8433    if (depth < 0) {
   8434      JS_ReportErrorASCII(cx, "Nesting depth cannot be negative");
   8435      return false;
   8436    }
   8437    if (depth + gcstats::MAX_PHASE_NESTING >
   8438        gcstats::Statistics::MAX_SUSPENDED_PHASES) {
   8439      JS_ReportErrorASCII(cx, "Nesting depth too large, would overflow");
   8440      return false;
   8441    }
   8442 
   8443    gcCallback::majorGCInfo.phases = phases;
   8444    gcCallback::majorGCInfo.depth = depth;
   8445    JS_SetGCCallback(cx, gcCallback::majorGC, &gcCallback::majorGCInfo);
   8446  } else if (StringEqualsLiteral(action, "enterNullRealm")) {
   8447    JS_SetGCCallback(cx, gcCallback::enterNullRealm, nullptr);
   8448  } else {
   8449    JS_ReportErrorASCII(cx, "Unknown GC callback action");
   8450    return false;
   8451  }
   8452 
   8453  args.rval().setUndefined();
   8454  return true;
   8455 }
   8456 
   8457 #ifdef DEBUG
   8458 static bool EnqueueMark(JSContext* cx, unsigned argc, Value* vp) {
   8459  CallArgs args = CallArgsFromVp(argc, vp);
   8460  gc::GCRuntime* gc = &cx->runtime()->gc;
   8461 
   8462  if (gc->isIncrementalGCInProgress() &&
   8463      gc->hasZealMode(gc::ZealMode::IncrementalMarkingValidator)) {
   8464    JS_ReportErrorASCII(
   8465        cx,
   8466        "Can't add to test mark queue during incremental GC if marking "
   8467        "validation is enabled as this can cause failures");
   8468    return false;
   8469  }
   8470 
   8471  if (args.get(0).isString()) {
   8472    RootedString val(cx, args[0].toString());
   8473    if (!val->ensureLinear(cx)) {
   8474      return false;
   8475    }
   8476    if (!gc->appendTestMarkQueue(StringValue(val))) {
   8477      JS_ReportOutOfMemory(cx);
   8478      return false;
   8479    }
   8480  } else if (args.get(0).isObject()) {
   8481    if (!gc->appendTestMarkQueue(args[0])) {
   8482      JS_ReportOutOfMemory(cx);
   8483      return false;
   8484    }
   8485  } else {
   8486    JS_ReportErrorASCII(cx, "Argument must be a string or object");
   8487    return false;
   8488  }
   8489 
   8490  args.rval().setUndefined();
   8491  return true;
   8492 }
   8493 
   8494 static bool GetMarkQueue(JSContext* cx, unsigned argc, Value* vp) {
   8495  CallArgs args = CallArgsFromVp(argc, vp);
   8496 
   8497  const auto& queue = cx->runtime()->gc.getTestMarkQueue();
   8498 
   8499  RootedObject result(cx, JS::NewArrayObject(cx, queue.length()));
   8500  if (!result) {
   8501    return false;
   8502  }
   8503  for (size_t i = 0; i < queue.length(); i++) {
   8504    RootedValue val(cx, queue[i]);
   8505    if (!JS_WrapValue(cx, &val)) {
   8506      return false;
   8507    }
   8508    if (!JS_SetElement(cx, result, i, val)) {
   8509      return false;
   8510    }
   8511  }
   8512 
   8513  args.rval().setObject(*result);
   8514  return true;
   8515 }
   8516 
   8517 static bool ClearMarkQueue(JSContext* cx, unsigned argc, Value* vp) {
   8518  CallArgs args = CallArgsFromVp(argc, vp);
   8519 
   8520  cx->runtime()->gc.clearTestMarkQueue();
   8521  args.rval().setUndefined();
   8522  return true;
   8523 }
   8524 #endif  // DEBUG
   8525 
   8526 static bool NurseryStringsEnabled(JSContext* cx, unsigned argc, Value* vp) {
   8527  CallArgs args = CallArgsFromVp(argc, vp);
   8528  args.rval().setBoolean(cx->zone()->allocNurseryStrings());
   8529  return true;
   8530 }
   8531 
   8532 static bool IsNurseryAllocated(JSContext* cx, unsigned argc, Value* vp) {
   8533  CallArgs args = CallArgsFromVp(argc, vp);
   8534  if (!args.get(0).isGCThing()) {
   8535    JS_ReportErrorASCII(
   8536        cx, "The function takes one argument, which must be a GC thing");
   8537    return false;
   8538  }
   8539 
   8540  args.rval().setBoolean(IsInsideNursery(args[0].toGCThing()));
   8541  return true;
   8542 }
   8543 
   8544 static bool NumAllocSitesPretenured(JSContext* cx, unsigned argc, Value* vp) {
   8545  CallArgs args = CallArgsFromVp(argc, vp);
   8546  args.rval().setInt32(cx->realm()->numAllocSitesPretenured);
   8547  return true;
   8548 }
   8549 
   8550 static bool GetLcovInfo(JSContext* cx, unsigned argc, Value* vp) {
   8551  CallArgs args = CallArgsFromVp(argc, vp);
   8552 
   8553  if (args.length() > 1) {
   8554    JS_ReportErrorASCII(cx, "Wrong number of arguments");
   8555    return false;
   8556  }
   8557 
   8558  if (!coverage::IsLCovEnabled()) {
   8559    JS_ReportErrorASCII(cx, "Coverage not enabled for process.");
   8560    return false;
   8561  }
   8562 
   8563  RootedObject global(cx);
   8564  if (args.hasDefined(0)) {
   8565    global = ToObject(cx, args[0]);
   8566    if (!global) {
   8567      JS_ReportErrorASCII(cx, "Permission denied to access global");
   8568      return false;
   8569    }
   8570    global = CheckedUnwrapDynamic(global, cx, /* stopAtWindowProxy = */ false);
   8571    if (!global) {
   8572      ReportAccessDenied(cx);
   8573      return false;
   8574    }
   8575    if (!global->is<GlobalObject>()) {
   8576      JS_ReportErrorASCII(cx, "Argument must be a global object");
   8577      return false;
   8578    }
   8579  } else {
   8580    global = JS::CurrentGlobalOrNull(cx);
   8581  }
   8582 
   8583  size_t length = 0;
   8584  UniqueChars content;
   8585  {
   8586    AutoRealm ar(cx, global);
   8587    content = js::GetCodeCoverageSummary(cx, &length);
   8588  }
   8589 
   8590  if (!content) {
   8591    return false;
   8592  }
   8593 
   8594  JSString* str =
   8595      JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(content.get(), length));
   8596  if (!str) {
   8597    return false;
   8598  }
   8599 
   8600  args.rval().setString(str);
   8601  return true;
   8602 }
   8603 
   8604 #ifdef DEBUG
   8605 static bool SetRNGState(JSContext* cx, unsigned argc, Value* vp) {
   8606  CallArgs args = CallArgsFromVp(argc, vp);
   8607  if (!args.requireAtLeast(cx, "SetRNGState", 2)) {
   8608    return false;
   8609  }
   8610 
   8611  double d0;
   8612  if (!ToNumber(cx, args[0], &d0)) {
   8613    return false;
   8614  }
   8615 
   8616  double d1;
   8617  if (!ToNumber(cx, args[1], &d1)) {
   8618    return false;
   8619  }
   8620 
   8621  uint64_t seed0 = static_cast<uint64_t>(d0);
   8622  uint64_t seed1 = static_cast<uint64_t>(d1);
   8623 
   8624  if (seed0 == 0 && seed1 == 0) {
   8625    JS_ReportErrorASCII(cx, "RNG requires non-zero seed");
   8626    return false;
   8627  }
   8628 
   8629  cx->realm()->getOrCreateRandomNumberGenerator().setState(seed0, seed1);
   8630 
   8631  args.rval().setUndefined();
   8632  return true;
   8633 }
   8634 #endif
   8635 
   8636 static bool GetTimeZone(JSContext* cx, unsigned argc, Value* vp) {
   8637  CallArgs args = CallArgsFromVp(argc, vp);
   8638  RootedObject callee(cx, &args.callee());
   8639 
   8640  if (args.length() != 0) {
   8641    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   8642    return false;
   8643  }
   8644 
   8645 #ifndef __wasi__
   8646  auto getTimeZone = [](std::time_t* now) -> const char* {
   8647    std::tm local{};
   8648 #  if defined(_WIN32)
   8649    _tzset();
   8650    if (localtime_s(&local, now) == 0) {
   8651      return _tzname[local.tm_isdst > 0];
   8652    }
   8653 #  else
   8654    tzset();
   8655 #    if defined(HAVE_LOCALTIME_R)
   8656    if (localtime_r(now, &local)) {
   8657 #    else
   8658    std::tm* localtm = std::localtime(now);
   8659    if (localtm) {
   8660      *local = *localtm;
   8661 #    endif /* HAVE_LOCALTIME_R */
   8662 
   8663 #    if defined(HAVE_TM_ZONE_TM_GMTOFF)
   8664      return local.tm_zone;
   8665 #    else
   8666      return tzname[local.tm_isdst > 0];
   8667 #    endif /* HAVE_TM_ZONE_TM_GMTOFF */
   8668    }
   8669 #  endif   /* _WIN32 */
   8670    return nullptr;
   8671  };
   8672 
   8673  std::time_t now = std::time(nullptr);
   8674  if (now != static_cast<std::time_t>(-1)) {
   8675    if (const char* tz = getTimeZone(&now)) {
   8676      return ReturnStringCopy(cx, args, tz);
   8677    }
   8678  }
   8679 #endif /* __wasi__ */
   8680  args.rval().setUndefined();
   8681  return true;
   8682 }
   8683 
   8684 static bool SetTimeZone(JSContext* cx, unsigned argc, Value* vp) {
   8685  CallArgs args = CallArgsFromVp(argc, vp);
   8686  RootedObject callee(cx, &args.callee());
   8687 
   8688  if (args.length() != 1) {
   8689    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   8690    return false;
   8691  }
   8692 
   8693  if (!args[0].isString() && !args[0].isUndefined()) {
   8694    ReportUsageErrorASCII(cx, callee,
   8695                          "First argument should be a string or undefined");
   8696    return false;
   8697  }
   8698 
   8699 #ifndef __wasi__
   8700  auto setTimeZone = [](const char* value) {
   8701 #  if defined(_WIN32)
   8702    return _putenv_s("TZ", value) == 0;
   8703 #  else
   8704    return setenv("TZ", value, true) == 0;
   8705 #  endif /* _WIN32 */
   8706  };
   8707 
   8708  auto unsetTimeZone = []() {
   8709 #  if defined(_WIN32)
   8710    return _putenv_s("TZ", "") == 0;
   8711 #  else
   8712    return unsetenv("TZ") == 0;
   8713 #  endif /* _WIN32 */
   8714  };
   8715 
   8716  if (args[0].isString() && !args[0].toString()->empty()) {
   8717    Rooted<JSString*> str(cx, args[0].toString());
   8718    if (!str) {
   8719      return false;
   8720    }
   8721 
   8722    UniqueChars timeZone =
   8723        StringToTimeZone(cx, callee, str, AllowTimeZoneLink::Yes);
   8724    if (!timeZone) {
   8725      return false;
   8726    }
   8727 
   8728    if (!setTimeZone(timeZone.get())) {
   8729      JS_ReportErrorASCII(cx, "Failed to set 'TZ' environment variable");
   8730      return false;
   8731    }
   8732  } else {
   8733    if (!unsetTimeZone()) {
   8734      JS_ReportErrorASCII(cx, "Failed to unset 'TZ' environment variable");
   8735      return false;
   8736    }
   8737  }
   8738 
   8739 #  if defined(_WIN32)
   8740  _tzset();
   8741 #  else
   8742  tzset();
   8743 #  endif /* _WIN32 */
   8744 
   8745  JS::ResetTimeZone();
   8746 
   8747 #endif /* __wasi__ */
   8748  args.rval().setUndefined();
   8749  return true;
   8750 }
   8751 
   8752 static bool GetRealmTimeZone(JSContext* cx, unsigned argc, Value* vp) {
   8753  CallArgs args = CallArgsFromVp(argc, vp);
   8754  RootedObject callee(cx, &args.callee());
   8755 
   8756  if (args.length() != 0) {
   8757    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   8758    return false;
   8759  }
   8760 
   8761 #ifdef JS_HAS_INTL_API
   8762  auto* str = cx->global()->globalIntlData().defaultTimeZone(cx);
   8763  if (!str) {
   8764    return false;
   8765  }
   8766 
   8767  args.rval().setString(str);
   8768 #else
   8769  // Realm time zones require Intl support.
   8770  args.rval().setString(cx->emptyString());
   8771 #endif
   8772 
   8773  return true;
   8774 }
   8775 
   8776 static bool SetRealmTimeZone(JSContext* cx, unsigned argc, Value* vp) {
   8777  CallArgs args = CallArgsFromVp(argc, vp);
   8778  RootedObject callee(cx, &args.callee());
   8779 
   8780  if (args.length() != 1) {
   8781    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   8782    return false;
   8783  }
   8784 
   8785  if (!args[0].isString() && !args[0].isUndefined()) {
   8786    ReportUsageErrorASCII(cx, callee,
   8787                          "First argument should be a string or undefined");
   8788    return false;
   8789  }
   8790 
   8791  if (args[0].isString() && !args[0].toString()->empty()) {
   8792    Rooted<JSString*> str(cx, args[0].toString());
   8793    if (!str) {
   8794      return false;
   8795    }
   8796 
   8797    auto timeZone = StringToTimeZone(cx, callee, str, AllowTimeZoneLink::No);
   8798    if (!timeZone) {
   8799      return false;
   8800    }
   8801 
   8802    cx->realm()->setTimeZoneOverride(timeZone.get());
   8803  } else {
   8804    // Reset to use the system default time zone.
   8805    cx->realm()->setTimeZoneOverride(nullptr);
   8806  }
   8807 
   8808  args.rval().setUndefined();
   8809  return true;
   8810 }
   8811 
   8812 static bool GetCoreCount(JSContext* cx, unsigned argc, Value* vp) {
   8813  CallArgs args = CallArgsFromVp(argc, vp);
   8814  RootedObject callee(cx, &args.callee());
   8815 
   8816  if (args.length() != 0) {
   8817    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   8818    return false;
   8819  }
   8820 
   8821  args.rval().setInt32(GetCPUCount());
   8822  return true;
   8823 }
   8824 
   8825 static bool GetDefaultLocale(JSContext* cx, unsigned argc, Value* vp) {
   8826  CallArgs args = CallArgsFromVp(argc, vp);
   8827  RootedObject callee(cx, &args.callee());
   8828 
   8829  if (args.length() != 0) {
   8830    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   8831    return false;
   8832  }
   8833 
   8834  UniqueChars locale = JS_GetDefaultLocale(cx);
   8835  if (!locale) {
   8836    return false;
   8837  }
   8838 
   8839  return ReturnStringCopy(cx, args, locale.get());
   8840 }
   8841 
   8842 static bool SetDefaultLocale(JSContext* cx, unsigned argc, Value* vp) {
   8843  CallArgs args = CallArgsFromVp(argc, vp);
   8844  RootedObject callee(cx, &args.callee());
   8845 
   8846  if (args.length() != 1) {
   8847    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   8848    return false;
   8849  }
   8850 
   8851  if (!args[0].isString() && !args[0].isUndefined()) {
   8852    ReportUsageErrorASCII(cx, callee,
   8853                          "First argument should be a string or undefined");
   8854    return false;
   8855  }
   8856 
   8857  if (args[0].isString() && !args[0].toString()->empty()) {
   8858    RootedString str(cx, args[0].toString());
   8859    UniqueChars locale = StringToLocale(cx, callee, str);
   8860    if (!locale) {
   8861      return false;
   8862    }
   8863 
   8864    if (!JS_SetDefaultLocale(cx->runtime(), locale.get())) {
   8865      ReportOutOfMemory(cx);
   8866      return false;
   8867    }
   8868  } else {
   8869    JS_ResetDefaultLocale(cx->runtime());
   8870  }
   8871 
   8872  args.rval().setUndefined();
   8873  return true;
   8874 }
   8875 
   8876 static bool GetRealmLocale(JSContext* cx, unsigned argc, Value* vp) {
   8877  CallArgs args = CallArgsFromVp(argc, vp);
   8878  RootedObject callee(cx, &args.callee());
   8879 
   8880  if (args.length() != 0) {
   8881    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   8882    return false;
   8883  }
   8884 
   8885 #ifdef JS_HAS_INTL_API
   8886  auto* str = cx->global()->globalIntlData().defaultLocale(cx);
   8887  if (!str) {
   8888    return false;
   8889  }
   8890 
   8891  args.rval().setString(str);
   8892 #else
   8893  // Realm locales require Intl support.
   8894  args.rval().setString(cx->emptyString());
   8895 #endif
   8896 
   8897  return true;
   8898 }
   8899 
   8900 static bool SetRealmLocale(JSContext* cx, unsigned argc, Value* vp) {
   8901  CallArgs args = CallArgsFromVp(argc, vp);
   8902  RootedObject callee(cx, &args.callee());
   8903 
   8904  if (args.length() != 1) {
   8905    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   8906    return false;
   8907  }
   8908 
   8909  if (!args[0].isString() && !args[0].isUndefined()) {
   8910    ReportUsageErrorASCII(cx, callee,
   8911                          "First argument should be a string or undefined");
   8912    return false;
   8913  }
   8914 
   8915  if (args[0].isString() && !args[0].toString()->empty()) {
   8916    Rooted<JSString*> str(cx, args[0].toString());
   8917    if (!str) {
   8918      return false;
   8919    }
   8920 
   8921    auto locale = StringToLocale(cx, callee, str);
   8922    if (!locale) {
   8923      return false;
   8924    }
   8925 
   8926    cx->realm()->setLocaleOverride(locale.get());
   8927  } else {
   8928    // Reset to use the system default locale.
   8929    cx->realm()->setLocaleOverride(nullptr);
   8930  }
   8931 
   8932  args.rval().setUndefined();
   8933  return true;
   8934 }
   8935 
   8936 #ifdef AFLFUZZ
   8937 static bool AflLoop(JSContext* cx, unsigned argc, Value* vp) {
   8938  CallArgs args = CallArgsFromVp(argc, vp);
   8939 
   8940  uint32_t max_cnt;
   8941  if (!ToUint32(cx, args.get(0), &max_cnt)) {
   8942    return false;
   8943  }
   8944 
   8945  args.rval().setBoolean(!!__AFL_LOOP(max_cnt));
   8946  return true;
   8947 }
   8948 #endif
   8949 
   8950 static bool MonotonicNow(JSContext* cx, unsigned argc, Value* vp) {
   8951  CallArgs args = CallArgsFromVp(argc, vp);
   8952  double now;
   8953 
   8954 // The std::chrono symbols are too new to be present in STL on all platforms we
   8955 // care about, so use raw POSIX clock APIs when it might be necessary.
   8956 #if defined(XP_UNIX) && !defined(XP_DARWIN)
   8957  auto ComputeNow = [](const timespec& ts) {
   8958    return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
   8959  };
   8960 
   8961  timespec ts;
   8962  if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
   8963    // Use a monotonic clock if available.
   8964    now = ComputeNow(ts);
   8965  } else {
   8966    // Use a realtime clock as fallback.
   8967    if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
   8968      // Fail if no clock is available.
   8969      JS_ReportErrorASCII(cx, "can't retrieve system clock");
   8970      return false;
   8971    }
   8972 
   8973    now = ComputeNow(ts);
   8974 
   8975    // Manually enforce atomicity on a non-monotonic clock.
   8976    {
   8977      static mozilla::Atomic<bool, mozilla::ReleaseAcquire> spinLock;
   8978      while (!spinLock.compareExchange(false, true)) {
   8979        continue;
   8980      }
   8981 
   8982      static double lastNow = -FLT_MAX;
   8983      now = lastNow = std::max(now, lastNow);
   8984 
   8985      spinLock = false;
   8986    }
   8987  }
   8988 #else
   8989  using std::chrono::duration_cast;
   8990  using std::chrono::milliseconds;
   8991  using std::chrono::steady_clock;
   8992  now = duration_cast<milliseconds>(steady_clock::now().time_since_epoch())
   8993            .count();
   8994 #endif  // XP_UNIX && !XP_DARWIN
   8995 
   8996  args.rval().setNumber(now);
   8997  return true;
   8998 }
   8999 
   9000 static bool TimeSinceCreation(JSContext* cx, unsigned argc, Value* vp) {
   9001  CallArgs args = CallArgsFromVp(argc, vp);
   9002  double when =
   9003      (mozilla::TimeStamp::Now() - mozilla::TimeStamp::ProcessCreation())
   9004          .ToMilliseconds();
   9005  args.rval().setNumber(when);
   9006  return true;
   9007 }
   9008 
   9009 static bool GetInnerMostEnvironmentObject(JSContext* cx, unsigned argc,
   9010                                          Value* vp) {
   9011  CallArgs args = CallArgsFromVp(argc, vp);
   9012 
   9013  FrameIter iter(cx);
   9014  if (iter.done()) {
   9015    args.rval().setNull();
   9016    return true;
   9017  }
   9018 
   9019  args.rval().setObjectOrNull(iter.environmentChain(cx));
   9020  return true;
   9021 }
   9022 
   9023 static bool GetEnclosingEnvironmentObject(JSContext* cx, unsigned argc,
   9024                                          Value* vp) {
   9025  CallArgs args = CallArgsFromVp(argc, vp);
   9026  if (!args.requireAtLeast(cx, "getEnclosingEnvironmentObject", 1)) {
   9027    return false;
   9028  }
   9029 
   9030  if (!args[0].isObject()) {
   9031    args.rval().setUndefined();
   9032    return true;
   9033  }
   9034 
   9035  JSObject* envObj = &args[0].toObject();
   9036 
   9037  if (envObj->is<EnvironmentObject>()) {
   9038    EnvironmentObject* env = &envObj->as<EnvironmentObject>();
   9039    args.rval().setObject(env->enclosingEnvironment());
   9040    return true;
   9041  }
   9042 
   9043  if (envObj->is<DebugEnvironmentProxy>()) {
   9044    DebugEnvironmentProxy* envProxy = &envObj->as<DebugEnvironmentProxy>();
   9045    args.rval().setObject(envProxy->enclosingEnvironment());
   9046    return true;
   9047  }
   9048 
   9049  args.rval().setNull();
   9050  return true;
   9051 }
   9052 
   9053 static bool GetEnvironmentObjectType(JSContext* cx, unsigned argc, Value* vp) {
   9054  CallArgs args = CallArgsFromVp(argc, vp);
   9055  if (!args.requireAtLeast(cx, "getEnvironmentObjectType", 1)) {
   9056    return false;
   9057  }
   9058 
   9059  if (!args[0].isObject()) {
   9060    args.rval().setUndefined();
   9061    return true;
   9062  }
   9063 
   9064  JSObject* envObj = &args[0].toObject();
   9065 
   9066  if (envObj->is<EnvironmentObject>()) {
   9067    EnvironmentObject* env = &envObj->as<EnvironmentObject>();
   9068    return ReturnStringCopy(cx, args, env->typeString());
   9069  }
   9070  if (envObj->is<DebugEnvironmentProxy>()) {
   9071    DebugEnvironmentProxy* envProxy = &envObj->as<DebugEnvironmentProxy>();
   9072    EnvironmentObject* env = &envProxy->environment();
   9073    char buf[256] = {'\0'};
   9074    SprintfLiteral(buf, "[DebugProxy] %s", env->typeString());
   9075    return ReturnStringCopy(cx, args, buf);
   9076  }
   9077 
   9078  args.rval().setUndefined();
   9079  return true;
   9080 }
   9081 
   9082 static bool AssertRealmFuseInvariants(JSContext* cx, unsigned argc, Value* vp) {
   9083  CallArgs args = CallArgsFromVp(argc, vp);
   9084  // Note: This will crash if any invariant isn't held, so it's sufficient to
   9085  // simply return true always.
   9086  cx->realm()->realmFuses.assertInvariants(cx);
   9087  args.rval().setUndefined();
   9088  return true;
   9089 }
   9090 
   9091 static bool AssertRuntimeFuseInvariants(JSContext* cx, unsigned argc,
   9092                                        Value* vp) {
   9093  CallArgs args = CallArgsFromVp(argc, vp);
   9094  // Note: This will crash if any invariant isn't held, so it's sufficient to
   9095  // simply return true always.
   9096  cx->runtime()->runtimeFuses.ref().assertInvariants(cx);
   9097  args.rval().setUndefined();
   9098  return true;
   9099 }
   9100 
   9101 static bool GetFuseState(JSContext* cx, unsigned argc, Value* vp) {
   9102  CallArgs args = CallArgsFromVp(argc, vp);
   9103 
   9104  cx->realm()->realmFuses.assertInvariants(cx);
   9105 
   9106  cx->runtime()->runtimeFuses.ref().assertInvariants(cx);
   9107 
   9108  RootedObject returnObj(cx, JS_NewPlainObject(cx));
   9109  if (!returnObj) {
   9110    return false;
   9111  }
   9112 
   9113  RootedObject fuseObj(cx);
   9114  RootedValue intactValue(cx);
   9115 
   9116 #define REALM_FUSE(Name, LowerName)                                          \
   9117  fuseObj = JS_NewPlainObject(cx);                                           \
   9118  if (!fuseObj) {                                                            \
   9119    return false;                                                            \
   9120  }                                                                          \
   9121  intactValue.setBoolean(cx->realm()->realmFuses.LowerName.intact());        \
   9122  if (!JS_DefineProperty(cx, fuseObj, "intact", intactValue,                 \
   9123                         JSPROP_ENUMERATE)) {                                \
   9124    return false;                                                            \
   9125  }                                                                          \
   9126  if (!JS_DefineProperty(cx, returnObj, #Name, fuseObj, JSPROP_ENUMERATE)) { \
   9127    return false;                                                            \
   9128  }
   9129 
   9130  FOR_EACH_REALM_FUSE(REALM_FUSE)
   9131 #undef REALM_FUSE
   9132 
   9133 #define RUNTIME_FUSE(Name, LowerName)                                        \
   9134  fuseObj = JS_NewPlainObject(cx);                                           \
   9135  if (!fuseObj) {                                                            \
   9136    return false;                                                            \
   9137  }                                                                          \
   9138  intactValue.setBoolean(                                                    \
   9139      cx->runtime()->runtimeFuses.ref().LowerName.intact());                 \
   9140  if (!JS_DefineProperty(cx, fuseObj, "intact", intactValue,                 \
   9141                         JSPROP_ENUMERATE)) {                                \
   9142    return false;                                                            \
   9143  }                                                                          \
   9144  if (!JS_DefineProperty(cx, returnObj, #Name, fuseObj, JSPROP_ENUMERATE)) { \
   9145    return false;                                                            \
   9146  }
   9147 
   9148  FOR_EACH_RUNTIME_FUSE(RUNTIME_FUSE)
   9149 #undef RUNTIME_FUSE
   9150 
   9151  args.rval().setObject(*returnObj);
   9152  return true;
   9153 }
   9154 
   9155 static bool PopAllFusesInRealm(JSContext* cx, unsigned argc, Value* vp) {
   9156  CallArgs args = CallArgsFromVp(argc, vp);
   9157 
   9158  MOZ_ASSERT(cx->realm());
   9159 
   9160  RealmFuses& realmFuses = cx->realm()->realmFuses;
   9161 
   9162 #define FUSE(Name, LowerName) realmFuses.LowerName.popFuse(cx, realmFuses);
   9163  FOR_EACH_REALM_FUSE(FUSE)
   9164 #undef FUSE
   9165 
   9166  args.rval().setUndefined();
   9167  return true;
   9168 }
   9169 
   9170 static bool PopAllFusesInRuntime(JSContext* cx, unsigned argc, Value* vp) {
   9171  CallArgs args = CallArgsFromVp(argc, vp);
   9172 
   9173  RuntimeFuses& runtimeFuses = cx->runtime()->runtimeFuses.ref();
   9174 
   9175 #define FUSE(Name, LowerName) runtimeFuses.LowerName.popFuse(cx);
   9176  FOR_EACH_RUNTIME_FUSE(FUSE)
   9177 #undef FUSE
   9178 
   9179  args.rval().setUndefined();
   9180  return true;
   9181 }
   9182 
   9183 static bool GetAllPrefNames(JSContext* cx, unsigned argc, Value* vp) {
   9184  CallArgs args = CallArgsFromVp(argc, vp);
   9185 
   9186  RootedValueVector values(cx);
   9187 
   9188  auto addPref = [cx, &values](const char* name) {
   9189    JSString* s = JS_NewStringCopyZ(cx, name);
   9190    if (!s) {
   9191      return false;
   9192    }
   9193    return values.append(StringValue(s));
   9194  };
   9195 
   9196 #define ADD_NAME(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \
   9197  if (!addPref(NAME)) {                                         \
   9198    return false;                                               \
   9199  }
   9200  FOR_EACH_JS_PREF(ADD_NAME)
   9201 #undef ADD_NAME
   9202 
   9203  ArrayObject* arr = NewDenseCopiedArray(cx, values.length(), values.begin());
   9204  if (!arr) {
   9205    return false;
   9206  }
   9207 
   9208  args.rval().setObject(*arr);
   9209  return true;
   9210 }
   9211 
   9212 static bool GetPrefValue(JSContext* cx, unsigned argc, Value* vp) {
   9213  CallArgs args = CallArgsFromVp(argc, vp);
   9214  if (!args.requireAtLeast(cx, "getPrefValue", 1)) {
   9215    return false;
   9216  }
   9217 
   9218  if (!args[0].isString()) {
   9219    JS_ReportErrorASCII(cx, "expected string argument");
   9220    return false;
   9221  }
   9222 
   9223  Rooted<JSLinearString*> name(cx, args[0].toString()->ensureLinear(cx));
   9224  if (!name) {
   9225    return false;
   9226  }
   9227 
   9228  auto setReturnValue = [&args](auto value) {
   9229    using T = decltype(value);
   9230    if constexpr (std::is_same_v<T, bool>) {
   9231      args.rval().setBoolean(value);
   9232    } else {
   9233      static_assert(std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>);
   9234      args.rval().setNumber(value);
   9235    }
   9236  };
   9237 
   9238  // Search for a matching pref and return its value.
   9239 #define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \
   9240  if (StringEqualsLiteral(name, NAME)) {                          \
   9241    setReturnValue(JS::Prefs::CPP_NAME());                        \
   9242    return true;                                                  \
   9243  }
   9244  FOR_EACH_JS_PREF(CHECK_PREF)
   9245 #undef CHECK_PREF
   9246 
   9247  JS_ReportErrorASCII(cx, "invalid pref name");
   9248  return false;
   9249 }
   9250 
   9251 static bool GetErrorNotes(JSContext* cx, unsigned argc, Value* vp) {
   9252  CallArgs args = CallArgsFromVp(argc, vp);
   9253  if (!args.requireAtLeast(cx, "getErrorNotes", 1)) {
   9254    return false;
   9255  }
   9256 
   9257  if (!args[0].isObject() || !args[0].toObject().is<ErrorObject>()) {
   9258    args.rval().setNull();
   9259    return true;
   9260  }
   9261 
   9262  JSErrorReport* report = args[0].toObject().as<ErrorObject>().getErrorReport();
   9263  if (!report) {
   9264    args.rval().setNull();
   9265    return true;
   9266  }
   9267 
   9268  RootedObject notesArray(cx, CreateErrorNotesArray(cx, report));
   9269  if (!notesArray) {
   9270    return false;
   9271  }
   9272 
   9273  args.rval().setObject(*notesArray);
   9274  return true;
   9275 }
   9276 
   9277 static bool IsConstructor(JSContext* cx, unsigned argc, Value* vp) {
   9278  CallArgs args = CallArgsFromVp(argc, vp);
   9279  if (args.length() < 1) {
   9280    args.rval().setBoolean(false);
   9281  } else {
   9282    args.rval().setBoolean(IsConstructor(args[0]));
   9283  }
   9284  return true;
   9285 }
   9286 
   9287 static bool SetTimeResolution(JSContext* cx, unsigned argc, Value* vp) {
   9288  CallArgs args = CallArgsFromVp(argc, vp);
   9289  RootedObject callee(cx, &args.callee());
   9290 
   9291  if (!args.requireAtLeast(cx, "setTimeResolution", 2)) {
   9292    return false;
   9293  }
   9294 
   9295  if (!args[0].isInt32()) {
   9296    ReportUsageErrorASCII(cx, callee, "First argument must be an Int32.");
   9297    return false;
   9298  }
   9299  int32_t resolution = args[0].toInt32();
   9300 
   9301  if (!args[1].isBoolean()) {
   9302    ReportUsageErrorASCII(cx, callee, "Second argument must be a Boolean");
   9303    return false;
   9304  }
   9305  bool jitter = args[1].toBoolean();
   9306 
   9307  JS::SetTimeResolutionUsec(resolution, jitter);
   9308 
   9309  args.rval().setUndefined();
   9310  return true;
   9311 }
   9312 
   9313 static bool ScriptedCallerGlobal(JSContext* cx, unsigned argc, Value* vp) {
   9314  CallArgs args = CallArgsFromVp(argc, vp);
   9315 
   9316  RootedObject obj(cx, JS::GetScriptedCallerGlobal(cx));
   9317  if (!obj) {
   9318    args.rval().setNull();
   9319    return true;
   9320  }
   9321 
   9322  obj = ToWindowProxyIfWindow(obj);
   9323 
   9324  if (!cx->compartment()->wrap(cx, &obj)) {
   9325    return false;
   9326  }
   9327 
   9328  args.rval().setObject(*obj);
   9329  return true;
   9330 }
   9331 
   9332 static bool ObjectGlobal(JSContext* cx, unsigned argc, Value* vp) {
   9333  CallArgs args = CallArgsFromVp(argc, vp);
   9334  RootedObject callee(cx, &args.callee());
   9335 
   9336  if (!args.get(0).isObject()) {
   9337    ReportUsageErrorASCII(cx, callee, "Argument must be an object");
   9338    return false;
   9339  }
   9340 
   9341  RootedObject obj(cx, &args[0].toObject());
   9342  if (IsCrossCompartmentWrapper(obj)) {
   9343    args.rval().setNull();
   9344    return true;
   9345  }
   9346 
   9347  obj = ToWindowProxyIfWindow(&obj->nonCCWGlobal());
   9348 
   9349  args.rval().setObject(*obj);
   9350  return true;
   9351 }
   9352 
   9353 static bool IsSameCompartment(JSContext* cx, unsigned argc, Value* vp) {
   9354  CallArgs args = CallArgsFromVp(argc, vp);
   9355  RootedObject callee(cx, &args.callee());
   9356 
   9357  if (!args.get(0).isObject() || !args.get(1).isObject()) {
   9358    ReportUsageErrorASCII(cx, callee, "Both arguments must be objects");
   9359    return false;
   9360  }
   9361 
   9362  RootedObject obj1(cx, UncheckedUnwrap(&args[0].toObject()));
   9363  RootedObject obj2(cx, UncheckedUnwrap(&args[1].toObject()));
   9364 
   9365  args.rval().setBoolean(obj1->compartment() == obj2->compartment());
   9366  return true;
   9367 }
   9368 
   9369 static bool FirstGlobalInCompartment(JSContext* cx, unsigned argc, Value* vp) {
   9370  CallArgs args = CallArgsFromVp(argc, vp);
   9371  RootedObject callee(cx, &args.callee());
   9372 
   9373  if (!args.get(0).isObject()) {
   9374    ReportUsageErrorASCII(cx, callee, "Argument must be an object");
   9375    return false;
   9376  }
   9377 
   9378  RootedObject obj(cx, UncheckedUnwrap(&args[0].toObject()));
   9379  obj = ToWindowProxyIfWindow(GetFirstGlobalInCompartment(obj->compartment()));
   9380 
   9381  if (!cx->compartment()->wrap(cx, &obj)) {
   9382    return false;
   9383  }
   9384 
   9385  args.rval().setObject(*obj);
   9386  return true;
   9387 }
   9388 
   9389 static bool AssertCorrectRealm(JSContext* cx, unsigned argc, Value* vp) {
   9390  CallArgs args = CallArgsFromVp(argc, vp);
   9391  MOZ_RELEASE_ASSERT(cx->realm() == args.callee().as<JSFunction>().realm());
   9392  args.rval().setUndefined();
   9393  return true;
   9394 }
   9395 
   9396 static bool GlobalLexicals(JSContext* cx, unsigned argc, Value* vp) {
   9397  CallArgs args = CallArgsFromVp(argc, vp);
   9398 
   9399  Rooted<GlobalLexicalEnvironmentObject*> globalLexical(
   9400      cx, &cx->global()->lexicalEnvironment());
   9401 
   9402  RootedIdVector props(cx);
   9403  if (!GetPropertyKeys(cx, globalLexical, JSITER_HIDDEN, &props)) {
   9404    return false;
   9405  }
   9406 
   9407  RootedObject res(cx, JS_NewPlainObject(cx));
   9408  if (!res) {
   9409    return false;
   9410  }
   9411 
   9412  RootedValue val(cx);
   9413  for (size_t i = 0; i < props.length(); i++) {
   9414    HandleId id = props[i];
   9415    if (!JS_GetPropertyById(cx, globalLexical, id, &val)) {
   9416      return false;
   9417    }
   9418    if (val.isMagic(JS_UNINITIALIZED_LEXICAL)) {
   9419      continue;
   9420    }
   9421    if (!JS_DefinePropertyById(cx, res, id, val, JSPROP_ENUMERATE)) {
   9422      return false;
   9423    }
   9424  }
   9425 
   9426  args.rval().setObject(*res);
   9427  return true;
   9428 }
   9429 
   9430 static bool EncodeAsUtf8InBuffer(JSContext* cx, unsigned argc, Value* vp) {
   9431  CallArgs args = CallArgsFromVp(argc, vp);
   9432  if (!args.requireAtLeast(cx, "encodeAsUtf8InBuffer", 2)) {
   9433    return false;
   9434  }
   9435 
   9436  RootedObject callee(cx, &args.callee());
   9437 
   9438  if (!args[0].isString()) {
   9439    ReportUsageErrorASCII(cx, callee, "First argument must be a String");
   9440    return false;
   9441  }
   9442 
   9443  // Create the amounts array early so that the raw pointer into Uint8Array
   9444  // data has as short a lifetime as possible
   9445  Rooted<ArrayObject*> array(cx, NewDenseFullyAllocatedArray(cx, 2));
   9446  if (!array) {
   9447    return false;
   9448  }
   9449  array->ensureDenseInitializedLength(0, 2);
   9450 
   9451  JSObject* obj = args[1].isObject() ? &args[1].toObject() : nullptr;
   9452  Rooted<JS::Uint8Array> view(cx, JS::Uint8Array::unwrap(obj));
   9453  if (!view) {
   9454    ReportUsageErrorASCII(cx, callee, "Second argument must be a Uint8Array");
   9455    return false;
   9456  }
   9457 
   9458  mozilla::Span<uint8_t> span;
   9459  bool isSharedMemory = false;
   9460  {
   9461    // The hazard analysis does not track the data pointer, so it can neither
   9462    // tell that `data` is dead if ReportUsageErrorASCII is called, nor that
   9463    // its live range ends at the call to AsWritableChars(). Construct a
   9464    // temporary scope to hide from the analysis. This should really be replaced
   9465    // with a safer mechanism.
   9466    JS::AutoCheckCannotGC nogc(cx);
   9467    if (!view.isDetached()) {
   9468      span = view.get().getData(&isSharedMemory, nogc);
   9469    }
   9470  }
   9471 
   9472  if (isSharedMemory ||  // exclude views of SharedArrayBuffers
   9473      !span.data()) {    // exclude views of detached ArrayBuffers
   9474    ReportUsageErrorASCII(
   9475        cx, callee,
   9476        "Second argument must be an unshared, non-detached Uint8Array");
   9477    return false;
   9478  }
   9479 
   9480  Maybe<std::tuple<size_t, size_t>> amounts =
   9481      JS_EncodeStringToUTF8BufferPartial(cx, args[0].toString(),
   9482                                         AsWritableChars(span));
   9483  if (!amounts) {
   9484    ReportOutOfMemory(cx);
   9485    return false;
   9486  }
   9487 
   9488  auto [unitsRead, bytesWritten] = *amounts;
   9489 
   9490  array->initDenseElement(0, Int32Value(AssertedCast<int32_t>(unitsRead)));
   9491  array->initDenseElement(1, Int32Value(AssertedCast<int32_t>(bytesWritten)));
   9492 
   9493  args.rval().setObject(*array);
   9494  return true;
   9495 }
   9496 
   9497 JSScript* js::TestingFunctionArgumentToScript(
   9498    JSContext* cx, HandleValue v, JSFunction** funp /* = nullptr */) {
   9499  if (v.isString()) {
   9500    // To convert a string to a script, compile it. Parse it as an ES6 Program.
   9501    Rooted<JSString*> str(cx, v.toString());
   9502    AutoStableStringChars linearChars(cx);
   9503    if (!linearChars.initTwoByte(cx, str)) {
   9504      return nullptr;
   9505    }
   9506    SourceText<char16_t> source;
   9507    if (!source.initMaybeBorrowed(cx, linearChars)) {
   9508      return nullptr;
   9509    }
   9510 
   9511    CompileOptions options(cx);
   9512    return JS::Compile(cx, options, source);
   9513  }
   9514 
   9515  RootedFunction fun(cx, JS_ValueToFunction(cx, v));
   9516  if (!fun) {
   9517    return nullptr;
   9518  }
   9519 
   9520  if (!fun->isInterpreted()) {
   9521    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
   9522                              JSMSG_TESTING_SCRIPTS_ONLY);
   9523    return nullptr;
   9524  }
   9525 
   9526  JSScript* script = JSFunction::getOrCreateScript(cx, fun);
   9527  if (!script) {
   9528    return nullptr;
   9529  }
   9530 
   9531  if (funp) {
   9532    *funp = fun;
   9533  }
   9534 
   9535  return script;
   9536 }
   9537 
   9538 static bool BaselineCompile(JSContext* cx, unsigned argc, Value* vp) {
   9539  CallArgs args = CallArgsFromVp(argc, vp);
   9540  RootedObject callee(cx, &args.callee());
   9541 
   9542  RootedScript script(cx);
   9543  if (args.length() == 0) {
   9544    NonBuiltinScriptFrameIter iter(cx);
   9545    if (iter.done()) {
   9546      ReportUsageErrorASCII(cx, callee,
   9547                            "no script argument and no script caller");
   9548      return false;
   9549    }
   9550    script = iter.script();
   9551  } else {
   9552    script = TestingFunctionArgumentToScript(cx, args[0]);
   9553    if (!script) {
   9554      return false;
   9555    }
   9556  }
   9557 
   9558  bool forceDebug = false;
   9559  if (args.length() > 1) {
   9560    if (args.length() > 2) {
   9561      ReportUsageErrorASCII(cx, callee, "too many arguments");
   9562      return false;
   9563    }
   9564    if (!args[1].isBoolean() && !args[1].isUndefined()) {
   9565      ReportUsageErrorASCII(
   9566          cx, callee, "forceDebugInstrumentation argument should be boolean");
   9567      return false;
   9568    }
   9569    forceDebug = ToBoolean(args[1]);
   9570  }
   9571 
   9572  const char* returnedStr = nullptr;
   9573  do {
   9574    // In order to check for differential behaviour, baselineCompile should have
   9575    // the same output whether --no-baseline is used or not.
   9576    if (js::SupportDifferentialTesting()) {
   9577      returnedStr = "skipped (differential testing)";
   9578      break;
   9579    }
   9580 
   9581    AutoRealm ar(cx, script);
   9582    if (script->isBaselineCompilingOffThread()) {
   9583      CancelOffThreadBaselineCompile(script);
   9584    }
   9585 
   9586    if (script->hasBaselineScript()) {
   9587      if (forceDebug && !script->baselineScript()->hasDebugInstrumentation()) {
   9588        // There isn't an easy way to do this for a script that might be on
   9589        // stack right now. See
   9590        // js::jit::RecompileOnStackBaselineScriptsForDebugMode.
   9591        ReportUsageErrorASCII(
   9592            cx, callee, "unsupported case: recompiling script for debug mode");
   9593        return false;
   9594      }
   9595 
   9596      args.rval().setUndefined();
   9597      return true;
   9598    }
   9599 
   9600    if (!jit::IsBaselineJitEnabled(cx)) {
   9601      returnedStr = "baseline disabled";
   9602      break;
   9603    }
   9604    if (!script->canBaselineCompile()) {
   9605      returnedStr = "can't compile";
   9606      break;
   9607    }
   9608    if (!cx->zone()->ensureJitZoneExists(cx)) {
   9609      return false;
   9610    }
   9611 
   9612    jit::BaselineOptions options = {
   9613        jit::BaselineOption::ForceMainThreadCompilation};
   9614    if (forceDebug) {
   9615      options.setFlag(jit::BaselineOption::ForceDebugInstrumentation);
   9616    }
   9617    jit::MethodStatus status = jit::BaselineCompile(cx, script, options);
   9618    switch (status) {
   9619      case jit::Method_Error:
   9620        return false;
   9621      case jit::Method_CantCompile:
   9622        returnedStr = "can't compile";
   9623        break;
   9624      case jit::Method_Skipped:
   9625        returnedStr = "skipped";
   9626        break;
   9627      case jit::Method_Compiled:
   9628        args.rval().setUndefined();
   9629    }
   9630  } while (false);
   9631 
   9632  if (returnedStr) {
   9633    return ReturnStringCopy(cx, args, returnedStr);
   9634  }
   9635 
   9636  return true;
   9637 }
   9638 
   9639 static bool SetBaselineHint(JSContext* cx, unsigned argc, Value* vp) {
   9640  CallArgs args = CallArgsFromVp(argc, vp);
   9641  RootedObject callee(cx, &args.callee());
   9642 
   9643  if (args.length() != 1) {
   9644    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   9645    return false;
   9646  }
   9647 
   9648  RootedScript script(cx, TestingFunctionArgumentToScript(cx, args[0]));
   9649  if (!script) {
   9650    return false;
   9651  }
   9652 
   9653  if (!cx->runtime()->jitRuntime() ||
   9654      !cx->runtime()->jitRuntime()->hasJitHintsMap()) {
   9655    args.rval().setUndefined();
   9656    return true;
   9657  }
   9658 
   9659  jit::JitHintsMap* jitHints = cx->runtime()->jitRuntime()->getJitHintsMap();
   9660  jitHints->setEagerBaselineHint(script);
   9661 
   9662  args.rval().setUndefined();
   9663  return true;
   9664 }
   9665 
   9666 static bool HasBaselineHint(JSContext* cx, unsigned argc, Value* vp) {
   9667  CallArgs args = CallArgsFromVp(argc, vp);
   9668  RootedObject callee(cx, &args.callee());
   9669 
   9670  if (args.length() != 1) {
   9671    ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
   9672    return false;
   9673  }
   9674 
   9675  RootedScript script(cx, TestingFunctionArgumentToScript(cx, args[0]));
   9676  if (!script) {
   9677    return false;
   9678  }
   9679 
   9680  if (!cx->runtime()->jitRuntime() ||
   9681      !cx->runtime()->jitRuntime()->hasJitHintsMap()) {
   9682    args.rval().setBoolean(false);
   9683    return true;
   9684  }
   9685 
   9686  jit::JitHintsMap* jitHints = cx->runtime()->jitRuntime()->getJitHintsMap();
   9687  bool hasHint = jitHints->mightHaveEagerBaselineHint(script);
   9688 
   9689  args.rval().setBoolean(hasHint);
   9690  return true;
   9691 }
   9692 
   9693 static bool ClearKeptObjects(JSContext* cx, unsigned argc, Value* vp) {
   9694  CallArgs args = CallArgsFromVp(argc, vp);
   9695  JS::ClearKeptObjects(cx);
   9696  args.rval().setUndefined();
   9697  return true;
   9698 }
   9699 
   9700 static bool NumberToDouble(JSContext* cx, unsigned argc, Value* vp) {
   9701  CallArgs args = CallArgsFromVp(argc, vp);
   9702  if (!args.requireAtLeast(cx, "numberToDouble", 1)) {
   9703    return false;
   9704  }
   9705 
   9706  if (!args[0].isNumber()) {
   9707    RootedObject callee(cx, &args.callee());
   9708    ReportUsageErrorASCII(cx, callee, "argument must be a number");
   9709    return false;
   9710  }
   9711 
   9712  args.rval().setDouble(args[0].toNumber());
   9713  return true;
   9714 }
   9715 
   9716 static bool GetICUOptions(JSContext* cx, unsigned argc, Value* vp) {
   9717  CallArgs args = CallArgsFromVp(argc, vp);
   9718 
   9719  RootedObject info(cx, JS_NewPlainObject(cx));
   9720  if (!info) {
   9721    return false;
   9722  }
   9723 
   9724 #ifdef JS_HAS_INTL_API
   9725  RootedString str(cx);
   9726 
   9727  str = NewStringCopy<CanGC>(cx, mozilla::intl::ICU4CLibrary::GetVersion());
   9728  if (!str || !JS_DefineProperty(cx, info, "version", str, JSPROP_ENUMERATE)) {
   9729    return false;
   9730  }
   9731 
   9732  str = NewStringCopy<CanGC>(cx, mozilla::intl::String::GetUnicodeVersion());
   9733  if (!str || !JS_DefineProperty(cx, info, "unicode", str, JSPROP_ENUMERATE)) {
   9734    return false;
   9735  }
   9736 
   9737  str = NewStringCopyZ<CanGC>(cx, mozilla::intl::Locale::GetDefaultLocale());
   9738  if (!str || !JS_DefineProperty(cx, info, "locale", str, JSPROP_ENUMERATE)) {
   9739    return false;
   9740  }
   9741 
   9742  auto tzdataVersion = mozilla::intl::TimeZone::GetTZDataVersion();
   9743  if (tzdataVersion.isErr()) {
   9744    intl::ReportInternalError(cx, tzdataVersion.unwrapErr());
   9745    return false;
   9746  }
   9747 
   9748  str = NewStringCopy<CanGC>(cx, tzdataVersion.unwrap());
   9749  if (!str || !JS_DefineProperty(cx, info, "tzdata", str, JSPROP_ENUMERATE)) {
   9750    return false;
   9751  }
   9752 
   9753  TimeZoneIdentifierVector timeZoneId;
   9754  if (!DateTimeInfo::timeZoneId(nullptr, timeZoneId)) {
   9755    ReportOutOfMemory(cx);
   9756    return false;
   9757  }
   9758 
   9759  str = NewStringCopy<CanGC>(
   9760      cx, static_cast<mozilla::Span<const char>>(timeZoneId));
   9761  if (!str || !JS_DefineProperty(cx, info, "timezone", str, JSPROP_ENUMERATE)) {
   9762    return false;
   9763  }
   9764 
   9765  intl::FormatBuffer<char16_t, intl::INITIAL_CHAR_BUFFER_SIZE> buf(cx);
   9766  if (auto ok = mozilla::intl::TimeZone::GetHostTimeZone(buf); ok.isErr()) {
   9767    intl::ReportInternalError(cx, ok.unwrapErr());
   9768    return false;
   9769  }
   9770 
   9771  str = buf.toString(cx);
   9772  if (!str ||
   9773      !JS_DefineProperty(cx, info, "host-timezone", str, JSPROP_ENUMERATE)) {
   9774    return false;
   9775  }
   9776 #endif
   9777 
   9778  args.rval().setObject(*info);
   9779  return true;
   9780 }
   9781 
   9782 static bool GetAvailableLocalesOf(JSContext* cx, unsigned argc, Value* vp) {
   9783  CallArgs args = CallArgsFromVp(argc, vp);
   9784  RootedObject callee(cx, &args.callee());
   9785 
   9786  if (!args.requireAtLeast(cx, "getAvailableLocalesOf", 1)) {
   9787    return false;
   9788  }
   9789 
   9790  HandleValue arg = args[0];
   9791  if (!arg.isString()) {
   9792    ReportUsageErrorASCII(cx, callee, "First argument must be a string");
   9793    return false;
   9794  }
   9795 
   9796  ArrayObject* result;
   9797 #ifdef JS_HAS_INTL_API
   9798  using AvailableLocaleKind = js::intl::AvailableLocaleKind;
   9799 
   9800  AvailableLocaleKind kind;
   9801  {
   9802    JSLinearString* typeStr = arg.toString()->ensureLinear(cx);
   9803    if (!typeStr) {
   9804      return false;
   9805    }
   9806 
   9807    if (StringEqualsLiteral(typeStr, "Collator")) {
   9808      kind = AvailableLocaleKind::Collator;
   9809    } else if (StringEqualsLiteral(typeStr, "DateTimeFormat")) {
   9810      kind = AvailableLocaleKind::DateTimeFormat;
   9811    } else if (StringEqualsLiteral(typeStr, "DisplayNames")) {
   9812      kind = AvailableLocaleKind::DisplayNames;
   9813    } else if (StringEqualsLiteral(typeStr, "DurationFormat")) {
   9814      kind = AvailableLocaleKind::DurationFormat;
   9815    } else if (StringEqualsLiteral(typeStr, "ListFormat")) {
   9816      kind = AvailableLocaleKind::ListFormat;
   9817    } else if (StringEqualsLiteral(typeStr, "NumberFormat")) {
   9818      kind = AvailableLocaleKind::NumberFormat;
   9819    } else if (StringEqualsLiteral(typeStr, "PluralRules")) {
   9820      kind = AvailableLocaleKind::PluralRules;
   9821    } else if (StringEqualsLiteral(typeStr, "RelativeTimeFormat")) {
   9822      kind = AvailableLocaleKind::RelativeTimeFormat;
   9823    } else if (StringEqualsLiteral(typeStr, "Segmenter")) {
   9824      kind = AvailableLocaleKind::Segmenter;
   9825    } else {
   9826      ReportUsageErrorASCII(cx, callee, "Unsupported Intl constructor name");
   9827      return false;
   9828    }
   9829  }
   9830 
   9831  intl::SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref();
   9832  result = sharedIntlData.availableLocalesOf(cx, kind);
   9833 #else
   9834  result = NewDenseEmptyArray(cx);
   9835 #endif
   9836  if (!result) {
   9837    return false;
   9838  }
   9839 
   9840  args.rval().setObject(*result);
   9841  return true;
   9842 }
   9843 
   9844 static bool IsSmallFunction(JSContext* cx, unsigned argc, Value* vp) {
   9845  CallArgs args = CallArgsFromVp(argc, vp);
   9846  RootedObject callee(cx, &args.callee());
   9847 
   9848  if (!args.requireAtLeast(cx, "IsSmallFunction", 1)) {
   9849    return false;
   9850  }
   9851 
   9852  HandleValue arg = args[0];
   9853  if (!arg.isObject() || !arg.toObject().is<JSFunction>()) {
   9854    ReportUsageErrorASCII(cx, callee, "First argument must be a function");
   9855    return false;
   9856  }
   9857 
   9858  RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
   9859  if (!fun->isInterpreted()) {
   9860    ReportUsageErrorASCII(cx, callee,
   9861                          "First argument must be an interpreted function");
   9862    return false;
   9863  }
   9864 
   9865  JSScript* script = JSFunction::getOrCreateScript(cx, fun);
   9866  if (!script) {
   9867    return false;
   9868  }
   9869 
   9870  args.rval().setBoolean(jit::JitOptions.isSmallFunction(script));
   9871  return true;
   9872 }
   9873 
   9874 static bool PCCountProfiling_Start(JSContext* cx, unsigned argc, Value* vp) {
   9875  CallArgs args = CallArgsFromVp(argc, vp);
   9876 
   9877  JS::StartPCCountProfiling(cx);
   9878 
   9879  args.rval().setUndefined();
   9880  return true;
   9881 }
   9882 
   9883 static bool PCCountProfiling_Stop(JSContext* cx, unsigned argc, Value* vp) {
   9884  CallArgs args = CallArgsFromVp(argc, vp);
   9885 
   9886  JS::StopPCCountProfiling(cx);
   9887 
   9888  args.rval().setUndefined();
   9889  return true;
   9890 }
   9891 
   9892 static bool PCCountProfiling_Purge(JSContext* cx, unsigned argc, Value* vp) {
   9893  CallArgs args = CallArgsFromVp(argc, vp);
   9894 
   9895  JS::PurgePCCounts(cx);
   9896 
   9897  args.rval().setUndefined();
   9898  return true;
   9899 }
   9900 
   9901 static bool PCCountProfiling_ScriptCount(JSContext* cx, unsigned argc,
   9902                                         Value* vp) {
   9903  CallArgs args = CallArgsFromVp(argc, vp);
   9904 
   9905  size_t length = JS::GetPCCountScriptCount(cx);
   9906 
   9907  args.rval().setNumber(double(length));
   9908  return true;
   9909 }
   9910 
   9911 static bool PCCountProfiling_ScriptSummary(JSContext* cx, unsigned argc,
   9912                                           Value* vp) {
   9913  CallArgs args = CallArgsFromVp(argc, vp);
   9914  if (!args.requireAtLeast(cx, "summary", 1)) {
   9915    return false;
   9916  }
   9917 
   9918  uint32_t index;
   9919  if (!JS::ToUint32(cx, args[0], &index)) {
   9920    return false;
   9921  }
   9922 
   9923  JSString* str = JS::GetPCCountScriptSummary(cx, index);
   9924  if (!str) {
   9925    return false;
   9926  }
   9927 
   9928  args.rval().setString(str);
   9929  return true;
   9930 }
   9931 
   9932 static bool PCCountProfiling_ScriptContents(JSContext* cx, unsigned argc,
   9933                                            Value* vp) {
   9934  CallArgs args = CallArgsFromVp(argc, vp);
   9935  if (!args.requireAtLeast(cx, "contents", 1)) {
   9936    return false;
   9937  }
   9938 
   9939  uint32_t index;
   9940  if (!JS::ToUint32(cx, args[0], &index)) {
   9941    return false;
   9942  }
   9943 
   9944  JSString* str = JS::GetPCCountScriptContents(cx, index);
   9945  if (!str) {
   9946    return false;
   9947  }
   9948 
   9949  args.rval().setString(str);
   9950  return true;
   9951 }
   9952 
   9953 static bool NukeCCW(JSContext* cx, unsigned argc, Value* vp) {
   9954  CallArgs args = CallArgsFromVp(argc, vp);
   9955 
   9956  if (args.length() != 1 || !args[0].isObject() ||
   9957      !IsCrossCompartmentWrapper(&args[0].toObject())) {
   9958    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS,
   9959                              "nukeCCW");
   9960    return false;
   9961  }
   9962 
   9963  NukeCrossCompartmentWrapper(cx, &args[0].toObject());
   9964  args.rval().setUndefined();
   9965  return true;
   9966 }
   9967 
   9968 static bool IsCCW(JSContext* cx, unsigned argc, Value* vp) {
   9969  CallArgs args = CallArgsFromVp(argc, vp);
   9970 
   9971  if (args.length() != 1 || !args[0].isObject()) {
   9972    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS,
   9973                              "IsCCW");
   9974    return false;
   9975  }
   9976 
   9977  args.rval().setBoolean(IsCrossCompartmentWrapper(&args[0].toObject()));
   9978  return true;
   9979 }
   9980 
   9981 static bool FdLibM_Pow(JSContext* cx, unsigned argc, Value* vp) {
   9982  CallArgs args = CallArgsFromVp(argc, vp);
   9983 
   9984  double x;
   9985  if (!JS::ToNumber(cx, args.get(0), &x)) {
   9986    return false;
   9987  }
   9988 
   9989  double y;
   9990  if (!JS::ToNumber(cx, args.get(1), &y)) {
   9991    return false;
   9992  }
   9993 
   9994  // Because C99 and ECMA specify different behavior for pow(), we need to wrap
   9995  // the fdlibm call to make it ECMA compliant.
   9996  if (!std::isfinite(y) && (x == 1.0 || x == -1.0)) {
   9997    args.rval().setNaN();
   9998  } else {
   9999    args.rval().setDouble(fdlibm_pow(x, y));
  10000  }
  10001  return true;
  10002 }
  10003 
  10004 static bool HadOutOfMemory(JSContext* cx, unsigned argc, Value* vp) {
  10005  CallArgs args = CallArgsFromVp(argc, vp);
  10006  args.rval().setBoolean(cx->runtime()->hadOutOfMemory);
  10007  return true;
  10008 }
  10009 
  10010 static bool WaitForDone(JSContext* cx, unsigned argc, Value* vp) {
  10011  CallArgs args = CallArgsFromVp(argc, vp);
  10012  if (args.length() != 1) {
  10013    JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
  10014    return false;
  10015  }
  10016 
  10017  if (!args[0].isObject()) {
  10018    JS_ReportErrorASCII(cx, "The first argument should be an object.");
  10019    return false;
  10020  }
  10021 
  10022  RootedObject obj(cx, &args[0].toObject());
  10023  RootedValue val(cx);
  10024  if (!JS_GetProperty(cx, obj, "done", &val)) {
  10025    return false;
  10026  }
  10027 
  10028  args.rval().setUndefined();
  10029 
  10030  while (!val.isBoolean() || !val.toBoolean()) {
  10031    js::RunJobs(cx);
  10032    if (!JS_GetProperty(cx, obj, "done", &val)) {
  10033      return false;
  10034    }
  10035  }
  10036  return true;
  10037 }
  10038 
  10039 static bool TestingFunc_SupportDifferentialTesting(JSContext* cx, unsigned argc,
  10040                                                   Value* vp) {
  10041  CallArgs args = CallArgsFromVp(argc, vp);
  10042  args.rval().setBoolean(SupportDifferentialTesting());
  10043  return true;
  10044 }
  10045 
  10046 static bool GetLastOOMStackTrace(JSContext* cx, unsigned argc, Value* vp) {
  10047  CallArgs args = CallArgsFromVp(argc, vp);
  10048 
  10049  if (!cx->hasOOMStackTrace()) {
  10050    args.rval().setNull();
  10051    return true;
  10052  }
  10053 
  10054  const char* stackTrace = cx->getOOMStackTrace();
  10055  MOZ_ASSERT(stackTrace);
  10056 
  10057  // The stackTrace persists, so we can clear the flag here in case copying the
  10058  // string fails
  10059  cx->unsetOOMStackTrace();
  10060 
  10061  JSString* str = JS_NewStringCopyZ(cx, stackTrace);
  10062  if (!str) {
  10063    return false;
  10064  }
  10065 
  10066  args.rval().setString(str);
  10067  return true;
  10068 }
  10069 
  10070 // clang-format off
  10071 static const JSFunctionSpecWithHelp TestingFunctions[] = {
  10072    JS_FN_HELP("gc", ::GC, 0, 0,
  10073 "gc([obj] | 'zone' [, ('shrinking' | 'last-ditch') ])",
  10074 "  Run the garbage collector.\n"
  10075 "  The first parameter describes which zones to collect: if an object is\n"
  10076 "  given, GC only its zone. If 'zone' is given, GC any zones that were\n"
  10077 "  scheduled via schedulegc.\n"
  10078 "  The second parameter is optional and may be 'shrinking' to perform a\n"
  10079 "  shrinking GC or 'last-ditch' for a shrinking, last-ditch GC."),
  10080 
  10081    JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
  10082 "minorgc([aboutToOverflow])",
  10083 "  Run a minor collector on the Nursery. When aboutToOverflow is true, marks\n"
  10084 "  the store buffer as about-to-overflow before collecting."),
  10085 
  10086    JS_FN_HELP("maybegc", ::MaybeGC, 0, 0,
  10087 "maybegc()",
  10088 "  Hint to the engine that now is an ok time to run the garbage collector.\n"),
  10089 
  10090    JS_FN_HELP("gcparam", GCParameter, 2, 0,
  10091 "gcparam(name [, value])",
  10092 "  Wrapper for JS_[GS]etGCParameter. The name is one of:" GC_PARAMETER_ARGS_LIST),
  10093 
  10094    JS_FN_HELP("finishBackgroundFree", FinishBackgroundFree, 0, 0,
  10095 "finishBackgroundFree()",
  10096 "  Wait for the GC's background free task to finish.\n"),
  10097 
  10098    JS_FN_HELP("hasDisassembler", HasDisassembler, 0, 0,
  10099 "hasDisassembler()",
  10100 "  Return true if a disassembler is present (for disnative and wasmDis)."),
  10101 
  10102    JS_FN_HELP("disnative", DisassembleNative, 2, 0,
  10103 "disnative(fun,[path])",
  10104 "  Disassemble a function into its native code. Optionally write the native code bytes to a file on disk.\n"),
  10105 
  10106    JS_FN_HELP("disblic", DisassembleBaselineICs, 1, 0,
  10107 "disblic(fun)",
  10108 "  Disassemble the baseline ICs for a function into native code.\n"),
  10109 
  10110    JS_FN_HELP("relazifyFunctions", RelazifyFunctions, 0, 0,
  10111 "relazifyFunctions(...)",
  10112 "  Perform a GC and allow relazification of functions. Accepts the same\n"
  10113 "  arguments as gc()."),
  10114 
  10115    JS_FN_HELP("getBuildConfiguration", GetBuildConfiguration, 1, 0,
  10116 "getBuildConfiguration([option])",
  10117 "  Query the options SpiderMonkey was built with, or return an object\n"
  10118 "  with the options if no argument is given."),
  10119 
  10120    JS_FN_HELP("getRealmConfiguration", GetRealmConfiguration, 1, 0,
  10121 "getRealmConfiguration([option])",
  10122 "  Query the runtime options SpiderMonkey is running with, or return an\n."
  10123 "  object with the options if no argument is given."),
  10124 
  10125    JS_FN_HELP("isLcovEnabled", ::IsLCovEnabled, 0, 0,
  10126 "isLcovEnabled()",
  10127 "  Return true if JS LCov support is enabled."),
  10128 
  10129  JS_FN_HELP("trialInline", TrialInline, 0, 0,
  10130 "trialInline()",
  10131 "  Perform trial-inlining for the caller's frame if it's a BaselineFrame."),
  10132 
  10133    JS_FN_HELP("hasChild", HasChild, 0, 0,
  10134 "hasChild(parent, child)",
  10135 "  Return true if |child| is a child of |parent|, as determined by a call to\n"
  10136 "  TraceChildren"),
  10137 
  10138    JS_FN_HELP("setSavedStacksRNGState", SetSavedStacksRNGState, 1, 0,
  10139 "setSavedStacksRNGState(seed)",
  10140 "  Set this compartment's SavedStacks' RNG state.\n"),
  10141 
  10142    JS_FN_HELP("getSavedFrameCount", GetSavedFrameCount, 0, 0,
  10143 "getSavedFrameCount()",
  10144 "  Return the number of SavedFrame instances stored in this compartment's\n"
  10145 "  SavedStacks cache."),
  10146 
  10147    JS_FN_HELP("clearSavedFrames", ClearSavedFrames, 0, 0,
  10148 "clearSavedFrames()",
  10149 "  Empty the current compartment's cache of SavedFrame objects, so that\n"
  10150 "  subsequent stack captures allocate fresh objects to represent frames.\n"
  10151 "  Clear the current stack's LiveSavedFrameCaches."),
  10152 
  10153    JS_FN_HELP("saveStack", SaveStack, 0, 0,
  10154 "saveStack([maxDepth [, compartment]])",
  10155 "  Capture a stack. If 'maxDepth' is given, capture at most 'maxDepth' number\n"
  10156 "  of frames. If 'compartment' is given, allocate the js::SavedFrame instances\n"
  10157 "  with the given object's compartment."),
  10158 
  10159    JS_FN_HELP("captureFirstSubsumedFrame", CaptureFirstSubsumedFrame, 1, 0,
  10160 "saveStack(object [, shouldIgnoreSelfHosted = true]])",
  10161 "  Capture a stack back to the first frame whose principals are subsumed by the\n"
  10162 "  object's compartment's principals. If 'shouldIgnoreSelfHosted' is given,\n"
  10163 "  control whether self-hosted frames are considered when checking principals."),
  10164 
  10165    JS_FN_HELP("callFunctionFromNativeFrame", CallFunctionFromNativeFrame, 1, 0,
  10166 "callFunctionFromNativeFrame(function)",
  10167 "  Call 'function' with a (C++-)native frame on stack.\n"
  10168 "  Required for testing that SaveStack properly handles native frames."),
  10169 
  10170    JS_FN_HELP("callFunctionWithAsyncStack", CallFunctionWithAsyncStack, 0, 0,
  10171 "callFunctionWithAsyncStack(function, stack, asyncCause)",
  10172 "  Call 'function', using the provided stack as the async stack responsible\n"
  10173 "  for the call, and propagate its return value or the exception it throws.\n"
  10174 "  The function is called with no arguments, and 'this' is 'undefined'. The\n"
  10175 "  specified |asyncCause| is attached to the provided stack frame."),
  10176 
  10177    JS_FN_HELP("enableTrackAllocations", EnableTrackAllocations, 0, 0,
  10178 "enableTrackAllocations()",
  10179 "  Start capturing the JS stack at every allocation. Note that this sets an\n"
  10180 "  object metadata callback that will override any other object metadata\n"
  10181 "  callback that may be set."),
  10182 
  10183    JS_FN_HELP("disableTrackAllocations", DisableTrackAllocations, 0, 0,
  10184 "disableTrackAllocations()",
  10185 "  Stop capturing the JS stack at every allocation."),
  10186 
  10187    JS_FN_HELP("setTestFilenameValidationCallback", SetTestFilenameValidationCallback, 0, 0,
  10188 "setTestFilenameValidationCallback()",
  10189 "  Set the filename validation callback to a callback that accepts only\n"
  10190 "  filenames starting with 'safe' or (only in system realms) 'system'."),
  10191 
  10192    JS_FN_HELP("newObjectWithAddPropertyHook", NewObjectWithAddPropertyHook, 0, 0,
  10193 "newObjectWithAddPropertyHook()",
  10194 "  Returns a new object with an addProperty JSClass hook. This hook\n"
  10195 "  increments the value of the _propertiesAdded data property on the object\n"
  10196 "  when a new property is added."),
  10197 
  10198    JS_FN_HELP("newObjectWithCallHook", NewObjectWithCallHook, 0, 0,
  10199 "newObjectWithCallHook()",
  10200 "  Returns a new object with call/construct JSClass hooks. These hooks return\n"
  10201 "  a new object that contains the Values supplied by the caller."),
  10202 
  10203    JS_FN_HELP("newObjectWithManyReservedSlots", NewObjectWithManyReservedSlots, 0, 0,
  10204 "newObjectWithManyReservedSlots()",
  10205 "  Returns a new object with many reserved slots. The slots are initialized to int32\n"
  10206 "  values. checkObjectWithManyReservedSlots can be used to check the slots still\n"
  10207 "  hold these values."),
  10208 
  10209    JS_FN_HELP("checkObjectWithManyReservedSlots", CheckObjectWithManyReservedSlots, 1, 0,
  10210 "checkObjectWithManyReservedSlots(obj)",
  10211 "  Checks the reserved slots set by newObjectWithManyReservedSlots still hold the expected\n"
  10212 "  values."),
  10213 
  10214    JS_FN_HELP("addObjectFuse", AddObjectFuse, 1, 0,
  10215 "addObjectFuse(object)",
  10216 "  Mark 'object' as having an ObjectFuse."),
  10217 
  10218    JS_FN_HELP("getObjectFuseState", GetObjectFuseState, 1, 0,
  10219 "getObjectFuseState(object)",
  10220 "  Returns a new object that contains information about the ObjectFuse of 'object'."),
  10221 
  10222    JS_FN_HELP("getWatchtowerLog", GetWatchtowerLog, 0, 0,
  10223 "getWatchtowerLog()",
  10224 "  Returns the Watchtower log recording object changes for objects for which\n"
  10225 "  addWatchtowerTarget was called. The internal log is cleared. The return\n"
  10226 "  value is an array of plain objects with the following properties:\n"
  10227 "  - kind: a string describing the kind of mutation, for example \"add-prop\"\n"
  10228 "  - object: the object being mutated\n"
  10229 "  - extra: an extra value, for example the name of the property being added"),
  10230 
  10231    JS_FN_HELP("addWatchtowerTarget", AddWatchtowerTarget, 1, 0,
  10232 "addWatchtowerTarget(object)",
  10233 "  Invoke the watchtower callback for changes to this object."),
  10234 
  10235    JS_FN_HELP("newString", NewString, 2, 0,
  10236 "newString(str[, options])",
  10237 "  Copies str's chars and returns a new string. Valid options:\n"
  10238 "  \n"
  10239 "   - tenured: allocate directly into the tenured heap.\n"
  10240 "  \n"
  10241 "   - twoByte: create a \"two byte\" string, not a latin1 string, regardless of the\n"
  10242 "      input string's characters. Latin1 will be used by default if possible\n"
  10243 "      (again regardless of the input string.)\n"
  10244 "  \n"
  10245 "   - newStringBuffer: create a new string that uses a refcounted StringBuffer for\n"
  10246 "     the characters.\n"
  10247 "  \n"
  10248 "   - shareStringBuffer: create a new string that shares str's StringBuffer.\n"
  10249 "  \n"
  10250 "   - external: create an external string. External strings are always twoByte and\n"
  10251 "     tenured.\n"
  10252 "  \n"
  10253 "   - maybeExternal: create an external string, unless the data fits within an\n"
  10254 "     inline string. Inline strings may be nursery-allocated.\n"
  10255 "  \n"
  10256 "   - capacity: create an extensible string with the given capacity"),
  10257 
  10258    JS_FN_HELP("newDependentString", NewDependentString, 2, 0,
  10259 "newDependentString(str, indexStart[, indexEnd] [, options])",
  10260 "  Essentially the same as str.substring() but insist on\n"
  10261 "  creating a dependent string and failing if not. Also has options to\n"
  10262 "  control the heap the string object is allocated into:\n"
  10263 "  \n"
  10264 "   - tenured: if true, allocate in the tenured heap or throw. If false,\n"
  10265 "     allocate in the nursery or throw.\n"
  10266 "   - suppress-contraction: prevent the optimization of using a base's base rather\n"
  10267 "     than creating a chain of dependent string bases."),
  10268 
  10269    JS_FN_HELP("ensureLinearString", EnsureLinearString, 1, 0,
  10270 "ensureLinearString(str)",
  10271 "  Ensures str is a linear (non-rope) string and returns it."),
  10272 
  10273    JS_FN_HELP("representativeStringArray", RepresentativeStringArray, 0, 0,
  10274 "representativeStringArray()",
  10275 "  Returns an array of strings that represent the various internal string\n"
  10276 "  types and character encodings."),
  10277 
  10278 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
  10279 
  10280    JS_FN_HELP("oomThreadTypes", OOMThreadTypes, 0, 0,
  10281 "oomThreadTypes()",
  10282 "  Get the number of thread types that can be used as an argument for\n"
  10283 "  oomAfterAllocations() and oomAtAllocation()."),
  10284 
  10285    JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 2, 0,
  10286 "oomAfterAllocations(count [,threadType])",
  10287 "  After 'count' js_malloc memory allocations, fail every following allocation\n"
  10288 "  (return nullptr). The optional thread type limits the effect to the\n"
  10289 "  specified type of helper thread."),
  10290 
  10291    JS_FN_HELP("oomAtAllocation", OOMAtAllocation, 2, 0,
  10292 "oomAtAllocation(count [,threadType])",
  10293 "  After 'count' js_malloc memory allocations, fail the next allocation\n"
  10294 "  (return nullptr). The optional thread type limits the effect to the\n"
  10295 "  specified type of helper thread."),
  10296 
  10297    JS_FN_HELP("resetOOMFailure", ResetOOMFailure, 0, 0,
  10298 "resetOOMFailure()",
  10299 "  Remove the allocation failure scheduled by either oomAfterAllocations() or\n"
  10300 "  oomAtAllocation() and return whether any allocation had been caused to fail."),
  10301 
  10302    JS_FN_HELP("oomTest", OOMTest, 0, 0,
  10303 "oomTest(function, [expectExceptionOnFailure = true | options])",
  10304 "  Test that the passed function behaves correctly under OOM conditions by\n"
  10305 "  repeatedly executing it and simulating allocation failure at successive\n"
  10306 "  allocations until the function completes without seeing a failure.\n"
  10307 "  By default this tests that an exception is raised if execution fails, but\n"
  10308 "  this can be disabled by passing false as the optional second parameter.\n"
  10309 "  This is also disabled when --fuzzing-safe is specified.\n"
  10310 "  Alternatively an object can be passed to set the following options:\n"
  10311 "    expectExceptionOnFailure: bool - as described above.\n"
  10312 "    keepFailing: bool - continue to fail after first simulated failure.\n"
  10313 "\n"
  10314 "  WARNING: By design, oomTest assumes the test-function follows the same\n"
  10315 "  code path each time it is called, right up to the point where OOM occurs.\n"
  10316 "  If on iteration 70 it finishes and caches a unit of work that saves 65\n"
  10317 "  allocations the next time we run, then the subsequent 65 allocation\n"
  10318 "  points will go untested.\n"
  10319 "\n"
  10320 "  Things in this category include lazy parsing and baseline compilation,\n"
  10321 "  so it is very easy to accidentally write an oomTest that only tests one\n"
  10322 "  or the other of those, and not the functionality you meant to test!\n"
  10323 "  To avoid lazy parsing, call the test function once first before passing\n"
  10324 "  it to oomTest. The jits can be disabled via the test harness.\n"),
  10325 
  10326    JS_FN_HELP("stackTest", StackTest, 0, 0,
  10327 "stackTest(function, [expectExceptionOnFailure = true])",
  10328 "  This function behaves exactly like oomTest with the difference that\n"
  10329 "  instead of simulating regular OOM conditions, it simulates the engine\n"
  10330 "  running out of stack space (failing recursion check).\n"
  10331 "\n"
  10332 "  See the WARNING in help('oomTest').\n"),
  10333 
  10334    JS_FN_HELP("interruptTest", InterruptTest, 0, 0,
  10335 "interruptTest(function)",
  10336 "  This function simulates interrupts similar to how oomTest simulates OOM conditions."
  10337 "\n"
  10338 "  See the WARNING in help('oomTest').\n"),
  10339 
  10340 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
  10341 
  10342    JS_FN_HELP("newRope", NewRope, 3, 0,
  10343 "newRope(left, right[, options])",
  10344 "  Creates a rope with the given left/right strings.\n"
  10345 "  Available options:\n"
  10346 "    nursery: bool - force the string to be created in/out of the nursery, if possible.\n"),
  10347 
  10348    JS_FN_HELP("isRope", IsRope, 1, 0,
  10349 "isRope(str)",
  10350 "  Returns true if the parameter is a rope"),
  10351 
  10352    JS_FN_HELP("settlePromiseNow", SettlePromiseNow, 1, 0,
  10353 "settlePromiseNow(promise)",
  10354 "  'Settle' a 'promise' immediately. This just marks the promise as resolved\n"
  10355 "  with a value of `undefined` and causes the firing of any onPromiseSettled\n"
  10356 "  hooks set on Debugger instances that are observing the given promise's\n"
  10357 "  global as a debuggee."),
  10358    JS_FN_HELP("getWaitForAllPromise", GetWaitForAllPromise, 1, 0,
  10359 "getWaitForAllPromise(densePromisesArray)",
  10360 "  Calls the 'GetWaitForAllPromise' JSAPI function and returns the result\n"
  10361 "  Promise."),
  10362 JS_FN_HELP("resolvePromise", ResolvePromise, 2, 0,
  10363 "resolvePromise(promise, resolution)",
  10364 "  Resolve a Promise by calling the JSAPI function JS::ResolvePromise."),
  10365 JS_FN_HELP("rejectPromise", RejectPromise, 2, 0,
  10366 "rejectPromise(promise, reason)",
  10367 "  Reject a Promise by calling the JSAPI function JS::RejectPromise."),
  10368 
  10369    JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
  10370 "makeFinalizeObserver()",
  10371 "  Get a special object whose finalization increases the counter returned\n"
  10372 "  by the finalizeCount function."),
  10373 
  10374    JS_FN_HELP("finalizeCount", FinalizeCount, 0, 0,
  10375 "finalizeCount()",
  10376 "  Return the current value of the finalization counter that is incremented\n"
  10377 "  each time an object returned by the makeFinalizeObserver is finalized."),
  10378 
  10379    JS_FN_HELP("resetFinalizeCount", ResetFinalizeCount, 0, 0,
  10380 "resetFinalizeCount()",
  10381 "  Reset the value returned by finalizeCount()."),
  10382 
  10383    JS_FN_HELP("gcPreserveCode", GCPreserveCode, 0, 0,
  10384 "gcPreserveCode()",
  10385 "  Preserve JIT code during garbage collections."),
  10386 
  10387 #ifdef JS_GC_ZEAL
  10388    JS_FN_HELP("gczeal", GCZeal, 2, 0,
  10389 "gczeal([mode, [frequency]])",
  10390 gc::ZealModeHelpText),
  10391 
  10392    JS_FN_HELP("unsetgczeal", UnsetGCZeal, 2, 0,
  10393 "unsetgczeal(mode)",
  10394 "  Turn off the mode or modes given that were set with gczeal() or JS_GC_ZEAL\n"
  10395 "  and don't finish any ongoing collection that may be happening."),
  10396 
  10397    JS_FN_HELP("schedulegc", ScheduleGC, 1, 0,
  10398 "schedulegc([num])",
  10399 "  If num is given, schedule a GC after num allocations.\n"
  10400 "  Returns the number of allocations before the next trigger."),
  10401 
  10402    JS_FN_HELP("selectforgc", SelectForGC, 0, 0,
  10403 "selectforgc(obj1, obj2, ...)",
  10404 "  Schedule the given objects to be marked in the next GC slice."),
  10405 
  10406    JS_FN_HELP("verifyprebarriers", VerifyPreBarriers, 0, 0,
  10407 "verifyprebarriers()",
  10408 "  Start or end a run of the pre-write barrier verifier."),
  10409 
  10410    JS_FN_HELP("verifypostbarriers", VerifyPostBarriers, 0, 0,
  10411 "verifypostbarriers()",
  10412 "  Enable or disable the post-write barrier verifier."),
  10413 
  10414    JS_FN_HELP("currentgc", CurrentGC, 0, 0,
  10415 "currentgc()",
  10416 "  Report various information about the currently running incremental GC,\n"
  10417 "  if one is running."),
  10418 
  10419    JS_FN_HELP("deterministicgc", DeterministicGC, 1, 0,
  10420 "deterministicgc(true|false)",
  10421 "  If true, only allow determinstic GCs to run."),
  10422 
  10423    JS_FN_HELP("dumpGCArenaInfo", DumpGCArenaInfo, 0, 0,
  10424 "dumpGCArenaInfo()",
  10425 "  Prints information about the different GC things and how they are arranged\n"
  10426 "  in arenas.\n"),
  10427 
  10428    JS_FN_HELP("setMarkStackLimit", SetMarkStackLimit, 1, 0,
  10429 "markStackLimit(limit)",
  10430 "  Sets a limit on the number of words used for the mark stack. Used to test OOM"
  10431 "  handling during marking.\n"),
  10432 
  10433 #endif
  10434 
  10435    JS_FN_HELP("gcstate", GCState, 0, 0,
  10436 "gcstate([obj])",
  10437 "  Report the global GC state, or the GC state for the zone containing |obj|."),
  10438 
  10439    JS_FN_HELP("schedulezone", ScheduleZoneForGC, 1, 0,
  10440 "schedulezone([obj | string])",
  10441 "  If obj is given, schedule a GC of obj's zone.\n"
  10442 "  If string is given, schedule a GC of the string's zone if possible."),
  10443 
  10444    JS_FN_HELP("startgc", StartGC, 1, 0,
  10445 "startgc([n [, 'shrinking']])",
  10446 "  Start an incremental GC and run a slice that processes about n objects.\n"
  10447 "  If 'shrinking' is passesd as the optional second argument, perform a\n"
  10448 "  shrinking GC rather than a normal GC. If no zones have been selected with\n"
  10449 "  schedulezone(), a full GC will be performed."),
  10450 
  10451    JS_FN_HELP("finishgc", FinishGC, 0, 0,
  10452 "finishgc()",
  10453 "   Finish an in-progress incremental GC, if none is running then do nothing."),
  10454 
  10455    JS_FN_HELP("gcslice", GCSlice, 1, 0,
  10456 "gcslice([n [, options]])",
  10457 "  Start or continue an an incremental GC, running a slice that processes\n"
  10458 "  about n objects. Takes an optional options object, which may contain the\n"
  10459 "  following properties:\n"
  10460 "    dontStart: do not start a new incremental GC if one is not already\n"
  10461 "               running"),
  10462 
  10463    JS_FN_HELP("abortgc", AbortGC, 1, 0,
  10464 "abortgc()",
  10465 "  Abort the current incremental GC."),
  10466 
  10467    JS_FN_HELP("isAtomMarked", IsAtomMarked, 2, 0,
  10468 "isAtomMarked(obj, atom)",
  10469 "  Return whether |atom| is marked relative to the zone containing |obj|."),
  10470 
  10471    JS_FN_HELP("getAtomMarkIndex", GetAtomMarkIndex, 1, 0,
  10472 "getAtomMarkIndex(atom)",
  10473 "  Return the atom marking bitmap's index for |atom|."),
  10474 
  10475    JS_FN_HELP("getAtomMarkColor", GetAtomMarkColor, 2, 0,
  10476 "getAtomMarkColor(obj, index)",
  10477 "  Return the atom marking bitmap's mark color for |index| relative to the zone containing |obj|."),
  10478 
  10479    JS_FN_HELP("setMallocMaxDirtyPageModifier", SetMallocMaxDirtyPageModifier, 1, 0,
  10480 "setMallocMaxDirtyPageModifier(value)",
  10481 "  Change the maximum size of jemalloc's page cache. The value should be between\n"
  10482 "  -5 and 16 (inclusive). See moz_set_max_dirty_page_modifier.\n"),
  10483 
  10484    JS_FN_HELP("fullcompartmentchecks", FullCompartmentChecks, 1, 0,
  10485 "fullcompartmentchecks(true|false)",
  10486 "  If true, check for compartment mismatches before every GC."),
  10487 
  10488    JS_FN_HELP("nondeterministicGetWeakMapSize", NondeterministicGetWeakMapSize, 1, 0,
  10489 "nondeterministicGetWeakMapSize(weakmap)",
  10490 "  Returns the number of entries in the given WeakMap."),
  10491 
  10492    JS_FN_HELP("nondeterministicGetWeakMapKeys", NondeterministicGetWeakMapKeys, 1, 0,
  10493 "nondeterministicGetWeakMapKeys(weakmap)",
  10494 "  Return an array of the keys in the given WeakMap."),
  10495 
  10496    JS_FN_HELP("grayBitsValid", GrayBitsValid, 0, 0,
  10497 "grayBitsValid()",
  10498 "  Return whether the gray bits state is valid."),
  10499 
  10500    JS_FN_HELP("setGrayBitsInvalid", SetGrayBitsInvalid, 0, 0,
  10501 "setGrayBitsInvalid()",
  10502 "  Set the gray bits state to invalid."),
  10503 
  10504    JS_FN_HELP("internalConst", InternalConst, 1, 0,
  10505 "internalConst(name)",
  10506 "  Query an internal constant for the engine. See InternalConst source for\n"
  10507 "  the list of constant names."),
  10508 
  10509    JS_FN_HELP("isProxy", IsProxy, 1, 0,
  10510 "isProxy(obj)",
  10511 "  If true, obj is a proxy of some sort"),
  10512 
  10513    JS_FN_HELP("dumpHeap", DumpHeap, 1, 0,
  10514 "dumpHeap([filename])",
  10515 "  Dump reachable and unreachable objects to the named file, or to stdout. Objects\n"
  10516 "  in the nursery are ignored, so if you wish to include them, consider calling\n"
  10517 "  minorgc() first."),
  10518 
  10519    JS_FN_HELP("terminate", Terminate, 0, 0,
  10520 "terminate()",
  10521 "  Terminate JavaScript execution, as if we had run out of\n"
  10522 "  memory or been terminated by the slow script dialog."),
  10523 
  10524    JS_FN_HELP("readGeckoProfilingStack", ReadGeckoProfilingStack, 0, 0,
  10525 "readGeckoProfilingStack()",
  10526 "  Reads the JIT/Wasm stack using ProfilingFrameIterator. Skips non-JIT/Wasm frames."),
  10527 
  10528    JS_FN_HELP("readGeckoInterpProfilingStack", ReadGeckoInterpProfilingStack, 0, 0,
  10529 "readGeckoInterpProfilingStack()",
  10530 "  Reads the C++ interpreter profiling stack. Skips JIT/Wasm frames."),
  10531 
  10532    JS_FN_HELP("enableOsiPointRegisterChecks", EnableOsiPointRegisterChecks, 0, 0,
  10533 "enableOsiPointRegisterChecks()",
  10534 "  Emit extra code to verify live regs at the start of a VM call are not\n"
  10535 "  modified before its OsiPoint."),
  10536 
  10537    JS_FN_HELP("displayName", DisplayName, 1, 0,
  10538 "displayName(fn)",
  10539 "  Gets the display name for a function, which can possibly be a guessed or\n"
  10540 "  inferred name based on where the function was defined. This can be\n"
  10541 "  different from the 'name' property on the function."),
  10542 
  10543    JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable, 0, 0,
  10544 "isAsmJSCompilationAvailable",
  10545 "  Returns whether asm.js compilation is currently available or whether it is disabled\n"
  10546 "  (e.g., by the debugger)."),
  10547 
  10548    JS_FN_HELP("getJitCompilerOptions", GetJitCompilerOptions, 0, 0,
  10549 "getJitCompilerOptions()",
  10550 "  Return an object describing some of the JIT compiler options.\n"),
  10551 
  10552    JS_FN_HELP("isAsmJSModule", IsAsmJSModule, 1, 0,
  10553 "isAsmJSModule(fn)",
  10554 "  Returns whether the given value is a function containing \"use asm\" that has been\n"
  10555 "  validated according to the asm.js spec."),
  10556 
  10557    JS_FN_HELP("isAsmJSFunction", IsAsmJSFunction, 1, 0,
  10558 "isAsmJSFunction(fn)",
  10559 "  Returns whether the given value is a nested function in an asm.js module that has been\n"
  10560 "  both compile- and link-time validated."),
  10561 
  10562    JS_FN_HELP("isAvxPresent", IsAvxPresent, 0, 0,
  10563 "isAvxPresent([minVersion])",
  10564 "  Returns whether AVX is present and enabled. If minVersion specified,\n"
  10565 "  use 1 - to check if AVX is enabled (default), 2 - if AVX2 is enabled."),
  10566 
  10567    JS_FN_HELP("wasmIsSupported", WasmIsSupported, 0, 0,
  10568 "wasmIsSupported()",
  10569 "  Returns a boolean indicating whether WebAssembly is supported on the current device."),
  10570 
  10571    JS_FN_HELP("wasmIsSupportedByHardware", WasmIsSupportedByHardware, 0, 0,
  10572 "wasmIsSupportedByHardware()",
  10573 "  Returns a boolean indicating whether WebAssembly is supported on the current hardware (regardless of whether we've enabled support)."),
  10574 
  10575    JS_FN_HELP("wasmDebuggingEnabled", WasmDebuggingEnabled, 0, 0,
  10576 "wasmDebuggingEnabled()",
  10577 "  Returns a boolean indicating whether WebAssembly debugging is supported on the current device;\n"
  10578 "  returns false also if WebAssembly is not supported"),
  10579 
  10580    JS_FN_HELP("wasmStreamingEnabled", WasmStreamingEnabled, 0, 0,
  10581 "wasmStreamingEnabled()",
  10582 "  Returns a boolean indicating whether WebAssembly caching is supported by the runtime."),
  10583 
  10584    JS_FN_HELP("wasmCachingEnabled", WasmCachingEnabled, 0, 0,
  10585 "wasmCachingEnabled()",
  10586 "  Returns a boolean indicating whether WebAssembly caching is supported by the runtime."),
  10587 
  10588    JS_FN_HELP("wasmHugeMemorySupported", WasmHugeMemorySupported, 0, 0,
  10589 "wasmHugeMemorySupported()",
  10590 "  Returns a boolean indicating whether WebAssembly supports using a large"
  10591 "  virtual memory reservation in order to elide bounds checks on this platform."),
  10592 
  10593    JS_FN_HELP("wasmMaxMemoryPages", WasmMaxMemoryPages, 1, 0,
  10594 "wasmMaxMemoryPages(addressType)",
  10595 "  Returns an int with the maximum number of pages that can be allocated to a memory."
  10596 "  This is an implementation artifact that does depend on the address type, the hardware,"
  10597 "  the operating system, the build configuration, and flags.  The result is constant for"
  10598 "  a given combination of those; there is no guarantee that that size allocation will"
  10599 "  always succeed, only that it can succeed in principle.  The addressType is a string,"
  10600 "  'i32' or 'i64'."),
  10601 
  10602 #define WASM_FEATURE(NAME, ...) \
  10603    JS_FN_HELP("wasm" #NAME "Enabled", Wasm##NAME##Enabled, 0, 0, \
  10604 "wasm" #NAME "Enabled()", \
  10605 "  Returns a boolean indicating whether the WebAssembly " #NAME " proposal is enabled."),
  10606 JS_FOR_WASM_FEATURES(WASM_FEATURE)
  10607 #undef WASM_FEATURE
  10608 
  10609    JS_FN_HELP("wasmThreadsEnabled", WasmThreadsEnabled, 0, 0,
  10610 "wasmThreadsEnabled()",
  10611 "  Returns a boolean indicating whether the WebAssembly threads proposal is\n"
  10612 "  supported on the current device."),
  10613 
  10614    JS_FN_HELP("wasmSimdEnabled", WasmSimdEnabled, 0, 0,
  10615 "wasmSimdEnabled()",
  10616 "  Returns a boolean indicating whether WebAssembly SIMD proposal is\n"
  10617 "  supported by the current device."),
  10618 
  10619 #if defined(ENABLE_WASM_SIMD) && defined(DEBUG)
  10620    JS_FN_HELP("wasmSimdAnalysis", WasmSimdAnalysis, 1, 0,
  10621 "wasmSimdAnalysis(...)",
  10622 "  Unstable API for white-box testing.\n"),
  10623 #endif
  10624 
  10625    JS_FN_HELP("wasmGlobalFromArrayBuffer", WasmGlobalFromArrayBuffer, 2, 0,
  10626 "wasmGlobalFromArrayBuffer(type, arrayBuffer)",
  10627 "  Create a WebAssembly.Global object from a provided ArrayBuffer. The type\n"
  10628 "  must be POD (i32, i64, f32, f64, v128). The buffer must be the same\n"
  10629 "  size as the type in bytes.\n"),
  10630    JS_FN_HELP("wasmGlobalExtractLane", WasmGlobalExtractLane, 3, 0,
  10631 "wasmGlobalExtractLane(global, laneInterp, laneIndex)",
  10632 "  Extract a lane from a WebAssembly.Global object that contains a v128 value\n"
  10633 "  and return it as a new WebAssembly.Global object of the appropriate type.\n"
  10634 "  The supported laneInterp values are i32x4, i64x2, f32x4, and\n"
  10635 "  f64x2.\n"),
  10636    JS_FN_HELP("wasmGlobalsEqual", WasmGlobalsEqual, 2, 0,
  10637 "wasmGlobalsEqual(globalA, globalB)",
  10638 "  Compares two WebAssembly.Global objects for if their types and values are\n"
  10639 "  equal. Mutability is not compared. Floating point values are compared for\n"
  10640 "  bitwise equality, not IEEE 754 equality.\n"),
  10641    JS_FN_HELP("wasmGlobalIsNaN", WasmGlobalIsNaN, 2, 0,
  10642 "wasmGlobalIsNaN(global, flavor)",
  10643 "  Compares a floating point WebAssembly.Global object for if its value is a\n"
  10644 "  specific NaN flavor. Valid flavors are `arithmetic_nan` and `canonical_nan`.\n"),
  10645    JS_FN_HELP("wasmGlobalToString", WasmGlobalToString, 1, 0,
  10646 "wasmGlobalToString(global)",
  10647 "  Returns a debug representation of the contents of a WebAssembly.Global\n"
  10648 "  object.\n"),
  10649    JS_FN_HELP("wasmLosslessInvoke", WasmLosslessInvoke, 1, 0,
  10650 "wasmLosslessInvoke(wasmFunc, args...)",
  10651 "  Invokes the provided WebAssembly function using a modified conversion\n"
  10652 "  function that allows providing a param as a WebAssembly.Global and\n"
  10653 "  returning a result as a WebAssembly.Global.\n"),
  10654 
  10655    JS_FN_HELP("wasmCompilersPresent", WasmCompilersPresent, 0, 0,
  10656 "wasmCompilersPresent()",
  10657 "  Returns a string indicating the present wasm compilers: a comma-separated list\n"
  10658 "  of 'baseline', 'ion'.  A compiler is present in the executable if it is compiled\n"
  10659 "  in and can generate code for the current architecture."),
  10660 
  10661    JS_FN_HELP("wasmCompileMode", WasmCompileMode, 0, 0,
  10662 "wasmCompileMode()",
  10663 "  Returns a string indicating the available wasm compilers: 'baseline', 'ion',\n"
  10664 "  'baseline+ion', or 'none'.  A compiler is available if it is present in the\n"
  10665 "  executable and not disabled by switches or runtime conditions.  At most one\n"
  10666 "  baseline and one optimizing compiler can be available."),
  10667 
  10668    JS_FN_HELP("wasmLazyTieringEnabled", WasmLazyTieringEnabled, 0, 0,
  10669 "wasmLazyTieringEnabled()",
  10670 "  Returns a boolean indicating whether compilation will be performed\n"
  10671 "  using lazy tiering."),
  10672 
  10673    JS_FN_HELP("wasmBaselineDisabledByFeatures", WasmBaselineDisabledByFeatures, 0, 0,
  10674 "wasmBaselineDisabledByFeatures()",
  10675 "  If some feature is enabled at compile-time or run-time that prevents baseline\n"
  10676 "  from being used then this returns a truthy string describing the features that\n."
  10677 "  are disabling it.  Otherwise it returns false."),
  10678 
  10679    JS_FN_HELP("wasmIonDisabledByFeatures", WasmIonDisabledByFeatures, 0, 0,
  10680 "wasmIonDisabledByFeatures()",
  10681 "  If some feature is enabled at compile-time or run-time that prevents Ion\n"
  10682 "  from being used then this returns a truthy string describing the features that\n."
  10683 "  are disabling it.  Otherwise it returns false."),
  10684 
  10685    JS_FN_HELP("wasmHasTier2CompilationCompleted", WasmHasTier2CompilationCompleted, 1, 0,
  10686 "wasmHasTier2CompilationCompleted(module)",
  10687 "  Returns a boolean indicating whether a given module has finished compiled code for tier2. \n"
  10688 "This will return true early if compilation isn't two-tiered. "),
  10689 
  10690    JS_FN_HELP("wasmLoadedFromCache", WasmLoadedFromCache, 1, 0,
  10691 "wasmLoadedFromCache(module)",
  10692 "  Returns a boolean indicating whether a given module was deserialized directly from a\n"
  10693 "  cache (as opposed to compiled from bytecode)."),
  10694 
  10695    JS_FN_HELP("wasmBuiltinI8VecMul", WasmBuiltinI8VecMul, 0, 0,
  10696 "wasmBuiltinI8VecMul()",
  10697 "  Returns a module that implements an i8 vector pairwise multiplication intrinsic."),
  10698 
  10699    JS_FN_HELP("wasmGcReadField", WasmGcReadField, 2, 0,
  10700 "wasmGcReadField(obj, index)",
  10701 "  Gets a field of a WebAssembly GC struct or array."),
  10702 
  10703    JS_FN_HELP("wasmGcArrayLength", WasmGcArrayLength, 1, 0,
  10704 "wasmGcArrayLength(arr)",
  10705 "  Gets the length of a WebAssembly GC array."),
  10706 
  10707 #ifdef ENABLE_WASM_BRANCH_HINTING
  10708    JS_FN_HELP("wasmParsedBranchHints", WasmParsedBranchHints, 1, 0,
  10709 "wasmParsedBranchHints(module)",
  10710 "  Returns a boolean indicating whether a given module has successfully parsed a\n"
  10711 "  custom branch hinting section."),
  10712 
  10713 #endif // ENABLE_WASM_BRANCH_HINTING
  10714 
  10715    JS_FN_HELP("largeArrayBufferSupported", LargeArrayBufferSupported, 0, 0,
  10716 "largeArrayBufferSupported()",
  10717 "  Returns true if array buffers larger than 2GB can be allocated."),
  10718 
  10719    JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0,
  10720 "isLazyFunction(fun)",
  10721 "  True if fun is a lazy JSFunction."),
  10722 
  10723    JS_FN_HELP("isRelazifiableFunction", IsRelazifiableFunction, 1, 0,
  10724 "isRelazifiableFunction(fun)",
  10725 "  True if fun is a JSFunction with a relazifiable JSScript."),
  10726 
  10727    JS_FN_HELP("hasSameBytecodeData", HasSameBytecodeData, 2, 0,
  10728 "hasSameBytecodeData(fun1, fun2)",
  10729 "  True if fun1 and fun2 share the same copy of bytecode data. This will\n"
  10730 "  delazify the function if necessary."),
  10731 
  10732    JS_FN_HELP("enableShellAllocationMetadataBuilder", EnableShellAllocationMetadataBuilder, 0, 0,
  10733 "enableShellAllocationMetadataBuilder()",
  10734 "  Use ShellAllocationMetadataBuilder to supply metadata for all newly created objects."),
  10735 
  10736    JS_FN_HELP("getAllocationMetadata", GetAllocationMetadata, 1, 0,
  10737 "getAllocationMetadata(obj)",
  10738 "  Get the metadata for an object."),
  10739 
  10740    JS_INLINABLE_FN_HELP("bailout", testingFunc_bailout, 0, 0, TestBailout,
  10741 "bailout()",
  10742 "  Force a bailout out of ionmonkey (if running in ionmonkey)."),
  10743 
  10744    JS_FN_HELP("bailAfter", testingFunc_bailAfter, 1, 0,
  10745 "bailAfter(number)",
  10746 "  Start a counter to bail once after passing the given amount of possible bailout positions in\n"
  10747 "  ionmonkey.\n"),
  10748 
  10749    JS_FN_HELP("invalidate", testingFunc_invalidate, 0, 0,
  10750 "invalidate()",
  10751 "  Force an immediate invalidation (if running in Warp)."),
  10752 
  10753    JS_FN_HELP("inJit", testingFunc_inJit, 0, 0,
  10754 "inJit()",
  10755 "  Returns true when called within (jit-)compiled code. When jit compilation is disabled this\n"
  10756 "  function returns an error string. This function returns false in all other cases.\n"
  10757 "  Depending on truthiness, you should continue to wait for compilation to happen or stop execution.\n"),
  10758 
  10759    JS_FN_HELP("inIon", testingFunc_inIon, 0, 0,
  10760 "inIon()",
  10761 "  Returns true when called within ion. When ion is disabled or when compilation is abnormally\n"
  10762 "  slow to start, this function returns an error string. Otherwise, this function returns false.\n"
  10763 "  This behaviour ensures that a falsy value means that we are not in ion, but expect a\n"
  10764 "  compilation to occur in the future. Conversely, a truthy value means that we are either in\n"
  10765 "  ion or that there is litle or no chance of ion ever compiling the current script."),
  10766 
  10767    JS_FN_HELP("assertJitStackInvariants", TestingFunc_assertJitStackInvariants, 0, 0,
  10768 "assertJitStackInvariants()",
  10769 "  Iterates the Jit stack and check that stack invariants hold."),
  10770 
  10771    JS_FN_HELP("setIonCheckGraphCoherency", SetIonCheckGraphCoherency, 1, 0,
  10772 "setIonCheckGraphCoherency(bool)",
  10773 "  Set whether Ion should perform graph consistency (DEBUG-only) assertions. These assertions\n"
  10774 "  are valuable and should be generally enabled, however they can be very expensive for large\n"
  10775 "  (wasm) programs."),
  10776 
  10777    JS_FN_HELP("serialize", testingFunc_serialize, 1, 0,
  10778 "serialize(data, [transferables, [policy]])",
  10779 "  Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
  10780 "  clone buffer object. 'policy' may be an options hash. Valid keys:\n"
  10781 "    'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n"
  10782 "      to specify whether SharedArrayBuffers may be serialized.\n"
  10783 "    'scope' - SameProcess, DifferentProcess, or\n"
  10784 "      DifferentProcessForIndexedDB. Determines how some values will be\n"
  10785 "      serialized. Clone buffers may only be deserialized with a compatible\n"
  10786 "      scope. NOTE - For DifferentProcess/DifferentProcessForIndexedDB,\n"
  10787 "      must also set SharedArrayBuffer:'deny' if data contains any shared memory\n"
  10788 "      object."),
  10789 
  10790    JS_FN_HELP("deserialize", Deserialize, 1, 0,
  10791 "deserialize(clonebuffer[, opts])",
  10792 "  Deserialize data generated by serialize. 'opts' may be an options hash.\n"
  10793 "  Valid keys:\n"
  10794 "    'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n"
  10795 "      to specify whether SharedArrayBuffers may be serialized.\n"
  10796 "    'scope', which limits the clone buffers that are considered\n"
  10797 "  valid. Allowed values: ''SameProcess', 'DifferentProcess',\n"
  10798 "  and 'DifferentProcessForIndexedDB'. So for example, a\n"
  10799 "  DifferentProcessForIndexedDB clone buffer may be deserialized in any scope, but\n"
  10800 "  a SameProcess clone buffer cannot be deserialized in a\n"
  10801 "  DifferentProcess scope."),
  10802 
  10803    JS_FN_HELP("detachArrayBuffer", DetachArrayBuffer, 1, 0,
  10804 "detachArrayBuffer(buffer)",
  10805 "  Detach the given ArrayBuffer object from its memory, i.e. as if it\n"
  10806 "  had been transferred to a WebWorker."),
  10807 
  10808    JS_FN_HELP("makeSerializable", MakeSerializable, 1, 0,
  10809 "makeSerializable(numeric id, [behavior])",
  10810 "  Make a custom serializable, transferable object. It will have a single accessor\n"
  10811 "  obj.log that will give a history of all operations on all such objects in the\n"
  10812 "  current thread as an array [id, action, id, action, ...] where the id\n"
  10813 "  is the number passed into this function, and the action is one of:\n"
  10814 "     ? - the canTransfer() hook was called.\n"
  10815 "     w - the write() hook was called.\n"
  10816 "     W - the writeTransfer() hook was called.\n"
  10817 "     R - the readTransfer() hook was called.\n"
  10818 "     r - the read() hook was called.\n"
  10819 "     F - the freeTransfer() hook was called.\n"
  10820 "  The `behavior` parameter can be used to force a failure during processing:\n"
  10821 "     1 - fail during readTransfer() hook\n"
  10822 "     2 - fail during read() hook\n"
  10823 "  Set the log to null to clear it."),
  10824 
  10825    JS_FN_HELP("ensureNonInline", EnsureNonInline, 1, 0,
  10826 "ensureNonInline(view or buffer)",
  10827 "  Ensure that the memory for the given ArrayBuffer or ArrayBufferView\n"
  10828 "  is not inline."),
  10829 
  10830    JS_FN_HELP("pinArrayBufferOrViewLength", PinArrayBufferOrViewLength, 1, 0,
  10831 "pinArrayBufferOrViewLength(view or buffer[, pin])",
  10832 "  Prevent or allow (if `pin` is false) changes to the length of the given\n"
  10833 "  ArrayBuffer or ArrayBufferView. `pin` defaults to true."),
  10834 
  10835    JS_FN_HELP("JSONStringify", JSONStringify, 4, 0,
  10836 "JSONStringify(value, behavior)",
  10837 "  Same as JSON.stringify(value), but allows setting behavior:\n"
  10838 "    Normal: the default\n"
  10839 "    FastOnly: throw if the fast path bails\n"
  10840 "    SlowOnly: skip the fast path entirely\n"
  10841 "    Compare: run both the fast and slow paths and compare the result. Crash if\n"
  10842 "      they do not match. If the fast path bails, no comparison is done."),
  10843 
  10844    JS_FN_HELP("helperThreadCount", HelperThreadCount, 0, 0,
  10845 "helperThreadCount()",
  10846 "  Returns the number of helper threads available for off-thread tasks."),
  10847 
  10848    JS_FN_HELP("createShapeSnapshot", CreateShapeSnapshot, 1, 0,
  10849 "createShapeSnapshot(obj)",
  10850 "  Returns an object containing a shape snapshot for use with\n"
  10851 "  checkShapeSnapshot.\n"),
  10852 
  10853    JS_FN_HELP("checkShapeSnapshot", CheckShapeSnapshot, 2, 0,
  10854 "checkShapeSnapshot(snapshot, [obj])",
  10855 "  Check shape invariants based on the given snapshot and optional object.\n"
  10856 "  If there's no object argument, the snapshot's object is used.\n"),
  10857 
  10858    JS_FN_HELP("enableShapeConsistencyChecks", EnableShapeConsistencyChecks, 0, 0,
  10859 "enableShapeConsistencyChecks()",
  10860 "  Enable some slow Shape assertions.\n"),
  10861 
  10862    JS_FN_HELP("reportOutOfMemory", ReportOutOfMemory, 0, 0,
  10863 "reportOutOfMemory()",
  10864 "  Report OOM, then clear the exception and return undefined. For crash testing."),
  10865 
  10866    JS_FN_HELP("throwOutOfMemory", ThrowOutOfMemory, 0, 0,
  10867 "throwOutOfMemory()",
  10868 "  Throw out of memory exception, for OOM handling testing."),
  10869 
  10870    JS_FN_HELP("reportLargeAllocationFailure", ReportLargeAllocationFailure, 0, 0,
  10871 "reportLargeAllocationFailure([bytes])",
  10872 "  Call the large allocation failure callback, as though a large malloc call failed,\n"
  10873 "  then return undefined. In Gecko, this sends a memory pressure notification, which\n"
  10874 "  can free up some memory."),
  10875 
  10876    JS_FN_HELP("findPath", FindPath, 2, 0,
  10877 "findPath(start, target)",
  10878 "  Return an array describing one of the shortest paths of GC heap edges from\n"
  10879 "  |start| to |target|, or |undefined| if |target| is unreachable from |start|.\n"
  10880 "  Each element of the array is either of the form:\n"
  10881 "    { node: <object or string>, edge: <string describing edge from node> }\n"
  10882 "  if the node is a JavaScript object or value; or of the form:\n"
  10883 "    { type: <string describing node>, edge: <string describing edge> }\n"
  10884 "  if the node is some internal thing that is not a proper JavaScript value\n"
  10885 "  (like a shape or a scope chain element). The destination of the i'th array\n"
  10886 "  element's edge is the node of the i+1'th array element; the destination of\n"
  10887 "  the last array element is implicitly |target|.\n"),
  10888 
  10889    JS_FN_HELP("sharedMemoryEnabled", SharedMemoryEnabled, 0, 0,
  10890 "sharedMemoryEnabled()",
  10891 "  Return true if SharedArrayBuffer and Atomics are enabled"),
  10892 
  10893    JS_FN_HELP("sharedArrayRawBufferRefcount", SharedArrayRawBufferRefcount, 0, 0,
  10894 "sharedArrayRawBufferRefcount(sab)",
  10895 "  Return the reference count of the SharedArrayRawBuffer object held by sab"),
  10896 
  10897 #ifdef NIGHTLY_BUILD
  10898    JS_FN_HELP("objectAddress", ObjectAddress, 1, 0,
  10899 "objectAddress(obj)",
  10900 "  Return the current address of the object. For debugging only--this\n"
  10901 "  address may change during a moving GC."),
  10902 
  10903    JS_FN_HELP("scriptAddressForFunction", ScriptAddressForFunction, 1, 0,
  10904 "scriptAddressForFunction(fun)",
  10905 "  Return the current address of a function's script."),
  10906 
  10907    JS_FN_HELP("sharedAddress", SharedAddress, 1, 0,
  10908 "sharedAddress(obj)",
  10909 "  Return the address of the shared storage of a SharedArrayBuffer."),
  10910 #endif
  10911 
  10912    JS_FN_HELP("hasInvalidatedTeleporting", HasInvalidatedTeleporting, 1, 0,
  10913 "hasInvalidatedTeleporting(obj)",
  10914 "  Return true if the shape teleporting optimization has been disabled for |obj|."),
  10915 
  10916    JS_FN_HELP("evalReturningScope", EvalReturningScope, 1, 0,
  10917 "evalReturningScope(scriptStr, [global])",
  10918 "  Evaluate the script in a new scope and return the scope.\n"
  10919 "  If |global| is present, clone the script to |global| before executing."),
  10920 
  10921    JS_FN_HELP("backtrace", DumpBacktrace, 1, 0,
  10922 "backtrace()",
  10923 "  Dump out a brief backtrace."),
  10924 
  10925    JS_FN_HELP("getBacktrace", GetBacktrace, 1, 0,
  10926 "getBacktrace([options])",
  10927 "  Return the current stack as a string. Takes an optional options object,\n"
  10928 "  which may contain any or all of the boolean properties:\n"
  10929 "    options.args - show arguments to each function\n"
  10930 "    options.locals - show local variables in each frame\n"
  10931 "    options.thisprops - show the properties of the 'this' object of each frame\n"),
  10932 
  10933    JS_FN_HELP("byteSize", ByteSize, 1, 0,
  10934 "byteSize(value)",
  10935 "  Return the size in bytes occupied by |value|, or |undefined| if value\n"
  10936 "  is not allocated in memory.\n"),
  10937 
  10938    JS_FN_HELP("byteSizeOfScript", ByteSizeOfScript, 1, 0,
  10939 "byteSizeOfScript(f)",
  10940 "  Return the size in bytes occupied by the function |f|'s JSScript.\n"),
  10941 
  10942    JS_FN_HELP("setImmutablePrototype", SetImmutablePrototype, 1, 0,
  10943 "setImmutablePrototype(obj)",
  10944 "  Try to make obj's [[Prototype]] immutable, such that subsequent attempts to\n"
  10945 "  change it will fail.  Return true if obj's [[Prototype]] was successfully made\n"
  10946 "  immutable (or if it already was immutable), false otherwise.  Throws in case\n"
  10947 "  of internal error, or if the operation doesn't even make sense (for example,\n"
  10948 "  because the object is a revoked proxy)."),
  10949 
  10950    JS_FN_HELP("allocationMarker", AllocationMarker, 0, 0,
  10951 "allocationMarker([options])",
  10952 "  Return a freshly allocated object whose [[Class]] name is\n"
  10953 "  \"AllocationMarker\". Such objects are allocated only by calls\n"
  10954 "  to this function, never implicitly by the system, making them\n"
  10955 "  suitable for use in allocation tooling tests. Takes an optional\n"
  10956 "  options object which may contain the following properties:\n"
  10957 "    * nursery: bool, whether to allocate the object in the nursery\n"),
  10958 
  10959    JS_FN_HELP("setGCCallback", SetGCCallback, 1, 0,
  10960 "setGCCallback({action:\"...\", options...})",
  10961 "  Set the GC callback. action may be:\n"
  10962 "    'minorGC' - run a nursery collection\n"
  10963 "    'majorGC' - run a major collection, nesting up to a given 'depth'\n"),
  10964 
  10965 #ifdef DEBUG
  10966    JS_FN_HELP("enqueueMark", EnqueueMark, 1, 0,
  10967 "enqueueMark(obj|string)",
  10968 "  Add an object to the queue of objects to mark at the beginning every GC. (Note\n"
  10969 "  that the objects will actually be marked at the beginning of every slice, but\n"
  10970 "  after the first slice they will already be marked so nothing will happen.)\n"
  10971 "  \n"
  10972 "  Instead of an object, a few magic strings may be used:\n"
  10973 "    'yield' - cause the current marking slice to end, as if the mark budget were\n"
  10974 "      exceeded.\n"
  10975 "    'enter-weak-marking-mode' - divide the list into two segments. The items after\n"
  10976 "      this string will not be marked until we enter weak marking mode. Note that weak\n"
  10977 "      marking mode may be entered zero or multiple times for one GC.\n"
  10978 "    'abort-weak-marking-mode' - same as above, but then abort weak marking to fall back\n"
  10979 "      on the old iterative marking code path.\n"
  10980 "    'drain' - fully drain the mark stack before continuing.\n"
  10981 "    'set-color-black' - force everything following in the mark queue to be marked black.\n"
  10982 "    'set-color-gray' - continue with the regular GC until gray marking is possible, then force\n"
  10983 "       everything following in the mark queue to be marked gray.\n"
  10984 "    'unset-color' - stop forcing the mark color."),
  10985 
  10986    JS_FN_HELP("clearMarkQueue", ClearMarkQueue, 0, 0,
  10987 "clearMarkQueue()",
  10988 "  Cancel the special marking of all objects enqueue with enqueueMark()."),
  10989 
  10990    JS_FN_HELP("getMarkQueue", GetMarkQueue, 0, 0,
  10991 "getMarkQueue()",
  10992 "  Return the current mark queue set up via enqueueMark calls. Note that all\n"
  10993 "  returned values will be wrapped into the current compartment, so this loses\n"
  10994 "  some fidelity."),
  10995 #endif // DEBUG
  10996 
  10997    JS_FN_HELP("nurseryStringsEnabled", NurseryStringsEnabled, 0, 0,
  10998 "nurseryStringsEnabled()",
  10999 "  Return whether strings are currently allocated in the nursery for the current\n"
  11000 "  global\n"),
  11001 
  11002    JS_FN_HELP("isNurseryAllocated", IsNurseryAllocated, 1, 0,
  11003 "isNurseryAllocated(thing)",
  11004 "  Return whether a GC thing is nursery allocated.\n"),
  11005 
  11006    JS_FN_HELP("numAllocSitesPretenured", NumAllocSitesPretenured, 0, 0,
  11007 "numAllocSitesPretenured()",
  11008 "  Return the number of allocation sites that were pretenured for the current\n"
  11009 "  global\n"),
  11010 
  11011    JS_FN_HELP("getLcovInfo", GetLcovInfo, 1, 0,
  11012 "getLcovInfo(global)",
  11013 "  Generate LCOV tracefile for the given compartment.  If no global are provided then\n"
  11014 "  the current global is used as the default one.\n"),
  11015 
  11016 #ifdef DEBUG
  11017    JS_FN_HELP("setRNGState", SetRNGState, 2, 0,
  11018 "setRNGState(seed0, seed1)",
  11019 "  Set this compartment's RNG state.\n"),
  11020 #endif
  11021 
  11022 #ifdef AFLFUZZ
  11023    JS_FN_HELP("aflloop", AflLoop, 1, 0,
  11024 "aflloop(max_cnt)",
  11025 "  Call the __AFL_LOOP() runtime function (see AFL docs)\n"),
  11026 #endif
  11027 
  11028    JS_FN_HELP("monotonicNow", MonotonicNow, 0, 0,
  11029 "monotonicNow()",
  11030 "  Return a timestamp reflecting the current elapsed system time.\n"
  11031 "  This is monotonically increasing.\n"),
  11032 
  11033    JS_FN_HELP("timeSinceCreation", TimeSinceCreation, 0, 0,
  11034 "TimeSinceCreation()",
  11035 "  Returns the time in milliseconds since process creation.\n"
  11036 "  This uses a clock compatible with the profiler.\n"),
  11037 
  11038    JS_FN_HELP("isConstructor", IsConstructor, 1, 0,
  11039 "isConstructor(value)",
  11040 "  Returns whether the value is considered IsConstructor.\n"),
  11041 
  11042    JS_FN_HELP("getTimeZone", GetTimeZone, 0, 0,
  11043 "getTimeZone()",
  11044 "  Get the current time zone.\n"),
  11045 
  11046    JS_FN_HELP("getDefaultLocale", GetDefaultLocale, 0, 0,
  11047 "getDefaultLocale()",
  11048 "  Get the current default locale.\n"),
  11049 
  11050    JS_FN_HELP("getCoreCount", GetCoreCount, 0, 0,
  11051 "getCoreCount()",
  11052 "  Get the number of CPU cores from the platform layer.  Typically this\n"
  11053 "  means the number of hyperthreads on systems where that makes sense.\n"),
  11054 
  11055    JS_FN_HELP("setTimeResolution", SetTimeResolution, 2, 0,
  11056 "setTimeResolution(resolution, jitter)",
  11057 "  Enables time clamping and jittering. Specify a time resolution in\n"
  11058 "  microseconds and whether or not to jitter\n"),
  11059 
  11060    JS_FN_HELP("scriptedCallerGlobal", ScriptedCallerGlobal, 0, 0,
  11061 "scriptedCallerGlobal()",
  11062 "  Get the caller's global (or null). See JS::GetScriptedCallerGlobal.\n"),
  11063 
  11064    JS_FN_HELP("objectGlobal", ObjectGlobal, 1, 0,
  11065 "objectGlobal(obj)",
  11066 "  Returns the object's global object or null if the object is a wrapper.\n"),
  11067 
  11068    JS_FN_HELP("isSameCompartment", IsSameCompartment, 2, 0,
  11069 "isSameCompartment(obj1, obj2)",
  11070 "  Unwraps obj1 and obj2 and returns whether the unwrapped objects are\n"
  11071 "  same-compartment.\n"),
  11072 
  11073    JS_FN_HELP("firstGlobalInCompartment", FirstGlobalInCompartment, 1, 0,
  11074 "firstGlobalInCompartment(obj)",
  11075 "  Returns the first global in obj's compartment.\n"),
  11076 
  11077    JS_FN_HELP("assertCorrectRealm", AssertCorrectRealm, 0, 0,
  11078 "assertCorrectRealm()",
  11079 "  Asserts cx->realm matches callee->realm.\n"),
  11080 
  11081    JS_FN_HELP("globalLexicals", GlobalLexicals, 0, 0,
  11082 "globalLexicals()",
  11083 "  Returns an object containing a copy of all global lexical bindings.\n"
  11084 "  Example use: let x = 1; assertEq(globalLexicals().x, 1);\n"),
  11085 
  11086    JS_FN_HELP("baselineCompile", BaselineCompile, 2, 0,
  11087 "baselineCompile([fun/code], forceDebugInstrumentation=false)",
  11088 "  Baseline-compiles the given JS function or script.\n"
  11089 "  Without arguments, baseline-compiles the caller's script; but note\n"
  11090 "  that extra boilerplate is needed afterwards to cause the VM to start\n"
  11091 "  running the jitcode rather than staying in the interpreter:\n"
  11092 "    baselineCompile();  for (var i=0; i<1; i++) {} ...\n"
  11093 "  The interpreter will enter the new jitcode at the loop header unless\n"
  11094 "  baselineCompile returned a string or threw an error.\n"),
  11095    JS_FN_HELP("setBaselineHint", SetBaselineHint, 1, 0,
  11096 "setBaselineHint(fun)",
  11097 "  Sets a baseline JIT hint for the given function, marking it for eager\n"
  11098 "  baseline compilation on subsequent executions.\n"),
  11099    JS_FN_HELP("hasBaselineHint", HasBaselineHint, 1, 0,
  11100 "hasBaselineHint(fun)",
  11101 "  Returns true if the given function has a baseline JIT hint set.\n"),
  11102 
  11103    JS_FN_HELP("encodeAsUtf8InBuffer", EncodeAsUtf8InBuffer, 2, 0,
  11104 "encodeAsUtf8InBuffer(str, uint8Array)",
  11105 "  Encode as many whole code points from the string str into the provided\n"
  11106 "  Uint8Array as will completely fit in it, converting lone surrogates to\n"
  11107 "  REPLACEMENT CHARACTER.  Return an array [r, w] where |r| is the\n"
  11108 "  number of 16-bit units read and |w| is the number of bytes of UTF-8\n"
  11109 "  written."),
  11110 
  11111   JS_FN_HELP("clearKeptObjects", ClearKeptObjects, 0, 0,
  11112 "clearKeptObjects()",
  11113 "Perform the ECMAScript ClearKeptObjects operation, clearing the list of\n"
  11114 "observed WeakRef targets that are kept alive until the next synchronous\n"
  11115 "sequence of ECMAScript execution completes. This is used for testing\n"
  11116 "WeakRefs.\n"),
  11117 
  11118  JS_FN_HELP("numberToDouble", NumberToDouble, 1, 0,
  11119 "numberToDouble(number)",
  11120 "  Return the input number as double-typed number."),
  11121 
  11122 JS_FN_HELP("getICUOptions", GetICUOptions, 0, 0,
  11123 "getICUOptions()",
  11124 "  Return an object describing the following ICU options.\n\n"
  11125 "    version: a string containing the ICU version number, e.g. '67.1'\n"
  11126 "    unicode: a string containing the Unicode version number, e.g. '13.0'\n"
  11127 "    locale: the ICU default locale, e.g. 'en_US'\n"
  11128 "    tzdata: a string containing the tzdata version number, e.g. '2020a'\n"
  11129 "    timezone: the ICU default time zone, e.g. 'America/Los_Angeles'\n"
  11130 "    host-timezone: the host time zone, e.g. 'America/Los_Angeles'"),
  11131 
  11132 JS_FN_HELP("getAvailableLocalesOf", GetAvailableLocalesOf, 0, 0,
  11133 "getAvailableLocalesOf(name)",
  11134 "  Return an array of all available locales for the given Intl constuctor."),
  11135 
  11136 JS_FN_HELP("isSmallFunction", IsSmallFunction, 1, 0,
  11137 "isSmallFunction(fun)",
  11138 "  Returns true if a scripted function is small enough to be inlinable."),
  11139 
  11140    JS_FN_HELP("compileToStencil", CompileToStencil, 1, 0,
  11141 "compileToStencil(string, [options])",
  11142 "  Parses the given string argument as js script, returns the stencil"
  11143 "  for it."),
  11144 
  11145    JS_FN_HELP("evalStencil", EvalStencil, 1, 0,
  11146 "evalStencil(stencil, [options])",
  11147 "  Instantiates the given stencil, and evaluates the top-level script it"
  11148 "  defines."),
  11149 
  11150    JS_FN_HELP("compileToStencilXDR", CompileToStencilXDR, 1, 0,
  11151 "compileToStencilXDR(string, [options])",
  11152 "  Parses the given string argument as js script, produces the stencil"
  11153 "  for it, XDR-encodes the stencil, and returns an object that contains the"
  11154 "  XDR buffer."),
  11155 
  11156    JS_FN_HELP("evalStencilXDR", EvalStencilXDR, 1, 0,
  11157 "evalStencilXDR(stencilXDR, [options])",
  11158 "  Reads the given stencil XDR object, and evaluates the top-level script it"
  11159 "  defines."),
  11160 
  11161    JS_FN_HELP("getExceptionInfo", GetExceptionInfo, 1, 0,
  11162 "getExceptionInfo(fun)",
  11163 "  Calls the given function and returns information about the exception it"
  11164 "  throws. Returns null if the function didn't throw an exception."),
  11165 
  11166    JS_FN_HELP("nukeCCW", NukeCCW, 1, 0,
  11167 "nukeCCW(wrapper)",
  11168 "  Nuke a CrossCompartmentWrapper, which turns it into a DeadProxyObject."),
  11169 
  11170  JS_FN_HELP("assertRealmFuseInvariants", AssertRealmFuseInvariants, 0, 0,
  11171  "assertRealmFuseInvariants()",
  11172  " Runs the realm's fuse invariant checks -- these will crash on failure. "
  11173  " Only available in fuzzing or debug builds, so usage should be guarded. "),
  11174 
  11175  JS_FN_HELP("assertRuntimeFuseInvariants", AssertRuntimeFuseInvariants, 0, 0,
  11176  "assertRuntimeFuseInvariants()",
  11177  " Runs the runtime's fuse invariant checks -- these will crash on failure. "
  11178  " Only available in fuzzing or debug builds, so usage should be guarded. "),
  11179 
  11180    JS_FN_HELP("isCCW", IsCCW, 1, 0,
  11181 "isCCW(object)",
  11182 "  Return true if an object is a CCW."),
  11183 
  11184  JS_FN_HELP("popAllFusesInRealm", PopAllFusesInRealm, 0, 0,
  11185  "popAllFusesInRealm()",
  11186  " Pops all the fuses in the current realm"),
  11187 
  11188    JS_FN_HELP("getLastOOMStackTrace", GetLastOOMStackTrace, 0, 0,
  11189 "getLastOOMStackTrace()",
  11190 "  Returns the stack trace captured from the most recent out-of-memory exception,\n"
  11191 "  or null if no OOM stack trace is available. The stack trace shows the JavaScript\n"
  11192 "  call stack at the time the out-of-memory condition occurred."),
  11193 
  11194  JS_FN_HELP("popAllFusesInRuntime", PopAllFusesInRuntime, 0, 0,
  11195  "popAllFusesInRuntime()",
  11196  " Pops all the fuses in the runtime"),
  11197 
  11198    JS_FN_HELP("getAllPrefNames", GetAllPrefNames, 0, 0,
  11199 "getAllPrefNames()",
  11200 "  Returns an array containing the names of all JS prefs."),
  11201 
  11202    JS_FN_HELP("getPrefValue", GetPrefValue, 1, 0,
  11203 "getPrefValue(name)",
  11204 "  Return the value of the JS pref with the given name."),
  11205 
  11206  JS_FN_HELP("hadOutOfMemory", HadOutOfMemory, 0, 0,
  11207 "hadOutOfMemory()",
  11208 "  Return the runtime's internal hadOutOfMemory flag that is set when\n"
  11209 "  out of memory is hit with an exception being propagated. "),
  11210 
  11211 JS_FN_HELP("supportDifferentialTesting", TestingFunc_SupportDifferentialTesting, 0, 0,
  11212 "supportDifferentialTesting()",
  11213 "  Return the value of JS::SupportDifferentialTesting."),
  11214 
  11215  JS_FS_HELP_END
  11216 };
  11217 // clang-format on
  11218 
  11219 // clang-format off
  11220 static const JSFunctionSpecWithHelp FuzzingUnsafeTestingFunctions[] = {
  11221 JS_FN_HELP("getErrorNotes", GetErrorNotes, 1, 0,
  11222 "getErrorNotes(error)",
  11223 "  Returns an array of error notes."),
  11224 
  11225 JS_FN_HELP("setTimeZone", SetTimeZone, 1, 0,
  11226 "setTimeZone(tzname)",
  11227 "  Set the 'TZ' environment variable to the given time zone and applies the new time zone.\n"
  11228 "  The time zone given is validated according to the current environment.\n"
  11229 "  An empty string or undefined resets the time zone to its default value."),
  11230 
  11231 JS_FN_HELP("getRealmTimeZone", GetRealmTimeZone, 0, 0,
  11232 "getRealmTimeZone()",
  11233 "  Get the time zone for the current realm.\n"),
  11234 
  11235 JS_FN_HELP("setRealmTimeZone", SetRealmTimeZone, 1, 0,
  11236 "setRealmTimeZone(tzname)",
  11237 "  Set the time zone for the current realm.\n"
  11238 "  The time zone must be a valid IANA time zone identifier.\n"
  11239 "  An empty string or undefined resets the realm time zone to the system default time zone."),
  11240 
  11241 JS_FN_HELP("setDefaultLocale", SetDefaultLocale, 1, 0,
  11242 "setDefaultLocale(locale)",
  11243 "  Set the runtime default locale to the given value.\n"
  11244 "  An empty string or undefined resets the runtime locale to its default value.\n"
  11245 "  NOTE: The input string is not fully validated, it must be a valid BCP-47 language tag."),
  11246 
  11247 JS_FN_HELP("getRealmLocale", GetRealmLocale, 0, 0,
  11248 "getRealmLocale()",
  11249 "  Get the locale for the current realm."),
  11250 
  11251 JS_FN_HELP("setRealmLocale", SetRealmLocale, 1, 0,
  11252 "setRealmLocale(locale)",
  11253 "  Set the locale for the current realm.\n"
  11254 "  The locale must be a valid BCP-47 locale identifier.\n"
  11255 "  An empty string or undefined resets the realm locale to the system default locale."),
  11256 
  11257 JS_FN_HELP("isCollectingDelazifications", IsCollectingDelazifications, 1, 0,
  11258 "isCollectingDelazifications(fun)",
  11259 "  True if script for the function is collecting delazifications."),
  11260 
  11261 JS_FN_HELP("isDelazificationPopulatedFor", IsDelazificationsPopulated, 1, 0,
  11262 "isDelazificationPopulatedFor(fun)",
  11263 "  True if fun is available in the shared stencils."),
  11264 
  11265 JS_FN_HELP("waitForDelazificationOf", WaitForDelazificationOf, 1, 0,
  11266 "waitForDelazificationOf(fun)",
  11267 "  Block main thread execution until the function is made available in the\n"
  11268 "  shared stencils. If this function isn't sharing stencils, return immediately."),
  11269 
  11270 JS_FN_HELP("waitForDone", WaitForDone, 1, 0,
  11271 "waitForDone(obj)",
  11272 "  Loop calling `RunJobs` until the `done` value of `obj` is true"),
  11273 
  11274 JS_FN_HELP("getInnerMostEnvironmentObject", GetInnerMostEnvironmentObject, 0, 0,
  11275 "getInnerMostEnvironmentObject()",
  11276 "  Return the inner-most environment object for current execution."),
  11277 
  11278 JS_FN_HELP("getEnclosingEnvironmentObject", GetEnclosingEnvironmentObject, 1, 0,
  11279 "getEnclosingEnvironmentObject(env)",
  11280 "  Return the enclosing environment object for given environment object."),
  11281 
  11282 JS_FN_HELP("getEnvironmentObjectType", GetEnvironmentObjectType, 1, 0,
  11283 "getEnvironmentObjectType(env)",
  11284 "  Return a string represents the type of given environment object."),
  11285 
  11286 JS_FN_HELP("shortestPaths", ShortestPaths, 3, 0,
  11287 "shortestPaths(targets, options)",
  11288 "  Return an array of arrays of shortest retaining paths. There is an array of\n"
  11289      "  shortest retaining paths for each object in |targets|. Each element in a path\n"
  11290      "  is of the form |{ predecessor, edge }|. |options| may contain:\n"
  11291      "  \n"
  11292      "    maxNumPaths: The maximum number of paths returned in each of those arrays\n"
  11293      "      (default 3).\n"
  11294      "    start: The object to start all paths from. If not given, then\n"
  11295      "      the starting point will be the set of GC roots."),
  11296 
  11297 JS_FN_HELP("getFuseState", GetFuseState, 0, 0,
  11298 "getFuseState()",
  11299  "  Return an object describing the calling realm's fuse state, "
  11300  "  as well as the state of any runtime fuses."),
  11301 
  11302 #if defined(DEBUG) || defined(JS_JITSPEW)
  11303    JS_FN_HELP("dumpObject", DumpObject, 1, 0,
  11304 "dumpObject(obj)",
  11305 "  Dump an internal representation of an object."),
  11306 
  11307    JS_FN_HELP("dumpValue", DumpValue, 1, 0,
  11308 "dumpValue(v)",
  11309 "  Dump an internal representation of a value."),
  11310 
  11311    JS_FN_HELP("dumpValueToString", DumpValueToString, 1, 0,
  11312 "dumpValue(v)",
  11313 "  Return a dump of an internal representation of a value."),
  11314 
  11315    JS_FN_HELP("dumpStringRepresentation", DumpStringRepresentation, 1, 0,
  11316 "dumpStringRepresentation(str)",
  11317 "  Print a human-readable description of how the string |str| is represented.\n"),
  11318 
  11319    JS_FN_HELP("stringRepresentation", GetStringRepresentation, 1, 0,
  11320 "stringRepresentation(str)",
  11321 "  Return a human-readable description of how the string |str| is represented.\n"),
  11322 #endif
  11323 
  11324    JS_FN_HELP("wasmExtractCode", WasmExtractCode, 1, 0,
  11325 "wasmExtractCode(module[, tier])",
  11326 "  Extracts generated machine code from WebAssembly.Module.  The tier is a string,\n"
  11327 "  'stable', 'best', 'baseline', or 'ion'; the default is 'stable'.  If the request\n"
  11328 "  cannot be satisfied then null is returned.  If the request is 'ion' then block\n"
  11329 "  until background compilation is complete."),
  11330 
  11331    JS_FN_HELP("wasmDis", WasmDisassemble, 1, 0,
  11332 "wasmDis(wasmObject[, options])\n",
  11333 "  Disassembles generated machine code from an exported WebAssembly function,\n"
  11334 "  or from all the functions defined in the module or instance, exported and not.\n"
  11335 "  The `options` is an object with the following optional keys:\n"
  11336 "    asString: boolean - if true, return a string rather than printing on stderr,\n"
  11337 "          the default is false.\n"
  11338 "    tier: string - one of 'stable', 'best', 'baseline', or 'ion'; the default is\n"
  11339 "          'stable'.\n"
  11340 "    kinds: string - if set, and the wasmObject is a module or instance, a\n"
  11341 "           comma-separated list of the following keys, the default is `Function`:\n"
  11342 "      Function         - functions defined in the module\n"
  11343 "      InterpEntry      - C++-to-wasm stubs\n"
  11344 "      JitEntry         - jitted-js-to-wasm stubs\n"
  11345 "      ImportInterpExit - wasm-to-C++ stubs\n"
  11346 "      ImportJitExit    - wasm-to-jitted-JS stubs\n"
  11347 "      all              - all kinds, including obscure ones\n"),
  11348 
  11349    JS_FN_HELP("wasmModuleToText", WasmModuleToText, 1, 0,
  11350 "wasmModuleToText(wasmModule[, options])\n",
  11351 "  Converts a compiled wasm module to the wasm text format.\n"),
  11352 
  11353    JS_FN_HELP("wasmDumpIon", WasmDumpIon, 2, 0,
  11354 "wasmDumpIon(bytecode, funcIndex, [, contents])\n",
  11355 "wasmDumpIon(bytecode, funcIndex, [, contents])"
  11356 "  Returns a dump of compiling a function in the specified module with Ion."
  11357 "  The `contents` flag controls what is dumped. one of:"
  11358 "    `mir` | `unopt-mir`: Unoptimized MIR (the default)"
  11359 "    `opt-mir`: Optimized MIR"
  11360 "    `lir`: LIR"),
  11361 
  11362    JS_FN_HELP("wasmFunctionTier", WasmFunctionTier, 1, 0,
  11363 "wasmFunctionTier(wasmFunc)\n",
  11364 "  Returns the best compiled tier for a function. Either 'baseline' or 'optimized'."),
  11365 
  11366    JS_FN_HELP("wasmMetadataAnalysis", wasmMetadataAnalysis, 1, 0,
  11367 "wasmMetadataAnalysis(wasmObject)",
  11368 "  Prints an analysis of the size of metadata on this wasm object.\n"),
  11369 
  11370    JS_FS_HELP_END
  11371 };
  11372 // clang-format on
  11373 
  11374 // clang-format off
  11375 static const JSFunctionSpecWithHelp PCCountProfilingTestingFunctions[] = {
  11376    JS_FN_HELP("start", PCCountProfiling_Start, 0, 0,
  11377    "start()",
  11378    "  Start PC count profiling."),
  11379 
  11380    JS_FN_HELP("stop", PCCountProfiling_Stop, 0, 0,
  11381    "stop()",
  11382    "  Stop PC count profiling."),
  11383 
  11384    JS_FN_HELP("purge", PCCountProfiling_Purge, 0, 0,
  11385    "purge()",
  11386    "  Purge the collected PC count profiling data."),
  11387 
  11388    JS_FN_HELP("count", PCCountProfiling_ScriptCount, 0, 0,
  11389    "count()",
  11390    "  Return the number of profiled scripts."),
  11391 
  11392    JS_FN_HELP("summary", PCCountProfiling_ScriptSummary, 1, 0,
  11393    "summary(index)",
  11394    "  Return the PC count profiling summary for the given script index.\n"
  11395    "  The script index must be in the range [0, pc.count())."),
  11396 
  11397    JS_FN_HELP("contents", PCCountProfiling_ScriptContents, 1, 0,
  11398    "contents(index)",
  11399    "  Return the complete profiling contents for the given script index.\n"
  11400    "  The script index must be in the range [0, pc.count())."),
  11401 
  11402    JS_FS_HELP_END
  11403 };
  11404 // clang-format on
  11405 
  11406 // clang-format off
  11407 static const JSFunctionSpecWithHelp FdLibMTestingFunctions[] = {
  11408    JS_FN_HELP("pow", FdLibM_Pow, 2, 0,
  11409    "pow(x, y)",
  11410    "  Return x ** y."),
  11411 
  11412    JS_FS_HELP_END
  11413 };
  11414 // clang-format on
  11415 
  11416 bool js::InitTestingFunctions() { return disasmPrinter.init(); }
  11417 
  11418 bool js::DefineTestingFunctions(JSContext* cx, HandleObject obj,
  11419                                bool fuzzingSafe_, bool disableOOMFunctions_) {
  11420  fuzzingSafe = fuzzingSafe_;
  11421  if (EnvVarIsDefined("MOZ_FUZZING_SAFE")) {
  11422    fuzzingSafe = true;
  11423  }
  11424 
  11425  disableOOMFunctions = disableOOMFunctions_;
  11426 
  11427  if (!fuzzingSafe) {
  11428    if (!JS_DefineFunctionsWithHelp(cx, obj, FuzzingUnsafeTestingFunctions)) {
  11429      return false;
  11430    }
  11431 
  11432    RootedObject pccount(cx, JS_NewPlainObject(cx));
  11433    if (!pccount) {
  11434      return false;
  11435    }
  11436 
  11437    if (!JS_DefineProperty(cx, obj, "pccount", pccount, 0)) {
  11438      return false;
  11439    }
  11440 
  11441    if (!JS_DefineFunctionsWithHelp(cx, pccount,
  11442                                    PCCountProfilingTestingFunctions)) {
  11443      return false;
  11444    }
  11445  }
  11446 
  11447  RootedObject fdlibm(cx, JS_NewPlainObject(cx));
  11448  if (!fdlibm) {
  11449    return false;
  11450  }
  11451 
  11452  if (!JS_DefineProperty(cx, obj, "fdlibm", fdlibm, 0)) {
  11453    return false;
  11454  }
  11455 
  11456  if (!JS_DefineFunctionsWithHelp(cx, fdlibm, FdLibMTestingFunctions)) {
  11457    return false;
  11458  }
  11459 
  11460  return JS_DefineFunctionsWithHelp(cx, obj, TestingFunctions);
  11461 }
  11462 
  11463 #ifdef FUZZING_JS_FUZZILLI
  11464 uint32_t js::FuzzilliHashDouble(double value) {
  11465  // We shouldn't GC here as this is called directly from IC code.
  11466  AutoUnsafeCallWithABI unsafe;
  11467  uint64_t v = mozilla::BitwiseCast<uint64_t>(value);
  11468  return static_cast<uint32_t>(v) + static_cast<uint32_t>(v >> 32);
  11469 }
  11470 
  11471 uint32_t js::FuzzilliHashBigInt(BigInt* bigInt) {
  11472  // We shouldn't GC here as this is called directly from IC code.
  11473  AutoUnsafeCallWithABI unsafe;
  11474  return bigInt->hash();
  11475 }
  11476 
  11477 void js::FuzzilliHashObject(JSContext* cx, JSObject* obj) {
  11478  // called from IC and baseline/interpreter
  11479  uint32_t hash;
  11480  FuzzilliHashObjectInl(cx, obj, &hash);
  11481 
  11482  cx->executionHashInputs += 1;
  11483  cx->executionHash = mozilla::RotateLeft(cx->executionHash + hash, 1);
  11484 }
  11485 
  11486 void js::FuzzilliHashObjectInl(JSContext* cx, JSObject* obj, uint32_t* out) {
  11487  *out = 0;
  11488  if (!js::SupportDifferentialTesting()) {
  11489    return;
  11490  }
  11491 
  11492  RootedValue v(cx);
  11493  v.setObject(*obj);
  11494 
  11495  JSAutoStructuredCloneBuffer JSCloner(
  11496      JS::StructuredCloneScope::DifferentProcess, nullptr, nullptr);
  11497  if (JSCloner.write(cx, v)) {
  11498    JSStructuredCloneData& data = JSCloner.data();
  11499    data.ForEachDataChunk([&](const char* aData, size_t aSize) {
  11500      uint32_t h = mozilla::HashBytes(aData, aSize);
  11501      h = (h << 1) | 1;
  11502      *out ^= h;
  11503      *out *= h;
  11504      return true;
  11505    });
  11506  } else if (JS_IsExceptionPending(cx)) {
  11507    JS_ClearPendingException(cx);
  11508  }
  11509 }
  11510 #endif