Source code

Revision control

Other Tools

/* -*- 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"
#include "mozilla/Maybe.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Span.h"
#include "mozilla/Sprintf.h"
#include "mozilla/TextUtils.h"
#include "mozilla/ThreadLocal.h"
#include "mozilla/Tuple.h"
#include "mozilla/Unused.h"
#include <algorithm>
#include <cfloat>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <initializer_list>
#include <utility>
#if defined(XP_UNIX) && !defined(XP_DARWIN)
# include <time.h>
#else
# include <chrono>
#endif
#include "jsapi.h"
#include "jsfriendapi.h"
#ifdef JS_HAS_INTL_API
# include "builtin/intl/CommonFunctions.h"
#endif
#include "builtin/ModuleObject.h"
#include "builtin/Promise.h"
#include "builtin/SelfHostingDefines.h"
#ifdef DEBUG
# include "frontend/TokenStream.h"
#endif
#include "frontend/BytecodeCompilation.h"
#include "frontend/CompilationInfo.h" // frontend::CompilationInfo, frontend::CompilationInfoVector
#include "gc/Allocator.h"
#include "gc/Zone.h"
#include "jit/BaselineJIT.h"
#include "jit/Disassemble.h"
#include "jit/InlinableNatives.h"
#include "jit/Ion.h"
#include "jit/JitRuntime.h"
#include "jit/TrialInlining.h"
#include "js/Array.h" // JS::NewArrayObject
#include "js/ArrayBuffer.h" // JS::{DetachArrayBuffer,GetArrayBufferLengthAndData,NewArrayBufferWithContents}
#include "js/CharacterEncoding.h"
#include "js/CompilationAndEvaluation.h"
#include "js/CompileOptions.h"
#include "js/Date.h"
#include "js/Debug.h"
#include "js/experimental/CodeCoverage.h" // js::GetCodeCoverageSummary
#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/HashTable.h"
#include "js/LocaleSensitive.h"
#include "js/OffThreadScriptCompilation.h" // js::UseOffThreadParseGlobal
#include "js/PropertySpec.h"
#include "js/RegExpFlags.h" // JS::RegExpFlag, JS::RegExpFlags
#include "js/SourceText.h"
#include "js/StableStringChars.h"
#include "js/String.h" // JS::GetLinearStringLength, JS::StringToLinearString
#include "js/StructuredClone.h"
#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"
#ifdef JS_HAS_INTL_API
# include "unicode/ucal.h"
# include "unicode/uchar.h"
# include "unicode/uloc.h"
# include "unicode/utypes.h"
# include "unicode/uversion.h"
#endif
#include "util/StringBuffer.h"
#include "util/Text.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "vm/ErrorObject.h"
#include "vm/GlobalObject.h"
#include "vm/HelperThreadState.h"
#include "vm/Interpreter.h"
#include "vm/Iteration.h"
#include "vm/JSContext.h"
#include "vm/JSObject.h"
#include "vm/PlainObject.h" // js::PlainObject
#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseSlot_*
#include "vm/ProxyObject.h"
#include "vm/SavedStacks.h"
#include "vm/ScopeKind.h"
#include "vm/Stack.h"
#include "vm/StringType.h"
#include "vm/TraceLogging.h"
#include "wasm/AsmJS.h"
#include "wasm/WasmBaselineCompile.h"
#include "wasm/WasmCraneliftCompile.h"
#include "wasm/WasmInstance.h"
#include "wasm/WasmIonCompile.h"
#include "wasm/WasmJS.h"
#include "wasm/WasmModule.h"
#include "wasm/WasmSignalHandlers.h"
#include "wasm/WasmTypes.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/StringType-inl.h"
using namespace js;
using mozilla::ArrayLength;
using mozilla::AssertedCast;
using mozilla::AsWritableChars;
using mozilla::Maybe;
using mozilla::Span;
using mozilla::Tie;
using mozilla::Tuple;
using JS::AutoStableStringChars;
using JS::CompileOptions;
using JS::RegExpFlag;
using JS::RegExpFlags;
using JS::SourceOwnership;
using JS::SourceText;
// If fuzzingSafe is set, remove functionality that could cause problems with
// fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
mozilla::Atomic<bool> 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 info(cx, JS_NewPlainObject(cx));
if (!info) {
return false;
}
bool privateFields = cx->options().privateClassFields();
if (!JS_SetProperty(cx, info, "privateFields",
privateFields ? TrueHandleValue : FalseHandleValue)) {
return false;
}
bool privateMethods = cx->options().privateClassMethods();
if (!JS_SetProperty(cx, info, "privateMethods",
privateFields && privateMethods ? TrueHandleValue
: FalseHandleValue)) {
return false;
}
bool offThreadParseGlobal = js::UseOffThreadParseGlobal();
if (!JS_SetProperty(
cx, info, "offThreadParseGlobal",
offThreadParseGlobal ? TrueHandleValue : FalseHandleValue)) {
return false;
}
args.rval().setObject(*info);
return true;
}
static bool GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject info(cx, JS_NewPlainObject(cx));
if (!info) {
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 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 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 JS_MORE_DETERMINISTIC
value = BooleanValue(true);
#else
value = BooleanValue(false);
#endif
if (!JS_SetProperty(cx, info, "more-deterministic", 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;
}
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 IsTypeInferenceEnabled(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
// TODO(no-TI): remove testing function.
args.rval().setBoolean(false);
return true;
}
static bool TrialInline(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setUndefined();
if (!jit::JitOptions.warpBuilder) {
return true;
}
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(UncheckedUnwrap(&arg.toObject())->zone());
zone = true;
}
}
JSGCInvocationKind gckind = GC_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) {
gckind = GC_SHRINK;
} else if (last_ditch) {
gckind = GC_SHRINK;
reason = JS::GCReason::LAST_DITCH;
}
}
}
#ifndef JS_MORE_DETERMINISTIC
size_t preBytes = cx->runtime()->gc.heapSize.bytes();
#endif
if (zone) {
PrepareForDebugGC(cx->runtime());
} else {
JS::PrepareForFullGC(cx);
}
JS::NonIncrementalGC(cx, gckind, reason);
char buf[256] = {'\0'};
#ifndef JS_MORE_DETERMINISTIC
SprintfLiteral(buf, "before %zu, after %zu\n", preBytes,
cx->runtime()->gc.heapSize.bytes());
#endif
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)) {
cx->runtime()->gc.storeBuffer().setAboutToOverflow(
JS::GCReason::FULL_GENERIC_BUFFER);
}
cx->minorGC(JS::GCReason::API);
args.rval().setUndefined();
return true;
}
#define FOR_EACH_GC_PARAM(_) \
_("maxBytes", JSGC_MAX_BYTES, true) \
_("minNurseryBytes", JSGC_MIN_NURSERY_BYTES, true) \
_("maxNurseryBytes", JSGC_MAX_NURSERY_BYTES, true) \
_("gcBytes", JSGC_BYTES, false) \
_("nurseryBytes", JSGC_NURSERY_BYTES, false) \
_("gcNumber", JSGC_NUMBER, false) \
_("mode", JSGC_MODE, true) \
_("unusedChunks", JSGC_UNUSED_CHUNKS, false) \
_("totalChunks", JSGC_TOTAL_CHUNKS, false) \
_("sliceTimeBudgetMS", JSGC_SLICE_TIME_BUDGET_MS, true) \
_("markStackLimit", JSGC_MARK_STACK_LIMIT, true) \
_("highFrequencyTimeLimit", JSGC_HIGH_FREQUENCY_TIME_LIMIT, true) \
_("smallHeapSizeMax", JSGC_SMALL_HEAP_SIZE_MAX, true) \
_("largeHeapSizeMin", JSGC_LARGE_HEAP_SIZE_MIN, true) \
_("highFrequencySmallHeapGrowth", JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH, \
true) \
_("highFrequencyLargeHeapGrowth", JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH, \
true) \
_("lowFrequencyHeapGrowth", JSGC_LOW_FREQUENCY_HEAP_GROWTH, true) \
_("allocationThreshold", JSGC_ALLOCATION_THRESHOLD, true) \
_("smallHeapIncrementalLimit", JSGC_SMALL_HEAP_INCREMENTAL_LIMIT, true) \
_("largeHeapIncrementalLimit", JSGC_LARGE_HEAP_INCREMENTAL_LIMIT, true) \
_("minEmptyChunkCount", JSGC_MIN_EMPTY_CHUNK_COUNT, true) \
_("maxEmptyChunkCount", JSGC_MAX_EMPTY_CHUNK_COUNT, true) \
_("compactingEnabled", JSGC_COMPACTING_ENABLED, true) \
_("minLastDitchGCPeriod", JSGC_MIN_LAST_DITCH_GC_PERIOD, true) \
_("nurseryFreeThresholdForIdleCollection", \
JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION, true) \
_("nurseryFreeThresholdForIdleCollectionPercent", \
JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT, true) \
_("pretenureThreshold", JSGC_PRETENURE_THRESHOLD, true) \
_("pretenureGroupThreshold", JSGC_PRETENURE_GROUP_THRESHOLD, true) \
_("zoneAllocDelayKB", JSGC_ZONE_ALLOC_DELAY_KB, true) \
_("mallocThresholdBase", JSGC_MALLOC_THRESHOLD_BASE, true) \
_("mallocGrowthFactor", JSGC_MALLOC_GROWTH_FACTOR, true) \
_("chunkBytes", JSGC_CHUNK_BYTES, false) \
_("helperThreadRatio", JSGC_HELPER_THREAD_RATIO, true) \
_("maxHelperThreads", JSGC_MAX_HELPER_THREADS, true) \
_("helperThreadCount", JSGC_HELPER_THREAD_COUNT, false)
static const struct ParamInfo {
const char* name;
JSGCParamKey param;
bool writable;
} paramMap[] = {
#define DEFINE_PARAM_INFO(name, key, writable) {name, key, writable},
FOR_EACH_GC_PARAM(DEFINE_PARAM_INFO)
#undef DEFINE_PARAM_INFO
};
#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;
}
JSLinearString* linearStr = JS_EnsureLinearString(cx, str);
if (!linearStr) {
return false;
}
size_t paramIndex = 0;
for (;; paramIndex++) {
if (paramIndex == ArrayLength(paramMap)) {
JS_ReportErrorASCII(
cx, "the first argument must be one of:" GC_PARAMETER_ARGS_LIST);
return false;
}
if (JS_LinearStringEqualsAscii(linearStr, paramMap[paramIndex].name)) {
break;
}
}
const ParamInfo& info = paramMap[paramIndex];
JSGCParamKey param = info.param;
// Request mode.
if (args.length() == 1) {
uint32_t value = JS_GetGCParameter(cx, param);
args.rval().setNumber(value);
return true;
}
if (!info.writable) {
JS_ReportErrorASCII(cx, "Attempt to change read-only parameter %s",
info.name);
return false;
}
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);
if (param == JSGC_MARK_STACK_LIMIT && JS::IsIncrementalGCInProgress(cx)) {
JS_ReportErrorASCII(
cx, "attempt to set markStackLimit while a GC is in progress");
return false;
}
bool ok = cx->runtime()->gc.setParameter(param, value);
if (!ok) {
JS_ReportErrorASCII(cx, "Parameter value out of range");
return false;
}
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, GC_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(cx));
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 WasmThreadsEnabled(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setBoolean(wasm::ThreadsAvailable(cx));
return true;
}
static bool WasmReftypesEnabled(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setBoolean(wasm::ReftypesAvailable(cx));
return true;
}
static bool WasmFunctionReferencesEnabled(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setBoolean(wasm::FunctionReferencesAvailable(cx));
return true;
}
static bool WasmGcEnabled(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setBoolean(wasm::GcTypesAvailable(cx));
return true;
}
static bool WasmMultiValueEnabled(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setBoolean(wasm::MultiValuesAvailable(cx));
return true;
}
static bool WasmSimdEnabled(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setBoolean(wasm::SimdAvailable(cx));
return true;
}
static bool WasmSimdExperimentalEnabled(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
#ifdef ENABLE_WASM_SIMD_EXPERIMENTAL
args.rval().setBoolean(wasm::SimdAvailable(cx));
#else
args.rval().setBoolean(false);
#endif
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");
}
#ifdef ENABLE_WASM_CRANELIFT
if (wasm::CraneliftPlatformSupport()) {
if (*buf) {
strcat(buf, ",");
}
strcat(buf, "cranelift");
}
#else
if (wasm::IonPlatformSupport()) {
if (*buf) {
strcat(buf, ",");
}
strcat(buf, "ion");
}
#endif
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 cranelift = wasm::CraneliftAvailable(cx);
bool none = !baseline && !ion && !cranelift;
bool tiered = baseline && (ion || cranelift);
MOZ_ASSERT(!(ion && cranelift));
JSStringBuilder result(cx);
if (none && !result.append("none", 4)) {
return false;
}
if (baseline && !result.append("baseline", 8)) {
return false;
}
if (tiered && !result.append("+", 1)) {
return false;
}
if (ion && !result.append("ion", 3)) {
return false;
}
if (cranelift && !result.append("cranelift", 9)) {
return false;
}
if (JSString* str = result.finishString()) {
args.rval().setString(str);
return true;
}
return false;
}
static bool WasmCraneliftDisabledByFeatures(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
bool isDisabled = false;
JSStringBuilder reason(cx);
if (!wasm::CraneliftDisabledByFeatures(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* v) {
strncpy(lastAnalysisResult, v, 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 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.stableTier();
} else if (bestTier) {
*tier = code.bestTier();
} 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 (!cx->options().wasm()) {
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().stableTier();
;
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;
}
struct DisasmBuffer {
JSStringBuilder builder;
bool oom;
explicit DisasmBuffer(JSContext* cx) : builder(cx), oom(false) {}
};
static bool HasDisassembler(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setBoolean(jit::HasDisassembler());
return true;
}
MOZ_THREAD_LOCAL(DisasmBuffer*) disasmBuf;
static void captureDisasmText(const char* text) {
DisasmBuffer* buf = disasmBuf.get();
if (!buf->builder.append(text, strlen(text)) || !buf->builder.append('\n')) {
buf->oom = true;
}
}
static bool DisassembleNative(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setUndefined();
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;
}
Sprinter 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 (fun->isAsmJSNative() && !sprinter.jsprintf("; backend=asmjs\n")) {
return false;
}
if (!sprinter.jsprintf("; backend=wasm\n")) {
return false;
}
const Value& v2 =
fun->getExtendedSlot(FunctionExtended::WASM_INSTANCE_SLOT);
WasmInstanceObject* instobj = &v2.toObject().as<WasmInstanceObject>();
js::wasm::Instance& inst = instobj->instance();
const js::wasm::Code& code = inst.code();
js::wasm::Tier tier = code.bestTier();
const js::wasm::MetadataTier& meta = inst.metadata(tier);
const js::wasm::CodeSegment& segment = code.segment(tier);
const uint32_t funcIndex = code.getFuncIndex(&*fun);
const js::wasm::FuncExport& func = meta.lookupFuncExport(funcIndex);
const js::wasm::CodeRange& codeRange = meta.codeRange(func);
jit_begin = segment.base() + codeRange.begin();
jit_end = segment.base() + codeRange.end();
} else if (fun->hasJitScript()) {
JSScript* script = fun->nonLazyScript();
if (script == nullptr) {
return false;
}
js::jit::IonScript* ion =
script->hasIonScript() ? script->ionScript() : nullptr;
js::jit::BaselineScript* baseline =
script->hasBaselineScript() ? script->baselineScript() : nullptr;
if (ion && ion->method()) {
if (!sprinter.jsprintf("; backend=ion\n")) {
return false;
}
jit_begin = ion->method()->raw();
jit_end = ion->method()->rawEnd();
} else if (baseline) {
if (!sprinter.jsprintf("; backend=baseline\n")) {
return false;
}
jit_begin = baseline->method()->raw();
jit_end = baseline->method()->rawEnd();
}
} else {
return false;
}
if (jit_begin == nullptr || jit_end == nullptr) {
return false;
}
DisasmBuffer buf(cx);
disasmBuf.set(&buf);
auto onFinish = mozilla::MakeScopeExit([&] { disasmBuf.set(nullptr); });
jit::Disassemble(jit_begin, jit_end - jit_begin, &captureDisasmText);
if (buf.oom) {
ReportOutOfMemory(cx);
return false;
}
JSString* sresult = buf.builder.finishString();
if (!sresult) {
ReportOutOfMemory(cx);
return false;
}
sprinter.putString(sresult);
if (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);
}
JSString* str = JS_NewStringCopyZ(cx, sprinter.string());
if (!str) {
return false;
}
args[0].setUndefined();
args.rval().setString(str);
return true;
}
static bool WasmDisassemble(JSContext* cx, unsigned argc, Value* vp) {
if (!cx->options().wasm()) {
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 || !wasm::IsWasmExportedFunction(func)) {
JS_ReportErrorASCII(cx, "argument is not an exported wasm function");
return false;
}
wasm::Instance& instance = wasm::ExportedFunctionToInstance(func);
uint32_t funcIndex = wasm::ExportedFunctionToFuncIndex(func);
wasm::Tier tier = instance.code().stableTier();
if (args.length() > 1 &&
!ConvertToTier(cx, args[1], instance.code(), &tier)) {
JS_ReportErrorASCII(cx, "invalid tier");
return false;
}
if (!instance.code().hasTier(tier)) {
JS_ReportErrorASCII(cx, "function missing selected tier");
return false;
}
if (args.length() > 2 && args[2].isBoolean() && args[2].toBoolean()) {
DisasmBuffer buf(cx);
disasmBuf.set(&buf);
auto onFinish = mozilla::MakeScopeExit([&] { disasmBuf.set(nullptr); });
instance.disassembleExport(cx, funcIndex, tier, captureDisasmText);
if (buf.oom) {
ReportOutOfMemory(cx);
return false;
}
JSString* sresult = buf.builder.finishString();
if (!sresult) {
ReportOutOfMemory(cx);
return false;
}
args.rval().setString(sresult);
return true;
}
instance.disassembleExport(cx, funcIndex, tier, [](const char* text) {
fprintf(stderr, "%s\n", text);
});
return true;
}
enum class Flag { Tier2Complete, Deserialized };
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;
}
args.rval().set(BooleanValue(b));
return true;
}
static bool WasmHasTier2CompilationCompleted(JSContext* cx, unsigned argc,
Value* vp) {
return WasmReturnFlag(cx, argc, vp, Flag::Tier2Complete);
}
static bool WasmLoadedFromCache(JSContext* cx, unsigned argc, Value* vp) {
return WasmReturnFlag(cx, argc, vp, Flag::Deserialized);
}
static bool IsLazyFunction(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[0].toObject().is<JSFunction>()) {
JS_ReportErrorASCII(cx, "The first argument should be a function.");
return false;
}
JSFunction* fun = &args[0].toObject().as<JSFunction>();
args.rval().setBoolean(fun->isInterpreted() && !fun->hasBytecode());
return true;
}
static bool IsRelazifiableFunction(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[0].toObject().is<JSFunction>()) {
JS_ReportErrorASCII(cx, "The first argument should be a function.");
return false;
}
JSFunction* fun = &args[0].toObject().as<JSFunction>();
args.rval().setBoolean(fun->hasBytecode() &&
fun->nonLazyScript()->allowRelazify());
return true;
}
static bool HasSameBytecodeData(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 2) {
JS_ReportErrorASCII(cx, "The function takes exactly two argument.");
return false;
}
auto GetSharedData = [](JSContext* cx,
HandleValue v) -> SharedImmutableScriptData* {
if (!v.isObject()) {
JS_ReportErrorASCII(cx, "The arguments must be interpreted functions.");
return nullptr;
}
RootedObject obj(cx, CheckedUnwrapDynamic(&v.toObject(), cx));
if (!obj) {
return nullptr;
}
if (!obj->is<JSFunction>() || !obj->as<JSFunction>().isInterpreted()) {
JS_ReportErrorASCII(cx, "The arguments must be interpreted functions.");
return nullptr;
}
AutoRealm ar(cx, obj);
RootedFunction fun(cx, &obj->as<JSFunction>());
RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
if (!script) {
return nullptr;
}
MOZ_ASSERT(script->sharedData());
return script->sharedData();
};
// NOTE: We use RefPtr below to keep the data alive across possible GC since
// the functions may be in different Zones.
RefPtr<SharedImmutableScriptData> sharedData1 = GetSharedData(cx, args[0]);
if (!sharedData1) {
return false;
}
RefPtr<SharedImmutableScriptData> sharedData2 = GetSharedData(cx, args[1]);
if (!sharedData2) {
return false;
}
args.rval().setBoolean(sharedData1 == sharedData2);
return true;
}
static bool InternalConst(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() == 0) {
JS_ReportErrorASCII(cx, "the function takes exactly one argument");
return false;
}
JSString* str = ToString(cx, args[0]);
if (!str) {
return false;
}
JSLinearString* linear = JS_EnsureLinearString(cx, str);
if (!linear) {
return false;
}
if (JS_LinearStringEqualsLiteral(linear,
"INCREMENTAL_MARK_STACK_BASE_CAPACITY")) {
args.rval().setNumber(uint32_t(js::INCREMENTAL_MARK_STACK_BASE_CAPACITY));
} else {
JS_ReportErrorASCII(cx, "unknown const name");
return false;
}
return true;
}
static bool GCPreserveCode(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 0) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
return false;
}
cx->runtime()->gc.setAlwaysPreserveCode();
args.rval().setUndefined();
return true;
}
#ifdef JS_GC_ZEAL
static bool ParseGCZealMode(JSContext* cx, const CallArgs& args,
uint8_t* zeal) {
uint32_t value;
if (!ToUint32(cx, args.get(0), &value)) {
return false;
}
if (value > uint32_t(gc::ZealMode::Limit)) {
JS_ReportErrorASCII(cx, "gczeal argument out of range");
return false;
}
*zeal = static_cast<uint8_t>(value);
return true;
}
static bool GCZeal(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() > 2) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Too many arguments");
return false;
}
uint8_t zeal;
if (!ParseGCZealMode(cx, args, &zeal)) {
return false;
}
uint32_t frequency = JS_DEFAULT_ZEAL_FREQ;
if (args.length() >= 2) {
if (!ToUint32(cx, args.get(1), &frequency)) {
return false;
}
}
JS_SetGCZeal(cx, zeal, frequency);
args.rval().setUndefined();
return true;
}
static bool UnsetGCZeal(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() > 1) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Too many arguments");
return false;
}
uint8_t zeal;
if (!ParseGCZealMode(cx, args, &zeal)) {
return false;
}
JS_UnsetGCZeal(cx, zeal);
args.rval().setUndefined();
return true;
}
static bool ScheduleGC(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() > 1) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Too many arguments");
return false;
}
if (args.length() == 0) {
/* Fetch next zeal trigger only. */
} else if (args[0].isNumber()) {
/* Schedule a GC to happen after |arg| allocations. */
JS_ScheduleGC(cx, std::max(int(args[0].toNumber()), 0));
} else {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Bad argument - expecting number");
return false;
}
uint32_t zealBits;
uint32_t freq;
uint32_t next;
JS_GetGCZealBits(cx, &zealBits, &freq, &next);
args.rval().setInt32(next);
return true;
}
static bool SelectForGC(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
/*
* The selectedForMarking set is intended to be manually marked at slice
* start to detect missing pre-barriers. It is invalid for nursery things
* to be in the set, so evict the nursery before adding items.
*/
cx->runtime()->gc.evictNursery();
for (unsigned i = 0; i < args.length(); i++) {
if (args[i].isObject()) {
if (!cx->runtime()->gc.selectForMarking(&args[i].toObject())) {
return false;
}
}
}
args.rval().setUndefined();
return true;
}
static bool VerifyPreBarriers(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() > 0) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Too many arguments");
return false;
}
gc::VerifyBarriers(cx->runtime(), gc::PreBarrierVerifier);
args.rval().setUndefined();
return true;
}
static bool VerifyPostBarriers(JSContext* cx, unsigned argc, Value* vp) {
// This is a no-op since the post barrier verifier was removed.
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length()) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Too many arguments");
return false;
}
args.rval().setUndefined();
return true;
}
static bool CurrentGC(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 0) {
RootedObject callee(cx, &args.callee());
ReportUsageErrorASCII(cx, callee, "Too many arguments");
return false;
}
RootedObject result(cx, JS_NewPlainObject(cx));
if (!result) {
return false;
}
js::gc::GCRuntime& gc = cx->runtime()->gc;
const char* state = StateName(gc.state());
RootedString str(cx, JS_NewStringCopyZ(cx, state));
if (!str) {
return false;
}
RootedValue val(cx, StringValue(str));
if (!JS_DefineProperty(cx, result, "incrementalState", val,
JSPROP_ENUMERATE)) {
return false;
}
if (gc.state() == js::gc::State::Sweep) {
val = Int32Value(gc.getCurrentSweepGroupIndex());
if (!JS_DefineProperty(cx, result, "sweepGroup", val, JSPROP_ENUMERATE)) {
return false;
}
}
val = BooleanValue(