Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/builtin/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 324 kB image not shown  

Quelle  TestingFunctions.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "builtin/TestingFunctions.h"

#include "mozilla/Atomics.h"
#include "mozilla/Casting.h"
#include "mozilla/FloatingPoint.h"
#ifdef JS_HAS_INTL_API
#  include "mozilla/intl/ICU4CLibrary.h"
#  include "mozilla/intl/Locale.h"
#  include "mozilla/intl/String.h"
#  include "mozilla/intl/TimeZone.h"
#endif
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Span.h"
#include "mozilla/Sprintf.h"
#include "mozilla/StringBuffer.h"
#include "mozilla/TextUtils.h"
#include "mozilla/ThreadLocal.h"

#include <algorithm>
#include <cfloat>
#include <cinttypes>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <functional>
#include <initializer_list>
#include <iterator>
#include <utility>

#if defined(XP_UNIX) && !defined(XP_DARWIN)
#  include <time.h>
#else
#  include <chrono>
#endif

#include "fdlibm.h"
#include "jsapi.h"
#include "jsfriendapi.h"

#ifdef JS_HAS_INTL_API
#  include "builtin/intl/CommonFunctions.h"
#  include "builtin/intl/FormatBuffer.h"
#  include "builtin/intl/SharedIntlData.h"
#endif
#include "builtin/BigInt.h"
#include "builtin/JSON.h"
#include "builtin/MapObject.h"
#include "builtin/Promise.h"
#include "builtin/TestingUtility.h"  // js::ParseCompileOptions, js::ParseDebugMetadata
#include "ds/IdValuePair.h"          // js::IdValuePair
#include "frontend/CompilationStencil.h"  // frontend::CompilationStencil
#include "frontend/FrontendContext.h"     // AutoReportFrontendContext
#include "gc/GC.h"
#include "gc/GCEnum.h"
#include "gc/GCLock.h"
#include "gc/Zone.h"
#include "jit/BaselineJIT.h"
#include "jit/CacheIRSpewer.h"
#include "jit/Disassemble.h"
#include "jit/InlinableNatives.h"
#include "jit/Invalidation.h"
#include "jit/Ion.h"
#include "jit/JitOptions.h"
#include "jit/JitRuntime.h"
#include "jit/JitScript.h"
#include "jit/TrialInlining.h"
#include "js/Array.h"        // JS::NewArrayObject
#include "js/ArrayBuffer.h"  // JS::{DetachArrayBuffer,GetArrayBufferLengthAndData,NewArrayBufferWithContents}
#include "js/CallAndConstruct.h"  // JS::Call, JS::IsCallable, JS::IsConstructor, JS_CallFunction
#include "js/CharacterEncoding.h"
#include "js/CompilationAndEvaluation.h"
#include "js/CompileOptions.h"  // JS::CompileOptions, JS::DecodeOptions, JS::InstantiateOptions
#include "js/Conversions.h"
#include "js/Date.h"
#include "js/experimental/CodeCoverage.h"   // js::GetCodeCoverageSummary
#include "js/experimental/CompileScript.h"  // JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil, JS::PrepareForInstantiate
#include "js/experimental/JSStencil.h"  // JS::Stencil, JS::EncodeStencil, JS::DecodeStencil, JS::InstantiateGlobalStencil
#include "js/experimental/PCCountProfiling.h"  // JS::{Start,Stop}PCCountProfiling, JS::PurgePCCounts, JS::GetPCCountScript{Count,Summary,Contents}
#include "js/experimental/TypedData.h"         // JS_GetObjectAsUint8Array
#include "js/friend/DumpFunctions.h"  // js::Dump{Backtrace,Heap,Object}, JS::FormatStackDump, js::IgnoreNurseryObjects
#include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
#include "js/friend/WindowProxy.h"    // js::ToWindowProxyIfWindow
#include "js/GlobalObject.h"
#include "js/HashTable.h"
#include "js/Interrupt.h"
#include "js/LocaleSensitive.h"
#include "js/Prefs.h"
#include "js/Printf.h"
#include "js/PropertyAndElement.h"  // JS_DefineProperties, JS_DefineProperty, JS_DefinePropertyById, JS_Enumerate, JS_GetProperty, JS_GetPropertyById, JS_HasProperty, JS_SetElement, JS_SetProperty
#include "js/PropertySpec.h"
#include "js/SourceText.h"
#include "js/StableStringChars.h"
#include "js/Stack.h"
#include "js/String.h"  // JS::GetLinearStringLength, JS::StringToLinearString
#include "js/StructuredClone.h"
#include "js/Transcoding.h"  // JS::TranscodeResult, JS::TranscodeRange, JS::TranscodeBuffer, JS::IsTranscodeFailureResult
#include "js/UbiNode.h"
#include "js/UbiNodeBreadthFirst.h"
#include "js/UbiNodeShortestPaths.h"
#include "js/UniquePtr.h"
#include "js/Vector.h"
#include "js/Wrapper.h"
#include "threading/CpuCount.h"
#include "util/DifferentialTesting.h"
#include "util/StringBuilder.h"
#include "util/Text.h"
#include "vm/BooleanObject.h"
#include "vm/DateObject.h"
#include "vm/DateTime.h"
#include "vm/ErrorObject.h"
#include "vm/GlobalObject.h"
#include "vm/HelperThreads.h"
#include "vm/HelperThreadState.h"
#include "vm/Interpreter.h"
#include "vm/JSContext.h"
#include "vm/JSObject.h"
#include "vm/NumberObject.h"
#include "vm/PlainObject.h"    // js::PlainObject
#include "vm/PromiseObject.h"  // js::PromiseObject, js::PromiseSlot_*
#include "vm/ProxyObject.h"
#include "vm/RealmFuses.h"
#include "vm/SavedStacks.h"
#include "vm/ScopeKind.h"
#include "vm/Stack.h"
#include "vm/StencilObject.h"  // StencilObject, StencilXDRBufferObject
#include "vm/StringObject.h"
#include "vm/StringType.h"
#include "wasm/AsmJS.h"
#include "wasm/WasmBaselineCompile.h"
#include "wasm/WasmBuiltinModule.h"
#include "wasm/WasmFeatures.h"
#include "wasm/WasmGcObject.h"
#include "wasm/WasmInstance.h"
#include "wasm/WasmIonCompile.h"
#include "wasm/WasmJS.h"
#include "wasm/WasmModule.h"
#include "wasm/WasmValType.h"
#include "wasm/WasmValue.h"

#include "debugger/DebugAPI-inl.h"
#include "vm/Compartment-inl.h"
#include "vm/EnvironmentObject-inl.h"
#include "vm/JSContext-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/ObjectFlags-inl.h"
#include "vm/StringType-inl.h"
#include "wasm/WasmInstance-inl.h"

using namespace js;

using mozilla::AssertedCast;
using mozilla::AsWritableChars;
using mozilla::Maybe;
using mozilla::Span;

using JS::AutoStableStringChars;
using JS::CompileOptions;
using JS::SliceBudget;
using JS::SourceText;
using JS::WorkBudget;

// If fuzzingSafe is set, remove functionality that could cause problems with
// fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
mozilla::Atomic<bool> js::fuzzingSafe(false);

// If disableOOMFunctions is set, disable functionality that causes artificial
// OOM conditions.
static mozilla::Atomic<bool> disableOOMFunctions(false);

static bool EnvVarIsDefined(const char* name) {
  const char* value = getenv(name);
  return value && *value;
}

#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
static bool EnvVarAsInt(const char* name, int* valueOut) {
  if (!EnvVarIsDefined(name)) {
    return false;
  }

  *valueOut = atoi(getenv(name));
  return true;
}
#endif

static bool GetRealmConfiguration(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  RootedObject callee(cx, &args.callee());
  RootedObject info(cx, JS_NewPlainObject(cx));
  if (!info) {
    return false;
  }
  if (args.length() > 1) {
    ReportUsageErrorASCII(cx, callee, "Must have zero or one arguments");
    return false;
  }
  if (args.length() == 1 && !args[0].isString()) {
    ReportUsageErrorASCII(cx, callee, "Argument must be a string");
    return false;
  }

  bool importAttributes = cx->options().importAttributes();
  if (!JS_SetProperty(cx, info, "importAttributes",
                      importAttributes ? TrueHandleValue : FalseHandleValue)) {
    return false;
  }

  if (args.length() == 1) {
    RootedString str(cx, ToString(cx, args[0]));
    if (!str) {
      return false;
    }
    RootedId id(cx);
    if (!JS_StringToId(cx, str, &id)) {
      return false;
    }

    bool hasProperty;
    if (JS_HasPropertyById(cx, info, id, &hasProperty) && hasProperty) {
      // Returning a true/false from GetProperty
      return GetProperty(cx, info, info, id, args.rval());
    }

    ReportUsageErrorASCII(cx, callee, "Invalid option name");
    return false;
  }

  args.rval().setObject(*info);
  return true;
}

