js/src/builtin/TestingFunctions.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "builtin/TestingFunctions.h"
     9 #include "jsapi.h"
    10 #include "jscntxt.h"
    11 #include "jsfriendapi.h"
    12 #include "jsgc.h"
    13 #include "jsobj.h"
    14 #ifndef JS_MORE_DETERMINISTIC
    15 #include "jsprf.h"
    16 #endif
    17 #include "jswrapper.h"
    19 #include "jit/AsmJS.h"
    20 #include "jit/AsmJSLink.h"
    21 #include "js/StructuredClone.h"
    22 #include "vm/ForkJoin.h"
    23 #include "vm/GlobalObject.h"
    24 #include "vm/Interpreter.h"
    25 #include "vm/ProxyObject.h"
    26 #include "vm/SavedStacks.h"
    27 #include "vm/TraceLogging.h"
    29 #include "jscntxtinlines.h"
    30 #include "jsobjinlines.h"
    32 using namespace js;
    33 using namespace JS;
    35 using mozilla::ArrayLength;
    37 // If fuzzingSafe is set, remove functionality that could cause problems with
    38 // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
    39 static bool fuzzingSafe = false;
    41 static bool
    42 GetBuildConfiguration(JSContext *cx, unsigned argc, jsval *vp)
    43 {
    44     CallArgs args = CallArgsFromVp(argc, vp);
    45     RootedObject info(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
    46     if (!info)
    47         return false;
    49     RootedValue value(cx, BooleanValue(false));
    50     if (!JS_SetProperty(cx, info, "rooting-analysis", value))
    51         return false;
    53 #ifdef JSGC_USE_EXACT_ROOTING
    54     value = BooleanValue(true);
    55 #else
    56     value = BooleanValue(false);
    57 #endif
    58     if (!JS_SetProperty(cx, info, "exact-rooting", value))
    59         return false;
    61 #ifdef DEBUG
    62     value = BooleanValue(true);
    63 #else
    64     value = BooleanValue(false);
    65 #endif
    66     if (!JS_SetProperty(cx, info, "debug", value))
    67         return false;
    69 #ifdef JS_HAS_CTYPES
    70     value = BooleanValue(true);
    71 #else
    72     value = BooleanValue(false);
    73 #endif
    74     if (!JS_SetProperty(cx, info, "has-ctypes", value))
    75         return false;
    77 #ifdef JS_CPU_X86
    78     value = BooleanValue(true);
    79 #else
    80     value = BooleanValue(false);
    81 #endif
    82     if (!JS_SetProperty(cx, info, "x86", value))
    83         return false;
    85 #ifdef JS_CPU_X64
    86     value = BooleanValue(true);
    87 #else
    88     value = BooleanValue(false);
    89 #endif
    90     if (!JS_SetProperty(cx, info, "x64", value))
    91         return false;
    93 #ifdef JS_ARM_SIMULATOR
    94     value = BooleanValue(true);
    95 #else
    96     value = BooleanValue(false);
    97 #endif
    98     if (!JS_SetProperty(cx, info, "arm-simulator", value))
    99         return false;
   101 #ifdef MOZ_ASAN
   102     value = BooleanValue(true);
   103 #else
   104     value = BooleanValue(false);
   105 #endif
   106     if (!JS_SetProperty(cx, info, "asan", value))
   107         return false;
   109 #ifdef JS_GC_ZEAL
   110     value = BooleanValue(true);
   111 #else
   112     value = BooleanValue(false);
   113 #endif
   114     if (!JS_SetProperty(cx, info, "has-gczeal", value))
   115         return false;
   117 #ifdef JS_THREADSAFE
   118     value = BooleanValue(true);
   119 #else
   120     value = BooleanValue(false);
   121 #endif
   122     if (!JS_SetProperty(cx, info, "threadsafe", value))
   123         return false;
   125 #ifdef JS_MORE_DETERMINISTIC
   126     value = BooleanValue(true);
   127 #else
   128     value = BooleanValue(false);
   129 #endif
   130     if (!JS_SetProperty(cx, info, "more-deterministic", value))
   131         return false;
   133 #ifdef MOZ_PROFILING
   134     value = BooleanValue(true);
   135 #else
   136     value = BooleanValue(false);
   137 #endif
   138     if (!JS_SetProperty(cx, info, "profiling", value))
   139         return false;
   141 #ifdef INCLUDE_MOZILLA_DTRACE
   142     value = BooleanValue(true);
   143 #else
   144     value = BooleanValue(false);
   145 #endif
   146     if (!JS_SetProperty(cx, info, "dtrace", value))
   147         return false;
   149 #ifdef MOZ_TRACE_JSCALLS
   150     value = BooleanValue(true);
   151 #else
   152     value = BooleanValue(false);
   153 #endif
   154     if (!JS_SetProperty(cx, info, "trace-jscalls-api", value))
   155         return false;
   157 #ifdef JSGC_INCREMENTAL
   158     value = BooleanValue(true);
   159 #else
   160     value = BooleanValue(false);
   161 #endif
   162     if (!JS_SetProperty(cx, info, "incremental-gc", value))
   163         return false;
   165 #ifdef JSGC_GENERATIONAL
   166     value = BooleanValue(true);
   167 #else
   168     value = BooleanValue(false);
   169 #endif
   170     if (!JS_SetProperty(cx, info, "generational-gc", value))
   171         return false;
   173 #ifdef MOZ_VALGRIND
   174     value = BooleanValue(true);
   175 #else
   176     value = BooleanValue(false);
   177 #endif
   178     if (!JS_SetProperty(cx, info, "valgrind", value))
   179         return false;
   181 #ifdef JS_OOM_DO_BACKTRACES
   182     value = BooleanValue(true);
   183 #else
   184     value = BooleanValue(false);
   185 #endif
   186     if (!JS_SetProperty(cx, info, "oom-backtraces", value))
   187         return false;
   189 #ifdef ENABLE_PARALLEL_JS
   190     value = BooleanValue(true);
   191 #else
   192     value = BooleanValue(false);
   193 #endif
   194     if (!JS_SetProperty(cx, info, "parallelJS", value))
   195         return false;
   197 #ifdef ENABLE_BINARYDATA
   198     value = BooleanValue(true);
   199 #else
   200     value = BooleanValue(false);
   201 #endif
   202     if (!JS_SetProperty(cx, info, "binary-data", value))
   203         return false;
   205 #ifdef EXPOSE_INTL_API
   206     value = BooleanValue(true);
   207 #else
   208     value = BooleanValue(false);
   209 #endif
   210     if (!JS_SetProperty(cx, info, "intl-api", value))
   211         return false;
   213     args.rval().setObject(*info);
   214     return true;
   215 }
   217 static bool
   218 GC(JSContext *cx, unsigned argc, jsval *vp)
   219 {
   220     CallArgs args = CallArgsFromVp(argc, vp);
   222     /*
   223      * If the first argument is 'compartment', we collect any compartments
   224      * previously scheduled for GC via schedulegc. If the first argument is an
   225      * object, we collect the object's compartment (and any other compartments
   226      * scheduled for GC). Otherwise, we collect all compartments.
   227      */
   228     bool compartment = false;
   229     if (args.length() == 1) {
   230         Value arg = args[0];
   231         if (arg.isString()) {
   232             if (!JS_StringEqualsAscii(cx, arg.toString(), "compartment", &compartment))
   233                 return false;
   234         } else if (arg.isObject()) {
   235             PrepareZoneForGC(UncheckedUnwrap(&arg.toObject())->zone());
   236             compartment = true;
   237         }
   238     }
   240 #ifndef JS_MORE_DETERMINISTIC
   241     size_t preBytes = cx->runtime()->gcBytes;
   242 #endif
   244     if (compartment)
   245         PrepareForDebugGC(cx->runtime());
   246     else
   247         PrepareForFullGC(cx->runtime());
   248     GCForReason(cx->runtime(), gcreason::API);
   250     char buf[256] = { '\0' };
   251 #ifndef JS_MORE_DETERMINISTIC
   252     JS_snprintf(buf, sizeof(buf), "before %lu, after %lu\n",
   253                 (unsigned long)preBytes, (unsigned long)cx->runtime()->gcBytes);
   254 #endif
   255     JSString *str = JS_NewStringCopyZ(cx, buf);
   256     if (!str)
   257         return false;
   258     args.rval().setString(str);
   259     return true;
   260 }
   262 static bool
   263 MinorGC(JSContext *cx, unsigned argc, jsval *vp)
   264 {
   265     CallArgs args = CallArgsFromVp(argc, vp);
   266 #ifdef JSGC_GENERATIONAL
   267     if (args.get(0) == BooleanValue(true))
   268         cx->runtime()->gcStoreBuffer.setAboutToOverflow();
   270     MinorGC(cx, gcreason::API);
   271 #endif
   272     args.rval().setUndefined();
   273     return true;
   274 }
   276 static const struct ParamPair {
   277     const char      *name;
   278     JSGCParamKey    param;
   279 } paramMap[] = {
   280     {"maxBytes",            JSGC_MAX_BYTES },
   281     {"maxMallocBytes",      JSGC_MAX_MALLOC_BYTES},
   282     {"gcBytes",             JSGC_BYTES},
   283     {"gcNumber",            JSGC_NUMBER},
   284     {"sliceTimeBudget",     JSGC_SLICE_TIME_BUDGET},
   285     {"markStackLimit",      JSGC_MARK_STACK_LIMIT}
   286 };
   288 // Keep this in sync with above params.
   289 #define GC_PARAMETER_ARGS_LIST "maxBytes, maxMallocBytes, gcBytes, gcNumber, sliceTimeBudget, or markStackLimit"
   291 static bool
   292 GCParameter(JSContext *cx, unsigned argc, Value *vp)
   293 {
   294     CallArgs args = CallArgsFromVp(argc, vp);
   296     JSString *str = ToString(cx, args.get(0));
   297     if (!str)
   298         return false;
   300     JSFlatString *flatStr = JS_FlattenString(cx, str);
   301     if (!flatStr)
   302         return false;
   304     size_t paramIndex = 0;
   305     for (;; paramIndex++) {
   306         if (paramIndex == ArrayLength(paramMap)) {
   307             JS_ReportError(cx,
   308                            "the first argument must be one of " GC_PARAMETER_ARGS_LIST);
   309             return false;
   310         }
   311         if (JS_FlatStringEqualsAscii(flatStr, paramMap[paramIndex].name))
   312             break;
   313     }
   314     JSGCParamKey param = paramMap[paramIndex].param;
   316     // Request mode.
   317     if (args.length() == 1) {
   318         uint32_t value = JS_GetGCParameter(cx->runtime(), param);
   319         args.rval().setNumber(value);
   320         return true;
   321     }
   323     if (param == JSGC_NUMBER || param == JSGC_BYTES) {
   324         JS_ReportError(cx, "Attempt to change read-only parameter %s",
   325                        paramMap[paramIndex].name);
   326         return false;
   327     }
   329     uint32_t value;
   330     if (!ToUint32(cx, args[1], &value))
   331         return false;
   333     if (!value) {
   334         JS_ReportError(cx, "the second argument must be convertable to uint32_t "
   335                            "with non-zero value");
   336         return false;
   337     }
   339     if (param == JSGC_MARK_STACK_LIMIT && IsIncrementalGCInProgress(cx->runtime())) {
   340         JS_ReportError(cx, "attempt to set markStackLimit while a GC is in progress");
   341         return false;
   342     }
   344     if (param == JSGC_MAX_BYTES) {
   345         uint32_t gcBytes = JS_GetGCParameter(cx->runtime(), JSGC_BYTES);
   346         if (value < gcBytes) {
   347             JS_ReportError(cx,
   348                            "attempt to set maxBytes to the value less than the current "
   349                            "gcBytes (%u)",
   350                            gcBytes);
   351             return false;
   352         }
   353     }
   355     JS_SetGCParameter(cx->runtime(), param, value);
   356     args.rval().setUndefined();
   357     return true;
   358 }
   360 static bool
   361 IsProxy(JSContext *cx, unsigned argc, Value *vp)
   362 {
   363     CallArgs args = CallArgsFromVp(argc, vp);
   364     if (args.length() != 1) {
   365         JS_ReportError(cx, "the function takes exactly one argument");
   366         return false;
   367     }
   368     if (!args[0].isObject()) {
   369         args.rval().setBoolean(false);
   370         return true;
   371     }
   372     args.rval().setBoolean(args[0].toObject().is<ProxyObject>());
   373     return true;
   374 }
   376 static bool
   377 IsLazyFunction(JSContext *cx, unsigned argc, Value *vp)
   378 {
   379     CallArgs args = CallArgsFromVp(argc, vp);
   380     if (args.length() != 1) {
   381         JS_ReportError(cx, "The function takes exactly one argument.");
   382         return false;
   383     }
   384     if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
   385         JS_ReportError(cx, "The first argument should be a function.");
   386         return true;
   387     }
   388     args.rval().setBoolean(args[0].toObject().as<JSFunction>().isInterpretedLazy());
   389     return true;
   390 }
   392 static bool
   393 IsRelazifiableFunction(JSContext *cx, unsigned argc, Value *vp)
   394 {
   395     CallArgs args = CallArgsFromVp(argc, vp);
   396     if (args.length() != 1) {
   397         JS_ReportError(cx, "The function takes exactly one argument.");
   398         return false;
   399     }
   400     if (!args[0].isObject() ||
   401         !args[0].toObject().is<JSFunction>())
   402     {
   403         JS_ReportError(cx, "The first argument should be a function.");
   404         return true;
   405     }
   407     JSFunction *fun = &args[0].toObject().as<JSFunction>();
   408     args.rval().setBoolean(fun->hasScript() && fun->nonLazyScript()->isRelazifiable());
   409     return true;
   410 }
   412 static bool
   413 InternalConst(JSContext *cx, unsigned argc, jsval *vp)
   414 {
   415     CallArgs args = CallArgsFromVp(argc, vp);
   416     if (args.length() == 0) {
   417         JS_ReportError(cx, "the function takes exactly one argument");
   418         return false;
   419     }
   421     JSString *str = ToString(cx, args[0]);
   422     if (!str)
   423         return false;
   424     JSFlatString *flat = JS_FlattenString(cx, str);
   425     if (!flat)
   426         return false;
   428     if (JS_FlatStringEqualsAscii(flat, "INCREMENTAL_MARK_STACK_BASE_CAPACITY")) {
   429         args.rval().setNumber(uint32_t(js::INCREMENTAL_MARK_STACK_BASE_CAPACITY));
   430     } else {
   431         JS_ReportError(cx, "unknown const name");
   432         return false;
   433     }
   434     return true;
   435 }
   437 static bool
   438 GCPreserveCode(JSContext *cx, unsigned argc, jsval *vp)
   439 {
   440     CallArgs args = CallArgsFromVp(argc, vp);
   442     if (args.length() != 0) {
   443         RootedObject callee(cx, &args.callee());
   444         ReportUsageError(cx, callee, "Wrong number of arguments");
   445         return false;
   446     }
   448     cx->runtime()->alwaysPreserveCode = true;
   450     args.rval().setUndefined();
   451     return true;
   452 }
   454 #ifdef JS_GC_ZEAL
   455 static bool
   456 GCZeal(JSContext *cx, unsigned argc, Value *vp)
   457 {
   458     CallArgs args = CallArgsFromVp(argc, vp);
   460     if (args.length() > 2) {
   461         RootedObject callee(cx, &args.callee());
   462         ReportUsageError(cx, callee, "Too many arguments");
   463         return false;
   464     }
   466     uint32_t zeal;
   467     if (!ToUint32(cx, args.get(0), &zeal))
   468         return false;
   470     uint32_t frequency = JS_DEFAULT_ZEAL_FREQ;
   471     if (args.length() >= 2) {
   472         if (!ToUint32(cx, args.get(1), &frequency))
   473             return false;
   474     }
   476     JS_SetGCZeal(cx, (uint8_t)zeal, frequency);
   477     args.rval().setUndefined();
   478     return true;
   479 }
   481 static bool
   482 ScheduleGC(JSContext *cx, unsigned argc, Value *vp)
   483 {
   484     CallArgs args = CallArgsFromVp(argc, vp);
   486     if (args.length() != 1) {
   487         RootedObject callee(cx, &args.callee());
   488         ReportUsageError(cx, callee, "Wrong number of arguments");
   489         return false;
   490     }
   492     if (args[0].isInt32()) {
   493         /* Schedule a GC to happen after |arg| allocations. */
   494         JS_ScheduleGC(cx, args[0].toInt32());
   495     } else if (args[0].isObject()) {
   496         /* Ensure that |zone| is collected during the next GC. */
   497         Zone *zone = UncheckedUnwrap(&args[0].toObject())->zone();
   498         PrepareZoneForGC(zone);
   499     } else if (args[0].isString()) {
   500         /* This allows us to schedule atomsCompartment for GC. */
   501         PrepareZoneForGC(args[0].toString()->zone());
   502     }
   504     args.rval().setUndefined();
   505     return true;
   506 }
   508 static bool
   509 SelectForGC(JSContext *cx, unsigned argc, Value *vp)
   510 {
   511     CallArgs args = CallArgsFromVp(argc, vp);
   513     JSRuntime *rt = cx->runtime();
   514     for (unsigned i = 0; i < args.length(); i++) {
   515         if (args[i].isObject()) {
   516             if (!rt->gcSelectedForMarking.append(&args[i].toObject()))
   517                 return false;
   518         }
   519     }
   521     args.rval().setUndefined();
   522     return true;
   523 }
   525 static bool
   526 VerifyPreBarriers(JSContext *cx, unsigned argc, jsval *vp)
   527 {
   528     CallArgs args = CallArgsFromVp(argc, vp);
   530     if (args.length() > 0) {
   531         RootedObject callee(cx, &args.callee());
   532         ReportUsageError(cx, callee, "Too many arguments");
   533         return false;
   534     }
   536     gc::VerifyBarriers(cx->runtime(), gc::PreBarrierVerifier);
   537     args.rval().setUndefined();
   538     return true;
   539 }
   541 static bool
   542 VerifyPostBarriers(JSContext *cx, unsigned argc, jsval *vp)
   543 {
   544     CallArgs args = CallArgsFromVp(argc, vp);
   545     if (args.length()) {
   546         RootedObject callee(cx, &args.callee());
   547         ReportUsageError(cx, callee, "Too many arguments");
   548         return false;
   549     }
   550     gc::VerifyBarriers(cx->runtime(), gc::PostBarrierVerifier);
   551     args.rval().setUndefined();
   552     return true;
   553 }
   555 static bool
   556 GCState(JSContext *cx, unsigned argc, jsval *vp)
   557 {
   558     CallArgs args = CallArgsFromVp(argc, vp);
   560     if (args.length() != 0) {
   561         RootedObject callee(cx, &args.callee());
   562         ReportUsageError(cx, callee, "Too many arguments");
   563         return false;
   564     }
   566     const char *state;
   567     gc::State globalState = cx->runtime()->gcIncrementalState;
   568     if (globalState == gc::NO_INCREMENTAL)
   569         state = "none";
   570     else if (globalState == gc::MARK)
   571         state = "mark";
   572     else if (globalState == gc::SWEEP)
   573         state = "sweep";
   574     else
   575         MOZ_ASSUME_UNREACHABLE("Unobserveable global GC state");
   577     JSString *str = JS_NewStringCopyZ(cx, state);
   578     if (!str)
   579         return false;
   580     args.rval().setString(str);
   581     return true;
   582 }
   584 static bool
   585 DeterministicGC(JSContext *cx, unsigned argc, jsval *vp)
   586 {
   587     CallArgs args = CallArgsFromVp(argc, vp);
   589     if (args.length() != 1) {
   590         RootedObject callee(cx, &args.callee());
   591         ReportUsageError(cx, callee, "Wrong number of arguments");
   592         return false;
   593     }
   595     gc::SetDeterministicGC(cx, ToBoolean(args[0]));
   596     args.rval().setUndefined();
   597     return true;
   598 }
   599 #endif /* JS_GC_ZEAL */
   601 static bool
   602 GCSlice(JSContext *cx, unsigned argc, Value *vp)
   603 {
   604     CallArgs args = CallArgsFromVp(argc, vp);
   606     if (args.length() > 1) {
   607         RootedObject callee(cx, &args.callee());
   608         ReportUsageError(cx, callee, "Wrong number of arguments");
   609         return false;
   610     }
   612     bool limit = true;
   613     uint32_t budget = 0;
   614     if (args.length() == 1) {
   615         if (!ToUint32(cx, args[0], &budget))
   616             return false;
   617     } else {
   618         limit = false;
   619     }
   621     GCDebugSlice(cx->runtime(), limit, budget);
   622     args.rval().setUndefined();
   623     return true;
   624 }
   626 static bool
   627 ValidateGC(JSContext *cx, unsigned argc, jsval *vp)
   628 {
   629     CallArgs args = CallArgsFromVp(argc, vp);
   631     if (args.length() != 1) {
   632         RootedObject callee(cx, &args.callee());
   633         ReportUsageError(cx, callee, "Wrong number of arguments");
   634         return false;
   635     }
   637     gc::SetValidateGC(cx, ToBoolean(args[0]));
   638     args.rval().setUndefined();
   639     return true;
   640 }
   642 static bool
   643 FullCompartmentChecks(JSContext *cx, unsigned argc, jsval *vp)
   644 {
   645     CallArgs args = CallArgsFromVp(argc, vp);
   647     if (args.length() != 1) {
   648         RootedObject callee(cx, &args.callee());
   649         ReportUsageError(cx, callee, "Wrong number of arguments");
   650         return false;
   651     }
   653     gc::SetFullCompartmentChecks(cx, ToBoolean(args[0]));
   654     args.rval().setUndefined();
   655     return true;
   656 }
   658 static bool
   659 NondeterministicGetWeakMapKeys(JSContext *cx, unsigned argc, jsval *vp)
   660 {
   661     CallArgs args = CallArgsFromVp(argc, vp);
   663     if (args.length() != 1) {
   664         RootedObject callee(cx, &args.callee());
   665         ReportUsageError(cx, callee, "Wrong number of arguments");
   666         return false;
   667     }
   668     if (!args[0].isObject()) {
   669         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
   670                              "nondeterministicGetWeakMapKeys", "WeakMap",
   671                              InformalValueTypeName(args[0]));
   672         return false;
   673     }
   674     RootedObject arr(cx);
   675     RootedObject mapObj(cx, &args[0].toObject());
   676     if (!JS_NondeterministicGetWeakMapKeys(cx, mapObj, &arr))
   677         return false;
   678     if (!arr) {
   679         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
   680                              "nondeterministicGetWeakMapKeys", "WeakMap",
   681                              args[0].toObject().getClass()->name);
   682         return false;
   683     }
   684     args.rval().setObject(*arr);
   685     return true;
   686 }
   688 struct JSCountHeapNode {
   689     void                *thing;
   690     JSGCTraceKind       kind;
   691     JSCountHeapNode     *next;
   692 };
   694 typedef HashSet<void *, PointerHasher<void *, 3>, SystemAllocPolicy> VisitedSet;
   696 class CountHeapTracer
   697 {
   698   public:
   699     CountHeapTracer(JSRuntime *rt, JSTraceCallback callback) : base(rt, callback) {}
   701     JSTracer            base;
   702     VisitedSet          visited;
   703     JSCountHeapNode     *traceList;
   704     JSCountHeapNode     *recycleList;
   705     bool                ok;
   706 };
   708 static void
   709 CountHeapNotify(JSTracer *trc, void **thingp, JSGCTraceKind kind)
   710 {
   711     JS_ASSERT(trc->callback == CountHeapNotify);
   713     CountHeapTracer *countTracer = (CountHeapTracer *)trc;
   714     void *thing = *thingp;
   716     if (!countTracer->ok)
   717         return;
   719     VisitedSet::AddPtr p = countTracer->visited.lookupForAdd(thing);
   720     if (p)
   721         return;
   723     if (!countTracer->visited.add(p, thing)) {
   724         countTracer->ok = false;
   725         return;
   726     }
   728     JSCountHeapNode *node = countTracer->recycleList;
   729     if (node) {
   730         countTracer->recycleList = node->next;
   731     } else {
   732         node = js_pod_malloc<JSCountHeapNode>();
   733         if (!node) {
   734             countTracer->ok = false;
   735             return;
   736         }
   737     }
   738     node->thing = thing;
   739     node->kind = kind;
   740     node->next = countTracer->traceList;
   741     countTracer->traceList = node;
   742 }
   744 static const struct TraceKindPair {
   745     const char       *name;
   746     int32_t           kind;
   747 } traceKindNames[] = {
   748     { "all",        -1                  },
   749     { "object",     JSTRACE_OBJECT      },
   750     { "string",     JSTRACE_STRING      },
   751 };
   753 static bool
   754 CountHeap(JSContext *cx, unsigned argc, jsval *vp)
   755 {
   756     CallArgs args = CallArgsFromVp(argc, vp);
   758     RootedValue startValue(cx, UndefinedValue());
   759     if (args.length() > 0) {
   760         jsval v = args[0];
   761         if (v.isMarkable()) {
   762             startValue = v;
   763         } else if (!v.isNull()) {
   764             JS_ReportError(cx,
   765                            "the first argument is not null or a heap-allocated "
   766                            "thing");
   767             return false;
   768         }
   769     }
   771     RootedValue traceValue(cx);
   772     int32_t traceKind = -1;
   773     void *traceThing = nullptr;
   774     if (args.length() > 1) {
   775         JSString *str = ToString(cx, args[1]);
   776         if (!str)
   777             return false;
   778         JSFlatString *flatStr = JS_FlattenString(cx, str);
   779         if (!flatStr)
   780             return false;
   781         if (JS_FlatStringEqualsAscii(flatStr, "specific")) {
   782             if (args.length() < 3) {
   783                 JS_ReportError(cx, "tracing of specific value requested "
   784                                "but no value provided");
   785                 return false;
   786             }
   787             traceValue = args[2];
   788             if (!traceValue.isMarkable()){
   789                 JS_ReportError(cx, "cannot trace this kind of value");
   790                 return false;
   791             }
   792             traceThing = traceValue.toGCThing();
   793         } else {
   794             for (size_t i = 0; ;) {
   795                 if (JS_FlatStringEqualsAscii(flatStr, traceKindNames[i].name)) {
   796                     traceKind = traceKindNames[i].kind;
   797                     break;
   798                 }
   799                 if (++i == ArrayLength(traceKindNames)) {
   800                     JSAutoByteString bytes(cx, str);
   801                     if (!!bytes)
   802                         JS_ReportError(cx, "trace kind name '%s' is unknown", bytes.ptr());
   803                     return false;
   804                 }
   805             }
   806         }
   807     }
   809     CountHeapTracer countTracer(JS_GetRuntime(cx), CountHeapNotify);
   810     if (!countTracer.visited.init()) {
   811         JS_ReportOutOfMemory(cx);
   812         return false;
   813     }
   814     countTracer.ok = true;
   815     countTracer.traceList = nullptr;
   816     countTracer.recycleList = nullptr;
   818     if (startValue.isUndefined()) {
   819         JS_TraceRuntime(&countTracer.base);
   820     } else {
   821         JS_CallValueTracer(&countTracer.base, startValue.address(), "root");
   822     }
   824     JSCountHeapNode *node;
   825     size_t counter = 0;
   826     while ((node = countTracer.traceList) != nullptr) {
   827         if (traceThing == nullptr) {
   828             // We are looking for all nodes with a specific kind
   829             if (traceKind == -1 || node->kind == traceKind)
   830                 counter++;
   831         } else {
   832             // We are looking for some specific thing
   833             if (node->thing == traceThing)
   834                 counter++;
   835         }
   836         countTracer.traceList = node->next;
   837         node->next = countTracer.recycleList;
   838         countTracer.recycleList = node;
   839         JS_TraceChildren(&countTracer.base, node->thing, node->kind);
   840     }
   841     while ((node = countTracer.recycleList) != nullptr) {
   842         countTracer.recycleList = node->next;
   843         js_free(node);
   844     }
   845     if (!countTracer.ok) {
   846         JS_ReportOutOfMemory(cx);
   847         return false;
   848     }
   850     args.rval().setNumber(double(counter));
   851     return true;
   852 }
   854 static bool
   855 GetSavedFrameCount(JSContext *cx, unsigned argc, jsval *vp)
   856 {
   857     CallArgs args = CallArgsFromVp(argc, vp);
   858     args.rval().setNumber(cx->compartment()->savedStacks().count());
   859     return true;
   860 }
   862 static bool
   863 SaveStack(JSContext *cx, unsigned argc, jsval *vp)
   864 {
   865     CallArgs args = CallArgsFromVp(argc, vp);
   866     Rooted<SavedFrame*> frame(cx);
   867     if (!cx->compartment()->savedStacks().saveCurrentStack(cx, &frame))
   868         return false;
   869     args.rval().setObject(*frame.get());
   870     return true;
   871 }
   873 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
   874 static bool
   875 OOMAfterAllocations(JSContext *cx, unsigned argc, jsval *vp)
   876 {
   877     CallArgs args = CallArgsFromVp(argc, vp);
   878     if (args.length() != 1) {
   879         JS_ReportError(cx, "count argument required");
   880         return false;
   881     }
   883     uint32_t count;
   884     if (!JS::ToUint32(cx, args[0], &count))
   885         return false;
   887     OOM_maxAllocations = OOM_counter + count;
   888     return true;
   889 }
   890 #endif
   892 static unsigned finalizeCount = 0;
   894 static void
   895 finalize_counter_finalize(JSFreeOp *fop, JSObject *obj)
   896 {
   897     ++finalizeCount;
   898 }
   900 static const JSClass FinalizeCounterClass = {
   901     "FinalizeCounter", JSCLASS_IS_ANONYMOUS,
   902     JS_PropertyStub,       /* addProperty */
   903     JS_DeletePropertyStub, /* delProperty */
   904     JS_PropertyStub,       /* getProperty */
   905     JS_StrictPropertyStub, /* setProperty */
   906     JS_EnumerateStub,
   907     JS_ResolveStub,
   908     JS_ConvertStub,
   909     finalize_counter_finalize
   910 };
   912 static bool
   913 MakeFinalizeObserver(JSContext *cx, unsigned argc, jsval *vp)
   914 {
   915     CallArgs args = CallArgsFromVp(argc, vp);
   916     RootedObject scope(cx, JS::CurrentGlobalOrNull(cx));
   917     if (!scope)
   918         return false;
   920     JSObject *obj = JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, JS::NullPtr(), scope);
   921     if (!obj)
   922         return false;
   924     args.rval().setObject(*obj);
   925     return true;
   926 }
   928 static bool
   929 FinalizeCount(JSContext *cx, unsigned argc, jsval *vp)
   930 {
   931     CallArgs args = CallArgsFromVp(argc, vp);
   932     args.rval().setInt32(finalizeCount);
   933     return true;
   934 }
   936 static bool
   937 DumpHeapComplete(JSContext *cx, unsigned argc, jsval *vp)
   938 {
   939     CallArgs args = CallArgsFromVp(argc, vp);
   941     DumpHeapNurseryBehaviour nurseryBehaviour = js::IgnoreNurseryObjects;
   942     FILE *dumpFile = nullptr;
   944     unsigned i = 0;
   945     if (args.length() > i) {
   946         Value v = args[i];
   947         if (v.isString()) {
   948             JSString *str = v.toString();
   949             bool same = false;
   950             if (!JS_StringEqualsAscii(cx, str, "collectNurseryBeforeDump", &same))
   951                 return false;
   952             if (same) {
   953                 nurseryBehaviour = js::CollectNurseryBeforeDump;
   954                 ++i;
   955             }
   956         }
   957     }
   959     if (args.length() > i) {
   960         Value v = args[i];
   961         if (v.isString()) {
   962             if (!fuzzingSafe) {
   963                 JSString *str = v.toString();
   964                 JSAutoByteString fileNameBytes;
   965                 if (!fileNameBytes.encodeLatin1(cx, str))
   966                     return false;
   967                 const char *fileName = fileNameBytes.ptr();
   968                 dumpFile = fopen(fileName, "w");
   969                 if (!dumpFile) {
   970                     JS_ReportError(cx, "can't open %s", fileName);
   971                     return false;
   972                 }
   973             }
   974             ++i;
   975         }
   976     }
   978     if (i != args.length()) {
   979         JS_ReportError(cx, "bad arguments passed to dumpHeapComplete");
   980         return false;
   981     }
   983     js::DumpHeapComplete(JS_GetRuntime(cx), dumpFile ? dumpFile : stdout, nurseryBehaviour);
   985     if (dumpFile)
   986         fclose(dumpFile);
   988     args.rval().setUndefined();
   989     return true;
   990 }
   992 static bool
   993 Terminate(JSContext *cx, unsigned arg, jsval *vp)
   994 {
   995 #ifdef JS_MORE_DETERMINISTIC
   996     // Print a message to stderr in more-deterministic builds to help jsfunfuzz
   997     // find uncatchable-exception bugs.
   998     fprintf(stderr, "terminate called\n");
   999 #endif
  1001     JS_ClearPendingException(cx);
  1002     return false;
  1005 static bool
  1006 EnableSPSProfilingAssertions(JSContext *cx, unsigned argc, jsval *vp)
  1008     CallArgs args = CallArgsFromVp(argc, vp);
  1009     if (!args.get(0).isBoolean()) {
  1010         RootedObject arg(cx, &args.callee());
  1011         ReportUsageError(cx, arg, "Must have one boolean argument");
  1012         return false;
  1015     static ProfileEntry stack[1000];
  1016     static uint32_t stack_size = 0;
  1018     // Disable before re-enabling; see the assertion in |SPSProfiler::setProfilingStack|.
  1019     if (cx->runtime()->spsProfiler.installed())
  1020         cx->runtime()->spsProfiler.enable(false);
  1021     SetRuntimeProfilingStack(cx->runtime(), stack, &stack_size, 1000);
  1022     cx->runtime()->spsProfiler.enableSlowAssertions(args[0].toBoolean());
  1023     cx->runtime()->spsProfiler.enable(true);
  1025     args.rval().setUndefined();
  1026     return true;
  1029 static bool
  1030 DisableSPSProfiling(JSContext *cx, unsigned argc, jsval *vp)
  1032     if (cx->runtime()->spsProfiler.installed())
  1033         cx->runtime()->spsProfiler.enable(false);
  1034     return true;
  1037 static bool
  1038 EnableOsiPointRegisterChecks(JSContext *, unsigned argc, jsval *vp)
  1040     CallArgs args = CallArgsFromVp(argc, vp);
  1041 #if defined(JS_ION) && defined(CHECK_OSIPOINT_REGISTERS)
  1042     jit::js_JitOptions.checkOsiPointRegisters = true;
  1043 #endif
  1044     args.rval().setUndefined();
  1045     return true;
  1048 static bool
  1049 DisplayName(JSContext *cx, unsigned argc, jsval *vp)
  1051     CallArgs args = CallArgsFromVp(argc, vp);
  1052     if (!args.get(0).isObject() || !args[0].toObject().is<JSFunction>()) {
  1053         RootedObject arg(cx, &args.callee());
  1054         ReportUsageError(cx, arg, "Must have one function argument");
  1055         return false;
  1058     JSFunction *fun = &args[0].toObject().as<JSFunction>();
  1059     JSString *str = fun->displayAtom();
  1060     args.rval().setString(str ? str : cx->runtime()->emptyString);
  1061     return true;
  1064 bool
  1065 js::testingFunc_inParallelSection(JSContext *cx, unsigned argc, jsval *vp)
  1067     CallArgs args = CallArgsFromVp(argc, vp);
  1069     // If we were actually *in* a parallel section, then this function
  1070     // would be inlined to TRUE in ion-generated code.
  1071     JS_ASSERT(!InParallelSection());
  1072     args.rval().setBoolean(false);
  1073     return true;
  1076 static bool
  1077 ShellObjectMetadataCallback(JSContext *cx, JSObject **pmetadata)
  1079     RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_));
  1080     if (!obj)
  1081         return false;
  1083     RootedObject stack(cx, NewDenseEmptyArray(cx));
  1084     if (!stack)
  1085         return false;
  1087     static int createdIndex = 0;
  1088     createdIndex++;
  1090     if (!JS_DefineProperty(cx, obj, "index", createdIndex, 0,
  1091                            JS_PropertyStub, JS_StrictPropertyStub))
  1093         return false;
  1096     if (!JS_DefineProperty(cx, obj, "stack", stack, 0,
  1097                            JS_PropertyStub, JS_StrictPropertyStub))
  1099         return false;
  1102     int stackIndex = 0;
  1103     for (NonBuiltinScriptFrameIter iter(cx); !iter.done(); ++iter) {
  1104         if (iter.isFunctionFrame() && iter.compartment() == cx->compartment()) {
  1105             if (!JS_DefinePropertyById(cx, stack, INT_TO_JSID(stackIndex), ObjectValue(*iter.callee()),
  1106                                        JS_PropertyStub, JS_StrictPropertyStub, 0))
  1108                 return false;
  1110             stackIndex++;
  1114     *pmetadata = obj;
  1115     return true;
  1118 static bool
  1119 SetObjectMetadataCallback(JSContext *cx, unsigned argc, jsval *vp)
  1121     CallArgs args = CallArgsFromVp(argc, vp);
  1123     bool enabled = args.length() ? ToBoolean(args[0]) : false;
  1124     SetObjectMetadataCallback(cx, enabled ? ShellObjectMetadataCallback : nullptr);
  1126     args.rval().setUndefined();
  1127     return true;
  1130 static bool
  1131 SetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp)
  1133     CallArgs args = CallArgsFromVp(argc, vp);
  1134     if (args.length() != 2 || !args[0].isObject() || !args[1].isObject()) {
  1135         JS_ReportError(cx, "Both arguments must be objects");
  1136         return false;
  1139     args.rval().setUndefined();
  1141     RootedObject obj(cx, &args[0].toObject());
  1142     RootedObject metadata(cx, &args[1].toObject());
  1143     return SetObjectMetadata(cx, obj, metadata);
  1146 static bool
  1147 GetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp)
  1149     CallArgs args = CallArgsFromVp(argc, vp);
  1150     if (args.length() != 1 || !args[0].isObject()) {
  1151         JS_ReportError(cx, "Argument must be an object");
  1152         return false;
  1155     args.rval().setObjectOrNull(GetObjectMetadata(&args[0].toObject()));
  1156     return true;
  1159 bool
  1160 js::testingFunc_bailout(JSContext *cx, unsigned argc, jsval *vp)
  1162     CallArgs args = CallArgsFromVp(argc, vp);
  1164     // NOP when not in IonMonkey
  1165     args.rval().setUndefined();
  1166     return true;
  1169 bool
  1170 js::testingFunc_assertFloat32(JSContext *cx, unsigned argc, jsval *vp)
  1172     CallArgs args = CallArgsFromVp(argc, vp);
  1174     // NOP when not in IonMonkey
  1175     args.rval().setUndefined();
  1176     return true;
  1179 static bool
  1180 SetJitCompilerOption(JSContext *cx, unsigned argc, jsval *vp)
  1182     CallArgs args = CallArgsFromVp(argc, vp);
  1183     RootedObject callee(cx, &args.callee());
  1185     if (args.length() != 2) {
  1186         ReportUsageError(cx, callee, "Wrong number of arguments.");
  1187         return false;
  1190     if (!args[0].isString()) {
  1191         ReportUsageError(cx, callee, "First argument must be a String.");
  1192         return false;
  1195     if (!args[1].isInt32()) {
  1196         ReportUsageError(cx, callee, "Second argument must be an Int32.");
  1197         return false;
  1200     JSFlatString *strArg = JS_FlattenString(cx, args[0].toString());
  1202 #define JIT_COMPILER_MATCH(key, string)                 \
  1203     else if (JS_FlatStringEqualsAscii(strArg, string))  \
  1204         opt = JSJITCOMPILER_ ## key;
  1206     JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION;
  1207     if (false) {}
  1208     JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH);
  1209 #undef JIT_COMPILER_MATCH
  1211     if (opt == JSJITCOMPILER_NOT_AN_OPTION) {
  1212         ReportUsageError(cx, callee, "First argument does not name a valid option (see jsapi.h).");
  1213         return false;
  1216     int32_t number = args[1].toInt32();
  1217     if (number < 0)
  1218         number = -1;
  1220     JS_SetGlobalJitCompilerOption(cx->runtime(), opt, uint32_t(number));
  1222     args.rval().setUndefined();
  1223     return true;
  1226 static bool
  1227 GetJitCompilerOptions(JSContext *cx, unsigned argc, jsval *vp)
  1229     CallArgs args = CallArgsFromVp(argc, vp);
  1230     RootedObject info(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
  1231     if (!info)
  1232         return false;
  1234     RootedValue value(cx);
  1236 #define JIT_COMPILER_MATCH(key, string)                                \
  1237     opt = JSJITCOMPILER_ ## key;                                       \
  1238     value.setInt32(JS_GetGlobalJitCompilerOption(cx->runtime(), opt)); \
  1239     if (!JS_SetProperty(cx, info, string, value))                      \
  1240         return false;
  1242     JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION;
  1243     JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH);
  1244 #undef JIT_COMPILER_MATCH
  1246     args.rval().setObject(*info);
  1248     return true;
  1251 static bool
  1252 SetIonCheckGraphCoherency(JSContext *cx, unsigned argc, jsval *vp)
  1254     CallArgs args = CallArgsFromVp(argc, vp);
  1255 #ifdef JS_ION
  1256     jit::js_JitOptions.checkGraphConsistency = ToBoolean(args.get(0));
  1257 #endif
  1258     args.rval().setUndefined();
  1259     return true;
  1262 class CloneBufferObject : public JSObject {
  1263     static const JSPropertySpec props_[2];
  1264     static const size_t DATA_SLOT   = 0;
  1265     static const size_t LENGTH_SLOT = 1;
  1266     static const size_t NUM_SLOTS   = 2;
  1268   public:
  1269     static const Class class_;
  1271     static CloneBufferObject *Create(JSContext *cx) {
  1272         RootedObject obj(cx, JS_NewObject(cx, Jsvalify(&class_), JS::NullPtr(), JS::NullPtr()));
  1273         if (!obj)
  1274             return nullptr;
  1275         obj->setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
  1276         obj->setReservedSlot(LENGTH_SLOT, Int32Value(0));
  1278         if (!JS_DefineProperties(cx, obj, props_))
  1279             return nullptr;
  1281         return &obj->as<CloneBufferObject>();
  1284     static CloneBufferObject *Create(JSContext *cx, JSAutoStructuredCloneBuffer *buffer) {
  1285         Rooted<CloneBufferObject*> obj(cx, Create(cx));
  1286         if (!obj)
  1287             return nullptr;
  1288         uint64_t *datap;
  1289         size_t nbytes;
  1290         buffer->steal(&datap, &nbytes);
  1291         obj->setData(datap);
  1292         obj->setNBytes(nbytes);
  1293         return obj;
  1296     uint64_t *data() const {
  1297         return static_cast<uint64_t*>(getReservedSlot(DATA_SLOT).toPrivate());
  1300     void setData(uint64_t *aData) {
  1301         JS_ASSERT(!data());
  1302         setReservedSlot(DATA_SLOT, PrivateValue(aData));
  1305     size_t nbytes() const {
  1306         return getReservedSlot(LENGTH_SLOT).toInt32();
  1309     void setNBytes(size_t nbytes) {
  1310         JS_ASSERT(nbytes <= UINT32_MAX);
  1311         setReservedSlot(LENGTH_SLOT, Int32Value(nbytes));
  1314     // Discard an owned clone buffer.
  1315     void discard() {
  1316         if (data())
  1317             JS_ClearStructuredClone(data(), nbytes(), nullptr, nullptr);
  1318         setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
  1321     static bool
  1322     setCloneBuffer_impl(JSContext* cx, CallArgs args) {
  1323         if (args.length() != 1 || !args[0].isString()) {
  1324             JS_ReportError(cx,
  1325                            "the first argument argument must be maxBytes, "
  1326                            "maxMallocBytes, gcStackpoolLifespan, gcBytes or "
  1327                            "gcNumber");
  1328             JS_ReportError(cx, "clonebuffer setter requires a single string argument");
  1329             return false;
  1332         if (fuzzingSafe) {
  1333             // A manually-created clonebuffer could easily trigger a crash
  1334             args.rval().setUndefined();
  1335             return true;
  1338         Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
  1339         obj->discard();
  1341         char *str = JS_EncodeString(cx, args[0].toString());
  1342         if (!str)
  1343             return false;
  1344         obj->setData(reinterpret_cast<uint64_t*>(str));
  1345         obj->setNBytes(JS_GetStringLength(args[0].toString()));
  1347         args.rval().setUndefined();
  1348         return true;
  1351     static bool
  1352     is(HandleValue v) {
  1353         return v.isObject() && v.toObject().is<CloneBufferObject>();
  1356     static bool
  1357     setCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
  1358         CallArgs args = CallArgsFromVp(argc, vp);
  1359         return CallNonGenericMethod<is, setCloneBuffer_impl>(cx, args);
  1362     static bool
  1363     getCloneBuffer_impl(JSContext* cx, CallArgs args) {
  1364         Rooted<CloneBufferObject*> obj(cx, &args.thisv().toObject().as<CloneBufferObject>());
  1365         JS_ASSERT(args.length() == 0);
  1367         if (!obj->data()) {
  1368             args.rval().setUndefined();
  1369             return true;
  1372         bool hasTransferable;
  1373         if (!JS_StructuredCloneHasTransferables(obj->data(), obj->nbytes(), &hasTransferable))
  1374             return false;
  1376         if (hasTransferable) {
  1377             JS_ReportError(cx, "cannot retrieve structured clone buffer with transferables");
  1378             return false;
  1381         JSString *str = JS_NewStringCopyN(cx, reinterpret_cast<char*>(obj->data()), obj->nbytes());
  1382         if (!str)
  1383             return false;
  1384         args.rval().setString(str);
  1385         return true;
  1388     static bool
  1389     getCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
  1390         CallArgs args = CallArgsFromVp(argc, vp);
  1391         return CallNonGenericMethod<is, getCloneBuffer_impl>(cx, args);
  1394     static void Finalize(FreeOp *fop, JSObject *obj) {
  1395         obj->as<CloneBufferObject>().discard();
  1397 };
  1399 const Class CloneBufferObject::class_ = {
  1400     "CloneBuffer", JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS),
  1401     JS_PropertyStub,       /* addProperty */
  1402     JS_DeletePropertyStub, /* delProperty */
  1403     JS_PropertyStub,       /* getProperty */
  1404     JS_StrictPropertyStub, /* setProperty */
  1405     JS_EnumerateStub,
  1406     JS_ResolveStub,
  1407     JS_ConvertStub,
  1408     Finalize,
  1409     nullptr,                  /* call */
  1410     nullptr,                  /* hasInstance */
  1411     nullptr,                  /* construct */
  1412     nullptr,                  /* trace */
  1413     JS_NULL_CLASS_SPEC,
  1414     JS_NULL_CLASS_EXT,
  1415     JS_NULL_OBJECT_OPS
  1416 };
  1418 const JSPropertySpec CloneBufferObject::props_[] = {
  1419     JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0),
  1420     JS_PS_END
  1421 };
  1423 static bool
  1424 Serialize(JSContext *cx, unsigned argc, jsval *vp)
  1426     CallArgs args = CallArgsFromVp(argc, vp);
  1428     JSAutoStructuredCloneBuffer clonebuf;
  1429     if (!clonebuf.write(cx, args.get(0), args.get(1)))
  1430         return false;
  1432     RootedObject obj(cx, CloneBufferObject::Create(cx, &clonebuf));
  1433     if (!obj)
  1434         return false;
  1436     args.rval().setObject(*obj);
  1437     return true;
  1440 static bool
  1441 Deserialize(JSContext *cx, unsigned argc, jsval *vp)
  1443     CallArgs args = CallArgsFromVp(argc, vp);
  1445     if (args.length() != 1 || !args[0].isObject()) {
  1446         JS_ReportError(cx, "deserialize requires a single clonebuffer argument");
  1447         return false;
  1450     if (!args[0].toObject().is<CloneBufferObject>()) {
  1451         JS_ReportError(cx, "deserialize requires a clonebuffer");
  1452         return false;
  1455     Rooted<CloneBufferObject*> obj(cx, &args[0].toObject().as<CloneBufferObject>());
  1457     // Clone buffer was already consumed?
  1458     if (!obj->data()) {
  1459         JS_ReportError(cx, "deserialize given invalid clone buffer "
  1460                        "(transferables already consumed?)");
  1461         return false;
  1464     bool hasTransferable;
  1465     if (!JS_StructuredCloneHasTransferables(obj->data(), obj->nbytes(), &hasTransferable))
  1466         return false;
  1468     RootedValue deserialized(cx);
  1469     if (!JS_ReadStructuredClone(cx, obj->data(), obj->nbytes(),
  1470                                 JS_STRUCTURED_CLONE_VERSION, &deserialized, nullptr, nullptr)) {
  1471         return false;
  1473     args.rval().set(deserialized);
  1475     if (hasTransferable)
  1476         obj->discard();
  1478     return true;
  1481 static bool
  1482 Neuter(JSContext *cx, unsigned argc, jsval *vp)
  1484     CallArgs args = CallArgsFromVp(argc, vp);
  1486     if (args.length() != 2) {
  1487         JS_ReportError(cx, "wrong number of arguments to neuter()");
  1488         return false;
  1491     RootedObject obj(cx);
  1492     if (!JS_ValueToObject(cx, args[0], &obj))
  1493         return false;
  1495     if (!obj) {
  1496         JS_ReportError(cx, "neuter must be passed an object");
  1497         return false;
  1500     NeuterDataDisposition changeData;
  1501     RootedString str(cx, JS::ToString(cx, args[1]));
  1502     if (!str)
  1503         return false;
  1504     JSAutoByteString dataDisposition(cx, str);
  1505     if (!dataDisposition)
  1506         return false;
  1507     if (strcmp(dataDisposition.ptr(), "same-data") == 0) {
  1508         changeData = KeepData;
  1509     } else if (strcmp(dataDisposition.ptr(), "change-data") == 0) {
  1510         changeData = ChangeData;
  1511     } else {
  1512         JS_ReportError(cx, "unknown parameter 2 to neuter()");
  1513         return false;
  1516     if (!JS_NeuterArrayBuffer(cx, obj, changeData))
  1517         return false;
  1519     args.rval().setUndefined();
  1520     return true;
  1523 static bool
  1524 WorkerThreadCount(JSContext *cx, unsigned argc, jsval *vp)
  1526     CallArgs args = CallArgsFromVp(argc, vp);
  1527 #ifdef JS_THREADSAFE
  1528     args.rval().setInt32(cx->runtime()->useHelperThreads() ? WorkerThreadState().threadCount : 0);
  1529 #else
  1530     args.rval().setInt32(0);
  1531 #endif
  1532     return true;
  1535 static bool
  1536 TimesAccessed(JSContext *cx, unsigned argc, jsval *vp)
  1538     static int32_t accessed = 0;
  1539     CallArgs args = CallArgsFromVp(argc, vp);
  1540     args.rval().setInt32(++accessed);
  1541     return true;
  1544 static bool
  1545 EnableTraceLogger(JSContext *cx, unsigned argc, jsval *vp)
  1547     CallArgs args = CallArgsFromVp(argc, vp);
  1548     TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
  1549     args.rval().setBoolean(TraceLoggerEnable(logger));
  1551     return true;
  1554 static bool
  1555 DisableTraceLogger(JSContext *cx, unsigned argc, jsval *vp)
  1557     CallArgs args = CallArgsFromVp(argc, vp);
  1558     TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
  1559     args.rval().setBoolean(TraceLoggerDisable(logger));
  1561     return true;
  1564 static const JSFunctionSpecWithHelp TestingFunctions[] = {
  1565     JS_FN_HELP("gc", ::GC, 0, 0,
  1566 "gc([obj] | 'compartment')",
  1567 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
  1568 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
  1569 "  GC via schedulegc."),
  1571     JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
  1572 "minorgc([aboutToOverflow])",
  1573 "  Run a minor collector on the Nursery. When aboutToOverflow is true, marks\n"
  1574 "  the store buffer as about-to-overflow before collecting."),
  1576     JS_FN_HELP("gcparam", GCParameter, 2, 0,
  1577 "gcparam(name [, value])",
  1578 "  Wrapper for JS_[GS]etGCParameter. The name is one of " GC_PARAMETER_ARGS_LIST),
  1580     JS_FN_HELP("getBuildConfiguration", GetBuildConfiguration, 0, 0,
  1581 "getBuildConfiguration()",
  1582 "  Return an object describing some of the configuration options SpiderMonkey\n"
  1583 "  was built with."),
  1585     JS_FN_HELP("countHeap", CountHeap, 0, 0,
  1586 "countHeap([start[, kind[, thing]]])",
  1587 "  Count the number of live GC things in the heap or things reachable from\n"
  1588 "  start when it is given and is not null. kind is either 'all' (default) to\n"
  1589 "  count all things or one of 'object', 'double', 'string', 'function'\n"
  1590 "  to count only things of that kind. If kind is the string 'specific',\n"
  1591 "  then you can provide an extra argument with some specific traceable\n"
  1592 "  thing to count.\n"),
  1594     JS_FN_HELP("getSavedFrameCount", GetSavedFrameCount, 0, 0,
  1595 "getSavedFrameCount()",
  1596 "  Return the number of SavedFrame instances stored in this compartment's\n"
  1597 "  SavedStacks cache."),
  1599     JS_FN_HELP("saveStack", SaveStack, 0, 0,
  1600 "saveStack()",
  1601 "  Capture a stack.\n"),
  1603 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
  1604     JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 1, 0,
  1605 "oomAfterAllocations(count)",
  1606 "  After 'count' js_malloc memory allocations, fail every following allocation\n"
  1607 "  (return NULL)."),
  1608 #endif
  1610     JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
  1611 "makeFinalizeObserver()",
  1612 "  Get a special object whose finalization increases the counter returned\n"
  1613 "  by the finalizeCount function."),
  1615     JS_FN_HELP("finalizeCount", FinalizeCount, 0, 0,
  1616 "finalizeCount()",
  1617 "  Return the current value of the finalization counter that is incremented\n"
  1618 "  each time an object returned by the makeFinalizeObserver is finalized."),
  1620     JS_FN_HELP("gcPreserveCode", GCPreserveCode, 0, 0,
  1621 "gcPreserveCode()",
  1622 "  Preserve JIT code during garbage collections."),
  1624 #ifdef JS_GC_ZEAL
  1625     JS_FN_HELP("gczeal", GCZeal, 2, 0,
  1626 "gczeal(level, [period])",
  1627 "  Specifies how zealous the garbage collector should be. Values for level:\n"
  1628 "    0: Normal amount of collection\n"
  1629 "    1: Collect when roots are added or removed\n"
  1630 "    2: Collect when memory is allocated\n"
  1631 "    3: Collect when the window paints (browser only)\n"
  1632 "    4: Verify pre write barriers between instructions\n"
  1633 "    5: Verify pre write barriers between paints\n"
  1634 "    6: Verify stack rooting\n"
  1635 "    7: Collect the nursery every N nursery allocations\n"
  1636 "    8: Incremental GC in two slices: 1) mark roots 2) finish collection\n"
  1637 "    9: Incremental GC in two slices: 1) mark all 2) new marking and finish\n"
  1638 "   10: Incremental GC in multiple slices\n"
  1639 "   11: Verify post write barriers between instructions\n"
  1640 "   12: Verify post write barriers between paints\n"
  1641 "   13: Check internal hashtables on minor GC\n"
  1642 "  Period specifies that collection happens every n allocations.\n"),
  1644     JS_FN_HELP("schedulegc", ScheduleGC, 1, 0,
  1645 "schedulegc(num | obj)",
  1646 "  If num is given, schedule a GC after num allocations.\n"
  1647 "  If obj is given, schedule a GC of obj's compartment."),
  1649     JS_FN_HELP("selectforgc", SelectForGC, 0, 0,
  1650 "selectforgc(obj1, obj2, ...)",
  1651 "  Schedule the given objects to be marked in the next GC slice."),
  1653     JS_FN_HELP("verifyprebarriers", VerifyPreBarriers, 0, 0,
  1654 "verifyprebarriers()",
  1655 "  Start or end a run of the pre-write barrier verifier."),
  1657     JS_FN_HELP("verifypostbarriers", VerifyPostBarriers, 0, 0,
  1658 "verifypostbarriers()",
  1659 "  Start or end a run of the post-write barrier verifier."),
  1661     JS_FN_HELP("gcstate", GCState, 0, 0,
  1662 "gcstate()",
  1663 "  Report the global GC state."),
  1665     JS_FN_HELP("deterministicgc", DeterministicGC, 1, 0,
  1666 "deterministicgc(true|false)",
  1667 "  If true, only allow determinstic GCs to run."),
  1668 #endif
  1670     JS_FN_HELP("gcslice", GCSlice, 1, 0,
  1671 "gcslice(n)",
  1672 "  Run an incremental GC slice that marks about n objects."),
  1674     JS_FN_HELP("validategc", ValidateGC, 1, 0,
  1675 "validategc(true|false)",
  1676 "  If true, a separate validation step is performed after an incremental GC."),
  1678     JS_FN_HELP("fullcompartmentchecks", FullCompartmentChecks, 1, 0,
  1679 "fullcompartmentchecks(true|false)",
  1680 "  If true, check for compartment mismatches before every GC."),
  1682     JS_FN_HELP("nondeterministicGetWeakMapKeys", NondeterministicGetWeakMapKeys, 1, 0,
  1683 "nondeterministicGetWeakMapKeys(weakmap)",
  1684 "  Return an array of the keys in the given WeakMap."),
  1686     JS_FN_HELP("internalConst", InternalConst, 1, 0,
  1687 "internalConst(name)",
  1688 "  Query an internal constant for the engine. See InternalConst source for\n"
  1689 "  the list of constant names."),
  1691     JS_FN_HELP("isProxy", IsProxy, 1, 0,
  1692 "isProxy(obj)",
  1693 "  If true, obj is a proxy of some sort"),
  1695     JS_FN_HELP("dumpHeapComplete", DumpHeapComplete, 1, 0,
  1696 "dumpHeapComplete(['collectNurseryBeforeDump'], [filename])",
  1697 "  Dump reachable and unreachable objects to the named file, or to stdout.  If\n"
  1698 "  'collectNurseryBeforeDump' is specified, a minor GC is performed first,\n"
  1699 "  otherwise objects in the nursery are ignored."),
  1701     JS_FN_HELP("terminate", Terminate, 0, 0,
  1702 "terminate()",
  1703 "  Terminate JavaScript execution, as if we had run out of\n"
  1704 "  memory or been terminated by the slow script dialog."),
  1706     JS_FN_HELP("enableSPSProfilingAssertions", EnableSPSProfilingAssertions, 1, 0,
  1707 "enableSPSProfilingAssertions(slow)",
  1708 "  Enables SPS instrumentation and corresponding assertions. If 'slow' is\n"
  1709 "  true, then even slower assertions are enabled for all generated JIT code.\n"
  1710 "  When 'slow' is false, then instrumentation is enabled, but the slow\n"
  1711 "  assertions are disabled."),
  1713     JS_FN_HELP("disableSPSProfiling", DisableSPSProfiling, 1, 0,
  1714 "disableSPSProfiling()",
  1715 "  Disables SPS instrumentation"),
  1717     JS_FN_HELP("enableOsiPointRegisterChecks", EnableOsiPointRegisterChecks, 0, 0,
  1718 "enableOsiPointRegisterChecks()",
  1719 "Emit extra code to verify live regs at the start of a VM call are not\n"
  1720 "modified before its OsiPoint."),
  1722     JS_FN_HELP("displayName", DisplayName, 1, 0,
  1723 "displayName(fn)",
  1724 "  Gets the display name for a function, which can possibly be a guessed or\n"
  1725 "  inferred name based on where the function was defined. This can be\n"
  1726 "  different from the 'name' property on the function."),
  1728     JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable, 0, 0,
  1729 "isAsmJSCompilationAvailable",
  1730 "  Returns whether asm.js compilation is currently available or whether it is disabled\n"
  1731 "  (e.g., by the debugger)."),
  1733     JS_FN_HELP("getJitCompilerOptions", GetJitCompilerOptions, 0, 0,
  1734 "getCompilerOptions()",
  1735 "Return an object describing some of the JIT compiler options.\n"),
  1737     JS_FN_HELP("isAsmJSModule", IsAsmJSModule, 1, 0,
  1738 "isAsmJSModule(fn)",
  1739 "  Returns whether the given value is a function containing \"use asm\" that has been\n"
  1740 "  validated according to the asm.js spec."),
  1742     JS_FN_HELP("isAsmJSModuleLoadedFromCache", IsAsmJSModuleLoadedFromCache, 1, 0,
  1743 "isAsmJSModuleLoadedFromCache(fn)",
  1744 "  Return whether the given asm.js module function has been loaded directly\n"
  1745 "  from the cache. This function throws an error if fn is not a validated asm.js\n"
  1746 "  module."),
  1748     JS_FN_HELP("isAsmJSFunction", IsAsmJSFunction, 1, 0,
  1749 "isAsmJSFunction(fn)",
  1750 "  Returns whether the given value is a nested function in an asm.js module that has been\n"
  1751 "  both compile- and link-time validated."),
  1753     JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0,
  1754 "isLazyFunction(fun)",
  1755 "  True if fun is a lazy JSFunction."),
  1757     JS_FN_HELP("isRelazifiableFunction", IsRelazifiableFunction, 1, 0,
  1758 "isRelazifiableFunction(fun)",
  1759 "  Ture if fun is a JSFunction with a relazifiable JSScript."),
  1761     JS_FN_HELP("inParallelSection", testingFunc_inParallelSection, 0, 0,
  1762 "inParallelSection()",
  1763 "  True if this code is executing within a parallel section."),
  1765     JS_FN_HELP("setObjectMetadataCallback", SetObjectMetadataCallback, 1, 0,
  1766 "setObjectMetadataCallback(fn)",
  1767 "  Specify function to supply metadata for all newly created objects."),
  1769     JS_FN_HELP("setObjectMetadata", SetObjectMetadata, 2, 0,
  1770 "setObjectMetadata(obj, metadataObj)",
  1771 "  Change the metadata for an object."),
  1773     JS_FN_HELP("getObjectMetadata", GetObjectMetadata, 1, 0,
  1774 "getObjectMetadata(obj)",
  1775 "  Get the metadata for an object."),
  1777     JS_FN_HELP("bailout", testingFunc_bailout, 0, 0,
  1778 "bailout()",
  1779 "  Force a bailout out of ionmonkey (if running in ionmonkey)."),
  1781     JS_FN_HELP("setJitCompilerOption", SetJitCompilerOption, 2, 0,
  1782 "setCompilerOption(<option>, <number>)",
  1783 "  Set a compiler option indexed in JSCompileOption enum to a number.\n"),
  1785     JS_FN_HELP("setIonCheckGraphCoherency", SetIonCheckGraphCoherency, 1, 0,
  1786 "setIonCheckGraphCoherency(bool)",
  1787 "  Set whether Ion should perform graph consistency (DEBUG-only) assertions. These assertions\n"
  1788 "  are valuable and should be generally enabled, however they can be very expensive for large\n"
  1789 "  (asm.js) programs."),
  1791     JS_FN_HELP("serialize", Serialize, 1, 0,
  1792 "serialize(data, [transferables])",
  1793 "  Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
  1794 "  clone buffer object."),
  1796     JS_FN_HELP("deserialize", Deserialize, 1, 0,
  1797 "deserialize(clonebuffer)",
  1798 "  Deserialize data generated by serialize."),
  1800     JS_FN_HELP("neuter", Neuter, 1, 0,
  1801 "neuter(buffer, \"change-data\"|\"same-data\")",
  1802 "  Neuter the given ArrayBuffer object as if it had been transferred to a\n"
  1803 "  WebWorker. \"change-data\" will update the internal data pointer.\n"
  1804 "  \"same-data\" will leave it set to its original value, to mimic eg\n"
  1805 "  asm.js ArrayBuffer neutering."),
  1807     JS_FN_HELP("workerThreadCount", WorkerThreadCount, 0, 0,
  1808 "workerThreadCount()",
  1809 "  Returns the number of worker threads available for off-main-thread tasks."),
  1811     JS_FN_HELP("startTraceLogger", EnableTraceLogger, 0, 0,
  1812 "startTraceLogger()",
  1813 "  Start logging the mainThread.\n"
  1814 "  Note: tracelogging starts automatically. Disable it by setting environment variable\n"
  1815 "  TLOPTIONS=disableMainThread"),
  1817     JS_FN_HELP("stopTraceLogger", DisableTraceLogger, 0, 0,
  1818 "startTraceLogger()",
  1819 "  Stop logging the mainThread."),
  1820     JS_FS_HELP_END
  1821 };
  1823 static const JSPropertySpec TestingProperties[] = {
  1824     JS_PSG("timesAccessed", TimesAccessed, 0),
  1825     JS_PS_END
  1826 };
  1828 bool
  1829 js::DefineTestingFunctions(JSContext *cx, HandleObject obj, bool fuzzingSafe_)
  1831     fuzzingSafe = fuzzingSafe_;
  1832     if (getenv("MOZ_FUZZING_SAFE") && getenv("MOZ_FUZZING_SAFE")[0] != '0')
  1833         fuzzingSafe = true;
  1835     if (!JS_DefineProperties(cx, obj, TestingProperties))
  1836         return false;
  1838     return JS_DefineFunctionsWithHelp(cx, obj, TestingFunctions);

mercurial