Wed, 31 Dec 2014 06:09:35 +0100
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;
1003 }
1005 static bool
1006 EnableSPSProfilingAssertions(JSContext *cx, unsigned argc, jsval *vp)
1007 {
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;
1013 }
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;
1027 }
1029 static bool
1030 DisableSPSProfiling(JSContext *cx, unsigned argc, jsval *vp)
1031 {
1032 if (cx->runtime()->spsProfiler.installed())
1033 cx->runtime()->spsProfiler.enable(false);
1034 return true;
1035 }
1037 static bool
1038 EnableOsiPointRegisterChecks(JSContext *, unsigned argc, jsval *vp)
1039 {
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;
1046 }
1048 static bool
1049 DisplayName(JSContext *cx, unsigned argc, jsval *vp)
1050 {
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;
1056 }
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;
1062 }
1064 bool
1065 js::testingFunc_inParallelSection(JSContext *cx, unsigned argc, jsval *vp)
1066 {
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;
1074 }
1076 static bool
1077 ShellObjectMetadataCallback(JSContext *cx, JSObject **pmetadata)
1078 {
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))
1092 {
1093 return false;
1094 }
1096 if (!JS_DefineProperty(cx, obj, "stack", stack, 0,
1097 JS_PropertyStub, JS_StrictPropertyStub))
1098 {
1099 return false;
1100 }
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))
1107 {
1108 return false;
1109 }
1110 stackIndex++;
1111 }
1112 }
1114 *pmetadata = obj;
1115 return true;
1116 }
1118 static bool
1119 SetObjectMetadataCallback(JSContext *cx, unsigned argc, jsval *vp)
1120 {
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;
1128 }
1130 static bool
1131 SetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp)
1132 {
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;
1137 }
1139 args.rval().setUndefined();
1141 RootedObject obj(cx, &args[0].toObject());
1142 RootedObject metadata(cx, &args[1].toObject());
1143 return SetObjectMetadata(cx, obj, metadata);
1144 }
1146 static bool
1147 GetObjectMetadata(JSContext *cx, unsigned argc, jsval *vp)
1148 {
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;
1153 }
1155 args.rval().setObjectOrNull(GetObjectMetadata(&args[0].toObject()));
1156 return true;
1157 }
1159 bool
1160 js::testingFunc_bailout(JSContext *cx, unsigned argc, jsval *vp)
1161 {
1162 CallArgs args = CallArgsFromVp(argc, vp);
1164 // NOP when not in IonMonkey
1165 args.rval().setUndefined();
1166 return true;
1167 }
1169 bool
1170 js::testingFunc_assertFloat32(JSContext *cx, unsigned argc, jsval *vp)
1171 {
1172 CallArgs args = CallArgsFromVp(argc, vp);
1174 // NOP when not in IonMonkey
1175 args.rval().setUndefined();
1176 return true;
1177 }
1179 static bool
1180 SetJitCompilerOption(JSContext *cx, unsigned argc, jsval *vp)
1181 {
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;
1188 }
1190 if (!args[0].isString()) {
1191 ReportUsageError(cx, callee, "First argument must be a String.");
1192 return false;
1193 }
1195 if (!args[1].isInt32()) {
1196 ReportUsageError(cx, callee, "Second argument must be an Int32.");
1197 return false;
1198 }
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;
1214 }
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;
1224 }
1226 static bool
1227 GetJitCompilerOptions(JSContext *cx, unsigned argc, jsval *vp)
1228 {
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;
1249 }
1251 static bool
1252 SetIonCheckGraphCoherency(JSContext *cx, unsigned argc, jsval *vp)
1253 {
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;
1260 }
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>();
1282 }
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;
1294 }
1296 uint64_t *data() const {
1297 return static_cast<uint64_t*>(getReservedSlot(DATA_SLOT).toPrivate());
1298 }
1300 void setData(uint64_t *aData) {
1301 JS_ASSERT(!data());
1302 setReservedSlot(DATA_SLOT, PrivateValue(aData));
1303 }
1305 size_t nbytes() const {
1306 return getReservedSlot(LENGTH_SLOT).toInt32();
1307 }
1309 void setNBytes(size_t nbytes) {
1310 JS_ASSERT(nbytes <= UINT32_MAX);
1311 setReservedSlot(LENGTH_SLOT, Int32Value(nbytes));
1312 }
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));
1319 }
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;
1330 }
1332 if (fuzzingSafe) {
1333 // A manually-created clonebuffer could easily trigger a crash
1334 args.rval().setUndefined();
1335 return true;
1336 }
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;
1349 }
1351 static bool
1352 is(HandleValue v) {
1353 return v.isObject() && v.toObject().is<CloneBufferObject>();
1354 }
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);
1360 }
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;
1370 }
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;
1379 }
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;
1386 }
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);
1392 }
1394 static void Finalize(FreeOp *fop, JSObject *obj) {
1395 obj->as<CloneBufferObject>().discard();
1396 }
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)
1425 {
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;
1438 }
1440 static bool
1441 Deserialize(JSContext *cx, unsigned argc, jsval *vp)
1442 {
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;
1448 }
1450 if (!args[0].toObject().is<CloneBufferObject>()) {
1451 JS_ReportError(cx, "deserialize requires a clonebuffer");
1452 return false;
1453 }
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;
1462 }
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;
1472 }
1473 args.rval().set(deserialized);
1475 if (hasTransferable)
1476 obj->discard();
1478 return true;
1479 }
1481 static bool
1482 Neuter(JSContext *cx, unsigned argc, jsval *vp)
1483 {
1484 CallArgs args = CallArgsFromVp(argc, vp);
1486 if (args.length() != 2) {
1487 JS_ReportError(cx, "wrong number of arguments to neuter()");
1488 return false;
1489 }
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;
1498 }
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;
1514 }
1516 if (!JS_NeuterArrayBuffer(cx, obj, changeData))
1517 return false;
1519 args.rval().setUndefined();
1520 return true;
1521 }
1523 static bool
1524 WorkerThreadCount(JSContext *cx, unsigned argc, jsval *vp)
1525 {
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;
1533 }
1535 static bool
1536 TimesAccessed(JSContext *cx, unsigned argc, jsval *vp)
1537 {
1538 static int32_t accessed = 0;
1539 CallArgs args = CallArgsFromVp(argc, vp);
1540 args.rval().setInt32(++accessed);
1541 return true;
1542 }
1544 static bool
1545 EnableTraceLogger(JSContext *cx, unsigned argc, jsval *vp)
1546 {
1547 CallArgs args = CallArgsFromVp(argc, vp);
1548 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
1549 args.rval().setBoolean(TraceLoggerEnable(logger));
1551 return true;
1552 }
1554 static bool
1555 DisableTraceLogger(JSContext *cx, unsigned argc, jsval *vp)
1556 {
1557 CallArgs args = CallArgsFromVp(argc, vp);
1558 TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
1559 args.rval().setBoolean(TraceLoggerDisable(logger));
1561 return true;
1562 }
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_)
1830 {
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);
1839 }