static bool GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  RootedObject callee(cx, &args.callee());
  RootedObject info(cx, JS_NewPlainObject(cx));
  if (!info) {
    return false;
  }
  if (args.length() > 1) {
    ReportUsageErrorASCII(cx, callee, "Must have zero or one arguments");
    return false;
  }
  if (args.length() == 1 && !args[0].isString()) {
    ReportUsageErrorASCII(cx, callee, "Argument must be a string");
    return false;
  }

  if (!JS_SetProperty(cx, info, "rooting-analysis", FalseHandleValue)) {
    return false;
  }

  if (!JS_SetProperty(cx, info, "exact-rooting", TrueHandleValue)) {
    return false;
  }

  if (!JS_SetProperty(cx, info, "trace-jscalls-api", FalseHandleValue)) {
    return false;
  }

  if (!JS_SetProperty(cx, info, "incremental-gc", TrueHandleValue)) {
    return false;
  }

  if (!JS_SetProperty(cx, info, "generational-gc", TrueHandleValue)) {
    return false;
  }

  if (!JS_SetProperty(cx, info, "oom-backtraces", FalseHandleValue)) {
    return false;
  }

  RootedValue value(cx);
#ifdef DEBUG
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "debug", value)) {
    return false;
  }

#ifdef RELEASE_OR_BETA
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "release_or_beta", value)) {
    return false;
  }

#ifdef EARLY_BETA_OR_EARLIER
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "early_beta_or_earlier", value)) {
    return false;
  }

#ifdef MOZ_CODE_COVERAGE
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "coverage", value)) {
    return false;
  }

#ifdef JS_HAS_CTYPES
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "has-ctypes", value)) {
    return false;
  }

#if defined(_M_IX86) || defined(__i386__)
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "x86", value)) {
    return false;
  }

#if defined(_M_X64) || defined(__x86_64__)
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "x64", value)) {
    return false;
  }

#ifdef JS_CODEGEN_ARM
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "arm", value)) {
    return false;
  }

#ifdef JS_SIMULATOR_ARM
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "arm-simulator", value)) {
    return false;
  }

#ifdef ANDROID
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "android", value)) {
    return false;
  }

#ifdef XP_WIN
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "windows", value)) {
    return false;
  }

#ifdef XP_MACOSX
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "osx", value)) {
    return false;
  }

#ifdef JS_CODEGEN_ARM64
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "arm64", value)) {
    return false;
  }

#ifdef JS_SIMULATOR_ARM64
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "arm64-simulator", value)) {
    return false;
  }

#ifdef JS_CODEGEN_MIPS32
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "mips32", value)) {
    return false;
  }

#ifdef JS_CODEGEN_MIPS64
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "mips64", value)) {
    return false;
  }

#ifdef JS_SIMULATOR_MIPS32
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "mips32-simulator", value)) {
    return false;
  }

#ifdef JS_SIMULATOR_MIPS64
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "mips64-simulator", value)) {
    return false;
  }

#ifdef JS_SIMULATOR
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "simulator", value)) {
    return false;
  }

#ifdef __wasi__
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "wasi", value)) {
    return false;
  }

#ifdef ENABLE_PORTABLE_BASELINE_INTERP
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "pbl", value)) {
    return false;
  }

#ifdef JS_CODEGEN_LOONG64
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "loong64", value)) {
    return false;
  }

#ifdef JS_SIMULATOR_LOONG64
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "loong64-simulator", value)) {
    return false;
  }

#ifdef JS_CODEGEN_RISCV64
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "riscv64", value)) {
    return false;
  }

#ifdef JS_SIMULATOR_RISCV64
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "riscv64-simulator", value)) {
    return false;
  }

#ifdef MOZ_ASAN
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "asan", value)) {
    return false;
  }

#ifdef MOZ_TSAN
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "tsan", value)) {
    return false;
  }

#ifdef MOZ_UBSAN
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "ubsan", value)) {
    return false;
  }

#ifdef JS_GC_ZEAL
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "has-gczeal", value)) {
    return false;
  }

#ifdef MOZ_PROFILING
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "profiling", value)) {
    return false;
  }

#ifdef INCLUDE_MOZILLA_DTRACE
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "dtrace", value)) {
    return false;
  }

#ifdef MOZ_VALGRIND
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "valgrind", value)) {
    return false;
  }

#ifdef JS_HAS_INTL_API
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "intl-api", value)) {
    return false;
  }

#if defined(SOLARIS)
  value = BooleanValue(false);
#else
  value = BooleanValue(true);
#endif
  if (!JS_SetProperty(cx, info, "mapped-array-buffer", value)) {
    return false;
  }

#ifdef MOZ_MEMORY
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "moz-memory", value)) {
    return false;
  }

  value.setInt32(sizeof(void*));
  if (!JS_SetProperty(cx, info, "pointer-byte-size", value)) {
    return false;
  }

#ifdef ENABLE_DECORATORS
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "decorators", value)) {
    return false;
  }

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "explicit-resource-management", value)) {
    return false;
  }

#ifdef FUZZING
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "fuzzing-defined", value)) {
    return false;
  }

#if (defined(__GNUC__) && defined(__SSE__) && defined(__x86_64__)) || \
    defined(__arm__) || defined(__aarch64__)
  // See js.cpp "disable-main-thread-denormals" command line option.
  value = BooleanValue(true);
#else
  value = BooleanValue(false);
#endif
  if (!JS_SetProperty(cx, info, "can-disable-main-thread-denormals", value)) {
    return false;
  }

  value = Int32Value(JSFatInlineString::MAX_LENGTH_LATIN1);
  if (!JS_SetProperty(cx, info, "inline-latin1-chars", value)) {
    return false;
  }

  value = Int32Value(JSFatInlineString::MAX_LENGTH_TWO_BYTE);
  if (!JS_SetProperty(cx, info, "inline-two-byte-chars", value)) {
    return false;
  }

  value = Int32Value(JSThinInlineString::MAX_LENGTH_LATIN1);
  if (!JS_SetProperty(cx, info, "thin-inline-latin1-chars", value)) {
    return false;
  }

  value = Int32Value(JSThinInlineString::MAX_LENGTH_TWO_BYTE);
  if (!JS_SetProperty(cx, info, "thin-inline-two-byte-chars", value)) {
    return false;
  }

  if (js::ThinInlineAtom::EverInstantiated) {
    value = Int32Value(js::ThinInlineAtom::MAX_LENGTH_LATIN1);
    if (!JS_SetProperty(cx, info, "thin-inline-atom-latin1-chars", value)) {
      return false;
    }

    value = Int32Value(js::ThinInlineAtom::MAX_LENGTH_TWO_BYTE);
    if (!JS_SetProperty(cx, info, "thin-inline-atom-two-byte-chars", value)) {
      return false;
    }
  }

  value = Int32Value(js::FatInlineAtom::MAX_LENGTH_LATIN1);
  if (!JS_SetProperty(cx, info, "fat-inline-atom-latin1-chars", value)) {
    return false;
  }

  value = Int32Value(js::FatInlineAtom::MAX_LENGTH_TWO_BYTE);
  if (!JS_SetProperty(cx, info, "fat-inline-atom-two-byte-chars", value)) {
    return false;
  }

  if (args.length() == 1) {
    RootedString str(cx, ToString(cx, args[0]));
    if (!str) {
      return false;
    }
    RootedId id(cx);
    if (!JS_StringToId(cx, str, &id)) {
      return false;
    }

    bool hasProperty;
    if (JS_HasPropertyById(cx, info, id, &hasProperty) && hasProperty) {
      // Returning a true/false from GetProperty
      return GetProperty(cx, info, info, id, args.rval());
    }

    ReportUsageErrorASCII(cx, callee, "Invalid option name");
    return false;
  }

  args.rval().setObject(*info);
  return true;
}

static bool IsLCovEnabled(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  args.rval().setBoolean(coverage::IsLCovEnabled());
  return true;
}

