michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "builtin/TestingFunctions.h" michael@0: michael@0: #include "jsapi.h" michael@0: #include "jscntxt.h" michael@0: #include "jsfriendapi.h" michael@0: #include "jsgc.h" michael@0: #include "jsobj.h" michael@0: #ifndef JS_MORE_DETERMINISTIC michael@0: #include "jsprf.h" michael@0: #endif michael@0: #include "jswrapper.h" michael@0: michael@0: #include "jit/AsmJS.h" michael@0: #include "jit/AsmJSLink.h" michael@0: #include "js/StructuredClone.h" michael@0: #include "vm/ForkJoin.h" michael@0: #include "vm/GlobalObject.h" michael@0: #include "vm/Interpreter.h" michael@0: #include "vm/ProxyObject.h" michael@0: #include "vm/SavedStacks.h" michael@0: #include "vm/TraceLogging.h" michael@0: michael@0: #include "jscntxtinlines.h" michael@0: #include "jsobjinlines.h" michael@0: michael@0: using namespace js; michael@0: using namespace JS; michael@0: michael@0: using mozilla::ArrayLength; michael@0: michael@0: // If fuzzingSafe is set, remove functionality that could cause problems with michael@0: // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE. michael@0: static bool fuzzingSafe = false; michael@0: michael@0: static bool michael@0: GetBuildConfiguration(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RootedObject info(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); michael@0: if (!info) michael@0: return false; michael@0: michael@0: RootedValue value(cx, BooleanValue(false)); michael@0: if (!JS_SetProperty(cx, info, "rooting-analysis", value)) michael@0: return false; michael@0: michael@0: #ifdef JSGC_USE_EXACT_ROOTING michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "exact-rooting", value)) michael@0: return false; michael@0: michael@0: #ifdef DEBUG michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "debug", value)) michael@0: return false; michael@0: michael@0: #ifdef JS_HAS_CTYPES michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "has-ctypes", value)) michael@0: return false; michael@0: michael@0: #ifdef JS_CPU_X86 michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "x86", value)) michael@0: return false; michael@0: michael@0: #ifdef JS_CPU_X64 michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "x64", value)) michael@0: return false; michael@0: michael@0: #ifdef JS_ARM_SIMULATOR michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "arm-simulator", value)) michael@0: return false; michael@0: michael@0: #ifdef MOZ_ASAN michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "asan", value)) michael@0: return false; michael@0: michael@0: #ifdef JS_GC_ZEAL michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "has-gczeal", value)) michael@0: return false; michael@0: michael@0: #ifdef JS_THREADSAFE michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "threadsafe", value)) michael@0: return false; michael@0: michael@0: #ifdef JS_MORE_DETERMINISTIC michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "more-deterministic", value)) michael@0: return false; michael@0: michael@0: #ifdef MOZ_PROFILING michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "profiling", value)) michael@0: return false; michael@0: michael@0: #ifdef INCLUDE_MOZILLA_DTRACE michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "dtrace", value)) michael@0: return false; michael@0: michael@0: #ifdef MOZ_TRACE_JSCALLS michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "trace-jscalls-api", value)) michael@0: return false; michael@0: michael@0: #ifdef JSGC_INCREMENTAL michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "incremental-gc", value)) michael@0: return false; michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "generational-gc", value)) michael@0: return false; michael@0: michael@0: #ifdef MOZ_VALGRIND michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "valgrind", value)) michael@0: return false; michael@0: michael@0: #ifdef JS_OOM_DO_BACKTRACES michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "oom-backtraces", value)) michael@0: return false; michael@0: michael@0: #ifdef ENABLE_PARALLEL_JS michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "parallelJS", value)) michael@0: return false; michael@0: michael@0: #ifdef ENABLE_BINARYDATA michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "binary-data", value)) michael@0: return false; michael@0: michael@0: #ifdef EXPOSE_INTL_API michael@0: value = BooleanValue(true); michael@0: #else michael@0: value = BooleanValue(false); michael@0: #endif michael@0: if (!JS_SetProperty(cx, info, "intl-api", value)) michael@0: return false; michael@0: michael@0: args.rval().setObject(*info); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: GC(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: /* michael@0: * If the first argument is 'compartment', we collect any compartments michael@0: * previously scheduled for GC via schedulegc. If the first argument is an michael@0: * object, we collect the object's compartment (and any other compartments michael@0: * scheduled for GC). Otherwise, we collect all compartments. michael@0: */ michael@0: bool compartment = false; michael@0: if (args.length() == 1) { michael@0: Value arg = args[0]; michael@0: if (arg.isString()) { michael@0: if (!JS_StringEqualsAscii(cx, arg.toString(), "compartment", &compartment)) michael@0: return false; michael@0: } else if (arg.isObject()) { michael@0: PrepareZoneForGC(UncheckedUnwrap(&arg.toObject())->zone()); michael@0: compartment = true; michael@0: } michael@0: } michael@0: michael@0: #ifndef JS_MORE_DETERMINISTIC michael@0: size_t preBytes = cx->runtime()->gcBytes; michael@0: #endif michael@0: michael@0: if (compartment) michael@0: PrepareForDebugGC(cx->runtime()); michael@0: else michael@0: PrepareForFullGC(cx->runtime()); michael@0: GCForReason(cx->runtime(), gcreason::API); michael@0: michael@0: char buf[256] = { '\0' }; michael@0: #ifndef JS_MORE_DETERMINISTIC michael@0: JS_snprintf(buf, sizeof(buf), "before %lu, after %lu\n", michael@0: (unsigned long)preBytes, (unsigned long)cx->runtime()->gcBytes); michael@0: #endif michael@0: JSString *str = JS_NewStringCopyZ(cx, buf); michael@0: if (!str) michael@0: return false; michael@0: args.rval().setString(str); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: MinorGC(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: #ifdef JSGC_GENERATIONAL michael@0: if (args.get(0) == BooleanValue(true)) michael@0: cx->runtime()->gcStoreBuffer.setAboutToOverflow(); michael@0: michael@0: MinorGC(cx, gcreason::API); michael@0: #endif michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static const struct ParamPair { michael@0: const char *name; michael@0: JSGCParamKey param; michael@0: } paramMap[] = { michael@0: {"maxBytes", JSGC_MAX_BYTES }, michael@0: {"maxMallocBytes", JSGC_MAX_MALLOC_BYTES}, michael@0: {"gcBytes", JSGC_BYTES}, michael@0: {"gcNumber", JSGC_NUMBER}, michael@0: {"sliceTimeBudget", JSGC_SLICE_TIME_BUDGET}, michael@0: {"markStackLimit", JSGC_MARK_STACK_LIMIT} michael@0: }; michael@0: michael@0: // Keep this in sync with above params. michael@0: #define GC_PARAMETER_ARGS_LIST "maxBytes, maxMallocBytes, gcBytes, gcNumber, sliceTimeBudget, or markStackLimit" michael@0: michael@0: static bool michael@0: GCParameter(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: JSString *str = ToString(cx, args.get(0)); michael@0: if (!str) michael@0: return false; michael@0: michael@0: JSFlatString *flatStr = JS_FlattenString(cx, str); michael@0: if (!flatStr) michael@0: return false; michael@0: michael@0: size_t paramIndex = 0; michael@0: for (;; paramIndex++) { michael@0: if (paramIndex == ArrayLength(paramMap)) { michael@0: JS_ReportError(cx, michael@0: "the first argument must be one of " GC_PARAMETER_ARGS_LIST); michael@0: return false; michael@0: } michael@0: if (JS_FlatStringEqualsAscii(flatStr, paramMap[paramIndex].name)) michael@0: break; michael@0: } michael@0: JSGCParamKey param = paramMap[paramIndex].param; michael@0: michael@0: // Request mode. michael@0: if (args.length() == 1) { michael@0: uint32_t value = JS_GetGCParameter(cx->runtime(), param); michael@0: args.rval().setNumber(value); michael@0: return true; michael@0: } michael@0: michael@0: if (param == JSGC_NUMBER || param == JSGC_BYTES) { michael@0: JS_ReportError(cx, "Attempt to change read-only parameter %s", michael@0: paramMap[paramIndex].name); michael@0: return false; michael@0: } michael@0: michael@0: uint32_t value; michael@0: if (!ToUint32(cx, args[1], &value)) michael@0: return false; michael@0: michael@0: if (!value) { michael@0: JS_ReportError(cx, "the second argument must be convertable to uint32_t " michael@0: "with non-zero value"); michael@0: return false; michael@0: } michael@0: michael@0: if (param == JSGC_MARK_STACK_LIMIT && IsIncrementalGCInProgress(cx->runtime())) { michael@0: JS_ReportError(cx, "attempt to set markStackLimit while a GC is in progress"); michael@0: return false; michael@0: } michael@0: michael@0: if (param == JSGC_MAX_BYTES) { michael@0: uint32_t gcBytes = JS_GetGCParameter(cx->runtime(), JSGC_BYTES); michael@0: if (value < gcBytes) { michael@0: JS_ReportError(cx, michael@0: "attempt to set maxBytes to the value less than the current " michael@0: "gcBytes (%u)", michael@0: gcBytes); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: JS_SetGCParameter(cx->runtime(), param, value); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: IsProxy(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1) { michael@0: JS_ReportError(cx, "the function takes exactly one argument"); michael@0: return false; michael@0: } michael@0: if (!args[0].isObject()) { michael@0: args.rval().setBoolean(false); michael@0: return true; michael@0: } michael@0: args.rval().setBoolean(args[0].toObject().is()); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: IsLazyFunction(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1) { michael@0: JS_ReportError(cx, "The function takes exactly one argument."); michael@0: return false; michael@0: } michael@0: if (!args[0].isObject() || !args[0].toObject().is()) { michael@0: JS_ReportError(cx, "The first argument should be a function."); michael@0: return true; michael@0: } michael@0: args.rval().setBoolean(args[0].toObject().as().isInterpretedLazy()); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: IsRelazifiableFunction(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1) { michael@0: JS_ReportError(cx, "The function takes exactly one argument."); michael@0: return false; michael@0: } michael@0: if (!args[0].isObject() || michael@0: !args[0].toObject().is()) michael@0: { michael@0: JS_ReportError(cx, "The first argument should be a function."); michael@0: return true; michael@0: } michael@0: michael@0: JSFunction *fun = &args[0].toObject().as(); michael@0: args.rval().setBoolean(fun->hasScript() && fun->nonLazyScript()->isRelazifiable()); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: InternalConst(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() == 0) { michael@0: JS_ReportError(cx, "the function takes exactly one argument"); michael@0: return false; michael@0: } michael@0: michael@0: JSString *str = ToString(cx, args[0]); michael@0: if (!str) michael@0: return false; michael@0: JSFlatString *flat = JS_FlattenString(cx, str); michael@0: if (!flat) michael@0: return false; michael@0: michael@0: if (JS_FlatStringEqualsAscii(flat, "INCREMENTAL_MARK_STACK_BASE_CAPACITY")) { michael@0: args.rval().setNumber(uint32_t(js::INCREMENTAL_MARK_STACK_BASE_CAPACITY)); michael@0: } else { michael@0: JS_ReportError(cx, "unknown const name"); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: GCPreserveCode(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() != 0) { michael@0: RootedObject callee(cx, &args.callee()); michael@0: ReportUsageError(cx, callee, "Wrong number of arguments"); michael@0: return false; michael@0: } michael@0: michael@0: cx->runtime()->alwaysPreserveCode = true; michael@0: michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: #ifdef JS_GC_ZEAL michael@0: static bool michael@0: GCZeal(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() > 2) { michael@0: RootedObject callee(cx, &args.callee()); michael@0: ReportUsageError(cx, callee, "Too many arguments"); michael@0: return false; michael@0: } michael@0: michael@0: uint32_t zeal; michael@0: if (!ToUint32(cx, args.get(0), &zeal)) michael@0: return false; michael@0: michael@0: uint32_t frequency = JS_DEFAULT_ZEAL_FREQ; michael@0: if (args.length() >= 2) { michael@0: if (!ToUint32(cx, args.get(1), &frequency)) michael@0: return false; michael@0: } michael@0: michael@0: JS_SetGCZeal(cx, (uint8_t)zeal, frequency); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: ScheduleGC(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() != 1) { michael@0: RootedObject callee(cx, &args.callee()); michael@0: ReportUsageError(cx, callee, "Wrong number of arguments"); michael@0: return false; michael@0: } michael@0: michael@0: if (args[0].isInt32()) { michael@0: /* Schedule a GC to happen after |arg| allocations. */ michael@0: JS_ScheduleGC(cx, args[0].toInt32()); michael@0: } else if (args[0].isObject()) { michael@0: /* Ensure that |zone| is collected during the next GC. */ michael@0: Zone *zone = UncheckedUnwrap(&args[0].toObject())->zone(); michael@0: PrepareZoneForGC(zone); michael@0: } else if (args[0].isString()) { michael@0: /* This allows us to schedule atomsCompartment for GC. */ michael@0: PrepareZoneForGC(args[0].toString()->zone()); michael@0: } michael@0: michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: SelectForGC(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: JSRuntime *rt = cx->runtime(); michael@0: for (unsigned i = 0; i < args.length(); i++) { michael@0: if (args[i].isObject()) { michael@0: if (!rt->gcSelectedForMarking.append(&args[i].toObject())) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: VerifyPreBarriers(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() > 0) { michael@0: RootedObject callee(cx, &args.callee()); michael@0: ReportUsageError(cx, callee, "Too many arguments"); michael@0: return false; michael@0: } michael@0: michael@0: gc::VerifyBarriers(cx->runtime(), gc::PreBarrierVerifier); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: VerifyPostBarriers(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length()) { michael@0: RootedObject callee(cx, &args.callee()); michael@0: ReportUsageError(cx, callee, "Too many arguments"); michael@0: return false; michael@0: } michael@0: gc::VerifyBarriers(cx->runtime(), gc::PostBarrierVerifier); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: GCState(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() != 0) { michael@0: RootedObject callee(cx, &args.callee()); michael@0: ReportUsageError(cx, callee, "Too many arguments"); michael@0: return false; michael@0: } michael@0: michael@0: const char *state; michael@0: gc::State globalState = cx->runtime()->gcIncrementalState; michael@0: if (globalState == gc::NO_INCREMENTAL) michael@0: state = "none"; michael@0: else if (globalState == gc::MARK) michael@0: state = "mark"; michael@0: else if (globalState == gc::SWEEP) michael@0: state = "sweep"; michael@0: else michael@0: MOZ_ASSUME_UNREACHABLE("Unobserveable global GC state"); michael@0: michael@0: JSString *str = JS_NewStringCopyZ(cx, state); michael@0: if (!str) michael@0: return false; michael@0: args.rval().setString(str); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: DeterministicGC(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() != 1) { michael@0: RootedObject callee(cx, &args.callee()); michael@0: ReportUsageError(cx, callee, "Wrong number of arguments"); michael@0: return false; michael@0: } michael@0: michael@0: gc::SetDeterministicGC(cx, ToBoolean(args[0])); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: #endif /* JS_GC_ZEAL */ michael@0: michael@0: static bool michael@0: GCSlice(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() > 1) { michael@0: RootedObject callee(cx, &args.callee()); michael@0: ReportUsageError(cx, callee, "Wrong number of arguments"); michael@0: return false; michael@0: } michael@0: michael@0: bool limit = true; michael@0: uint32_t budget = 0; michael@0: if (args.length() == 1) { michael@0: if (!ToUint32(cx, args[0], &budget)) michael@0: return false; michael@0: } else { michael@0: limit = false; michael@0: } michael@0: michael@0: GCDebugSlice(cx->runtime(), limit, budget); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: ValidateGC(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() != 1) { michael@0: RootedObject callee(cx, &args.callee()); michael@0: ReportUsageError(cx, callee, "Wrong number of arguments"); michael@0: return false; michael@0: } michael@0: michael@0: gc::SetValidateGC(cx, ToBoolean(args[0])); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: FullCompartmentChecks(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() != 1) { michael@0: RootedObject callee(cx, &args.callee()); michael@0: ReportUsageError(cx, callee, "Wrong number of arguments"); michael@0: return false; michael@0: } michael@0: michael@0: gc::SetFullCompartmentChecks(cx, ToBoolean(args[0])); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: NondeterministicGetWeakMapKeys(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() != 1) { michael@0: RootedObject callee(cx, &args.callee()); michael@0: ReportUsageError(cx, callee, "Wrong number of arguments"); michael@0: return false; michael@0: } michael@0: if (!args[0].isObject()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, michael@0: "nondeterministicGetWeakMapKeys", "WeakMap", michael@0: InformalValueTypeName(args[0])); michael@0: return false; michael@0: } michael@0: RootedObject arr(cx); michael@0: RootedObject mapObj(cx, &args[0].toObject()); michael@0: if (!JS_NondeterministicGetWeakMapKeys(cx, mapObj, &arr)) michael@0: return false; michael@0: if (!arr) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, michael@0: "nondeterministicGetWeakMapKeys", "WeakMap", michael@0: args[0].toObject().getClass()->name); michael@0: return false; michael@0: } michael@0: args.rval().setObject(*arr); michael@0: return true; michael@0: } michael@0: michael@0: struct JSCountHeapNode { michael@0: void *thing; michael@0: JSGCTraceKind kind; michael@0: JSCountHeapNode *next; michael@0: }; michael@0: michael@0: typedef HashSet, SystemAllocPolicy> VisitedSet; michael@0: michael@0: class CountHeapTracer michael@0: { michael@0: public: michael@0: CountHeapTracer(JSRuntime *rt, JSTraceCallback callback) : base(rt, callback) {} michael@0: michael@0: JSTracer base; michael@0: VisitedSet visited; michael@0: JSCountHeapNode *traceList; michael@0: JSCountHeapNode *recycleList; michael@0: bool ok; michael@0: }; michael@0: michael@0: static void michael@0: CountHeapNotify(JSTracer *trc, void **thingp, JSGCTraceKind kind) michael@0: { michael@0: JS_ASSERT(trc->callback == CountHeapNotify); michael@0: michael@0: CountHeapTracer *countTracer = (CountHeapTracer *)trc; michael@0: void *thing = *thingp; michael@0: michael@0: if (!countTracer->ok) michael@0: return; michael@0: michael@0: VisitedSet::AddPtr p = countTracer->visited.lookupForAdd(thing); michael@0: if (p) michael@0: return; michael@0: michael@0: if (!countTracer->visited.add(p, thing)) { michael@0: countTracer->ok = false; michael@0: return; michael@0: } michael@0: michael@0: JSCountHeapNode *node = countTracer->recycleList; michael@0: if (node) { michael@0: countTracer->recycleList = node->next; michael@0: } else { michael@0: node = js_pod_malloc(); michael@0: if (!node) { michael@0: countTracer->ok = false; michael@0: return; michael@0: } michael@0: } michael@0: node->thing = thing; michael@0: node->kind = kind; michael@0: node->next = countTracer->traceList; michael@0: countTracer->traceList = node; michael@0: } michael@0: michael@0: static const struct TraceKindPair { michael@0: const char *name; michael@0: int32_t kind; michael@0: } traceKindNames[] = { michael@0: { "all", -1 }, michael@0: { "object", JSTRACE_OBJECT }, michael@0: { "string", JSTRACE_STRING }, michael@0: }; michael@0: michael@0: static bool michael@0: CountHeap(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: RootedValue startValue(cx, UndefinedValue()); michael@0: if (args.length() > 0) { michael@0: jsval v = args[0]; michael@0: if (v.isMarkable()) { michael@0: startValue = v; michael@0: } else if (!v.isNull()) { michael@0: JS_ReportError(cx, michael@0: "the first argument is not null or a heap-allocated " michael@0: "thing"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: RootedValue traceValue(cx); michael@0: int32_t traceKind = -1; michael@0: void *traceThing = nullptr; michael@0: if (args.length() > 1) { michael@0: JSString *str = ToString(cx, args[1]); michael@0: if (!str) michael@0: return false; michael@0: JSFlatString *flatStr = JS_FlattenString(cx, str); michael@0: if (!flatStr) michael@0: return false; michael@0: if (JS_FlatStringEqualsAscii(flatStr, "specific")) { michael@0: if (args.length() < 3) { michael@0: JS_ReportError(cx, "tracing of specific value requested " michael@0: "but no value provided"); michael@0: return false; michael@0: } michael@0: traceValue = args[2]; michael@0: if (!traceValue.isMarkable()){ michael@0: JS_ReportError(cx, "cannot trace this kind of value"); michael@0: return false; michael@0: } michael@0: traceThing = traceValue.toGCThing(); michael@0: } else { michael@0: for (size_t i = 0; ;) { michael@0: if (JS_FlatStringEqualsAscii(flatStr, traceKindNames[i].name)) { michael@0: traceKind = traceKindNames[i].kind; michael@0: break; michael@0: } michael@0: if (++i == ArrayLength(traceKindNames)) { michael@0: JSAutoByteString bytes(cx, str); michael@0: if (!!bytes) michael@0: JS_ReportError(cx, "trace kind name '%s' is unknown", bytes.ptr()); michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: CountHeapTracer countTracer(JS_GetRuntime(cx), CountHeapNotify); michael@0: if (!countTracer.visited.init()) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return false; michael@0: } michael@0: countTracer.ok = true; michael@0: countTracer.traceList = nullptr; michael@0: countTracer.recycleList = nullptr; michael@0: michael@0: if (startValue.isUndefined()) { michael@0: JS_TraceRuntime(&countTracer.base); michael@0: } else { michael@0: JS_CallValueTracer(&countTracer.base, startValue.address(), "root"); michael@0: } michael@0: michael@0: JSCountHeapNode *node; michael@0: size_t counter = 0; michael@0: while ((node = countTracer.traceList) != nullptr) { michael@0: if (traceThing == nullptr) { michael@0: // We are looking for all nodes with a specific kind michael@0: if (traceKind == -1 || node->kind == traceKind) michael@0: counter++; michael@0: } else { michael@0: // We are looking for some specific thing michael@0: if (node->thing == traceThing) michael@0: counter++; michael@0: } michael@0: countTracer.traceList = node->next; michael@0: node->next = countTracer.recycleList; michael@0: countTracer.recycleList = node; michael@0: JS_TraceChildren(&countTracer.base, node->thing, node->kind); michael@0: } michael@0: while ((node = countTracer.recycleList) != nullptr) { michael@0: countTracer.recycleList = node->next; michael@0: js_free(node); michael@0: } michael@0: if (!countTracer.ok) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return false; michael@0: } michael@0: michael@0: args.rval().setNumber(double(counter)); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: GetSavedFrameCount(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: args.rval().setNumber(cx->compartment()->savedStacks().count()); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: SaveStack(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: Rooted frame(cx); michael@0: if (!cx->compartment()->savedStacks().saveCurrentStack(cx, &frame)) michael@0: return false; michael@0: args.rval().setObject(*frame.get()); michael@0: return true; michael@0: } michael@0: michael@0: #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) michael@0: static bool michael@0: OOMAfterAllocations(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1) { michael@0: JS_ReportError(cx, "count argument required"); michael@0: return false; michael@0: } michael@0: michael@0: uint32_t count; michael@0: if (!JS::ToUint32(cx, args[0], &count)) michael@0: return false; michael@0: michael@0: OOM_maxAllocations = OOM_counter + count; michael@0: return true; michael@0: } michael@0: #endif michael@0: michael@0: static unsigned finalizeCount = 0; michael@0: michael@0: static void michael@0: finalize_counter_finalize(JSFreeOp *fop, JSObject *obj) michael@0: { michael@0: ++finalizeCount; michael@0: } michael@0: michael@0: static const JSClass FinalizeCounterClass = { michael@0: "FinalizeCounter", JSCLASS_IS_ANONYMOUS, michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: finalize_counter_finalize michael@0: }; michael@0: michael@0: static bool michael@0: MakeFinalizeObserver(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RootedObject scope(cx, JS::CurrentGlobalOrNull(cx)); michael@0: if (!scope) michael@0: return false; michael@0: michael@0: JSObject *obj = JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, JS::NullPtr(), scope); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: FinalizeCount(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: args.rval().setInt32(finalizeCount); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: DumpHeapComplete(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: DumpHeapNurseryBehaviour nurseryBehaviour = js::IgnoreNurseryObjects; michael@0: FILE *dumpFile = nullptr; michael@0: michael@0: unsigned i = 0; michael@0: if (args.length() > i) { michael@0: Value v = args[i]; michael@0: if (v.isString()) { michael@0: JSString *str = v.toString(); michael@0: bool same = false; michael@0: if (!JS_StringEqualsAscii(cx, str, "collectNurseryBeforeDump", &same)) michael@0: return false; michael@0: if (same) { michael@0: nurseryBehaviour = js::CollectNurseryBeforeDump; michael@0: ++i; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (args.length() > i) { michael@0: Value v = args[i]; michael@0: if (v.isString()) { michael@0: if (!fuzzingSafe) { michael@0: JSString *str = v.toString(); michael@0: JSAutoByteString fileNameBytes; michael@0: if (!fileNameBytes.encodeLatin1(cx, str)) michael@0: return false; michael@0: const char *fileName = fileNameBytes.ptr(); michael@0: dumpFile = fopen(fileName, "w"); michael@0: if (!dumpFile) { michael@0: JS_ReportError(cx, "can't open %s", fileName); michael@0: return false; michael@0: } michael@0: } michael@0: ++i; michael@0: } michael@0: } michael@0: michael@0: if (i != args.length()) { michael@0: JS_ReportError(cx, "bad arguments passed to dumpHeapComplete"); michael@0: return false; michael@0: } michael@0: michael@0: js::DumpHeapComplete(JS_GetRuntime(cx), dumpFile ? dumpFile : stdout, nurseryBehaviour); michael@0: michael@0: if (dumpFile) michael@0: fclose(dumpFile); michael@0: michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: Terminate(JSContext *cx, unsigned arg, jsval *vp) michael@0: { michael@0: #ifdef JS_MORE_DETERMINISTIC michael@0: // Print a message to stderr in more-deterministic builds to help jsfunfuzz michael@0: // find uncatchable-exception bugs. michael@0: fprintf(stderr, "terminate called\n"); michael@0: #endif michael@0: michael@0: JS_ClearPendingException(cx); michael@0: return false; michael@0: } michael@0: michael@0: static bool michael@0: EnableSPSProfilingAssertions(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (!args.get(0).isBoolean()) { michael@0: RootedObject arg(cx, &args.callee()); michael@0: ReportUsageError(cx, arg, "Must have one boolean argument"); michael@0: return false; michael@0: } michael@0: michael@0: static ProfileEntry stack[1000]; michael@0: static uint32_t stack_size = 0; michael@0: michael@0: // Disable before re-enabling; see the assertion in |SPSProfiler::setProfilingStack|. michael@0: if (cx->runtime()->spsProfiler.installed()) michael@0: cx->runtime()->spsProfiler.enable(false); michael@0: SetRuntimeProfilingStack(cx->runtime(), stack, &stack_size, 1000); michael@0: cx->runtime()->spsProfiler.enableSlowAssertions(args[0].toBoolean()); michael@0: cx->runtime()->spsProfiler.enable(true); michael@0: michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: DisableSPSProfiling(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: if (cx->runtime()->spsProfiler.installed()) michael@0: cx->runtime()->spsProfiler.enable(false); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: EnableOsiPointRegisterChecks(JSContext *, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: #if defined(JS_ION) && defined(CHECK_OSIPOINT_REGISTERS) michael@0: jit::js_JitOptions.checkOsiPointRegisters = true; michael@0: #endif michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: DisplayName(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (!args.get(0).isObject() || !args[0].toObject().is()) { michael@0: RootedObject arg(cx, &args.callee()); michael@0: ReportUsageError(cx, arg, "Must have one function argument"); michael@0: return false; michael@0: } michael@0: michael@0: JSFunction *fun = &args[0].toObject().as(); michael@0: JSString *str = fun->displayAtom(); michael@0: args.rval().setString(str ? str : cx->runtime()->emptyString); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::testingFunc_inParallelSection(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: // If we were actually *in* a parallel section, then this function michael@0: // would be inlined to TRUE in ion-generated code. michael@0: JS_ASSERT(!InParallelSection()); michael@0: args.rval().setBoolean(false); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: ShellObjectMetadataCallback(JSContext *cx, JSObject **pmetadata) michael@0: { michael@0: RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: RootedObject stack(cx, NewDenseEmptyArray(cx)); michael@0: if (!stack) michael@0: return false; michael@0: michael@0: static int createdIndex = 0; michael@0: createdIndex++; michael@0: michael@0: if (!JS_DefineProperty(cx, obj, "index", createdIndex, 0, michael@0: JS_PropertyStub, JS_StrictPropertyStub)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: if (!JS_DefineProperty(cx, obj, "stack", stack, 0, michael@0: JS_PropertyStub, JS_StrictPropertyStub)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: int stackIndex = 0; michael@0: for (NonBuiltinScriptFrameIter iter(cx); !iter.done(); ++iter) { michael@0: if (iter.isFunctionFrame() && iter.compartment() == cx->compartment()) { michael@0: if (!JS_DefinePropertyById(cx, stack, INT_TO_JSID(stackIndex), ObjectValue(*iter.callee()), michael@0: JS_PropertyStub, JS_StrictPropertyStub, 0)) michael@0: { michael@0: return false; michael@0: } michael@0: stackIndex++; michael@0: } michael@0: } michael@0: michael@0: *pmetadata = obj; michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: SetObjectMetadataCallback(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: bool enabled = args.length() ? ToBoolean(args[0]) : false; michael@0: SetObjectMetadataCallback(cx, enabled ? ShellObjectMetadataCallback : nullptr); michael@0: michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: SetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 2 || !args[0].isObject() || !args[1].isObject()) { michael@0: JS_ReportError(cx, "Both arguments must be objects"); michael@0: return false; michael@0: } michael@0: michael@0: args.rval().setUndefined(); michael@0: michael@0: RootedObject obj(cx, &args[0].toObject()); michael@0: RootedObject metadata(cx, &args[1].toObject()); michael@0: return SetObjectMetadata(cx, obj, metadata); michael@0: } michael@0: michael@0: static bool michael@0: GetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1 || !args[0].isObject()) { michael@0: JS_ReportError(cx, "Argument must be an object"); michael@0: return false; michael@0: } michael@0: michael@0: args.rval().setObjectOrNull(GetObjectMetadata(&args[0].toObject())); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::testingFunc_bailout(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: // NOP when not in IonMonkey michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: js::testingFunc_assertFloat32(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: // NOP when not in IonMonkey michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: SetJitCompilerOption(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RootedObject callee(cx, &args.callee()); michael@0: michael@0: if (args.length() != 2) { michael@0: ReportUsageError(cx, callee, "Wrong number of arguments."); michael@0: return false; michael@0: } michael@0: michael@0: if (!args[0].isString()) { michael@0: ReportUsageError(cx, callee, "First argument must be a String."); michael@0: return false; michael@0: } michael@0: michael@0: if (!args[1].isInt32()) { michael@0: ReportUsageError(cx, callee, "Second argument must be an Int32."); michael@0: return false; michael@0: } michael@0: michael@0: JSFlatString *strArg = JS_FlattenString(cx, args[0].toString()); michael@0: michael@0: #define JIT_COMPILER_MATCH(key, string) \ michael@0: else if (JS_FlatStringEqualsAscii(strArg, string)) \ michael@0: opt = JSJITCOMPILER_ ## key; michael@0: michael@0: JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION; michael@0: if (false) {} michael@0: JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH); michael@0: #undef JIT_COMPILER_MATCH michael@0: michael@0: if (opt == JSJITCOMPILER_NOT_AN_OPTION) { michael@0: ReportUsageError(cx, callee, "First argument does not name a valid option (see jsapi.h)."); michael@0: return false; michael@0: } michael@0: michael@0: int32_t number = args[1].toInt32(); michael@0: if (number < 0) michael@0: number = -1; michael@0: michael@0: JS_SetGlobalJitCompilerOption(cx->runtime(), opt, uint32_t(number)); michael@0: michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: GetJitCompilerOptions(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: RootedObject info(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); michael@0: if (!info) michael@0: return false; michael@0: michael@0: RootedValue value(cx); michael@0: michael@0: #define JIT_COMPILER_MATCH(key, string) \ michael@0: opt = JSJITCOMPILER_ ## key; \ michael@0: value.setInt32(JS_GetGlobalJitCompilerOption(cx->runtime(), opt)); \ michael@0: if (!JS_SetProperty(cx, info, string, value)) \ michael@0: return false; michael@0: michael@0: JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION; michael@0: JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH); michael@0: #undef JIT_COMPILER_MATCH michael@0: michael@0: args.rval().setObject(*info); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: SetIonCheckGraphCoherency(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: #ifdef JS_ION michael@0: jit::js_JitOptions.checkGraphConsistency = ToBoolean(args.get(0)); michael@0: #endif michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: class CloneBufferObject : public JSObject { michael@0: static const JSPropertySpec props_[2]; michael@0: static const size_t DATA_SLOT = 0; michael@0: static const size_t LENGTH_SLOT = 1; michael@0: static const size_t NUM_SLOTS = 2; michael@0: michael@0: public: michael@0: static const Class class_; michael@0: michael@0: static CloneBufferObject *Create(JSContext *cx) { michael@0: RootedObject obj(cx, JS_NewObject(cx, Jsvalify(&class_), JS::NullPtr(), JS::NullPtr())); michael@0: if (!obj) michael@0: return nullptr; michael@0: obj->setReservedSlot(DATA_SLOT, PrivateValue(nullptr)); michael@0: obj->setReservedSlot(LENGTH_SLOT, Int32Value(0)); michael@0: michael@0: if (!JS_DefineProperties(cx, obj, props_)) michael@0: return nullptr; michael@0: michael@0: return &obj->as(); michael@0: } michael@0: michael@0: static CloneBufferObject *Create(JSContext *cx, JSAutoStructuredCloneBuffer *buffer) { michael@0: Rooted obj(cx, Create(cx)); michael@0: if (!obj) michael@0: return nullptr; michael@0: uint64_t *datap; michael@0: size_t nbytes; michael@0: buffer->steal(&datap, &nbytes); michael@0: obj->setData(datap); michael@0: obj->setNBytes(nbytes); michael@0: return obj; michael@0: } michael@0: michael@0: uint64_t *data() const { michael@0: return static_cast(getReservedSlot(DATA_SLOT).toPrivate()); michael@0: } michael@0: michael@0: void setData(uint64_t *aData) { michael@0: JS_ASSERT(!data()); michael@0: setReservedSlot(DATA_SLOT, PrivateValue(aData)); michael@0: } michael@0: michael@0: size_t nbytes() const { michael@0: return getReservedSlot(LENGTH_SLOT).toInt32(); michael@0: } michael@0: michael@0: void setNBytes(size_t nbytes) { michael@0: JS_ASSERT(nbytes <= UINT32_MAX); michael@0: setReservedSlot(LENGTH_SLOT, Int32Value(nbytes)); michael@0: } michael@0: michael@0: // Discard an owned clone buffer. michael@0: void discard() { michael@0: if (data()) michael@0: JS_ClearStructuredClone(data(), nbytes(), nullptr, nullptr); michael@0: setReservedSlot(DATA_SLOT, PrivateValue(nullptr)); michael@0: } michael@0: michael@0: static bool michael@0: setCloneBuffer_impl(JSContext* cx, CallArgs args) { michael@0: if (args.length() != 1 || !args[0].isString()) { michael@0: JS_ReportError(cx, michael@0: "the first argument argument must be maxBytes, " michael@0: "maxMallocBytes, gcStackpoolLifespan, gcBytes or " michael@0: "gcNumber"); michael@0: JS_ReportError(cx, "clonebuffer setter requires a single string argument"); michael@0: return false; michael@0: } michael@0: michael@0: if (fuzzingSafe) { michael@0: // A manually-created clonebuffer could easily trigger a crash michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: Rooted obj(cx, &args.thisv().toObject().as()); michael@0: obj->discard(); michael@0: michael@0: char *str = JS_EncodeString(cx, args[0].toString()); michael@0: if (!str) michael@0: return false; michael@0: obj->setData(reinterpret_cast(str)); michael@0: obj->setNBytes(JS_GetStringLength(args[0].toString())); michael@0: michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: is(HandleValue v) { michael@0: return v.isObject() && v.toObject().is(); michael@0: } michael@0: michael@0: static bool michael@0: setCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: static bool michael@0: getCloneBuffer_impl(JSContext* cx, CallArgs args) { michael@0: Rooted obj(cx, &args.thisv().toObject().as()); michael@0: JS_ASSERT(args.length() == 0); michael@0: michael@0: if (!obj->data()) { michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: bool hasTransferable; michael@0: if (!JS_StructuredCloneHasTransferables(obj->data(), obj->nbytes(), &hasTransferable)) michael@0: return false; michael@0: michael@0: if (hasTransferable) { michael@0: JS_ReportError(cx, "cannot retrieve structured clone buffer with transferables"); michael@0: return false; michael@0: } michael@0: michael@0: JSString *str = JS_NewStringCopyN(cx, reinterpret_cast(obj->data()), obj->nbytes()); michael@0: if (!str) michael@0: return false; michael@0: args.rval().setString(str); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: getCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: static void Finalize(FreeOp *fop, JSObject *obj) { michael@0: obj->as().discard(); michael@0: } michael@0: }; michael@0: michael@0: const Class CloneBufferObject::class_ = { michael@0: "CloneBuffer", JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS), michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: Finalize, michael@0: nullptr, /* call */ michael@0: nullptr, /* hasInstance */ michael@0: nullptr, /* construct */ michael@0: nullptr, /* trace */ michael@0: JS_NULL_CLASS_SPEC, michael@0: JS_NULL_CLASS_EXT, michael@0: JS_NULL_OBJECT_OPS michael@0: }; michael@0: michael@0: const JSPropertySpec CloneBufferObject::props_[] = { michael@0: JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0), michael@0: JS_PS_END michael@0: }; michael@0: michael@0: static bool michael@0: Serialize(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: JSAutoStructuredCloneBuffer clonebuf; michael@0: if (!clonebuf.write(cx, args.get(0), args.get(1))) michael@0: return false; michael@0: michael@0: RootedObject obj(cx, CloneBufferObject::Create(cx, &clonebuf)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: Deserialize(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() != 1 || !args[0].isObject()) { michael@0: JS_ReportError(cx, "deserialize requires a single clonebuffer argument"); michael@0: return false; michael@0: } michael@0: michael@0: if (!args[0].toObject().is()) { michael@0: JS_ReportError(cx, "deserialize requires a clonebuffer"); michael@0: return false; michael@0: } michael@0: michael@0: Rooted obj(cx, &args[0].toObject().as()); michael@0: michael@0: // Clone buffer was already consumed? michael@0: if (!obj->data()) { michael@0: JS_ReportError(cx, "deserialize given invalid clone buffer " michael@0: "(transferables already consumed?)"); michael@0: return false; michael@0: } michael@0: michael@0: bool hasTransferable; michael@0: if (!JS_StructuredCloneHasTransferables(obj->data(), obj->nbytes(), &hasTransferable)) michael@0: return false; michael@0: michael@0: RootedValue deserialized(cx); michael@0: if (!JS_ReadStructuredClone(cx, obj->data(), obj->nbytes(), michael@0: JS_STRUCTURED_CLONE_VERSION, &deserialized, nullptr, nullptr)) { michael@0: return false; michael@0: } michael@0: args.rval().set(deserialized); michael@0: michael@0: if (hasTransferable) michael@0: obj->discard(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: Neuter(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: if (args.length() != 2) { michael@0: JS_ReportError(cx, "wrong number of arguments to neuter()"); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject obj(cx); michael@0: if (!JS_ValueToObject(cx, args[0], &obj)) michael@0: return false; michael@0: michael@0: if (!obj) { michael@0: JS_ReportError(cx, "neuter must be passed an object"); michael@0: return false; michael@0: } michael@0: michael@0: NeuterDataDisposition changeData; michael@0: RootedString str(cx, JS::ToString(cx, args[1])); michael@0: if (!str) michael@0: return false; michael@0: JSAutoByteString dataDisposition(cx, str); michael@0: if (!dataDisposition) michael@0: return false; michael@0: if (strcmp(dataDisposition.ptr(), "same-data") == 0) { michael@0: changeData = KeepData; michael@0: } else if (strcmp(dataDisposition.ptr(), "change-data") == 0) { michael@0: changeData = ChangeData; michael@0: } else { michael@0: JS_ReportError(cx, "unknown parameter 2 to neuter()"); michael@0: return false; michael@0: } michael@0: michael@0: if (!JS_NeuterArrayBuffer(cx, obj, changeData)) michael@0: return false; michael@0: michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: WorkerThreadCount(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: #ifdef JS_THREADSAFE michael@0: args.rval().setInt32(cx->runtime()->useHelperThreads() ? WorkerThreadState().threadCount : 0); michael@0: #else michael@0: args.rval().setInt32(0); michael@0: #endif michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: TimesAccessed(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: static int32_t accessed = 0; michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: args.rval().setInt32(++accessed); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: EnableTraceLogger(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); michael@0: args.rval().setBoolean(TraceLoggerEnable(logger)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: DisableTraceLogger(JSContext *cx, unsigned argc, jsval *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); michael@0: args.rval().setBoolean(TraceLoggerDisable(logger)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static const JSFunctionSpecWithHelp TestingFunctions[] = { michael@0: JS_FN_HELP("gc", ::GC, 0, 0, michael@0: "gc([obj] | 'compartment')", michael@0: " Run the garbage collector. When obj is given, GC only its compartment.\n" michael@0: " If 'compartment' is given, GC any compartments that were scheduled for\n" michael@0: " GC via schedulegc."), michael@0: michael@0: JS_FN_HELP("minorgc", ::MinorGC, 0, 0, michael@0: "minorgc([aboutToOverflow])", michael@0: " Run a minor collector on the Nursery. When aboutToOverflow is true, marks\n" michael@0: " the store buffer as about-to-overflow before collecting."), michael@0: michael@0: JS_FN_HELP("gcparam", GCParameter, 2, 0, michael@0: "gcparam(name [, value])", michael@0: " Wrapper for JS_[GS]etGCParameter. The name is one of " GC_PARAMETER_ARGS_LIST), michael@0: michael@0: JS_FN_HELP("getBuildConfiguration", GetBuildConfiguration, 0, 0, michael@0: "getBuildConfiguration()", michael@0: " Return an object describing some of the configuration options SpiderMonkey\n" michael@0: " was built with."), michael@0: michael@0: JS_FN_HELP("countHeap", CountHeap, 0, 0, michael@0: "countHeap([start[, kind[, thing]]])", michael@0: " Count the number of live GC things in the heap or things reachable from\n" michael@0: " start when it is given and is not null. kind is either 'all' (default) to\n" michael@0: " count all things or one of 'object', 'double', 'string', 'function'\n" michael@0: " to count only things of that kind. If kind is the string 'specific',\n" michael@0: " then you can provide an extra argument with some specific traceable\n" michael@0: " thing to count.\n"), michael@0: michael@0: JS_FN_HELP("getSavedFrameCount", GetSavedFrameCount, 0, 0, michael@0: "getSavedFrameCount()", michael@0: " Return the number of SavedFrame instances stored in this compartment's\n" michael@0: " SavedStacks cache."), michael@0: michael@0: JS_FN_HELP("saveStack", SaveStack, 0, 0, michael@0: "saveStack()", michael@0: " Capture a stack.\n"), michael@0: michael@0: #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) michael@0: JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 1, 0, michael@0: "oomAfterAllocations(count)", michael@0: " After 'count' js_malloc memory allocations, fail every following allocation\n" michael@0: " (return NULL)."), michael@0: #endif michael@0: michael@0: JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0, michael@0: "makeFinalizeObserver()", michael@0: " Get a special object whose finalization increases the counter returned\n" michael@0: " by the finalizeCount function."), michael@0: michael@0: JS_FN_HELP("finalizeCount", FinalizeCount, 0, 0, michael@0: "finalizeCount()", michael@0: " Return the current value of the finalization counter that is incremented\n" michael@0: " each time an object returned by the makeFinalizeObserver is finalized."), michael@0: michael@0: JS_FN_HELP("gcPreserveCode", GCPreserveCode, 0, 0, michael@0: "gcPreserveCode()", michael@0: " Preserve JIT code during garbage collections."), michael@0: michael@0: #ifdef JS_GC_ZEAL michael@0: JS_FN_HELP("gczeal", GCZeal, 2, 0, michael@0: "gczeal(level, [period])", michael@0: " Specifies how zealous the garbage collector should be. Values for level:\n" michael@0: " 0: Normal amount of collection\n" michael@0: " 1: Collect when roots are added or removed\n" michael@0: " 2: Collect when memory is allocated\n" michael@0: " 3: Collect when the window paints (browser only)\n" michael@0: " 4: Verify pre write barriers between instructions\n" michael@0: " 5: Verify pre write barriers between paints\n" michael@0: " 6: Verify stack rooting\n" michael@0: " 7: Collect the nursery every N nursery allocations\n" michael@0: " 8: Incremental GC in two slices: 1) mark roots 2) finish collection\n" michael@0: " 9: Incremental GC in two slices: 1) mark all 2) new marking and finish\n" michael@0: " 10: Incremental GC in multiple slices\n" michael@0: " 11: Verify post write barriers between instructions\n" michael@0: " 12: Verify post write barriers between paints\n" michael@0: " 13: Check internal hashtables on minor GC\n" michael@0: " Period specifies that collection happens every n allocations.\n"), michael@0: michael@0: JS_FN_HELP("schedulegc", ScheduleGC, 1, 0, michael@0: "schedulegc(num | obj)", michael@0: " If num is given, schedule a GC after num allocations.\n" michael@0: " If obj is given, schedule a GC of obj's compartment."), michael@0: michael@0: JS_FN_HELP("selectforgc", SelectForGC, 0, 0, michael@0: "selectforgc(obj1, obj2, ...)", michael@0: " Schedule the given objects to be marked in the next GC slice."), michael@0: michael@0: JS_FN_HELP("verifyprebarriers", VerifyPreBarriers, 0, 0, michael@0: "verifyprebarriers()", michael@0: " Start or end a run of the pre-write barrier verifier."), michael@0: michael@0: JS_FN_HELP("verifypostbarriers", VerifyPostBarriers, 0, 0, michael@0: "verifypostbarriers()", michael@0: " Start or end a run of the post-write barrier verifier."), michael@0: michael@0: JS_FN_HELP("gcstate", GCState, 0, 0, michael@0: "gcstate()", michael@0: " Report the global GC state."), michael@0: michael@0: JS_FN_HELP("deterministicgc", DeterministicGC, 1, 0, michael@0: "deterministicgc(true|false)", michael@0: " If true, only allow determinstic GCs to run."), michael@0: #endif michael@0: michael@0: JS_FN_HELP("gcslice", GCSlice, 1, 0, michael@0: "gcslice(n)", michael@0: " Run an incremental GC slice that marks about n objects."), michael@0: michael@0: JS_FN_HELP("validategc", ValidateGC, 1, 0, michael@0: "validategc(true|false)", michael@0: " If true, a separate validation step is performed after an incremental GC."), michael@0: michael@0: JS_FN_HELP("fullcompartmentchecks", FullCompartmentChecks, 1, 0, michael@0: "fullcompartmentchecks(true|false)", michael@0: " If true, check for compartment mismatches before every GC."), michael@0: michael@0: JS_FN_HELP("nondeterministicGetWeakMapKeys", NondeterministicGetWeakMapKeys, 1, 0, michael@0: "nondeterministicGetWeakMapKeys(weakmap)", michael@0: " Return an array of the keys in the given WeakMap."), michael@0: michael@0: JS_FN_HELP("internalConst", InternalConst, 1, 0, michael@0: "internalConst(name)", michael@0: " Query an internal constant for the engine. See InternalConst source for\n" michael@0: " the list of constant names."), michael@0: michael@0: JS_FN_HELP("isProxy", IsProxy, 1, 0, michael@0: "isProxy(obj)", michael@0: " If true, obj is a proxy of some sort"), michael@0: michael@0: JS_FN_HELP("dumpHeapComplete", DumpHeapComplete, 1, 0, michael@0: "dumpHeapComplete(['collectNurseryBeforeDump'], [filename])", michael@0: " Dump reachable and unreachable objects to the named file, or to stdout. If\n" michael@0: " 'collectNurseryBeforeDump' is specified, a minor GC is performed first,\n" michael@0: " otherwise objects in the nursery are ignored."), michael@0: michael@0: JS_FN_HELP("terminate", Terminate, 0, 0, michael@0: "terminate()", michael@0: " Terminate JavaScript execution, as if we had run out of\n" michael@0: " memory or been terminated by the slow script dialog."), michael@0: michael@0: JS_FN_HELP("enableSPSProfilingAssertions", EnableSPSProfilingAssertions, 1, 0, michael@0: "enableSPSProfilingAssertions(slow)", michael@0: " Enables SPS instrumentation and corresponding assertions. If 'slow' is\n" michael@0: " true, then even slower assertions are enabled for all generated JIT code.\n" michael@0: " When 'slow' is false, then instrumentation is enabled, but the slow\n" michael@0: " assertions are disabled."), michael@0: michael@0: JS_FN_HELP("disableSPSProfiling", DisableSPSProfiling, 1, 0, michael@0: "disableSPSProfiling()", michael@0: " Disables SPS instrumentation"), michael@0: michael@0: JS_FN_HELP("enableOsiPointRegisterChecks", EnableOsiPointRegisterChecks, 0, 0, michael@0: "enableOsiPointRegisterChecks()", michael@0: "Emit extra code to verify live regs at the start of a VM call are not\n" michael@0: "modified before its OsiPoint."), michael@0: michael@0: JS_FN_HELP("displayName", DisplayName, 1, 0, michael@0: "displayName(fn)", michael@0: " Gets the display name for a function, which can possibly be a guessed or\n" michael@0: " inferred name based on where the function was defined. This can be\n" michael@0: " different from the 'name' property on the function."), michael@0: michael@0: JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable, 0, 0, michael@0: "isAsmJSCompilationAvailable", michael@0: " Returns whether asm.js compilation is currently available or whether it is disabled\n" michael@0: " (e.g., by the debugger)."), michael@0: michael@0: JS_FN_HELP("getJitCompilerOptions", GetJitCompilerOptions, 0, 0, michael@0: "getCompilerOptions()", michael@0: "Return an object describing some of the JIT compiler options.\n"), michael@0: michael@0: JS_FN_HELP("isAsmJSModule", IsAsmJSModule, 1, 0, michael@0: "isAsmJSModule(fn)", michael@0: " Returns whether the given value is a function containing \"use asm\" that has been\n" michael@0: " validated according to the asm.js spec."), michael@0: michael@0: JS_FN_HELP("isAsmJSModuleLoadedFromCache", IsAsmJSModuleLoadedFromCache, 1, 0, michael@0: "isAsmJSModuleLoadedFromCache(fn)", michael@0: " Return whether the given asm.js module function has been loaded directly\n" michael@0: " from the cache. This function throws an error if fn is not a validated asm.js\n" michael@0: " module."), michael@0: michael@0: JS_FN_HELP("isAsmJSFunction", IsAsmJSFunction, 1, 0, michael@0: "isAsmJSFunction(fn)", michael@0: " Returns whether the given value is a nested function in an asm.js module that has been\n" michael@0: " both compile- and link-time validated."), michael@0: michael@0: JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0, michael@0: "isLazyFunction(fun)", michael@0: " True if fun is a lazy JSFunction."), michael@0: michael@0: JS_FN_HELP("isRelazifiableFunction", IsRelazifiableFunction, 1, 0, michael@0: "isRelazifiableFunction(fun)", michael@0: " Ture if fun is a JSFunction with a relazifiable JSScript."), michael@0: michael@0: JS_FN_HELP("inParallelSection", testingFunc_inParallelSection, 0, 0, michael@0: "inParallelSection()", michael@0: " True if this code is executing within a parallel section."), michael@0: michael@0: JS_FN_HELP("setObjectMetadataCallback", SetObjectMetadataCallback, 1, 0, michael@0: "setObjectMetadataCallback(fn)", michael@0: " Specify function to supply metadata for all newly created objects."), michael@0: michael@0: JS_FN_HELP("setObjectMetadata", SetObjectMetadata, 2, 0, michael@0: "setObjectMetadata(obj, metadataObj)", michael@0: " Change the metadata for an object."), michael@0: michael@0: JS_FN_HELP("getObjectMetadata", GetObjectMetadata, 1, 0, michael@0: "getObjectMetadata(obj)", michael@0: " Get the metadata for an object."), michael@0: michael@0: JS_FN_HELP("bailout", testingFunc_bailout, 0, 0, michael@0: "bailout()", michael@0: " Force a bailout out of ionmonkey (if running in ionmonkey)."), michael@0: michael@0: JS_FN_HELP("setJitCompilerOption", SetJitCompilerOption, 2, 0, michael@0: "setCompilerOption(