1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
6
7 #include "builtin/TestingFunctions.h"
8
9 #include "mozilla/Atomics.h"
10 #include "mozilla/Casting.h"
11 #include "mozilla/FloatingPoint.h"
12 #include "mozilla/Maybe.h"
13 #include "mozilla/Span.h"
14 #include "mozilla/Sprintf.h"
15 #include "mozilla/TextUtils.h"
16 #include "mozilla/Tuple.h"
17 #include "mozilla/Unused.h"
18
19 #include <algorithm>
20 #include <cfloat>
21 #include <cmath>
22 #include <cstdlib>
23 #include <ctime>
24 #include <utility>
25
26 #if defined(XP_UNIX) && !defined(XP_DARWIN)
27 # include <time.h>
28 #else
29 # include <chrono>
30 #endif
31
32 #include "jsapi.h"
33 #include "jsfriendapi.h"
34
35 #include "builtin/Promise.h"
36 #include "builtin/SelfHostingDefines.h"
37 #ifdef DEBUG
38 # include "frontend/TokenStream.h"
39 # ifndef ENABLE_NEW_REGEP
40 # include "irregexp/RegExpAST.h"
41 # include "irregexp/RegExpEngine.h"
42 # include "irregexp/RegExpParser.h"
43 # endif
44 #endif
45 #include "gc/Allocator.h"
46 #include "gc/Zone.h"
47 #include "jit/BaselineJIT.h"
48 #include "jit/InlinableNatives.h"
49 #include "jit/Ion.h"
50 #include "jit/JitRealm.h"
51 #include "js/Array.h" // JS::NewArrayObject
52 #include "js/ArrayBuffer.h" // JS::{DetachArrayBuffer,GetArrayBufferLengthAndData,NewArrayBufferWithContents}
53 #include "js/CharacterEncoding.h"
54 #include "js/CompilationAndEvaluation.h"
55 #include "js/CompileOptions.h"
56 #include "js/Date.h"
57 #include "js/Debug.h"
58 #include "js/HashTable.h"
59 #include "js/LocaleSensitive.h"
60 #include "js/PropertySpec.h"
61 #include "js/RegExpFlags.h" // JS::RegExpFlag, JS::RegExpFlags
62 #include "js/SourceText.h"
63 #include "js/StableStringChars.h"
64 #include "js/StructuredClone.h"
65 #include "js/UbiNode.h"
66 #include "js/UbiNodeBreadthFirst.h"
67 #include "js/UbiNodeShortestPaths.h"
68 #include "js/UniquePtr.h"
69 #include "js/Vector.h"
70 #include "js/Wrapper.h"
71 #include "threading/CpuCount.h"
72 #include "util/StringBuffer.h"
73 #include "util/Text.h"
74 #include "vm/AsyncFunction.h"
75 #include "vm/AsyncIteration.h"
76 #include "vm/ErrorObject.h"
77 #include "vm/GlobalObject.h"
78 #include "vm/Interpreter.h"
79 #include "vm/Iteration.h"
80 #include "vm/JSContext.h"
81 #include "vm/JSObject.h"
82 #include "vm/PlainObject.h" // js::PlainObject
83 #include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseSlot_*
84 #include "vm/ProxyObject.h"
85 #include "vm/SavedStacks.h"
86 #include "vm/Stack.h"
87 #include "vm/StringType.h"
88 #include "vm/TraceLogging.h"
89 #include "wasm/AsmJS.h"
90 #include "wasm/WasmBaselineCompile.h"
91 #include "wasm/WasmCraneliftCompile.h"
92 #include "wasm/WasmInstance.h"
93 #include "wasm/WasmIonCompile.h"
94 #include "wasm/WasmJS.h"
95 #include "wasm/WasmModule.h"
96 #include "wasm/WasmSignalHandlers.h"
97 #include "wasm/WasmTypes.h"
98
99 #include "debugger/DebugAPI-inl.h"
100 #include "vm/Compartment-inl.h"
101 #include "vm/EnvironmentObject-inl.h"
102 #include "vm/JSContext-inl.h"
103 #include "vm/JSObject-inl.h"
104 #include "vm/NativeObject-inl.h"
105 #include "vm/StringType-inl.h"
106
107 using namespace js;
108
109 using mozilla::ArrayLength;
110 using mozilla::AssertedCast;
111 using mozilla::AsWritableChars;
112 using mozilla::MakeSpan;
113 using mozilla::Maybe;
114 using mozilla::Tie;
115 using mozilla::Tuple;
116
117 using JS::AutoStableStringChars;
118 using JS::CompileOptions;
119 using JS::RegExpFlag;
120 using JS::RegExpFlags;
121 using JS::SourceOwnership;
122 using JS::SourceText;
123
124 // If fuzzingSafe is set, remove functionality that could cause problems with
125 // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
126 mozilla::Atomic<bool> fuzzingSafe(false);
127
128 // If disableOOMFunctions is set, disable functionality that causes artificial
129 // OOM conditions.
130 static mozilla::Atomic<bool> disableOOMFunctions(false);
131
EnvVarIsDefined(const char * name)132 static bool EnvVarIsDefined(const char* name) {
133 const char* value = getenv(name);
134 return value && *value;
135 }
136
137 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
EnvVarAsInt(const char * name,int * valueOut)138 static bool EnvVarAsInt(const char* name, int* valueOut) {
139 if (!EnvVarIsDefined(name)) {
140 return false;
141 }
142
143 *valueOut = atoi(getenv(name));
144 return true;
145 }
146 #endif
147
GetBuildConfiguration(JSContext * cx,unsigned argc,Value * vp)148 static bool GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) {
149 CallArgs args = CallArgsFromVp(argc, vp);
150 RootedObject info(cx, JS_NewPlainObject(cx));
151 if (!info) {
152 return false;
153 }
154
155 if (!JS_SetProperty(cx, info, "rooting-analysis", FalseHandleValue)) {
156 return false;
157 }
158
159 if (!JS_SetProperty(cx, info, "exact-rooting", TrueHandleValue)) {
160 return false;
161 }
162
163 if (!JS_SetProperty(cx, info, "trace-jscalls-api", FalseHandleValue)) {
164 return false;
165 }
166
167 if (!JS_SetProperty(cx, info, "incremental-gc", TrueHandleValue)) {
168 return false;
169 }
170
171 if (!JS_SetProperty(cx, info, "generational-gc", TrueHandleValue)) {
172 return false;
173 }
174
175 if (!JS_SetProperty(cx, info, "oom-backtraces", FalseHandleValue)) {
176 return false;
177 }
178
179 RootedValue value(cx);
180 #ifdef DEBUG
181 value = BooleanValue(true);
182 #else
183 value = BooleanValue(false);
184 #endif
185 if (!JS_SetProperty(cx, info, "debug", value)) {
186 return false;
187 }
188
189 #ifdef RELEASE_OR_BETA
190 value = BooleanValue(true);
191 #else
192 value = BooleanValue(false);
193 #endif
194 if (!JS_SetProperty(cx, info, "release_or_beta", value)) {
195 return false;
196 }
197
198 #ifdef MOZ_CODE_COVERAGE
199 value = BooleanValue(true);
200 #else
201 value = BooleanValue(false);
202 #endif
203 if (!JS_SetProperty(cx, info, "coverage", value)) {
204 return false;
205 }
206
207 #ifdef JS_HAS_CTYPES
208 value = BooleanValue(true);
209 #else
210 value = BooleanValue(false);
211 #endif
212 if (!JS_SetProperty(cx, info, "has-ctypes", value)) {
213 return false;
214 }
215
216 #if defined(_M_IX86) || defined(__i386__)
217 value = BooleanValue(true);
218 #else
219 value = BooleanValue(false);
220 #endif
221 if (!JS_SetProperty(cx, info, "x86", value)) {
222 return false;
223 }
224
225 #if defined(_M_X64) || defined(__x86_64__)
226 value = BooleanValue(true);
227 #else
228 value = BooleanValue(false);
229 #endif
230 if (!JS_SetProperty(cx, info, "x64", value)) {
231 return false;
232 }
233
234 #ifdef JS_CODEGEN_ARM
235 value = BooleanValue(true);
236 #else
237 value = BooleanValue(false);
238 #endif
239 if (!JS_SetProperty(cx, info, "arm", value)) {
240 return false;
241 }
242
243 #ifdef JS_SIMULATOR_ARM
244 value = BooleanValue(true);
245 #else
246 value = BooleanValue(false);
247 #endif
248 if (!JS_SetProperty(cx, info, "arm-simulator", value)) {
249 return false;
250 }
251
252 #ifdef ANDROID
253 value = BooleanValue(true);
254 #else
255 value = BooleanValue(false);
256 #endif
257 if (!JS_SetProperty(cx, info, "android", value)) {
258 return false;
259 }
260
261 #ifdef XP_WIN
262 value = BooleanValue(true);
263 #else
264 value = BooleanValue(false);
265 #endif
266 if (!JS_SetProperty(cx, info, "windows", value)) {
267 return false;
268 }
269
270 #ifdef JS_CODEGEN_ARM64
271 value = BooleanValue(true);
272 #else
273 value = BooleanValue(false);
274 #endif
275 if (!JS_SetProperty(cx, info, "arm64", value)) {
276 return false;
277 }
278
279 #ifdef JS_SIMULATOR_ARM64
280 value = BooleanValue(true);
281 #else
282 value = BooleanValue(false);
283 #endif
284 if (!JS_SetProperty(cx, info, "arm64-simulator", value)) {
285 return false;
286 }
287
288 #ifdef JS_CODEGEN_MIPS32
289 value = BooleanValue(true);
290 #else
291 value = BooleanValue(false);
292 #endif
293 if (!JS_SetProperty(cx, info, "mips32", value)) {
294 return false;
295 }
296
297 #ifdef JS_CODEGEN_MIPS64
298 value = BooleanValue(true);
299 #else
300 value = BooleanValue(false);
301 #endif
302 if (!JS_SetProperty(cx, info, "mips64", value)) {
303 return false;
304 }
305
306 #ifdef JS_SIMULATOR_MIPS32
307 value = BooleanValue(true);
308 #else
309 value = BooleanValue(false);
310 #endif
311 if (!JS_SetProperty(cx, info, "mips32-simulator", value)) {
312 return false;
313 }
314
315 #ifdef JS_SIMULATOR_MIPS64
316 value = BooleanValue(true);
317 #else
318 value = BooleanValue(false);
319 #endif
320 if (!JS_SetProperty(cx, info, "mips64-simulator", value)) {
321 return false;
322 }
323
324 #ifdef MOZ_ASAN
325 value = BooleanValue(true);
326 #else
327 value = BooleanValue(false);
328 #endif
329 if (!JS_SetProperty(cx, info, "asan", value)) {
330 return false;
331 }
332
333 #ifdef MOZ_TSAN
334 value = BooleanValue(true);
335 #else
336 value = BooleanValue(false);
337 #endif
338 if (!JS_SetProperty(cx, info, "tsan", value)) {
339 return false;
340 }
341
342 #ifdef MOZ_UBSAN
343 value = BooleanValue(true);
344 #else
345 value = BooleanValue(false);
346 #endif
347 if (!JS_SetProperty(cx, info, "ubsan", value)) {
348 return false;
349 }
350
351 #ifdef JS_GC_ZEAL
352 value = BooleanValue(true);
353 #else
354 value = BooleanValue(false);
355 #endif
356 if (!JS_SetProperty(cx, info, "has-gczeal", value)) {
357 return false;
358 }
359
360 #ifdef JS_MORE_DETERMINISTIC
361 value = BooleanValue(true);
362 #else
363 value = BooleanValue(false);
364 #endif
365 if (!JS_SetProperty(cx, info, "more-deterministic", value)) {
366 return false;
367 }
368
369 #ifdef MOZ_PROFILING
370 value = BooleanValue(true);
371 #else
372 value = BooleanValue(false);
373 #endif
374 if (!JS_SetProperty(cx, info, "profiling", value)) {
375 return false;
376 }
377
378 #ifdef INCLUDE_MOZILLA_DTRACE
379 value = BooleanValue(true);
380 #else
381 value = BooleanValue(false);
382 #endif
383 if (!JS_SetProperty(cx, info, "dtrace", value)) {
384 return false;
385 }
386
387 #ifdef MOZ_VALGRIND
388 value = BooleanValue(true);
389 #else
390 value = BooleanValue(false);
391 #endif
392 if (!JS_SetProperty(cx, info, "valgrind", value)) {
393 return false;
394 }
395
396 #ifdef JS_HAS_TYPED_OBJECTS
397 value = BooleanValue(true);
398 #else
399 value = BooleanValue(false);
400 #endif
401 if (!JS_SetProperty(cx, info, "typed-objects", value)) {
402 return false;
403 }
404
405 #ifdef JS_HAS_INTL_API
406 value = BooleanValue(true);
407 #else
408 value = BooleanValue(false);
409 #endif
410 if (!JS_SetProperty(cx, info, "intl-api", value)) {
411 return false;
412 }
413
414 #if defined(SOLARIS)
415 value = BooleanValue(false);
416 #else
417 value = BooleanValue(true);
418 #endif
419 if (!JS_SetProperty(cx, info, "mapped-array-buffer", value)) {
420 return false;
421 }
422
423 #ifdef MOZ_MEMORY
424 value = BooleanValue(true);
425 #else
426 value = BooleanValue(false);
427 #endif
428 if (!JS_SetProperty(cx, info, "moz-memory", value)) {
429 return false;
430 }
431
432 #if defined(JS_BUILD_BINAST)
433 value = BooleanValue(true);
434 #else
435 value = BooleanValue(false);
436 #endif
437 if (!JS_SetProperty(cx, info, "binast", value)) {
438 return false;
439 }
440
441 value.setInt32(sizeof(void*));
442 if (!JS_SetProperty(cx, info, "pointer-byte-size", value)) {
443 return false;
444 }
445
446 args.rval().setObject(*info);
447 return true;
448 }
449
IsLCovEnabled(JSContext * cx,unsigned argc,Value * vp)450 static bool IsLCovEnabled(JSContext* cx, unsigned argc, Value* vp) {
451 CallArgs args = CallArgsFromVp(argc, vp);
452 args.rval().setBoolean(coverage::IsLCovEnabled());
453 return true;
454 }
455
IsTypeInferenceEnabled(JSContext * cx,unsigned argc,Value * vp)456 static bool IsTypeInferenceEnabled(JSContext* cx, unsigned argc, Value* vp) {
457 CallArgs args = CallArgsFromVp(argc, vp);
458 args.rval().setBoolean(js::IsTypeInferenceEnabled());
459 return true;
460 }
461
ReturnStringCopy(JSContext * cx,CallArgs & args,const char * message)462 static bool ReturnStringCopy(JSContext* cx, CallArgs& args,
463 const char* message) {
464 JSString* str = JS_NewStringCopyZ(cx, message);
465 if (!str) {
466 return false;
467 }
468
469 args.rval().setString(str);
470 return true;
471 }
472
MaybeGC(JSContext * cx,unsigned argc,Value * vp)473 static bool MaybeGC(JSContext* cx, unsigned argc, Value* vp) {
474 CallArgs args = CallArgsFromVp(argc, vp);
475 JS_MaybeGC(cx);
476 args.rval().setUndefined();
477 return true;
478 }
479
GC(JSContext * cx,unsigned argc,Value * vp)480 static bool GC(JSContext* cx, unsigned argc, Value* vp) {
481 CallArgs args = CallArgsFromVp(argc, vp);
482
483 /*
484 * If the first argument is 'zone', we collect any zones previously
485 * scheduled for GC via schedulegc. If the first argument is an object, we
486 * collect the object's zone (and any other zones scheduled for
487 * GC). Otherwise, we collect all zones.
488 */
489 bool zone = false;
490 if (args.length() >= 1) {
491 Value arg = args[0];
492 if (arg.isString()) {
493 if (!JS_StringEqualsLiteral(cx, arg.toString(), "zone", &zone)) {
494 return false;
495 }
496 } else if (arg.isObject()) {
497 PrepareZoneForGC(cx, UncheckedUnwrap(&arg.toObject())->zone());
498 zone = true;
499 }
500 }
501
502 JSGCInvocationKind gckind = GC_NORMAL;
503 JS::GCReason reason = JS::GCReason::API;
504 if (args.length() >= 2) {
505 Value arg = args[1];
506 if (arg.isString()) {
507 bool shrinking = false;
508 bool last_ditch = false;
509 if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking",
510 &shrinking)) {
511 return false;
512 }
513 if (!JS_StringEqualsLiteral(cx, arg.toString(), "last-ditch",
514 &last_ditch)) {
515 return false;
516 }
517 if (shrinking) {
518 gckind = GC_SHRINK;
519 } else if (last_ditch) {
520 gckind = GC_SHRINK;
521 reason = JS::GCReason::LAST_DITCH;
522 }
523 }
524 }
525
526 #ifndef JS_MORE_DETERMINISTIC
527 size_t preBytes = cx->runtime()->gc.heapSize.bytes();
528 #endif
529
530 if (zone) {
531 PrepareForDebugGC(cx->runtime());
532 } else {
533 JS::PrepareForFullGC(cx);
534 }
535
536 JS::NonIncrementalGC(cx, gckind, reason);
537
538 char buf[256] = {'\0'};
539 #ifndef JS_MORE_DETERMINISTIC
540 SprintfLiteral(buf, "before %zu, after %zu\n", preBytes,
541 cx->runtime()->gc.heapSize.bytes());
542 #endif
543 return ReturnStringCopy(cx, args, buf);
544 }
545
MinorGC(JSContext * cx,unsigned argc,Value * vp)546 static bool MinorGC(JSContext* cx, unsigned argc, Value* vp) {
547 CallArgs args = CallArgsFromVp(argc, vp);
548 if (args.get(0) == BooleanValue(true)) {
549 cx->runtime()->gc.storeBuffer().setAboutToOverflow(
550 JS::GCReason::FULL_GENERIC_BUFFER);
551 }
552
553 cx->minorGC(JS::GCReason::API);
554 args.rval().setUndefined();
555 return true;
556 }
557
558 #define FOR_EACH_GC_PARAM(_) \
559 _("maxBytes", JSGC_MAX_BYTES, true) \
560 _("minNurseryBytes", JSGC_MIN_NURSERY_BYTES, true) \
561 _("maxNurseryBytes", JSGC_MAX_NURSERY_BYTES, true) \
562 _("gcBytes", JSGC_BYTES, false) \
563 _("nurseryBytes", JSGC_NURSERY_BYTES, false) \
564 _("gcNumber", JSGC_NUMBER, false) \
565 _("mode", JSGC_MODE, true) \
566 _("unusedChunks", JSGC_UNUSED_CHUNKS, false) \
567 _("totalChunks", JSGC_TOTAL_CHUNKS, false) \
568 _("sliceTimeBudgetMS", JSGC_SLICE_TIME_BUDGET_MS, true) \
569 _("markStackLimit", JSGC_MARK_STACK_LIMIT, true) \
570 _("highFrequencyTimeLimit", JSGC_HIGH_FREQUENCY_TIME_LIMIT, true) \
571 _("smallHeapSizeMax", JSGC_SMALL_HEAP_SIZE_MAX, true) \
572 _("largeHeapSizeMin", JSGC_LARGE_HEAP_SIZE_MIN, true) \
573 _("highFrequencySmallHeapGrowth", JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH, \
574 true) \
575 _("highFrequencyLargeHeapGrowth", JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH, \
576 true) \
577 _("lowFrequencyHeapGrowth", JSGC_LOW_FREQUENCY_HEAP_GROWTH, true) \
578 _("allocationThreshold", JSGC_ALLOCATION_THRESHOLD, true) \
579 _("smallHeapIncrementalLimit", JSGC_SMALL_HEAP_INCREMENTAL_LIMIT, true) \
580 _("largeHeapIncrementalLimit", JSGC_LARGE_HEAP_INCREMENTAL_LIMIT, true) \
581 _("minEmptyChunkCount", JSGC_MIN_EMPTY_CHUNK_COUNT, true) \
582 _("maxEmptyChunkCount", JSGC_MAX_EMPTY_CHUNK_COUNT, true) \
583 _("compactingEnabled", JSGC_COMPACTING_ENABLED, true) \
584 _("minLastDitchGCPeriod", JSGC_MIN_LAST_DITCH_GC_PERIOD, true) \
585 _("nurseryFreeThresholdForIdleCollection", \
586 JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION, true) \
587 _("nurseryFreeThresholdForIdleCollectionPercent", \
588 JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT, true) \
589 _("pretenureThreshold", JSGC_PRETENURE_THRESHOLD, true) \
590 _("pretenureGroupThreshold", JSGC_PRETENURE_GROUP_THRESHOLD, true) \
591 _("zoneAllocDelayKB", JSGC_ZONE_ALLOC_DELAY_KB, true) \
592 _("mallocThresholdBase", JSGC_MALLOC_THRESHOLD_BASE, true) \
593 _("mallocGrowthFactor", JSGC_MALLOC_GROWTH_FACTOR, true)
594
595 static const struct ParamInfo {
596 const char* name;
597 JSGCParamKey param;
598 bool writable;
599 } paramMap[] = {
600 #define DEFINE_PARAM_INFO(name, key, writable) {name, key, writable},
601 FOR_EACH_GC_PARAM(DEFINE_PARAM_INFO)
602 #undef DEFINE_PARAM_INFO
603 };
604
605 #define PARAM_NAME_LIST_ENTRY(name, key, writable) " " name
606 #define GC_PARAMETER_ARGS_LIST FOR_EACH_GC_PARAM(PARAM_NAME_LIST_ENTRY)
607
GCParameter(JSContext * cx,unsigned argc,Value * vp)608 static bool GCParameter(JSContext* cx, unsigned argc, Value* vp) {
609 CallArgs args = CallArgsFromVp(argc, vp);
610
611 JSString* str = ToString(cx, args.get(0));
612 if (!str) {
613 return false;
614 }
615
616 JSLinearString* linearStr = JS_EnsureLinearString(cx, str);
617 if (!linearStr) {
618 return false;
619 }
620
621 size_t paramIndex = 0;
622 for (;; paramIndex++) {
623 if (paramIndex == ArrayLength(paramMap)) {
624 JS_ReportErrorASCII(
625 cx, "the first argument must be one of:" GC_PARAMETER_ARGS_LIST);
626 return false;
627 }
628 if (JS_LinearStringEqualsAscii(linearStr, paramMap[paramIndex].name)) {
629 break;
630 }
631 }
632 const ParamInfo& info = paramMap[paramIndex];
633 JSGCParamKey param = info.param;
634
635 // Request mode.
636 if (args.length() == 1) {
637 uint32_t value = JS_GetGCParameter(cx, param);
638 args.rval().setNumber(value);
639 return true;
640 }
641
642 if (!info.writable) {
643 JS_ReportErrorASCII(cx, "Attempt to change read-only parameter %s",
644 info.name);
645 return false;
646 }
647
648 if (disableOOMFunctions) {
649 switch (param) {
650 case JSGC_MAX_BYTES:
651 case JSGC_MAX_NURSERY_BYTES:
652 args.rval().setUndefined();
653 return true;
654 default:
655 break;
656 }
657 }
658
659 double d;
660 if (!ToNumber(cx, args[1], &d)) {
661 return false;
662 }
663
664 if (d < 0 || d > UINT32_MAX) {
665 JS_ReportErrorASCII(cx, "Parameter value out of range");
666 return false;
667 }
668
669 uint32_t value = floor(d);
670 if (param == JSGC_MARK_STACK_LIMIT && JS::IsIncrementalGCInProgress(cx)) {
671 JS_ReportErrorASCII(
672 cx, "attempt to set markStackLimit while a GC is in progress");
673 return false;
674 }
675
676 bool ok = cx->runtime()->gc.setParameter(param, value);
677 if (!ok) {
678 JS_ReportErrorASCII(cx, "Parameter value out of range");
679 return false;
680 }
681
682 args.rval().setUndefined();
683 return true;
684 }
685
RelazifyFunctions(JSContext * cx,unsigned argc,Value * vp)686 static bool RelazifyFunctions(JSContext* cx, unsigned argc, Value* vp) {
687 // Relazifying functions on GC is usually only done for compartments that are
688 // not active. To aid fuzzing, this testing function allows us to relazify
689 // even if the compartment is active.
690
691 CallArgs args = CallArgsFromVp(argc, vp);
692
693 // Disable relazification of all scripts on stack. It is a pervasive
694 // assumption in the engine that running scripts still have bytecode.
695 for (AllScriptFramesIter i(cx); !i.done(); ++i) {
696 i.script()->clearAllowRelazify();
697 }
698
699 cx->runtime()->allowRelazificationForTesting = true;
700
701 JS::PrepareForFullGC(cx);
702 JS::NonIncrementalGC(cx, GC_SHRINK, JS::GCReason::API);
703
704 cx->runtime()->allowRelazificationForTesting = false;
705
706 args.rval().setUndefined();
707 return true;
708 }
709
IsProxy(JSContext * cx,unsigned argc,Value * vp)710 static bool IsProxy(JSContext* cx, unsigned argc, Value* vp) {
711 CallArgs args = CallArgsFromVp(argc, vp);
712 if (args.length() != 1) {
713 JS_ReportErrorASCII(cx, "the function takes exactly one argument");
714 return false;
715 }
716 if (!args[0].isObject()) {
717 args.rval().setBoolean(false);
718 return true;
719 }
720 args.rval().setBoolean(args[0].toObject().is<ProxyObject>());
721 return true;
722 }
723
WasmIsSupported(JSContext * cx,unsigned argc,Value * vp)724 static bool WasmIsSupported(JSContext* cx, unsigned argc, Value* vp) {
725 CallArgs args = CallArgsFromVp(argc, vp);
726 args.rval().setBoolean(wasm::HasSupport(cx));
727 return true;
728 }
729
WasmIsSupportedByHardware(JSContext * cx,unsigned argc,Value * vp)730 static bool WasmIsSupportedByHardware(JSContext* cx, unsigned argc, Value* vp) {
731 CallArgs args = CallArgsFromVp(argc, vp);
732 args.rval().setBoolean(wasm::HasPlatformSupport(cx));
733 return true;
734 }
735
WasmDebuggingIsSupported(JSContext * cx,unsigned argc,Value * vp)736 static bool WasmDebuggingIsSupported(JSContext* cx, unsigned argc, Value* vp) {
737 CallArgs args = CallArgsFromVp(argc, vp);
738 args.rval().setBoolean(wasm::HasSupport(cx) && wasm::BaselineAvailable(cx));
739 return true;
740 }
741
WasmStreamingIsSupported(JSContext * cx,unsigned argc,Value * vp)742 static bool WasmStreamingIsSupported(JSContext* cx, unsigned argc, Value* vp) {
743 CallArgs args = CallArgsFromVp(argc, vp);
744 args.rval().setBoolean(wasm::StreamingCompilationAvailable(cx));
745 return true;
746 }
747
WasmCachingIsSupported(JSContext * cx,unsigned argc,Value * vp)748 static bool WasmCachingIsSupported(JSContext* cx, unsigned argc, Value* vp) {
749 CallArgs args = CallArgsFromVp(argc, vp);
750 args.rval().setBoolean(wasm::CodeCachingAvailable(cx));
751 return true;
752 }
753
WasmHugeMemoryIsSupported(JSContext * cx,unsigned argc,Value * vp)754 static bool WasmHugeMemoryIsSupported(JSContext* cx, unsigned argc, Value* vp) {
755 CallArgs args = CallArgsFromVp(argc, vp);
756 #ifdef WASM_SUPPORTS_HUGE_MEMORY
757 args.rval().setBoolean(true);
758 #else
759 args.rval().setBoolean(false);
760 #endif
761 return true;
762 }
763
WasmThreadsSupported(JSContext * cx,unsigned argc,Value * vp)764 static bool WasmThreadsSupported(JSContext* cx, unsigned argc, Value* vp) {
765 CallArgs args = CallArgsFromVp(argc, vp);
766 args.rval().setBoolean(wasm::ThreadsAvailable(cx));
767 return true;
768 }
769
WasmBulkMemSupported(JSContext * cx,unsigned argc,Value * vp)770 static bool WasmBulkMemSupported(JSContext* cx, unsigned argc, Value* vp) {
771 CallArgs args = CallArgsFromVp(argc, vp);
772 #ifdef ENABLE_WASM_BULKMEM_OPS
773 bool isSupported = true;
774 #else
775 bool isSupported =
776 cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
777 #endif
778 args.rval().setBoolean(isSupported);
779 return true;
780 }
781
WasmReftypesEnabled(JSContext * cx,unsigned argc,Value * vp)782 static bool WasmReftypesEnabled(JSContext* cx, unsigned argc, Value* vp) {
783 CallArgs args = CallArgsFromVp(argc, vp);
784 args.rval().setBoolean(wasm::ReftypesAvailable(cx));
785 return true;
786 }
787
WasmGcEnabled(JSContext * cx,unsigned argc,Value * vp)788 static bool WasmGcEnabled(JSContext* cx, unsigned argc, Value* vp) {
789 CallArgs args = CallArgsFromVp(argc, vp);
790 args.rval().setBoolean(wasm::GcTypesAvailable(cx));
791 return true;
792 }
793
WasmMultiValueEnabled(JSContext * cx,unsigned argc,Value * vp)794 static bool WasmMultiValueEnabled(JSContext* cx, unsigned argc, Value* vp) {
795 CallArgs args = CallArgsFromVp(argc, vp);
796 args.rval().setBoolean(wasm::MultiValuesAvailable(cx));
797 return true;
798 }
799
WasmSimdSupported(JSContext * cx,unsigned argc,Value * vp)800 static bool WasmSimdSupported(JSContext* cx, unsigned argc, Value* vp) {
801 CallArgs args = CallArgsFromVp(argc, vp);
802 args.rval().setBoolean(wasm::SimdAvailable(cx));
803 return true;
804 }
805
WasmCompilersPresent(JSContext * cx,unsigned argc,Value * vp)806 static bool WasmCompilersPresent(JSContext* cx, unsigned argc, Value* vp) {
807 CallArgs args = CallArgsFromVp(argc, vp);
808
809 char buf[256];
810 *buf = 0;
811 if (wasm::BaselinePlatformSupport()) {
812 strcat(buf, "baseline");
813 }
814 if (wasm::IonPlatformSupport()) {
815 if (*buf) {
816 strcat(buf, ",");
817 }
818 strcat(buf, "ion");
819 }
820 if (wasm::CraneliftPlatformSupport()) {
821 if (*buf) {
822 strcat(buf, ",");
823 }
824 strcat(buf, "cranelift");
825 }
826
827 JSString* result = JS_NewStringCopyZ(cx, buf);
828 if (!result) {
829 return false;
830 }
831
832 args.rval().setString(result);
833 return true;
834 }
835
WasmCompileMode(JSContext * cx,unsigned argc,Value * vp)836 static bool WasmCompileMode(JSContext* cx, unsigned argc, Value* vp) {
837 CallArgs args = CallArgsFromVp(argc, vp);
838
839 // This triplet of predicates will select zero or one baseline compiler and
840 // zero or one optimizing compiler.
841 bool baseline = wasm::BaselineAvailable(cx);
842 bool ion = wasm::IonAvailable(cx);
843 bool cranelift = wasm::CraneliftAvailable(cx);
844
845 MOZ_ASSERT(!(ion && cranelift));
846
847 JSString* result;
848 if (!wasm::HasSupport(cx)) {
849 result = JS_NewStringCopyZ(cx, "none");
850 } else if (baseline && ion) {
851 result = JS_NewStringCopyZ(cx, "baseline+ion");
852 } else if (baseline && cranelift) {
853 result = JS_NewStringCopyZ(cx, "baseline+cranelift");
854 } else if (baseline) {
855 result = JS_NewStringCopyZ(cx, "baseline");
856 } else if (cranelift) {
857 result = JS_NewStringCopyZ(cx, "cranelift");
858 } else {
859 result = JS_NewStringCopyZ(cx, "ion");
860 }
861
862 if (!result) {
863 return false;
864 }
865
866 args.rval().setString(result);
867 return true;
868 }
869
WasmCraneliftDisabledByFeatures(JSContext * cx,unsigned argc,Value * vp)870 static bool WasmCraneliftDisabledByFeatures(JSContext* cx, unsigned argc,
871 Value* vp) {
872 CallArgs args = CallArgsFromVp(argc, vp);
873 bool isDisabled = false;
874 JSStringBuilder reason(cx);
875 if (!wasm::CraneliftDisabledByFeatures(cx, &isDisabled, &reason)) {
876 return false;
877 }
878 if (isDisabled) {
879 JSString* result = reason.finishString();
880 if (!result) {
881 return false;
882 }
883 args.rval().setString(result);
884 } else {
885 args.rval().setBoolean(false);
886 }
887 return true;
888 }
889
WasmIonDisabledByFeatures(JSContext * cx,unsigned argc,Value * vp)890 static bool WasmIonDisabledByFeatures(JSContext* cx, unsigned argc, Value* vp) {
891 CallArgs args = CallArgsFromVp(argc, vp);
892 bool isDisabled = false;
893 JSStringBuilder reason(cx);
894 if (!wasm::IonDisabledByFeatures(cx, &isDisabled, &reason)) {
895 return false;
896 }
897 if (isDisabled) {
898 JSString* result = reason.finishString();
899 if (!result) {
900 return false;
901 }
902 args.rval().setString(result);
903 } else {
904 args.rval().setBoolean(false);
905 }
906 return true;
907 }
908
909 #ifdef ENABLE_WASM_SIMD
910 # ifdef DEBUG
911 static char lastAnalysisResult[1024];
912
913 namespace js {
914 namespace wasm {
ReportSimdAnalysis(const char * v)915 void ReportSimdAnalysis(const char* v) {
916 strncpy(lastAnalysisResult, v, sizeof(lastAnalysisResult));
917 lastAnalysisResult[sizeof(lastAnalysisResult) - 1] = 0;
918 }
919 } // namespace wasm
920 } // namespace js
921
922 // Unstable API for white-box testing of SIMD optimizations.
923 //
924 // Current API: takes no arguments, returns a string describing the last Simd
925 // simplification applied.
926
WasmSimdAnalysis(JSContext * cx,unsigned argc,Value * vp)927 static bool WasmSimdAnalysis(JSContext* cx, unsigned argc, Value* vp) {
928 CallArgs args = CallArgsFromVp(argc, vp);
929 JSString* result =
930 JS_NewStringCopyZ(cx, *lastAnalysisResult ? lastAnalysisResult : "none");
931 if (!result) {
932 return false;
933 }
934 args.rval().setString(result);
935 *lastAnalysisResult = (char)0;
936 return true;
937 }
938 # endif
939 #endif
940
ConvertToTier(JSContext * cx,HandleValue value,const wasm::Code & code,wasm::Tier * tier)941 static bool ConvertToTier(JSContext* cx, HandleValue value,
942 const wasm::Code& code, wasm::Tier* tier) {
943 RootedString option(cx, JS::ToString(cx, value));
944
945 if (!option) {
946 return false;
947 }
948
949 bool stableTier = false;
950 bool bestTier = false;
951 bool baselineTier = false;
952 bool ionTier = false;
953
954 if (!JS_StringEqualsLiteral(cx, option, "stable", &stableTier) ||
955 !JS_StringEqualsLiteral(cx, option, "best", &bestTier) ||
956 !JS_StringEqualsLiteral(cx, option, "baseline", &baselineTier) ||
957 !JS_StringEqualsLiteral(cx, option, "ion", &ionTier)) {
958 return false;
959 }
960
961 if (stableTier) {
962 *tier = code.stableTier();
963 } else if (bestTier) {
964 *tier = code.bestTier();
965 } else if (baselineTier) {
966 *tier = wasm::Tier::Baseline;
967 } else if (ionTier) {
968 *tier = wasm::Tier::Optimized;
969 } else {
970 // You can omit the argument but you can't pass just anything you like
971 return false;
972 }
973
974 return true;
975 }
976
WasmExtractCode(JSContext * cx,unsigned argc,Value * vp)977 static bool WasmExtractCode(JSContext* cx, unsigned argc, Value* vp) {
978 if (!cx->options().wasm()) {
979 JS_ReportErrorASCII(cx, "wasm support unavailable");
980 return false;
981 }
982
983 CallArgs args = CallArgsFromVp(argc, vp);
984
985 if (!args.get(0).isObject()) {
986 JS_ReportErrorASCII(cx, "argument is not an object");
987 return false;
988 }
989
990 Rooted<WasmModuleObject*> module(
991 cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>());
992 if (!module) {
993 JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
994 return false;
995 }
996
997 wasm::Tier tier = module->module().code().stableTier();
998 ;
999 if (args.length() > 1 &&
1000 !ConvertToTier(cx, args[1], module->module().code(), &tier)) {
1001 args.rval().setNull();
1002 return false;
1003 }
1004
1005 RootedValue result(cx);
1006 if (!module->module().extractCode(cx, tier, &result)) {
1007 return false;
1008 }
1009
1010 args.rval().set(result);
1011 return true;
1012 }
1013
WasmDisassemble(JSContext * cx,unsigned argc,Value * vp)1014 static bool WasmDisassemble(JSContext* cx, unsigned argc, Value* vp) {
1015 if (!cx->options().wasm()) {
1016 JS_ReportErrorASCII(cx, "wasm support unavailable");
1017 return false;
1018 }
1019
1020 CallArgs args = CallArgsFromVp(argc, vp);
1021
1022 args.rval().set(UndefinedValue());
1023
1024 if (!args.get(0).isObject()) {
1025 JS_ReportErrorASCII(cx, "argument is not an object");
1026 return false;
1027 }
1028
1029 RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>());
1030
1031 if (!func || !wasm::IsWasmExportedFunction(func)) {
1032 JS_ReportErrorASCII(cx, "argument is not an exported wasm function");
1033 return false;
1034 }
1035
1036 wasm::Instance& instance = wasm::ExportedFunctionToInstance(func);
1037 uint32_t funcIndex = wasm::ExportedFunctionToFuncIndex(func);
1038
1039 wasm::Tier tier = instance.code().stableTier();
1040
1041 if (args.length() > 1 &&
1042 !ConvertToTier(cx, args[1], instance.code(), &tier)) {
1043 JS_ReportErrorASCII(cx, "invalid tier");
1044 return false;
1045 }
1046
1047 if (!instance.code().hasTier(tier)) {
1048 JS_ReportErrorASCII(cx, "function missing selected tier");
1049 return false;
1050 }
1051
1052 instance.disassembleExport(cx, funcIndex, tier, [](const char* text) {
1053 fprintf(stderr, "%s\n", text);
1054 });
1055
1056 return true;
1057 }
1058
1059 enum class Flag { Tier2Complete, Deserialized };
1060
WasmReturnFlag(JSContext * cx,unsigned argc,Value * vp,Flag flag)1061 static bool WasmReturnFlag(JSContext* cx, unsigned argc, Value* vp, Flag flag) {
1062 CallArgs args = CallArgsFromVp(argc, vp);
1063
1064 if (!args.get(0).isObject()) {
1065 JS_ReportErrorASCII(cx, "argument is not an object");
1066 return false;
1067 }
1068
1069 Rooted<WasmModuleObject*> module(
1070 cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>());
1071 if (!module) {
1072 JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
1073 return false;
1074 }
1075
1076 bool b;
1077 switch (flag) {
1078 case Flag::Tier2Complete:
1079 b = !module->module().testingTier2Active();
1080 break;
1081 case Flag::Deserialized:
1082 b = module->module().loggingDeserialized();
1083 break;
1084 }
1085
1086 args.rval().set(BooleanValue(b));
1087 return true;
1088 }
1089
WasmHasTier2CompilationCompleted(JSContext * cx,unsigned argc,Value * vp)1090 static bool WasmHasTier2CompilationCompleted(JSContext* cx, unsigned argc,
1091 Value* vp) {
1092 return WasmReturnFlag(cx, argc, vp, Flag::Tier2Complete);
1093 }
1094
WasmLoadedFromCache(JSContext * cx,unsigned argc,Value * vp)1095 static bool WasmLoadedFromCache(JSContext* cx, unsigned argc, Value* vp) {
1096 return WasmReturnFlag(cx, argc, vp, Flag::Deserialized);
1097 }
1098
IsLazyFunction(JSContext * cx,unsigned argc,Value * vp)1099 static bool IsLazyFunction(JSContext* cx, unsigned argc, Value* vp) {
1100 CallArgs args = CallArgsFromVp(argc, vp);
1101 if (args.length() != 1) {
1102 JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
1103 return false;
1104 }
1105 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
1106 JS_ReportErrorASCII(cx, "The first argument should be a function.");
1107 return false;
1108 }
1109 JSFunction* fun = &args[0].toObject().as<JSFunction>();
1110 args.rval().setBoolean(fun->isInterpreted() && !fun->hasBytecode());
1111 return true;
1112 }
1113
IsRelazifiableFunction(JSContext * cx,unsigned argc,Value * vp)1114 static bool IsRelazifiableFunction(JSContext* cx, unsigned argc, Value* vp) {
1115 CallArgs args = CallArgsFromVp(argc, vp);
1116 if (args.length() != 1) {
1117 JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
1118 return false;
1119 }
1120 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
1121 JS_ReportErrorASCII(cx, "The first argument should be a function.");
1122 return false;
1123 }
1124
1125 JSFunction* fun = &args[0].toObject().as<JSFunction>();
1126 args.rval().setBoolean(fun->hasBytecode() &&
1127 fun->nonLazyScript()->allowRelazify());
1128 return true;
1129 }
1130
InternalConst(JSContext * cx,unsigned argc,Value * vp)1131 static bool InternalConst(JSContext* cx, unsigned argc, Value* vp) {
1132 CallArgs args = CallArgsFromVp(argc, vp);
1133 if (args.length() == 0) {
1134 JS_ReportErrorASCII(cx, "the function takes exactly one argument");
1135 return false;
1136 }
1137
1138 JSString* str = ToString(cx, args[0]);
1139 if (!str) {
1140 return false;
1141 }
1142 JSLinearString* linear = JS_EnsureLinearString(cx, str);
1143 if (!linear) {
1144 return false;
1145 }
1146
1147 if (JS_LinearStringEqualsLiteral(linear,
1148 "INCREMENTAL_MARK_STACK_BASE_CAPACITY")) {
1149 args.rval().setNumber(uint32_t(js::INCREMENTAL_MARK_STACK_BASE_CAPACITY));
1150 } else {
1151 JS_ReportErrorASCII(cx, "unknown const name");
1152 return false;
1153 }
1154 return true;
1155 }
1156
GCPreserveCode(JSContext * cx,unsigned argc,Value * vp)1157 static bool GCPreserveCode(JSContext* cx, unsigned argc, Value* vp) {
1158 CallArgs args = CallArgsFromVp(argc, vp);
1159
1160 if (args.length() != 0) {
1161 RootedObject callee(cx, &args.callee());
1162 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1163 return false;
1164 }
1165
1166 cx->runtime()->gc.setAlwaysPreserveCode();
1167
1168 args.rval().setUndefined();
1169 return true;
1170 }
1171
1172 #ifdef JS_GC_ZEAL
1173
ParseGCZealMode(JSContext * cx,const CallArgs & args,uint8_t * zeal)1174 static bool ParseGCZealMode(JSContext* cx, const CallArgs& args,
1175 uint8_t* zeal) {
1176 uint32_t value;
1177 if (!ToUint32(cx, args.get(0), &value)) {
1178 return false;
1179 }
1180
1181 if (value > uint32_t(gc::ZealMode::Limit)) {
1182 JS_ReportErrorASCII(cx, "gczeal argument out of range");
1183 return false;
1184 }
1185
1186 *zeal = static_cast<uint8_t>(value);
1187 return true;
1188 }
1189
GCZeal(JSContext * cx,unsigned argc,Value * vp)1190 static bool GCZeal(JSContext* cx, unsigned argc, Value* vp) {
1191 CallArgs args = CallArgsFromVp(argc, vp);
1192
1193 if (args.length() > 2) {
1194 RootedObject callee(cx, &args.callee());
1195 ReportUsageErrorASCII(cx, callee, "Too many arguments");
1196 return false;
1197 }
1198
1199 uint8_t zeal;
1200 if (!ParseGCZealMode(cx, args, &zeal)) {
1201 return false;
1202 }
1203
1204 uint32_t frequency = JS_DEFAULT_ZEAL_FREQ;
1205 if (args.length() >= 2) {
1206 if (!ToUint32(cx, args.get(1), &frequency)) {
1207 return false;
1208 }
1209 }
1210
1211 JS_SetGCZeal(cx, zeal, frequency);
1212 args.rval().setUndefined();
1213 return true;
1214 }
1215
UnsetGCZeal(JSContext * cx,unsigned argc,Value * vp)1216 static bool UnsetGCZeal(JSContext* cx, unsigned argc, Value* vp) {
1217 CallArgs args = CallArgsFromVp(argc, vp);
1218
1219 if (args.length() > 1) {
1220 RootedObject callee(cx, &args.callee());
1221 ReportUsageErrorASCII(cx, callee, "Too many arguments");
1222 return false;
1223 }
1224
1225 uint8_t zeal;
1226 if (!ParseGCZealMode(cx, args, &zeal)) {
1227 return false;
1228 }
1229
1230 JS_UnsetGCZeal(cx, zeal);
1231 args.rval().setUndefined();
1232 return true;
1233 }
1234
ScheduleGC(JSContext * cx,unsigned argc,Value * vp)1235 static bool ScheduleGC(JSContext* cx, unsigned argc, Value* vp) {
1236 CallArgs args = CallArgsFromVp(argc, vp);
1237
1238 if (args.length() > 1) {
1239 RootedObject callee(cx, &args.callee());
1240 ReportUsageErrorASCII(cx, callee, "Too many arguments");
1241 return false;
1242 }
1243
1244 if (args.length() == 0) {
1245 /* Fetch next zeal trigger only. */
1246 } else if (args[0].isNumber()) {
1247 /* Schedule a GC to happen after |arg| allocations. */
1248 JS_ScheduleGC(cx, std::max(int(args[0].toNumber()), 0));
1249 } else {
1250 RootedObject callee(cx, &args.callee());
1251 ReportUsageErrorASCII(cx, callee, "Bad argument - expecting number");
1252 return false;
1253 }
1254
1255 uint32_t zealBits;
1256 uint32_t freq;
1257 uint32_t next;
1258 JS_GetGCZealBits(cx, &zealBits, &freq, &next);
1259 args.rval().setInt32(next);
1260 return true;
1261 }
1262
SelectForGC(JSContext * cx,unsigned argc,Value * vp)1263 static bool SelectForGC(JSContext* cx, unsigned argc, Value* vp) {
1264 CallArgs args = CallArgsFromVp(argc, vp);
1265
1266 /*
1267 * The selectedForMarking set is intended to be manually marked at slice
1268 * start to detect missing pre-barriers. It is invalid for nursery things
1269 * to be in the set, so evict the nursery before adding items.
1270 */
1271 cx->runtime()->gc.evictNursery();
1272
1273 for (unsigned i = 0; i < args.length(); i++) {
1274 if (args[i].isObject()) {
1275 if (!cx->runtime()->gc.selectForMarking(&args[i].toObject())) {
1276 return false;
1277 }
1278 }
1279 }
1280
1281 args.rval().setUndefined();
1282 return true;
1283 }
1284
VerifyPreBarriers(JSContext * cx,unsigned argc,Value * vp)1285 static bool VerifyPreBarriers(JSContext* cx, unsigned argc, Value* vp) {
1286 CallArgs args = CallArgsFromVp(argc, vp);
1287
1288 if (args.length() > 0) {
1289 RootedObject callee(cx, &args.callee());
1290 ReportUsageErrorASCII(cx, callee, "Too many arguments");
1291 return false;
1292 }
1293
1294 gc::VerifyBarriers(cx->runtime(), gc::PreBarrierVerifier);
1295 args.rval().setUndefined();
1296 return true;
1297 }
1298
VerifyPostBarriers(JSContext * cx,unsigned argc,Value * vp)1299 static bool VerifyPostBarriers(JSContext* cx, unsigned argc, Value* vp) {
1300 // This is a no-op since the post barrier verifier was removed.
1301 CallArgs args = CallArgsFromVp(argc, vp);
1302 if (args.length()) {
1303 RootedObject callee(cx, &args.callee());
1304 ReportUsageErrorASCII(cx, callee, "Too many arguments");
1305 return false;
1306 }
1307 args.rval().setUndefined();
1308 return true;
1309 }
1310
CurrentGC(JSContext * cx,unsigned argc,Value * vp)1311 static bool CurrentGC(JSContext* cx, unsigned argc, Value* vp) {
1312 CallArgs args = CallArgsFromVp(argc, vp);
1313
1314 if (args.length() != 0) {
1315 RootedObject callee(cx, &args.callee());
1316 ReportUsageErrorASCII(cx, callee, "Too many arguments");
1317 return false;
1318 }
1319
1320 RootedObject result(cx, JS_NewPlainObject(cx));
1321 if (!result) {
1322 return false;
1323 }
1324
1325 js::gc::GCRuntime& gc = cx->runtime()->gc;
1326 const char* state = StateName(gc.state());
1327
1328 RootedString str(cx, JS_NewStringCopyZ(cx, state));
1329 if (!str) {
1330 return false;
1331 }
1332 RootedValue val(cx, StringValue(str));
1333 if (!JS_DefineProperty(cx, result, "incrementalState", val,
1334 JSPROP_ENUMERATE)) {
1335 return false;
1336 }
1337
1338 if (gc.state() == js::gc::State::Sweep) {
1339 val = Int32Value(gc.getCurrentSweepGroupIndex());
1340 if (!JS_DefineProperty(cx, result, "sweepGroup", val, JSPROP_ENUMERATE)) {
1341 return false;
1342 }
1343 }
1344
1345 val = BooleanValue(gc.isShrinkingGC());
1346 if (!JS_DefineProperty(cx, result, "isShrinking", val, JSPROP_ENUMERATE)) {
1347 return false;
1348 }
1349
1350 val = Int32Value(gc.gcNumber());
1351 if (!JS_DefineProperty(cx, result, "number", val, JSPROP_ENUMERATE)) {
1352 return false;
1353 }
1354
1355 val = Int32Value(gc.minorGCCount());
1356 if (!JS_DefineProperty(cx, result, "minorCount", val, JSPROP_ENUMERATE)) {
1357 return false;
1358 }
1359
1360 val = Int32Value(gc.majorGCCount());
1361 if (!JS_DefineProperty(cx, result, "majorCount", val, JSPROP_ENUMERATE)) {
1362 return false;
1363 }
1364
1365 val = BooleanValue(gc.isFullGc());
1366 if (!JS_DefineProperty(cx, result, "isFull", val, JSPROP_ENUMERATE)) {
1367 return false;
1368 }
1369
1370 val = BooleanValue(gc.isCompactingGc());
1371 if (!JS_DefineProperty(cx, result, "isCompacting", val, JSPROP_ENUMERATE)) {
1372 return false;
1373 }
1374
1375 # ifdef DEBUG
1376 val = Int32Value(gc.marker.queuePos);
1377 if (!JS_DefineProperty(cx, result, "queuePos", val, JSPROP_ENUMERATE)) {
1378 return false;
1379 }
1380 # endif
1381
1382 args.rval().setObject(*result);
1383 return true;
1384 }
1385
DeterministicGC(JSContext * cx,unsigned argc,Value * vp)1386 static bool DeterministicGC(JSContext* cx, unsigned argc, Value* vp) {
1387 CallArgs args = CallArgsFromVp(argc, vp);
1388
1389 if (args.length() != 1) {
1390 RootedObject callee(cx, &args.callee());
1391 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1392 return false;
1393 }
1394
1395 cx->runtime()->gc.setDeterministic(ToBoolean(args[0]));
1396 args.rval().setUndefined();
1397 return true;
1398 }
1399
DumpGCArenaInfo(JSContext * cx,unsigned argc,Value * vp)1400 static bool DumpGCArenaInfo(JSContext* cx, unsigned argc, Value* vp) {
1401 CallArgs args = CallArgsFromVp(argc, vp);
1402 js::gc::DumpArenaInfo();
1403 args.rval().setUndefined();
1404 return true;
1405 }
1406
1407 #endif /* JS_GC_ZEAL */
1408
GCState(JSContext * cx,unsigned argc,Value * vp)1409 static bool GCState(JSContext* cx, unsigned argc, Value* vp) {
1410 CallArgs args = CallArgsFromVp(argc, vp);
1411
1412 if (args.length() > 1) {
1413 RootedObject callee(cx, &args.callee());
1414 ReportUsageErrorASCII(cx, callee, "Too many arguments");
1415 return false;
1416 }
1417
1418 const char* state;
1419
1420 if (args.length() == 1) {
1421 if (!args[0].isObject()) {
1422 RootedObject callee(cx, &args.callee());
1423 ReportUsageErrorASCII(cx, callee, "Expected object");
1424 return false;
1425 }
1426
1427 JSObject* obj = UncheckedUnwrap(&args[0].toObject());
1428 state = gc::StateName(obj->zone()->gcState());
1429 } else {
1430 state = gc::StateName(cx->runtime()->gc.state());
1431 }
1432
1433 return ReturnStringCopy(cx, args, state);
1434 }
1435
ScheduleZoneForGC(JSContext * cx,unsigned argc,Value * vp)1436 static bool ScheduleZoneForGC(JSContext* cx, unsigned argc, Value* vp) {
1437 CallArgs args = CallArgsFromVp(argc, vp);
1438
1439 if (args.length() != 1) {
1440 RootedObject callee(cx, &args.callee());
1441 ReportUsageErrorASCII(cx, callee, "Expecting a single argument");
1442 return false;
1443 }
1444
1445 if (args[0].isObject()) {
1446 // Ensure that |zone| is collected during the next GC.
1447 Zone* zone = UncheckedUnwrap(&args[0].toObject())->zone();
1448 PrepareZoneForGC(cx, zone);
1449 } else if (args[0].isString()) {
1450 // This allows us to schedule the atoms zone for GC.
1451 Zone* zone = args[0].toString()->zoneFromAnyThread();
1452 if (!CurrentThreadCanAccessZone(zone)) {
1453 RootedObject callee(cx, &args.callee());
1454 ReportUsageErrorASCII(cx, callee, "Specified zone not accessible for GC");
1455 return false;
1456 }
1457 PrepareZoneForGC(cx, zone);
1458 } else {
1459 RootedObject callee(cx, &args.callee());
1460 ReportUsageErrorASCII(cx, callee,
1461 "Bad argument - expecting object or string");
1462 return false;
1463 }
1464
1465 args.rval().setUndefined();
1466 return true;
1467 }
1468
StartGC(JSContext * cx,unsigned argc,Value * vp)1469 static bool StartGC(JSContext* cx, unsigned argc, Value* vp) {
1470 CallArgs args = CallArgsFromVp(argc, vp);
1471
1472 if (args.length() > 2) {
1473 RootedObject callee(cx, &args.callee());
1474 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1475 return false;
1476 }
1477
1478 auto budget = SliceBudget::unlimited();
1479 if (args.length() >= 1) {
1480 uint32_t work = 0;
1481 if (!ToUint32(cx, args[0], &work)) {
1482 return false;
1483 }
1484 budget = SliceBudget(WorkBudget(work));
1485 }
1486
1487 bool shrinking = false;
1488 if (args.length() >= 2) {
1489 Value arg = args[1];
1490 if (arg.isString()) {
1491 if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking",
1492 &shrinking)) {
1493 return false;
1494 }
1495 }
1496 }
1497
1498 JSRuntime* rt = cx->runtime();
1499 if (rt->gc.isIncrementalGCInProgress()) {
1500 RootedObject callee(cx, &args.callee());
1501 JS_ReportErrorASCII(cx, "Incremental GC already in progress");
1502 return false;
1503 }
1504
1505 JSGCInvocationKind gckind = shrinking ? GC_SHRINK : GC_NORMAL;
1506 rt->gc.startDebugGC(gckind, budget);
1507
1508 args.rval().setUndefined();
1509 return true;
1510 }
1511
FinishGC(JSContext * cx,unsigned argc,Value * vp)1512 static bool FinishGC(JSContext* cx, unsigned argc, Value* vp) {
1513 CallArgs args = CallArgsFromVp(argc, vp);
1514
1515 if (args.length() > 0) {
1516 RootedObject callee(cx, &args.callee());
1517 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1518 return false;
1519 }
1520
1521 JSRuntime* rt = cx->runtime();
1522 if (rt->gc.isIncrementalGCInProgress()) {
1523 rt->gc.finishGC(JS::GCReason::DEBUG_GC);
1524 }
1525
1526 args.rval().setUndefined();
1527 return true;
1528 }
1529
GCSlice(JSContext * cx,unsigned argc,Value * vp)1530 static bool GCSlice(JSContext* cx, unsigned argc, Value* vp) {
1531 CallArgs args = CallArgsFromVp(argc, vp);
1532
1533 if (args.length() > 1) {
1534 RootedObject callee(cx, &args.callee());
1535 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1536 return false;
1537 }
1538
1539 auto budget = SliceBudget::unlimited();
1540 if (args.length() == 1) {
1541 uint32_t work = 0;
1542 if (!ToUint32(cx, args[0], &work)) {
1543 return false;
1544 }
1545 budget = SliceBudget(WorkBudget(work));
1546 }
1547
1548 JSRuntime* rt = cx->runtime();
1549 if (!rt->gc.isIncrementalGCInProgress()) {
1550 rt->gc.startDebugGC(GC_NORMAL, budget);
1551 } else {
1552 rt->gc.debugGCSlice(budget);
1553 }
1554
1555 args.rval().setUndefined();
1556 return true;
1557 }
1558
AbortGC(JSContext * cx,unsigned argc,Value * vp)1559 static bool AbortGC(JSContext* cx, unsigned argc, Value* vp) {
1560 CallArgs args = CallArgsFromVp(argc, vp);
1561
1562 if (args.length() != 0) {
1563 RootedObject callee(cx, &args.callee());
1564 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1565 return false;
1566 }
1567
1568 JS::AbortIncrementalGC(cx);
1569 args.rval().setUndefined();
1570 return true;
1571 }
1572
FullCompartmentChecks(JSContext * cx,unsigned argc,Value * vp)1573 static bool FullCompartmentChecks(JSContext* cx, unsigned argc, Value* vp) {
1574 CallArgs args = CallArgsFromVp(argc, vp);
1575
1576 if (args.length() != 1) {
1577 RootedObject callee(cx, &args.callee());
1578 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1579 return false;
1580 }
1581
1582 cx->runtime()->gc.setFullCompartmentChecks(ToBoolean(args[0]));
1583 args.rval().setUndefined();
1584 return true;
1585 }
1586
NondeterministicGetWeakMapKeys(JSContext * cx,unsigned argc,Value * vp)1587 static bool NondeterministicGetWeakMapKeys(JSContext* cx, unsigned argc,
1588 Value* vp) {
1589 CallArgs args = CallArgsFromVp(argc, vp);
1590
1591 if (args.length() != 1) {
1592 RootedObject callee(cx, &args.callee());
1593 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1594 return false;
1595 }
1596 if (!args[0].isObject()) {
1597 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1598 JSMSG_NOT_EXPECTED_TYPE,
1599 "nondeterministicGetWeakMapKeys", "WeakMap",
1600 InformalValueTypeName(args[0]));
1601 return false;
1602 }
1603 RootedObject arr(cx);
1604 RootedObject mapObj(cx, &args[0].toObject());
1605 if (!JS_NondeterministicGetWeakMapKeys(cx, mapObj, &arr)) {
1606 return false;
1607 }
1608 if (!arr) {
1609 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1610 JSMSG_NOT_EXPECTED_TYPE,
1611 "nondeterministicGetWeakMapKeys", "WeakMap",
1612 args[0].toObject().getClass()->name);
1613 return false;
1614 }
1615 args.rval().setObject(*arr);
1616 return true;
1617 }
1618
1619 class HasChildTracer final : public JS::CallbackTracer {
1620 RootedValue child_;
1621 bool found_;
1622
onChild(const JS::GCCellPtr & thing)1623 bool onChild(const JS::GCCellPtr& thing) override {
1624 if (thing.asCell() == child_.toGCThing()) {
1625 found_ = true;
1626 }
1627 return true;
1628 }
1629
1630 public:
HasChildTracer(JSContext * cx,HandleValue child)1631 HasChildTracer(JSContext* cx, HandleValue child)
1632 : JS::CallbackTracer(cx, TraceWeakMapKeysValues),
1633 child_(cx, child),
1634 found_(false) {}
1635
found() const1636 bool found() const { return found_; }
1637 };
1638
HasChild(JSContext * cx,unsigned argc,Value * vp)1639 static bool HasChild(JSContext* cx, unsigned argc, Value* vp) {
1640 CallArgs args = CallArgsFromVp(argc, vp);
1641 RootedValue parent(cx, args.get(0));
1642 RootedValue child(cx, args.get(1));
1643
1644 if (!parent.isGCThing() || !child.isGCThing()) {
1645 args.rval().setBoolean(false);
1646 return true;
1647 }
1648
1649 HasChildTracer trc(cx, child);
1650 TraceChildren(&trc, parent.toGCThing(), parent.traceKind());
1651 args.rval().setBoolean(trc.found());
1652 return true;
1653 }
1654
SetSavedStacksRNGState(JSContext * cx,unsigned argc,Value * vp)1655 static bool SetSavedStacksRNGState(JSContext* cx, unsigned argc, Value* vp) {
1656 CallArgs args = CallArgsFromVp(argc, vp);
1657 if (!args.requireAtLeast(cx, "setSavedStacksRNGState", 1)) {
1658 return false;
1659 }
1660
1661 int32_t seed;
1662 if (!ToInt32(cx, args[0], &seed)) {
1663 return false;
1664 }
1665
1666 // Either one or the other of the seed arguments must be non-zero;
1667 // make this true no matter what value 'seed' has.
1668 cx->realm()->savedStacks().setRNGState(seed, (seed + 1) * 33);
1669 return true;
1670 }
1671
GetSavedFrameCount(JSContext * cx,unsigned argc,Value * vp)1672 static bool GetSavedFrameCount(JSContext* cx, unsigned argc, Value* vp) {
1673 CallArgs args = CallArgsFromVp(argc, vp);
1674 args.rval().setNumber(cx->realm()->savedStacks().count());
1675 return true;
1676 }
1677
ClearSavedFrames(JSContext * cx,unsigned argc,Value * vp)1678 static bool ClearSavedFrames(JSContext* cx, unsigned argc, Value* vp) {
1679 CallArgs args = CallArgsFromVp(argc, vp);
1680
1681 js::SavedStacks& savedStacks = cx->realm()->savedStacks();
1682 savedStacks.clear();
1683
1684 for (ActivationIterator iter(cx); !iter.done(); ++iter) {
1685 iter->clearLiveSavedFrameCache();
1686 }
1687
1688 args.rval().setUndefined();
1689 return true;
1690 }
1691
SaveStack(JSContext * cx,unsigned argc,Value * vp)1692 static bool SaveStack(JSContext* cx, unsigned argc, Value* vp) {
1693 CallArgs args = CallArgsFromVp(argc, vp);
1694
1695 JS::StackCapture capture((JS::AllFrames()));
1696 if (args.length() >= 1) {
1697 double maxDouble;
1698 if (!ToNumber(cx, args[0], &maxDouble)) {
1699 return false;
1700 }
1701 if (mozilla::IsNaN(maxDouble) || maxDouble < 0 || maxDouble > UINT32_MAX) {
1702 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
1703 nullptr, "not a valid maximum frame count");
1704 return false;
1705 }
1706 uint32_t max = uint32_t(maxDouble);
1707 if (max > 0) {
1708 capture = JS::StackCapture(JS::MaxFrames(max));
1709 }
1710 }
1711
1712 RootedObject compartmentObject(cx);
1713 if (args.length() >= 2) {
1714 if (!args[1].isObject()) {
1715 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
1716 nullptr, "not an object");
1717 return false;
1718 }
1719 compartmentObject = UncheckedUnwrap(&args[1].toObject());
1720 if (!compartmentObject) {
1721 return false;
1722 }
1723 }
1724
1725 RootedObject stack(cx);
1726 {
1727 Maybe<AutoRealm> ar;
1728 if (compartmentObject) {
1729 ar.emplace(cx, compartmentObject);
1730 }
1731 if (!JS::CaptureCurrentStack(cx, &stack, std::move(capture))) {
1732 return false;
1733 }
1734 }
1735
1736 if (stack && !cx->compartment()->wrap(cx, &stack)) {
1737 return false;
1738 }
1739
1740 args.rval().setObjectOrNull(stack);
1741 return true;
1742 }
1743
CaptureFirstSubsumedFrame(JSContext * cx,unsigned argc,JS::Value * vp)1744 static bool CaptureFirstSubsumedFrame(JSContext* cx, unsigned argc,
1745 JS::Value* vp) {
1746 CallArgs args = CallArgsFromVp(argc, vp);
1747 if (!args.requireAtLeast(cx, "captureFirstSubsumedFrame", 1)) {
1748 return false;
1749 }
1750
1751 if (!args[0].isObject()) {
1752 JS_ReportErrorASCII(cx, "The argument must be an object");
1753 return false;
1754 }
1755
1756 RootedObject obj(cx, &args[0].toObject());
1757 obj = CheckedUnwrapStatic(obj);
1758 if (!obj) {
1759 JS_ReportErrorASCII(cx, "Denied permission to object.");
1760 return false;
1761 }
1762
1763 JS::StackCapture capture(
1764 JS::FirstSubsumedFrame(cx, obj->nonCCWRealm()->principals()));
1765 if (args.length() > 1) {
1766 capture.as<JS::FirstSubsumedFrame>().ignoreSelfHosted =
1767 JS::ToBoolean(args[1]);
1768 }
1769
1770 JS::RootedObject capturedStack(cx);
1771 if (!JS::CaptureCurrentStack(cx, &capturedStack, std::move(capture))) {
1772 return false;
1773 }
1774
1775 args.rval().setObjectOrNull(capturedStack);
1776 return true;
1777 }
1778
CallFunctionFromNativeFrame(JSContext * cx,unsigned argc,Value * vp)1779 static bool CallFunctionFromNativeFrame(JSContext* cx, unsigned argc,
1780 Value* vp) {
1781 CallArgs args = CallArgsFromVp(argc, vp);
1782
1783 if (args.length() != 1) {
1784 JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
1785 return false;
1786 }
1787 if (!args[0].isObject() || !IsCallable(args[0])) {
1788 JS_ReportErrorASCII(cx, "The first argument should be a function.");
1789 return false;
1790 }
1791
1792 RootedObject function(cx, &args[0].toObject());
1793 return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(),
1794 args.rval());
1795 }
1796
CallFunctionWithAsyncStack(JSContext * cx,unsigned argc,Value * vp)1797 static bool CallFunctionWithAsyncStack(JSContext* cx, unsigned argc,
1798 Value* vp) {
1799 CallArgs args = CallArgsFromVp(argc, vp);
1800
1801 if (args.length() != 3) {
1802 JS_ReportErrorASCII(cx, "The function takes exactly three arguments.");
1803 return false;
1804 }
1805 if (!args[0].isObject() || !IsCallable(args[0])) {
1806 JS_ReportErrorASCII(cx, "The first argument should be a function.");
1807 return false;
1808 }
1809 if (!args[1].isObject() || !args[1].toObject().is<SavedFrame>()) {
1810 JS_ReportErrorASCII(cx, "The second argument should be a SavedFrame.");
1811 return false;
1812 }
1813 if (!args[2].isString() || args[2].toString()->empty()) {
1814 JS_ReportErrorASCII(cx, "The third argument should be a non-empty string.");
1815 return false;
1816 }
1817
1818 RootedObject function(cx, &args[0].toObject());
1819 RootedObject stack(cx, &args[1].toObject());
1820 RootedString asyncCause(cx, args[2].toString());
1821 UniqueChars utf8Cause = JS_EncodeStringToUTF8(cx, asyncCause);
1822 if (!utf8Cause) {
1823 MOZ_ASSERT(cx->isExceptionPending());
1824 return false;
1825 }
1826
1827 JS::AutoSetAsyncStackForNewCalls sas(
1828 cx, stack, utf8Cause.get(),
1829 JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
1830 return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(),
1831 args.rval());
1832 }
1833
EnableTrackAllocations(JSContext * cx,unsigned argc,Value * vp)1834 static bool EnableTrackAllocations(JSContext* cx, unsigned argc, Value* vp) {
1835 SetAllocationMetadataBuilder(cx, &SavedStacks::metadataBuilder);
1836 return true;
1837 }
1838
DisableTrackAllocations(JSContext * cx,unsigned argc,Value * vp)1839 static bool DisableTrackAllocations(JSContext* cx, unsigned argc, Value* vp) {
1840 SetAllocationMetadataBuilder(cx, nullptr);
1841 return true;
1842 }
1843
SetTestFilenameValidationCallback(JSContext * cx,unsigned argc,Value * vp)1844 static bool SetTestFilenameValidationCallback(JSContext* cx, unsigned argc,
1845 Value* vp) {
1846 CallArgs args = CallArgsFromVp(argc, vp);
1847
1848 // Accept all filenames that start with "safe". In system code also accept
1849 // filenames starting with "system".
1850 auto testCb = [](const char* filename, bool isSystemRealm) -> bool {
1851 if (strstr(filename, "safe") == filename) {
1852 return true;
1853 }
1854 if (isSystemRealm && strstr(filename, "system") == filename) {
1855 return true;
1856 }
1857 return false;
1858 };
1859 JS::SetFilenameValidationCallback(testCb);
1860
1861 args.rval().setUndefined();
1862 return true;
1863 }
1864
1865 struct TestExternalString : public JSExternalStringCallbacks {
finalizeTestExternalString1866 void finalize(char16_t* chars) const override { js_free(chars); }
sizeOfBufferTestExternalString1867 size_t sizeOfBuffer(const char16_t* chars,
1868 mozilla::MallocSizeOf mallocSizeOf) const override {
1869 return mallocSizeOf(chars);
1870 }
1871 };
1872
1873 static constexpr TestExternalString TestExternalStringCallbacks;
1874
NewExternalString(JSContext * cx,unsigned argc,Value * vp)1875 static bool NewExternalString(JSContext* cx, unsigned argc, Value* vp) {
1876 CallArgs args = CallArgsFromVp(argc, vp);
1877
1878 if (args.length() != 1 || !args[0].isString()) {
1879 JS_ReportErrorASCII(cx,
1880 "newExternalString takes exactly one string argument.");
1881 return false;
1882 }
1883
1884 RootedString str(cx, args[0].toString());
1885 size_t len = str->length();
1886
1887 auto buf = cx->make_pod_array<char16_t>(len);
1888 if (!buf) {
1889 return false;
1890 }
1891
1892 if (!JS_CopyStringChars(cx, mozilla::Range<char16_t>(buf.get(), len), str)) {
1893 return false;
1894 }
1895
1896 JSString* res =
1897 JS_NewExternalString(cx, buf.get(), len, &TestExternalStringCallbacks);
1898 if (!res) {
1899 return false;
1900 }
1901
1902 mozilla::Unused << buf.release();
1903 args.rval().setString(res);
1904 return true;
1905 }
1906
NewMaybeExternalString(JSContext * cx,unsigned argc,Value * vp)1907 static bool NewMaybeExternalString(JSContext* cx, unsigned argc, Value* vp) {
1908 CallArgs args = CallArgsFromVp(argc, vp);
1909
1910 if (args.length() != 1 || !args[0].isString()) {
1911 JS_ReportErrorASCII(
1912 cx, "newMaybeExternalString takes exactly one string argument.");
1913 return false;
1914 }
1915
1916 RootedString str(cx, args[0].toString());
1917 size_t len = str->length();
1918
1919 auto buf = cx->make_pod_array<char16_t>(len);
1920 if (!buf) {
1921 return false;
1922 }
1923
1924 if (!JS_CopyStringChars(cx, mozilla::Range<char16_t>(buf.get(), len), str)) {
1925 return false;
1926 }
1927
1928 bool allocatedExternal;
1929 JSString* res = JS_NewMaybeExternalString(
1930 cx, buf.get(), len, &TestExternalStringCallbacks, &allocatedExternal);
1931 if (!res) {
1932 return false;
1933 }
1934
1935 if (allocatedExternal) {
1936 mozilla::Unused << buf.release();
1937 }
1938 args.rval().setString(res);
1939 return true;
1940 }
1941
1942 // Warning! This will let you create ropes that I'm not sure would be possible
1943 // otherwise, specifically:
1944 //
1945 // - a rope with a zero-length child
1946 // - a rope that would fit into an inline string
1947 //
NewRope(JSContext * cx,unsigned argc,Value * vp)1948 static bool NewRope(JSContext* cx, unsigned argc, Value* vp) {
1949 CallArgs args = CallArgsFromVp(argc, vp);
1950
1951 if (!args.get(0).isString() || !args.get(1).isString()) {
1952 JS_ReportErrorASCII(cx, "newRope requires two string arguments.");
1953 return false;
1954 }
1955
1956 gc::InitialHeap heap = js::gc::DefaultHeap;
1957 if (args.get(2).isObject()) {
1958 RootedObject options(cx, &args[2].toObject());
1959 RootedValue v(cx);
1960 if (!JS_GetProperty(cx, options, "nursery", &v)) {
1961 return false;
1962 }
1963 if (!v.isUndefined() && !ToBoolean(v)) {
1964 heap = js::gc::TenuredHeap;
1965 }
1966 }
1967
1968 RootedString left(cx, args[0].toString());
1969 RootedString right(cx, args[1].toString());
1970 size_t length = JS_GetStringLength(left) + JS_GetStringLength(right);
1971 if (length > JSString::MAX_LENGTH) {
1972 JS_ReportErrorASCII(cx, "rope length exceeds maximum string length");
1973 return false;
1974 }
1975
1976 Rooted<JSRope*> str(cx, JSRope::new_<CanGC>(cx, left, right, length, heap));
1977 if (!str) {
1978 return false;
1979 }
1980
1981 args.rval().setString(str);
1982 return true;
1983 }
1984
IsRope(JSContext * cx,unsigned argc,Value * vp)1985 static bool IsRope(JSContext* cx, unsigned argc, Value* vp) {
1986 CallArgs args = CallArgsFromVp(argc, vp);
1987
1988 if (!args.get(0).isString()) {
1989 JS_ReportErrorASCII(cx, "isRope requires a string argument.");
1990 return false;
1991 }
1992
1993 JSString* str = args[0].toString();
1994 args.rval().setBoolean(str->isRope());
1995 return true;
1996 }
1997
EnsureLinearString(JSContext * cx,unsigned argc,Value * vp)1998 static bool EnsureLinearString(JSContext* cx, unsigned argc, Value* vp) {
1999 CallArgs args = CallArgsFromVp(argc, vp);
2000
2001 if (args.length() != 1 || !args[0].isString()) {
2002 JS_ReportErrorASCII(
2003 cx, "ensureLinearString takes exactly one string argument.");
2004 return false;
2005 }
2006
2007 JSLinearString* linear = args[0].toString()->ensureLinear(cx);
2008 if (!linear) {
2009 return false;
2010 }
2011
2012 args.rval().setString(linear);
2013 return true;
2014 }
2015
RepresentativeStringArray(JSContext * cx,unsigned argc,Value * vp)2016 static bool RepresentativeStringArray(JSContext* cx, unsigned argc, Value* vp) {
2017 CallArgs args = CallArgsFromVp(argc, vp);
2018
2019 RootedObject array(cx, JS::NewArrayObject(cx, 0));
2020 if (!array) {
2021 return false;
2022 }
2023
2024 if (!JSString::fillWithRepresentatives(cx, array.as<ArrayObject>())) {
2025 return false;
2026 }
2027
2028 args.rval().setObject(*array);
2029 return true;
2030 }
2031
2032 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
2033
OOMThreadTypes(JSContext * cx,unsigned argc,Value * vp)2034 static bool OOMThreadTypes(JSContext* cx, unsigned argc, Value* vp) {
2035 CallArgs args = CallArgsFromVp(argc, vp);
2036 args.rval().setInt32(js::THREAD_TYPE_MAX);
2037 return true;
2038 }
2039
CheckCanSimulateOOM(JSContext * cx)2040 static bool CheckCanSimulateOOM(JSContext* cx) {
2041 if (js::oom::GetThreadType() != js::THREAD_TYPE_MAIN) {
2042 JS_ReportErrorASCII(
2043 cx, "Simulated OOM failure is only supported on the main thread");
2044 return false;
2045 }
2046
2047 return true;
2048 }
2049
SetupOOMFailure(JSContext * cx,bool failAlways,unsigned argc,Value * vp)2050 static bool SetupOOMFailure(JSContext* cx, bool failAlways, unsigned argc,
2051 Value* vp) {
2052 CallArgs args = CallArgsFromVp(argc, vp);
2053
2054 if (disableOOMFunctions) {
2055 args.rval().setUndefined();
2056 return true;
2057 }
2058
2059 if (args.length() < 1) {
2060 JS_ReportErrorASCII(cx, "Count argument required");
2061 return false;
2062 }
2063
2064 if (args.length() > 2) {
2065 JS_ReportErrorASCII(cx, "Too many arguments");
2066 return false;
2067 }
2068
2069 int32_t count;
2070 if (!JS::ToInt32(cx, args.get(0), &count)) {
2071 return false;
2072 }
2073
2074 if (count <= 0) {
2075 JS_ReportErrorASCII(cx, "OOM cutoff should be positive");
2076 return false;
2077 }
2078
2079 uint32_t targetThread = js::THREAD_TYPE_MAIN;
2080 if (args.length() > 1 && !ToUint32(cx, args[1], &targetThread)) {
2081 return false;
2082 }
2083
2084 if (targetThread == js::THREAD_TYPE_NONE ||
2085 targetThread == js::THREAD_TYPE_WORKER ||
2086 targetThread >= js::THREAD_TYPE_MAX) {
2087 JS_ReportErrorASCII(cx, "Invalid thread type specified");
2088 return false;
2089 }
2090
2091 if (!CheckCanSimulateOOM(cx)) {
2092 return false;
2093 }
2094
2095 js::oom::simulator.simulateFailureAfter(js::oom::FailureSimulator::Kind::OOM,
2096 count, targetThread, failAlways);
2097 args.rval().setUndefined();
2098 return true;
2099 }
2100
OOMAfterAllocations(JSContext * cx,unsigned argc,Value * vp)2101 static bool OOMAfterAllocations(JSContext* cx, unsigned argc, Value* vp) {
2102 return SetupOOMFailure(cx, true, argc, vp);
2103 }
2104
OOMAtAllocation(JSContext * cx,unsigned argc,Value * vp)2105 static bool OOMAtAllocation(JSContext* cx, unsigned argc, Value* vp) {
2106 return SetupOOMFailure(cx, false, argc, vp);
2107 }
2108
ResetOOMFailure(JSContext * cx,unsigned argc,Value * vp)2109 static bool ResetOOMFailure(JSContext* cx, unsigned argc, Value* vp) {
2110 CallArgs args = CallArgsFromVp(argc, vp);
2111
2112 if (!CheckCanSimulateOOM(cx)) {
2113 return false;
2114 }
2115
2116 args.rval().setBoolean(js::oom::HadSimulatedOOM());
2117 js::oom::simulator.reset();
2118 return true;
2119 }
2120
CountCompartments(JSContext * cx)2121 static size_t CountCompartments(JSContext* cx) {
2122 size_t count = 0;
2123 for (auto zone : cx->runtime()->gc.zones()) {
2124 count += zone->compartments().length();
2125 }
2126 return count;
2127 }
2128
2129 // Iterative failure testing: test a function by simulating failures at indexed
2130 // locations throughout the normal execution path and checking that the
2131 // resulting state of the environment is consistent with the error result.
2132 //
2133 // For example, trigger OOM at every allocation point and test that the function
2134 // either recovers and succeeds or raises an exception and fails.
2135
2136 struct MOZ_STACK_CLASS IterativeFailureTestParams {
IterativeFailureTestParamsIterativeFailureTestParams2137 explicit IterativeFailureTestParams(JSContext* cx) : testFunction(cx) {}
2138
2139 RootedFunction testFunction;
2140 unsigned threadStart = 0;
2141 unsigned threadEnd = 0;
2142 bool expectExceptionOnFailure = true;
2143 bool keepFailing = false;
2144 bool verbose = false;
2145 };
2146
2147 struct IterativeFailureSimulator {
setupIterativeFailureSimulator2148 virtual void setup(JSContext* cx) {}
teardownIterativeFailureSimulator2149 virtual void teardown(JSContext* cx) {}
2150 virtual void startSimulating(JSContext* cx, unsigned iteration,
2151 unsigned thread, bool keepFailing) = 0;
2152 virtual bool stopSimulating() = 0;
cleanupIterativeFailureSimulator2153 virtual void cleanup(JSContext* cx) {}
2154 };
2155
RunIterativeFailureTest(JSContext * cx,const IterativeFailureTestParams & params,IterativeFailureSimulator & simulator)2156 bool RunIterativeFailureTest(JSContext* cx,
2157 const IterativeFailureTestParams& params,
2158 IterativeFailureSimulator& simulator) {
2159 if (disableOOMFunctions) {
2160 return true;
2161 }
2162
2163 if (!CheckCanSimulateOOM(cx)) {
2164 return false;
2165 }
2166
2167 // Disallow nested tests.
2168 if (cx->runningOOMTest) {
2169 JS_ReportErrorASCII(
2170 cx, "Nested call to iterative failure test is not allowed.");
2171 return false;
2172 }
2173 cx->runningOOMTest = true;
2174
2175 MOZ_ASSERT(!cx->isExceptionPending());
2176
2177 # ifdef JS_GC_ZEAL
2178 JS_SetGCZeal(cx, 0, JS_DEFAULT_ZEAL_FREQ);
2179 # endif
2180
2181 // Delazify the function here if necessary so we don't end up testing that.
2182 if (params.testFunction->isInterpreted() &&
2183 !JSFunction::getOrCreateScript(cx, params.testFunction)) {
2184 return false;
2185 }
2186
2187 size_t compartmentCount = CountCompartments(cx);
2188
2189 RootedValue exception(cx);
2190
2191 simulator.setup(cx);
2192
2193 for (unsigned thread = params.threadStart; thread <= params.threadEnd;
2194 thread++) {
2195 if (params.verbose) {
2196 fprintf(stderr, "thread %u\n", thread);
2197 }
2198
2199 unsigned iteration = 1;
2200 bool failureWasSimulated;
2201 do {
2202 if (params.verbose) {
2203 fprintf(stderr, " iteration %u\n", iteration);
2204 }
2205
2206 MOZ_ASSERT(!cx->isExceptionPending());
2207
2208 simulator.startSimulating(cx, iteration, thread, params.keepFailing);
2209
2210 RootedValue result(cx);
2211 bool ok = JS_CallFunction(cx, cx->global(), params.testFunction,
2212 HandleValueArray::empty(), &result);
2213
2214 failureWasSimulated = simulator.stopSimulating();
2215
2216 if (ok) {
2217 MOZ_ASSERT(!cx->isExceptionPending(),
2218 "Thunk execution succeeded but an exception was raised - "
2219 "missing error check?");
2220 } else if (params.expectExceptionOnFailure) {
2221 MOZ_ASSERT(cx->isExceptionPending(),
2222 "Thunk execution failed but no exception was raised - "
2223 "missing call to js::ReportOutOfMemory()?");
2224 }
2225
2226 // Note that it is possible that the function throws an exception
2227 // unconnected to the simulated failure, in which case we ignore
2228 // it. More correct would be to have the caller pass some kind of
2229 // exception specification and to check the exception against it.
2230
2231 if (!failureWasSimulated && cx->isExceptionPending()) {
2232 if (!cx->getPendingException(&exception)) {
2233 return false;
2234 }
2235 }
2236 cx->clearPendingException();
2237 simulator.cleanup(cx);
2238
2239 gc::FinishGC(cx);
2240
2241 // Some tests create a new compartment or zone on every
2242 // iteration. Our GC is triggered by GC allocations and not by
2243 // number of compartments or zones, so these won't normally get
2244 // cleaned up. The check here stops some tests running out of
2245 // memory. ("Gentlemen, you can't fight in here! This is the
2246 // War oom!")
2247 if (CountCompartments(cx) > compartmentCount + 100) {
2248 JS_GC(cx);
2249 compartmentCount = CountCompartments(cx);
2250 }
2251
2252 # ifdef JS_TRACE_LOGGING
2253 // Reset the TraceLogger state if enabled.
2254 TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
2255 if (logger && logger->enabled()) {
2256 while (logger->enabled()) {
2257 logger->disable();
2258 }
2259 logger->enable(cx);
2260 }
2261 # endif
2262
2263 iteration++;
2264 } while (failureWasSimulated);
2265
2266 if (params.verbose) {
2267 fprintf(stderr, " finished after %u iterations\n", iteration - 1);
2268 if (!exception.isUndefined()) {
2269 RootedString str(cx, JS::ToString(cx, exception));
2270 if (!str) {
2271 fprintf(stderr,
2272 " error while trying to print exception, giving up\n");
2273 return false;
2274 }
2275 UniqueChars bytes(JS_EncodeStringToLatin1(cx, str));
2276 if (!bytes) {
2277 return false;
2278 }
2279 fprintf(stderr, " threw %s\n", bytes.get());
2280 }
2281 }
2282 }
2283
2284 simulator.teardown(cx);
2285
2286 cx->runningOOMTest = false;
2287 return true;
2288 }
2289
ParseIterativeFailureTestParams(JSContext * cx,const CallArgs & args,IterativeFailureTestParams * params)2290 bool ParseIterativeFailureTestParams(JSContext* cx, const CallArgs& args,
2291 IterativeFailureTestParams* params) {
2292 MOZ_ASSERT(params);
2293
2294 if (args.length() < 1 || args.length() > 2) {
2295 JS_ReportErrorASCII(cx, "function takes between 1 and 2 arguments.");
2296 return false;
2297 }
2298
2299 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
2300 JS_ReportErrorASCII(cx, "The first argument must be the function to test.");
2301 return false;
2302 }
2303 params->testFunction = &args[0].toObject().as<JSFunction>();
2304
2305 if (args.length() == 2) {
2306 if (args[1].isBoolean()) {
2307 params->expectExceptionOnFailure = args[1].toBoolean();
2308 } else if (args[1].isObject()) {
2309 RootedObject options(cx, &args[1].toObject());
2310 RootedValue value(cx);
2311
2312 if (!JS_GetProperty(cx, options, "expectExceptionOnFailure", &value)) {
2313 return false;
2314 }
2315 if (!value.isUndefined()) {
2316 params->expectExceptionOnFailure = ToBoolean(value);
2317 }
2318
2319 if (!JS_GetProperty(cx, options, "keepFailing", &value)) {
2320 return false;
2321 }
2322 if (!value.isUndefined()) {
2323 params->keepFailing = ToBoolean(value);
2324 }
2325 } else {
2326 JS_ReportErrorASCII(
2327 cx, "The optional second argument must be an object or a boolean.");
2328 return false;
2329 }
2330 }
2331
2332 // There are some places where we do fail without raising an exception, so
2333 // we can't expose this to the fuzzers by default.
2334 if (fuzzingSafe) {
2335 params->expectExceptionOnFailure = false;
2336 }
2337
2338 // Test all threads by default except worker threads.
2339 params->threadStart = oom::FirstThreadTypeToTest;
2340 params->threadEnd = oom::LastThreadTypeToTest;
2341
2342 // Test a single thread type if specified by the OOM_THREAD environment
2343 // variable.
2344 int threadOption = 0;
2345 if (EnvVarAsInt("OOM_THREAD", &threadOption)) {
2346 if (threadOption < oom::FirstThreadTypeToTest ||
2347 threadOption > oom::LastThreadTypeToTest) {
2348 JS_ReportErrorASCII(cx, "OOM_THREAD value out of range.");
2349 return false;
2350 }
2351
2352 params->threadStart = threadOption;
2353 params->threadEnd = threadOption;
2354 }
2355
2356 params->verbose = EnvVarIsDefined("OOM_VERBOSE");
2357
2358 return true;
2359 }
2360
2361 struct OOMSimulator : public IterativeFailureSimulator {
setupOOMSimulator2362 void setup(JSContext* cx) override { cx->runtime()->hadOutOfMemory = false; }
2363
startSimulatingOOMSimulator2364 void startSimulating(JSContext* cx, unsigned i, unsigned thread,
2365 bool keepFailing) override {
2366 MOZ_ASSERT(!cx->runtime()->hadOutOfMemory);
2367 js::oom::simulator.simulateFailureAfter(
2368 js::oom::FailureSimulator::Kind::OOM, i, thread, keepFailing);
2369 }
2370
stopSimulatingOOMSimulator2371 bool stopSimulating() override {
2372 bool handledOOM = js::oom::HadSimulatedOOM();
2373 js::oom::simulator.reset();
2374 return handledOOM;
2375 }
2376
cleanupOOMSimulator2377 void cleanup(JSContext* cx) override {
2378 cx->runtime()->hadOutOfMemory = false;
2379 }
2380 };
2381
OOMTest(JSContext * cx,unsigned argc,Value * vp)2382 static bool OOMTest(JSContext* cx, unsigned argc, Value* vp) {
2383 CallArgs args = CallArgsFromVp(argc, vp);
2384
2385 IterativeFailureTestParams params(cx);
2386 if (!ParseIterativeFailureTestParams(cx, args, ¶ms)) {
2387 return false;
2388 }
2389
2390 OOMSimulator simulator;
2391 if (!RunIterativeFailureTest(cx, params, simulator)) {
2392 return false;
2393 }
2394
2395 args.rval().setUndefined();
2396 return true;
2397 }
2398
2399 struct StackOOMSimulator : public IterativeFailureSimulator {
startSimulatingStackOOMSimulator2400 void startSimulating(JSContext* cx, unsigned i, unsigned thread,
2401 bool keepFailing) override {
2402 js::oom::simulator.simulateFailureAfter(
2403 js::oom::FailureSimulator::Kind::StackOOM, i, thread, keepFailing);
2404 }
2405
stopSimulatingStackOOMSimulator2406 bool stopSimulating() override {
2407 bool handledOOM = js::oom::HadSimulatedStackOOM();
2408 js::oom::simulator.reset();
2409 return handledOOM;
2410 }
2411 };
2412
StackTest(JSContext * cx,unsigned argc,Value * vp)2413 static bool StackTest(JSContext* cx, unsigned argc, Value* vp) {
2414 CallArgs args = CallArgsFromVp(argc, vp);
2415
2416 IterativeFailureTestParams params(cx);
2417 if (!ParseIterativeFailureTestParams(cx, args, ¶ms)) {
2418 return false;
2419 }
2420
2421 StackOOMSimulator simulator;
2422 if (!RunIterativeFailureTest(cx, params, simulator)) {
2423 return false;
2424 }
2425
2426 args.rval().setUndefined();
2427 return true;
2428 }
2429
2430 struct FailingIterruptSimulator : public IterativeFailureSimulator {
2431 JSInterruptCallback* prevEnd = nullptr;
2432
failingInterruptCallbackFailingIterruptSimulator2433 static bool failingInterruptCallback(JSContext* cx) { return false; }
2434
setupFailingIterruptSimulator2435 void setup(JSContext* cx) override {
2436 prevEnd = cx->interruptCallbacks().end();
2437 JS_AddInterruptCallback(cx, failingInterruptCallback);
2438 }
2439
teardownFailingIterruptSimulator2440 void teardown(JSContext* cx) override {
2441 cx->interruptCallbacks().erase(prevEnd, cx->interruptCallbacks().end());
2442 }
2443
startSimulatingFailingIterruptSimulator2444 void startSimulating(JSContext* cx, unsigned i, unsigned thread,
2445 bool keepFailing) override {
2446 js::oom::simulator.simulateFailureAfter(
2447 js::oom::FailureSimulator::Kind::Interrupt, i, thread, keepFailing);
2448 }
2449
stopSimulatingFailingIterruptSimulator2450 bool stopSimulating() override {
2451 bool handledInterrupt = js::oom::HadSimulatedInterrupt();
2452 js::oom::simulator.reset();
2453 return handledInterrupt;
2454 }
2455 };
2456
InterruptTest(JSContext * cx,unsigned argc,Value * vp)2457 static bool InterruptTest(JSContext* cx, unsigned argc, Value* vp) {
2458 CallArgs args = CallArgsFromVp(argc, vp);
2459
2460 IterativeFailureTestParams params(cx);
2461 if (!ParseIterativeFailureTestParams(cx, args, ¶ms)) {
2462 return false;
2463 }
2464
2465 FailingIterruptSimulator simulator;
2466 if (!RunIterativeFailureTest(cx, params, simulator)) {
2467 return false;
2468 }
2469
2470 args.rval().setUndefined();
2471 return true;
2472 }
2473
2474 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
2475
SettlePromiseNow(JSContext * cx,unsigned argc,Value * vp)2476 static bool SettlePromiseNow(JSContext* cx, unsigned argc, Value* vp) {
2477 CallArgs args = CallArgsFromVp(argc, vp);
2478 if (!args.requireAtLeast(cx, "settlePromiseNow", 1)) {
2479 return false;
2480 }
2481 if (!args[0].isObject() || !args[0].toObject().is<PromiseObject>()) {
2482 JS_ReportErrorASCII(cx, "first argument must be a Promise object");
2483 return false;
2484 }
2485
2486 Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
2487 if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
2488 JS_ReportErrorASCII(
2489 cx, "async function/generator's promise shouldn't be manually settled");
2490 return false;
2491 }
2492
2493 if (promise->state() != JS::PromiseState::Pending) {
2494 JS_ReportErrorASCII(cx, "cannot settle an already-resolved promise");
2495 return false;
2496 }
2497
2498 int32_t flags = promise->flags();
2499 promise->setFixedSlot(
2500 PromiseSlot_Flags,
2501 Int32Value(flags | PROMISE_FLAG_RESOLVED | PROMISE_FLAG_FULFILLED));
2502 promise->setFixedSlot(PromiseSlot_ReactionsOrResult, UndefinedValue());
2503
2504 DebugAPI::onPromiseSettled(cx, promise);
2505 return true;
2506 }
2507
GetWaitForAllPromise(JSContext * cx,unsigned argc,Value * vp)2508 static bool GetWaitForAllPromise(JSContext* cx, unsigned argc, Value* vp) {
2509 CallArgs args = CallArgsFromVp(argc, vp);
2510 if (!args.requireAtLeast(cx, "getWaitForAllPromise", 1)) {
2511 return false;
2512 }
2513 if (!args[0].isObject() || !args[0].toObject().is<ArrayObject>() ||
2514 args[0].toObject().as<NativeObject>().isIndexed()) {
2515 JS_ReportErrorASCII(
2516 cx, "first argument must be a dense Array of Promise objects");
2517 return false;
2518 }
2519 RootedNativeObject list(cx, &args[0].toObject().as<NativeObject>());
2520 RootedObjectVector promises(cx);
2521 uint32_t count = list->getDenseInitializedLength();
2522 if (!promises.resize(count)) {
2523 return false;
2524 }
2525
2526 for (uint32_t i = 0; i < count; i++) {
2527 RootedValue elem(cx, list->getDenseElement(i));
2528 if (!elem.isObject() || !elem.toObject().is<PromiseObject>()) {
2529 JS_ReportErrorASCII(
2530 cx, "Each entry in the passed-in Array must be a Promise");
2531 return false;
2532 }
2533 promises[i].set(&elem.toObject());
2534 }
2535
2536 RootedObject resultPromise(cx, JS::GetWaitForAllPromise(cx, promises));
2537 if (!resultPromise) {
2538 return false;
2539 }
2540
2541 args.rval().set(ObjectValue(*resultPromise));
2542 return true;
2543 }
2544
ResolvePromise(JSContext * cx,unsigned argc,Value * vp)2545 static bool ResolvePromise(JSContext* cx, unsigned argc, Value* vp) {
2546 CallArgs args = CallArgsFromVp(argc, vp);
2547 if (!args.requireAtLeast(cx, "resolvePromise", 2)) {
2548 return false;
2549 }
2550 if (!args[0].isObject() ||
2551 !UncheckedUnwrap(&args[0].toObject())->is<PromiseObject>()) {
2552 JS_ReportErrorASCII(
2553 cx, "first argument must be a maybe-wrapped Promise object");
2554 return false;
2555 }
2556
2557 RootedObject promise(cx, &args[0].toObject());
2558 RootedValue resolution(cx, args[1]);
2559 mozilla::Maybe<AutoRealm> ar;
2560 if (IsWrapper(promise)) {
2561 promise = UncheckedUnwrap(promise);
2562 ar.emplace(cx, promise);
2563 if (!cx->compartment()->wrap(cx, &resolution)) {
2564 return false;
2565 }
2566 }
2567
2568 if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
2569 JS_ReportErrorASCII(
2570 cx,
2571 "async function/generator's promise shouldn't be manually resolved");
2572 return false;
2573 }
2574
2575 bool result = JS::ResolvePromise(cx, promise, resolution);
2576 if (result) {
2577 args.rval().setUndefined();
2578 }
2579 return result;
2580 }
2581
RejectPromise(JSContext * cx,unsigned argc,Value * vp)2582 static bool RejectPromise(JSContext* cx, unsigned argc, Value* vp) {
2583 CallArgs args = CallArgsFromVp(argc, vp);
2584 if (!args.requireAtLeast(cx, "rejectPromise", 2)) {
2585 return false;
2586 }
2587 if (!args[0].isObject() ||
2588 !UncheckedUnwrap(&args[0].toObject())->is<PromiseObject>()) {
2589 JS_ReportErrorASCII(
2590 cx, "first argument must be a maybe-wrapped Promise object");
2591 return false;
2592 }
2593
2594 RootedObject promise(cx, &args[0].toObject());
2595 RootedValue reason(cx, args[1]);
2596 mozilla::Maybe<AutoRealm> ar;
2597 if (IsWrapper(promise)) {
2598 promise = UncheckedUnwrap(promise);
2599 ar.emplace(cx, promise);
2600 if (!cx->compartment()->wrap(cx, &reason)) {
2601 return false;
2602 }
2603 }
2604
2605 if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
2606 JS_ReportErrorASCII(
2607 cx,
2608 "async function/generator's promise shouldn't be manually rejected");
2609 return false;
2610 }
2611
2612 bool result = JS::RejectPromise(cx, promise, reason);
2613 if (result) {
2614 args.rval().setUndefined();
2615 }
2616 return result;
2617 }
2618
StreamsAreEnabled(JSContext * cx,unsigned argc,Value * vp)2619 static bool StreamsAreEnabled(JSContext* cx, unsigned argc, Value* vp) {
2620 CallArgs args = CallArgsFromVp(argc, vp);
2621 args.rval().setBoolean(cx->realm()->creationOptions().getStreamsEnabled());
2622 return true;
2623 }
2624
2625 static unsigned finalizeCount = 0;
2626
finalize_counter_finalize(JSFreeOp * fop,JSObject * obj)2627 static void finalize_counter_finalize(JSFreeOp* fop, JSObject* obj) {
2628 ++finalizeCount;
2629 }
2630
2631 static const JSClassOps FinalizeCounterClassOps = {
2632 nullptr, // addProperty
2633 nullptr, // delProperty
2634 nullptr, // enumerate
2635 nullptr, // newEnumerate
2636 nullptr, // resolve
2637 nullptr, // mayResolve
2638 finalize_counter_finalize, // finalize
2639 nullptr, // call
2640 nullptr, // hasInstance
2641 nullptr, // construct
2642 nullptr, // trace
2643 };
2644
2645 static const JSClass FinalizeCounterClass = {
2646 "FinalizeCounter", JSCLASS_FOREGROUND_FINALIZE, &FinalizeCounterClassOps};
2647
MakeFinalizeObserver(JSContext * cx,unsigned argc,Value * vp)2648 static bool MakeFinalizeObserver(JSContext* cx, unsigned argc, Value* vp) {
2649 CallArgs args = CallArgsFromVp(argc, vp);
2650
2651 JSObject* obj =
2652 JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, nullptr);
2653 if (!obj) {
2654 return false;
2655 }
2656
2657 args.rval().setObject(*obj);
2658 return true;
2659 }
2660
FinalizeCount(JSContext * cx,unsigned argc,Value * vp)2661 static bool FinalizeCount(JSContext* cx, unsigned argc, Value* vp) {
2662 CallArgs args = CallArgsFromVp(argc, vp);
2663 args.rval().setInt32(finalizeCount);
2664 return true;
2665 }
2666
ResetFinalizeCount(JSContext * cx,unsigned argc,Value * vp)2667 static bool ResetFinalizeCount(JSContext* cx, unsigned argc, Value* vp) {
2668 CallArgs args = CallArgsFromVp(argc, vp);
2669 finalizeCount = 0;
2670 args.rval().setUndefined();
2671 return true;
2672 }
2673
DumpHeap(JSContext * cx,unsigned argc,Value * vp)2674 static bool DumpHeap(JSContext* cx, unsigned argc, Value* vp) {
2675 CallArgs args = CallArgsFromVp(argc, vp);
2676
2677 FILE* dumpFile = stdout;
2678
2679 if (args.length() > 1) {
2680 RootedObject callee(cx, &args.callee());
2681 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2682 return false;
2683 }
2684
2685 if (!args.get(0).isUndefined()) {
2686 RootedString str(cx, ToString(cx, args[0]));
2687 if (!str) {
2688 return false;
2689 }
2690 if (!fuzzingSafe) {
2691 UniqueChars fileNameBytes = JS_EncodeStringToLatin1(cx, str);
2692 if (!fileNameBytes) {
2693 return false;
2694 }
2695 dumpFile = fopen(fileNameBytes.get(), "w");
2696 if (!dumpFile) {
2697 fileNameBytes = JS_EncodeStringToLatin1(cx, str);
2698 if (!fileNameBytes) {
2699 return false;
2700 }
2701 JS_ReportErrorLatin1(cx, "can't open %s", fileNameBytes.get());
2702 return false;
2703 }
2704 }
2705 }
2706
2707 js::DumpHeap(cx, dumpFile, js::IgnoreNurseryObjects);
2708
2709 if (dumpFile != stdout) {
2710 fclose(dumpFile);
2711 }
2712
2713 args.rval().setUndefined();
2714 return true;
2715 }
2716
Terminate(JSContext * cx,unsigned arg,Value * vp)2717 static bool Terminate(JSContext* cx, unsigned arg, Value* vp) {
2718 #ifdef JS_MORE_DETERMINISTIC
2719 // Print a message to stderr in more-deterministic builds to help jsfunfuzz
2720 // find uncatchable-exception bugs.
2721 fprintf(stderr, "terminate called\n");
2722 #endif
2723
2724 JS_ClearPendingException(cx);
2725 return false;
2726 }
2727
ReadGeckoProfilingStack(JSContext * cx,unsigned argc,Value * vp)2728 static bool ReadGeckoProfilingStack(JSContext* cx, unsigned argc, Value* vp) {
2729 CallArgs args = CallArgsFromVp(argc, vp);
2730 args.rval().setUndefined();
2731
2732 // Return boolean 'false' if profiler is not enabled.
2733 if (!cx->runtime()->geckoProfiler().enabled()) {
2734 args.rval().setBoolean(false);
2735 return true;
2736 }
2737
2738 // Array holding physical jit stack frames.
2739 RootedObject stack(cx, NewDenseEmptyArray(cx));
2740 if (!stack) {
2741 return false;
2742 }
2743
2744 // If profiler sampling has been suppressed, return an empty
2745 // stack.
2746 if (!cx->isProfilerSamplingEnabled()) {
2747 args.rval().setObject(*stack);
2748 return true;
2749 }
2750
2751 struct InlineFrameInfo {
2752 InlineFrameInfo(const char* kind, UniqueChars label)
2753 : kind(kind), label(std::move(label)) {}
2754 const char* kind;
2755 UniqueChars label;
2756 };
2757
2758 Vector<Vector<InlineFrameInfo, 0, TempAllocPolicy>, 0, TempAllocPolicy>
2759 frameInfo(cx);
2760
2761 JS::ProfilingFrameIterator::RegisterState state;
2762 for (JS::ProfilingFrameIterator i(cx, state); !i.done(); ++i) {
2763 MOZ_ASSERT(i.stackAddress() != nullptr);
2764
2765 if (!frameInfo.emplaceBack(cx)) {
2766 return false;
2767 }
2768
2769 const size_t MaxInlineFrames = 16;
2770 JS::ProfilingFrameIterator::Frame frames[MaxInlineFrames];
2771 uint32_t nframes = i.extractStack(frames, 0, MaxInlineFrames);
2772 MOZ_ASSERT(nframes <= MaxInlineFrames);
2773 for (uint32_t i = 0; i < nframes; i++) {
2774 const char* frameKindStr = nullptr;
2775 switch (frames[i].kind) {
2776 case JS::ProfilingFrameIterator::Frame_BaselineInterpreter:
2777 frameKindStr = "baseline-interpreter";
2778 break;
2779 case JS::ProfilingFrameIterator::Frame_Baseline:
2780 frameKindStr = "baseline-jit";
2781 break;
2782 case JS::ProfilingFrameIterator::Frame_Ion:
2783 frameKindStr = "ion";
2784 break;
2785 case JS::ProfilingFrameIterator::Frame_Wasm:
2786 frameKindStr = "wasm";
2787 break;
2788 default:
2789 frameKindStr = "unknown";
2790 }
2791
2792 UniqueChars label =
2793 DuplicateStringToArena(js::StringBufferArena, cx, frames[i].label);
2794 if (!label) {
2795 return false;
2796 }
2797
2798 if (!frameInfo.back().emplaceBack(frameKindStr, std::move(label))) {
2799 return false;
2800 }
2801 }
2802 }
2803
2804 RootedObject inlineFrameInfo(cx);
2805 RootedString frameKind(cx);
2806 RootedString frameLabel(cx);
2807 RootedId idx(cx);
2808
2809 const unsigned propAttrs = JSPROP_ENUMERATE;
2810
2811 uint32_t physicalFrameNo = 0;
2812 for (auto& frame : frameInfo) {
2813 // Array holding all inline frames in a single physical jit stack frame.
2814 RootedObject inlineStack(cx, NewDenseEmptyArray(cx));
2815 if (!inlineStack) {
2816 return false;
2817 }
2818
2819 uint32_t inlineFrameNo = 0;
2820 for (auto& inlineFrame : frame) {
2821 // Object holding frame info.
2822 RootedObject inlineFrameInfo(cx,
2823 NewBuiltinClassInstance<PlainObject>(cx));
2824 if (!inlineFrameInfo) {
2825 return false;
2826 }
2827
2828 frameKind = NewStringCopyZ<CanGC>(cx, inlineFrame.kind);
2829 if (!frameKind) {
2830 return false;
2831 }
2832
2833 if (!JS_DefineProperty(cx, inlineFrameInfo, "kind", frameKind,
2834 propAttrs)) {
2835 return false;
2836 }
2837
2838 frameLabel = NewLatin1StringZ(cx, std::move(inlineFrame.label));
2839 if (!frameLabel) {
2840 return false;
2841 }
2842
2843 if (!JS_DefineProperty(cx, inlineFrameInfo, "label", frameLabel,
2844 propAttrs)) {
2845 return false;
2846 }
2847
2848 idx = INT_TO_JSID(inlineFrameNo);
2849 if (!JS_DefinePropertyById(cx, inlineStack, idx, inlineFrameInfo, 0)) {
2850 return false;
2851 }
2852
2853 ++inlineFrameNo;
2854 }
2855
2856 // Push inline array into main array.
2857 idx = INT_TO_JSID(physicalFrameNo);
2858 if (!JS_DefinePropertyById(cx, stack, idx, inlineStack, 0)) {
2859 return false;
2860 }
2861
2862 ++physicalFrameNo;
2863 }
2864
2865 args.rval().setObject(*stack);
2866 return true;
2867 }
2868
EnableOsiPointRegisterChecks(JSContext *,unsigned argc,Value * vp)2869 static bool EnableOsiPointRegisterChecks(JSContext*, unsigned argc, Value* vp) {
2870 CallArgs args = CallArgsFromVp(argc, vp);
2871 #ifdef CHECK_OSIPOINT_REGISTERS
2872 jit::JitOptions.checkOsiPointRegisters = true;
2873 #endif
2874 args.rval().setUndefined();
2875 return true;
2876 }
2877
DisplayName(JSContext * cx,unsigned argc,Value * vp)2878 static bool DisplayName(JSContext* cx, unsigned argc, Value* vp) {
2879 CallArgs args = CallArgsFromVp(argc, vp);
2880 if (!args.get(0).isObject() || !args[0].toObject().is<JSFunction>()) {
2881 RootedObject arg(cx, &args.callee());
2882 ReportUsageErrorASCII(cx, arg, "Must have one function argument");
2883 return false;
2884 }
2885
2886 JSFunction* fun = &args[0].toObject().as<JSFunction>();
2887 JSString* str = fun->displayAtom();
2888 args.rval().setString(str ? str : cx->runtime()->emptyString.ref());
2889 return true;
2890 }
2891
2892 class ShellAllocationMetadataBuilder : public AllocationMetadataBuilder {
2893 public:
ShellAllocationMetadataBuilder()2894 ShellAllocationMetadataBuilder() : AllocationMetadataBuilder() {}
2895
2896 virtual JSObject* build(JSContext* cx, HandleObject,
2897 AutoEnterOOMUnsafeRegion& oomUnsafe) const override;
2898
2899 static const ShellAllocationMetadataBuilder metadataBuilder;
2900 };
2901
build(JSContext * cx,HandleObject,AutoEnterOOMUnsafeRegion & oomUnsafe) const2902 JSObject* ShellAllocationMetadataBuilder::build(
2903 JSContext* cx, HandleObject, AutoEnterOOMUnsafeRegion& oomUnsafe) const {
2904 RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
2905 if (!obj) {
2906 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2907 }
2908
2909 RootedObject stack(cx, NewDenseEmptyArray(cx));
2910 if (!stack) {
2911 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2912 }
2913
2914 static int createdIndex = 0;
2915 createdIndex++;
2916
2917 if (!JS_DefineProperty(cx, obj, "index", createdIndex, 0)) {
2918 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2919 }
2920
2921 if (!JS_DefineProperty(cx, obj, "stack", stack, 0)) {
2922 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2923 }
2924
2925 int stackIndex = 0;
2926 RootedId id(cx);
2927 RootedValue callee(cx);
2928 for (NonBuiltinScriptFrameIter iter(cx); !iter.done(); ++iter) {
2929 if (iter.isFunctionFrame() && iter.compartment() == cx->compartment()) {
2930 id = INT_TO_JSID(stackIndex);
2931 RootedObject callee(cx, iter.callee(cx));
2932 if (!JS_DefinePropertyById(cx, stack, id, callee, 0)) {
2933 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
2934 }
2935 stackIndex++;
2936 }
2937 }
2938
2939 return obj;
2940 }
2941
2942 const ShellAllocationMetadataBuilder
2943 ShellAllocationMetadataBuilder::metadataBuilder;
2944
EnableShellAllocationMetadataBuilder(JSContext * cx,unsigned argc,Value * vp)2945 static bool EnableShellAllocationMetadataBuilder(JSContext* cx, unsigned argc,
2946 Value* vp) {
2947 CallArgs args = CallArgsFromVp(argc, vp);
2948
2949 SetAllocationMetadataBuilder(
2950 cx, &ShellAllocationMetadataBuilder::metadataBuilder);
2951
2952 args.rval().setUndefined();
2953 return true;
2954 }
2955
GetAllocationMetadata(JSContext * cx,unsigned argc,Value * vp)2956 static bool GetAllocationMetadata(JSContext* cx, unsigned argc, Value* vp) {
2957 CallArgs args = CallArgsFromVp(argc, vp);
2958 if (args.length() != 1 || !args[0].isObject()) {
2959 JS_ReportErrorASCII(cx, "Argument must be an object");
2960 return false;
2961 }
2962
2963 args.rval().setObjectOrNull(GetAllocationMetadata(&args[0].toObject()));
2964 return true;
2965 }
2966
testingFunc_bailout(JSContext * cx,unsigned argc,Value * vp)2967 static bool testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp) {
2968 CallArgs args = CallArgsFromVp(argc, vp);
2969
2970 // NOP when not in IonMonkey
2971 args.rval().setUndefined();
2972 return true;
2973 }
2974
testingFunc_bailAfter(JSContext * cx,unsigned argc,Value * vp)2975 static bool testingFunc_bailAfter(JSContext* cx, unsigned argc, Value* vp) {
2976 CallArgs args = CallArgsFromVp(argc, vp);
2977 if (args.length() != 1 || !args[0].isInt32() || args[0].toInt32() < 0) {
2978 JS_ReportErrorASCII(
2979 cx, "Argument must be a positive number that fits in an int32");
2980 return false;
2981 }
2982
2983 #ifdef DEBUG
2984 if (auto* jitRuntime = cx->runtime()->jitRuntime()) {
2985 uint32_t bailAfter = args[0].toInt32();
2986 bool enableBailAfter = bailAfter > 0;
2987 if (jitRuntime->ionBailAfterEnabled() != enableBailAfter) {
2988 // Force JIT code to be recompiled with (or without) instrumentation.
2989 ReleaseAllJITCode(cx->defaultFreeOp());
2990 jitRuntime->setIonBailAfterEnabled(enableBailAfter);
2991 }
2992 jitRuntime->setIonBailAfterCounter(bailAfter);
2993 }
2994 #endif
2995
2996 args.rval().setUndefined();
2997 return true;
2998 }
2999
3000 static constexpr unsigned JitWarmupResetLimit = 20;
3001 static_assert(JitWarmupResetLimit <=
3002 unsigned(JSScript::MutableFlags::WarmupResets_MASK),
3003 "JitWarmupResetLimit exceeds max value");
3004
testingFunc_inJit(JSContext * cx,unsigned argc,Value * vp)3005 static bool testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp) {
3006 CallArgs args = CallArgsFromVp(argc, vp);
3007
3008 if (!jit::IsBaselineJitEnabled(cx)) {
3009 return ReturnStringCopy(cx, args, "Baseline is disabled.");
3010 }
3011
3012 // Use frame iterator to inspect caller.
3013 FrameIter iter(cx);
3014
3015 // We may be invoked directly, not in a JS context, e.g. if inJit is added as
3016 // a callback on the event queue.
3017 if (iter.done()) {
3018 args.rval().setBoolean(false);
3019 return true;
3020 }
3021
3022 if (iter.hasScript()) {
3023 // Detect repeated attempts to compile, resetting the counter if inJit
3024 // succeeds. Note: This script may have be inlined into its caller.
3025 if (iter.isJSJit()) {
3026 iter.script()->resetWarmUpResetCounter();
3027 } else if (iter.script()->getWarmUpResetCount() >= JitWarmupResetLimit) {
3028 return ReturnStringCopy(
3029 cx, args, "Compilation is being repeatedly prevented. Giving up.");
3030 }
3031 }
3032
3033 // Returns true for any JIT (including WASM).
3034 MOZ_ASSERT_IF(iter.isJSJit(), cx->currentlyRunningInJit());
3035 args.rval().setBoolean(cx->currentlyRunningInJit());
3036 return true;
3037 }
3038
testingFunc_inIon(JSContext * cx,unsigned argc,Value * vp)3039 static bool testingFunc_inIon(JSContext* cx, unsigned argc, Value* vp) {
3040 CallArgs args = CallArgsFromVp(argc, vp);
3041
3042 if (!jit::IsIonEnabled(cx)) {
3043 return ReturnStringCopy(cx, args, "Ion is disabled.");
3044 }
3045
3046 // Use frame iterator to inspect caller.
3047 FrameIter iter(cx);
3048
3049 // We may be invoked directly, not in a JS context, e.g. if inJson is added as
3050 // a callback on the event queue.
3051 if (iter.done()) {
3052 args.rval().setBoolean(false);
3053 return true;
3054 }
3055
3056 if (iter.hasScript()) {
3057 // Detect repeated attempts to compile, resetting the counter if inIon
3058 // succeeds. Note: This script may have be inlined into its caller.
3059 if (iter.isIon()) {
3060 iter.script()->resetWarmUpResetCounter();
3061 } else if (!iter.script()->canIonCompile()) {
3062 return ReturnStringCopy(cx, args, "Unable to Ion-compile this script.");
3063 } else if (iter.script()->getWarmUpResetCount() >= JitWarmupResetLimit) {
3064 return ReturnStringCopy(
3065 cx, args, "Compilation is being repeatedly prevented. Giving up.");
3066 }
3067 }
3068
3069 args.rval().setBoolean(iter.isIon());
3070 return true;
3071 }
3072
testingFunc_assertFloat32(JSContext * cx,unsigned argc,Value * vp)3073 bool js::testingFunc_assertFloat32(JSContext* cx, unsigned argc, Value* vp) {
3074 CallArgs args = CallArgsFromVp(argc, vp);
3075 if (args.length() != 2) {
3076 JS_ReportErrorASCII(cx, "Expects only 2 arguments");
3077 return false;
3078 }
3079
3080 // NOP when not in IonMonkey
3081 args.rval().setUndefined();
3082 return true;
3083 }
3084
TestingFunc_assertJitStackInvariants(JSContext * cx,unsigned argc,Value * vp)3085 static bool TestingFunc_assertJitStackInvariants(JSContext* cx, unsigned argc,
3086 Value* vp) {
3087 CallArgs args = CallArgsFromVp(argc, vp);
3088
3089 jit::AssertJitStackInvariants(cx);
3090 args.rval().setUndefined();
3091 return true;
3092 }
3093
testingFunc_assertRecoveredOnBailout(JSContext * cx,unsigned argc,Value * vp)3094 bool js::testingFunc_assertRecoveredOnBailout(JSContext* cx, unsigned argc,
3095 Value* vp) {
3096 CallArgs args = CallArgsFromVp(argc, vp);
3097 if (args.length() != 2) {
3098 JS_ReportErrorASCII(cx, "Expects only 2 arguments");
3099 return false;
3100 }
3101
3102 // NOP when not in IonMonkey
3103 args.rval().setUndefined();
3104 return true;
3105 }
3106
GetJitCompilerOptions(JSContext * cx,unsigned argc,Value * vp)3107 static bool GetJitCompilerOptions(JSContext* cx, unsigned argc, Value* vp) {
3108 CallArgs args = CallArgsFromVp(argc, vp);
3109 RootedObject info(cx, JS_NewPlainObject(cx));
3110 if (!info) {
3111 return false;
3112 }
3113
3114 uint32_t intValue = 0;
3115 RootedValue value(cx);
3116
3117 #define JIT_COMPILER_MATCH(key, string) \
3118 opt = JSJITCOMPILER_##key; \
3119 if (JS_GetGlobalJitCompilerOption(cx, opt, &intValue)) { \
3120 value.setInt32(intValue); \
3121 if (!JS_SetProperty(cx, info, string, value)) return false; \
3122 }
3123
3124 JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION;
3125 JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH);
3126 #undef JIT_COMPILER_MATCH
3127
3128 args.rval().setObject(*info);
3129 return true;
3130 }
3131
SetIonCheckGraphCoherency(JSContext * cx,unsigned argc,Value * vp)3132 static bool SetIonCheckGraphCoherency(JSContext* cx, unsigned argc, Value* vp) {
3133 CallArgs args = CallArgsFromVp(argc, vp);
3134 jit::JitOptions.checkGraphConsistency = ToBoolean(args.get(0));
3135 args.rval().setUndefined();
3136 return true;
3137 }
3138
3139 // A JSObject that holds structured clone data, similar to the C++ class
3140 // JSAutoStructuredCloneBuffer.
3141 class CloneBufferObject : public NativeObject {
3142 static const JSPropertySpec props_[3];
3143
3144 static const size_t DATA_SLOT = 0;
3145 static const size_t SYNTHETIC_SLOT = 1;
3146 static const size_t NUM_SLOTS = 2;
3147
3148 public:
3149 static const JSClass class_;
3150
Create(JSContext * cx)3151 static CloneBufferObject* Create(JSContext* cx) {
3152 RootedObject obj(cx, JS_NewObject(cx, &class_));
3153 if (!obj) {
3154 return nullptr;
3155 }
3156 obj->as<CloneBufferObject>().setReservedSlot(DATA_SLOT,
3157 PrivateValue(nullptr));
3158 obj->as<CloneBufferObject>().setReservedSlot(SYNTHETIC_SLOT,
3159 BooleanValue(false));
3160
3161 if (!JS_DefineProperties(cx, obj, props_)) {
3162 return nullptr;
3163 }
3164
3165 return &obj->as<CloneBufferObject>();
3166 }
3167
Create(JSContext * cx,JSAutoStructuredCloneBuffer * buffer)3168 static CloneBufferObject* Create(JSContext* cx,
3169 JSAutoStructuredCloneBuffer* buffer) {
3170 Rooted<CloneBufferObject*> obj(cx, Create(cx));
3171 if (!obj) {
3172 return nullptr;
3173 }
3174 auto data = js::MakeUnique<JSStructuredCloneData>(buffer->scope());
3175 if (!data) {
3176 ReportOutOfMemory(cx);
3177 return nullptr;
3178 }
3179 buffer->steal(data.get());
3180 obj->setData(data.release(), false);
3181 return obj;
3182 }
3183
data() const3184 JSStructuredCloneData* data() const {
3185 return static_cast<JSStructuredCloneData*>(
3186 getReservedSlot(DATA_SLOT).toPrivate());
3187 }
3188
isSynthetic() const3189 bool isSynthetic() const {
3190 return getReservedSlot(SYNTHETIC_SLOT).toBoolean();
3191 }
3192
setData(JSStructuredCloneData * aData,bool synthetic)3193 void setData(JSStructuredCloneData* aData, bool synthetic) {
3194 MOZ_ASSERT(!data());
3195 setReservedSlot(DATA_SLOT, PrivateValue(aData));
3196 setReservedSlot(SYNTHETIC_SLOT, BooleanValue(synthetic));
3197 }
3198
3199 // Discard an owned clone buffer.
discard()3200 void discard() {
3201 js_delete(data());
3202 setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
3203 }
3204
setCloneBuffer_impl(JSContext * cx,const CallArgs & args)3205 static bool setCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
3206 Rooted<CloneBufferObject*> obj(
3207 cx, &args.thisv().toObject().as<CloneBufferObject>());
3208
3209 const char* data = nullptr;
3210 UniqueChars dataOwner;
3211 uint32_t nbytes;
3212
3213 if (args.get(0).isObject() && args[0].toObject().is<ArrayBufferObject>()) {
3214 ArrayBufferObject* buffer = &args[0].toObject().as<ArrayBufferObject>();
3215 bool isSharedMemory;
3216 uint8_t* dataBytes = nullptr;
3217 JS::GetArrayBufferLengthAndData(buffer, &nbytes, &isSharedMemory,
3218 &dataBytes);
3219 MOZ_ASSERT(!isSharedMemory);
3220 data = reinterpret_cast<char*>(dataBytes);
3221 } else {
3222 JSString* str = JS::ToString(cx, args.get(0));
3223 if (!str) {
3224 return false;
3225 }
3226 dataOwner = JS_EncodeStringToLatin1(cx, str);
3227 if (!dataOwner) {
3228 return false;
3229 }
3230 data = dataOwner.get();
3231 nbytes = JS_GetStringLength(str);
3232 }
3233
3234 if (nbytes == 0 || (nbytes % sizeof(uint64_t) != 0)) {
3235 JS_ReportErrorASCII(cx, "Invalid length for clonebuffer data");
3236 return false;
3237 }
3238
3239 auto buf = js::MakeUnique<JSStructuredCloneData>(
3240 JS::StructuredCloneScope::DifferentProcess);
3241 if (!buf || !buf->Init(nbytes)) {
3242 ReportOutOfMemory(cx);
3243 return false;
3244 }
3245
3246 MOZ_ALWAYS_TRUE(buf->AppendBytes(data, nbytes));
3247 obj->discard();
3248 obj->setData(buf.release(), true);
3249
3250 args.rval().setUndefined();
3251 return true;
3252 }
3253
is(HandleValue v)3254 static bool is(HandleValue v) {
3255 return v.isObject() && v.toObject().is<CloneBufferObject>();
3256 }
3257
setCloneBuffer(JSContext * cx,unsigned int argc,JS::Value * vp)3258 static bool setCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
3259 CallArgs args = CallArgsFromVp(argc, vp);
3260 return CallNonGenericMethod<is, setCloneBuffer_impl>(cx, args);
3261 }
3262
getData(JSContext * cx,Handle<CloneBufferObject * > obj,JSStructuredCloneData ** data)3263 static bool getData(JSContext* cx, Handle<CloneBufferObject*> obj,
3264 JSStructuredCloneData** data) {
3265 if (!obj->data()) {
3266 *data = nullptr;
3267 return true;
3268 }
3269
3270 bool hasTransferable;
3271 if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable)) {
3272 return false;
3273 }
3274
3275 if (hasTransferable) {
3276 JS_ReportErrorASCII(
3277 cx, "cannot retrieve structured clone buffer with transferables");
3278 return false;
3279 }
3280
3281 *data = obj->data();
3282 return true;
3283 }
3284
getCloneBuffer_impl(JSContext * cx,const CallArgs & args)3285 static bool getCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
3286 Rooted<CloneBufferObject*> obj(
3287 cx, &args.thisv().toObject().as<CloneBufferObject>());
3288 MOZ_ASSERT(args.length() == 0);
3289
3290 JSStructuredCloneData* data;
3291 if (!getData(cx, obj, &data)) {
3292 return false;
3293 }
3294
3295 size_t size = data->Size();
3296 UniqueChars buffer(js_pod_malloc<char>(size));
3297 if (!buffer) {
3298 ReportOutOfMemory(cx);
3299 return false;
3300 }
3301 auto iter = data->Start();
3302 if (!data->ReadBytes(iter, buffer.get(), size)) {
3303 ReportOutOfMemory(cx);
3304 return false;
3305 }
3306 JSString* str = JS_NewStringCopyN(cx, buffer.get(), size);
3307 if (!str) {
3308 return false;
3309 }
3310 args.rval().setString(str);
3311 return true;
3312 }
3313
getCloneBuffer(JSContext * cx,unsigned int argc,JS::Value * vp)3314 static bool getCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
3315 CallArgs args = CallArgsFromVp(argc, vp);
3316 return CallNonGenericMethod<is, getCloneBuffer_impl>(cx, args);
3317 }
3318
getCloneBufferAsArrayBuffer_impl(JSContext * cx,const CallArgs & args)3319 static bool getCloneBufferAsArrayBuffer_impl(JSContext* cx,
3320 const CallArgs& args) {
3321 Rooted<CloneBufferObject*> obj(
3322 cx, &args.thisv().toObject().as<CloneBufferObject>());
3323 MOZ_ASSERT(args.length() == 0);
3324
3325 JSStructuredCloneData* data;
3326 if (!getData(cx, obj, &data)) {
3327 return false;
3328 }
3329
3330 size_t size = data->Size();
3331 UniqueChars buffer(js_pod_malloc<char>(size));
3332 if (!buffer) {
3333 ReportOutOfMemory(cx);
3334 return false;
3335 }
3336 auto iter = data->Start();
3337 if (!data->ReadBytes(iter, buffer.get(), size)) {
3338 ReportOutOfMemory(cx);
3339 return false;
3340 }
3341
3342 auto* rawBuffer = buffer.release();
3343 JSObject* arrayBuffer = JS::NewArrayBufferWithContents(cx, size, rawBuffer);
3344 if (!arrayBuffer) {
3345 js_free(rawBuffer);
3346 return false;
3347 }
3348
3349 args.rval().setObject(*arrayBuffer);
3350 return true;
3351 }
3352
getCloneBufferAsArrayBuffer(JSContext * cx,unsigned int argc,JS::Value * vp)3353 static bool getCloneBufferAsArrayBuffer(JSContext* cx, unsigned int argc,
3354 JS::Value* vp) {
3355 CallArgs args = CallArgsFromVp(argc, vp);
3356 return CallNonGenericMethod<is, getCloneBufferAsArrayBuffer_impl>(cx, args);
3357 }
3358
Finalize(JSFreeOp * fop,JSObject * obj)3359 static void Finalize(JSFreeOp* fop, JSObject* obj) {
3360 obj->as<CloneBufferObject>().discard();
3361 }
3362 };
3363
3364 static const JSClassOps CloneBufferObjectClassOps = {
3365 nullptr, // addProperty
3366 nullptr, // delProperty
3367 nullptr, // enumerate
3368 nullptr, // newEnumerate
3369 nullptr, // resolve
3370 nullptr, // mayResolve
3371 CloneBufferObject::Finalize, // finalize
3372 nullptr, // call
3373 nullptr, // hasInstance
3374 nullptr, // construct
3375 nullptr, // trace
3376 };
3377
3378 const JSClass CloneBufferObject::class_ = {
3379 "CloneBuffer",
3380 JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS) |
3381 JSCLASS_FOREGROUND_FINALIZE,
3382 &CloneBufferObjectClassOps};
3383
3384 const JSPropertySpec CloneBufferObject::props_[] = {
3385 JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0),
3386 JS_PSGS("arraybuffer", getCloneBufferAsArrayBuffer, setCloneBuffer, 0),
3387 JS_PS_END};
3388
ParseCloneScope(JSContext * cx,HandleString str)3389 static mozilla::Maybe<JS::StructuredCloneScope> ParseCloneScope(
3390 JSContext* cx, HandleString str) {
3391 mozilla::Maybe<JS::StructuredCloneScope> scope;
3392
3393 JSLinearString* scopeStr = str->ensureLinear(cx);
3394 if (!scopeStr) {
3395 return scope;
3396 }
3397
3398 if (StringEqualsLiteral(scopeStr, "SameProcess")) {
3399 scope.emplace(JS::StructuredCloneScope::SameProcess);
3400 } else if (StringEqualsLiteral(scopeStr, "DifferentProcess")) {
3401 scope.emplace(JS::StructuredCloneScope::DifferentProcess);
3402 } else if (StringEqualsLiteral(scopeStr, "DifferentProcessForIndexedDB")) {
3403 scope.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB);
3404 }
3405
3406 return scope;
3407 }
3408
testingFunc_serialize(JSContext * cx,unsigned argc,Value * vp)3409 bool js::testingFunc_serialize(JSContext* cx, unsigned argc, Value* vp) {
3410 CallArgs args = CallArgsFromVp(argc, vp);
3411
3412 mozilla::Maybe<JSAutoStructuredCloneBuffer> clonebuf;
3413 JS::CloneDataPolicy policy;
3414
3415 if (!args.get(2).isUndefined()) {
3416 RootedObject opts(cx, ToObject(cx, args.get(2)));
3417 if (!opts) {
3418 return false;
3419 }
3420
3421 RootedValue v(cx);
3422 if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v)) {
3423 return false;
3424 }
3425
3426 if (!v.isUndefined()) {
3427 JSString* str = JS::ToString(cx, v);
3428 if (!str) {
3429 return false;
3430 }
3431 JSLinearString* poli = str->ensureLinear(cx);
3432 if (!poli) {
3433 return false;
3434 }
3435
3436 if (StringEqualsLiteral(poli, "allow")) {
3437 policy.allowSharedMemoryObjects();
3438 policy.allowIntraClusterClonableSharedObjects();
3439 } else if (StringEqualsLiteral(poli, "deny")) {
3440 // default
3441 } else {
3442 JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'");
3443 return false;
3444 }
3445 }
3446
3447 if (!JS_GetProperty(cx, opts, "scope", &v)) {
3448 return false;
3449 }
3450
3451 if (!v.isUndefined()) {
3452 RootedString str(cx, JS::ToString(cx, v));
3453 if (!str) {
3454 return false;
3455 }
3456 auto scope = ParseCloneScope(cx, str);
3457 if (!scope) {
3458 JS_ReportErrorASCII(cx, "Invalid structured clone scope");
3459 return false;
3460 }
3461 clonebuf.emplace(*scope, nullptr, nullptr);
3462 }
3463 }
3464
3465 if (!clonebuf) {
3466 clonebuf.emplace(JS::StructuredCloneScope::SameProcess, nullptr, nullptr);
3467 }
3468
3469 if (!clonebuf->write(cx, args.get(0), args.get(1), policy)) {
3470 return false;
3471 }
3472
3473 RootedObject obj(cx, CloneBufferObject::Create(cx, clonebuf.ptr()));
3474 if (!obj) {
3475 return false;
3476 }
3477
3478 args.rval().setObject(*obj);
3479 return true;
3480 }
3481
Deserialize(JSContext * cx,unsigned argc,Value * vp)3482 static bool Deserialize(JSContext* cx, unsigned argc, Value* vp) {
3483 CallArgs args = CallArgsFromVp(argc, vp);
3484
3485 if (!args.get(0).isObject() || !args[0].toObject().is<CloneBufferObject>()) {
3486 JS_ReportErrorASCII(cx, "deserialize requires a clonebuffer argument");
3487 return false;
3488 }
3489 Rooted<CloneBufferObject*> obj(cx,
3490 &args[0].toObject().as<CloneBufferObject>());
3491
3492 JS::CloneDataPolicy policy;
3493 JS::StructuredCloneScope scope =
3494 obj->isSynthetic() ? JS::StructuredCloneScope::DifferentProcess
3495 : JS::StructuredCloneScope::SameProcess;
3496 if (args.get(1).isObject()) {
3497 RootedObject opts(cx, &args[1].toObject());
3498 if (!opts) {
3499 return false;
3500 }
3501
3502 RootedValue v(cx);
3503 if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v)) {
3504 return false;
3505 }
3506
3507 if (!v.isUndefined()) {
3508 JSString* str = JS::ToString(cx, v);
3509 if (!str) {
3510 return false;
3511 }
3512 JSLinearString* poli = str->ensureLinear(cx);
3513 if (!poli) {
3514 return false;
3515 }
3516
3517 if (StringEqualsLiteral(poli, "allow")) {
3518 policy.allowSharedMemoryObjects();
3519 policy.allowIntraClusterClonableSharedObjects();
3520 } else if (StringEqualsLiteral(poli, "deny")) {
3521 // default
3522 } else {
3523 JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'");
3524 return false;
3525 }
3526 }
3527
3528 if (!JS_GetProperty(cx, opts, "scope", &v)) {
3529 return false;
3530 }
3531
3532 if (!v.isUndefined()) {
3533 RootedString str(cx, JS::ToString(cx, v));
3534 if (!str) {
3535 return false;
3536 }
3537 auto maybeScope = ParseCloneScope(cx, str);
3538 if (!maybeScope) {
3539 JS_ReportErrorASCII(cx, "Invalid structured clone scope");
3540 return false;
3541 }
3542
3543 if (*maybeScope < scope) {
3544 JS_ReportErrorASCII(cx,
3545 "Cannot use less restrictive scope "
3546 "than the deserialized clone buffer's scope");
3547 return false;
3548 }
3549
3550 scope = *maybeScope;
3551 }
3552 }
3553
3554 // Clone buffer was already consumed?
3555 if (!obj->data()) {
3556 JS_ReportErrorASCII(cx,
3557 "deserialize given invalid clone buffer "
3558 "(transferables already consumed?)");
3559 return false;
3560 }
3561
3562 bool hasTransferable;
3563 if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable)) {
3564 return false;
3565 }
3566
3567 RootedValue deserialized(cx);
3568 if (!JS_ReadStructuredClone(cx, *obj->data(), JS_STRUCTURED_CLONE_VERSION,
3569 scope, &deserialized, policy, nullptr, nullptr)) {
3570 return false;
3571 }
3572 args.rval().set(deserialized);
3573
3574 // Consume any clone buffer with transferables; throw an error if it is
3575 // deserialized again.
3576 if (hasTransferable) {
3577 obj->discard();
3578 }
3579
3580 return true;
3581 }
3582
DetachArrayBuffer(JSContext * cx,unsigned argc,Value * vp)3583 static bool DetachArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
3584 CallArgs args = CallArgsFromVp(argc, vp);
3585
3586 if (args.length() != 1) {
3587 JS_ReportErrorASCII(cx, "detachArrayBuffer() requires a single argument");
3588 return false;
3589 }
3590
3591 if (!args[0].isObject()) {
3592 JS_ReportErrorASCII(cx, "detachArrayBuffer must be passed an object");
3593 return false;
3594 }
3595
3596 RootedObject obj(cx, &args[0].toObject());
3597 if (!JS::DetachArrayBuffer(cx, obj)) {
3598 return false;
3599 }
3600
3601 args.rval().setUndefined();
3602 return true;
3603 }
3604
HelperThreadCount(JSContext * cx,unsigned argc,Value * vp)3605 static bool HelperThreadCount(JSContext* cx, unsigned argc, Value* vp) {
3606 CallArgs args = CallArgsFromVp(argc, vp);
3607 #ifdef JS_MORE_DETERMINISTIC
3608 // Always return 0 to get consistent output with and without --no-threads.
3609 args.rval().setInt32(0);
3610 #else
3611 if (CanUseExtraThreads()) {
3612 args.rval().setInt32(HelperThreadState().threadCount);
3613 } else {
3614 args.rval().setInt32(0);
3615 }
3616 #endif
3617 return true;
3618 }
3619
EnableShapeConsistencyChecks(JSContext * cx,unsigned argc,Value * vp)3620 static bool EnableShapeConsistencyChecks(JSContext* cx, unsigned argc,
3621 Value* vp) {
3622 CallArgs args = CallArgsFromVp(argc, vp);
3623 #ifdef DEBUG
3624 NativeObject::enableShapeConsistencyChecks();
3625 #endif
3626 args.rval().setUndefined();
3627 return true;
3628 }
3629
3630 #ifdef JS_TRACE_LOGGING
EnableTraceLogger(JSContext * cx,unsigned argc,Value * vp)3631 static bool EnableTraceLogger(JSContext* cx, unsigned argc, Value* vp) {
3632 CallArgs args = CallArgsFromVp(argc, vp);
3633 TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
3634 if (!TraceLoggerEnable(logger, cx)) {
3635 return false;
3636 }
3637
3638 args.rval().setUndefined();
3639 return true;
3640 }
3641
DisableTraceLogger(JSContext * cx,unsigned argc,Value * vp)3642 static bool DisableTraceLogger(JSContext* cx, unsigned argc, Value* vp) {
3643 CallArgs args = CallArgsFromVp(argc, vp);
3644 TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
3645 args.rval().setBoolean(TraceLoggerDisable(logger));
3646
3647 return true;
3648 }
3649 #endif
3650
3651 #if defined(DEBUG) || defined(JS_JITSPEW)
DumpObject(JSContext * cx,unsigned argc,Value * vp)3652 static bool DumpObject(JSContext* cx, unsigned argc, Value* vp) {
3653 CallArgs args = CallArgsFromVp(argc, vp);
3654 RootedObject obj(cx, ToObject(cx, args.get(0)));
3655 if (!obj) {
3656 return false;
3657 }
3658
3659 DumpObject(obj);
3660
3661 args.rval().setUndefined();
3662 return true;
3663 }
3664 #endif
3665
SharedMemoryEnabled(JSContext * cx,unsigned argc,Value * vp)3666 static bool SharedMemoryEnabled(JSContext* cx, unsigned argc, Value* vp) {
3667 CallArgs args = CallArgsFromVp(argc, vp);
3668 args.rval().setBoolean(
3669 cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
3670 return true;
3671 }
3672
SharedArrayRawBufferCount(JSContext * cx,unsigned argc,Value * vp)3673 static bool SharedArrayRawBufferCount(JSContext* cx, unsigned argc, Value* vp) {
3674 CallArgs args = CallArgsFromVp(argc, vp);
3675 args.rval().setInt32(LiveMappedBufferCount());
3676 return true;
3677 }
3678
SharedArrayRawBufferRefcount(JSContext * cx,unsigned argc,Value * vp)3679 static bool SharedArrayRawBufferRefcount(JSContext* cx, unsigned argc,
3680 Value* vp) {
3681 CallArgs args = CallArgsFromVp(argc, vp);
3682 if (args.length() != 1 || !args[0].isObject()) {
3683 JS_ReportErrorASCII(cx, "Expected SharedArrayBuffer object");
3684 return false;
3685 }
3686 RootedObject obj(cx, &args[0].toObject());
3687 if (!obj->is<SharedArrayBufferObject>()) {
3688 JS_ReportErrorASCII(cx, "Expected SharedArrayBuffer object");
3689 return false;
3690 }
3691 args.rval().setInt32(
3692 obj->as<SharedArrayBufferObject>().rawBufferObject()->refcount());
3693 return true;
3694 }
3695
3696 #ifdef NIGHTLY_BUILD
ObjectAddress(JSContext * cx,unsigned argc,Value * vp)3697 static bool ObjectAddress(JSContext* cx, unsigned argc, Value* vp) {
3698 CallArgs args = CallArgsFromVp(argc, vp);
3699 if (args.length() != 1) {
3700 RootedObject callee(cx, &args.callee());
3701 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
3702 return false;
3703 }
3704 if (!args[0].isObject()) {
3705 RootedObject callee(cx, &args.callee());
3706 ReportUsageErrorASCII(cx, callee, "Expected object");
3707 return false;
3708 }
3709
3710 # ifdef JS_MORE_DETERMINISTIC
3711 args.rval().setInt32(0);
3712 return true;
3713 # else
3714 void* ptr = js::UncheckedUnwrap(&args[0].toObject(), true);
3715 char buffer[64];
3716 SprintfLiteral(buffer, "%p", ptr);
3717
3718 return ReturnStringCopy(cx, args, buffer);
3719 # endif
3720 }
3721
SharedAddress(JSContext * cx,unsigned argc,Value * vp)3722 static bool SharedAddress(JSContext* cx, unsigned argc, Value* vp) {
3723 CallArgs args = CallArgsFromVp(argc, vp);
3724 if (args.length() != 1) {
3725 RootedObject callee(cx, &args.callee());
3726 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
3727 return false;
3728 }
3729 if (!args[0].isObject()) {
3730 RootedObject callee(cx, &args.callee());
3731 ReportUsageErrorASCII(cx, callee, "Expected object");
3732 return false;
3733 }
3734
3735 # ifdef JS_MORE_DETERMINISTIC
3736 args.rval().setString(cx->staticStrings().getUint(0));
3737 # else
3738 RootedObject obj(cx, CheckedUnwrapStatic(&args[0].toObject()));
3739 if (!obj) {
3740 ReportAccessDenied(cx);
3741 return false;
3742 }
3743 if (!obj->is<SharedArrayBufferObject>()) {
3744 JS_ReportErrorASCII(cx, "Argument must be a SharedArrayBuffer");
3745 return false;
3746 }
3747 char buffer[64];
3748 uint32_t nchar = SprintfLiteral(
3749 buffer, "%p",
3750 obj->as<SharedArrayBufferObject>().dataPointerShared().unwrap(
3751 /*safeish*/));
3752
3753 JSString* str = JS_NewStringCopyN(cx, buffer, nchar);
3754 if (!str) {
3755 return false;
3756 }
3757
3758 args.rval().setString(str);
3759 # endif
3760
3761 return true;
3762 }
3763 #endif
3764
DumpBacktrace(JSContext * cx,unsigned argc,Value * vp)3765 static bool DumpBacktrace(JSContext* cx, unsigned argc, Value* vp) {
3766 CallArgs args = CallArgsFromVp(argc, vp);
3767 DumpBacktrace(cx);
3768 args.rval().setUndefined();
3769 return true;
3770 }
3771
GetBacktrace(JSContext * cx,unsigned argc,Value * vp)3772 static bool GetBacktrace(JSContext* cx, unsigned argc, Value* vp) {
3773 CallArgs args = CallArgsFromVp(argc, vp);
3774
3775 bool showArgs = false;
3776 bool showLocals = false;
3777 bool showThisProps = false;
3778
3779 if (args.length() > 1) {
3780 RootedObject callee(cx, &args.callee());
3781 ReportUsageErrorASCII(cx, callee, "Too many arguments");
3782 return false;
3783 }
3784
3785 if (args.length() == 1) {
3786 RootedObject cfg(cx, ToObject(cx, args[0]));
3787 if (!cfg) {
3788 return false;
3789 }
3790 RootedValue v(cx);
3791
3792 if (!JS_GetProperty(cx, cfg, "args", &v)) {
3793 return false;
3794 }
3795 showArgs = ToBoolean(v);
3796
3797 if (!JS_GetProperty(cx, cfg, "locals", &v)) {
3798 return false;
3799 }
3800 showLocals = ToBoolean(v);
3801
3802 if (!JS_GetProperty(cx, cfg, "thisprops", &v)) {
3803 return false;
3804 }
3805 showThisProps = ToBoolean(v);
3806 }
3807
3808 JS::UniqueChars buf =
3809 JS::FormatStackDump(cx, showArgs, showLocals, showThisProps);
3810 if (!buf) {
3811 return false;
3812 }
3813
3814 JS::ConstUTF8CharsZ utf8chars(buf.get(), strlen(buf.get()));
3815 JSString* str = NewStringCopyUTF8Z<CanGC>(cx, utf8chars);
3816 if (!str) {
3817 return false;
3818 }
3819
3820 args.rval().setString(str);
3821 return true;
3822 }
3823
ReportOutOfMemory(JSContext * cx,unsigned argc,Value * vp)3824 static bool ReportOutOfMemory(JSContext* cx, unsigned argc, Value* vp) {
3825 CallArgs args = CallArgsFromVp(argc, vp);
3826 JS_ReportOutOfMemory(cx);
3827 cx->clearPendingException();
3828 args.rval().setUndefined();
3829 return true;
3830 }
3831
ThrowOutOfMemory(JSContext * cx,unsigned argc,Value * vp)3832 static bool ThrowOutOfMemory(JSContext* cx, unsigned argc, Value* vp) {
3833 JS_ReportOutOfMemory(cx);
3834 return false;
3835 }
3836
ReportLargeAllocationFailure(JSContext * cx,unsigned argc,Value * vp)3837 static bool ReportLargeAllocationFailure(JSContext* cx, unsigned argc,
3838 Value* vp) {
3839 CallArgs args = CallArgsFromVp(argc, vp);
3840 void* buf = cx->runtime()->onOutOfMemoryCanGC(
3841 AllocFunction::Malloc, js::MallocArena, JSRuntime::LARGE_ALLOCATION);
3842 js_free(buf);
3843 args.rval().setUndefined();
3844 return true;
3845 }
3846
3847 namespace heaptools {
3848
3849 using EdgeName = UniqueTwoByteChars;
3850
3851 // An edge to a node from its predecessor in a path through the graph.
3852 class BackEdge {
3853 // The node from which this edge starts.
3854 JS::ubi::Node predecessor_;
3855
3856 // The name of this edge.
3857 EdgeName name_;
3858
3859 public:
BackEdge()3860 BackEdge() : name_(nullptr) {}
3861 // Construct an initialized back edge, taking ownership of |name|.
BackEdge(JS::ubi::Node predecessor,EdgeName name)3862 BackEdge(JS::ubi::Node predecessor, EdgeName name)
3863 : predecessor_(predecessor), name_(std::move(name)) {}
BackEdge(BackEdge && rhs)3864 BackEdge(BackEdge&& rhs)
3865 : predecessor_(rhs.predecessor_), name_(std::move(rhs.name_)) {}
operator =(BackEdge && rhs)3866 BackEdge& operator=(BackEdge&& rhs) {
3867 MOZ_ASSERT(&rhs != this);
3868 this->~BackEdge();
3869 new (this) BackEdge(std::move(rhs));
3870 return *this;
3871 }
3872
forgetName()3873 EdgeName forgetName() { return std::move(name_); }
predecessor() const3874 JS::ubi::Node predecessor() const { return predecessor_; }
3875
3876 private:
3877 // No copy constructor or copying assignment.
3878 BackEdge(const BackEdge&) = delete;
3879 BackEdge& operator=(const BackEdge&) = delete;
3880 };
3881
3882 // A path-finding handler class for use with JS::ubi::BreadthFirst.
3883 struct FindPathHandler {
3884 using NodeData = BackEdge;
3885 using Traversal = JS::ubi::BreadthFirst<FindPathHandler>;
3886
FindPathHandlerheaptools::FindPathHandler3887 FindPathHandler(JSContext* cx, JS::ubi::Node start, JS::ubi::Node target,
3888 MutableHandle<GCVector<Value>> nodes, Vector<EdgeName>& edges)
3889 : cx(cx),
3890 start(start),
3891 target(target),
3892 foundPath(false),
3893 nodes(nodes),
3894 edges(edges) {}
3895
operator ()heaptools::FindPathHandler3896 bool operator()(Traversal& traversal, JS::ubi::Node origin,
3897 const JS::ubi::Edge& edge, BackEdge* backEdge, bool first) {
3898 // We take care of each node the first time we visit it, so there's
3899 // nothing to be done on subsequent visits.
3900 if (!first) {
3901 return true;
3902 }
3903
3904 // Record how we reached this node. This is the last edge on a
3905 // shortest path to this node.
3906 EdgeName edgeName =
3907 DuplicateStringToArena(js::StringBufferArena, cx, edge.name.get());
3908 if (!edgeName) {
3909 return false;
3910 }
3911 *backEdge = BackEdge(origin, std::move(edgeName));
3912
3913 // Have we reached our final target node?
3914 if (edge.referent == target) {
3915 // Record the path that got us here, which must be a shortest path.
3916 if (!recordPath(traversal, backEdge)) {
3917 return false;
3918 }
3919 foundPath = true;
3920 traversal.stop();
3921 }
3922
3923 return true;
3924 }
3925
3926 // We've found a path to our target. Walk the backlinks to produce the
3927 // (reversed) path, saving the path in |nodes| and |edges|. |nodes| is
3928 // rooted, so it can hold the path's nodes as we leave the scope of
3929 // the AutoCheckCannotGC. Note that nodes are added to |visited| after we
3930 // return from operator() so we have to pass the target BackEdge* to this
3931 // function.
recordPathheaptools::FindPathHandler3932 bool recordPath(Traversal& traversal, BackEdge* targetBackEdge) {
3933 JS::ubi::Node here = target;
3934
3935 do {
3936 BackEdge* backEdge = targetBackEdge;
3937 if (here != target) {
3938 Traversal::NodeMap::Ptr p = traversal.visited.lookup(here);
3939 MOZ_ASSERT(p);
3940 backEdge = &p->value();
3941 }
3942 JS::ubi::Node predecessor = backEdge->predecessor();
3943 if (!nodes.append(predecessor.exposeToJS()) ||
3944 !edges.append(backEdge->forgetName())) {
3945 return false;
3946 }
3947 here = predecessor;
3948 } while (here != start);
3949
3950 return true;
3951 }
3952
3953 JSContext* cx;
3954
3955 // The node we're starting from.
3956 JS::ubi::Node start;
3957
3958 // The node we're looking for.
3959 JS::ubi::Node target;
3960
3961 // True if we found a path to target, false if we didn't.
3962 bool foundPath;
3963
3964 // The nodes and edges of the path --- should we find one. The path is
3965 // stored in reverse order, because that's how it's easiest for us to
3966 // construct it:
3967 // - edges[i] is the name of the edge from nodes[i] to nodes[i-1].
3968 // - edges[0] is the name of the edge from nodes[0] to the target.
3969 // - The last node, nodes[n-1], is the start node.
3970 MutableHandle<GCVector<Value>> nodes;
3971 Vector<EdgeName>& edges;
3972 };
3973
3974 } // namespace heaptools
3975
FindPath(JSContext * cx,unsigned argc,Value * vp)3976 static bool FindPath(JSContext* cx, unsigned argc, Value* vp) {
3977 CallArgs args = CallArgsFromVp(argc, vp);
3978 if (!args.requireAtLeast(cx, "findPath", 2)) {
3979 return false;
3980 }
3981
3982 // We don't ToString non-objects given as 'start' or 'target', because this
3983 // test is all about object identity, and ToString doesn't preserve that.
3984 // Non-GCThing endpoints don't make much sense.
3985 if (!args[0].isObject() && !args[0].isString() && !args[0].isSymbol()) {
3986 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
3987 nullptr, "not an object, string, or symbol");
3988 return false;
3989 }
3990
3991 if (!args[1].isObject() && !args[1].isString() && !args[1].isSymbol()) {
3992 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
3993 nullptr, "not an object, string, or symbol");
3994 return false;
3995 }
3996
3997 Rooted<GCVector<Value>> nodes(cx, GCVector<Value>(cx));
3998 Vector<heaptools::EdgeName> edges(cx);
3999
4000 {
4001 // We can't tolerate the GC moving things around while we're searching
4002 // the heap. Check that nothing we do causes a GC.
4003 JS::AutoCheckCannotGC autoCannotGC;
4004
4005 JS::ubi::Node start(args[0]), target(args[1]);
4006
4007 heaptools::FindPathHandler handler(cx, start, target, &nodes, edges);
4008 heaptools::FindPathHandler::Traversal traversal(cx, handler, autoCannotGC);
4009 if (!traversal.addStart(start)) {
4010 ReportOutOfMemory(cx);
4011 return false;
4012 }
4013
4014 if (!traversal.traverse()) {
4015 if (!cx->isExceptionPending()) {
4016 ReportOutOfMemory(cx);
4017 }
4018 return false;
4019 }
4020
4021 if (!handler.foundPath) {
4022 // We didn't find any paths from the start to the target.
4023 args.rval().setUndefined();
4024 return true;
4025 }
4026 }
4027
4028 // |nodes| and |edges| contain the path from |start| to |target|, reversed.
4029 // Construct a JavaScript array describing the path from the start to the
4030 // target. Each element has the form:
4031 //
4032 // {
4033 // node: <object or string or symbol>,
4034 // edge: <string describing outgoing edge from node>
4035 // }
4036 //
4037 // or, if the node is some internal thing that isn't a proper JavaScript
4038 // value:
4039 //
4040 // { node: undefined, edge: <string> }
4041 size_t length = nodes.length();
4042 RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length));
4043 if (!result) {
4044 return false;
4045 }
4046 result->ensureDenseInitializedLength(cx, 0, length);
4047
4048 // Walk |nodes| and |edges| in the stored order, and construct the result
4049 // array in start-to-target order.
4050 for (size_t i = 0; i < length; i++) {
4051 // Build an object describing the node and edge.
4052 RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
4053 if (!obj) {
4054 return false;
4055 }
4056
4057 RootedValue wrapped(cx, nodes[i]);
4058 if (!cx->compartment()->wrap(cx, &wrapped)) {
4059 return false;
4060 }
4061
4062 if (!JS_DefineProperty(cx, obj, "node", wrapped, JSPROP_ENUMERATE)) {
4063 return false;
4064 }
4065
4066 heaptools::EdgeName edgeName = std::move(edges[i]);
4067
4068 size_t edgeNameLength = js_strlen(edgeName.get());
4069 RootedString edgeStr(
4070 cx, NewString<CanGC>(cx, std::move(edgeName), edgeNameLength));
4071 if (!edgeStr) {
4072 return false;
4073 }
4074
4075 if (!JS_DefineProperty(cx, obj, "edge", edgeStr, JSPROP_ENUMERATE)) {
4076 return false;
4077 }
4078
4079 result->setDenseElement(length - i - 1, ObjectValue(*obj));
4080 }
4081
4082 args.rval().setObject(*result);
4083 return true;
4084 }
4085
ShortestPaths(JSContext * cx,unsigned argc,Value * vp)4086 static bool ShortestPaths(JSContext* cx, unsigned argc, Value* vp) {
4087 CallArgs args = CallArgsFromVp(argc, vp);
4088 if (!args.requireAtLeast(cx, "shortestPaths", 3)) {
4089 return false;
4090 }
4091
4092 // We don't ToString non-objects given as 'start' or 'target', because this
4093 // test is all about object identity, and ToString doesn't preserve that.
4094 // Non-GCThing endpoints don't make much sense.
4095 if (!args[0].isObject() && !args[0].isString() && !args[0].isSymbol()) {
4096 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
4097 nullptr, "not an object, string, or symbol");
4098 return false;
4099 }
4100
4101 if (!args[1].isObject() || !args[1].toObject().is<ArrayObject>()) {
4102 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[1],
4103 nullptr, "not an array object");
4104 return false;
4105 }
4106
4107 RootedArrayObject objs(cx, &args[1].toObject().as<ArrayObject>());
4108 size_t length = objs->getDenseInitializedLength();
4109 if (length == 0) {
4110 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[1],
4111 nullptr,
4112 "not a dense array object with one or more elements");
4113 return false;
4114 }
4115
4116 for (size_t i = 0; i < length; i++) {
4117 RootedValue el(cx, objs->getDenseElement(i));
4118 if (!el.isObject() && !el.isString() && !el.isSymbol()) {
4119 JS_ReportErrorASCII(cx,
4120 "Each target must be an object, string, or symbol");
4121 return false;
4122 }
4123 }
4124
4125 int32_t maxNumPaths;
4126 if (!JS::ToInt32(cx, args[2], &maxNumPaths)) {
4127 return false;
4128 }
4129 if (maxNumPaths <= 0) {
4130 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[2],
4131 nullptr, "not greater than 0");
4132 return false;
4133 }
4134
4135 // We accumulate the results into a GC-stable form, due to the fact that the
4136 // JS::ubi::ShortestPaths lifetime (when operating on the live heap graph)
4137 // is bounded within an AutoCheckCannotGC.
4138 Rooted<GCVector<GCVector<GCVector<Value>>>> values(
4139 cx, GCVector<GCVector<GCVector<Value>>>(cx));
4140 Vector<Vector<Vector<JS::ubi::EdgeName>>> names(cx);
4141
4142 {
4143 JS::AutoCheckCannotGC noGC(cx);
4144
4145 JS::ubi::NodeSet targets;
4146
4147 for (size_t i = 0; i < length; i++) {
4148 RootedValue val(cx, objs->getDenseElement(i));
4149 JS::ubi::Node node(val);
4150 if (!targets.put(node)) {
4151 ReportOutOfMemory(cx);
4152 return false;
4153 }
4154 }
4155
4156 JS::ubi::Node root(args[0]);
4157 auto maybeShortestPaths = JS::ubi::ShortestPaths::Create(
4158 cx, noGC, maxNumPaths, root, std::move(targets));
4159 if (maybeShortestPaths.isNothing()) {
4160 ReportOutOfMemory(cx);
4161 return false;
4162 }
4163 auto& shortestPaths = *maybeShortestPaths;
4164
4165 for (size_t i = 0; i < length; i++) {
4166 if (!values.append(GCVector<GCVector<Value>>(cx)) ||
4167 !names.append(Vector<Vector<JS::ubi::EdgeName>>(cx))) {
4168 return false;
4169 }
4170
4171 RootedValue val(cx, objs->getDenseElement(i));
4172 JS::ubi::Node target(val);
4173
4174 bool ok = shortestPaths.forEachPath(target, [&](JS::ubi::Path& path) {
4175 Rooted<GCVector<Value>> pathVals(cx, GCVector<Value>(cx));
4176 Vector<JS::ubi::EdgeName> pathNames(cx);
4177
4178 for (auto& part : path) {
4179 if (!pathVals.append(part->predecessor().exposeToJS()) ||
4180 !pathNames.append(std::move(part->name()))) {
4181 return false;
4182 }
4183 }
4184
4185 return values.back().append(std::move(pathVals.get())) &&
4186 names.back().append(std::move(pathNames));
4187 });
4188
4189 if (!ok) {
4190 return false;
4191 }
4192 }
4193 }
4194
4195 MOZ_ASSERT(values.length() == names.length());
4196 MOZ_ASSERT(values.length() == length);
4197
4198 RootedArrayObject results(cx, NewDenseFullyAllocatedArray(cx, length));
4199 if (!results) {
4200 return false;
4201 }
4202 results->ensureDenseInitializedLength(cx, 0, length);
4203
4204 for (size_t i = 0; i < length; i++) {
4205 size_t numPaths = values[i].length();
4206 MOZ_ASSERT(names[i].length() == numPaths);
4207
4208 RootedArrayObject pathsArray(cx, NewDenseFullyAllocatedArray(cx, numPaths));
4209 if (!pathsArray) {
4210 return false;
4211 }
4212 pathsArray->ensureDenseInitializedLength(cx, 0, numPaths);
4213
4214 for (size_t j = 0; j < numPaths; j++) {
4215 size_t pathLength = values[i][j].length();
4216 MOZ_ASSERT(names[i][j].length() == pathLength);
4217
4218 RootedArrayObject path(cx, NewDenseFullyAllocatedArray(cx, pathLength));
4219 if (!path) {
4220 return false;
4221 }
4222 path->ensureDenseInitializedLength(cx, 0, pathLength);
4223
4224 for (size_t k = 0; k < pathLength; k++) {
4225 RootedPlainObject part(cx, NewBuiltinClassInstance<PlainObject>(cx));
4226 if (!part) {
4227 return false;
4228 }
4229
4230 RootedValue predecessor(cx, values[i][j][k]);
4231 if (!cx->compartment()->wrap(cx, &predecessor) ||
4232 !JS_DefineProperty(cx, part, "predecessor", predecessor,
4233 JSPROP_ENUMERATE)) {
4234 return false;
4235 }
4236
4237 if (names[i][j][k]) {
4238 RootedString edge(cx,
4239 NewStringCopyZ<CanGC>(cx, names[i][j][k].get()));
4240 if (!edge ||
4241 !JS_DefineProperty(cx, part, "edge", edge, JSPROP_ENUMERATE)) {
4242 return false;
4243 }
4244 }
4245
4246 path->setDenseElement(k, ObjectValue(*part));
4247 }
4248
4249 pathsArray->setDenseElement(j, ObjectValue(*path));
4250 }
4251
4252 results->setDenseElement(i, ObjectValue(*pathsArray));
4253 }
4254
4255 args.rval().setObject(*results);
4256 return true;
4257 }
4258
EvalReturningScope(JSContext * cx,unsigned argc,Value * vp)4259 static bool EvalReturningScope(JSContext* cx, unsigned argc, Value* vp) {
4260 CallArgs args = CallArgsFromVp(argc, vp);
4261 if (!args.requireAtLeast(cx, "evalReturningScope", 1)) {
4262 return false;
4263 }
4264
4265 RootedString str(cx, ToString(cx, args[0]));
4266 if (!str) {
4267 return false;
4268 }
4269
4270 RootedObject global(cx);
4271 if (args.hasDefined(1)) {
4272 global = ToObject(cx, args[1]);
4273 if (!global) {
4274 return false;
4275 }
4276 }
4277
4278 AutoStableStringChars strChars(cx);
4279 if (!strChars.initTwoByte(cx, str)) {
4280 return false;
4281 }
4282
4283 mozilla::Range<const char16_t> chars = strChars.twoByteRange();
4284 size_t srclen = chars.length();
4285 const char16_t* src = chars.begin().get();
4286
4287 JS::AutoFilename filename;
4288 unsigned lineno;
4289
4290 JS::DescribeScriptedCaller(cx, &filename, &lineno);
4291
4292 JS::CompileOptions options(cx);
4293 options.setFileAndLine(filename.get(), lineno);
4294 options.setNoScriptRval(true);
4295
4296 JS::SourceText<char16_t> srcBuf;
4297 if (!srcBuf.init(cx, src, srclen, SourceOwnership::Borrowed)) {
4298 return false;
4299 }
4300
4301 RootedScript script(cx, JS::CompileForNonSyntacticScope(cx, options, srcBuf));
4302 if (!script) {
4303 return false;
4304 }
4305
4306 if (global) {
4307 global = CheckedUnwrapDynamic(global, cx, /* stopAtWindowProxy = */ false);
4308 if (!global) {
4309 JS_ReportErrorASCII(cx, "Permission denied to access global");
4310 return false;
4311 }
4312 if (!global->is<GlobalObject>()) {
4313 JS_ReportErrorASCII(cx, "Argument must be a global object");
4314 return false;
4315 }
4316 } else {
4317 global = JS::CurrentGlobalOrNull(cx);
4318 }
4319
4320 RootedObject varObj(cx);
4321 RootedObject lexicalScope(cx);
4322
4323 {
4324 // If we're switching globals here, ExecuteInFrameScriptEnvironment will
4325 // take care of cloning the script into that compartment before
4326 // executing it.
4327 AutoRealm ar(cx, global);
4328 JS::RootedObject obj(cx, JS_NewPlainObject(cx));
4329 if (!obj) {
4330 return false;
4331 }
4332
4333 if (!js::ExecuteInFrameScriptEnvironment(cx, obj, script, &lexicalScope)) {
4334 return false;
4335 }
4336
4337 varObj = lexicalScope->enclosingEnvironment()->enclosingEnvironment();
4338 }
4339
4340 RootedObject rv(cx, JS_NewPlainObject(cx));
4341 if (!rv) {
4342 return false;
4343 }
4344
4345 RootedValue varObjVal(cx, ObjectValue(*varObj));
4346 if (!cx->compartment()->wrap(cx, &varObjVal)) {
4347 return false;
4348 }
4349 if (!JS_SetProperty(cx, rv, "vars", varObjVal)) {
4350 return false;
4351 }
4352
4353 RootedValue lexicalScopeVal(cx, ObjectValue(*lexicalScope));
4354 if (!cx->compartment()->wrap(cx, &lexicalScopeVal)) {
4355 return false;
4356 }
4357 if (!JS_SetProperty(cx, rv, "lexicals", lexicalScopeVal)) {
4358 return false;
4359 }
4360
4361 args.rval().setObject(*rv);
4362 return true;
4363 }
4364
ShellCloneAndExecuteScript(JSContext * cx,unsigned argc,Value * vp)4365 static bool ShellCloneAndExecuteScript(JSContext* cx, unsigned argc,
4366 Value* vp) {
4367 CallArgs args = CallArgsFromVp(argc, vp);
4368 if (!args.requireAtLeast(cx, "cloneAndExecuteScript", 2)) {
4369 return false;
4370 }
4371
4372 RootedString str(cx, ToString(cx, args[0]));
4373 if (!str) {
4374 return false;
4375 }
4376
4377 RootedObject global(cx, ToObject(cx, args[1]));
4378 if (!global) {
4379 return false;
4380 }
4381
4382 AutoStableStringChars strChars(cx);
4383 if (!strChars.initTwoByte(cx, str)) {
4384 return false;
4385 }
4386
4387 mozilla::Range<const char16_t> chars = strChars.twoByteRange();
4388 size_t srclen = chars.length();
4389 const char16_t* src = chars.begin().get();
4390
4391 JS::AutoFilename filename;
4392 unsigned lineno;
4393
4394 JS::DescribeScriptedCaller(cx, &filename, &lineno);
4395
4396 JS::CompileOptions options(cx);
4397 options.setFileAndLine(filename.get(), lineno);
4398
4399 JS::SourceText<char16_t> srcBuf;
4400 if (!srcBuf.init(cx, src, srclen, SourceOwnership::Borrowed)) {
4401 return false;
4402 }
4403
4404 RootedScript script(cx, JS::Compile(cx, options, srcBuf));
4405 if (!script) {
4406 return false;
4407 }
4408
4409 global = CheckedUnwrapDynamic(global, cx, /* stopAtWindowProxy = */ false);
4410 if (!global) {
4411 JS_ReportErrorASCII(cx, "Permission denied to access global");
4412 return false;
4413 }
4414 if (!global->is<GlobalObject>()) {
4415 JS_ReportErrorASCII(cx, "Argument must be a global object");
4416 return false;
4417 }
4418
4419 JS::RootedValue rval(cx);
4420 {
4421 AutoRealm ar(cx, global);
4422 if (!JS::CloneAndExecuteScript(cx, script, &rval)) {
4423 return false;
4424 }
4425 }
4426
4427 if (!cx->compartment()->wrap(cx, &rval)) {
4428 return false;
4429 }
4430
4431 args.rval().set(rval);
4432 return true;
4433 }
4434
ByteSize(JSContext * cx,unsigned argc,Value * vp)4435 static bool ByteSize(JSContext* cx, unsigned argc, Value* vp) {
4436 CallArgs args = CallArgsFromVp(argc, vp);
4437 mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf;
4438
4439 {
4440 // We can't tolerate the GC moving things around while we're using a
4441 // ubi::Node. Check that nothing we do causes a GC.
4442 JS::AutoCheckCannotGC autoCannotGC;
4443
4444 JS::ubi::Node node = args.get(0);
4445 if (node) {
4446 args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
4447 } else {
4448 args.rval().setUndefined();
4449 }
4450 }
4451 return true;
4452 }
4453
ByteSizeOfScript(JSContext * cx,unsigned argc,Value * vp)4454 static bool ByteSizeOfScript(JSContext* cx, unsigned argc, Value* vp) {
4455 CallArgs args = CallArgsFromVp(argc, vp);
4456 if (!args.requireAtLeast(cx, "byteSizeOfScript", 1)) {
4457 return false;
4458 }
4459 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
4460 JS_ReportErrorASCII(cx, "Argument must be a Function object");
4461 return false;
4462 }
4463
4464 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
4465 if (fun->isNative()) {
4466 JS_ReportErrorASCII(cx, "Argument must be a scripted function");
4467 return false;
4468 }
4469
4470 RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
4471 if (!script) {
4472 return false;
4473 }
4474
4475 mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf;
4476
4477 {
4478 // We can't tolerate the GC moving things around while we're using a
4479 // ubi::Node. Check that nothing we do causes a GC.
4480 JS::AutoCheckCannotGC autoCannotGC;
4481
4482 JS::ubi::Node node = script;
4483 if (node) {
4484 args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
4485 } else {
4486 args.rval().setUndefined();
4487 }
4488 }
4489 return true;
4490 }
4491
SetImmutablePrototype(JSContext * cx,unsigned argc,Value * vp)4492 static bool SetImmutablePrototype(JSContext* cx, unsigned argc, Value* vp) {
4493 CallArgs args = CallArgsFromVp(argc, vp);
4494 if (!args.get(0).isObject()) {
4495 JS_ReportErrorASCII(cx, "setImmutablePrototype: object expected");
4496 return false;
4497 }
4498
4499 RootedObject obj(cx, &args[0].toObject());
4500
4501 bool succeeded;
4502 if (!js::SetImmutablePrototype(cx, obj, &succeeded)) {
4503 return false;
4504 }
4505
4506 args.rval().setBoolean(succeeded);
4507 return true;
4508 }
4509
4510 #ifdef DEBUG
DumpStringRepresentation(JSContext * cx,unsigned argc,Value * vp)4511 static bool DumpStringRepresentation(JSContext* cx, unsigned argc, Value* vp) {
4512 CallArgs args = CallArgsFromVp(argc, vp);
4513
4514 RootedString str(cx, ToString(cx, args.get(0)));
4515 if (!str) {
4516 return false;
4517 }
4518
4519 Fprinter out(stderr);
4520 str->dumpRepresentation(out, 0);
4521
4522 args.rval().setUndefined();
4523 return true;
4524 }
4525 #endif
4526
SetLazyParsingDisabled(JSContext * cx,unsigned argc,Value * vp)4527 static bool SetLazyParsingDisabled(JSContext* cx, unsigned argc, Value* vp) {
4528 CallArgs args = CallArgsFromVp(argc, vp);
4529
4530 bool disable = !args.hasDefined(0) || ToBoolean(args[0]);
4531 cx->realm()->behaviors().setDisableLazyParsing(disable);
4532
4533 args.rval().setUndefined();
4534 return true;
4535 }
4536
SetDiscardSource(JSContext * cx,unsigned argc,Value * vp)4537 static bool SetDiscardSource(JSContext* cx, unsigned argc, Value* vp) {
4538 CallArgs args = CallArgsFromVp(argc, vp);
4539
4540 bool discard = !args.hasDefined(0) || ToBoolean(args[0]);
4541 cx->realm()->behaviors().setDiscardSource(discard);
4542
4543 args.rval().setUndefined();
4544 return true;
4545 }
4546
GetConstructorName(JSContext * cx,unsigned argc,Value * vp)4547 static bool GetConstructorName(JSContext* cx, unsigned argc, Value* vp) {
4548 CallArgs args = CallArgsFromVp(argc, vp);
4549 if (!args.requireAtLeast(cx, "getConstructorName", 1)) {
4550 return false;
4551 }
4552
4553 if (!args[0].isObject()) {
4554 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4555 JSMSG_NOT_EXPECTED_TYPE, "getConstructorName",
4556 "Object", InformalValueTypeName(args[0]));
4557 return false;
4558 }
4559
4560 RootedAtom name(cx);
4561 RootedObject obj(cx, &args[0].toObject());
4562 if (!JSObject::constructorDisplayAtom(cx, obj, &name)) {
4563 return false;
4564 }
4565
4566 if (name) {
4567 args.rval().setString(name);
4568 } else {
4569 args.rval().setNull();
4570 }
4571 return true;
4572 }
4573
4574 class AllocationMarkerObject : public NativeObject {
4575 public:
4576 static const JSClass class_;
4577 };
4578
4579 const JSClass AllocationMarkerObject::class_ = {"AllocationMarker"};
4580
AllocationMarker(JSContext * cx,unsigned argc,Value * vp)4581 static bool AllocationMarker(JSContext* cx, unsigned argc, Value* vp) {
4582 CallArgs args = CallArgsFromVp(argc, vp);
4583
4584 bool allocateInsideNursery = true;
4585 if (args.length() > 0 && args[0].isObject()) {
4586 RootedObject options(cx, &args[0].toObject());
4587
4588 RootedValue nurseryVal(cx);
4589 if (!JS_GetProperty(cx, options, "nursery", &nurseryVal)) {
4590 return false;
4591 }
4592 allocateInsideNursery = ToBoolean(nurseryVal);
4593 }
4594
4595 JSObject* obj =
4596 allocateInsideNursery
4597 ? NewObjectWithGivenProto<AllocationMarkerObject>(cx, nullptr)
4598 : NewTenuredObjectWithGivenProto<AllocationMarkerObject>(cx, nullptr);
4599 if (!obj) {
4600 return false;
4601 }
4602
4603 args.rval().setObject(*obj);
4604 return true;
4605 }
4606
4607 namespace gcCallback {
4608
4609 struct MajorGC {
4610 int32_t depth;
4611 int32_t phases;
4612 };
4613
majorGC(JSContext * cx,JSGCStatus status,JS::GCReason reason,void * data)4614 static void majorGC(JSContext* cx, JSGCStatus status, JS::GCReason reason,
4615 void* data) {
4616 auto info = static_cast<MajorGC*>(data);
4617 if (!(info->phases & (1 << status))) {
4618 return;
4619 }
4620
4621 if (info->depth > 0) {
4622 info->depth--;
4623 JS::PrepareForFullGC(cx);
4624 JS::NonIncrementalGC(cx, GC_NORMAL, JS::GCReason::API);
4625 info->depth++;
4626 }
4627 }
4628
4629 struct MinorGC {
4630 int32_t phases;
4631 bool active;
4632 };
4633
minorGC(JSContext * cx,JSGCStatus status,JS::GCReason reason,void * data)4634 static void minorGC(JSContext* cx, JSGCStatus status, JS::GCReason reason,
4635 void* data) {
4636 auto info = static_cast<MinorGC*>(data);
4637 if (!(info->phases & (1 << status))) {
4638 return;
4639 }
4640
4641 if (info->active) {
4642 info->active = false;
4643 if (cx->zone() && !cx->zone()->isAtomsZone()) {
4644 cx->runtime()->gc.evictNursery(JS::GCReason::DEBUG_GC);
4645 }
4646 info->active = true;
4647 }
4648 }
4649
4650 // Process global, should really be runtime-local.
4651 static MajorGC majorGCInfo;
4652 static MinorGC minorGCInfo;
4653
enterNullRealm(JSContext * cx,JSGCStatus status,JS::GCReason reason,void * data)4654 static void enterNullRealm(JSContext* cx, JSGCStatus status,
4655 JS::GCReason reason, void* data) {
4656 JSAutoNullableRealm enterRealm(cx, nullptr);
4657 }
4658
4659 } /* namespace gcCallback */
4660
SetGCCallback(JSContext * cx,unsigned argc,Value * vp)4661 static bool SetGCCallback(JSContext* cx, unsigned argc, Value* vp) {
4662 CallArgs args = CallArgsFromVp(argc, vp);
4663
4664 if (args.length() != 1) {
4665 JS_ReportErrorASCII(cx, "Wrong number of arguments");
4666 return false;
4667 }
4668
4669 RootedObject opts(cx, ToObject(cx, args[0]));
4670 if (!opts) {
4671 return false;
4672 }
4673
4674 RootedValue v(cx);
4675 if (!JS_GetProperty(cx, opts, "action", &v)) {
4676 return false;
4677 }
4678
4679 JSString* str = JS::ToString(cx, v);
4680 if (!str) {
4681 return false;
4682 }
4683 RootedLinearString action(cx, str->ensureLinear(cx));
4684 if (!action) {
4685 return false;
4686 }
4687
4688 int32_t phases = 0;
4689 if (StringEqualsLiteral(action, "minorGC") ||
4690 StringEqualsLiteral(action, "majorGC")) {
4691 if (!JS_GetProperty(cx, opts, "phases", &v)) {
4692 return false;
4693 }
4694 if (v.isUndefined()) {
4695 phases = (1 << JSGC_END);
4696 } else {
4697 JSString* str = JS::ToString(cx, v);
4698 if (!str) {
4699 return false;
4700 }
4701 JSLinearString* phasesStr = str->ensureLinear(cx);
4702 if (!phasesStr) {
4703 return false;
4704 }
4705
4706 if (StringEqualsLiteral(phasesStr, "begin")) {
4707 phases = (1 << JSGC_BEGIN);
4708 } else if (StringEqualsLiteral(phasesStr, "end")) {
4709 phases = (1 << JSGC_END);
4710 } else if (StringEqualsLiteral(phasesStr, "both")) {
4711 phases = (1 << JSGC_BEGIN) | (1 << JSGC_END);
4712 } else {
4713 JS_ReportErrorASCII(cx, "Invalid callback phase");
4714 return false;
4715 }
4716 }
4717 }
4718
4719 if (StringEqualsLiteral(action, "minorGC")) {
4720 gcCallback::minorGCInfo.phases = phases;
4721 gcCallback::minorGCInfo.active = true;
4722 JS_SetGCCallback(cx, gcCallback::minorGC, &gcCallback::minorGCInfo);
4723 } else if (StringEqualsLiteral(action, "majorGC")) {
4724 if (!JS_GetProperty(cx, opts, "depth", &v)) {
4725 return false;
4726 }
4727 int32_t depth = 1;
4728 if (!v.isUndefined()) {
4729 if (!ToInt32(cx, v, &depth)) {
4730 return false;
4731 }
4732 }
4733 if (depth < 0) {
4734 JS_ReportErrorASCII(cx, "Nesting depth cannot be negative");
4735 return false;
4736 }
4737 if (depth + gcstats::MAX_PHASE_NESTING >
4738 gcstats::Statistics::MAX_SUSPENDED_PHASES) {
4739 JS_ReportErrorASCII(cx, "Nesting depth too large, would overflow");
4740 return false;
4741 }
4742
4743 gcCallback::majorGCInfo.phases = phases;
4744 gcCallback::majorGCInfo.depth = depth;
4745 JS_SetGCCallback(cx, gcCallback::majorGC, &gcCallback::majorGCInfo);
4746 } else if (StringEqualsLiteral(action, "enterNullRealm")) {
4747 JS_SetGCCallback(cx, gcCallback::enterNullRealm, nullptr);
4748 } else {
4749 JS_ReportErrorASCII(cx, "Unknown GC callback action");
4750 return false;
4751 }
4752
4753 args.rval().setUndefined();
4754 return true;
4755 }
4756
4757 #ifdef DEBUG
EnqueueMark(JSContext * cx,unsigned argc,Value * vp)4758 static bool EnqueueMark(JSContext* cx, unsigned argc, Value* vp) {
4759 CallArgs args = CallArgsFromVp(argc, vp);
4760
4761 auto& queue = cx->runtime()->gc.marker.markQueue;
4762
4763 if (args.get(0).isString()) {
4764 RootedString val(cx, args[0].toString());
4765 if (!val->ensureLinear(cx)) {
4766 return false;
4767 }
4768 if (!queue.append(StringValue(val))) {
4769 JS_ReportOutOfMemory(cx);
4770 return false;
4771 }
4772 } else if (args.get(0).isObject()) {
4773 if (!queue.append(args[0])) {
4774 JS_ReportOutOfMemory(cx);
4775 return false;
4776 }
4777 } else {
4778 JS_ReportErrorASCII(cx, "Argument must be a string or object");
4779 return false;
4780 }
4781
4782 args.rval().setUndefined();
4783 return true;
4784 }
4785
GetMarkQueue(JSContext * cx,unsigned argc,Value * vp)4786 static bool GetMarkQueue(JSContext* cx, unsigned argc, Value* vp) {
4787 CallArgs args = CallArgsFromVp(argc, vp);
4788
4789 auto& queue = cx->runtime()->gc.marker.markQueue.get();
4790
4791 RootedObject result(cx, JS::NewArrayObject(cx, queue.length()));
4792 if (!result) {
4793 return false;
4794 }
4795 for (size_t i = 0; i < queue.length(); i++) {
4796 RootedValue val(cx, queue[i]);
4797 if (!JS_WrapValue(cx, &val)) {
4798 return false;
4799 }
4800 if (!JS_SetElement(cx, result, i, val)) {
4801 return false;
4802 }
4803 }
4804
4805 args.rval().setObject(*result);
4806 return true;
4807 }
4808
ClearMarkQueue(JSContext * cx,unsigned argc,Value * vp)4809 static bool ClearMarkQueue(JSContext* cx, unsigned argc, Value* vp) {
4810 CallArgs args = CallArgsFromVp(argc, vp);
4811
4812 cx->runtime()->gc.marker.markQueue.clear();
4813 args.rval().setUndefined();
4814 return true;
4815 }
4816 #endif // DEBUG
4817
GetLcovInfo(JSContext * cx,unsigned argc,Value * vp)4818 static bool GetLcovInfo(JSContext* cx, unsigned argc, Value* vp) {
4819 CallArgs args = CallArgsFromVp(argc, vp);
4820
4821 if (args.length() > 1) {
4822 JS_ReportErrorASCII(cx, "Wrong number of arguments");
4823 return false;
4824 }
4825
4826 if (!coverage::IsLCovEnabled()) {
4827 JS_ReportErrorASCII(cx, "Coverage not enabled for process.");
4828 return false;
4829 }
4830
4831 RootedObject global(cx);
4832 if (args.hasDefined(0)) {
4833 global = ToObject(cx, args[0]);
4834 if (!global) {
4835 JS_ReportErrorASCII(cx, "Permission denied to access global");
4836 return false;
4837 }
4838 global = CheckedUnwrapDynamic(global, cx, /* stopAtWindowProxy = */ false);
4839 if (!global) {
4840 ReportAccessDenied(cx);
4841 return false;
4842 }
4843 if (!global->is<GlobalObject>()) {
4844 JS_ReportErrorASCII(cx, "Argument must be a global object");
4845 return false;
4846 }
4847 } else {
4848 global = JS::CurrentGlobalOrNull(cx);
4849 }
4850
4851 size_t length = 0;
4852 UniqueChars content;
4853 {
4854 AutoRealm ar(cx, global);
4855 content = js::GetCodeCoverageSummary(cx, &length);
4856 }
4857
4858 if (!content) {
4859 return false;
4860 }
4861
4862 JSString* str = JS_NewStringCopyN(cx, content.get(), length);
4863 if (!str) {
4864 return false;
4865 }
4866
4867 args.rval().setString(str);
4868 return true;
4869 }
4870
4871 #ifdef DEBUG
SetRNGState(JSContext * cx,unsigned argc,Value * vp)4872 static bool SetRNGState(JSContext* cx, unsigned argc, Value* vp) {
4873 CallArgs args = CallArgsFromVp(argc, vp);
4874 if (!args.requireAtLeast(cx, "SetRNGState", 2)) {
4875 return false;
4876 }
4877
4878 double d0;
4879 if (!ToNumber(cx, args[0], &d0)) {
4880 return false;
4881 }
4882
4883 double d1;
4884 if (!ToNumber(cx, args[1], &d1)) {
4885 return false;
4886 }
4887
4888 uint64_t seed0 = static_cast<uint64_t>(d0);
4889 uint64_t seed1 = static_cast<uint64_t>(d1);
4890
4891 if (seed0 == 0 && seed1 == 0) {
4892 JS_ReportErrorASCII(cx, "RNG requires non-zero seed");
4893 return false;
4894 }
4895
4896 cx->realm()->getOrCreateRandomNumberGenerator().setState(seed0, seed1);
4897
4898 args.rval().setUndefined();
4899 return true;
4900 }
4901 #endif
4902
GetModuleEnvironment(JSContext * cx,HandleModuleObject module)4903 static ModuleEnvironmentObject* GetModuleEnvironment(
4904 JSContext* cx, HandleModuleObject module) {
4905 // Use the initial environment so that tests can check bindings exists
4906 // before they have been instantiated.
4907 RootedModuleEnvironmentObject env(cx, &module->initialEnvironment());
4908 MOZ_ASSERT(env);
4909 return env;
4910 }
4911
GetModuleEnvironmentNames(JSContext * cx,unsigned argc,Value * vp)4912 static bool GetModuleEnvironmentNames(JSContext* cx, unsigned argc, Value* vp) {
4913 CallArgs args = CallArgsFromVp(argc, vp);
4914 if (args.length() != 1) {
4915 JS_ReportErrorASCII(cx, "Wrong number of arguments");
4916 return false;
4917 }
4918
4919 if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
4920 JS_ReportErrorASCII(cx, "First argument should be a ModuleObject");
4921 return false;
4922 }
4923
4924 RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
4925 if (module->hadEvaluationError()) {
4926 JS_ReportErrorASCII(cx, "Module environment unavailable");
4927 return false;
4928 }
4929
4930 RootedModuleEnvironmentObject env(cx, GetModuleEnvironment(cx, module));
4931 Rooted<IdVector> ids(cx, IdVector(cx));
4932 if (!JS_Enumerate(cx, env, &ids)) {
4933 return false;
4934 }
4935
4936 uint32_t length = ids.length();
4937 RootedArrayObject array(cx, NewDenseFullyAllocatedArray(cx, length));
4938 if (!array) {
4939 return false;
4940 }
4941
4942 array->setDenseInitializedLength(length);
4943 for (uint32_t i = 0; i < length; i++) {
4944 array->initDenseElement(i, StringValue(JSID_TO_STRING(ids[i])));
4945 }
4946
4947 args.rval().setObject(*array);
4948 return true;
4949 }
4950
GetModuleEnvironmentValue(JSContext * cx,unsigned argc,Value * vp)4951 static bool GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp) {
4952 CallArgs args = CallArgsFromVp(argc, vp);
4953 if (args.length() != 2) {
4954 JS_ReportErrorASCII(cx, "Wrong number of arguments");
4955 return false;
4956 }
4957
4958 if (!args[0].isObject() || !args[0].toObject().is<ModuleObject>()) {
4959 JS_ReportErrorASCII(cx, "First argument should be a ModuleObject");
4960 return false;
4961 }
4962
4963 if (!args[1].isString()) {
4964 JS_ReportErrorASCII(cx, "Second argument should be a string");
4965 return false;
4966 }
4967
4968 RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
4969 if (module->hadEvaluationError()) {
4970 JS_ReportErrorASCII(cx, "Module environment unavailable");
4971 return false;
4972 }
4973
4974 RootedModuleEnvironmentObject env(cx, GetModuleEnvironment(cx, module));
4975 RootedString name(cx, args[1].toString());
4976 RootedId id(cx);
4977 if (!JS_StringToId(cx, name, &id)) {
4978 return false;
4979 }
4980
4981 if (!GetProperty(cx, env, env, id, args.rval())) {
4982 return false;
4983 }
4984
4985 if (args.rval().isMagic(JS_UNINITIALIZED_LEXICAL)) {
4986 ReportRuntimeLexicalError(cx, JSMSG_UNINITIALIZED_LEXICAL, id);
4987 return false;
4988 }
4989
4990 return true;
4991 }
4992
4993 #if defined(DEBUG) && !defined(ENABLE_NEW_REGEXP)
AssertionTypeToString(irregexp::RegExpAssertion::AssertionType type)4994 static const char* AssertionTypeToString(
4995 irregexp::RegExpAssertion::AssertionType type) {
4996 switch (type) {
4997 case irregexp::RegExpAssertion::START_OF_LINE:
4998 return "START_OF_LINE";
4999 case irregexp::RegExpAssertion::START_OF_INPUT:
5000 return "START_OF_INPUT";
5001 case irregexp::RegExpAssertion::END_OF_LINE:
5002 return "END_OF_LINE";
5003 case irregexp::RegExpAssertion::END_OF_INPUT:
5004 return "END_OF_INPUT";
5005 case irregexp::RegExpAssertion::BOUNDARY:
5006 return "BOUNDARY";
5007 case irregexp::RegExpAssertion::NON_BOUNDARY:
5008 return "NON_BOUNDARY";
5009 case irregexp::RegExpAssertion::NOT_AFTER_LEAD_SURROGATE:
5010 return "NOT_AFTER_LEAD_SURROGATE";
5011 case irregexp::RegExpAssertion::NOT_IN_SURROGATE_PAIR:
5012 return "NOT_IN_SURROGATE_PAIR";
5013 }
5014 MOZ_CRASH("unexpected AssertionType");
5015 }
5016
ConvertRegExpTreeToObject(JSContext * cx,LifoAlloc & alloc,irregexp::RegExpTree * tree)5017 static JSObject* ConvertRegExpTreeToObject(JSContext* cx, LifoAlloc& alloc,
5018 irregexp::RegExpTree* tree) {
5019 RootedObject obj(cx, JS_NewPlainObject(cx));
5020 if (!obj) {
5021 return nullptr;
5022 }
5023
5024 auto IntProp = [](JSContext* cx, HandleObject obj, const char* name,
5025 int32_t value) {
5026 RootedValue val(cx, Int32Value(value));
5027 return JS_SetProperty(cx, obj, name, val);
5028 };
5029
5030 auto BooleanProp = [](JSContext* cx, HandleObject obj, const char* name,
5031 bool value) {
5032 RootedValue val(cx, BooleanValue(value));
5033 return JS_SetProperty(cx, obj, name, val);
5034 };
5035
5036 auto StringProp = [](JSContext* cx, HandleObject obj, const char* name,
5037 const char* value) {
5038 RootedString valueStr(cx, JS_NewStringCopyZ(cx, value));
5039 if (!valueStr) {
5040 return false;
5041 }
5042
5043 RootedValue val(cx, StringValue(valueStr));
5044 return JS_SetProperty(cx, obj, name, val);
5045 };
5046
5047 auto ObjectProp = [](JSContext* cx, HandleObject obj, const char* name,
5048 HandleObject value) {
5049 RootedValue val(cx, ObjectValue(*value));
5050 return JS_SetProperty(cx, obj, name, val);
5051 };
5052
5053 auto CharVectorProp = [](JSContext* cx, HandleObject obj, const char* name,
5054 const irregexp::CharacterVector& data) {
5055 RootedString valueStr(cx,
5056 JS_NewUCStringCopyN(cx, data.begin(), data.length()));
5057 if (!valueStr) {
5058 return false;
5059 }
5060
5061 RootedValue val(cx, StringValue(valueStr));
5062 return JS_SetProperty(cx, obj, name, val);
5063 };
5064
5065 auto TreeProp = [&ObjectProp, &alloc](JSContext* cx, HandleObject obj,
5066 const char* name,
5067 irregexp::RegExpTree* tree) {
5068 RootedObject treeObj(cx, ConvertRegExpTreeToObject(cx, alloc, tree));
5069 if (!treeObj) {
5070 return false;
5071 }
5072 return ObjectProp(cx, obj, name, treeObj);
5073 };
5074
5075 auto TreeVectorProp = [&ObjectProp, &alloc](
5076 JSContext* cx, HandleObject obj, const char* name,
5077 const irregexp::RegExpTreeVector& nodes) {
5078 size_t len = nodes.length();
5079 RootedObject array(cx, JS::NewArrayObject(cx, len));
5080 if (!array) {
5081 return false;
5082 }
5083
5084 for (size_t i = 0; i < len; i++) {
5085 RootedObject child(cx, ConvertRegExpTreeToObject(cx, alloc, nodes[i]));
5086 if (!child) {
5087 return false;
5088 }
5089
5090 RootedValue childVal(cx, ObjectValue(*child));
5091 if (!JS_SetElement(cx, array, i, childVal)) {
5092 return false;
5093 }
5094 }
5095 return ObjectProp(cx, obj, name, array);
5096 };
5097
5098 auto CharRangesProp = [&ObjectProp](
5099 JSContext* cx, HandleObject obj, const char* name,
5100 const irregexp::CharacterRangeVector& ranges) {
5101 size_t len = ranges.length();
5102 RootedObject array(cx, JS::NewArrayObject(cx, len));
5103 if (!array) {
5104 return false;
5105 }
5106
5107 for (size_t i = 0; i < len; i++) {
5108 const irregexp::CharacterRange& range = ranges[i];
5109 RootedObject rangeObj(cx, JS_NewPlainObject(cx));
5110 if (!rangeObj) {
5111 return false;
5112 }
5113
5114 auto CharProp = [](JSContext* cx, HandleObject obj, const char* name,
5115 char16_t c) {
5116 RootedString valueStr(cx, JS_NewUCStringCopyN(cx, &c, 1));
5117 if (!valueStr) {
5118 return false;
5119 }
5120 RootedValue val(cx, StringValue(valueStr));
5121 return JS_SetProperty(cx, obj, name, val);
5122 };
5123
5124 if (!CharProp(cx, rangeObj, "from", range.from())) {
5125 return false;
5126 }
5127 if (!CharProp(cx, rangeObj, "to", range.to())) {
5128 return false;
5129 }
5130
5131 RootedValue rangeVal(cx, ObjectValue(*rangeObj));
5132 if (!JS_SetElement(cx, array, i, rangeVal)) {
5133 return false;
5134 }
5135 }
5136 return ObjectProp(cx, obj, name, array);
5137 };
5138
5139 auto ElemProp = [&ObjectProp, &alloc](
5140 JSContext* cx, HandleObject obj, const char* name,
5141 const irregexp::TextElementVector& elements) {
5142 size_t len = elements.length();
5143 RootedObject array(cx, JS::NewArrayObject(cx, len));
5144 if (!array) {
5145 return false;
5146 }
5147
5148 for (size_t i = 0; i < len; i++) {
5149 const irregexp::TextElement& element = elements[i];
5150 RootedObject elemTree(
5151 cx, ConvertRegExpTreeToObject(cx, alloc, element.tree()));
5152 if (!elemTree) {
5153 return false;
5154 }
5155
5156 RootedValue elemTreeVal(cx, ObjectValue(*elemTree));
5157 if (!JS_SetElement(cx, array, i, elemTreeVal)) {
5158 return false;
5159 }
5160 }
5161 return ObjectProp(cx, obj, name, array);
5162 };
5163
5164 if (tree->IsDisjunction()) {
5165 if (!StringProp(cx, obj, "type", "Disjunction")) {
5166 return nullptr;
5167 }
5168 irregexp::RegExpDisjunction* t = tree->AsDisjunction();
5169 if (!TreeVectorProp(cx, obj, "alternatives", t->alternatives())) {
5170 return nullptr;
5171 }
5172 return obj;
5173 }
5174 if (tree->IsAlternative()) {
5175 if (!StringProp(cx, obj, "type", "Alternative")) {
5176 return nullptr;
5177 }
5178 irregexp::RegExpAlternative* t = tree->AsAlternative();
5179 if (!TreeVectorProp(cx, obj, "nodes", t->nodes())) {
5180 return nullptr;
5181 }
5182 return obj;
5183 }
5184 if (tree->IsAssertion()) {
5185 if (!StringProp(cx, obj, "type", "Assertion")) {
5186 return nullptr;
5187 }
5188 irregexp::RegExpAssertion* t = tree->AsAssertion();
5189 if (!StringProp(cx, obj, "assertion_type",
5190 AssertionTypeToString(t->assertion_type()))) {
5191 return nullptr;
5192 }
5193 return obj;
5194 }
5195 if (tree->IsCharacterClass()) {
5196 if (!StringProp(cx, obj, "type", "CharacterClass")) {
5197 return nullptr;
5198 }
5199 irregexp::RegExpCharacterClass* t = tree->AsCharacterClass();
5200 if (!BooleanProp(cx, obj, "is_negated", t->is_negated())) {
5201 return nullptr;
5202 }
5203 if (!CharRangesProp(cx, obj, "ranges", t->ranges(&alloc))) {
5204 return nullptr;
5205 }
5206 return obj;
5207 }
5208 if (tree->IsAtom()) {
5209 if (!StringProp(cx, obj, "type", "Atom")) {
5210 return nullptr;
5211 }
5212 irregexp::RegExpAtom* t = tree->AsAtom();
5213 if (!CharVectorProp(cx, obj, "data", t->data())) {
5214 return nullptr;
5215 }
5216 return obj;
5217 }
5218 if (tree->IsText()) {
5219 if (!StringProp(cx, obj, "type", "Text")) {
5220 return nullptr;
5221 }
5222 irregexp::RegExpText* t = tree->AsText();
5223 if (!ElemProp(cx, obj, "elements", t->elements())) {
5224 return nullptr;
5225 }
5226 return obj;
5227 }
5228 if (tree->IsQuantifier()) {
5229 if (!StringProp(cx, obj, "type", "Quantifier")) {
5230 return nullptr;
5231 }
5232 irregexp::RegExpQuantifier* t = tree->AsQuantifier();
5233 if (!IntProp(cx, obj, "min", t->min())) {
5234 return nullptr;
5235 }
5236 if (!IntProp(cx, obj, "max", t->max())) {
5237 return nullptr;
5238 }
5239 if (!StringProp(cx, obj, "quantifier_type",
5240 t->is_possessive()
5241 ? "POSSESSIVE"
5242 : t->is_non_greedy() ? "NON_GREEDY" : "GREEDY"))
5243 return nullptr;
5244 if (!TreeProp(cx, obj, "body", t->body())) {
5245 return nullptr;
5246 }
5247 return obj;
5248 }
5249 if (tree->IsCapture()) {
5250 if (!StringProp(cx, obj, "type", "Capture")) {
5251 return nullptr;
5252 }
5253 irregexp::RegExpCapture* t = tree->AsCapture();
5254 if (!IntProp(cx, obj, "index", t->index())) {
5255 return nullptr;
5256 }
5257 if (!TreeProp(cx, obj, "body", t->body())) {
5258 return nullptr;
5259 }
5260 return obj;
5261 }
5262 if (tree->IsLookahead()) {
5263 if (!StringProp(cx, obj, "type", "Lookahead")) {
5264 return nullptr;
5265 }
5266 irregexp::RegExpLookahead* t = tree->AsLookahead();
5267 if (!BooleanProp(cx, obj, "is_positive", t->is_positive())) {
5268 return nullptr;
5269 }
5270 if (!TreeProp(cx, obj, "body", t->body())) {
5271 return nullptr;
5272 }
5273 return obj;
5274 }
5275 if (tree->IsBackReference()) {
5276 if (!StringProp(cx, obj, "type", "BackReference")) {
5277 return nullptr;
5278 }
5279 irregexp::RegExpBackReference* t = tree->AsBackReference();
5280 if (!IntProp(cx, obj, "index", t->index())) {
5281 return nullptr;
5282 }
5283 return obj;
5284 }
5285 if (tree->IsEmpty()) {
5286 if (!StringProp(cx, obj, "type", "Empty")) {
5287 return nullptr;
5288 }
5289 return obj;
5290 }
5291
5292 MOZ_CRASH("unexpected RegExpTree type");
5293 }
5294
ParseRegExp(JSContext * cx,unsigned argc,Value * vp)5295 static bool ParseRegExp(JSContext* cx, unsigned argc, Value* vp) {
5296 CallArgs args = CallArgsFromVp(argc, vp);
5297 RootedObject callee(cx, &args.callee());
5298
5299 if (args.length() == 0) {
5300 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
5301 return false;
5302 }
5303
5304 if (!args[0].isString()) {
5305 ReportUsageErrorASCII(cx, callee, "First argument must be a String");
5306 return false;
5307 }
5308
5309 RegExpFlags flags = RegExpFlag::NoFlags;
5310 if (!args.get(1).isUndefined()) {
5311 if (!args.get(1).isString()) {
5312 ReportUsageErrorASCII(cx, callee,
5313 "Second argument, if present, must be a String");
5314 return false;
5315 }
5316 RootedString flagStr(cx, args[1].toString());
5317 if (!ParseRegExpFlags(cx, flagStr, &flags)) {
5318 return false;
5319 }
5320 }
5321
5322 bool match_only = false;
5323 if (!args.get(2).isUndefined()) {
5324 if (!args.get(2).isBoolean()) {
5325 ReportUsageErrorASCII(cx, callee,
5326 "Third argument, if present, must be a Boolean");
5327 return false;
5328 }
5329 match_only = args[2].toBoolean();
5330 }
5331
5332 RootedAtom pattern(cx, AtomizeString(cx, args[0].toString()));
5333 if (!pattern) {
5334 return false;
5335 }
5336
5337 CompileOptions options(cx);
5338 frontend::DummyTokenStream dummyTokenStream(cx, options);
5339
5340 // Data lifetime is controlled by LifoAllocScope.
5341 LifoAllocScope allocScope(&cx->tempLifoAlloc());
5342 irregexp::RegExpCompileData data;
5343 if (!irregexp::ParsePattern(dummyTokenStream, allocScope.alloc(), pattern,
5344 match_only, flags, &data)) {
5345 return false;
5346 }
5347
5348 RootedObject obj(
5349 cx, ConvertRegExpTreeToObject(cx, allocScope.alloc(), data.tree));
5350 if (!obj) {
5351 return false;
5352 }
5353
5354 args.rval().setObject(*obj);
5355 return true;
5356 }
5357
DisRegExp(JSContext * cx,unsigned argc,Value * vp)5358 static bool DisRegExp(JSContext* cx, unsigned argc, Value* vp) {
5359 CallArgs args = CallArgsFromVp(argc, vp);
5360 RootedObject callee(cx, &args.callee());
5361
5362 if (args.length() == 0) {
5363 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
5364 return false;
5365 }
5366
5367 if (!args[0].isObject() || !args[0].toObject().is<RegExpObject>()) {
5368 ReportUsageErrorASCII(cx, callee, "First argument must be a RegExp");
5369 return false;
5370 }
5371
5372 Rooted<RegExpObject*> reobj(cx, &args[0].toObject().as<RegExpObject>());
5373
5374 RootedLinearString input(cx, cx->runtime()->emptyString);
5375 if (!args.get(1).isUndefined()) {
5376 if (!args.get(1).isString()) {
5377 ReportUsageErrorASCII(cx, callee,
5378 "Second argument, if present, must be a String");
5379 return false;
5380 }
5381 RootedString inputStr(cx, args[1].toString());
5382 input = inputStr->ensureLinear(cx);
5383 if (!input) {
5384 return false;
5385 }
5386 }
5387
5388 if (!RegExpObject::dumpBytecode(cx, reobj, input)) {
5389 return false;
5390 }
5391
5392 args.rval().setUndefined();
5393 return true;
5394 }
5395 #endif // DEBUG && !ENABLE_NEW_REGEXP
5396
GetTimeZone(JSContext * cx,unsigned argc,Value * vp)5397 static bool GetTimeZone(JSContext* cx, unsigned argc, Value* vp) {
5398 CallArgs args = CallArgsFromVp(argc, vp);
5399 RootedObject callee(cx, &args.callee());
5400
5401 if (args.length() != 0) {
5402 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
5403 return false;
5404 }
5405
5406 auto getTimeZone = [](std::time_t* now) -> const char* {
5407 std::tm local{};
5408 #if defined(_WIN32)
5409 _tzset();
5410 if (localtime_s(&local, now) == 0) {
5411 return _tzname[local.tm_isdst > 0];
5412 }
5413 #else
5414 tzset();
5415 # if defined(HAVE_LOCALTIME_R)
5416 if (localtime_r(now, &local)) {
5417 # else
5418 std::tm* localtm = std::localtime(now);
5419 if (localtm) {
5420 *local = *localtm;
5421 # endif /* HAVE_LOCALTIME_R */
5422
5423 # if defined(HAVE_TM_ZONE_TM_GMTOFF)
5424 return local.tm_zone;
5425 # else
5426 return tzname[local.tm_isdst > 0];
5427 # endif /* HAVE_TM_ZONE_TM_GMTOFF */
5428 }
5429 #endif /* _WIN32 */
5430 return nullptr;
5431 };
5432
5433 std::time_t now = std::time(nullptr);
5434 if (now != static_cast<std::time_t>(-1)) {
5435 if (const char* tz = getTimeZone(&now)) {
5436 return ReturnStringCopy(cx, args, tz);
5437 }
5438 }
5439
5440 args.rval().setUndefined();
5441 return true;
5442 }
5443
5444 static bool SetTimeZone(JSContext* cx, unsigned argc, Value* vp) {
5445 CallArgs args = CallArgsFromVp(argc, vp);
5446 RootedObject callee(cx, &args.callee());
5447
5448 if (args.length() != 1) {
5449 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
5450 return false;
5451 }
5452
5453 if (!args[0].isString() && !args[0].isUndefined()) {
5454 ReportUsageErrorASCII(cx, callee,
5455 "First argument should be a string or undefined");
5456 return false;
5457 }
5458
5459 auto setTimeZone = [](const char* value) {
5460 #if defined(_WIN32)
5461 return _putenv_s("TZ", value) == 0;
5462 #else
5463 return setenv("TZ", value, true) == 0;
5464 #endif /* _WIN32 */
5465 };
5466
5467 auto unsetTimeZone = []() {
5468 #if defined(_WIN32)
5469 return _putenv_s("TZ", "") == 0;
5470 #else
5471 return unsetenv("TZ") == 0;
5472 #endif /* _WIN32 */
5473 };
5474
5475 if (args[0].isString() && !args[0].toString()->empty()) {
5476 RootedLinearString str(cx, args[0].toString()->ensureLinear(cx));
5477 if (!str) {
5478 return false;
5479 }
5480
5481 if (!StringIsAscii(str)) {
5482 ReportUsageErrorASCII(cx, callee,
5483 "First argument contains non-ASCII characters");
5484 return false;
5485 }
5486
5487 UniqueChars timeZone = JS_EncodeStringToASCII(cx, str);
5488 if (!timeZone) {
5489 return false;
5490 }
5491
5492 if (!setTimeZone(timeZone.get())) {
5493 JS_ReportErrorASCII(cx, "Failed to set 'TZ' environment variable");
5494 return false;
5495 }
5496 } else {
5497 if (!unsetTimeZone()) {
5498 JS_ReportErrorASCII(cx, "Failed to unset 'TZ' environment variable");
5499 return false;
5500 }
5501 }
5502
5503 #if defined(_WIN32)
5504 _tzset();
5505 #else
5506 tzset();
5507 #endif /* _WIN32 */
5508
5509 JS::ResetTimeZone();
5510
5511 args.rval().setUndefined();
5512 return true;
5513 }
5514
5515 static bool GetCoreCount(JSContext* cx, unsigned argc, Value* vp) {
5516 CallArgs args = CallArgsFromVp(argc, vp);
5517 RootedObject callee(cx, &args.callee());
5518
5519 if (args.length() != 0) {
5520 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
5521 return false;
5522 }
5523
5524 args.rval().setInt32(GetCPUCount());
5525 return true;
5526 }
5527
5528 static bool GetDefaultLocale(JSContext* cx, unsigned argc, Value* vp) {
5529 CallArgs args = CallArgsFromVp(argc, vp);
5530 RootedObject callee(cx, &args.callee());
5531
5532 if (args.length() != 0) {
5533 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
5534 return false;
5535 }
5536
5537 UniqueChars locale = JS_GetDefaultLocale(cx);
5538 if (!locale) {
5539 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5540 JSMSG_DEFAULT_LOCALE_ERROR);
5541 return false;
5542 }
5543
5544 return ReturnStringCopy(cx, args, locale.get());
5545 }
5546
5547 static bool SetDefaultLocale(JSContext* cx, unsigned argc, Value* vp) {
5548 CallArgs args = CallArgsFromVp(argc, vp);
5549 RootedObject callee(cx, &args.callee());
5550
5551 if (args.length() != 1) {
5552 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
5553 return false;
5554 }
5555
5556 if (!args[0].isString() && !args[0].isUndefined()) {
5557 ReportUsageErrorASCII(cx, callee,
5558 "First argument should be a string or undefined");
5559 return false;
5560 }
5561
5562 if (args[0].isString() && !args[0].toString()->empty()) {
5563 RootedLinearString str(cx, args[0].toString()->ensureLinear(cx));
5564 if (!str) {
5565 return false;
5566 }
5567
5568 if (!StringIsAscii(str)) {
5569 ReportUsageErrorASCII(cx, callee,
5570 "First argument contains non-ASCII characters");
5571 return false;
5572 }
5573
5574 UniqueChars locale = JS_EncodeStringToASCII(cx, str);
5575 if (!locale) {
5576 return false;
5577 }
5578
5579 bool containsOnlyValidBCP47Characters =
5580 mozilla::IsAsciiAlpha(locale[0]) &&
5581 std::all_of(locale.get(), locale.get() + str->length(), [](auto c) {
5582 return mozilla::IsAsciiAlphanumeric(c) || c == '-';
5583 });
5584
5585 if (!containsOnlyValidBCP47Characters) {
5586 ReportUsageErrorASCII(cx, callee,
5587 "First argument should be a BCP47 language tag");
5588 return false;
5589 }
5590
5591 if (!JS_SetDefaultLocale(cx->runtime(), locale.get())) {
5592 ReportOutOfMemory(cx);
5593 return false;
5594 }
5595 } else {
5596 JS_ResetDefaultLocale(cx->runtime());
5597 }
5598
5599 args.rval().setUndefined();
5600 return true;
5601 }
5602
5603 #if defined(FUZZING) && defined(__AFL_COMPILER)
5604 static bool AflLoop(JSContext* cx, unsigned argc, Value* vp) {
5605 CallArgs args = CallArgsFromVp(argc, vp);
5606
5607 uint32_t max_cnt;
5608 if (!ToUint32(cx, args.get(0), &max_cnt)) {
5609 return false;
5610 }
5611
5612 args.rval().setBoolean(!!__AFL_LOOP(max_cnt));
5613 return true;
5614 }
5615 #endif
5616
5617 static bool MonotonicNow(JSContext* cx, unsigned argc, Value* vp) {
5618 CallArgs args = CallArgsFromVp(argc, vp);
5619 double now;
5620
5621 // The std::chrono symbols are too new to be present in STL on all platforms we
5622 // care about, so use raw POSIX clock APIs when it might be necessary.
5623 #if defined(XP_UNIX) && !defined(XP_DARWIN)
5624 auto ComputeNow = [](const timespec& ts) {
5625 return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
5626 };
5627
5628 timespec ts;
5629 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
5630 // Use a monotonic clock if available.
5631 now = ComputeNow(ts);
5632 } else {
5633 // Use a realtime clock as fallback.
5634 if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
5635 // Fail if no clock is available.
5636 JS_ReportErrorASCII(cx, "can't retrieve system clock");
5637 return false;
5638 }
5639
5640 now = ComputeNow(ts);
5641
5642 // Manually enforce atomicity on a non-monotonic clock.
5643 {
5644 static mozilla::Atomic<bool, mozilla::ReleaseAcquire> spinLock;
5645 while (!spinLock.compareExchange(false, true)) {
5646 continue;
5647 }
5648
5649 static double lastNow = -FLT_MAX;
5650 now = lastNow = std::max(now, lastNow);
5651
5652 spinLock = false;
5653 }
5654 }
5655 #else
5656 using std::chrono::duration_cast;
5657 using std::chrono::milliseconds;
5658 using std::chrono::steady_clock;
5659 now = duration_cast<milliseconds>(steady_clock::now().time_since_epoch())
5660 .count();
5661 #endif // XP_UNIX && !XP_DARWIN
5662
5663 args.rval().setNumber(now);
5664 return true;
5665 }
5666
5667 static bool TimeSinceCreation(JSContext* cx, unsigned argc, Value* vp) {
5668 CallArgs args = CallArgsFromVp(argc, vp);
5669 double when =
5670 (mozilla::TimeStamp::Now() - mozilla::TimeStamp::ProcessCreation())
5671 .ToMilliseconds();
5672 args.rval().setNumber(when);
5673 return true;
5674 }
5675
5676 static bool GetErrorNotes(JSContext* cx, unsigned argc, Value* vp) {
5677 CallArgs args = CallArgsFromVp(argc, vp);
5678 if (!args.requireAtLeast(cx, "getErrorNotes", 1)) {
5679 return false;
5680 }
5681
5682 if (!args[0].isObject() || !args[0].toObject().is<ErrorObject>()) {
5683 args.rval().setNull();
5684 return true;
5685 }
5686
5687 JSErrorReport* report = args[0].toObject().as<ErrorObject>().getErrorReport();
5688 if (!report) {
5689 args.rval().setNull();
5690 return true;
5691 }
5692
5693 RootedObject notesArray(cx, CreateErrorNotesArray(cx, report));
5694 if (!notesArray) {
5695 return false;
5696 }
5697
5698 args.rval().setObject(*notesArray);
5699 return true;
5700 }
5701
5702 static bool IsConstructor(JSContext* cx, unsigned argc, Value* vp) {
5703 CallArgs args = CallArgsFromVp(argc, vp);
5704 if (args.length() < 1) {
5705 args.rval().setBoolean(false);
5706 } else {
5707 args.rval().setBoolean(IsConstructor(args[0]));
5708 }
5709 return true;
5710 }
5711
5712 static bool SetTimeResolution(JSContext* cx, unsigned argc, Value* vp) {
5713 CallArgs args = CallArgsFromVp(argc, vp);
5714 RootedObject callee(cx, &args.callee());
5715
5716 if (!args.requireAtLeast(cx, "setTimeResolution", 2)) {
5717 return false;
5718 }
5719
5720 if (!args[0].isInt32()) {
5721 ReportUsageErrorASCII(cx, callee, "First argument must be an Int32.");
5722 return false;
5723 }
5724 int32_t resolution = args[0].toInt32();
5725
5726 if (!args[1].isBoolean()) {
5727 ReportUsageErrorASCII(cx, callee, "Second argument must be a Boolean");
5728 return false;
5729 }
5730 bool jitter = args[1].toBoolean();
5731
5732 JS::SetTimeResolutionUsec(resolution, jitter);
5733
5734 args.rval().setUndefined();
5735 return true;
5736 }
5737
5738 static bool ScriptedCallerGlobal(JSContext* cx, unsigned argc, Value* vp) {
5739 CallArgs args = CallArgsFromVp(argc, vp);
5740
5741 RootedObject obj(cx, JS::GetScriptedCallerGlobal(cx));
5742 if (!obj) {
5743 args.rval().setNull();
5744 return true;
5745 }
5746
5747 obj = ToWindowProxyIfWindow(obj);
5748
5749 if (!cx->compartment()->wrap(cx, &obj)) {
5750 return false;
5751 }
5752
5753 args.rval().setObject(*obj);
5754 return true;
5755 }
5756
5757 static bool ObjectGlobal(JSContext* cx, unsigned argc, Value* vp) {
5758 CallArgs args = CallArgsFromVp(argc, vp);
5759 RootedObject callee(cx, &args.callee());
5760
5761 if (!args.get(0).isObject()) {
5762 ReportUsageErrorASCII(cx, callee, "Argument must be an object");
5763 return false;
5764 }
5765
5766 RootedObject obj(cx, &args[0].toObject());
5767 if (IsCrossCompartmentWrapper(obj)) {
5768 args.rval().setNull();
5769 return true;
5770 }
5771
5772 obj = ToWindowProxyIfWindow(&obj->nonCCWGlobal());
5773
5774 args.rval().setObject(*obj);
5775 return true;
5776 }
5777
5778 static bool IsSameCompartment(JSContext* cx, unsigned argc, Value* vp) {
5779 CallArgs args = CallArgsFromVp(argc, vp);
5780 RootedObject callee(cx, &args.callee());
5781
5782 if (!args.get(0).isObject() || !args.get(1).isObject()) {
5783 ReportUsageErrorASCII(cx, callee, "Both arguments must be objects");
5784 return false;
5785 }
5786
5787 RootedObject obj1(cx, UncheckedUnwrap(&args[0].toObject()));
5788 RootedObject obj2(cx, UncheckedUnwrap(&args[1].toObject()));
5789
5790 args.rval().setBoolean(obj1->compartment() == obj2->compartment());
5791 return true;
5792 }
5793
5794 static bool FirstGlobalInCompartment(JSContext* cx, unsigned argc, Value* vp) {
5795 CallArgs args = CallArgsFromVp(argc, vp);
5796 RootedObject callee(cx, &args.callee());
5797
5798 if (!args.get(0).isObject()) {
5799 ReportUsageErrorASCII(cx, callee, "Argument must be an object");
5800 return false;
5801 }
5802
5803 RootedObject obj(cx, UncheckedUnwrap(&args[0].toObject()));
5804 obj = ToWindowProxyIfWindow(GetFirstGlobalInCompartment(obj->compartment()));
5805
5806 if (!cx->compartment()->wrap(cx, &obj)) {
5807 return false;
5808 }
5809
5810 args.rval().setObject(*obj);
5811 return true;
5812 }
5813
5814 static bool AssertCorrectRealm(JSContext* cx, unsigned argc, Value* vp) {
5815 CallArgs args = CallArgsFromVp(argc, vp);
5816 MOZ_RELEASE_ASSERT(cx->realm() == args.callee().as<JSFunction>().realm());
5817 args.rval().setUndefined();
5818 return true;
5819 }
5820
5821 static bool GlobalLexicals(JSContext* cx, unsigned argc, Value* vp) {
5822 CallArgs args = CallArgsFromVp(argc, vp);
5823
5824 Rooted<LexicalEnvironmentObject*> globalLexical(
5825 cx, &cx->global()->lexicalEnvironment());
5826
5827 RootedIdVector props(cx);
5828 if (!GetPropertyKeys(cx, globalLexical, JSITER_HIDDEN, &props)) {
5829 return false;
5830 }
5831
5832 RootedObject res(cx, JS_NewPlainObject(cx));
5833 if (!res) {
5834 return false;
5835 }
5836
5837 RootedValue val(cx);
5838 for (size_t i = 0; i < props.length(); i++) {
5839 HandleId id = props[i];
5840 if (!JS_GetPropertyById(cx, globalLexical, id, &val)) {
5841 return false;
5842 }
5843 if (val.isMagic(JS_UNINITIALIZED_LEXICAL)) {
5844 continue;
5845 }
5846 if (!JS_DefinePropertyById(cx, res, id, val, JSPROP_ENUMERATE)) {
5847 return false;
5848 }
5849 }
5850
5851 args.rval().setObject(*res);
5852 return true;
5853 }
5854
5855 static bool MonitorType(JSContext* cx, unsigned argc, Value* vp) {
5856 CallArgs args = CallArgsFromVp(argc, vp);
5857 RootedObject callee(cx, &args.callee());
5858
5859 // First argument must be either a scripted function or null/undefined (in
5860 // this case we use the caller's script).
5861 RootedFunction fun(cx);
5862 RootedScript script(cx);
5863 if (args.get(0).isNullOrUndefined()) {
5864 script = cx->currentScript();
5865 if (!script) {
5866 ReportUsageErrorASCII(cx, callee, "No scripted caller");
5867 return false;
5868 }
5869 } else {
5870 if (!IsFunctionObject(args.get(0), fun.address()) ||
5871 !fun->isInterpreted()) {
5872 ReportUsageErrorASCII(
5873 cx, callee,
5874 "First argument must be a scripted function or null/undefined");
5875 return false;
5876 }
5877
5878 script = JSFunction::getOrCreateScript(cx, fun);
5879 if (!script) {
5880 return false;
5881 }
5882 }
5883
5884 int32_t index;
5885 if (!ToInt32(cx, args.get(1), &index)) {
5886 return false;
5887 }
5888
5889 if (index < 0 || uint32_t(index) >= jit::JitScript::NumTypeSets(script)) {
5890 ReportUsageErrorASCII(cx, callee, "Index out of range");
5891 return false;
5892 }
5893
5894 RootedValue val(cx, args.get(2));
5895
5896 // If val is "unknown" or "unknownObject" we mark the TypeSet as unknown or
5897 // unknownObject, respectively.
5898 bool unknown = false;
5899 bool unknownObject = false;
5900 if (val.isString()) {
5901 if (!JS_StringEqualsLiteral(cx, val.toString(), "unknown", &unknown)) {
5902 return false;
5903 }
5904 if (!JS_StringEqualsLiteral(cx, val.toString(), "unknownObject",
5905 &unknownObject)) {
5906 return false;
5907 }
5908 }
5909
5910 // Avoid assertion failures if TI or Baseline Interpreter is disabled or if we
5911 // can't Baseline Interpret this script.
5912 if (!IsTypeInferenceEnabled() || !jit::IsBaselineInterpreterEnabled() ||
5913 !jit::CanBaselineInterpretScript(script)) {
5914 args.rval().setUndefined();
5915 return true;
5916 }
5917
5918 AutoRealm ar(cx, script);
5919
5920 if (!cx->realm()->ensureJitRealmExists(cx)) {
5921 return false;
5922 }
5923
5924 jit::AutoKeepJitScripts keepJitScript(cx);
5925 if (!script->ensureHasJitScript(cx, keepJitScript)) {
5926 return false;
5927 }
5928
5929 AutoEnterAnalysis enter(cx);
5930 AutoSweepJitScript sweep(script);
5931 StackTypeSet* typeSet = script->jitScript()->typeArray(sweep) + index;
5932 if (unknown) {
5933 typeSet->addType(sweep, cx, TypeSet::UnknownType());
5934 } else if (unknownObject) {
5935 typeSet->addType(sweep, cx, TypeSet::AnyObjectType());
5936 } else {
5937 typeSet->addType(sweep, cx, TypeSet::GetValueType(val));
5938 }
5939
5940 args.rval().setUndefined();
5941 return true;
5942 }
5943
5944 static bool MarkObjectPropertiesUnknown(JSContext* cx, unsigned argc,
5945 Value* vp) {
5946 CallArgs args = CallArgsFromVp(argc, vp);
5947 RootedObject callee(cx, &args.callee());
5948
5949 if (!args.get(0).isObject()) {
5950 ReportUsageErrorASCII(cx, callee, "Argument must be an object");
5951 return false;
5952 }
5953
5954 RootedObject obj(cx, &args[0].toObject());
5955 RootedObjectGroup group(cx, JSObject::getGroup(cx, obj));
5956 if (!group) {
5957 return false;
5958 }
5959
5960 MarkObjectGroupUnknownProperties(cx, group);
5961
5962 args.rval().setUndefined();
5963 return true;
5964 }
5965
5966 static bool EncodeAsUtf8InBuffer(JSContext* cx, unsigned argc, Value* vp) {
5967 CallArgs args = CallArgsFromVp(argc, vp);
5968 if (!args.requireAtLeast(cx, "encodeAsUtf8InBuffer", 2)) {
5969 return false;
5970 }
5971
5972 RootedObject callee(cx, &args.callee());
5973
5974 if (!args[0].isString()) {
5975 ReportUsageErrorASCII(cx, callee, "First argument must be a String");
5976 return false;
5977 }
5978
5979 // Create the amounts array early so that the raw pointer into Uint8Array
5980 // data has as short a lifetime as possible
5981 RootedArrayObject array(cx, NewDenseFullyAllocatedArray(cx, 2));
5982 if (!array) {
5983 return false;
5984 }
5985 array->ensureDenseInitializedLength(cx, 0, 2);
5986
5987 uint32_t length;
5988 bool isSharedMemory;
5989 uint8_t* data;
5990 if (!args[1].isObject() ||
5991 !JS_GetObjectAsUint8Array(&args[1].toObject(), &length, &isSharedMemory,
5992 &data) ||
5993 isSharedMemory || // excluded views of SharedArrayBuffers
5994 !data) { // exclude views of detached ArrayBuffers
5995 ReportUsageErrorASCII(cx, callee, "Second argument must be a Uint8Array");
5996 return false;
5997 }
5998
5999 Maybe<Tuple<size_t, size_t>> amounts = JS_EncodeStringToUTF8BufferPartial(
6000 cx, args[0].toString(), AsWritableChars(MakeSpan(data, length)));
6001 if (!amounts) {
6002 ReportOutOfMemory(cx);
6003 return false;
6004 }
6005
6006 size_t unitsRead, bytesWritten;
6007 Tie(unitsRead, bytesWritten) = *amounts;
6008
6009 array->initDenseElement(0, Int32Value(AssertedCast<int32_t>(unitsRead)));
6010 array->initDenseElement(1, Int32Value(AssertedCast<int32_t>(bytesWritten)));
6011
6012 args.rval().setObject(*array);
6013 return true;
6014 }
6015
6016 JSScript* js::TestingFunctionArgumentToScript(
6017 JSContext* cx, HandleValue v, JSFunction** funp /* = nullptr */) {
6018 if (v.isString()) {
6019 // To convert a string to a script, compile it. Parse it as an ES6 Program.
6020 RootedLinearString linearStr(cx, StringToLinearString(cx, v.toString()));
6021 if (!linearStr) {
6022 return nullptr;
6023 }
6024 size_t len = GetLinearStringLength(linearStr);
6025 AutoStableStringChars linearChars(cx);
6026 if (!linearChars.initTwoByte(cx, linearStr)) {
6027 return nullptr;
6028 }
6029 const char16_t* chars = linearChars.twoByteRange().begin().get();
6030
6031 SourceText<char16_t> source;
6032 if (!source.init(cx, chars, len, SourceOwnership::Borrowed)) {
6033 return nullptr;
6034 }
6035
6036 CompileOptions options(cx);
6037 return JS::Compile(cx, options, source);
6038 }
6039
6040 RootedFunction fun(cx, JS_ValueToFunction(cx, v));
6041 if (!fun) {
6042 return nullptr;
6043 }
6044
6045 // Unwrap bound functions.
6046 while (fun->isBoundFunction()) {
6047 JSObject* target = fun->getBoundFunctionTarget();
6048 if (target && target->is<JSFunction>()) {
6049 fun = &target->as<JSFunction>();
6050 } else {
6051 break;
6052 }
6053 }
6054
6055 if (!fun->isInterpreted()) {
6056 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
6057 JSMSG_TESTING_SCRIPTS_ONLY);
6058 return nullptr;
6059 }
6060
6061 JSScript* script = JSFunction::getOrCreateScript(cx, fun);
6062 if (!script) {
6063 return nullptr;
6064 }
6065
6066 if (funp) {
6067 *funp = fun;
6068 }
6069
6070 return script;
6071 }
6072
6073 static bool BaselineCompile(JSContext* cx, unsigned argc, Value* vp) {
6074 CallArgs args = CallArgsFromVp(argc, vp);
6075 RootedObject callee(cx, &args.callee());
6076
6077 RootedScript script(cx);
6078 if (args.length() == 0) {
6079 NonBuiltinScriptFrameIter iter(cx);
6080 if (iter.done()) {
6081 ReportUsageErrorASCII(cx, callee,
6082 "no script argument and no script caller");
6083 return false;
6084 }
6085 script = iter.script();
6086 } else {
6087 script = TestingFunctionArgumentToScript(cx, args[0]);
6088 if (!script) {
6089 return false;
6090 }
6091 }
6092
6093 bool forceDebug = false;
6094 if (args.length() > 1) {
6095 if (args.length() > 2) {
6096 ReportUsageErrorASCII(cx, callee, "too many arguments");
6097 return false;
6098 }
6099 if (!args[1].isBoolean() && !args[1].isUndefined()) {
6100 ReportUsageErrorASCII(
6101 cx, callee, "forceDebugInstrumentation argument should be boolean");
6102 return false;
6103 }
6104 forceDebug = ToBoolean(args[1]);
6105 }
6106
6107 const char* returnedStr = nullptr;
6108 do {
6109 #ifdef JS_MORE_DETERMINISTIC
6110 // In order to check for differential behaviour, baselineCompile should have
6111 // the same output whether --no-baseline is used or not.
6112 if (fuzzingSafe) {
6113 returnedStr = "skipped (fuzzing-safe)";
6114 break;
6115 }
6116 #endif
6117
6118 AutoRealm ar(cx, script);
6119 if (script->hasBaselineScript()) {
6120 if (forceDebug && !script->baselineScript()->hasDebugInstrumentation()) {
6121 // There isn't an easy way to do this for a script that might be on
6122 // stack right now. See
6123 // js::jit::RecompileOnStackBaselineScriptsForDebugMode.
6124 ReportUsageErrorASCII(
6125 cx, callee, "unsupported case: recompiling script for debug mode");
6126 return false;
6127 }
6128
6129 args.rval().setUndefined();
6130 return true;
6131 }
6132
6133 if (!jit::IsBaselineJitEnabled(cx)) {
6134 returnedStr = "baseline disabled";
6135 break;
6136 }
6137 if (!script->canBaselineCompile()) {
6138 returnedStr = "can't compile";
6139 break;
6140 }
6141 if (!cx->realm()->ensureJitRealmExists(cx)) {
6142 return false;
6143 }
6144
6145 jit::MethodStatus status = jit::BaselineCompile(cx, script, forceDebug);
6146 switch (status) {
6147 case jit::Method_Error:
6148 return false;
6149 case jit::Method_CantCompile:
6150 returnedStr = "can't compile";
6151 break;
6152 case jit::Method_Skipped:
6153 returnedStr = "skipped";
6154 break;
6155 case jit::Method_Compiled:
6156 args.rval().setUndefined();
6157 }
6158 } while (false);
6159
6160 if (returnedStr) {
6161 return ReturnStringCopy(cx, args, returnedStr);
6162 }
6163
6164 return true;
6165 }
6166
6167 static bool ClearKeptObjects(JSContext* cx, unsigned argc, Value* vp) {
6168 CallArgs args = CallArgsFromVp(argc, vp);
6169 JS::ClearKeptObjects(cx);
6170 args.rval().setUndefined();
6171 return true;
6172 }
6173
6174 static bool PCCountProfiling_Start(JSContext* cx, unsigned argc, Value* vp) {
6175 CallArgs args = CallArgsFromVp(argc, vp);
6176
6177 js::StartPCCountProfiling(cx);
6178
6179 args.rval().setUndefined();
6180 return true;
6181 }
6182
6183 static bool PCCountProfiling_Stop(JSContext* cx, unsigned argc, Value* vp) {
6184 CallArgs args = CallArgsFromVp(argc, vp);
6185
6186 js::StopPCCountProfiling(cx);
6187
6188 args.rval().setUndefined();
6189 return true;
6190 }
6191
6192 static bool PCCountProfiling_Purge(JSContext* cx, unsigned argc, Value* vp) {
6193 CallArgs args = CallArgsFromVp(argc, vp);
6194
6195 js::PurgePCCounts(cx);
6196
6197 args.rval().setUndefined();
6198 return true;
6199 }
6200
6201 static bool PCCountProfiling_ScriptCount(JSContext* cx, unsigned argc,
6202 Value* vp) {
6203 CallArgs args = CallArgsFromVp(argc, vp);
6204
6205 size_t length = js::GetPCCountScriptCount(cx);
6206
6207 args.rval().setNumber(double(length));
6208 return true;
6209 }
6210
6211 static bool PCCountProfiling_ScriptSummary(JSContext* cx, unsigned argc,
6212 Value* vp) {
6213 CallArgs args = CallArgsFromVp(argc, vp);
6214 if (!args.requireAtLeast(cx, "summary", 1)) {
6215 return false;
6216 }
6217
6218 uint32_t index;
6219 if (!JS::ToUint32(cx, args[0], &index)) {
6220 return false;
6221 }
6222
6223 JSString* str = js::GetPCCountScriptSummary(cx, index);
6224 if (!str) {
6225 return false;
6226 }
6227
6228 args.rval().setString(str);
6229 return true;
6230 }
6231
6232 static bool PCCountProfiling_ScriptContents(JSContext* cx, unsigned argc,
6233 Value* vp) {
6234 CallArgs args = CallArgsFromVp(argc, vp);
6235 if (!args.requireAtLeast(cx, "contents", 1)) {
6236 return false;
6237 }
6238
6239 uint32_t index;
6240 if (!JS::ToUint32(cx, args[0], &index)) {
6241 return false;
6242 }
6243
6244 JSString* str = js::GetPCCountScriptContents(cx, index);
6245 if (!str) {
6246 return false;
6247 }
6248
6249 args.rval().setString(str);
6250 return true;
6251 }
6252
6253 // clang-format off
6254 static const JSFunctionSpecWithHelp TestingFunctions[] = {
6255 JS_FN_HELP("gc", ::GC, 0, 0,
6256 "gc([obj] | 'zone' [, ('shrinking' | 'last-ditch') ])",
6257 " Run the garbage collector.\n"
6258 " The first parameter describes which zones to collect: if an object is\n"
6259 " given, GC only its zone. If 'zone' is given, GC any zones that were\n"
6260 " scheduled via schedulegc.\n"
6261 " The second parameter is optional and may be 'shrinking' to perform a\n"
6262 " shrinking GC or 'last-ditch' for a shrinking, last-ditch GC."),
6263
6264 JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
6265 "minorgc([aboutToOverflow])",
6266 " Run a minor collector on the Nursery. When aboutToOverflow is true, marks\n"
6267 " the store buffer as about-to-overflow before collecting."),
6268
6269 JS_FN_HELP("maybegc", ::MaybeGC, 0, 0,
6270 "maybegc()",
6271 " Hint to the engine that now is an ok time to run the garbage collector.\n"),
6272
6273 JS_FN_HELP("gcparam", GCParameter, 2, 0,
6274 "gcparam(name [, value])",
6275 " Wrapper for JS_[GS]etGCParameter. The name is one of:" GC_PARAMETER_ARGS_LIST),
6276
6277 JS_FN_HELP("relazifyFunctions", RelazifyFunctions, 0, 0,
6278 "relazifyFunctions(...)",
6279 " Perform a GC and allow relazification of functions. Accepts the same\n"
6280 " arguments as gc()."),
6281
6282 JS_FN_HELP("getBuildConfiguration", GetBuildConfiguration, 0, 0,
6283 "getBuildConfiguration()",
6284 " Return an object describing some of the configuration options SpiderMonkey\n"
6285 " was built with."),
6286
6287 JS_FN_HELP("isLcovEnabled", ::IsLCovEnabled, 0, 0,
6288 "isLcovEnabled()",
6289 " Return true if JS LCov support is enabled."),
6290
6291 JS_FN_HELP("isTypeInferenceEnabled", ::IsTypeInferenceEnabled, 0, 0,
6292 "isTypeInferenceEnabled()",
6293 " Return true if Type Inference is enabled."),
6294
6295 JS_FN_HELP("hasChild", HasChild, 0, 0,
6296 "hasChild(parent, child)",
6297 " Return true if |child| is a child of |parent|, as determined by a call to\n"
6298 " TraceChildren"),
6299
6300 JS_FN_HELP("setSavedStacksRNGState", SetSavedStacksRNGState, 1, 0,
6301 "setSavedStacksRNGState(seed)",
6302 " Set this compartment's SavedStacks' RNG state.\n"),
6303
6304 JS_FN_HELP("getSavedFrameCount", GetSavedFrameCount, 0, 0,
6305 "getSavedFrameCount()",
6306 " Return the number of SavedFrame instances stored in this compartment's\n"
6307 " SavedStacks cache."),
6308
6309 JS_FN_HELP("clearSavedFrames", ClearSavedFrames, 0, 0,
6310 "clearSavedFrames()",
6311 " Empty the current compartment's cache of SavedFrame objects, so that\n"
6312 " subsequent stack captures allocate fresh objects to represent frames.\n"
6313 " Clear the current stack's LiveSavedFrameCaches."),
6314
6315 JS_FN_HELP("saveStack", SaveStack, 0, 0,
6316 "saveStack([maxDepth [, compartment]])",
6317 " Capture a stack. If 'maxDepth' is given, capture at most 'maxDepth' number\n"
6318 " of frames. If 'compartment' is given, allocate the js::SavedFrame instances\n"
6319 " with the given object's compartment."),
6320
6321 JS_FN_HELP("captureFirstSubsumedFrame", CaptureFirstSubsumedFrame, 1, 0,
6322 "saveStack(object [, shouldIgnoreSelfHosted = true]])",
6323 " Capture a stack back to the first frame whose principals are subsumed by the\n"
6324 " object's compartment's principals. If 'shouldIgnoreSelfHosted' is given,\n"
6325 " control whether self-hosted frames are considered when checking principals."),
6326
6327 JS_FN_HELP("callFunctionFromNativeFrame", CallFunctionFromNativeFrame, 1, 0,
6328 "callFunctionFromNativeFrame(function)",
6329 " Call 'function' with a (C++-)native frame on stack.\n"
6330 " Required for testing that SaveStack properly handles native frames."),
6331
6332 JS_FN_HELP("callFunctionWithAsyncStack", CallFunctionWithAsyncStack, 0, 0,
6333 "callFunctionWithAsyncStack(function, stack, asyncCause)",
6334 " Call 'function', using the provided stack as the async stack responsible\n"
6335 " for the call, and propagate its return value or the exception it throws.\n"
6336 " The function is called with no arguments, and 'this' is 'undefined'. The\n"
6337 " specified |asyncCause| is attached to the provided stack frame."),
6338
6339 JS_FN_HELP("enableTrackAllocations", EnableTrackAllocations, 0, 0,
6340 "enableTrackAllocations()",
6341 " Start capturing the JS stack at every allocation. Note that this sets an\n"
6342 " object metadata callback that will override any other object metadata\n"
6343 " callback that may be set."),
6344
6345 JS_FN_HELP("disableTrackAllocations", DisableTrackAllocations, 0, 0,
6346 "disableTrackAllocations()",
6347 " Stop capturing the JS stack at every allocation."),
6348
6349 JS_FN_HELP("setTestFilenameValidationCallback", SetTestFilenameValidationCallback, 0, 0,
6350 "setTestFilenameValidationCallback()",
6351 " Set the filename validation callback to a callback that accepts only\n"
6352 " filenames starting with 'safe' or (only in system realms) 'system'."),
6353
6354 JS_FN_HELP("newExternalString", NewExternalString, 1, 0,
6355 "newExternalString(str)",
6356 " Copies str's chars and returns a new external string."),
6357
6358 JS_FN_HELP("newMaybeExternalString", NewMaybeExternalString, 1, 0,
6359 "newMaybeExternalString(str)",
6360 " Like newExternalString but uses the JS_NewMaybeExternalString API."),
6361
6362 JS_FN_HELP("ensureLinearString", EnsureLinearString, 1, 0,
6363 "ensureLinearString(str)",
6364 " Ensures str is a linear (non-rope) string and returns it."),
6365
6366 JS_FN_HELP("representativeStringArray", RepresentativeStringArray, 0, 0,
6367 "representativeStringArray()",
6368 " Returns an array of strings that represent the various internal string\n"
6369 " types and character encodings."),
6370
6371 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
6372
6373 JS_FN_HELP("oomThreadTypes", OOMThreadTypes, 0, 0,
6374 "oomThreadTypes()",
6375 " Get the number of thread types that can be used as an argument for\n"
6376 " oomAfterAllocations() and oomAtAllocation()."),
6377
6378 JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 2, 0,
6379 "oomAfterAllocations(count [,threadType])",
6380 " After 'count' js_malloc memory allocations, fail every following allocation\n"
6381 " (return nullptr). The optional thread type limits the effect to the\n"
6382 " specified type of helper thread."),
6383
6384 JS_FN_HELP("oomAtAllocation", OOMAtAllocation, 2, 0,
6385 "oomAtAllocation(count [,threadType])",
6386 " After 'count' js_malloc memory allocations, fail the next allocation\n"
6387 " (return nullptr). The optional thread type limits the effect to the\n"
6388 " specified type of helper thread."),
6389
6390 JS_FN_HELP("resetOOMFailure", ResetOOMFailure, 0, 0,
6391 "resetOOMFailure()",
6392 " Remove the allocation failure scheduled by either oomAfterAllocations() or\n"
6393 " oomAtAllocation() and return whether any allocation had been caused to fail."),
6394
6395 JS_FN_HELP("oomTest", OOMTest, 0, 0,
6396 "oomTest(function, [expectExceptionOnFailure = true | options])",
6397 " Test that the passed function behaves correctly under OOM conditions by\n"
6398 " repeatedly executing it and simulating allocation failure at successive\n"
6399 " allocations until the function completes without seeing a failure.\n"
6400 " By default this tests that an exception is raised if execution fails, but\n"
6401 " this can be disabled by passing false as the optional second parameter.\n"
6402 " This is also disabled when --fuzzing-safe is specified.\n"
6403 " Alternatively an object can be passed to set the following options:\n"
6404 " expectExceptionOnFailure: bool - as described above.\n"
6405 " keepFailing: bool - continue to fail after first simulated failure.\n"
6406 "\n"
6407 " WARNING: By design, oomTest assumes the test-function follows the same\n"
6408 " code path each time it is called, right up to the point where OOM occurs.\n"
6409 " If on iteration 70 it finishes and caches a unit of work that saves 65\n"
6410 " allocations the next time we run, then the subsequent 65 allocation\n"
6411 " points will go untested.\n"
6412 "\n"
6413 " Things in this category include lazy parsing and baseline compilation,\n"
6414 " so it is very easy to accidentally write an oomTest that only tests one\n"
6415 " or the other of those, and not the functionality you meant to test!\n"
6416 " To avoid lazy parsing, call the test function once first before passing\n"
6417 " it to oomTest. The jits can be disabled via the test harness.\n"),
6418
6419 JS_FN_HELP("stackTest", StackTest, 0, 0,
6420 "stackTest(function, [expectExceptionOnFailure = true])",
6421 " This function behaves exactly like oomTest with the difference that\n"
6422 " instead of simulating regular OOM conditions, it simulates the engine\n"
6423 " running out of stack space (failing recursion check).\n"
6424 "\n"
6425 " See the WARNING in help('oomTest').\n"),
6426
6427 JS_FN_HELP("interruptTest", InterruptTest, 0, 0,
6428 "interruptTest(function)",
6429 " This function simulates interrupts similar to how oomTest simulates OOM conditions."
6430 "\n"
6431 " See the WARNING in help('oomTest').\n"),
6432
6433 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
6434
6435 JS_FN_HELP("newRope", NewRope, 3, 0,
6436 "newRope(left, right[, options])",
6437 " Creates a rope with the given left/right strings.\n"
6438 " Available options:\n"
6439 " nursery: bool - force the string to be created in/out of the nursery, if possible.\n"),
6440
6441 JS_FN_HELP("isRope", IsRope, 1, 0,
6442 "isRope(str)",
6443 " Returns true if the parameter is a rope"),
6444
6445 JS_FN_HELP("settlePromiseNow", SettlePromiseNow, 1, 0,
6446 "settlePromiseNow(promise)",
6447 " 'Settle' a 'promise' immediately. This just marks the promise as resolved\n"
6448 " with a value of `undefined` and causes the firing of any onPromiseSettled\n"
6449 " hooks set on Debugger instances that are observing the given promise's\n"
6450 " global as a debuggee."),
6451 JS_FN_HELP("getWaitForAllPromise", GetWaitForAllPromise, 1, 0,
6452 "getWaitForAllPromise(densePromisesArray)",
6453 " Calls the 'GetWaitForAllPromise' JSAPI function and returns the result\n"
6454 " Promise."),
6455 JS_FN_HELP("resolvePromise", ResolvePromise, 2, 0,
6456 "resolvePromise(promise, resolution)",
6457 " Resolve a Promise by calling the JSAPI function JS::ResolvePromise."),
6458 JS_FN_HELP("rejectPromise", RejectPromise, 2, 0,
6459 "rejectPromise(promise, reason)",
6460 " Reject a Promise by calling the JSAPI function JS::RejectPromise."),
6461
6462 JS_FN_HELP("streamsAreEnabled", StreamsAreEnabled, 0, 0,
6463 "streamsAreEnabled()",
6464 " Returns a boolean indicating whether WHATWG Streams are enabled for the current realm."),
6465
6466 JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
6467 "makeFinalizeObserver()",
6468 " Get a special object whose finalization increases the counter returned\n"
6469 " by the finalizeCount function."),
6470
6471 JS_FN_HELP("finalizeCount", FinalizeCount, 0, 0,
6472 "finalizeCount()",
6473 " Return the current value of the finalization counter that is incremented\n"
6474 " each time an object returned by the makeFinalizeObserver is finalized."),
6475
6476 JS_FN_HELP("resetFinalizeCount", ResetFinalizeCount, 0, 0,
6477 "resetFinalizeCount()",
6478 " Reset the value returned by finalizeCount()."),
6479
6480 JS_FN_HELP("gcPreserveCode", GCPreserveCode, 0, 0,
6481 "gcPreserveCode()",
6482 " Preserve JIT code during garbage collections."),
6483
6484 #ifdef JS_GC_ZEAL
6485 JS_FN_HELP("gczeal", GCZeal, 2, 0,
6486 "gczeal(mode, [frequency])",
6487 gc::ZealModeHelpText),
6488
6489 JS_FN_HELP("unsetgczeal", UnsetGCZeal, 2, 0,
6490 "unsetgczeal(mode)",
6491 " Turn off a single zeal mode set with gczeal() and don't finish any ongoing\n"
6492 " collection that may be happening."),
6493
6494 JS_FN_HELP("schedulegc", ScheduleGC, 1, 0,
6495 "schedulegc([num])",
6496 " If num is given, schedule a GC after num allocations.\n"
6497 " Returns the number of allocations before the next trigger."),
6498
6499 JS_FN_HELP("selectforgc", SelectForGC, 0, 0,
6500 "selectforgc(obj1, obj2, ...)",
6501 " Schedule the given objects to be marked in the next GC slice."),
6502
6503 JS_FN_HELP("verifyprebarriers", VerifyPreBarriers, 0, 0,
6504 "verifyprebarriers()",
6505 " Start or end a run of the pre-write barrier verifier."),
6506
6507 JS_FN_HELP("verifypostbarriers", VerifyPostBarriers, 0, 0,
6508 "verifypostbarriers()",
6509 " Does nothing (the post-write barrier verifier has been remove)."),
6510
6511 JS_FN_HELP("currentgc", CurrentGC, 0, 0,
6512 "currentgc()",
6513 " Report various information about the currently running incremental GC,\n"
6514 " if one is running."),
6515
6516 JS_FN_HELP("deterministicgc", DeterministicGC, 1, 0,
6517 "deterministicgc(true|false)",
6518 " If true, only allow determinstic GCs to run."),
6519
6520 JS_FN_HELP("dumpGCArenaInfo", DumpGCArenaInfo, 0, 0,
6521 "dumpGCArenaInfo()",
6522 " Prints information about the different GC things and how they are arranged\n"
6523 " in arenas.\n"),
6524 #endif
6525
6526 JS_FN_HELP("gcstate", GCState, 0, 0,
6527 "gcstate([obj])",
6528 " Report the global GC state, or the GC state for the zone containing |obj|."),
6529
6530 JS_FN_HELP("schedulezone", ScheduleZoneForGC, 1, 0,
6531 "schedulezone([obj | string])",
6532 " If obj is given, schedule a GC of obj's zone.\n"
6533 " If string is given, schedule a GC of the string's zone if possible."),
6534
6535 JS_FN_HELP("startgc", StartGC, 1, 0,
6536 "startgc([n [, 'shrinking']])",
6537 " Start an incremental GC and run a slice that processes about n objects.\n"
6538 " If 'shrinking' is passesd as the optional second argument, perform a\n"
6539 " shrinking GC rather than a normal GC. If no zones have been selected with\n"
6540 " schedulegc(), a full GC will be performed."),
6541
6542 JS_FN_HELP("finishgc", FinishGC, 0, 0,
6543 "finishgc()",
6544 " Finish an in-progress incremental GC, if none is running then do nothing."),
6545
6546 JS_FN_HELP("gcslice", GCSlice, 1, 0,
6547 "gcslice([n])",
6548 " Start or continue an an incremental GC, running a slice that processes about n objects."),
6549
6550 JS_FN_HELP("abortgc", AbortGC, 1, 0,
6551 "abortgc()",
6552 " Abort the current incremental GC."),
6553
6554 JS_FN_HELP("fullcompartmentchecks", FullCompartmentChecks, 1, 0,
6555 "fullcompartmentchecks(true|false)",
6556 " If true, check for compartment mismatches before every GC."),
6557
6558 JS_FN_HELP("nondeterministicGetWeakMapKeys", NondeterministicGetWeakMapKeys, 1, 0,
6559 "nondeterministicGetWeakMapKeys(weakmap)",
6560 " Return an array of the keys in the given WeakMap."),
6561
6562 JS_FN_HELP("internalConst", InternalConst, 1, 0,
6563 "internalConst(name)",
6564 " Query an internal constant for the engine. See InternalConst source for\n"
6565 " the list of constant names."),
6566
6567 JS_FN_HELP("isProxy", IsProxy, 1, 0,
6568 "isProxy(obj)",
6569 " If true, obj is a proxy of some sort"),
6570
6571 JS_FN_HELP("dumpHeap", DumpHeap, 1, 0,
6572 "dumpHeap([filename])",
6573 " Dump reachable and unreachable objects to the named file, or to stdout. Objects\n"
6574 " in the nursery are ignored, so if you wish to include them, consider calling\n"
6575 " minorgc() first."),
6576
6577 JS_FN_HELP("terminate", Terminate, 0, 0,
6578 "terminate()",
6579 " Terminate JavaScript execution, as if we had run out of\n"
6580 " memory or been terminated by the slow script dialog."),
6581
6582 JS_FN_HELP("readGeckoProfilingStack", ReadGeckoProfilingStack, 0, 0,
6583 "readGeckoProfilingStack()",
6584 " Reads the jit stack using ProfilingFrameIterator."),
6585
6586 JS_FN_HELP("enableOsiPointRegisterChecks", EnableOsiPointRegisterChecks, 0, 0,
6587 "enableOsiPointRegisterChecks()",
6588 " Emit extra code to verify live regs at the start of a VM call are not\n"
6589 " modified before its OsiPoint."),
6590
6591 JS_FN_HELP("displayName", DisplayName, 1, 0,
6592 "displayName(fn)",
6593 " Gets the display name for a function, which can possibly be a guessed or\n"
6594 " inferred name based on where the function was defined. This can be\n"
6595 " different from the 'name' property on the function."),
6596
6597 JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable, 0, 0,
6598 "isAsmJSCompilationAvailable",
6599 " Returns whether asm.js compilation is currently available or whether it is disabled\n"
6600 " (e.g., by the debugger)."),
6601
6602 JS_FN_HELP("getJitCompilerOptions", GetJitCompilerOptions, 0, 0,
6603 "getJitCompilerOptions()",
6604 " Return an object describing some of the JIT compiler options.\n"),
6605
6606 JS_FN_HELP("isAsmJSModule", IsAsmJSModule, 1, 0,
6607 "isAsmJSModule(fn)",
6608 " Returns whether the given value is a function containing \"use asm\" that has been\n"
6609 " validated according to the asm.js spec."),
6610
6611 JS_FN_HELP("isAsmJSFunction", IsAsmJSFunction, 1, 0,
6612 "isAsmJSFunction(fn)",
6613 " Returns whether the given value is a nested function in an asm.js module that has been\n"
6614 " both compile- and link-time validated."),
6615
6616 JS_FN_HELP("wasmIsSupported", WasmIsSupported, 0, 0,
6617 "wasmIsSupported()",
6618 " Returns a boolean indicating whether WebAssembly is supported on the current device."),
6619
6620 JS_FN_HELP("wasmIsSupportedByHardware", WasmIsSupportedByHardware, 0, 0,
6621 "wasmIsSupportedByHardware()",
6622 " Returns a boolean indicating whether WebAssembly is supported on the current hardware (regardless of whether we've enabled support)."),
6623
6624 JS_FN_HELP("wasmDebuggingIsSupported", WasmDebuggingIsSupported, 0, 0,
6625 "wasmDebuggingIsSupported()",
6626 " Returns a boolean indicating whether WebAssembly debugging is supported on the current device;\n"
6627 " returns false also if WebAssembly is not supported"),
6628
6629 JS_FN_HELP("wasmStreamingIsSupported", WasmStreamingIsSupported, 0, 0,
6630 "wasmStreamingIsSupported()",
6631 " Returns a boolean indicating whether WebAssembly caching is supported by the runtime."),
6632
6633 JS_FN_HELP("wasmCachingIsSupported", WasmCachingIsSupported, 0, 0,
6634 "wasmCachingIsSupported()",
6635 " Returns a boolean indicating whether WebAssembly caching is supported by the runtime."),
6636
6637 JS_FN_HELP("wasmHugeMemoryIsSupported", WasmHugeMemoryIsSupported, 0, 0,
6638 "wasmHugeMemoryIsSupported()",
6639 " Returns a boolean indicating whether WebAssembly supports using a large"
6640 " virtual memory reservation in order to elide bounds checks on this platform."),
6641
6642 JS_FN_HELP("wasmThreadsSupported", WasmThreadsSupported, 0, 0,
6643 "wasmThreadsSupported()",
6644 " Returns a boolean indicating whether the WebAssembly threads proposal is\n"
6645 " supported on the current device."),
6646
6647 JS_FN_HELP("wasmBulkMemSupported", WasmBulkMemSupported, 0, 0,
6648 "wasmBulkMemSupported()",
6649 " Returns a boolean indicating whether the WebAssembly bulk memory proposal is\n"
6650 " supported on the current device."),
6651
6652 JS_FN_HELP("wasmSimdSupported", WasmSimdSupported, 0, 0,
6653 "wasmSimdSupported()",
6654 " Returns a boolean indicating whether WebAssembly SIMD is supported by the\n"
6655 " compilers and runtime."),
6656
6657 JS_FN_HELP("wasmCompilersPresent", WasmCompilersPresent, 0, 0,
6658 "wasmCompilersPresent()",
6659 " Returns a string indicating the present wasm compilers: a comma-separated list\n"
6660 " of 'baseline', 'ion', and 'cranelift'. A compiler is present in the executable\n"
6661 " if it is compiled in and can generate code for the current architecture."),
6662
6663 JS_FN_HELP("wasmCompileMode", WasmCompileMode, 0, 0,
6664 "wasmCompileMode()",
6665 " Returns a string indicating the available wasm compilers: 'baseline', 'ion',\n"
6666 " 'cranelift', 'baseline+ion', 'baseline+cranelift', or 'none'. A compiler is\n"
6667 " available if it is present in the executable and not disabled by switches\n"
6668 " or runtime conditions. At most one baseline and one optimizing compiler can\n"
6669 " be available."),
6670
6671 JS_FN_HELP("wasmCraneliftDisabledByFeatures", WasmCraneliftDisabledByFeatures, 0, 0,
6672 "wasmCraneliftDisabledByFeatures()",
6673 " If some feature is enabled at compile-time or run-time that prevents Cranelift\n"
6674 " from being used then this returns a truthy string describing the features that\n."
6675 " are disabling it. Otherwise it returns false."),
6676
6677 JS_FN_HELP("wasmIonDisabledByFeatures", WasmIonDisabledByFeatures, 0, 0,
6678 "wasmIonDisabledByFeatures()",
6679 " If some feature is enabled at compile-time or run-time that prevents Ion\n"
6680 " from being used then this returns a truthy string describing the features that\n."
6681 " are disabling it. Otherwise it returns false."),
6682
6683 JS_FN_HELP("wasmExtractCode", WasmExtractCode, 1, 0,
6684 "wasmExtractCode(module[, tier])",
6685 " Extracts generated machine code from WebAssembly.Module. The tier is a string,\n"
6686 " 'stable', 'best', 'baseline', or 'ion'; the default is 'stable'. If the request\n"
6687 " cannot be satisfied then null is returned. If the request is 'ion' then block\n"
6688 " until background compilation is complete."),
6689
6690 JS_FN_HELP("wasmDis", WasmDisassemble, 1, 0,
6691 "wasmDis(function[, tier])",
6692 " Disassembles generated machine code from an exported WebAssembly function.\n"
6693 " The tier is a string, 'stable', 'best', 'baseline', or 'ion'; the default is\n"
6694 " 'stable'."),
6695
6696 JS_FN_HELP("wasmHasTier2CompilationCompleted", WasmHasTier2CompilationCompleted, 1, 0,
6697 "wasmHasTier2CompilationCompleted(module)",
6698 " Returns a boolean indicating whether a given module has finished compiled code for tier2. \n"
6699 "This will return true early if compilation isn't two-tiered. "),
6700
6701 JS_FN_HELP("wasmLoadedFromCache", WasmLoadedFromCache, 1, 0,
6702 "wasmLoadedFromCache(module)",
6703 " Returns a boolean indicating whether a given module was deserialized directly from a\n"
6704 " cache (as opposed to compiled from bytecode)."),
6705
6706 JS_FN_HELP("wasmReftypesEnabled", WasmReftypesEnabled, 1, 0,
6707 "wasmReftypesEnabled()",
6708 " Returns a boolean indicating whether the WebAssembly reftypes proposal is enabled."),
6709
6710 JS_FN_HELP("wasmGcEnabled", WasmGcEnabled, 1, 0,
6711 "wasmGcEnabled()",
6712 " Returns a boolean indicating whether the WebAssembly GC types proposal is enabled."),
6713
6714 JS_FN_HELP("wasmMultiValueEnabled", WasmMultiValueEnabled, 1, 0,
6715 "wasmMultiValueEnabled()",
6716 " Returns a boolean indicating whether the WebAssembly multi-value proposal is enabled."),
6717
6718 #if defined(ENABLE_WASM_SIMD) && defined(DEBUG)
6719 JS_FN_HELP("wasmSimdAnalysis", WasmSimdAnalysis, 1, 0,
6720 "wasmSimdAnalysis(...)",
6721 " Unstable API for white-box testing.\n"),
6722 #endif
6723
6724 JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0,
6725 "isLazyFunction(fun)",
6726 " True if fun is a lazy JSFunction."),
6727
6728 JS_FN_HELP("isRelazifiableFunction", IsRelazifiableFunction, 1, 0,
6729 "isRelazifiableFunction(fun)",
6730 " True if fun is a JSFunction with a relazifiable JSScript."),
6731
6732 JS_FN_HELP("enableShellAllocationMetadataBuilder", EnableShellAllocationMetadataBuilder, 0, 0,
6733 "enableShellAllocationMetadataBuilder()",
6734 " Use ShellAllocationMetadataBuilder to supply metadata for all newly created objects."),
6735
6736 JS_FN_HELP("getAllocationMetadata", GetAllocationMetadata, 1, 0,
6737 "getAllocationMetadata(obj)",
6738 " Get the metadata for an object."),
6739
6740 JS_INLINABLE_FN_HELP("bailout", testingFunc_bailout, 0, 0, TestBailout,
6741 "bailout()",
6742 " Force a bailout out of ionmonkey (if running in ionmonkey)."),
6743
6744 JS_FN_HELP("bailAfter", testingFunc_bailAfter, 1, 0,
6745 "bailAfter(number)",
6746 " Start a counter to bail once after passing the given amount of possible bailout positions in\n"
6747 " ionmonkey.\n"),
6748
6749
6750 JS_FN_HELP("inJit", testingFunc_inJit, 0, 0,
6751 "inJit()",
6752 " Returns true when called within (jit-)compiled code. When jit compilation is disabled this\n"
6753 " function returns an error string. This function returns false in all other cases.\n"
6754 " Depending on truthiness, you should continue to wait for compilation to happen or stop execution.\n"),
6755
6756 JS_FN_HELP("inIon", testingFunc_inIon, 0, 0,
6757 "inIon()",
6758 " Returns true when called within ion. When ion is disabled or when compilation is abnormally\n"
6759 " slow to start, this function returns an error string. Otherwise, this function returns false.\n"
6760 " This behaviour ensures that a falsy value means that we are not in ion, but expect a\n"
6761 " compilation to occur in the future. Conversely, a truthy value means that we are either in\n"
6762 " ion or that there is litle or no chance of ion ever compiling the current script."),
6763
6764 JS_FN_HELP("assertJitStackInvariants", TestingFunc_assertJitStackInvariants, 0, 0,
6765 "assertJitStackInvariants()",
6766 " Iterates the Jit stack and check that stack invariants hold."),
6767
6768 JS_FN_HELP("setIonCheckGraphCoherency", SetIonCheckGraphCoherency, 1, 0,
6769 "setIonCheckGraphCoherency(bool)",
6770 " Set whether Ion should perform graph consistency (DEBUG-only) assertions. These assertions\n"
6771 " are valuable and should be generally enabled, however they can be very expensive for large\n"
6772 " (wasm) programs."),
6773
6774 JS_FN_HELP("serialize", testingFunc_serialize, 1, 0,
6775 "serialize(data, [transferables, [policy]])",
6776 " Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
6777 " clone buffer object. 'policy' may be an options hash. Valid keys:\n"
6778 " 'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n"
6779 " to specify whether SharedArrayBuffers may be serialized.\n"
6780 " 'scope' - SameProcess, DifferentProcess, or\n"
6781 " DifferentProcessForIndexedDB. Determines how some values will be\n"
6782 " serialized. Clone buffers may only be deserialized with a compatible\n"
6783 " scope. NOTE - For DifferentProcess/DifferentProcessForIndexedDB,\n"
6784 " must also set SharedArrayBuffer:'deny' if data contains any shared memory\n"
6785 " object."),
6786
6787 JS_FN_HELP("deserialize", Deserialize, 1, 0,
6788 "deserialize(clonebuffer[, opts])",
6789 " Deserialize data generated by serialize. 'opts' may be an options hash.\n"
6790 " Valid keys:\n"
6791 " 'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n"
6792 " to specify whether SharedArrayBuffers may be serialized.\n"
6793 " 'scope', which limits the clone buffers that are considered\n"
6794 " valid. Allowed values: ''SameProcess', 'DifferentProcess',\n"
6795 " and 'DifferentProcessForIndexedDB'. So for example, a\n"
6796 " DifferentProcessForIndexedDB clone buffer may be deserialized in any scope, but\n"
6797 " a SameProcess clone buffer cannot be deserialized in a\n"
6798 " DifferentProcess scope."),
6799
6800 JS_FN_HELP("detachArrayBuffer", DetachArrayBuffer, 1, 0,
6801 "detachArrayBuffer(buffer)",
6802 " Detach the given ArrayBuffer object from its memory, i.e. as if it\n"
6803 " had been transferred to a WebWorker."),
6804
6805 JS_FN_HELP("helperThreadCount", HelperThreadCount, 0, 0,
6806 "helperThreadCount()",
6807 " Returns the number of helper threads available for off-thread tasks."),
6808
6809 JS_FN_HELP("enableShapeConsistencyChecks", EnableShapeConsistencyChecks, 0, 0,
6810 "enableShapeConsistencyChecks()",
6811 " Enable some slow Shape assertions.\n"),
6812
6813 #ifdef JS_TRACE_LOGGING
6814 JS_FN_HELP("startTraceLogger", EnableTraceLogger, 0, 0,
6815 "startTraceLogger()",
6816 " Start logging this thread.\n"),
6817
6818 JS_FN_HELP("stopTraceLogger", DisableTraceLogger, 0, 0,
6819 "stopTraceLogger()",
6820 " Stop logging this thread."),
6821 #endif
6822
6823 JS_FN_HELP("reportOutOfMemory", ReportOutOfMemory, 0, 0,
6824 "reportOutOfMemory()",
6825 " Report OOM, then clear the exception and return undefined. For crash testing."),
6826
6827 JS_FN_HELP("throwOutOfMemory", ThrowOutOfMemory, 0, 0,
6828 "throwOutOfMemory()",
6829 " Throw out of memory exception, for OOM handling testing."),
6830
6831 JS_FN_HELP("reportLargeAllocationFailure", ReportLargeAllocationFailure, 0, 0,
6832 "reportLargeAllocationFailure()",
6833 " Call the large allocation failure callback, as though a large malloc call failed,\n"
6834 " then return undefined. In Gecko, this sends a memory pressure notification, which\n"
6835 " can free up some memory."),
6836
6837 JS_FN_HELP("findPath", FindPath, 2, 0,
6838 "findPath(start, target)",
6839 " Return an array describing one of the shortest paths of GC heap edges from\n"
6840 " |start| to |target|, or |undefined| if |target| is unreachable from |start|.\n"
6841 " Each element of the array is either of the form:\n"
6842 " { node: <object or string>, edge: <string describing edge from node> }\n"
6843 " if the node is a JavaScript object or value; or of the form:\n"
6844 " { type: <string describing node>, edge: <string describing edge> }\n"
6845 " if the node is some internal thing that is not a proper JavaScript value\n"
6846 " (like a shape or a scope chain element). The destination of the i'th array\n"
6847 " element's edge is the node of the i+1'th array element; the destination of\n"
6848 " the last array element is implicitly |target|.\n"),
6849
6850 JS_FN_HELP("shortestPaths", ShortestPaths, 3, 0,
6851 "shortestPaths(start, targets, maxNumPaths)",
6852 " Return an array of arrays of shortest retaining paths. There is an array of\n"
6853 " shortest retaining paths for each object in |targets|. The maximum number of\n"
6854 " paths in each of those arrays is bounded by |maxNumPaths|. Each element in a\n"
6855 " path is of the form |{ predecessor, edge }|."),
6856
6857 #if defined(DEBUG) || defined(JS_JITSPEW)
6858 JS_FN_HELP("dumpObject", DumpObject, 1, 0,
6859 "dumpObject()",
6860 " Dump an internal representation of an object."),
6861 #endif
6862
6863 JS_FN_HELP("sharedMemoryEnabled", SharedMemoryEnabled, 0, 0,
6864 "sharedMemoryEnabled()",
6865 " Return true if SharedArrayBuffer and Atomics are enabled"),
6866
6867 JS_FN_HELP("sharedArrayRawBufferCount", SharedArrayRawBufferCount, 0, 0,
6868 "sharedArrayRawBufferCount()",
6869 " Return the number of live SharedArrayRawBuffer objects"),
6870
6871 JS_FN_HELP("sharedArrayRawBufferRefcount", SharedArrayRawBufferRefcount, 0, 0,
6872 "sharedArrayRawBufferRefcount(sab)",
6873 " Return the reference count of the SharedArrayRawBuffer object held by sab"),
6874
6875 #ifdef NIGHTLY_BUILD
6876 JS_FN_HELP("objectAddress", ObjectAddress, 1, 0,
6877 "objectAddress(obj)",
6878 " Return the current address of the object. For debugging only--this\n"
6879 " address may change during a moving GC."),
6880
6881 JS_FN_HELP("sharedAddress", SharedAddress, 1, 0,
6882 "sharedAddress(obj)",
6883 " Return the address of the shared storage of a SharedArrayBuffer."),
6884 #endif
6885
6886 JS_FN_HELP("evalReturningScope", EvalReturningScope, 1, 0,
6887 "evalReturningScope(scriptStr, [global])",
6888 " Evaluate the script in a new scope and return the scope.\n"
6889 " If |global| is present, clone the script to |global| before executing."),
6890
6891 JS_FN_HELP("cloneAndExecuteScript", ShellCloneAndExecuteScript, 2, 0,
6892 "cloneAndExecuteScript(source, global)",
6893 " Compile |source| in the current compartment, clone it into |global|'s\n"
6894 " compartment, and run it there."),
6895
6896 JS_FN_HELP("backtrace", DumpBacktrace, 1, 0,
6897 "backtrace()",
6898 " Dump out a brief backtrace."),
6899
6900 JS_FN_HELP("getBacktrace", GetBacktrace, 1, 0,
6901 "getBacktrace([options])",
6902 " Return the current stack as a string. Takes an optional options object,\n"
6903 " which may contain any or all of the boolean properties\n"
6904 " options.args - show arguments to each function\n"
6905 " options.locals - show local variables in each frame\n"
6906 " options.thisprops - show the properties of the 'this' object of each frame\n"),
6907
6908 JS_FN_HELP("byteSize", ByteSize, 1, 0,
6909 "byteSize(value)",
6910 " Return the size in bytes occupied by |value|, or |undefined| if value\n"
6911 " is not allocated in memory.\n"),
6912
6913 JS_FN_HELP("byteSizeOfScript", ByteSizeOfScript, 1, 0,
6914 "byteSizeOfScript(f)",
6915 " Return the size in bytes occupied by the function |f|'s JSScript.\n"),
6916
6917 JS_FN_HELP("setImmutablePrototype", SetImmutablePrototype, 1, 0,
6918 "setImmutablePrototype(obj)",
6919 " Try to make obj's [[Prototype]] immutable, such that subsequent attempts to\n"
6920 " change it will fail. Return true if obj's [[Prototype]] was successfully made\n"
6921 " immutable (or if it already was immutable), false otherwise. Throws in case\n"
6922 " of internal error, or if the operation doesn't even make sense (for example,\n"
6923 " because the object is a revoked proxy)."),
6924
6925 #ifdef DEBUG
6926 JS_FN_HELP("dumpStringRepresentation", DumpStringRepresentation, 1, 0,
6927 "dumpStringRepresentation(str)",
6928 " Print a human-readable description of how the string |str| is represented.\n"),
6929 #endif
6930
6931 JS_FN_HELP("setLazyParsingDisabled", SetLazyParsingDisabled, 1, 0,
6932 "setLazyParsingDisabled(bool)",
6933 " Explicitly disable lazy parsing in the current compartment. The default is that lazy "
6934 " parsing is not explicitly disabled."),
6935
6936 JS_FN_HELP("setDiscardSource", SetDiscardSource, 1, 0,
6937 "setDiscardSource(bool)",
6938 " Explicitly enable source discarding in the current compartment. The default is that "
6939 " source discarding is not explicitly enabled."),
6940
6941 JS_FN_HELP("getConstructorName", GetConstructorName, 1, 0,
6942 "getConstructorName(object)",
6943 " If the given object was created with `new Ctor`, return the constructor's display name. "
6944 " Otherwise, return null."),
6945
6946 JS_FN_HELP("allocationMarker", AllocationMarker, 0, 0,
6947 "allocationMarker([options])",
6948 " Return a freshly allocated object whose [[Class]] name is\n"
6949 " \"AllocationMarker\". Such objects are allocated only by calls\n"
6950 " to this function, never implicitly by the system, making them\n"
6951 " suitable for use in allocation tooling tests. Takes an optional\n"
6952 " options object which may contain the following properties:\n"
6953 " * nursery: bool, whether to allocate the object in the nursery\n"),
6954
6955 JS_FN_HELP("setGCCallback", SetGCCallback, 1, 0,
6956 "setGCCallback({action:\"...\", options...})",
6957 " Set the GC callback. action may be:\n"
6958 " 'minorGC' - run a nursery collection\n"
6959 " 'majorGC' - run a major collection, nesting up to a given 'depth'\n"),
6960
6961 #ifdef DEBUG
6962 JS_FN_HELP("enqueueMark", EnqueueMark, 1, 0,
6963 "enqueueMark(obj|string)",
6964 " Add an object to the queue of objects to mark at the beginning every GC. (Note\n"
6965 " that the objects will actually be marked at the beginning of every slice, but\n"
6966 " after the first slice they will already be marked so nothing will happen.)\n"
6967 " \n"
6968 " Instead of an object, a few magic strings may be used:\n"
6969 " 'yield' - cause the current marking slice to end, as if the mark budget were\n"
6970 " exceeded.\n"
6971 " 'enter-weak-marking-mode' - divide the list into two segments. The items after\n"
6972 " this string will not be marked until we enter weak marking mode. Note that weak\n"
6973 " marking mode may be entered zero or multiple times for one GC.\n"
6974 " 'abort-weak-marking-mode' - same as above, but then abort weak marking to fall back\n"
6975 " on the old iterative marking code path.\n"
6976 " 'drain' - fully drain the mark stack before continuing.\n"
6977 " 'set-color-black' - force everything following in the mark queue to be marked black.\n"
6978 " 'set-color-gray' - continue with the regular GC until gray marking is possible, then force\n"
6979 " everything following in the mark queue to be marked gray.\n"
6980 " 'unset-color' - stop forcing the mark color."),
6981
6982 JS_FN_HELP("clearMarkQueue", ClearMarkQueue, 0, 0,
6983 "clearMarkQueue()",
6984 " Cancel the special marking of all objects enqueue with enqueueMark()."),
6985
6986 JS_FN_HELP("getMarkQueue", GetMarkQueue, 0, 0,
6987 "getMarkQueue()",
6988 " Return the current mark queue set up via enqueueMark calls. Note that all\n"
6989 " returned values will be wrapped into the current compartment, so this loses\n"
6990 " some fidelity."),
6991 #endif // DEBUG
6992
6993 JS_FN_HELP("getLcovInfo", GetLcovInfo, 1, 0,
6994 "getLcovInfo(global)",
6995 " Generate LCOV tracefile for the given compartment. If no global are provided then\n"
6996 " the current global is used as the default one.\n"),
6997
6998 #ifdef DEBUG
6999 JS_FN_HELP("setRNGState", SetRNGState, 2, 0,
7000 "setRNGState(seed0, seed1)",
7001 " Set this compartment's RNG state.\n"),
7002 #endif
7003
7004 JS_FN_HELP("getModuleEnvironmentNames", GetModuleEnvironmentNames, 1, 0,
7005 "getModuleEnvironmentNames(module)",
7006 " Get the list of a module environment's bound names for a specified module.\n"),
7007
7008 JS_FN_HELP("getModuleEnvironmentValue", GetModuleEnvironmentValue, 2, 0,
7009 "getModuleEnvironmentValue(module, name)",
7010 " Get the value of a bound name in a module environment.\n"),
7011
7012 #if defined(FUZZING) && defined(__AFL_COMPILER)
7013 JS_FN_HELP("aflloop", AflLoop, 1, 0,
7014 "aflloop(max_cnt)",
7015 " Call the __AFL_LOOP() runtime function (see AFL docs)\n"),
7016 #endif
7017
7018 JS_FN_HELP("monotonicNow", MonotonicNow, 0, 0,
7019 "monotonicNow()",
7020 " Return a timestamp reflecting the current elapsed system time.\n"
7021 " This is monotonically increasing.\n"),
7022
7023 JS_FN_HELP("timeSinceCreation", TimeSinceCreation, 0, 0,
7024 "TimeSinceCreation()",
7025 " Returns the time in milliseconds since process creation.\n"
7026 " This uses a clock compatible with the profiler.\n"),
7027
7028 JS_FN_HELP("isConstructor", IsConstructor, 1, 0,
7029 "isConstructor(value)",
7030 " Returns whether the value is considered IsConstructor.\n"),
7031
7032 JS_FN_HELP("getTimeZone", GetTimeZone, 0, 0,
7033 "getTimeZone()",
7034 " Get the current time zone.\n"),
7035
7036 JS_FN_HELP("getDefaultLocale", GetDefaultLocale, 0, 0,
7037 "getDefaultLocale()",
7038 " Get the current default locale.\n"),
7039
7040 JS_FN_HELP("getCoreCount", GetCoreCount, 0, 0,
7041 "getCoreCount()",
7042 " Get the number of CPU cores from the platform layer. Typically this\n"
7043 " means the number of hyperthreads on systems where that makes sense.\n"),
7044
7045 JS_FN_HELP("setTimeResolution", SetTimeResolution, 2, 0,
7046 "setTimeResolution(resolution, jitter)",
7047 " Enables time clamping and jittering. Specify a time resolution in\n"
7048 " microseconds and whether or not to jitter\n"),
7049
7050 JS_FN_HELP("scriptedCallerGlobal", ScriptedCallerGlobal, 0, 0,
7051 "scriptedCallerGlobal()",
7052 " Get the caller's global (or null). See JS::GetScriptedCallerGlobal.\n"),
7053
7054 JS_FN_HELP("objectGlobal", ObjectGlobal, 1, 0,
7055 "objectGlobal(obj)",
7056 " Returns the object's global object or null if the object is a wrapper.\n"),
7057
7058 JS_FN_HELP("isSameCompartment", IsSameCompartment, 2, 0,
7059 "isSameCompartment(obj1, obj2)",
7060 " Unwraps obj1 and obj2 and returns whether the unwrapped objects are\n"
7061 " same-compartment.\n"),
7062
7063 JS_FN_HELP("firstGlobalInCompartment", FirstGlobalInCompartment, 1, 0,
7064 "firstGlobalInCompartment(obj)",
7065 " Returns the first global in obj's compartment.\n"),
7066
7067 JS_FN_HELP("assertCorrectRealm", AssertCorrectRealm, 0, 0,
7068 "assertCorrectRealm()",
7069 " Asserts cx->realm matches callee->realm.\n"),
7070
7071 JS_FN_HELP("globalLexicals", GlobalLexicals, 0, 0,
7072 "globalLexicals()",
7073 " Returns an object containing a copy of all global lexical bindings.\n"
7074 " Example use: let x = 1; assertEq(globalLexicals().x, 1);\n"),
7075
7076 JS_FN_HELP("monitorType", MonitorType, 3, 0,
7077 "monitorType(fun, index, val)",
7078 " Adds val's type to the index'th StackTypeSet of the function's\n"
7079 " JitScript.\n"
7080 " If fun is null or undefined, the caller's script is used.\n"
7081 " If the value is the string 'unknown' or 'unknownObject'\n"
7082 " the TypeSet is marked as unknown or unknownObject, respectively.\n"),
7083
7084 JS_FN_HELP("baselineCompile", BaselineCompile, 2, 0,
7085 "baselineCompile([fun/code], forceDebugInstrumentation=false)",
7086 " Baseline-compiles the given JS function or script.\n"
7087 " Without arguments, baseline-compiles the caller's script; but note\n"
7088 " that extra boilerplate is needed afterwards to cause the VM to start\n"
7089 " running the jitcode rather than staying in the interpreter:\n"
7090 " baselineCompile(); for (var i=0; i<1; i++) {} ...\n"
7091 " The interpreter will enter the new jitcode at the loop header unless\n"
7092 " baselineCompile returned a string or threw an error.\n"),
7093
7094 JS_FN_HELP("markObjectPropertiesUnknown", MarkObjectPropertiesUnknown, 1, 0,
7095 "markObjectPropertiesUnknown(obj)",
7096 " Mark all objects in obj's object group as having unknown properties.\n"),
7097
7098 JS_FN_HELP("encodeAsUtf8InBuffer", EncodeAsUtf8InBuffer, 2, 0,
7099 "encodeAsUtf8InBuffer(str, uint8Array)",
7100 " Encode as many whole code points from the string str into the provided\n"
7101 " Uint8Array as will completely fit in it, converting lone surrogates to\n"
7102 " REPLACEMENT CHARACTER. Return an array [r, w] where |r| is the\n"
7103 " number of 16-bit units read and |w| is the number of bytes of UTF-8\n"
7104 " written."),
7105
7106 JS_FN_HELP("clearKeptObjects", ClearKeptObjects, 0, 0,
7107 "clearKeptObjects()",
7108 "Perform the ECMAScript ClearKeptObjects operation, clearing the list of\n"
7109 "observed WeakRef targets that are kept alive until the next synchronous\n"
7110 "sequence of ECMAScript execution completes. This is used for testing\n"
7111 "WeakRefs.\n"),
7112
7113 JS_FS_HELP_END
7114 };
7115 // clang-format on
7116
7117 // clang-format off
7118 static const JSFunctionSpecWithHelp FuzzingUnsafeTestingFunctions[] = {
7119 #if defined(DEBUG) && !defined(ENABLE_NEW_REGEXP)
7120 JS_FN_HELP("parseRegExp", ParseRegExp, 3, 0,
7121 "parseRegExp(pattern[, flags[, match_only])",
7122 " Parses a RegExp pattern and returns a tree, potentially throwing."),
7123
7124 JS_FN_HELP("disRegExp", DisRegExp, 3, 0,
7125 "disRegExp(regexp[, input])",
7126 " Dumps RegExp bytecode."),
7127 #endif
7128
7129 JS_FN_HELP("getErrorNotes", GetErrorNotes, 1, 0,
7130 "getErrorNotes(error)",
7131 " Returns an array of error notes."),
7132
7133 JS_FN_HELP("setTimeZone", SetTimeZone, 1, 0,
7134 "setTimeZone(tzname)",
7135 " Set the 'TZ' environment variable to the given time zone and applies the new time zone.\n"
7136 " An empty string or undefined resets the time zone to its default value.\n"
7137 " NOTE: The input string is not validated and will be passed verbatim to setenv()."),
7138
7139 JS_FN_HELP("setDefaultLocale", SetDefaultLocale, 1, 0,
7140 "setDefaultLocale(locale)",
7141 " Set the runtime default locale to the given value.\n"
7142 " An empty string or undefined resets the runtime locale to its default value.\n"
7143 " NOTE: The input string is not fully validated, it must be a valid BCP-47 language tag."),
7144
7145 JS_FS_HELP_END
7146 };
7147 // clang-format on
7148
7149 // clang-format off
7150 static const JSFunctionSpecWithHelp PCCountProfilingTestingFunctions[] = {
7151 JS_FN_HELP("start", PCCountProfiling_Start, 0, 0,
7152 "start()",
7153 " Start PC count profiling."),
7154
7155 JS_FN_HELP("stop", PCCountProfiling_Stop, 0, 0,
7156 "stop()",
7157 " Stop PC count profiling."),
7158
7159 JS_FN_HELP("purge", PCCountProfiling_Purge, 0, 0,
7160 "purge()",
7161 " Purge the collected PC count profiling data."),
7162
7163 JS_FN_HELP("count", PCCountProfiling_ScriptCount, 0, 0,
7164 "count()",
7165 " Return the number of profiled scripts."),
7166
7167 JS_FN_HELP("summary", PCCountProfiling_ScriptSummary, 1, 0,
7168 "summary(index)",
7169 " Return the PC count profiling summary for the given script index.\n"
7170 " The script index must be in the range [0, pc.count())."),
7171
7172 JS_FN_HELP("contents", PCCountProfiling_ScriptContents, 1, 0,
7173 "contents(index)",
7174 " Return the complete profiling contents for the given script index.\n"
7175 " The script index must be in the range [0, pc.count())."),
7176
7177 JS_FS_HELP_END
7178 };
7179 // clang-format on
7180
7181 bool js::DefineTestingFunctions(JSContext* cx, HandleObject obj,
7182 bool fuzzingSafe_, bool disableOOMFunctions_) {
7183 fuzzingSafe = fuzzingSafe_;
7184 if (EnvVarIsDefined("MOZ_FUZZING_SAFE")) {
7185 fuzzingSafe = true;
7186 }
7187
7188 disableOOMFunctions = disableOOMFunctions_;
7189
7190 if (!fuzzingSafe) {
7191 if (!JS_DefineFunctionsWithHelp(cx, obj, FuzzingUnsafeTestingFunctions)) {
7192 return false;
7193 }
7194
7195 RootedObject pccount(cx, JS_NewPlainObject(cx));
7196 if (!pccount) {
7197 return false;
7198 }
7199
7200 if (!JS_DefineProperty(cx, obj, "pccount", pccount, 0)) {
7201 return false;
7202 }
7203
7204 if (!JS_DefineFunctionsWithHelp(cx, pccount,
7205 PCCountProfilingTestingFunctions)) {
7206 return false;
7207 }
7208 }
7209
7210 return JS_DefineFunctionsWithHelp(cx, obj, TestingFunctions);
7211 }
7212