static bool TrialInline(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  args.rval().setUndefined();

  FrameIter iter(cx);
  if (iter.done() || !iter.isBaseline() || iter.realm() != cx->realm()) {
    return true;
  }

  jit::BaselineFrame* frame = iter.abstractFramePtr().asBaselineFrame();
  if (!jit::CanIonCompileScript(cx, frame->script())) {
    return true;
  }

  return jit::DoTrialInlining(cx, frame);
}

static bool ReturnStringCopy(JSContext* cx, CallArgs& args,
                             const char* message) {
  JSString* str = JS_NewStringCopyZ(cx, message);
  if (!str) {
    return false;
  }

  args.rval().setString(str);
  return true;
}

static bool MaybeGC(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  JS_MaybeGC(cx);
  args.rval().setUndefined();
  return true;
}

static bool GC(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  /*
   * If the first argument is 'zone', we collect any zones previously
   * scheduled for GC via schedulegc. If the first argument is an object, we
   * collect the object's zone (and any other zones scheduled for
   * GC). Otherwise, we collect all zones.
   */

  bool zone = false;
  if (args.length() >= 1) {
    Value arg = args[0];
    if (arg.isString()) {
      if (!JS_StringEqualsLiteral(cx, arg.toString(), "zone", &zone)) {
        return false;
      }
    } else if (arg.isObject()) {
      PrepareZoneForGC(cx, UncheckedUnwrap(&arg.toObject())->zone());
      zone = true;
    }
  }

  JS::GCOptions options = JS::GCOptions::Normal;
  JS::GCReason reason = JS::GCReason::API;
  if (args.length() >= 2) {
    Value arg = args[1];
    if (arg.isString()) {
      bool shrinking = false;
      bool last_ditch = false;
      if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking",
                                  &shrinking)) {
        return false;
      }
      if (!JS_StringEqualsLiteral(cx, arg.toString(), "last-ditch",
                                  &last_ditch)) {
        return false;
      }
      if (shrinking) {
        options = JS::GCOptions::Shrink;
      } else if (last_ditch) {
        options = JS::GCOptions::Shrink;
        reason = JS::GCReason::LAST_DITCH;
      }
    }
  }

  size_t preBytes = cx->runtime()->gc.heapSize.bytes();

  if (zone) {
    PrepareForDebugGC(cx->runtime());
  } else {
    JS::PrepareForFullGC(cx);
  }

  JS::NonIncrementalGC(cx, options, reason);

  char buf[256] = {'\0'};
  if (!js::SupportDifferentialTesting()) {
    SprintfLiteral(buf, "before %zu, after %zu\n", preBytes,
                   cx->runtime()->gc.heapSize.bytes());
  }
  return ReturnStringCopy(cx, args, buf);
}

static bool MinorGC(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  if (args.get(0) == BooleanValue(true)) {
    gc::GCRuntime& gc = cx->runtime()->gc;
    if (gc.nursery().isEnabled()) {
      gc.storeBuffer().setAboutToOverflow(JS::GCReason::FULL_GENERIC_BUFFER);
    }
  }

  cx->minorGC(JS::GCReason::API);
  args.rval().setUndefined();
  return true;
}

#define PARAM_NAME_LIST_ENTRY(name, key, writable) " " name
#define GC_PARAMETER_ARGS_LIST FOR_EACH_GC_PARAM(PARAM_NAME_LIST_ENTRY)

static bool GCParameter(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  JSString* str = ToString(cx, args.get(0));
  if (!str) {
    return false;
  }

  UniqueChars name = EncodeLatin1(cx, str);
  if (!name) {
    return false;
  }

  JSGCParamKey param;
  bool writable;
  if (!GetGCParameterInfo(name.get(), ¶m, &writable)) {
    JS_ReportErrorASCII(
        cx, "the first argument must be one of:" GC_PARAMETER_ARGS_LIST);
    return false;
  }

  // Request mode.
  if (args.length() == 1) {
    uint32_t value = JS_GetGCParameter(cx, param);
    args.rval().setNumber(value);
    return true;
  }

  if (!writable) {
    JS_ReportErrorASCII(cx, "Attempt to change read-only parameter %s",
                        name.get());
    return false;
  }

  if (fuzzingSafe) {
    // Some Params are not yet fuzzing safe and so we silently skip
    // changing said parameters.
    switch (param) {
      case JSGC_SEMISPACE_NURSERY_ENABLED:
        args.rval().setUndefined();
        return true;
      default:
        break;
    }
  }

  if (disableOOMFunctions) {
    switch (param) {
      case JSGC_MAX_BYTES:
      case JSGC_MAX_NURSERY_BYTES:
        args.rval().setUndefined();
        return true;
      default:
        break;
    }
  }

  double d;
  if (!ToNumber(cx, args[1], &d)) {
    return false;
  }

  if (d < 0 || d > UINT32_MAX) {
    JS_ReportErrorASCII(cx, "Parameter value out of range");
    return false;
  }

  uint32_t value = floor(d);
  bool ok = cx->runtime()->gc.setParameter(cx, param, value);
  if (!ok) {
    JS_ReportErrorASCII(cx, "Parameter value out of range");
    return false;
  }

  args.rval().setUndefined();
  return true;
}

static bool FinishBackgroundFree(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  cx->runtime()->gc.waitBackgroundFreeEnd();
  args.rval().setUndefined();
  return true;
}

static bool RelazifyFunctions(JSContext* cx, unsigned argc, Value* vp) {
  // Relazifying functions on GC is usually only done for compartments that are
  // not active. To aid fuzzing, this testing function allows us to relazify
  // even if the compartment is active.

  CallArgs args = CallArgsFromVp(argc, vp);

  // Disable relazification of all scripts on stack. It is a pervasive
  // assumption in the engine that running scripts still have bytecode.
  for (AllScriptFramesIter i(cx); !i.done(); ++i) {
    i.script()->clearAllowRelazify();
  }

  cx->runtime()->allowRelazificationForTesting = true;

  JS::PrepareForFullGC(cx);
  JS::NonIncrementalGC(cx, JS::GCOptions::Shrink, JS::GCReason::API);

  cx->runtime()->allowRelazificationForTesting = false;

  args.rval().setUndefined();
  return true;
}

static bool IsProxy(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  if (args.length() != 1) {
    JS_ReportErrorASCII(cx, "the function takes exactly one argument");
    return false;
  }
  if (!args[0].isObject()) {
    args.rval().setBoolean(false);
    return true;
  }
  args.rval().setBoolean(args[0].toObject().is<ProxyObject>());
  return true;
}

static bool WasmIsSupported(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  args.rval().setBoolean(wasm::HasSupport(cx) &&
                         wasm::AnyCompilerAvailable(cx));
  return true;
}

static bool WasmIsSupportedByHardware(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  args.rval().setBoolean(wasm::HasPlatformSupport());
  return true;
}

static bool WasmDebuggingEnabled(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  args.rval().setBoolean(wasm::HasSupport(cx) && wasm::BaselineAvailable(cx));
  return true;
}

static bool WasmStreamingEnabled(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  args.rval().setBoolean(wasm::StreamingCompilationAvailable(cx));
  return true;
}

static bool WasmCachingEnabled(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  args.rval().setBoolean(wasm::CodeCachingAvailable(cx));
  return true;
}

static bool WasmHugeMemorySupported(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
#ifdef WASM_SUPPORTS_HUGE_MEMORY
  args.rval().setBoolean(true);
#else
  args.rval().setBoolean(false);
#endif
  return true;
}

static bool WasmMaxMemoryPages(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  if (args.length() < 1) {
    JS_ReportErrorASCII(cx, "not enough arguments");
    return false;
  }
  if (!args.get(0).isString()) {
    JS_ReportErrorASCII(cx, "address type must be a string");
    return false;
  }
  RootedString s(cx, args.get(0).toString());
  Rooted<JSLinearString*> ls(cx, s->ensureLinear(cx));
  if (!ls) {
    return false;
  }
  if (StringEqualsLiteral(ls, "i32")) {
    args.rval().setInt32(
        int32_t(wasm::MaxMemoryPages(wasm::AddressType::I32).value()));
    return true;
  }
  if (StringEqualsLiteral(ls, "i64")) {
#ifdef ENABLE_WASM_MEMORY64
    if (wasm::Memory64Available(cx)) {
      args.rval().setInt32(
          int32_t(wasm::MaxMemoryPages(wasm::AddressType::I64).value()));
      return true;
    }
#endif
    JS_ReportErrorASCII(cx, "memory64 not enabled");
    return false;
  }
  JS_ReportErrorASCII(cx, "bad address type");
  return false;
}

static bool WasmThreadsEnabled(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  args.rval().setBoolean(wasm::ThreadsAvailable(cx));
  return true;
}

#define WASM_FEATURE(NAME, ...)                                              \
  static bool Wasm##NAME##Enabled(JSContext* cx, unsigned argc, Value* vp) { \
    CallArgs args = CallArgsFromVp(argc, vp);                                \
    args.rval().setBoolean(wasm::NAME##Available(cx));                       \
    return true;                                                             \
  }
JS_FOR_WASM_FEATURES(WASM_FEATURE);
#undef WASM_FEATURE

static bool WasmSimdEnabled(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  args.rval().setBoolean(wasm::SimdAvailable(cx));
  return true;
}

static bool WasmCompilersPresent(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  char buf[256];
  *buf = 0;
  if (wasm::BaselinePlatformSupport()) {
    strcat(buf, "baseline");
  }
  if (wasm::IonPlatformSupport()) {
    if (*buf) {
      strcat(buf, ",");
    }
    strcat(buf, "ion");
  }

  JSString* result = JS_NewStringCopyZ(cx, buf);
  if (!result) {
    return false;
  }

  args.rval().setString(result);
  return true;
}

static bool WasmCompileMode(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // This triplet of predicates will select zero or one baseline compiler and
  // zero or one optimizing compiler.
  bool baseline = wasm::BaselineAvailable(cx);
  bool ion = wasm::IonAvailable(cx);
  bool none = !baseline && !ion;
  bool tiered = baseline && ion;

  JSStringBuilder result(cx);
  if (none && !result.append("none")) {
    return false;
  }
  if (baseline && !result.append("baseline")) {
    return false;
  }
  if (tiered && !result.append("+")) {
    return false;
  }
  if (ion && !result.append("ion")) {
    return false;
  }
  if (JSString* str = result.finishString()) {
    args.rval().setString(str);
    return true;
  }
  return false;
}

static bool WasmBaselineDisabledByFeatures(JSContext* cx, unsigned argc,
                                           Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  bool isDisabled = false;
  JSStringBuilder reason(cx);
  if (!wasm::BaselineDisabledByFeatures(cx, &isDisabled, &reason)) {
    return false;
  }
  if (isDisabled) {
    JSString* result = reason.finishString();
    if (!result) {
      return false;
    }
    args.rval().setString(result);
  } else {
    args.rval().setBoolean(false);
  }
  return true;
}

static bool WasmIonDisabledByFeatures(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  bool isDisabled = false;
  JSStringBuilder reason(cx);
  if (!wasm::IonDisabledByFeatures(cx, &isDisabled, &reason)) {
    return false;
  }
  if (isDisabled) {
    JSString* result = reason.finishString();
    if (!result) {
      return false;
    }
    args.rval().setString(result);
  } else {
    args.rval().setBoolean(false);
  }
  return true;
}

#ifdef ENABLE_WASM_SIMD
#  ifdef DEBUG
static char lastAnalysisResult[1024];

namespace js {
namespace wasm {
void ReportSimdAnalysis(const char* data) {
  strncpy(lastAnalysisResult, data, sizeof(lastAnalysisResult));
  lastAnalysisResult[sizeof(lastAnalysisResult) - 1] = 0;
}
}  // namespace wasm
}  // namespace js

// Unstable API for white-box testing of SIMD optimizations.
//
// Current API: takes no arguments, returns a string describing the last Simd
// simplification applied.

static bool WasmSimdAnalysis(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  JSString* result =
      JS_NewStringCopyZ(cx, *lastAnalysisResult ? lastAnalysisResult : "none");
  if (!result) {
    return false;
  }
  args.rval().setString(result);
  *lastAnalysisResult = (char)0;
  return true;
}
#  endif
#endif

static bool WasmGlobalFromArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
  if (!wasm::HasSupport(cx)) {
    JS_ReportErrorASCII(cx, "wasm support unavailable");
    return false;
  }
  CallArgs args = CallArgsFromVp(argc, vp);

  if (args.length() < 2) {
    JS_ReportErrorASCII(cx, "not enough arguments");
    return false;
  }

  // Get the type of the value
  wasm::ValType valType;
  if (!wasm::ToValType(cx, args.get(0), &valType)) {
    return false;
  }

  // Get the array buffer for the value
  if (!args.get(1).isObject() ||
      !args.get(1).toObject().is<ArrayBufferObject>()) {
    JS_ReportErrorASCII(cx, "argument is not an array buffer");
    return false;
  }
  Rooted<ArrayBufferObject*> buffer(
      cx, &args.get(1).toObject().as<ArrayBufferObject>());

  // Only allow POD to be created from bytes
  switch (valType.kind()) {
    case wasm::ValType::I32:
    case wasm::ValType::I64:
    case wasm::ValType::F32:
    case wasm::ValType::F64:
    case wasm::ValType::V128:
      break;
    default:
      JS_ReportErrorASCII(
          cx, "invalid valtype for creating WebAssembly.Global from bytes");
      return false;
  }

  // Check we have all the bytes we need
  if (valType.size() != buffer->byteLength()) {
    JS_ReportErrorASCII(cx, "array buffer has incorrect size");
    return false;
  }

  // Copy the bytes from buffer into a tagged val
  wasm::RootedVal val(cx);
  val.get().initFromRootedLocation(valType, buffer->dataPointer());

  // Create the global object
  RootedObject proto(
      cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmGlobal));
  if (!proto) {
    return false;
  }
  Rooted<WasmGlobalObject*> result(
      cx, WasmGlobalObject::create(cx, val, false, proto));
  if (!result) {
    return false;
  }

  args.rval().setObject(*result.get());
  return true;
}

enum class LaneInterp {
  I32x4,
  I64x2,
  F32x4,
  F64x2,
};

size_t LaneInterpLanes(LaneInterp interp) {
  switch (interp) {
    case LaneInterp::I32x4:
      return 4;
    case LaneInterp::I64x2:
      return 2;
    case LaneInterp::F32x4:
      return 4;
    case LaneInterp::F64x2:
      return 2;
    default:
      MOZ_ASSERT_UNREACHABLE();
      return 0;
  }
}

static bool ToLaneInterp(JSContext* cx, HandleValue v, LaneInterp* out) {
  RootedString interpStr(cx, ToString(cx, v));
  if (!interpStr) {
    return false;
  }
  Rooted<JSLinearString*> interpLinearStr(cx, interpStr->ensureLinear(cx));
  if (!interpLinearStr) {
    return false;
  }

  if (StringEqualsLiteral(interpLinearStr, "i32x4")) {
    *out = LaneInterp::I32x4;
    return true;
  } else if (StringEqualsLiteral(interpLinearStr, "i64x2")) {
    *out = LaneInterp::I64x2;
    return true;
  } else if (StringEqualsLiteral(interpLinearStr, "f32x4")) {
    *out = LaneInterp::F32x4;
    return true;
  } else if (StringEqualsLiteral(interpLinearStr, "f64x2")) {
    *out = LaneInterp::F64x2;
    return true;
  }

  JS_ReportErrorASCII(cx, "invalid lane interpretation");
  return false;
}

static bool WasmGlobalExtractLane(JSContext* cx, unsigned argc, Value* vp) {
  if (!wasm::HasSupport(cx)) {
    JS_ReportErrorASCII(cx, "wasm support unavailable");
    return false;
  }
  CallArgs args = CallArgsFromVp(argc, vp);

  if (args.length() < 3) {
    JS_ReportErrorASCII(cx, "not enough arguments");
    return false;
  }

  // Get the global value
  if (!args.get(0).isObject() ||
      !args.get(0).toObject().is<WasmGlobalObject>()) {
    JS_ReportErrorASCII(cx, "argument is not wasm value");
    return false;
  }
  Rooted<WasmGlobalObject*> global(
      cx, &args.get(0).toObject().as<WasmGlobalObject>());

  // Check that we have a v128 value
  if (global->type().kind() != wasm::ValType::V128) {
    JS_ReportErrorASCII(cx, "global is not a v128 value");
    return false;
  }
  wasm::V128 v128 = global->val().get().v128();

  // Get the passed interpretation of lanes
  LaneInterp interp;
  if (!ToLaneInterp(cx, args.get(1), &interp)) {
    return false;
  }

  // Get the lane to extract
  int32_t lane;
  if (!ToInt32(cx, args.get(2), &lane)) {
    return false;
  }

  // Check that the lane interp is valid
  if (lane < 0 || size_t(lane) >= LaneInterpLanes(interp)) {
    JS_ReportErrorASCII(cx, "invalid lane for interp");
    return false;
  }

  wasm::RootedVal val(cx);
  switch (interp) {
    case LaneInterp::I32x4: {
      uint32_t i;
      v128.extractLane<uint32_t>(lane, &i);
      val.set(wasm::Val(i));
      break;
    }
    case LaneInterp::I64x2: {
      uint64_t i;
      v128.extractLane<uint64_t>(lane, &i);
      val.set(wasm::Val(i));
      break;
    }
    case LaneInterp::F32x4: {
      float f;
      v128.extractLane<float>(lane, &f);
      val.set(wasm::Val(f));
      break;
    }
    case LaneInterp::F64x2: {
      double d;
      v128.extractLane<double>(lane, &d);
      val.set(wasm::Val(d));
      break;
    }
    default:
      MOZ_ASSERT_UNREACHABLE();
  }

  RootedObject proto(
      cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmGlobal));
  Rooted<WasmGlobalObject*> result(
      cx, WasmGlobalObject::create(cx, val, false, proto));
  args.rval().setObject(*result.get());
  return true;
}

static bool WasmGlobalsEqual(JSContext* cx, unsigned argc, Value* vp) {
  if (!wasm::HasSupport(cx)) {
    JS_ReportErrorASCII(cx, "wasm support unavailable");
    return false;
  }
  CallArgs args = CallArgsFromVp(argc, vp);

  if (args.length() < 2) {
    JS_ReportErrorASCII(cx, "not enough arguments");
    return false;
  }

  if (!args.get(0).isObject() ||
      !args.get(0).toObject().is<WasmGlobalObject>() ||
      !args.get(1).isObject() ||
      !args.get(1).toObject().is<WasmGlobalObject>()) {
    JS_ReportErrorASCII(cx, "argument is not wasm value");
    return false;
  }

  Rooted<WasmGlobalObject*> a(cx,
                              &args.get(0).toObject().as<WasmGlobalObject>());
  Rooted<WasmGlobalObject*> b(cx,
                              &args.get(1).toObject().as<WasmGlobalObject>());

  if (a->type().kind() != b->type().kind()) {
    JS_ReportErrorASCII(cx, "globals are of different kind");
    return false;
  }

  bool result;
  const wasm::Val& aVal = a->val().get();
  const wasm::Val& bVal = b->val().get();
  switch (a->type().kind()) {
    case wasm::ValType::I32: {
      result = aVal.i32() == bVal.i32();
      break;
    }
    case wasm::ValType::I64: {
      result = aVal.i64() == bVal.i64();
      break;
    }
    case wasm::ValType::F32: {
      result = mozilla::BitwiseCast<uint32_t>(aVal.f32()) ==
               mozilla::BitwiseCast<uint32_t>(bVal.f32());
      break;
    }
    case wasm::ValType::F64: {
      result = mozilla::BitwiseCast<uint64_t>(aVal.f64()) ==
               mozilla::BitwiseCast<uint64_t>(bVal.f64());
      break;
    }
    case wasm::ValType::V128: {
      // Don't know the interpretation of the v128, so we only can do an exact
      // bitwise equality. Testing code can use wasmGlobalExtractLane to
      // workaround this if needed.
      result = aVal.v128() == bVal.v128();
      break;
    }
    case wasm::ValType::Ref: {
      result = aVal.ref() == bVal.ref();
      break;
    }
    default:
      JS_ReportErrorASCII(cx, "unsupported type");
      return false;
  }
  args.rval().setBoolean(result);
  return true;
}

// Flavors of NaN values for WebAssembly.
// See
// https://webassembly.github.io/spec/core/syntax/values.html#floating-point.
enum class NaNFlavor {
  // A canonical NaN value.
  //  - the sign bit is unspecified,
  //  - the 8-bit exponent is set to all 1s
  //  - the MSB of the payload is set to 1 (a quieted NaN) and all others to 0.
  Canonical,
  // An arithmetic NaN. This is the same as a canonical NaN including that the
  // payload MSB is set to 1, but one or more of the remaining payload bits MAY
  // BE set to 1 (a canonical NaN specifies all 0s).
  Arithmetic,
};

static bool IsNaNFlavor(uint32_t bits, NaNFlavor flavor) {
  switch (flavor) {
    case NaNFlavor::Canonical: {
      return (bits & 0x7fffffff) == 0x7fc00000;
    }
    case NaNFlavor::Arithmetic: {
      const uint32_t ArithmeticNaN = 0x7f800000;
      const uint32_t ArithmeticPayloadMSB = 0x00400000;
      bool isNaN = (bits & ArithmeticNaN) == ArithmeticNaN;
      bool isMSBSet = (bits & ArithmeticPayloadMSB) == ArithmeticPayloadMSB;
      return isNaN && isMSBSet;
    }
    default:
      MOZ_CRASH();
  }
}

static bool IsNaNFlavor(uint64_t bits, NaNFlavor flavor) {
  switch (flavor) {
    case NaNFlavor::Canonical: {
      return (bits & 0x7fffffffffffffff) == 0x7ff8000000000000;
    }
    case NaNFlavor::Arithmetic: {
      uint64_t ArithmeticNaN = 0x7ff0000000000000;
      uint64_t ArithmeticPayloadMSB = 0x0008000000000000;
      bool isNaN = (bits & ArithmeticNaN) == ArithmeticNaN;
      bool isMsbSet = (bits & ArithmeticPayloadMSB) == ArithmeticPayloadMSB;
      return isNaN && isMsbSet;
    }
    default:
      MOZ_CRASH();
  }
}

static bool ToNaNFlavor(JSContext* cx, HandleValue v, NaNFlavor* out) {
  RootedString flavorStr(cx, ToString(cx, v));
  if (!flavorStr) {
    return false;
  }
  Rooted<JSLinearString*> flavorLinearStr(cx, flavorStr->ensureLinear(cx));
  if (!flavorLinearStr) {
    return false;
  }

  if (StringEqualsLiteral(flavorLinearStr, "canonical_nan")) {
    *out = NaNFlavor::Canonical;
    return true;
  } else if (StringEqualsLiteral(flavorLinearStr, "arithmetic_nan")) {
    *out = NaNFlavor::Arithmetic;
    return true;
  }

  JS_ReportErrorASCII(cx, "invalid nan flavor");
  return false;
}

static bool WasmGlobalIsNaN(JSContext* cx, unsigned argc, Value* vp) {
  if (!wasm::HasSupport(cx)) {
    JS_ReportErrorASCII(cx, "wasm support unavailable");
    return false;
  }
  CallArgs args = CallArgsFromVp(argc, vp);

  if (args.length() < 2) {
    JS_ReportErrorASCII(cx, "not enough arguments");
    return false;
  }

  if (!args.get(0).isObject() ||
      !args.get(0).toObject().is<WasmGlobalObject>()) {
    JS_ReportErrorASCII(cx, "argument is not wasm value");
    return false;
  }
  Rooted<WasmGlobalObject*> global(
      cx, &args.get(0).toObject().as<WasmGlobalObject>());

  NaNFlavor flavor;
  if (!ToNaNFlavor(cx, args.get(1), &flavor)) {
    return false;
  }

  bool result;
  const wasm::Val& val = global->val().get();
  switch (global->type().kind()) {
    case wasm::ValType::F32: {
      result = IsNaNFlavor(mozilla::BitwiseCast<uint32_t>(val.f32()), flavor);
      break;
    }
    case wasm::ValType::F64: {
      result = IsNaNFlavor(mozilla::BitwiseCast<uint64_t>(val.f64()), flavor);
      break;
    }
    default:
      JS_ReportErrorASCII(cx, "global is not a floating point value");
      return false;
  }
  args.rval().setBoolean(result);
  return true;
}

static bool WasmGlobalToString(JSContext* cx, unsigned argc, Value* vp) {
  if (!wasm::HasSupport(cx)) {
    JS_ReportErrorASCII(cx, "wasm support unavailable");
    return false;
  }
  CallArgs args = CallArgsFromVp(argc, vp);

  if (args.length() < 1) {
    JS_ReportErrorASCII(cx, "not enough arguments");
    return false;
  }
  if (!args.get(0).isObject() ||
      !args.get(0).toObject().is<WasmGlobalObject>()) {
    JS_ReportErrorASCII(cx, "argument is not wasm value");
    return false;
  }
  Rooted<WasmGlobalObject*> global(
      cx, &args.get(0).toObject().as<WasmGlobalObject>());
  const wasm::Val& globalVal = global->val().get();

  UniqueChars result;
  switch (globalVal.type().kind()) {
    case wasm::ValType::I32: {
      result = JS_smprintf("i32:%" PRIx32, globalVal.i32());
      break;
    }
    case wasm::ValType::I64: {
      result = JS_smprintf("i64:%" PRIx64, globalVal.i64());
      break;
    }
    case wasm::ValType::F32: {
      result = JS_smprintf("f32:%f", globalVal.f32());
      break;
    }
    case wasm::ValType::F64: {
      result = JS_smprintf("f64:%lf", globalVal.f64());
      break;
    }
    case wasm::ValType::V128: {
      wasm::V128 v128 = globalVal.v128();
      result = JS_smprintf(
          "v128:%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x", v128.bytes[0],
          v128.bytes[1], v128.bytes[2], v128.bytes[3], v128.bytes[4],
          v128.bytes[5], v128.bytes[6], v128.bytes[7], v128.bytes[8],
          v128.bytes[9], v128.bytes[10], v128.bytes[11], v128.bytes[12],
          v128.bytes[13], v128.bytes[14], v128.bytes[15]);
      break;
    }
    case wasm::ValType::Ref: {
      result = JS_smprintf("ref:%" PRIxPTR, globalVal.ref().rawValue());
      break;
    }
    default:
      MOZ_ASSERT_UNREACHABLE();
  }

  args.rval().setString(JS_NewStringCopyZ(cx, result.get()));
  return true;
}

static bool WasmLosslessInvoke(JSContext* cx, unsigned argc, Value* vp) {
  if (!wasm::HasSupport(cx)) {
    JS_ReportErrorASCII(cx, "wasm support unavailable");
    return false;
  }
  CallArgs args = CallArgsFromVp(argc, vp);

  if (args.length() < 1) {
    JS_ReportErrorASCII(cx, "not enough arguments");
    return false;
  }
  if (!args.get(0).isObject() || !args.get(0).toObject().is<JSFunction>()) {
    JS_ReportErrorASCII(cx, "argument is not an object");
    return false;
  }

  RootedFunction func(cx, &args[0].toObject().as<JSFunction>());
  if (!func || !func->isWasm()) {
    JS_ReportErrorASCII(cx, "argument is not an exported wasm function");
    return false;
  }

  // Switch to the function's realm
  AutoRealm ar(cx, func);

  // Get the instance and funcIndex for calling the function
  wasm::Instance& instance = func->wasmInstance();
  uint32_t funcIndex = func->wasmFuncIndex();

  // Set up a modified call frame following the standard JS
  // [callee, this, arguments...] convention.
  RootedValueVector wasmCallFrame(cx);
  size_t len = 2 + args.length();
  if (!wasmCallFrame.resize(len)) {
    return false;
  }
  wasmCallFrame[0].set(ObjectValue(*func));
  wasmCallFrame[1].set(args.thisv());
  // Copy over the arguments needed to invoke the provided wasm function,
  // skipping the wasm function we're calling that is at `args.get(0)`.
  for (size_t i = 1; i < args.length(); i++) {
    size_t wasmArg = i - 1;
    wasmCallFrame[2 + wasmArg].set(args.get(i));
  }
  size_t wasmArgc = argc - 1;
  CallArgs wasmCallArgs(CallArgsFromVp(wasmArgc, wasmCallFrame.begin()));

  // Invoke the function with the new call frame
  bool result = instance.callExport(cx, funcIndex, wasmCallArgs,
                                    wasm::CoercionLevel::Lossless);
  // Assign the wasm rval to our rval
  args.rval().set(wasmCallArgs.rval());
  return result;
}

static bool ConvertToTier(JSContext* cx, HandleValue value,
                          const wasm::Code& code, wasm::Tier* tier) {
  RootedString option(cx, JS::ToString(cx, value));

  if (!option) {
    return false;
  }

  bool stableTier = false;
  bool bestTier = false;
  bool baselineTier = false;
  bool ionTier = false;

  if (!JS_StringEqualsLiteral(cx, option, "stable", &stableTier) ||
      !JS_StringEqualsLiteral(cx, option, "best", &bestTier) ||
      !JS_StringEqualsLiteral(cx, option, "baseline", &baselineTier) ||
      !JS_StringEqualsLiteral(cx, option, "ion", &ionTier)) {
    return false;
  }

  if (stableTier) {
    *tier = code.stableCompleteTier();
  } else if (bestTier) {
    *tier = code.bestCompleteTier();
  } else if (baselineTier) {
    *tier = wasm::Tier::Baseline;
  } else if (ionTier) {
    *tier = wasm::Tier::Optimized;
  } else {
    // You can omit the argument but you can't pass just anything you like
    return false;
  }

  return true;
}

static bool WasmExtractCode(JSContext* cx, unsigned argc, Value* vp) {
  if (!wasm::HasSupport(cx)) {
    JS_ReportErrorASCII(cx, "wasm support unavailable");
    return false;
  }

  CallArgs args = CallArgsFromVp(argc, vp);

  if (!args.get(0).isObject()) {
    JS_ReportErrorASCII(cx, "argument is not an object");
    return false;
  }

  Rooted<WasmModuleObject*> module(
      cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>());
  if (!module) {
    JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
    return false;
  }

  wasm::Tier tier = module->module().code().stableCompleteTier();
  if (args.length() > 1 &&
      !ConvertToTier(cx, args[1], module->module().code(), &tier)) {
    args.rval().setNull();
    return false;
  }

  RootedValue result(cx);
  if (!module->module().extractCode(cx, tier, &result)) {
    return false;
  }

  args.rval().set(result);
  return true;
}

static bool HasDisassembler(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  args.rval().setBoolean(jit::HasDisassembler());
  return true;
}

MOZ_THREAD_LOCAL(JSSprinter*) disasmPrinter;

static void captureDisasmText(const char* text) {
  JSSprinter* printer = disasmPrinter.get();
  printer->put(text);
  printer->printf("\n");
}

static bool DisassembleNative(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  if (args.length() < 1) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_MORE_ARGS_NEEDED, "disnative""1""",
                              "0");
    return false;
  }

  if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
    JS_ReportErrorASCII(cx, "The first argument must be a function.");
    return false;
  }

  JSSprinter sprinter(cx);
  if (!sprinter.init()) {
    return false;
  }

  RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());

  uint8_t* jit_begin = nullptr;
  uint8_t* jit_end = nullptr;

  if (fun->isAsmJSNative() || fun->isWasmWithJitEntry()) {
    if (IsAsmJSModule(fun)) {
      JS_ReportErrorASCII(cx, "Can't disassemble asm.js module function.");
      return false;
    }
    if (fun->isAsmJSNative()) {
      sprinter.printf("; backend=asmjs\n");
    }
    sprinter.printf("; backend=wasm\n");

    js::wasm::Instance& inst = fun->wasmInstance();
    const uint32_t funcIndex = fun->wasmFuncIndex();
    const js::wasm::CodeBlock& codeBlock = inst.code().funcCodeBlock(funcIndex);
    const js::wasm::CodeSegment& segment = *codeBlock.segment;
    const js::wasm::FuncExport& func = codeBlock.lookupFuncExport(funcIndex);
    const js::wasm::CodeRange& codeRange = codeBlock.codeRange(func);

    jit_begin = segment.base() + codeRange.begin();
    jit_end = segment.base() + codeRange.end();
  } else if (fun->hasJitScript() && fun->nonLazyScript()->hasIonScript()) {
    sprinter.printf("; backend=ion\n");
    jit_begin = fun->nonLazyScript()->ionScript()->method()->raw();
    jit_end = fun->nonLazyScript()->ionScript()->method()->rawEnd();
  } else if (fun->hasJitScript() && fun->nonLazyScript()->hasBaselineScript()) {
    sprinter.printf("; backend=baseline\n");
    jit_begin = fun->nonLazyScript()->baselineScript()->method()->raw();
    jit_end = fun->nonLazyScript()->baselineScript()->method()->rawEnd();
  } else {
    JS_ReportErrorASCII(cx,
                        "The function hasn't been warmed up, hence no JIT code "
                        "to disassemble.");
    return false;
  }

  MOZ_ASSERT(jit_begin);
  MOZ_ASSERT(jit_end);

#ifdef JS_CODEGEN_ARM
  // The ARM32 disassembler is currently not fuzzing-safe because it doesn't
  // handle constant pools correctly (bug 1875363).
  if (fuzzingSafe) {
    JS_ReportErrorASCII(cx, "disnative is not fuzzing-safe on ARM32");
    return false;
  }
#endif

  // Dump the raw code to a file before disassembling in case
  // finishString triggers a GC and discards the jitcode.
  if (!fuzzingSafe && args.length() > 1 && args[1].isString()) {
    RootedString str(cx, args[1].toString());
    JS::UniqueChars fileNameBytes = JS_EncodeStringToUTF8(cx, str);

    const char* fileName = fileNameBytes.get();
    if (!fileName) {
      ReportOutOfMemory(cx);
      return false;
    }

    FILE* f = fopen(fileName, "w");
    if (!f) {
      JS_ReportErrorASCII(cx, "Could not open file for writing.");
      return false;
    }

    uintptr_t expected_length = reinterpret_cast<uintptr_t>(jit_end) -
                                reinterpret_cast<uintptr_t>(jit_begin);
    if (expected_length != fwrite(jit_begin, jit_end - jit_begin, 1, f)) {
      JS_ReportErrorASCII(cx, "Did not write all function bytes to the file.");
      fclose(f);
      return false;
    }
    fclose(f);
  }

  disasmPrinter.set(&sprinter);
  auto onFinish = mozilla::MakeScopeExit([&] { disasmPrinter.set(nullptr); });

  jit::Disassemble(jit_begin, jit_end - jit_begin, &captureDisasmText);

  JSString* str = sprinter.release(cx);
  if (!str) {
    return false;
  }

  args.rval().setString(str);
  return true;
}

static bool DisassembleBaselineICs(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  args.rval().setUndefined();

  if (!args.requireAtLeast(cx, "disblic", 1)) {
    return false;
  }

  if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
    JS_ReportErrorASCII(cx, "The first argument must be a function.");
    return false;
  }

  JSSprinter sprinter(cx);
  if (!sprinter.init()) {
    return false;
  }

  disasmPrinter.set(&sprinter);
  auto onFinish = mozilla::MakeScopeExit([&] { disasmPrinter.set(nullptr); });

  RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());

  if (!fun->hasJitScript()) {
    args.rval().setUndefined();
    return true;
  }

#ifdef JS_CODEGEN_ARM
  // The ARM32 disassembler is currently not fuzzing-safe because it doesn't
  // handle constant pools correctly (bug 1875363).
  if (fuzzingSafe) {
    JS_ReportErrorASCII(cx, "disblic is not fuzzing-safe on ARM32");
    return false;
  }
#endif

  RootedScript script(cx, fun->nonLazyScript());
  jit::ICScript* icScript = script->jitScript()->icScript();
  for (uint32_t i = 0; i < icScript->numICEntries(); i++) {
    jit::ICEntry& entry = icScript->icEntry(i);
    jit::ICStub* stub = entry.firstStub();

    jit::ICStub* fallbackStub = stub;
    while (!fallbackStub->isFallback()) {
      fallbackStub = fallbackStub->toCacheIRStub()->next();
    }
    uint32_t pcOffset = fallbackStub->toFallbackStub()->pcOffset();
    sprinter.printf("; %s (pcOffset %05u)\n",
                    CodeName(JSOp(*script->offsetToPC(pcOffset))), pcOffset);

    uint32_t stubNum = 1;
    while (!stub->isFallback()) {
      sprinter.printf("; Stub #%d (entry count: %d)\n", stubNum,
                      stub->enteredCount());
      jit::ICCacheIRStub* cacheIRStub = stub->toCacheIRStub();
      uint8_t* jit_begin = stub->jitCode()->raw();
      uint8_t* jit_end = stub->jitCode()->rawEnd();
#ifdef JS_CACHEIR_SPEW
      sprinter.printf("; IR:\n");
      SpewCacheIROps(sprinter, "; ", cacheIRStub->stubInfo());
#endif

      jit::Disassemble(jit_begin, jit_end - jit_begin, &captureDisasmText);
      stub = cacheIRStub->next();
      stubNum++;
    }
  }

  JSString* str = sprinter.release(cx);
  if (!str) {
    return false;
  }

  args.rval().setString(str);
  return true;
}

static bool ComputeTier(JSContext* cx, const wasm::Code& code,
                        HandleValue tierSelection, wasm::Tier* tier) {
  *tier = code.stableCompleteTier();
  if (!tierSelection.isUndefined() &&
      !ConvertToTier(cx, tierSelection, code, tier)) {
    JS_ReportErrorASCII(cx, "invalid tier");
    return false;
  }

  return true;
}

template <typename DisasmFunction>
static bool DisassembleIt(JSContext* cx, bool asString, MutableHandleValue rval,
                          DisasmFunction&& disassembleIt) {
  if (asString) {
    JSSprinter sprinter(cx);
    if (!sprinter.init()) {
      return false;
    }
    disasmPrinter.set(&sprinter);
    auto onFinish = mozilla::MakeScopeExit([&] { disasmPrinter.set(nullptr); });
    disassembleIt(captureDisasmText);

    JSString* sresult = sprinter.release(cx);
    if (!sresult) {
      return false;
    }
    rval.setString(sresult);
    return true;
  }

  disassembleIt([](const char* text) { fprintf(stderr, "%s\n", text); });
  return true;
}

static bool WasmDisassembleFunction(JSContext* cx, const HandleFunction& func,
                                    HandleValue tierSelection, bool asString,
                                    MutableHandleValue rval) {
  wasm::Instance& instance = func->wasmInstance();
  uint32_t funcIndex = func->wasmFuncIndex();
  wasm::Tier tier;

  if (!ComputeTier(cx, instance.code(), tierSelection, &tier)) {
    return false;
  }

  if (!instance.code().funcHasTier(funcIndex, tier)) {
    JS_ReportErrorASCII(cx, "function missing selected tier");
    return false;
  }

  return DisassembleIt(
      cx, asString, rval, [&](void (*captureText)(const char*)) {
        instance.disassembleExport(cx, funcIndex, tier, captureText);
      });
}

static bool WasmDisassembleCode(JSContext* cx, const wasm::Code& code,
                                HandleValue tierSelection, int kindSelection,
                                bool asString, MutableHandleValue rval) {
  wasm::Tier tier;
  if (!ComputeTier(cx, code, tierSelection, &tier)) {
    return false;
  }

  return DisassembleIt(cx, asString, rval,
                       [&](void (*captureText)(const char*)) {
                         code.disassemble(cx, tier, kindSelection, captureText);
                       });
}

static bool WasmDisassemble(JSContext* cx, unsigned argc, Value* vp) {
  if (!wasm::HasSupport(cx)) {
    JS_ReportErrorASCII(cx, "wasm support unavailable");
    return false;
  }

  CallArgs args = CallArgsFromVp(argc, vp);

  args.rval().set(UndefinedValue());

  if (!args.get(0).isObject()) {
    JS_ReportErrorASCII(cx, "argument is not an object");
    return false;
  }

  bool asString = false;
  RootedValue tierSelection(cx);
  int kindSelection = (1 << wasm::CodeRange::Function);
  if (args.length() > 1 && args[1].isObject()) {
    RootedObject options(cx, &args[1].toObject());
    RootedValue val(cx);

    if (!JS_GetProperty(cx, options, "asString", &val)) {
      return false;
    }
    asString = val.isBoolean() && val.toBoolean();

    if (!JS_GetProperty(cx, options, "tier", &tierSelection)) {
      return false;
    }

    if (!JS_GetProperty(cx, options, "kinds", &val)) {
      return false;
    }
    if (val.isString() && val.toString()->hasLatin1Chars()) {
      AutoStableStringChars stable(cx);
      if (!stable.init(cx, val.toString())) {
        return false;
      }
      const char* p = (const char*)(stable.latin1Chars());
      const char* end = p + val.toString()->length();
      int selection = 0;
      for (;;) {
        if (strncmp(p, "Function", 8) == 0) {
          selection |= (1 << wasm::CodeRange::Function);
          p += 8;
        } else if (strncmp(p, "InterpEntry", 11) == 0) {
          selection |= (1 << wasm::CodeRange::InterpEntry);
          p += 11;
        } else if (strncmp(p, "JitEntry", 8) == 0) {
          selection |= (1 << wasm::CodeRange::JitEntry);
          p += 8;
        } else if (strncmp(p, "ImportInterpExit", 16) == 0) {
          selection |= (1 << wasm::CodeRange::ImportInterpExit);
          p += 16;
        } else if (strncmp(p, "ImportJitExit", 13) == 0) {
          selection |= (1 << wasm::CodeRange::ImportJitExit);
          p += 13;
        } else if (strncmp(p, "all", 3) == 0) {
          selection = ~0;
          p += 3;
        } else {
          break;
        }
        if (p == end || *p != ',') {
          break;
        }
        p++;
      }
      if (p == end) {
        kindSelection = selection;
      } else {
        JS_ReportErrorASCII(cx, "argument object has invalid `kinds`");
        return false;
      }
    }
  }

  RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>());
  if (func && func->isWasm()) {
    return WasmDisassembleFunction(cx, func, tierSelection, asString,
                                   args.rval());
  }
  if (args[0].toObject().is<WasmModuleObject>()) {
    return WasmDisassembleCode(
        cx, args[0].toObject().as<WasmModuleObject>().module().code(),
        tierSelection, kindSelection, asString, args.rval());
  }
  if (args[0].toObject().is<WasmInstanceObject>()) {
    return WasmDisassembleCode(
        cx, args[0].toObject().as<WasmInstanceObject>().instance().code(),
        tierSelection, kindSelection, asString, args.rval());
  }
  JS_ReportErrorASCII(
      cx, "argument is not an exported wasm function or a wasm module");
  return false;
}

static bool WasmFunctionTier(JSContext* cx, unsigned argc, Value* vp) {
  if (!wasm::HasSupport(cx)) {
    JS_ReportErrorASCII(cx, "wasm support unavailable");
    return false;
  }

  CallArgs args = CallArgsFromVp(argc, vp);

  args.rval().set(UndefinedValue());

  if (!args.get(0).isObject()) {
    JS_ReportErrorASCII(cx, "argument is not an object");
    return false;
  }

  RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>());
  if (func && func->isWasm()) {
    uint32_t funcIndex = func->wasmFuncIndex();
    wasm::Instance& instance = func->wasmInstance();
    if (funcIndex < instance.code().funcImports().length()) {
      JS_ReportErrorASCII(cx, "argument is an imported function");
      return false;
    }
    wasm::Tier tier = instance.code().funcTier(funcIndex);
    RootedString tierString(cx, JS_NewStringCopyZ(cx, wasm::ToString(tier)));
    if (!tierString) {
      ReportOutOfMemory(cx);
      return false;
    }
    args.rval().set(StringValue(tierString));
    return true;
  }
  JS_ReportErrorASCII(cx, "argument is not an exported wasm function");
  return false;
}

static bool ToIonDumpContents(JSContext* cx, HandleValue value,
                              wasm::IonDumpContents* contents) {
  RootedString option(cx, JS::ToString(cx, value));

  if (!option) {
    return false;
  }

  bool isEqual = false;
  if (!JS_StringEqualsLiteral(cx, option, "mir", &isEqual) || isEqual) {
    *contents = wasm::IonDumpContents::UnoptimizedMIR;
    return isEqual;
  } else if (!JS_StringEqualsLiteral(cx, option, "unopt-mir", &isEqual) ||
             isEqual) {
    *contents = wasm::IonDumpContents::UnoptimizedMIR;
    return isEqual;
  } else if (!JS_StringEqualsLiteral(cx, option, "opt-mir", &isEqual) ||
             isEqual) {
    *contents = wasm::IonDumpContents::OptimizedMIR;
    return isEqual;
  } else if (!JS_StringEqualsLiteral(cx, option, "lir", &isEqual) || isEqual) {
    *contents = wasm::IonDumpContents::LIR;
    return isEqual;
  } else {
    return false;
  }
}

static bool WasmDumpIon(JSContext* cx, unsigned argc, Value* vp) {
  if (!wasm::HasSupport(cx)) {
    JS_ReportErrorASCII(cx, "wasm support unavailable");
    return false;
  }

  CallArgs args = CallArgsFromVp(argc, vp);

  args.rval().set(UndefinedValue());

  if (!args.get(0).isObject()) {
    JS_ReportErrorASCII(cx, "argument is not an object");
    return false;
  }

  uint32_t targetFuncIndex;
  if (!ToUint32(cx, args.get(1), &targetFuncIndex)) {
    JS_ReportErrorASCII(cx, "argument is not a func index");
    return false;
  }

  wasm::IonDumpContents contents = wasm::IonDumpContents::Default;
  if (args.length() > 2 && !ToIonDumpContents(cx, args.get(2), &contents)) {
    JS_ReportErrorASCII(cx, "argument is not a valid dump contents");
    return false;
  }

  SharedMem<uint8_t*> dataPointer;
  size_t byteLength;
  if (!IsBufferSource(args.get(0).toObjectOrNull(), &dataPointer,
                      &byteLength)) {
    JS_ReportErrorASCII(cx, "argument is not a buffer source");
    return false;
  }

  wasm::MutableBytes bytecode = cx->new_<wasm::ShareableBytes>();
  if (!bytecode) {
    return false;
  }
  if (!bytecode->append(dataPointer.unwrap(), byteLength)) {
    ReportOutOfMemory(cx);
    return false;
  }

  UniqueChars error;
  JSSprinter out(cx);
  if (!out.init()) {
    ReportOutOfMemory(cx);
    return false;
  }

  if (!wasm::DumpIonFunctionInModule(*bytecode, targetFuncIndex, contents, out,
                                     &error)) {
    if (error) {
      JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
                               JSMSG_WASM_COMPILE_ERROR, error.get());
      return false;
    }
    ReportOutOfMemory(cx);
    return false;
  }

  JSString* str = out.release(cx);
  if (!str) {
    ReportOutOfMemory(cx);
    return false;
  }
  args.rval().set(StringValue(str));
  return true;
}

enum class Flag { Tier2Complete, Deserialized, ParsedBranchHints };

static bool WasmReturnFlag(JSContext* cx, unsigned argc, Value* vp, Flag flag) {
  CallArgs args = CallArgsFromVp(argc, vp);

  if (!args.get(0).isObject()) {
    JS_ReportErrorASCII(cx, "argument is not an object");
    return false;
  }

  Rooted<WasmModuleObject*> module(
      cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>());
  if (!module) {
    JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
    return false;
  }

  bool b;
  switch (flag) {
    case Flag::Tier2Complete:
      b = !module->module().testingTier2Active();
      break;
    case Flag::Deserialized:
      b = module->module().loggingDeserialized();
      break;
    case Flag::ParsedBranchHints:
      b = !module->module().codeMeta().branchHints.failedParse();
      break;
  }

  args.rval().set(BooleanValue(b));
  return true;
}

static bool wasmMetadataAnalysis(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  if (!args.get(0).isObject()) {
    JS_ReportErrorASCII(cx, "argument is not an object");
    return false;
  }

  if (args[0].toObject().is<WasmModuleObject>()) {
    HashMap<const char*, uint32_t, mozilla::CStringHasher, SystemAllocPolicy>
        hashmap = args[0]
                      .toObject()
                      .as<WasmModuleObject>()
                      .module()
                      .code()
                      .metadataAnalysis(cx);
    if (hashmap.empty()) {
      JS_ReportErrorASCII(cx, "Metadata analysis has failed");
      return false;
    }

    // metadataAnalysis returned a map of {key, value} with various statistics
    // convert it into a dictionary to be used by JS
    Rooted<IdValueVector> props(cx, IdValueVector(cx));

    for (auto iter = hashmap.iter(); !iter.done(); iter.next()) {
      const auto* key = iter.get().key();
      auto value = iter.get().value();

      JSString* string = JS_NewStringCopyZ(cx, key);
      if (!string) {
        return false;
      }

      if (!props.append(
              IdValuePair(NameToId(string->asLinear().toPropertyName(cx)),
                          NumberValue(value)))) {
        return false;
      }
    }

    JSObject* results = NewPlainObjectWithUniqueNames(cx, props);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=99 G=95

¤ Dauer der Verarbeitung: 0.64 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.