Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / js / src / builtin / TestingFunctions.cpp
blobd5ee10d13cf3f1e5ddfa65a49b032b35df1f10c1
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/. */
7 #include "builtin/TestingFunctions.h"
9 #include "mozilla/Atomics.h"
10 #include "mozilla/Casting.h"
11 #include "mozilla/FloatingPoint.h"
12 #ifdef JS_HAS_INTL_API
13 # include "mozilla/intl/ICU4CLibrary.h"
14 # include "mozilla/intl/Locale.h"
15 # include "mozilla/intl/String.h"
16 # include "mozilla/intl/TimeZone.h"
17 #endif
18 #include "mozilla/Maybe.h"
19 #include "mozilla/ScopeExit.h"
20 #include "mozilla/Span.h"
21 #include "mozilla/Sprintf.h"
22 #include "mozilla/TextUtils.h"
23 #include "mozilla/ThreadLocal.h"
25 #include <algorithm>
26 #include <cfloat>
27 #include <cinttypes>
28 #include <cmath>
29 #include <cstdlib>
30 #include <ctime>
31 #include <functional>
32 #include <initializer_list>
33 #include <iterator>
34 #include <utility>
36 #if defined(XP_UNIX) && !defined(XP_DARWIN)
37 # include <time.h>
38 #else
39 # include <chrono>
40 #endif
42 #include "fdlibm.h"
43 #include "jsapi.h"
44 #include "jsfriendapi.h"
46 #ifdef JS_HAS_INTL_API
47 # include "builtin/intl/CommonFunctions.h"
48 # include "builtin/intl/FormatBuffer.h"
49 # include "builtin/intl/SharedIntlData.h"
50 #endif
51 #include "builtin/BigInt.h"
52 #include "builtin/JSON.h"
53 #include "builtin/MapObject.h"
54 #include "builtin/Promise.h"
55 #include "builtin/TestingUtility.h" // js::ParseCompileOptions, js::ParseDebugMetadata
56 #include "ds/IdValuePair.h" // js::IdValuePair
57 #include "frontend/BytecodeCompiler.h" // frontend::{CompileGlobalScriptToExtensibleStencil,ParseModuleToExtensibleStencil}
58 #include "frontend/CompilationStencil.h" // frontend::CompilationStencil
59 #include "frontend/FrontendContext.h" // AutoReportFrontendContext
60 #include "gc/GC.h"
61 #include "gc/GCEnum.h"
62 #include "gc/GCLock.h"
63 #include "gc/Zone.h"
64 #include "jit/BaselineJIT.h"
65 #include "jit/Disassemble.h"
66 #include "jit/InlinableNatives.h"
67 #include "jit/Invalidation.h"
68 #include "jit/Ion.h"
69 #include "jit/JitOptions.h"
70 #include "jit/JitRuntime.h"
71 #include "jit/TrialInlining.h"
72 #include "js/Array.h" // JS::NewArrayObject
73 #include "js/ArrayBuffer.h" // JS::{DetachArrayBuffer,GetArrayBufferLengthAndData,NewArrayBufferWithContents}
74 #include "js/CallAndConstruct.h" // JS::Call, JS::IsCallable, JS::IsConstructor, JS_CallFunction
75 #include "js/CharacterEncoding.h"
76 #include "js/CompilationAndEvaluation.h"
77 #include "js/CompileOptions.h"
78 #include "js/Conversions.h"
79 #include "js/Date.h"
80 #include "js/experimental/CodeCoverage.h" // js::GetCodeCoverageSummary
81 #include "js/experimental/CompileScript.h" // JS::ParseGlobalScript, JS::PrepareForInstantiate
82 #include "js/experimental/JSStencil.h" // JS::Stencil
83 #include "js/experimental/PCCountProfiling.h" // JS::{Start,Stop}PCCountProfiling, JS::PurgePCCounts, JS::GetPCCountScript{Count,Summary,Contents}
84 #include "js/experimental/TypedData.h" // JS_GetObjectAsUint8Array
85 #include "js/friend/DumpFunctions.h" // js::Dump{Backtrace,Heap,Object}, JS::FormatStackDump, js::IgnoreNurseryObjects
86 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
87 #include "js/friend/WindowProxy.h" // js::ToWindowProxyIfWindow
88 #include "js/GlobalObject.h"
89 #include "js/HashTable.h"
90 #include "js/Interrupt.h"
91 #include "js/LocaleSensitive.h"
92 #include "js/Printf.h"
93 #include "js/PropertyAndElement.h" // JS_DefineProperties, JS_DefineProperty, JS_DefinePropertyById, JS_Enumerate, JS_GetProperty, JS_GetPropertyById, JS_HasProperty, JS_SetElement, JS_SetProperty
94 #include "js/PropertySpec.h"
95 #include "js/SourceText.h"
96 #include "js/StableStringChars.h"
97 #include "js/Stack.h"
98 #include "js/String.h" // JS::GetLinearStringLength, JS::StringToLinearString
99 #include "js/StructuredClone.h"
100 #include "js/UbiNode.h"
101 #include "js/UbiNodeBreadthFirst.h"
102 #include "js/UbiNodeShortestPaths.h"
103 #include "js/UniquePtr.h"
104 #include "js/Vector.h"
105 #include "js/Wrapper.h"
106 #include "threading/CpuCount.h"
107 #include "util/DifferentialTesting.h"
108 #include "util/StringBuffer.h"
109 #include "util/Text.h"
110 #include "vm/BooleanObject.h"
111 #include "vm/DateObject.h"
112 #include "vm/DateTime.h"
113 #include "vm/ErrorObject.h"
114 #include "vm/GlobalObject.h"
115 #include "vm/HelperThreads.h"
116 #include "vm/HelperThreadState.h"
117 #include "vm/Interpreter.h"
118 #include "vm/JSContext.h"
119 #include "vm/JSObject.h"
120 #include "vm/NumberObject.h"
121 #include "vm/PlainObject.h" // js::PlainObject
122 #include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseSlot_*
123 #include "vm/ProxyObject.h"
124 #include "vm/RealmFuses.h"
125 #include "vm/SavedStacks.h"
126 #include "vm/ScopeKind.h"
127 #include "vm/Stack.h"
128 #include "vm/StencilCache.h" // DelazificationCache
129 #include "vm/StencilObject.h" // StencilObject, StencilXDRBufferObject
130 #include "vm/StringObject.h"
131 #include "vm/StringType.h"
132 #include "wasm/AsmJS.h"
133 #include "wasm/WasmBaselineCompile.h"
134 #include "wasm/WasmBuiltinModule.h"
135 #include "wasm/WasmFeatures.h"
136 #include "wasm/WasmGcObject.h"
137 #include "wasm/WasmInstance.h"
138 #include "wasm/WasmIonCompile.h"
139 #include "wasm/WasmJS.h"
140 #include "wasm/WasmModule.h"
141 #include "wasm/WasmValType.h"
142 #include "wasm/WasmValue.h"
144 #include "debugger/DebugAPI-inl.h"
145 #include "vm/Compartment-inl.h"
146 #include "vm/EnvironmentObject-inl.h"
147 #include "vm/JSContext-inl.h"
148 #include "vm/JSObject-inl.h"
149 #include "vm/NativeObject-inl.h"
150 #include "vm/ObjectFlags-inl.h"
151 #include "vm/StringType-inl.h"
152 #include "wasm/WasmInstance-inl.h"
154 using namespace js;
156 using mozilla::AssertedCast;
157 using mozilla::AsWritableChars;
158 using mozilla::Maybe;
159 using mozilla::Span;
161 using JS::AutoStableStringChars;
162 using JS::CompileOptions;
163 using JS::SourceOwnership;
164 using JS::SourceText;
166 // If fuzzingSafe is set, remove functionality that could cause problems with
167 // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
168 mozilla::Atomic<bool> js::fuzzingSafe(false);
170 // If disableOOMFunctions is set, disable functionality that causes artificial
171 // OOM conditions.
172 static mozilla::Atomic<bool> disableOOMFunctions(false);
174 static bool EnvVarIsDefined(const char* name) {
175 const char* value = getenv(name);
176 return value && *value;
179 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
180 static bool EnvVarAsInt(const char* name, int* valueOut) {
181 if (!EnvVarIsDefined(name)) {
182 return false;
185 *valueOut = atoi(getenv(name));
186 return true;
188 #endif
190 static bool GetRealmConfiguration(JSContext* cx, unsigned argc, Value* vp) {
191 CallArgs args = CallArgsFromVp(argc, vp);
192 RootedObject callee(cx, &args.callee());
193 RootedObject info(cx, JS_NewPlainObject(cx));
194 if (!info) {
195 return false;
197 if (args.length() > 1) {
198 ReportUsageErrorASCII(cx, callee, "Must have zero or one arguments");
199 return false;
201 if (args.length() == 1 && !args[0].isString()) {
202 ReportUsageErrorASCII(cx, callee, "Argument must be a string");
203 return false;
206 bool importAttributes = cx->options().importAttributes();
207 if (!JS_SetProperty(cx, info, "importAttributes",
208 importAttributes ? TrueHandleValue : FalseHandleValue)) {
209 return false;
212 #ifdef NIGHTLY_BUILD
213 bool arrayGrouping = cx->realm()->creationOptions().getArrayGroupingEnabled();
214 if (!JS_SetProperty(cx, info, "enableArrayGrouping",
215 arrayGrouping ? TrueHandleValue : FalseHandleValue)) {
216 return false;
218 #endif
220 #ifdef NIGHTLY_BUILD
221 bool newSetMethods = cx->realm()->creationOptions().getNewSetMethodsEnabled();
222 if (!JS_SetProperty(cx, info, "enableNewSetMethods",
223 newSetMethods ? TrueHandleValue : FalseHandleValue)) {
224 return false;
226 #endif
228 if (args.length() == 1) {
229 RootedString str(cx, ToString(cx, args[0]));
230 if (!str) {
231 return false;
233 RootedId id(cx);
234 if (!JS_StringToId(cx, str, &id)) {
235 return false;
238 bool hasProperty;
239 if (JS_HasPropertyById(cx, info, id, &hasProperty) && hasProperty) {
240 // Returning a true/false from GetProperty
241 return GetProperty(cx, info, info, id, args.rval());
244 ReportUsageErrorASCII(cx, callee, "Invalid option name");
245 return false;
248 args.rval().setObject(*info);
249 return true;
252 static bool GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) {
253 CallArgs args = CallArgsFromVp(argc, vp);
254 RootedObject callee(cx, &args.callee());
255 RootedObject info(cx, JS_NewPlainObject(cx));
256 if (!info) {
257 return false;
259 if (args.length() > 1) {
260 ReportUsageErrorASCII(cx, callee, "Must have zero or one arguments");
261 return false;
263 if (args.length() == 1 && !args[0].isString()) {
264 ReportUsageErrorASCII(cx, callee, "Argument must be a string");
265 return false;
268 if (!JS_SetProperty(cx, info, "rooting-analysis", FalseHandleValue)) {
269 return false;
272 if (!JS_SetProperty(cx, info, "exact-rooting", TrueHandleValue)) {
273 return false;
276 if (!JS_SetProperty(cx, info, "trace-jscalls-api", FalseHandleValue)) {
277 return false;
280 if (!JS_SetProperty(cx, info, "incremental-gc", TrueHandleValue)) {
281 return false;
284 if (!JS_SetProperty(cx, info, "generational-gc", TrueHandleValue)) {
285 return false;
288 if (!JS_SetProperty(cx, info, "oom-backtraces", FalseHandleValue)) {
289 return false;
292 RootedValue value(cx);
293 #ifdef DEBUG
294 value = BooleanValue(true);
295 #else
296 value = BooleanValue(false);
297 #endif
298 if (!JS_SetProperty(cx, info, "debug", value)) {
299 return false;
302 #ifdef RELEASE_OR_BETA
303 value = BooleanValue(true);
304 #else
305 value = BooleanValue(false);
306 #endif
307 if (!JS_SetProperty(cx, info, "release_or_beta", value)) {
308 return false;
311 #ifdef EARLY_BETA_OR_EARLIER
312 value = BooleanValue(true);
313 #else
314 value = BooleanValue(false);
315 #endif
316 if (!JS_SetProperty(cx, info, "early_beta_or_earlier", value)) {
317 return false;
320 #ifdef MOZ_CODE_COVERAGE
321 value = BooleanValue(true);
322 #else
323 value = BooleanValue(false);
324 #endif
325 if (!JS_SetProperty(cx, info, "coverage", value)) {
326 return false;
329 #ifdef JS_HAS_CTYPES
330 value = BooleanValue(true);
331 #else
332 value = BooleanValue(false);
333 #endif
334 if (!JS_SetProperty(cx, info, "has-ctypes", value)) {
335 return false;
338 #if defined(_M_IX86) || defined(__i386__)
339 value = BooleanValue(true);
340 #else
341 value = BooleanValue(false);
342 #endif
343 if (!JS_SetProperty(cx, info, "x86", value)) {
344 return false;
347 #if defined(_M_X64) || defined(__x86_64__)
348 value = BooleanValue(true);
349 #else
350 value = BooleanValue(false);
351 #endif
352 if (!JS_SetProperty(cx, info, "x64", value)) {
353 return false;
356 #ifdef JS_CODEGEN_ARM
357 value = BooleanValue(true);
358 #else
359 value = BooleanValue(false);
360 #endif
361 if (!JS_SetProperty(cx, info, "arm", value)) {
362 return false;
365 #ifdef JS_SIMULATOR_ARM
366 value = BooleanValue(true);
367 #else
368 value = BooleanValue(false);
369 #endif
370 if (!JS_SetProperty(cx, info, "arm-simulator", value)) {
371 return false;
374 #ifdef ANDROID
375 value = BooleanValue(true);
376 #else
377 value = BooleanValue(false);
378 #endif
379 if (!JS_SetProperty(cx, info, "android", value)) {
380 return false;
383 #ifdef XP_WIN
384 value = BooleanValue(true);
385 #else
386 value = BooleanValue(false);
387 #endif
388 if (!JS_SetProperty(cx, info, "windows", value)) {
389 return false;
392 #ifdef XP_MACOSX
393 value = BooleanValue(true);
394 #else
395 value = BooleanValue(false);
396 #endif
397 if (!JS_SetProperty(cx, info, "osx", value)) {
398 return false;
401 #ifdef JS_CODEGEN_ARM64
402 value = BooleanValue(true);
403 #else
404 value = BooleanValue(false);
405 #endif
406 if (!JS_SetProperty(cx, info, "arm64", value)) {
407 return false;
410 #ifdef JS_SIMULATOR_ARM64
411 value = BooleanValue(true);
412 #else
413 value = BooleanValue(false);
414 #endif
415 if (!JS_SetProperty(cx, info, "arm64-simulator", value)) {
416 return false;
419 #ifdef JS_CODEGEN_MIPS32
420 value = BooleanValue(true);
421 #else
422 value = BooleanValue(false);
423 #endif
424 if (!JS_SetProperty(cx, info, "mips32", value)) {
425 return false;
428 #ifdef JS_CODEGEN_MIPS64
429 value = BooleanValue(true);
430 #else
431 value = BooleanValue(false);
432 #endif
433 if (!JS_SetProperty(cx, info, "mips64", value)) {
434 return false;
437 #ifdef JS_SIMULATOR_MIPS32
438 value = BooleanValue(true);
439 #else
440 value = BooleanValue(false);
441 #endif
442 if (!JS_SetProperty(cx, info, "mips32-simulator", value)) {
443 return false;
446 #ifdef JS_SIMULATOR_MIPS64
447 value = BooleanValue(true);
448 #else
449 value = BooleanValue(false);
450 #endif
451 if (!JS_SetProperty(cx, info, "mips64-simulator", value)) {
452 return false;
455 #ifdef JS_SIMULATOR
456 value = BooleanValue(true);
457 #else
458 value = BooleanValue(false);
459 #endif
460 if (!JS_SetProperty(cx, info, "simulator", value)) {
461 return false;
464 #ifdef __wasi__
465 value = BooleanValue(true);
466 #else
467 value = BooleanValue(false);
468 #endif
469 if (!JS_SetProperty(cx, info, "wasi", value)) {
470 return false;
473 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
474 value = BooleanValue(true);
475 #else
476 value = BooleanValue(false);
477 #endif
478 if (!JS_SetProperty(cx, info, "pbl", value)) {
479 return false;
482 #ifdef JS_CODEGEN_LOONG64
483 value = BooleanValue(true);
484 #else
485 value = BooleanValue(false);
486 #endif
487 if (!JS_SetProperty(cx, info, "loong64", value)) {
488 return false;
491 #ifdef JS_SIMULATOR_LOONG64
492 value = BooleanValue(true);
493 #else
494 value = BooleanValue(false);
495 #endif
496 if (!JS_SetProperty(cx, info, "loong64-simulator", value)) {
497 return false;
500 #ifdef JS_CODEGEN_RISCV64
501 value = BooleanValue(true);
502 #else
503 value = BooleanValue(false);
504 #endif
505 if (!JS_SetProperty(cx, info, "riscv64", value)) {
506 return false;
509 #ifdef JS_SIMULATOR_RISCV64
510 value = BooleanValue(true);
511 #else
512 value = BooleanValue(false);
513 #endif
514 if (!JS_SetProperty(cx, info, "riscv64-simulator", value)) {
515 return false;
518 #ifdef MOZ_ASAN
519 value = BooleanValue(true);
520 #else
521 value = BooleanValue(false);
522 #endif
523 if (!JS_SetProperty(cx, info, "asan", value)) {
524 return false;
527 #ifdef MOZ_TSAN
528 value = BooleanValue(true);
529 #else
530 value = BooleanValue(false);
531 #endif
532 if (!JS_SetProperty(cx, info, "tsan", value)) {
533 return false;
536 #ifdef MOZ_UBSAN
537 value = BooleanValue(true);
538 #else
539 value = BooleanValue(false);
540 #endif
541 if (!JS_SetProperty(cx, info, "ubsan", value)) {
542 return false;
545 #ifdef JS_GC_ZEAL
546 value = BooleanValue(true);
547 #else
548 value = BooleanValue(false);
549 #endif
550 if (!JS_SetProperty(cx, info, "has-gczeal", value)) {
551 return false;
554 #ifdef MOZ_PROFILING
555 value = BooleanValue(true);
556 #else
557 value = BooleanValue(false);
558 #endif
559 if (!JS_SetProperty(cx, info, "profiling", value)) {
560 return false;
563 #ifdef INCLUDE_MOZILLA_DTRACE
564 value = BooleanValue(true);
565 #else
566 value = BooleanValue(false);
567 #endif
568 if (!JS_SetProperty(cx, info, "dtrace", value)) {
569 return false;
572 #ifdef MOZ_VALGRIND
573 value = BooleanValue(true);
574 #else
575 value = BooleanValue(false);
576 #endif
577 if (!JS_SetProperty(cx, info, "valgrind", value)) {
578 return false;
581 #ifdef JS_HAS_INTL_API
582 value = BooleanValue(true);
583 #else
584 value = BooleanValue(false);
585 #endif
586 if (!JS_SetProperty(cx, info, "intl-api", value)) {
587 return false;
590 #if defined(SOLARIS)
591 value = BooleanValue(false);
592 #else
593 value = BooleanValue(true);
594 #endif
595 if (!JS_SetProperty(cx, info, "mapped-array-buffer", value)) {
596 return false;
599 #ifdef MOZ_MEMORY
600 value = BooleanValue(true);
601 #else
602 value = BooleanValue(false);
603 #endif
604 if (!JS_SetProperty(cx, info, "moz-memory", value)) {
605 return false;
608 value.setInt32(sizeof(void*));
609 if (!JS_SetProperty(cx, info, "pointer-byte-size", value)) {
610 return false;
613 #ifdef ENABLE_DECORATORS
614 value = BooleanValue(true);
615 #else
616 value = BooleanValue(false);
617 #endif
618 if (!JS_SetProperty(cx, info, "decorators", value)) {
619 return false;
622 #ifdef FUZZING
623 value = BooleanValue(true);
624 #else
625 value = BooleanValue(false);
626 #endif
627 if (!JS_SetProperty(cx, info, "fuzzing-defined", value)) {
628 return false;
631 value = Int32Value(JSFatInlineString::MAX_LENGTH_LATIN1);
632 if (!JS_SetProperty(cx, info, "inline-latin1-chars", value)) {
633 return false;
636 value = Int32Value(JSFatInlineString::MAX_LENGTH_TWO_BYTE);
637 if (!JS_SetProperty(cx, info, "inline-two-byte-chars", value)) {
638 return false;
641 value = Int32Value(JSThinInlineString::MAX_LENGTH_LATIN1);
642 if (!JS_SetProperty(cx, info, "thin-inline-latin1-chars", value)) {
643 return false;
646 value = Int32Value(JSThinInlineString::MAX_LENGTH_TWO_BYTE);
647 if (!JS_SetProperty(cx, info, "thin-inline-two-byte-chars", value)) {
648 return false;
651 if (js::ThinInlineAtom::EverInstantiated) {
652 value = Int32Value(js::ThinInlineAtom::MAX_LENGTH_LATIN1);
653 if (!JS_SetProperty(cx, info, "thin-inline-atom-latin1-chars", value)) {
654 return false;
657 value = Int32Value(js::ThinInlineAtom::MAX_LENGTH_TWO_BYTE);
658 if (!JS_SetProperty(cx, info, "thin-inline-atom-two-byte-chars", value)) {
659 return false;
663 value = Int32Value(js::FatInlineAtom::MAX_LENGTH_LATIN1);
664 if (!JS_SetProperty(cx, info, "fat-inline-atom-latin1-chars", value)) {
665 return false;
668 value = Int32Value(js::FatInlineAtom::MAX_LENGTH_TWO_BYTE);
669 if (!JS_SetProperty(cx, info, "fat-inline-atom-two-byte-chars", value)) {
670 return false;
673 if (args.length() == 1) {
674 RootedString str(cx, ToString(cx, args[0]));
675 if (!str) {
676 return false;
678 RootedId id(cx);
679 if (!JS_StringToId(cx, str, &id)) {
680 return false;
683 bool hasProperty;
684 if (JS_HasPropertyById(cx, info, id, &hasProperty) && hasProperty) {
685 // Returning a true/false from GetProperty
686 return GetProperty(cx, info, info, id, args.rval());
689 ReportUsageErrorASCII(cx, callee, "Invalid option name");
690 return false;
693 args.rval().setObject(*info);
694 return true;
697 static bool IsLCovEnabled(JSContext* cx, unsigned argc, Value* vp) {
698 CallArgs args = CallArgsFromVp(argc, vp);
699 args.rval().setBoolean(coverage::IsLCovEnabled());
700 return true;
703 static bool TrialInline(JSContext* cx, unsigned argc, Value* vp) {
704 CallArgs args = CallArgsFromVp(argc, vp);
705 args.rval().setUndefined();
707 FrameIter iter(cx);
708 if (iter.done() || !iter.isBaseline() || iter.realm() != cx->realm()) {
709 return true;
712 jit::BaselineFrame* frame = iter.abstractFramePtr().asBaselineFrame();
713 if (!jit::CanIonCompileScript(cx, frame->script())) {
714 return true;
717 return jit::DoTrialInlining(cx, frame);
720 static bool ReturnStringCopy(JSContext* cx, CallArgs& args,
721 const char* message) {
722 JSString* str = JS_NewStringCopyZ(cx, message);
723 if (!str) {
724 return false;
727 args.rval().setString(str);
728 return true;
731 static bool MaybeGC(JSContext* cx, unsigned argc, Value* vp) {
732 CallArgs args = CallArgsFromVp(argc, vp);
733 JS_MaybeGC(cx);
734 args.rval().setUndefined();
735 return true;
738 static bool GC(JSContext* cx, unsigned argc, Value* vp) {
739 CallArgs args = CallArgsFromVp(argc, vp);
742 * If the first argument is 'zone', we collect any zones previously
743 * scheduled for GC via schedulegc. If the first argument is an object, we
744 * collect the object's zone (and any other zones scheduled for
745 * GC). Otherwise, we collect all zones.
747 bool zone = false;
748 if (args.length() >= 1) {
749 Value arg = args[0];
750 if (arg.isString()) {
751 if (!JS_StringEqualsLiteral(cx, arg.toString(), "zone", &zone)) {
752 return false;
754 } else if (arg.isObject()) {
755 PrepareZoneForGC(cx, UncheckedUnwrap(&arg.toObject())->zone());
756 zone = true;
760 JS::GCOptions options = JS::GCOptions::Normal;
761 JS::GCReason reason = JS::GCReason::API;
762 if (args.length() >= 2) {
763 Value arg = args[1];
764 if (arg.isString()) {
765 bool shrinking = false;
766 bool last_ditch = false;
767 if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking",
768 &shrinking)) {
769 return false;
771 if (!JS_StringEqualsLiteral(cx, arg.toString(), "last-ditch",
772 &last_ditch)) {
773 return false;
775 if (shrinking) {
776 options = JS::GCOptions::Shrink;
777 } else if (last_ditch) {
778 options = JS::GCOptions::Shrink;
779 reason = JS::GCReason::LAST_DITCH;
784 size_t preBytes = cx->runtime()->gc.heapSize.bytes();
786 if (zone) {
787 PrepareForDebugGC(cx->runtime());
788 } else {
789 JS::PrepareForFullGC(cx);
792 JS::NonIncrementalGC(cx, options, reason);
794 char buf[256] = {'\0'};
795 if (!js::SupportDifferentialTesting()) {
796 SprintfLiteral(buf, "before %zu, after %zu\n", preBytes,
797 cx->runtime()->gc.heapSize.bytes());
799 return ReturnStringCopy(cx, args, buf);
802 static bool MinorGC(JSContext* cx, unsigned argc, Value* vp) {
803 CallArgs args = CallArgsFromVp(argc, vp);
804 if (args.get(0) == BooleanValue(true)) {
805 gc::GCRuntime& gc = cx->runtime()->gc;
806 if (gc.nursery().isEnabled()) {
807 gc.storeBuffer().setAboutToOverflow(JS::GCReason::FULL_GENERIC_BUFFER);
811 cx->minorGC(JS::GCReason::API);
812 args.rval().setUndefined();
813 return true;
816 #define PARAM_NAME_LIST_ENTRY(name, key, writable) " " name
817 #define GC_PARAMETER_ARGS_LIST FOR_EACH_GC_PARAM(PARAM_NAME_LIST_ENTRY)
819 static bool GCParameter(JSContext* cx, unsigned argc, Value* vp) {
820 CallArgs args = CallArgsFromVp(argc, vp);
822 JSString* str = ToString(cx, args.get(0));
823 if (!str) {
824 return false;
827 UniqueChars name = EncodeLatin1(cx, str);
828 if (!name) {
829 return false;
832 JSGCParamKey param;
833 bool writable;
834 if (!GetGCParameterInfo(name.get(), &param, &writable)) {
835 JS_ReportErrorASCII(
836 cx, "the first argument must be one of:" GC_PARAMETER_ARGS_LIST);
837 return false;
840 // Request mode.
841 if (args.length() == 1) {
842 uint32_t value = JS_GetGCParameter(cx, param);
843 args.rval().setNumber(value);
844 return true;
847 if (!writable) {
848 JS_ReportErrorASCII(cx, "Attempt to change read-only parameter %s",
849 name.get());
850 return false;
853 if (disableOOMFunctions) {
854 switch (param) {
855 case JSGC_MAX_BYTES:
856 case JSGC_MAX_NURSERY_BYTES:
857 args.rval().setUndefined();
858 return true;
859 default:
860 break;
864 double d;
865 if (!ToNumber(cx, args[1], &d)) {
866 return false;
869 if (d < 0 || d > UINT32_MAX) {
870 JS_ReportErrorASCII(cx, "Parameter value out of range");
871 return false;
874 uint32_t value = floor(d);
875 bool ok = cx->runtime()->gc.setParameter(cx, param, value);
876 if (!ok) {
877 JS_ReportErrorASCII(cx, "Parameter value out of range");
878 return false;
881 args.rval().setUndefined();
882 return true;
885 static bool RelazifyFunctions(JSContext* cx, unsigned argc, Value* vp) {
886 // Relazifying functions on GC is usually only done for compartments that are
887 // not active. To aid fuzzing, this testing function allows us to relazify
888 // even if the compartment is active.
890 CallArgs args = CallArgsFromVp(argc, vp);
892 // Disable relazification of all scripts on stack. It is a pervasive
893 // assumption in the engine that running scripts still have bytecode.
894 for (AllScriptFramesIter i(cx); !i.done(); ++i) {
895 i.script()->clearAllowRelazify();
898 cx->runtime()->allowRelazificationForTesting = true;
900 JS::PrepareForFullGC(cx);
901 JS::NonIncrementalGC(cx, JS::GCOptions::Shrink, JS::GCReason::API);
903 cx->runtime()->allowRelazificationForTesting = false;
905 args.rval().setUndefined();
906 return true;
909 static bool IsProxy(JSContext* cx, unsigned argc, Value* vp) {
910 CallArgs args = CallArgsFromVp(argc, vp);
911 if (args.length() != 1) {
912 JS_ReportErrorASCII(cx, "the function takes exactly one argument");
913 return false;
915 if (!args[0].isObject()) {
916 args.rval().setBoolean(false);
917 return true;
919 args.rval().setBoolean(args[0].toObject().is<ProxyObject>());
920 return true;
923 static bool WasmIsSupported(JSContext* cx, unsigned argc, Value* vp) {
924 CallArgs args = CallArgsFromVp(argc, vp);
925 args.rval().setBoolean(wasm::HasSupport(cx) &&
926 wasm::AnyCompilerAvailable(cx));
927 return true;
930 static bool WasmIsSupportedByHardware(JSContext* cx, unsigned argc, Value* vp) {
931 CallArgs args = CallArgsFromVp(argc, vp);
932 args.rval().setBoolean(wasm::HasPlatformSupport());
933 return true;
936 static bool WasmDebuggingEnabled(JSContext* cx, unsigned argc, Value* vp) {
937 CallArgs args = CallArgsFromVp(argc, vp);
938 args.rval().setBoolean(wasm::HasSupport(cx) && wasm::BaselineAvailable(cx));
939 return true;
942 static bool WasmStreamingEnabled(JSContext* cx, unsigned argc, Value* vp) {
943 CallArgs args = CallArgsFromVp(argc, vp);
944 args.rval().setBoolean(wasm::StreamingCompilationAvailable(cx));
945 return true;
948 static bool WasmCachingEnabled(JSContext* cx, unsigned argc, Value* vp) {
949 CallArgs args = CallArgsFromVp(argc, vp);
950 args.rval().setBoolean(wasm::CodeCachingAvailable(cx));
951 return true;
954 static bool WasmHugeMemorySupported(JSContext* cx, unsigned argc, Value* vp) {
955 CallArgs args = CallArgsFromVp(argc, vp);
956 #ifdef WASM_SUPPORTS_HUGE_MEMORY
957 args.rval().setBoolean(true);
958 #else
959 args.rval().setBoolean(false);
960 #endif
961 return true;
964 static bool WasmMaxMemoryPages(JSContext* cx, unsigned argc, Value* vp) {
965 CallArgs args = CallArgsFromVp(argc, vp);
966 if (args.length() < 1) {
967 JS_ReportErrorASCII(cx, "not enough arguments");
968 return false;
970 if (!args.get(0).isString()) {
971 JS_ReportErrorASCII(cx, "index type must be a string");
972 return false;
974 RootedString s(cx, args.get(0).toString());
975 Rooted<JSLinearString*> ls(cx, s->ensureLinear(cx));
976 if (!ls) {
977 return false;
979 if (StringEqualsLiteral(ls, "i32")) {
980 args.rval().setInt32(
981 int32_t(wasm::MaxMemoryPages(wasm::IndexType::I32).value()));
982 return true;
984 if (StringEqualsLiteral(ls, "i64")) {
985 #ifdef ENABLE_WASM_MEMORY64
986 if (wasm::Memory64Available(cx)) {
987 args.rval().setInt32(
988 int32_t(wasm::MaxMemoryPages(wasm::IndexType::I64).value()));
989 return true;
991 #endif
992 JS_ReportErrorASCII(cx, "memory64 not enabled");
993 return false;
995 JS_ReportErrorASCII(cx, "bad index type");
996 return false;
999 static bool WasmThreadsEnabled(JSContext* cx, unsigned argc, Value* vp) {
1000 CallArgs args = CallArgsFromVp(argc, vp);
1001 args.rval().setBoolean(wasm::ThreadsAvailable(cx));
1002 return true;
1005 #define WASM_FEATURE(NAME, ...) \
1006 static bool Wasm##NAME##Enabled(JSContext* cx, unsigned argc, Value* vp) { \
1007 CallArgs args = CallArgsFromVp(argc, vp); \
1008 args.rval().setBoolean(wasm::NAME##Available(cx)); \
1009 return true; \
1011 JS_FOR_WASM_FEATURES(WASM_FEATURE);
1012 #undef WASM_FEATURE
1014 static bool WasmSimdEnabled(JSContext* cx, unsigned argc, Value* vp) {
1015 CallArgs args = CallArgsFromVp(argc, vp);
1016 args.rval().setBoolean(wasm::SimdAvailable(cx));
1017 return true;
1020 static bool WasmCompilersPresent(JSContext* cx, unsigned argc, Value* vp) {
1021 CallArgs args = CallArgsFromVp(argc, vp);
1023 char buf[256];
1024 *buf = 0;
1025 if (wasm::BaselinePlatformSupport()) {
1026 strcat(buf, "baseline");
1028 if (wasm::IonPlatformSupport()) {
1029 if (*buf) {
1030 strcat(buf, ",");
1032 strcat(buf, "ion");
1035 JSString* result = JS_NewStringCopyZ(cx, buf);
1036 if (!result) {
1037 return false;
1040 args.rval().setString(result);
1041 return true;
1044 static bool WasmCompileMode(JSContext* cx, unsigned argc, Value* vp) {
1045 CallArgs args = CallArgsFromVp(argc, vp);
1047 // This triplet of predicates will select zero or one baseline compiler and
1048 // zero or one optimizing compiler.
1049 bool baseline = wasm::BaselineAvailable(cx);
1050 bool ion = wasm::IonAvailable(cx);
1051 bool none = !baseline && !ion;
1052 bool tiered = baseline && ion;
1054 JSStringBuilder result(cx);
1055 if (none && !result.append("none")) {
1056 return false;
1058 if (baseline && !result.append("baseline")) {
1059 return false;
1061 if (tiered && !result.append("+")) {
1062 return false;
1064 if (ion && !result.append("ion")) {
1065 return false;
1067 if (JSString* str = result.finishString()) {
1068 args.rval().setString(str);
1069 return true;
1071 return false;
1074 static bool WasmBaselineDisabledByFeatures(JSContext* cx, unsigned argc,
1075 Value* vp) {
1076 CallArgs args = CallArgsFromVp(argc, vp);
1077 bool isDisabled = false;
1078 JSStringBuilder reason(cx);
1079 if (!wasm::BaselineDisabledByFeatures(cx, &isDisabled, &reason)) {
1080 return false;
1082 if (isDisabled) {
1083 JSString* result = reason.finishString();
1084 if (!result) {
1085 return false;
1087 args.rval().setString(result);
1088 } else {
1089 args.rval().setBoolean(false);
1091 return true;
1094 static bool WasmIonDisabledByFeatures(JSContext* cx, unsigned argc, Value* vp) {
1095 CallArgs args = CallArgsFromVp(argc, vp);
1096 bool isDisabled = false;
1097 JSStringBuilder reason(cx);
1098 if (!wasm::IonDisabledByFeatures(cx, &isDisabled, &reason)) {
1099 return false;
1101 if (isDisabled) {
1102 JSString* result = reason.finishString();
1103 if (!result) {
1104 return false;
1106 args.rval().setString(result);
1107 } else {
1108 args.rval().setBoolean(false);
1110 return true;
1113 #ifdef ENABLE_WASM_SIMD
1114 # ifdef DEBUG
1115 static char lastAnalysisResult[1024];
1117 namespace js {
1118 namespace wasm {
1119 void ReportSimdAnalysis(const char* data) {
1120 strncpy(lastAnalysisResult, data, sizeof(lastAnalysisResult));
1121 lastAnalysisResult[sizeof(lastAnalysisResult) - 1] = 0;
1123 } // namespace wasm
1124 } // namespace js
1126 // Unstable API for white-box testing of SIMD optimizations.
1128 // Current API: takes no arguments, returns a string describing the last Simd
1129 // simplification applied.
1131 static bool WasmSimdAnalysis(JSContext* cx, unsigned argc, Value* vp) {
1132 CallArgs args = CallArgsFromVp(argc, vp);
1133 JSString* result =
1134 JS_NewStringCopyZ(cx, *lastAnalysisResult ? lastAnalysisResult : "none");
1135 if (!result) {
1136 return false;
1138 args.rval().setString(result);
1139 *lastAnalysisResult = (char)0;
1140 return true;
1142 # endif
1143 #endif
1145 static bool WasmGlobalFromArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
1146 if (!wasm::HasSupport(cx)) {
1147 JS_ReportErrorASCII(cx, "wasm support unavailable");
1148 return false;
1150 CallArgs args = CallArgsFromVp(argc, vp);
1152 if (args.length() < 2) {
1153 JS_ReportErrorASCII(cx, "not enough arguments");
1154 return false;
1157 // Get the type of the value
1158 wasm::ValType valType;
1159 if (!wasm::ToValType(cx, args.get(0), &valType)) {
1160 return false;
1163 // Get the array buffer for the value
1164 if (!args.get(1).isObject() ||
1165 !args.get(1).toObject().is<ArrayBufferObject>()) {
1166 JS_ReportErrorASCII(cx, "argument is not an array buffer");
1167 return false;
1169 Rooted<ArrayBufferObject*> buffer(
1170 cx, &args.get(1).toObject().as<ArrayBufferObject>());
1172 // Only allow POD to be created from bytes
1173 switch (valType.kind()) {
1174 case wasm::ValType::I32:
1175 case wasm::ValType::I64:
1176 case wasm::ValType::F32:
1177 case wasm::ValType::F64:
1178 case wasm::ValType::V128:
1179 break;
1180 default:
1181 JS_ReportErrorASCII(
1182 cx, "invalid valtype for creating WebAssembly.Global from bytes");
1183 return false;
1186 // Check we have all the bytes we need
1187 if (valType.size() != buffer->byteLength()) {
1188 JS_ReportErrorASCII(cx, "array buffer has incorrect size");
1189 return false;
1192 // Copy the bytes from buffer into a tagged val
1193 wasm::RootedVal val(cx);
1194 val.get().initFromRootedLocation(valType, buffer->dataPointer());
1196 // Create the global object
1197 RootedObject proto(
1198 cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmGlobal));
1199 if (!proto) {
1200 return false;
1202 Rooted<WasmGlobalObject*> result(
1203 cx, WasmGlobalObject::create(cx, val, false, proto));
1204 if (!result) {
1205 return false;
1208 args.rval().setObject(*result.get());
1209 return true;
1212 enum class LaneInterp {
1213 I32x4,
1214 I64x2,
1215 F32x4,
1216 F64x2,
1219 size_t LaneInterpLanes(LaneInterp interp) {
1220 switch (interp) {
1221 case LaneInterp::I32x4:
1222 return 4;
1223 case LaneInterp::I64x2:
1224 return 2;
1225 case LaneInterp::F32x4:
1226 return 4;
1227 case LaneInterp::F64x2:
1228 return 2;
1229 default:
1230 MOZ_ASSERT_UNREACHABLE();
1231 return 0;
1235 static bool ToLaneInterp(JSContext* cx, HandleValue v, LaneInterp* out) {
1236 RootedString interpStr(cx, ToString(cx, v));
1237 if (!interpStr) {
1238 return false;
1240 Rooted<JSLinearString*> interpLinearStr(cx, interpStr->ensureLinear(cx));
1241 if (!interpLinearStr) {
1242 return false;
1245 if (StringEqualsLiteral(interpLinearStr, "i32x4")) {
1246 *out = LaneInterp::I32x4;
1247 return true;
1248 } else if (StringEqualsLiteral(interpLinearStr, "i64x2")) {
1249 *out = LaneInterp::I64x2;
1250 return true;
1251 } else if (StringEqualsLiteral(interpLinearStr, "f32x4")) {
1252 *out = LaneInterp::F32x4;
1253 return true;
1254 } else if (StringEqualsLiteral(interpLinearStr, "f64x2")) {
1255 *out = LaneInterp::F64x2;
1256 return true;
1259 JS_ReportErrorASCII(cx, "invalid lane interpretation");
1260 return false;
1263 static bool WasmGlobalExtractLane(JSContext* cx, unsigned argc, Value* vp) {
1264 if (!wasm::HasSupport(cx)) {
1265 JS_ReportErrorASCII(cx, "wasm support unavailable");
1266 return false;
1268 CallArgs args = CallArgsFromVp(argc, vp);
1270 if (args.length() < 3) {
1271 JS_ReportErrorASCII(cx, "not enough arguments");
1272 return false;
1275 // Get the global value
1276 if (!args.get(0).isObject() ||
1277 !args.get(0).toObject().is<WasmGlobalObject>()) {
1278 JS_ReportErrorASCII(cx, "argument is not wasm value");
1279 return false;
1281 Rooted<WasmGlobalObject*> global(
1282 cx, &args.get(0).toObject().as<WasmGlobalObject>());
1284 // Check that we have a v128 value
1285 if (global->type().kind() != wasm::ValType::V128) {
1286 JS_ReportErrorASCII(cx, "global is not a v128 value");
1287 return false;
1289 wasm::V128 v128 = global->val().get().v128();
1291 // Get the passed interpretation of lanes
1292 LaneInterp interp;
1293 if (!ToLaneInterp(cx, args.get(1), &interp)) {
1294 return false;
1297 // Get the lane to extract
1298 int32_t lane;
1299 if (!ToInt32(cx, args.get(2), &lane)) {
1300 return false;
1303 // Check that the lane interp is valid
1304 if (lane < 0 || size_t(lane) >= LaneInterpLanes(interp)) {
1305 JS_ReportErrorASCII(cx, "invalid lane for interp");
1306 return false;
1309 wasm::RootedVal val(cx);
1310 switch (interp) {
1311 case LaneInterp::I32x4: {
1312 uint32_t i;
1313 v128.extractLane<uint32_t>(lane, &i);
1314 val.set(wasm::Val(i));
1315 break;
1317 case LaneInterp::I64x2: {
1318 uint64_t i;
1319 v128.extractLane<uint64_t>(lane, &i);
1320 val.set(wasm::Val(i));
1321 break;
1323 case LaneInterp::F32x4: {
1324 float f;
1325 v128.extractLane<float>(lane, &f);
1326 val.set(wasm::Val(f));
1327 break;
1329 case LaneInterp::F64x2: {
1330 double d;
1331 v128.extractLane<double>(lane, &d);
1332 val.set(wasm::Val(d));
1333 break;
1335 default:
1336 MOZ_ASSERT_UNREACHABLE();
1339 RootedObject proto(
1340 cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmGlobal));
1341 Rooted<WasmGlobalObject*> result(
1342 cx, WasmGlobalObject::create(cx, val, false, proto));
1343 args.rval().setObject(*result.get());
1344 return true;
1347 static bool WasmGlobalsEqual(JSContext* cx, unsigned argc, Value* vp) {
1348 if (!wasm::HasSupport(cx)) {
1349 JS_ReportErrorASCII(cx, "wasm support unavailable");
1350 return false;
1352 CallArgs args = CallArgsFromVp(argc, vp);
1354 if (args.length() < 2) {
1355 JS_ReportErrorASCII(cx, "not enough arguments");
1356 return false;
1359 if (!args.get(0).isObject() ||
1360 !args.get(0).toObject().is<WasmGlobalObject>() ||
1361 !args.get(1).isObject() ||
1362 !args.get(1).toObject().is<WasmGlobalObject>()) {
1363 JS_ReportErrorASCII(cx, "argument is not wasm value");
1364 return false;
1367 Rooted<WasmGlobalObject*> a(cx,
1368 &args.get(0).toObject().as<WasmGlobalObject>());
1369 Rooted<WasmGlobalObject*> b(cx,
1370 &args.get(1).toObject().as<WasmGlobalObject>());
1372 if (a->type().kind() != b->type().kind()) {
1373 JS_ReportErrorASCII(cx, "globals are of different kind");
1374 return false;
1377 bool result;
1378 const wasm::Val& aVal = a->val().get();
1379 const wasm::Val& bVal = b->val().get();
1380 switch (a->type().kind()) {
1381 case wasm::ValType::I32: {
1382 result = aVal.i32() == bVal.i32();
1383 break;
1385 case wasm::ValType::I64: {
1386 result = aVal.i64() == bVal.i64();
1387 break;
1389 case wasm::ValType::F32: {
1390 result = mozilla::BitwiseCast<uint32_t>(aVal.f32()) ==
1391 mozilla::BitwiseCast<uint32_t>(bVal.f32());
1392 break;
1394 case wasm::ValType::F64: {
1395 result = mozilla::BitwiseCast<uint64_t>(aVal.f64()) ==
1396 mozilla::BitwiseCast<uint64_t>(bVal.f64());
1397 break;
1399 case wasm::ValType::V128: {
1400 // Don't know the interpretation of the v128, so we only can do an exact
1401 // bitwise equality. Testing code can use wasmGlobalExtractLane to
1402 // workaround this if needed.
1403 result = aVal.v128() == bVal.v128();
1404 break;
1406 case wasm::ValType::Ref: {
1407 result = aVal.ref() == bVal.ref();
1408 break;
1410 default:
1411 JS_ReportErrorASCII(cx, "unsupported type");
1412 return false;
1414 args.rval().setBoolean(result);
1415 return true;
1418 // Flavors of NaN values for WebAssembly.
1419 // See
1420 // https://webassembly.github.io/spec/core/syntax/values.html#floating-point.
1421 enum class NaNFlavor {
1422 // A canonical NaN value.
1423 // - the sign bit is unspecified,
1424 // - the 8-bit exponent is set to all 1s
1425 // - the MSB of the payload is set to 1 (a quieted NaN) and all others to 0.
1426 Canonical,
1427 // An arithmetic NaN. This is the same as a canonical NaN including that the
1428 // payload MSB is set to 1, but one or more of the remaining payload bits MAY
1429 // BE set to 1 (a canonical NaN specifies all 0s).
1430 Arithmetic,
1433 static bool IsNaNFlavor(uint32_t bits, NaNFlavor flavor) {
1434 switch (flavor) {
1435 case NaNFlavor::Canonical: {
1436 return (bits & 0x7fffffff) == 0x7fc00000;
1438 case NaNFlavor::Arithmetic: {
1439 const uint32_t ArithmeticNaN = 0x7f800000;
1440 const uint32_t ArithmeticPayloadMSB = 0x00400000;
1441 bool isNaN = (bits & ArithmeticNaN) == ArithmeticNaN;
1442 bool isMSBSet = (bits & ArithmeticPayloadMSB) == ArithmeticPayloadMSB;
1443 return isNaN && isMSBSet;
1445 default:
1446 MOZ_CRASH();
1450 static bool IsNaNFlavor(uint64_t bits, NaNFlavor flavor) {
1451 switch (flavor) {
1452 case NaNFlavor::Canonical: {
1453 return (bits & 0x7fffffffffffffff) == 0x7ff8000000000000;
1455 case NaNFlavor::Arithmetic: {
1456 uint64_t ArithmeticNaN = 0x7ff0000000000000;
1457 uint64_t ArithmeticPayloadMSB = 0x0008000000000000;
1458 bool isNaN = (bits & ArithmeticNaN) == ArithmeticNaN;
1459 bool isMsbSet = (bits & ArithmeticPayloadMSB) == ArithmeticPayloadMSB;
1460 return isNaN && isMsbSet;
1462 default:
1463 MOZ_CRASH();
1467 static bool ToNaNFlavor(JSContext* cx, HandleValue v, NaNFlavor* out) {
1468 RootedString flavorStr(cx, ToString(cx, v));
1469 if (!flavorStr) {
1470 return false;
1472 Rooted<JSLinearString*> flavorLinearStr(cx, flavorStr->ensureLinear(cx));
1473 if (!flavorLinearStr) {
1474 return false;
1477 if (StringEqualsLiteral(flavorLinearStr, "canonical_nan")) {
1478 *out = NaNFlavor::Canonical;
1479 return true;
1480 } else if (StringEqualsLiteral(flavorLinearStr, "arithmetic_nan")) {
1481 *out = NaNFlavor::Arithmetic;
1482 return true;
1485 JS_ReportErrorASCII(cx, "invalid nan flavor");
1486 return false;
1489 static bool WasmGlobalIsNaN(JSContext* cx, unsigned argc, Value* vp) {
1490 if (!wasm::HasSupport(cx)) {
1491 JS_ReportErrorASCII(cx, "wasm support unavailable");
1492 return false;
1494 CallArgs args = CallArgsFromVp(argc, vp);
1496 if (args.length() < 2) {
1497 JS_ReportErrorASCII(cx, "not enough arguments");
1498 return false;
1501 if (!args.get(0).isObject() ||
1502 !args.get(0).toObject().is<WasmGlobalObject>()) {
1503 JS_ReportErrorASCII(cx, "argument is not wasm value");
1504 return false;
1506 Rooted<WasmGlobalObject*> global(
1507 cx, &args.get(0).toObject().as<WasmGlobalObject>());
1509 NaNFlavor flavor;
1510 if (!ToNaNFlavor(cx, args.get(1), &flavor)) {
1511 return false;
1514 bool result;
1515 const wasm::Val& val = global->val().get();
1516 switch (global->type().kind()) {
1517 case wasm::ValType::F32: {
1518 result = IsNaNFlavor(mozilla::BitwiseCast<uint32_t>(val.f32()), flavor);
1519 break;
1521 case wasm::ValType::F64: {
1522 result = IsNaNFlavor(mozilla::BitwiseCast<uint64_t>(val.f64()), flavor);
1523 break;
1525 default:
1526 JS_ReportErrorASCII(cx, "global is not a floating point value");
1527 return false;
1529 args.rval().setBoolean(result);
1530 return true;
1533 static bool WasmGlobalToString(JSContext* cx, unsigned argc, Value* vp) {
1534 if (!wasm::HasSupport(cx)) {
1535 JS_ReportErrorASCII(cx, "wasm support unavailable");
1536 return false;
1538 CallArgs args = CallArgsFromVp(argc, vp);
1540 if (args.length() < 1) {
1541 JS_ReportErrorASCII(cx, "not enough arguments");
1542 return false;
1544 if (!args.get(0).isObject() ||
1545 !args.get(0).toObject().is<WasmGlobalObject>()) {
1546 JS_ReportErrorASCII(cx, "argument is not wasm value");
1547 return false;
1549 Rooted<WasmGlobalObject*> global(
1550 cx, &args.get(0).toObject().as<WasmGlobalObject>());
1551 const wasm::Val& globalVal = global->val().get();
1553 UniqueChars result;
1554 switch (globalVal.type().kind()) {
1555 case wasm::ValType::I32: {
1556 result = JS_smprintf("i32:%" PRIx32, globalVal.i32());
1557 break;
1559 case wasm::ValType::I64: {
1560 result = JS_smprintf("i64:%" PRIx64, globalVal.i64());
1561 break;
1563 case wasm::ValType::F32: {
1564 result = JS_smprintf("f32:%f", globalVal.f32());
1565 break;
1567 case wasm::ValType::F64: {
1568 result = JS_smprintf("f64:%lf", globalVal.f64());
1569 break;
1571 case wasm::ValType::V128: {
1572 wasm::V128 v128 = globalVal.v128();
1573 result = JS_smprintf(
1574 "v128:%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x", v128.bytes[0],
1575 v128.bytes[1], v128.bytes[2], v128.bytes[3], v128.bytes[4],
1576 v128.bytes[5], v128.bytes[6], v128.bytes[7], v128.bytes[8],
1577 v128.bytes[9], v128.bytes[10], v128.bytes[11], v128.bytes[12],
1578 v128.bytes[13], v128.bytes[14], v128.bytes[15]);
1579 break;
1581 case wasm::ValType::Ref: {
1582 result = JS_smprintf("ref:%" PRIxPTR, globalVal.ref().rawValue());
1583 break;
1585 default:
1586 MOZ_ASSERT_UNREACHABLE();
1589 args.rval().setString(JS_NewStringCopyZ(cx, result.get()));
1590 return true;
1593 static bool WasmLosslessInvoke(JSContext* cx, unsigned argc, Value* vp) {
1594 if (!wasm::HasSupport(cx)) {
1595 JS_ReportErrorASCII(cx, "wasm support unavailable");
1596 return false;
1598 CallArgs args = CallArgsFromVp(argc, vp);
1600 if (args.length() < 1) {
1601 JS_ReportErrorASCII(cx, "not enough arguments");
1602 return false;
1604 if (!args.get(0).isObject()) {
1605 JS_ReportErrorASCII(cx, "argument is not an object");
1606 return false;
1609 RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>());
1610 if (!func || !wasm::IsWasmExportedFunction(func)) {
1611 JS_ReportErrorASCII(cx, "argument is not an exported wasm function");
1612 return false;
1615 // Get the instance and funcIndex for calling the function
1616 wasm::Instance& instance = wasm::ExportedFunctionToInstance(func);
1617 uint32_t funcIndex = wasm::ExportedFunctionToFuncIndex(func);
1619 // Set up a modified call frame following the standard JS
1620 // [callee, this, arguments...] convention.
1621 RootedValueVector wasmCallFrame(cx);
1622 size_t len = 2 + args.length();
1623 if (!wasmCallFrame.resize(len)) {
1624 return false;
1626 wasmCallFrame[0].set(args.calleev());
1627 wasmCallFrame[1].set(args.thisv());
1628 // Copy over the arguments needed to invoke the provided wasm function,
1629 // skipping the wasm function we're calling that is at `args.get(0)`.
1630 for (size_t i = 1; i < args.length(); i++) {
1631 size_t wasmArg = i - 1;
1632 wasmCallFrame[2 + wasmArg].set(args.get(i));
1634 size_t wasmArgc = argc - 1;
1635 CallArgs wasmCallArgs(CallArgsFromVp(wasmArgc, wasmCallFrame.begin()));
1637 // Invoke the function with the new call frame
1638 bool result = instance.callExport(cx, funcIndex, wasmCallArgs,
1639 wasm::CoercionLevel::Lossless);
1640 // Assign the wasm rval to our rval
1641 args.rval().set(wasmCallArgs.rval());
1642 return result;
1645 static bool ConvertToTier(JSContext* cx, HandleValue value,
1646 const wasm::Code& code, wasm::Tier* tier) {
1647 RootedString option(cx, JS::ToString(cx, value));
1649 if (!option) {
1650 return false;
1653 bool stableTier = false;
1654 bool bestTier = false;
1655 bool baselineTier = false;
1656 bool ionTier = false;
1658 if (!JS_StringEqualsLiteral(cx, option, "stable", &stableTier) ||
1659 !JS_StringEqualsLiteral(cx, option, "best", &bestTier) ||
1660 !JS_StringEqualsLiteral(cx, option, "baseline", &baselineTier) ||
1661 !JS_StringEqualsLiteral(cx, option, "ion", &ionTier)) {
1662 return false;
1665 if (stableTier) {
1666 *tier = code.stableTier();
1667 } else if (bestTier) {
1668 *tier = code.bestTier();
1669 } else if (baselineTier) {
1670 *tier = wasm::Tier::Baseline;
1671 } else if (ionTier) {
1672 *tier = wasm::Tier::Optimized;
1673 } else {
1674 // You can omit the argument but you can't pass just anything you like
1675 return false;
1678 return true;
1681 static bool WasmExtractCode(JSContext* cx, unsigned argc, Value* vp) {
1682 if (!wasm::HasSupport(cx)) {
1683 JS_ReportErrorASCII(cx, "wasm support unavailable");
1684 return false;
1687 CallArgs args = CallArgsFromVp(argc, vp);
1689 if (!args.get(0).isObject()) {
1690 JS_ReportErrorASCII(cx, "argument is not an object");
1691 return false;
1694 Rooted<WasmModuleObject*> module(
1695 cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>());
1696 if (!module) {
1697 JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
1698 return false;
1701 wasm::Tier tier = module->module().code().stableTier();
1703 if (args.length() > 1 &&
1704 !ConvertToTier(cx, args[1], module->module().code(), &tier)) {
1705 args.rval().setNull();
1706 return false;
1709 RootedValue result(cx);
1710 if (!module->module().extractCode(cx, tier, &result)) {
1711 return false;
1714 args.rval().set(result);
1715 return true;
1718 struct DisasmBuffer {
1719 JSStringBuilder builder;
1720 bool oom;
1721 explicit DisasmBuffer(JSContext* cx) : builder(cx), oom(false) {}
1724 static bool HasDisassembler(JSContext* cx, unsigned argc, Value* vp) {
1725 CallArgs args = CallArgsFromVp(argc, vp);
1726 args.rval().setBoolean(jit::HasDisassembler());
1727 return true;
1730 MOZ_THREAD_LOCAL(DisasmBuffer*) disasmBuf;
1732 static void captureDisasmText(const char* text) {
1733 DisasmBuffer* buf = disasmBuf.get();
1734 if (!buf->builder.append(text, strlen(text)) || !buf->builder.append('\n')) {
1735 buf->oom = true;
1739 static bool DisassembleNative(JSContext* cx, unsigned argc, Value* vp) {
1740 CallArgs args = CallArgsFromVp(argc, vp);
1741 args.rval().setUndefined();
1743 if (args.length() < 1) {
1744 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1745 JSMSG_MORE_ARGS_NEEDED, "disnative", "1", "",
1746 "0");
1747 return false;
1750 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
1751 JS_ReportErrorASCII(cx, "The first argument must be a function.");
1752 return false;
1755 JSSprinter sprinter(cx);
1756 if (!sprinter.init()) {
1757 return false;
1760 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
1762 uint8_t* jit_begin = nullptr;
1763 uint8_t* jit_end = nullptr;
1765 if (fun->isAsmJSNative() || fun->isWasmWithJitEntry()) {
1766 if (fun->isAsmJSNative()) {
1767 return false;
1769 sprinter.printf("; backend=asmjs\n");
1770 sprinter.printf("; backend=wasm\n");
1772 js::wasm::Instance& inst = fun->wasmInstance();
1773 const js::wasm::Code& code = inst.code();
1774 js::wasm::Tier tier = code.bestTier();
1776 const js::wasm::MetadataTier& meta = inst.metadata(tier);
1778 const js::wasm::CodeSegment& segment = code.segment(tier);
1779 const uint32_t funcIndex = code.getFuncIndex(&*fun);
1780 const js::wasm::FuncExport& func = meta.lookupFuncExport(funcIndex);
1781 const js::wasm::CodeRange& codeRange = meta.codeRange(func);
1783 jit_begin = segment.base() + codeRange.begin();
1784 jit_end = segment.base() + codeRange.end();
1785 } else if (fun->hasJitScript()) {
1786 JSScript* script = fun->nonLazyScript();
1787 if (script == nullptr) {
1788 return false;
1791 js::jit::IonScript* ion =
1792 script->hasIonScript() ? script->ionScript() : nullptr;
1793 js::jit::BaselineScript* baseline =
1794 script->hasBaselineScript() ? script->baselineScript() : nullptr;
1795 if (ion && ion->method()) {
1796 sprinter.printf("; backend=ion\n");
1797 jit_begin = ion->method()->raw();
1798 jit_end = ion->method()->rawEnd();
1799 } else if (baseline) {
1800 sprinter.printf("; backend=baseline\n");
1801 jit_begin = baseline->method()->raw();
1802 jit_end = baseline->method()->rawEnd();
1804 } else {
1805 JS_ReportErrorASCII(cx,
1806 "The function hasn't been warmed up, hence no JIT code "
1807 "to disassemble.");
1808 return false;
1811 if (jit_begin == nullptr || jit_end == nullptr) {
1812 return false;
1815 #ifdef JS_CODEGEN_ARM
1816 // The ARM32 disassembler is currently not fuzzing-safe because it doesn't
1817 // handle constant pools correctly (bug 1875363).
1818 if (fuzzingSafe) {
1819 JS_ReportErrorASCII(cx, "disnative is not fuzzing-safe on ARM32");
1820 return false;
1822 #endif
1824 // Dump the raw code to a file before disassembling in case
1825 // finishString triggers a GC and discards the jitcode.
1826 if (!fuzzingSafe && args.length() > 1 && args[1].isString()) {
1827 RootedString str(cx, args[1].toString());
1828 JS::UniqueChars fileNameBytes = JS_EncodeStringToUTF8(cx, str);
1830 const char* fileName = fileNameBytes.get();
1831 if (!fileName) {
1832 ReportOutOfMemory(cx);
1833 return false;
1836 FILE* f = fopen(fileName, "w");
1837 if (!f) {
1838 JS_ReportErrorASCII(cx, "Could not open file for writing.");
1839 return false;
1842 uintptr_t expected_length = reinterpret_cast<uintptr_t>(jit_end) -
1843 reinterpret_cast<uintptr_t>(jit_begin);
1844 if (expected_length != fwrite(jit_begin, jit_end - jit_begin, 1, f)) {
1845 JS_ReportErrorASCII(cx, "Did not write all function bytes to the file.");
1846 fclose(f);
1847 return false;
1849 fclose(f);
1852 DisasmBuffer buf(cx);
1853 disasmBuf.set(&buf);
1854 auto onFinish = mozilla::MakeScopeExit([&] { disasmBuf.set(nullptr); });
1856 jit::Disassemble(jit_begin, jit_end - jit_begin, &captureDisasmText);
1858 if (buf.oom) {
1859 ReportOutOfMemory(cx);
1860 return false;
1862 JSString* sresult = buf.builder.finishString();
1863 if (!sresult) {
1864 ReportOutOfMemory(cx);
1865 return false;
1867 sprinter.putString(cx, sresult);
1869 JSString* str = sprinter.release(cx);
1870 if (!str) {
1871 return false;
1874 args[0].setUndefined();
1875 args.rval().setString(str);
1877 return true;
1880 static bool ComputeTier(JSContext* cx, const wasm::Code& code,
1881 HandleValue tierSelection, wasm::Tier* tier) {
1882 *tier = code.stableTier();
1883 if (!tierSelection.isUndefined() &&
1884 !ConvertToTier(cx, tierSelection, code, tier)) {
1885 JS_ReportErrorASCII(cx, "invalid tier");
1886 return false;
1889 if (!code.hasTier(*tier)) {
1890 JS_ReportErrorASCII(cx, "function missing selected tier");
1891 return false;
1894 return true;
1897 template <typename DisasmFunction>
1898 static bool DisassembleIt(JSContext* cx, bool asString, MutableHandleValue rval,
1899 DisasmFunction&& disassembleIt) {
1900 if (asString) {
1901 DisasmBuffer buf(cx);
1902 disasmBuf.set(&buf);
1903 auto onFinish = mozilla::MakeScopeExit([&] { disasmBuf.set(nullptr); });
1904 disassembleIt(captureDisasmText);
1905 if (buf.oom) {
1906 ReportOutOfMemory(cx);
1907 return false;
1909 JSString* sresult = buf.builder.finishString();
1910 if (!sresult) {
1911 ReportOutOfMemory(cx);
1912 return false;
1914 rval.setString(sresult);
1915 return true;
1918 disassembleIt([](const char* text) { fprintf(stderr, "%s\n", text); });
1919 return true;
1922 static bool WasmDisassembleFunction(JSContext* cx, const HandleFunction& func,
1923 HandleValue tierSelection, bool asString,
1924 MutableHandleValue rval) {
1925 wasm::Instance& instance = wasm::ExportedFunctionToInstance(func);
1926 wasm::Tier tier;
1928 if (!ComputeTier(cx, instance.code(), tierSelection, &tier)) {
1929 return false;
1932 uint32_t funcIndex = wasm::ExportedFunctionToFuncIndex(func);
1933 return DisassembleIt(
1934 cx, asString, rval, [&](void (*captureText)(const char*)) {
1935 instance.disassembleExport(cx, funcIndex, tier, captureText);
1939 static bool WasmDisassembleCode(JSContext* cx, const wasm::Code& code,
1940 HandleValue tierSelection, int kindSelection,
1941 bool asString, MutableHandleValue rval) {
1942 wasm::Tier tier;
1943 if (!ComputeTier(cx, code, tierSelection, &tier)) {
1944 return false;
1947 return DisassembleIt(cx, asString, rval,
1948 [&](void (*captureText)(const char*)) {
1949 code.disassemble(cx, tier, kindSelection, captureText);
1953 static bool WasmDisassemble(JSContext* cx, unsigned argc, Value* vp) {
1954 if (!wasm::HasSupport(cx)) {
1955 JS_ReportErrorASCII(cx, "wasm support unavailable");
1956 return false;
1959 CallArgs args = CallArgsFromVp(argc, vp);
1961 args.rval().set(UndefinedValue());
1963 if (!args.get(0).isObject()) {
1964 JS_ReportErrorASCII(cx, "argument is not an object");
1965 return false;
1968 bool asString = false;
1969 RootedValue tierSelection(cx);
1970 int kindSelection = (1 << wasm::CodeRange::Function);
1971 if (args.length() > 1 && args[1].isObject()) {
1972 RootedObject options(cx, &args[1].toObject());
1973 RootedValue val(cx);
1975 if (!JS_GetProperty(cx, options, "asString", &val)) {
1976 return false;
1978 asString = val.isBoolean() && val.toBoolean();
1980 if (!JS_GetProperty(cx, options, "tier", &tierSelection)) {
1981 return false;
1984 if (!JS_GetProperty(cx, options, "kinds", &val)) {
1985 return false;
1987 if (val.isString() && val.toString()->hasLatin1Chars()) {
1988 AutoStableStringChars stable(cx);
1989 if (!stable.init(cx, val.toString())) {
1990 return false;
1992 const char* p = (const char*)(stable.latin1Chars());
1993 const char* end = p + val.toString()->length();
1994 int selection = 0;
1995 for (;;) {
1996 if (strncmp(p, "Function", 8) == 0) {
1997 selection |= (1 << wasm::CodeRange::Function);
1998 p += 8;
1999 } else if (strncmp(p, "InterpEntry", 11) == 0) {
2000 selection |= (1 << wasm::CodeRange::InterpEntry);
2001 p += 11;
2002 } else if (strncmp(p, "JitEntry", 8) == 0) {
2003 selection |= (1 << wasm::CodeRange::JitEntry);
2004 p += 8;
2005 } else if (strncmp(p, "ImportInterpExit", 16) == 0) {
2006 selection |= (1 << wasm::CodeRange::ImportInterpExit);
2007 p += 16;
2008 } else if (strncmp(p, "ImportJitExit", 13) == 0) {
2009 selection |= (1 << wasm::CodeRange::ImportJitExit);
2010 p += 13;
2011 } else if (strncmp(p, "all", 3) == 0) {
2012 selection = ~0;
2013 p += 3;
2014 } else {
2015 break;
2017 if (p == end || *p != ',') {
2018 break;
2020 p++;
2022 if (p == end) {
2023 kindSelection = selection;
2024 } else {
2025 JS_ReportErrorASCII(cx, "argument object has invalid `kinds`");
2026 return false;
2031 RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>());
2032 if (func && wasm::IsWasmExportedFunction(func)) {
2033 return WasmDisassembleFunction(cx, func, tierSelection, asString,
2034 args.rval());
2036 if (args[0].toObject().is<WasmModuleObject>()) {
2037 return WasmDisassembleCode(
2038 cx, args[0].toObject().as<WasmModuleObject>().module().code(),
2039 tierSelection, kindSelection, asString, args.rval());
2041 if (args[0].toObject().is<WasmInstanceObject>()) {
2042 return WasmDisassembleCode(
2043 cx, args[0].toObject().as<WasmInstanceObject>().instance().code(),
2044 tierSelection, kindSelection, asString, args.rval());
2046 JS_ReportErrorASCII(
2047 cx, "argument is not an exported wasm function or a wasm module");
2048 return false;
2051 enum class Flag { Tier2Complete, Deserialized };
2053 static bool WasmReturnFlag(JSContext* cx, unsigned argc, Value* vp, Flag flag) {
2054 CallArgs args = CallArgsFromVp(argc, vp);
2056 if (!args.get(0).isObject()) {
2057 JS_ReportErrorASCII(cx, "argument is not an object");
2058 return false;
2061 Rooted<WasmModuleObject*> module(
2062 cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>());
2063 if (!module) {
2064 JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
2065 return false;
2068 bool b;
2069 switch (flag) {
2070 case Flag::Tier2Complete:
2071 b = !module->module().testingTier2Active();
2072 break;
2073 case Flag::Deserialized:
2074 b = module->module().loggingDeserialized();
2075 break;
2078 args.rval().set(BooleanValue(b));
2079 return true;
2082 #if defined(DEBUG)
2083 static bool wasmMetadataAnalysis(JSContext* cx, unsigned argc, Value* vp) {
2084 CallArgs args = CallArgsFromVp(argc, vp);
2086 if (!args.get(0).isObject()) {
2087 JS_ReportErrorASCII(cx, "argument is not an object");
2088 return false;
2091 if (!cx->options().wasmTestMetadata()) {
2092 return false;
2095 if (args[0].toObject().is<WasmModuleObject>()) {
2096 HashMap<const char*, uint32_t, mozilla::CStringHasher, SystemAllocPolicy>
2097 hashmap = args[0]
2098 .toObject()
2099 .as<WasmModuleObject>()
2100 .module()
2101 .code()
2102 .metadataAnalysis(cx);
2103 if (hashmap.empty()) {
2104 JS_ReportErrorASCII(cx, "Metadata analysis has failed");
2107 // metadataAnalysis returned a map of {key, value} with various statistics
2108 // convert it into a dictionary to be used by JS
2109 Rooted<IdValueVector> props(cx, IdValueVector(cx));
2111 for (auto iter = hashmap.iter(); !iter.done(); iter.next()) {
2112 const auto* key = iter.get().key();
2113 auto value = iter.get().value();
2115 JSString* string = JS_NewStringCopyZ(cx, key);
2116 if (!props.append(
2117 IdValuePair(NameToId(string->asLinear().toPropertyName(cx)),
2118 NumberValue(value)))) {
2119 ReportOutOfMemory(cx);
2120 return false;
2124 JSObject* results =
2125 NewPlainObjectWithUniqueNames(cx, props.begin(), props.length());
2126 args.rval().setObject(*results);
2128 return true;
2131 JS_ReportErrorASCII(
2132 cx, "argument is not an exported wasm function or a wasm module");
2134 return false;
2136 #endif
2138 static bool WasmHasTier2CompilationCompleted(JSContext* cx, unsigned argc,
2139 Value* vp) {
2140 return WasmReturnFlag(cx, argc, vp, Flag::Tier2Complete);
2143 static bool WasmLoadedFromCache(JSContext* cx, unsigned argc, Value* vp) {
2144 return WasmReturnFlag(cx, argc, vp, Flag::Deserialized);
2147 static bool WasmBuiltinI8VecMul(JSContext* cx, unsigned argc, Value* vp) {
2148 if (!wasm::HasSupport(cx)) {
2149 JS_ReportErrorASCII(cx, "wasm support unavailable");
2150 return false;
2153 CallArgs args = CallArgsFromVp(argc, vp);
2155 Rooted<WasmModuleObject*> module(cx);
2156 if (!wasm::CompileBuiltinModule(cx, wasm::BuiltinModuleId::SelfTest,
2157 &module)) {
2158 return false;
2160 args.rval().set(ObjectValue(*module.get()));
2161 return true;
2164 #ifdef ENABLE_WASM_GC
2165 static bool WasmGcReadField(JSContext* cx, unsigned argc, Value* vp) {
2166 CallArgs args = CallArgsFromVp(argc, vp);
2167 RootedObject callee(cx, &args.callee());
2169 if (!args.requireAtLeast(cx, "wasmGcReadField", 2)) {
2170 return false;
2173 if (!args[0].isObject() || !args[0].toObject().is<WasmGcObject>()) {
2174 ReportUsageErrorASCII(cx, callee,
2175 "First argument must be a WebAssembly GC object");
2176 return false;
2179 int32_t fieldIndex;
2180 if (!JS::ToInt32(cx, args[1], &fieldIndex) || fieldIndex < 0) {
2181 ReportUsageErrorASCII(cx, callee,
2182 "Second argument must be a non-negative integer");
2183 return false;
2186 Rooted<WasmGcObject*> gcObject(cx, &args[0].toObject().as<WasmGcObject>());
2187 Rooted<Value> gcValue(cx);
2188 if (!WasmGcObject::loadValue(cx, gcObject, jsid::Int(int32_t(fieldIndex)),
2189 &gcValue)) {
2190 return false;
2193 args.rval().set(gcValue);
2194 return true;
2197 static bool WasmGcArrayLength(JSContext* cx, unsigned argc, Value* vp) {
2198 CallArgs args = CallArgsFromVp(argc, vp);
2199 RootedObject callee(cx, &args.callee());
2201 if (!args.requireAtLeast(cx, "wasmGcArrayLength", 1)) {
2202 return false;
2205 if (!args[0].isObject() || !args[0].toObject().is<WasmArrayObject>()) {
2206 ReportUsageErrorASCII(cx, callee,
2207 "First argument must be a WebAssembly GC array");
2208 return false;
2211 WasmArrayObject& arr = args[0].toObject().as<WasmArrayObject>();
2212 args.rval().setInt32(int32_t(arr.numElements_));
2213 return true;
2215 #endif // ENABLE_WASM_GC
2217 static bool LargeArrayBufferSupported(JSContext* cx, unsigned argc, Value* vp) {
2218 CallArgs args = CallArgsFromVp(argc, vp);
2219 args.rval().setBoolean(ArrayBufferObject::ByteLengthLimit >
2220 ArrayBufferObject::ByteLengthLimitForSmallBuffer);
2221 return true;
2224 static bool IsLazyFunction(JSContext* cx, unsigned argc, Value* vp) {
2225 CallArgs args = CallArgsFromVp(argc, vp);
2226 if (args.length() != 1) {
2227 JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
2228 return false;
2230 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
2231 JS_ReportErrorASCII(cx, "The first argument should be a function.");
2232 return false;
2234 JSFunction* fun = &args[0].toObject().as<JSFunction>();
2235 args.rval().setBoolean(fun->isInterpreted() && !fun->hasBytecode());
2236 return true;
2239 static bool IsRelazifiableFunction(JSContext* cx, unsigned argc, Value* vp) {
2240 CallArgs args = CallArgsFromVp(argc, vp);
2241 if (args.length() != 1) {
2242 JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
2243 return false;
2245 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
2246 JS_ReportErrorASCII(cx, "The first argument should be a function.");
2247 return false;
2250 JSFunction* fun = &args[0].toObject().as<JSFunction>();
2251 args.rval().setBoolean(fun->hasBytecode() &&
2252 fun->nonLazyScript()->allowRelazify());
2253 return true;
2256 static bool IsInStencilCache(JSContext* cx, unsigned argc, Value* vp) {
2257 CallArgs args = CallArgsFromVp(argc, vp);
2258 if (args.length() != 1) {
2259 JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
2260 return false;
2263 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
2264 JS_ReportErrorASCII(cx, "The first argument should be a function.");
2265 return false;
2268 if (fuzzingSafe) {
2269 // When running code concurrently to fill-up the stencil cache, the content
2270 // is not garanteed to be present.
2271 args.rval().setBoolean(false);
2272 return true;
2275 JSFunction* fun = &args[0].toObject().as<JSFunction>();
2276 BaseScript* script = fun->baseScript();
2277 RefPtr<ScriptSource> ss = script->scriptSource();
2278 DelazificationCache& cache = DelazificationCache::getSingleton();
2279 auto guard = cache.isSourceCached(ss);
2280 if (!guard) {
2281 args.rval().setBoolean(false);
2282 return true;
2285 StencilContext key(ss, script->extent());
2286 frontend::CompilationStencil* stencil = cache.lookup(guard, key);
2287 args.rval().setBoolean(bool(stencil));
2288 return true;
2291 static bool WaitForStencilCache(JSContext* cx, unsigned argc, Value* vp) {
2292 CallArgs args = CallArgsFromVp(argc, vp);
2293 if (args.length() != 1) {
2294 JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
2295 return false;
2298 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
2299 JS_ReportErrorASCII(cx, "The first argument should be a function.");
2300 return false;
2302 args.rval().setUndefined();
2304 JSFunction* fun = &args[0].toObject().as<JSFunction>();
2305 BaseScript* script = fun->baseScript();
2306 RefPtr<ScriptSource> ss = script->scriptSource();
2307 DelazificationCache& cache = DelazificationCache::getSingleton();
2308 StencilContext key(ss, script->extent());
2310 AutoLockHelperThreadState lock;
2311 if (!HelperThreadState().isInitialized(lock)) {
2312 return true;
2315 while (true) {
2317 // This capture a Mutex that we have to release before using the wait
2318 // function.
2319 auto guard = cache.isSourceCached(ss);
2320 if (!guard) {
2321 return true;
2324 frontend::CompilationStencil* stencil = cache.lookup(guard, key);
2325 if (stencil) {
2326 break;
2330 HelperThreadState().wait(lock);
2332 return true;
2335 static bool HasSameBytecodeData(JSContext* cx, unsigned argc, Value* vp) {
2336 CallArgs args = CallArgsFromVp(argc, vp);
2337 if (args.length() != 2) {
2338 JS_ReportErrorASCII(cx, "The function takes exactly two argument.");
2339 return false;
2342 auto GetSharedData = [](JSContext* cx,
2343 HandleValue v) -> SharedImmutableScriptData* {
2344 if (!v.isObject()) {
2345 JS_ReportErrorASCII(cx, "The arguments must be interpreted functions.");
2346 return nullptr;
2349 RootedObject obj(cx, CheckedUnwrapDynamic(&v.toObject(), cx));
2350 if (!obj) {
2351 return nullptr;
2354 if (!obj->is<JSFunction>() || !obj->as<JSFunction>().isInterpreted()) {
2355 JS_ReportErrorASCII(cx, "The arguments must be interpreted functions.");
2356 return nullptr;
2359 AutoRealm ar(cx, obj);
2360 RootedFunction fun(cx, &obj->as<JSFunction>());
2361 RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
2362 if (!script) {
2363 return nullptr;
2366 MOZ_ASSERT(script->sharedData());
2367 return script->sharedData();
2370 // NOTE: We use RefPtr below to keep the data alive across possible GC since
2371 // the functions may be in different Zones.
2373 RefPtr<SharedImmutableScriptData> sharedData1 = GetSharedData(cx, args[0]);
2374 if (!sharedData1) {
2375 return false;
2378 RefPtr<SharedImmutableScriptData> sharedData2 = GetSharedData(cx, args[1]);
2379 if (!sharedData2) {
2380 return false;
2383 args.rval().setBoolean(sharedData1 == sharedData2);
2384 return true;
2387 static bool InternalConst(JSContext* cx, unsigned argc, Value* vp) {
2388 CallArgs args = CallArgsFromVp(argc, vp);
2389 if (args.length() == 0) {
2390 JS_ReportErrorASCII(cx, "the function takes exactly one argument");
2391 return false;
2394 JSString* str = ToString(cx, args[0]);
2395 if (!str) {
2396 return false;
2398 JSLinearString* linear = JS_EnsureLinearString(cx, str);
2399 if (!linear) {
2400 return false;
2403 if (JS_LinearStringEqualsLiteral(linear, "MARK_STACK_BASE_CAPACITY")) {
2404 args.rval().setNumber(uint32_t(js::MARK_STACK_BASE_CAPACITY));
2405 } else {
2406 JS_ReportErrorASCII(cx, "unknown const name");
2407 return false;
2409 return true;
2412 static bool GCPreserveCode(JSContext* cx, unsigned argc, Value* vp) {
2413 CallArgs args = CallArgsFromVp(argc, vp);
2415 if (args.length() != 0) {
2416 RootedObject callee(cx, &args.callee());
2417 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2418 return false;
2421 cx->runtime()->gc.setAlwaysPreserveCode();
2423 args.rval().setUndefined();
2424 return true;
2427 #ifdef JS_GC_ZEAL
2429 static bool ParseGCZealMode(JSContext* cx, const CallArgs& args,
2430 uint8_t* zeal) {
2431 uint32_t value;
2432 if (!ToUint32(cx, args.get(0), &value)) {
2433 return false;
2436 if (value > uint32_t(gc::ZealMode::Limit)) {
2437 JS_ReportErrorASCII(cx, "gczeal argument out of range");
2438 return false;
2441 *zeal = static_cast<uint8_t>(value);
2442 return true;
2445 static bool GCZeal(JSContext* cx, unsigned argc, Value* vp) {
2446 CallArgs args = CallArgsFromVp(argc, vp);
2448 if (args.length() > 2) {
2449 RootedObject callee(cx, &args.callee());
2450 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2451 return false;
2454 if (args.length() == 0) {
2455 uint32_t zealBits, unused1, unused2;
2456 cx->runtime()->gc.getZealBits(&zealBits, &unused1, &unused2);
2457 args.rval().setNumber(zealBits);
2458 return true;
2461 uint8_t zeal;
2462 if (!ParseGCZealMode(cx, args, &zeal)) {
2463 return false;
2466 uint32_t frequency = JS_DEFAULT_ZEAL_FREQ;
2467 if (args.length() >= 2) {
2468 if (!ToUint32(cx, args.get(1), &frequency)) {
2469 return false;
2473 JS_SetGCZeal(cx, zeal, frequency);
2474 args.rval().setUndefined();
2475 return true;
2478 static bool UnsetGCZeal(JSContext* cx, unsigned argc, Value* vp) {
2479 CallArgs args = CallArgsFromVp(argc, vp);
2481 if (args.length() > 1) {
2482 RootedObject callee(cx, &args.callee());
2483 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2484 return false;
2487 uint8_t zeal;
2488 if (!ParseGCZealMode(cx, args, &zeal)) {
2489 return false;
2492 JS_UnsetGCZeal(cx, zeal);
2493 args.rval().setUndefined();
2494 return true;
2497 static bool ScheduleGC(JSContext* cx, unsigned argc, Value* vp) {
2498 CallArgs args = CallArgsFromVp(argc, vp);
2500 if (args.length() > 1) {
2501 RootedObject callee(cx, &args.callee());
2502 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2503 return false;
2506 if (args.length() == 0) {
2507 /* Fetch next zeal trigger only. */
2508 } else if (args[0].isNumber()) {
2509 /* Schedule a GC to happen after |arg| allocations. */
2510 JS_ScheduleGC(cx, std::max(int(args[0].toNumber()), 0));
2511 } else {
2512 RootedObject callee(cx, &args.callee());
2513 ReportUsageErrorASCII(cx, callee, "Bad argument - expecting number");
2514 return false;
2517 uint32_t zealBits;
2518 uint32_t freq;
2519 uint32_t next;
2520 JS_GetGCZealBits(cx, &zealBits, &freq, &next);
2521 args.rval().setInt32(next);
2522 return true;
2525 static bool SelectForGC(JSContext* cx, unsigned argc, Value* vp) {
2526 CallArgs args = CallArgsFromVp(argc, vp);
2529 * The selectedForMarking set is intended to be manually marked at slice
2530 * start to detect missing pre-barriers. It is invalid for nursery things
2531 * to be in the set, so evict the nursery before adding items.
2533 cx->runtime()->gc.evictNursery();
2535 for (unsigned i = 0; i < args.length(); i++) {
2536 if (args[i].isObject()) {
2537 if (!cx->runtime()->gc.selectForMarking(&args[i].toObject())) {
2538 return false;
2543 args.rval().setUndefined();
2544 return true;
2547 static bool VerifyPreBarriers(JSContext* cx, unsigned argc, Value* vp) {
2548 CallArgs args = CallArgsFromVp(argc, vp);
2550 if (args.length() > 0) {
2551 RootedObject callee(cx, &args.callee());
2552 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2553 return false;
2556 gc::VerifyBarriers(cx->runtime(), gc::PreBarrierVerifier);
2557 args.rval().setUndefined();
2558 return true;
2561 static bool VerifyPostBarriers(JSContext* cx, unsigned argc, Value* vp) {
2562 // This is a no-op since the post barrier verifier was removed.
2563 CallArgs args = CallArgsFromVp(argc, vp);
2564 if (args.length()) {
2565 RootedObject callee(cx, &args.callee());
2566 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2567 return false;
2569 args.rval().setUndefined();
2570 return true;
2573 static bool CurrentGC(JSContext* cx, unsigned argc, Value* vp) {
2574 CallArgs args = CallArgsFromVp(argc, vp);
2576 if (args.length() != 0) {
2577 RootedObject callee(cx, &args.callee());
2578 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2579 return false;
2582 RootedObject result(cx, JS_NewPlainObject(cx));
2583 if (!result) {
2584 return false;
2587 js::gc::GCRuntime& gc = cx->runtime()->gc;
2588 const char* state = StateName(gc.state());
2590 RootedString str(cx, JS_NewStringCopyZ(cx, state));
2591 if (!str) {
2592 return false;
2594 RootedValue val(cx, StringValue(str));
2595 if (!JS_DefineProperty(cx, result, "incrementalState", val,
2596 JSPROP_ENUMERATE)) {
2597 return false;
2600 if (gc.state() == js::gc::State::Sweep) {
2601 val = Int32Value(gc.getCurrentSweepGroupIndex());
2602 if (!JS_DefineProperty(cx, result, "sweepGroup", val, JSPROP_ENUMERATE)) {
2603 return false;
2607 val = BooleanValue(gc.isIncrementalGCInProgress() && gc.isShrinkingGC());
2608 if (!JS_DefineProperty(cx, result, "isShrinking", val, JSPROP_ENUMERATE)) {
2609 return false;
2612 val = Int32Value(gc.gcNumber());
2613 if (!JS_DefineProperty(cx, result, "number", val, JSPROP_ENUMERATE)) {
2614 return false;
2617 val = Int32Value(gc.minorGCCount());
2618 if (!JS_DefineProperty(cx, result, "minorCount", val, JSPROP_ENUMERATE)) {
2619 return false;
2622 val = Int32Value(gc.majorGCCount());
2623 if (!JS_DefineProperty(cx, result, "majorCount", val, JSPROP_ENUMERATE)) {
2624 return false;
2627 val = BooleanValue(gc.isFullGc());
2628 if (!JS_DefineProperty(cx, result, "isFull", val, JSPROP_ENUMERATE)) {
2629 return false;
2632 val = BooleanValue(gc.isCompactingGc());
2633 if (!JS_DefineProperty(cx, result, "isCompacting", val, JSPROP_ENUMERATE)) {
2634 return false;
2637 # ifdef DEBUG
2638 val = Int32Value(gc.testMarkQueuePos());
2639 if (!JS_DefineProperty(cx, result, "queuePos", val, JSPROP_ENUMERATE)) {
2640 return false;
2642 # endif
2644 args.rval().setObject(*result);
2645 return true;
2648 static bool DeterministicGC(JSContext* cx, unsigned argc, Value* vp) {
2649 CallArgs args = CallArgsFromVp(argc, vp);
2651 if (args.length() != 1) {
2652 RootedObject callee(cx, &args.callee());
2653 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2654 return false;
2657 cx->runtime()->gc.setDeterministic(ToBoolean(args[0]));
2658 args.rval().setUndefined();
2659 return true;
2662 static bool DumpGCArenaInfo(JSContext* cx, unsigned argc, Value* vp) {
2663 CallArgs args = CallArgsFromVp(argc, vp);
2664 js::gc::DumpArenaInfo();
2665 args.rval().setUndefined();
2666 return true;
2669 static bool SetMarkStackLimit(JSContext* cx, unsigned argc, Value* vp) {
2670 CallArgs args = CallArgsFromVp(argc, vp);
2671 if (args.length() != 1) {
2672 RootedObject callee(cx, &args.callee());
2673 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2674 return false;
2677 int32_t value;
2678 if (!ToInt32(cx, args[0], &value) || value <= 0) {
2679 JS_ReportErrorASCII(cx, "Bad argument to SetMarkStackLimit");
2680 return false;
2683 if (JS::IsIncrementalGCInProgress(cx)) {
2684 JS_ReportErrorASCII(
2685 cx, "Attempt to set markStackLimit while a GC is in progress");
2686 return false;
2689 JSRuntime* runtime = cx->runtime();
2690 AutoLockGC lock(runtime);
2691 runtime->gc.setMarkStackLimit(value, lock);
2692 args.rval().setUndefined();
2693 return true;
2696 #endif /* JS_GC_ZEAL */
2698 static bool SetMallocMaxDirtyPageModifier(JSContext* cx, unsigned argc,
2699 Value* vp) {
2700 CallArgs args = CallArgsFromVp(argc, vp);
2701 if (args.length() != 1) {
2702 RootedObject callee(cx, &args.callee());
2703 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2704 return false;
2707 constexpr int32_t MinSupportedValue = -5;
2708 constexpr int32_t MaxSupportedValue = 16;
2710 int32_t value;
2711 if (!ToInt32(cx, args[0], &value)) {
2712 return false;
2714 if (value < MinSupportedValue || value > MaxSupportedValue) {
2715 JS_ReportErrorASCII(cx, "Bad argument to setMallocMaxDirtyPageModifier");
2716 return false;
2719 moz_set_max_dirty_page_modifier(value);
2721 args.rval().setUndefined();
2722 return true;
2725 static bool GCState(JSContext* cx, unsigned argc, Value* vp) {
2726 CallArgs args = CallArgsFromVp(argc, vp);
2728 if (args.length() > 1) {
2729 RootedObject callee(cx, &args.callee());
2730 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2731 return false;
2734 const char* state;
2736 if (args.length() == 1) {
2737 if (!args[0].isObject()) {
2738 RootedObject callee(cx, &args.callee());
2739 ReportUsageErrorASCII(cx, callee, "Expected object");
2740 return false;
2743 JSObject* obj = UncheckedUnwrap(&args[0].toObject());
2744 state = gc::StateName(obj->zone()->gcState());
2745 } else {
2746 state = gc::StateName(cx->runtime()->gc.state());
2749 return ReturnStringCopy(cx, args, state);
2752 static bool ScheduleZoneForGC(JSContext* cx, unsigned argc, Value* vp) {
2753 CallArgs args = CallArgsFromVp(argc, vp);
2755 if (args.length() != 1) {
2756 RootedObject callee(cx, &args.callee());
2757 ReportUsageErrorASCII(cx, callee, "Expecting a single argument");
2758 return false;
2761 if (args[0].isObject()) {
2762 // Ensure that |zone| is collected during the next GC.
2763 Zone* zone = UncheckedUnwrap(&args[0].toObject())->zone();
2764 PrepareZoneForGC(cx, zone);
2765 } else if (args[0].isString()) {
2766 // This allows us to schedule the atoms zone for GC.
2767 Zone* zone = args[0].toString()->zoneFromAnyThread();
2768 if (!CurrentThreadCanAccessZone(zone)) {
2769 RootedObject callee(cx, &args.callee());
2770 ReportUsageErrorASCII(cx, callee, "Specified zone not accessible for GC");
2771 return false;
2773 PrepareZoneForGC(cx, zone);
2774 } else {
2775 RootedObject callee(cx, &args.callee());
2776 ReportUsageErrorASCII(cx, callee,
2777 "Bad argument - expecting object or string");
2778 return false;
2781 args.rval().setUndefined();
2782 return true;
2785 static bool StartGC(JSContext* cx, unsigned argc, Value* vp) {
2786 CallArgs args = CallArgsFromVp(argc, vp);
2788 if (args.length() > 2) {
2789 RootedObject callee(cx, &args.callee());
2790 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2791 return false;
2794 auto budget = SliceBudget::unlimited();
2795 if (args.length() >= 1) {
2796 uint32_t work = 0;
2797 if (!ToUint32(cx, args[0], &work)) {
2798 return false;
2800 budget = SliceBudget(WorkBudget(work));
2803 bool shrinking = false;
2804 if (args.length() >= 2) {
2805 Value arg = args[1];
2806 if (arg.isString()) {
2807 if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking",
2808 &shrinking)) {
2809 return false;
2814 JSRuntime* rt = cx->runtime();
2815 if (rt->gc.isIncrementalGCInProgress()) {
2816 RootedObject callee(cx, &args.callee());
2817 JS_ReportErrorASCII(cx, "Incremental GC already in progress");
2818 return false;
2821 JS::GCOptions options =
2822 shrinking ? JS::GCOptions::Shrink : JS::GCOptions::Normal;
2823 rt->gc.startDebugGC(options, budget);
2825 args.rval().setUndefined();
2826 return true;
2829 static bool FinishGC(JSContext* cx, unsigned argc, Value* vp) {
2830 CallArgs args = CallArgsFromVp(argc, vp);
2832 if (args.length() > 0) {
2833 RootedObject callee(cx, &args.callee());
2834 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2835 return false;
2838 JSRuntime* rt = cx->runtime();
2839 if (rt->gc.isIncrementalGCInProgress()) {
2840 rt->gc.finishGC(JS::GCReason::DEBUG_GC);
2843 args.rval().setUndefined();
2844 return true;
2847 static bool GCSlice(JSContext* cx, unsigned argc, Value* vp) {
2848 CallArgs args = CallArgsFromVp(argc, vp);
2850 if (args.length() > 2) {
2851 RootedObject callee(cx, &args.callee());
2852 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2853 return false;
2856 auto budget = SliceBudget::unlimited();
2857 if (args.length() >= 1) {
2858 uint32_t work = 0;
2859 if (!ToUint32(cx, args[0], &work)) {
2860 return false;
2862 budget = SliceBudget(WorkBudget(work));
2865 bool dontStart = false;
2866 if (args.get(1).isObject()) {
2867 RootedObject options(cx, &args[1].toObject());
2868 RootedValue v(cx);
2869 if (!JS_GetProperty(cx, options, "dontStart", &v)) {
2870 return false;
2872 dontStart = ToBoolean(v);
2875 JSRuntime* rt = cx->runtime();
2876 if (rt->gc.isIncrementalGCInProgress()) {
2877 rt->gc.debugGCSlice(budget);
2878 } else if (!dontStart) {
2879 rt->gc.startDebugGC(JS::GCOptions::Normal, budget);
2882 args.rval().setUndefined();
2883 return true;
2886 static bool AbortGC(JSContext* cx, unsigned argc, Value* vp) {
2887 CallArgs args = CallArgsFromVp(argc, vp);
2889 if (args.length() != 0) {
2890 RootedObject callee(cx, &args.callee());
2891 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2892 return false;
2895 JS::AbortIncrementalGC(cx);
2896 args.rval().setUndefined();
2897 return true;
2900 static bool FullCompartmentChecks(JSContext* cx, unsigned argc, Value* vp) {
2901 CallArgs args = CallArgsFromVp(argc, vp);
2903 if (args.length() != 1) {
2904 RootedObject callee(cx, &args.callee());
2905 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2906 return false;
2909 cx->runtime()->gc.setFullCompartmentChecks(ToBoolean(args[0]));
2910 args.rval().setUndefined();
2911 return true;
2914 static bool NondeterministicGetWeakMapKeys(JSContext* cx, unsigned argc,
2915 Value* vp) {
2916 CallArgs args = CallArgsFromVp(argc, vp);
2918 if (args.length() != 1) {
2919 RootedObject callee(cx, &args.callee());
2920 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2921 return false;
2923 if (!args[0].isObject()) {
2924 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2925 JSMSG_NOT_EXPECTED_TYPE,
2926 "nondeterministicGetWeakMapKeys", "WeakMap",
2927 InformalValueTypeName(args[0]));
2928 return false;
2930 RootedObject arr(cx);
2931 RootedObject mapObj(cx, &args[0].toObject());
2932 if (!JS_NondeterministicGetWeakMapKeys(cx, mapObj, &arr)) {
2933 return false;
2935 if (!arr) {
2936 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2937 JSMSG_NOT_EXPECTED_TYPE,
2938 "nondeterministicGetWeakMapKeys", "WeakMap",
2939 args[0].toObject().getClass()->name);
2940 return false;
2942 args.rval().setObject(*arr);
2943 return true;
2946 class HasChildTracer final : public JS::CallbackTracer {
2947 RootedValue child_;
2948 bool found_;
2950 void onChild(JS::GCCellPtr thing, const char* name) override {
2951 if (thing.asCell() == child_.toGCThing()) {
2952 found_ = true;
2956 public:
2957 HasChildTracer(JSContext* cx, HandleValue child)
2958 : JS::CallbackTracer(cx, JS::TracerKind::Callback,
2959 JS::WeakMapTraceAction::TraceKeysAndValues),
2960 child_(cx, child),
2961 found_(false) {}
2963 bool found() const { return found_; }
2966 static bool HasChild(JSContext* cx, unsigned argc, Value* vp) {
2967 CallArgs args = CallArgsFromVp(argc, vp);
2968 RootedValue parent(cx, args.get(0));
2969 RootedValue child(cx, args.get(1));
2971 if (!parent.isGCThing() || !child.isGCThing()) {
2972 args.rval().setBoolean(false);
2973 return true;
2976 HasChildTracer trc(cx, child);
2977 TraceChildren(&trc, JS::GCCellPtr(parent.toGCThing(), parent.traceKind()));
2978 args.rval().setBoolean(trc.found());
2979 return true;
2982 static bool SetSavedStacksRNGState(JSContext* cx, unsigned argc, Value* vp) {
2983 CallArgs args = CallArgsFromVp(argc, vp);
2984 if (!args.requireAtLeast(cx, "setSavedStacksRNGState", 1)) {
2985 return false;
2988 int32_t seed;
2989 if (!ToInt32(cx, args[0], &seed)) {
2990 return false;
2993 // Either one or the other of the seed arguments must be non-zero;
2994 // make this true no matter what value 'seed' has.
2995 cx->realm()->savedStacks().setRNGState(seed, (seed + 1) * 33);
2996 return true;
2999 static bool GetSavedFrameCount(JSContext* cx, unsigned argc, Value* vp) {
3000 CallArgs args = CallArgsFromVp(argc, vp);
3001 args.rval().setNumber(cx->realm()->savedStacks().count());
3002 return true;
3005 static bool ClearSavedFrames(JSContext* cx, unsigned argc, Value* vp) {
3006 CallArgs args = CallArgsFromVp(argc, vp);
3008 js::SavedStacks& savedStacks = cx->realm()->savedStacks();
3009 savedStacks.clear();
3011 for (ActivationIterator iter(cx); !iter.done(); ++iter) {
3012 iter->clearLiveSavedFrameCache();
3015 args.rval().setUndefined();
3016 return true;
3019 static bool SaveStack(JSContext* cx, unsigned argc, Value* vp) {
3020 CallArgs args = CallArgsFromVp(argc, vp);
3022 JS::StackCapture capture((JS::AllFrames()));
3023 if (args.length() >= 1) {
3024 double maxDouble;
3025 if (!ToNumber(cx, args[0], &maxDouble)) {
3026 return false;
3028 if (std::isnan(maxDouble) || maxDouble < 0 || maxDouble > UINT32_MAX) {
3029 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
3030 nullptr, "not a valid maximum frame count");
3031 return false;
3033 uint32_t max = uint32_t(maxDouble);
3034 if (max > 0) {
3035 capture = JS::StackCapture(JS::MaxFrames(max));
3039 RootedObject compartmentObject(cx);
3040 if (args.length() >= 2) {
3041 if (!args[1].isObject()) {
3042 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
3043 nullptr, "not an object");
3044 return false;
3046 compartmentObject = UncheckedUnwrap(&args[1].toObject());
3047 if (!compartmentObject) {
3048 return false;
3052 RootedObject stack(cx);
3054 Maybe<AutoRealm> ar;
3055 if (compartmentObject) {
3056 ar.emplace(cx, compartmentObject);
3058 if (!JS::CaptureCurrentStack(cx, &stack, std::move(capture))) {
3059 return false;
3063 if (stack && !cx->compartment()->wrap(cx, &stack)) {
3064 return false;
3067 args.rval().setObjectOrNull(stack);
3068 return true;
3071 static bool CaptureFirstSubsumedFrame(JSContext* cx, unsigned argc,
3072 JS::Value* vp) {
3073 CallArgs args = CallArgsFromVp(argc, vp);
3074 if (!args.requireAtLeast(cx, "captureFirstSubsumedFrame", 1)) {
3075 return false;
3078 if (!args[0].isObject()) {
3079 JS_ReportErrorASCII(cx, "The argument must be an object");
3080 return false;
3083 RootedObject obj(cx, &args[0].toObject());
3084 obj = CheckedUnwrapStatic(obj);
3085 if (!obj) {
3086 JS_ReportErrorASCII(cx, "Denied permission to object.");
3087 return false;
3090 JS::StackCapture capture(
3091 JS::FirstSubsumedFrame(cx, obj->nonCCWRealm()->principals()));
3092 if (args.length() > 1) {
3093 capture.as<JS::FirstSubsumedFrame>().ignoreSelfHosted =
3094 JS::ToBoolean(args[1]);
3097 JS::RootedObject capturedStack(cx);
3098 if (!JS::CaptureCurrentStack(cx, &capturedStack, std::move(capture))) {
3099 return false;
3102 args.rval().setObjectOrNull(capturedStack);
3103 return true;
3106 static bool CallFunctionFromNativeFrame(JSContext* cx, unsigned argc,
3107 Value* vp) {
3108 CallArgs args = CallArgsFromVp(argc, vp);
3110 if (args.length() != 1) {
3111 JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
3112 return false;
3114 if (!args[0].isObject() || !IsCallable(args[0])) {
3115 JS_ReportErrorASCII(cx, "The first argument should be a function.");
3116 return false;
3119 RootedObject function(cx, &args[0].toObject());
3120 return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(),
3121 args.rval());
3124 static bool CallFunctionWithAsyncStack(JSContext* cx, unsigned argc,
3125 Value* vp) {
3126 CallArgs args = CallArgsFromVp(argc, vp);
3128 if (args.length() != 3) {
3129 JS_ReportErrorASCII(cx, "The function takes exactly three arguments.");
3130 return false;
3132 if (!args[0].isObject() || !IsCallable(args[0])) {
3133 JS_ReportErrorASCII(cx, "The first argument should be a function.");
3134 return false;
3136 if (!args[1].isObject() || !args[1].toObject().is<SavedFrame>()) {
3137 JS_ReportErrorASCII(cx, "The second argument should be a SavedFrame.");
3138 return false;
3140 if (!args[2].isString() || args[2].toString()->empty()) {
3141 JS_ReportErrorASCII(cx, "The third argument should be a non-empty string.");
3142 return false;
3145 RootedObject function(cx, &args[0].toObject());
3146 RootedObject stack(cx, &args[1].toObject());
3147 RootedString asyncCause(cx, args[2].toString());
3148 UniqueChars utf8Cause = JS_EncodeStringToUTF8(cx, asyncCause);
3149 if (!utf8Cause) {
3150 MOZ_ASSERT(cx->isExceptionPending());
3151 return false;
3154 JS::AutoSetAsyncStackForNewCalls sas(
3155 cx, stack, utf8Cause.get(),
3156 JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
3157 return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(),
3158 args.rval());
3161 static bool EnableTrackAllocations(JSContext* cx, unsigned argc, Value* vp) {
3162 SetAllocationMetadataBuilder(cx, &SavedStacks::metadataBuilder);
3163 return true;
3166 static bool DisableTrackAllocations(JSContext* cx, unsigned argc, Value* vp) {
3167 SetAllocationMetadataBuilder(cx, nullptr);
3168 return true;
3171 static bool SetTestFilenameValidationCallback(JSContext* cx, unsigned argc,
3172 Value* vp) {
3173 CallArgs args = CallArgsFromVp(argc, vp);
3175 // Accept all filenames that start with "safe". In system code also accept
3176 // filenames starting with "system".
3177 auto testCb = [](JSContext* cx, const char* filename) -> bool {
3178 if (strstr(filename, "safe") == filename) {
3179 return true;
3181 if (cx->realm()->isSystem() && strstr(filename, "system") == filename) {
3182 return true;
3185 return false;
3187 JS::SetFilenameValidationCallback(testCb);
3189 args.rval().setUndefined();
3190 return true;
3193 static JSAtom* GetPropertiesAddedName(JSContext* cx) {
3194 const char* propName = "_propertiesAdded";
3195 return Atomize(cx, propName, strlen(propName));
3198 static bool NewObjectWithAddPropertyHook(JSContext* cx, unsigned argc,
3199 Value* vp) {
3200 CallArgs args = CallArgsFromVp(argc, vp);
3202 auto addPropHook = [](JSContext* cx, HandleObject obj, HandleId id,
3203 HandleValue v) -> bool {
3204 Rooted<JSAtom*> propName(cx, GetPropertiesAddedName(cx));
3205 if (!propName) {
3206 return false;
3208 // Don't do anything if we're adding the _propertiesAdded property.
3209 RootedId propId(cx, AtomToId(propName));
3210 if (id == propId) {
3211 return true;
3213 // Increment _propertiesAdded.
3214 RootedValue val(cx);
3215 if (!JS_GetPropertyById(cx, obj, propId, &val)) {
3216 return false;
3218 if (!val.isInt32() || val.toInt32() == INT32_MAX) {
3219 return true;
3221 val.setInt32(val.toInt32() + 1);
3222 return JS_DefinePropertyById(cx, obj, propId, val, 0);
3225 static const JSClassOps classOps = {
3226 addPropHook, // addProperty
3227 nullptr, // delProperty
3228 nullptr, // enumerate
3229 nullptr, // newEnumerate
3230 nullptr, // resolve
3231 nullptr, // mayResolve
3232 nullptr, // finalize
3233 nullptr, // call
3234 nullptr, // construct
3235 nullptr, // trace
3237 static const JSClass cls = {
3238 "ObjectWithAddPropHook",
3240 &classOps,
3243 RootedObject obj(cx, JS_NewObject(cx, &cls));
3244 if (!obj) {
3245 return false;
3248 // Initialize _propertiesAdded to 0.
3249 Rooted<JSAtom*> propName(cx, GetPropertiesAddedName(cx));
3250 if (!propName) {
3251 return false;
3253 RootedId propId(cx, AtomToId(propName));
3254 RootedValue val(cx, Int32Value(0));
3255 if (!JS_DefinePropertyById(cx, obj, propId, val, 0)) {
3256 return false;
3259 args.rval().setObject(*obj);
3260 return true;
3263 static bool NewObjectWithCallHook(JSContext* cx, unsigned argc, Value* vp) {
3264 CallArgs args = CallArgsFromVp(argc, vp);
3266 static auto hookShared = [](JSContext* cx, CallArgs& args) {
3267 Rooted<PlainObject*> obj(cx, NewPlainObject(cx));
3268 if (!obj) {
3269 return false;
3272 // Define |this|. We can't expose the MagicValue to JS, so we use
3273 // "<is_constructing>" in that case.
3274 Rooted<Value> thisv(cx, args.thisv());
3275 if (thisv.isMagic(JS_IS_CONSTRUCTING)) {
3276 JSString* str = NewStringCopyZ<CanGC>(cx, "<is_constructing>");
3277 if (!str) {
3278 return false;
3280 thisv.setString(str);
3282 if (!DefineDataProperty(cx, obj, cx->names().this_, thisv,
3283 JSPROP_ENUMERATE)) {
3284 return false;
3287 // Define |callee|.
3288 if (!DefineDataProperty(cx, obj, cx->names().callee, args.calleev(),
3289 JSPROP_ENUMERATE)) {
3290 return false;
3293 // Define |arguments| array.
3294 Rooted<ArrayObject*> arr(
3295 cx, NewDenseCopiedArray(cx, args.length(), args.array()));
3296 if (!arr) {
3297 return false;
3299 Rooted<Value> arrVal(cx, ObjectValue(*arr));
3300 if (!DefineDataProperty(cx, obj, cx->names().arguments, arrVal,
3301 JSPROP_ENUMERATE)) {
3302 return false;
3305 // Define |newTarget| if constructing.
3306 if (args.isConstructing()) {
3307 const char* propName = "newTarget";
3308 Rooted<JSAtom*> name(cx, Atomize(cx, propName, strlen(propName)));
3309 if (!name) {
3310 return false;
3312 Rooted<PropertyKey> key(cx, NameToId(name->asPropertyName()));
3313 if (!DefineDataProperty(cx, obj, key, args.newTarget(),
3314 JSPROP_ENUMERATE)) {
3315 return false;
3319 args.rval().setObject(*obj);
3320 return true;
3323 static auto callHook = [](JSContext* cx, unsigned argc, Value* vp) {
3324 CallArgs args = CallArgsFromVp(argc, vp);
3325 MOZ_ASSERT(!args.isConstructing());
3326 return hookShared(cx, args);
3328 static auto constructHook = [](JSContext* cx, unsigned argc, Value* vp) {
3329 CallArgs args = CallArgsFromVp(argc, vp);
3330 MOZ_ASSERT(args.isConstructing());
3331 return hookShared(cx, args);
3334 static const JSClassOps classOps = {
3335 nullptr, // addProperty
3336 nullptr, // delProperty
3337 nullptr, // enumerate
3338 nullptr, // newEnumerate
3339 nullptr, // resolve
3340 nullptr, // mayResolve
3341 nullptr, // finalize
3342 callHook, // call
3343 constructHook, // construct
3344 nullptr, // trace
3346 static const JSClass cls = {
3347 "ObjectWithCallHook",
3349 &classOps,
3352 Rooted<JSObject*> obj(cx, JS_NewObject(cx, &cls));
3353 if (!obj) {
3354 return false;
3357 args.rval().setObject(*obj);
3358 return true;
3361 static constexpr JSClass ObjectWithManyReservedSlotsClass = {
3362 "ObjectWithManyReservedSlots", JSCLASS_HAS_RESERVED_SLOTS(40)};
3364 static bool NewObjectWithManyReservedSlots(JSContext* cx, unsigned argc,
3365 Value* vp) {
3366 CallArgs args = CallArgsFromVp(argc, vp);
3368 static constexpr size_t NumReservedSlots =
3369 JSCLASS_RESERVED_SLOTS(&ObjectWithManyReservedSlotsClass);
3370 static_assert(NumReservedSlots > NativeObject::MAX_FIXED_SLOTS);
3372 RootedObject obj(cx, JS_NewObject(cx, &ObjectWithManyReservedSlotsClass));
3373 if (!obj) {
3374 return false;
3377 for (size_t i = 0; i < NumReservedSlots; i++) {
3378 JS_SetReservedSlot(obj, i, Int32Value(i));
3381 args.rval().setObject(*obj);
3382 return true;
3385 static bool CheckObjectWithManyReservedSlots(JSContext* cx, unsigned argc,
3386 Value* vp) {
3387 CallArgs args = CallArgsFromVp(argc, vp);
3389 if (args.length() != 1 || !args[0].isObject() ||
3390 args[0].toObject().getClass() != &ObjectWithManyReservedSlotsClass) {
3391 JS_ReportErrorASCII(cx,
3392 "Expected object from newObjectWithManyReservedSlots");
3393 return false;
3396 JSObject* obj = &args[0].toObject();
3398 static constexpr size_t NumReservedSlots =
3399 JSCLASS_RESERVED_SLOTS(&ObjectWithManyReservedSlotsClass);
3401 for (size_t i = 0; i < NumReservedSlots; i++) {
3402 MOZ_RELEASE_ASSERT(JS::GetReservedSlot(obj, i).toInt32() == int32_t(i));
3405 args.rval().setUndefined();
3406 return true;
3409 static bool GetWatchtowerLog(JSContext* cx, unsigned argc, Value* vp) {
3410 CallArgs args = CallArgsFromVp(argc, vp);
3412 Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
3414 if (auto* log = cx->runtime()->watchtowerTestingLog.ref().get()) {
3415 Rooted<JSObject*> elem(cx);
3416 for (PlainObject* obj : *log) {
3417 elem = obj;
3418 if (!cx->compartment()->wrap(cx, &elem)) {
3419 return false;
3421 if (!values.append(ObjectValue(*elem))) {
3422 return false;
3425 log->clearAndFree();
3428 ArrayObject* arr = NewDenseCopiedArray(cx, values.length(), values.begin());
3429 if (!arr) {
3430 return false;
3433 args.rval().setObject(*arr);
3434 return true;
3437 static bool AddWatchtowerTarget(JSContext* cx, unsigned argc, Value* vp) {
3438 CallArgs args = CallArgsFromVp(argc, vp);
3440 if (args.length() != 1 || !args[0].isObject()) {
3441 JS_ReportErrorASCII(cx, "Expected a single object argument.");
3442 return false;
3445 if (!cx->runtime()->watchtowerTestingLog.ref()) {
3446 auto vec = cx->make_unique<JSRuntime::RootedPlainObjVec>(cx);
3447 if (!vec) {
3448 return false;
3450 cx->runtime()->watchtowerTestingLog = std::move(vec);
3453 RootedObject obj(cx, &args[0].toObject());
3454 if (!JSObject::setUseWatchtowerTestingLog(cx, obj)) {
3455 return false;
3458 args.rval().setUndefined();
3459 return true;
3462 struct TestExternalString : public JSExternalStringCallbacks {
3463 void finalize(JS::Latin1Char* chars) const override { js_free(chars); }
3464 void finalize(char16_t* chars) const override { js_free(chars); }
3465 size_t sizeOfBuffer(const JS::Latin1Char* chars,
3466 mozilla::MallocSizeOf mallocSizeOf) const override {
3467 return mallocSizeOf(chars);
3469 size_t sizeOfBuffer(const char16_t* chars,
3470 mozilla::MallocSizeOf mallocSizeOf) const override {
3471 return mallocSizeOf(chars);
3475 static constexpr TestExternalString TestExternalStringCallbacks;
3477 static bool NewString(JSContext* cx, unsigned argc, Value* vp) {
3478 CallArgs args = CallArgsFromVp(argc, vp);
3480 RootedString src(cx, ToString(cx, args.get(0)));
3481 if (!src) {
3482 return false;
3485 gc::Heap heap = gc::Heap::Default;
3486 bool wantTwoByte = false;
3487 bool forceExternal = false;
3488 bool maybeExternal = false;
3489 uint32_t capacity = 0;
3491 if (args.get(1).isObject()) {
3492 RootedObject options(cx, &args[1].toObject());
3493 RootedValue v(cx);
3494 bool requestTenured = false;
3495 struct BoolSetting {
3496 const char* name;
3497 bool* value;
3499 for (auto [name, setting] :
3500 {BoolSetting{"tenured", &requestTenured},
3501 BoolSetting{"twoByte", &wantTwoByte},
3502 BoolSetting{"external", &forceExternal},
3503 BoolSetting{"maybeExternal", &maybeExternal}}) {
3504 if (!JS_GetProperty(cx, options, name, &v)) {
3505 return false;
3507 *setting = ToBoolean(v); // false if not given (or otherwise undefined)
3509 struct Uint32Setting {
3510 const char* name;
3511 uint32_t* value;
3513 for (auto [name, setting] : {Uint32Setting{"capacity", &capacity}}) {
3514 if (!JS_GetProperty(cx, options, name, &v)) {
3515 return false;
3517 int32_t i32;
3518 if (!ToInt32(cx, v, &i32)) {
3519 return false;
3521 if (i32 < 0) {
3522 JS_ReportErrorASCII(cx, "nonnegative value required");
3523 return false;
3525 *setting = static_cast<uint32_t>(i32);
3528 heap = requestTenured ? gc::Heap::Tenured : gc::Heap::Default;
3529 if (forceExternal || maybeExternal) {
3530 wantTwoByte = true;
3531 if (capacity != 0) {
3532 JS_ReportErrorASCII(cx,
3533 "strings cannot be both external and extensible");
3534 return false;
3539 auto len = src->length();
3540 RootedString dest(cx);
3542 if (forceExternal || maybeExternal) {
3543 auto buf = cx->make_pod_array<char16_t>(len);
3544 if (!buf) {
3545 return false;
3548 if (!JS_CopyStringChars(cx, mozilla::Range<char16_t>(buf.get(), len),
3549 src)) {
3550 return false;
3553 bool isExternal = true;
3554 if (forceExternal) {
3555 dest = JSExternalString::new_(cx, buf.get(), len,
3556 &TestExternalStringCallbacks);
3557 } else {
3558 dest = NewMaybeExternalString(
3559 cx, buf.get(), len, &TestExternalStringCallbacks, &isExternal, heap);
3561 if (dest && isExternal) {
3562 (void)buf.release(); // Ownership was transferred.
3564 } else {
3565 AutoStableStringChars stable(cx);
3566 if (!wantTwoByte && src->hasLatin1Chars()) {
3567 if (!stable.init(cx, src)) {
3568 return false;
3570 } else {
3571 if (!stable.initTwoByte(cx, src)) {
3572 return false;
3575 if (capacity) {
3576 if (capacity < len) {
3577 capacity = len;
3579 if (len == 0) {
3580 JS_ReportErrorASCII(cx, "Cannot set capacity of empty string");
3581 return false;
3584 auto createLinearString = [&](const auto* chars) -> JSLinearString* {
3585 using CharT =
3586 std::remove_const_t<std::remove_pointer_t<decltype(chars)>>;
3588 if (JSInlineString::lengthFits<CharT>(len)) {
3589 JS_ReportErrorASCII(cx, "Cannot create small non-inline strings");
3590 return nullptr;
3593 auto news =
3594 cx->make_pod_arena_array<CharT>(js::StringBufferArena, capacity);
3595 if (!news) {
3596 return nullptr;
3598 mozilla::PodCopy(news.get(), chars, len);
3599 Rooted<JSString::OwnedChars<CharT>> owned(cx, std::move(news), len,
3600 true);
3601 return JSLinearString::newValidLength<CanGC, CharT>(cx, &owned, heap);
3604 if (stable.isLatin1()) {
3605 dest = createLinearString(stable.latin1Chars());
3606 } else {
3607 dest = createLinearString(stable.twoByteChars());
3609 if (dest) {
3610 dest->asLinear().makeExtensible(capacity);
3612 } else if (wantTwoByte) {
3613 dest = NewStringCopyNDontDeflate<CanGC>(cx, stable.twoByteChars(), len,
3614 heap);
3615 } else if (stable.isLatin1()) {
3616 dest = NewStringCopyN<CanGC>(cx, stable.latin1Chars(), len, heap);
3617 } else {
3618 // Normal behavior: auto-deflate to latin1 if possible.
3619 dest = NewStringCopyN<CanGC>(cx, stable.twoByteChars(), len, heap);
3623 if (!dest) {
3624 return false;
3627 args.rval().setString(dest);
3628 return true;
3631 // Warning! This will let you create ropes that I'm not sure would be possible
3632 // otherwise, specifically:
3634 // - a rope with a zero-length child
3635 // - a rope that would fit into an inline string
3637 static bool NewRope(JSContext* cx, unsigned argc, Value* vp) {
3638 CallArgs args = CallArgsFromVp(argc, vp);
3640 if (!args.get(0).isString() || !args.get(1).isString()) {
3641 JS_ReportErrorASCII(cx, "newRope requires two string arguments.");
3642 return false;
3645 gc::Heap heap = js::gc::Heap::Default;
3646 if (args.get(2).isObject()) {
3647 RootedObject options(cx, &args[2].toObject());
3648 RootedValue v(cx);
3649 if (!JS_GetProperty(cx, options, "nursery", &v)) {
3650 return false;
3652 if (!v.isUndefined() && !ToBoolean(v)) {
3653 heap = js::gc::Heap::Tenured;
3657 RootedString left(cx, args[0].toString());
3658 RootedString right(cx, args[1].toString());
3659 size_t length = JS_GetStringLength(left) + JS_GetStringLength(right);
3660 if (length > JSString::MAX_LENGTH) {
3661 JS_ReportErrorASCII(cx, "rope length exceeds maximum string length");
3662 return false;
3665 // Disallow creating ropes where one side is empty.
3666 if (left->empty() || right->empty()) {
3667 JS_ReportErrorASCII(cx, "rope child mustn't be the empty string");
3668 return false;
3671 // Disallow creating ropes which fit into inline strings.
3672 if (left->hasLatin1Chars() && right->hasLatin1Chars()) {
3673 if (JSInlineString::lengthFits<JS::Latin1Char>(length)) {
3674 JS_ReportErrorASCII(cx, "Cannot create small non-inline ropes");
3675 return false;
3677 } else {
3678 if (JSInlineString::lengthFits<char16_t>(length)) {
3679 JS_ReportErrorASCII(cx, "Cannot create small non-inline ropes");
3680 return false;
3684 auto* str = JSRope::new_<CanGC>(cx, left, right, length, heap);
3685 if (!str) {
3686 return false;
3689 args.rval().setString(str);
3690 return true;
3693 static bool IsRope(JSContext* cx, unsigned argc, Value* vp) {
3694 CallArgs args = CallArgsFromVp(argc, vp);
3696 if (!args.get(0).isString()) {
3697 JS_ReportErrorASCII(cx, "isRope requires a string argument.");
3698 return false;
3701 JSString* str = args[0].toString();
3702 args.rval().setBoolean(str->isRope());
3703 return true;
3706 static bool EnsureLinearString(JSContext* cx, unsigned argc, Value* vp) {
3707 CallArgs args = CallArgsFromVp(argc, vp);
3709 if (args.length() != 1 || !args[0].isString()) {
3710 JS_ReportErrorASCII(
3711 cx, "ensureLinearString takes exactly one string argument.");
3712 return false;
3715 JSLinearString* linear = args[0].toString()->ensureLinear(cx);
3716 if (!linear) {
3717 return false;
3720 args.rval().setString(linear);
3721 return true;
3724 static bool RepresentativeStringArray(JSContext* cx, unsigned argc, Value* vp) {
3725 CallArgs args = CallArgsFromVp(argc, vp);
3727 RootedObject array(cx, JS::NewArrayObject(cx, 0));
3728 if (!array) {
3729 return false;
3732 if (!JSString::fillWithRepresentatives(cx, array.as<ArrayObject>())) {
3733 return false;
3736 args.rval().setObject(*array);
3737 return true;
3740 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
3742 static bool OOMThreadTypes(JSContext* cx, unsigned argc, Value* vp) {
3743 CallArgs args = CallArgsFromVp(argc, vp);
3744 args.rval().setInt32(js::THREAD_TYPE_MAX);
3745 return true;
3748 static bool CheckCanSimulateOOM(JSContext* cx) {
3749 if (js::oom::GetThreadType() != js::THREAD_TYPE_MAIN) {
3750 JS_ReportErrorASCII(
3751 cx, "Simulated OOM failure is only supported on the main thread");
3752 return false;
3755 return true;
3758 static bool SetupOOMFailure(JSContext* cx, bool failAlways, unsigned argc,
3759 Value* vp) {
3760 CallArgs args = CallArgsFromVp(argc, vp);
3762 if (disableOOMFunctions) {
3763 args.rval().setUndefined();
3764 return true;
3767 if (args.length() < 1) {
3768 JS_ReportErrorASCII(cx, "Count argument required");
3769 return false;
3772 if (args.length() > 2) {
3773 JS_ReportErrorASCII(cx, "Too many arguments");
3774 return false;
3777 int32_t count;
3778 if (!JS::ToInt32(cx, args.get(0), &count)) {
3779 return false;
3782 if (count <= 0) {
3783 JS_ReportErrorASCII(cx, "OOM cutoff should be positive");
3784 return false;
3787 uint32_t targetThread = js::THREAD_TYPE_MAIN;
3788 if (args.length() > 1 && !ToUint32(cx, args[1], &targetThread)) {
3789 return false;
3792 if (targetThread == js::THREAD_TYPE_NONE ||
3793 targetThread == js::THREAD_TYPE_WORKER ||
3794 targetThread >= js::THREAD_TYPE_MAX) {
3795 JS_ReportErrorASCII(cx, "Invalid thread type specified");
3796 return false;
3799 if (!CheckCanSimulateOOM(cx)) {
3800 return false;
3803 js::oom::simulator.simulateFailureAfter(js::oom::FailureSimulator::Kind::OOM,
3804 count, targetThread, failAlways);
3805 args.rval().setUndefined();
3806 return true;
3809 static bool OOMAfterAllocations(JSContext* cx, unsigned argc, Value* vp) {
3810 return SetupOOMFailure(cx, true, argc, vp);
3813 static bool OOMAtAllocation(JSContext* cx, unsigned argc, Value* vp) {
3814 return SetupOOMFailure(cx, false, argc, vp);
3817 static bool ResetOOMFailure(JSContext* cx, unsigned argc, Value* vp) {
3818 CallArgs args = CallArgsFromVp(argc, vp);
3820 if (!CheckCanSimulateOOM(cx)) {
3821 return false;
3824 args.rval().setBoolean(js::oom::HadSimulatedOOM());
3825 js::oom::simulator.reset();
3826 return true;
3829 static size_t CountCompartments(JSContext* cx) {
3830 size_t count = 0;
3831 for (auto zone : cx->runtime()->gc.zones()) {
3832 count += zone->compartments().length();
3834 return count;
3837 // Iterative failure testing: test a function by simulating failures at indexed
3838 // locations throughout the normal execution path and checking that the
3839 // resulting state of the environment is consistent with the error result.
3841 // For example, trigger OOM at every allocation point and test that the function
3842 // either recovers and succeeds or raises an exception and fails.
3844 class MOZ_STACK_CLASS IterativeFailureTest {
3845 public:
3846 struct FailureSimulator {
3847 virtual void setup(JSContext* cx) {}
3848 virtual void teardown(JSContext* cx) {}
3849 virtual void startSimulating(JSContext* cx, unsigned iteration,
3850 unsigned thread, bool keepFailing) = 0;
3851 virtual bool stopSimulating() = 0;
3852 virtual void cleanup(JSContext* cx) {}
3855 IterativeFailureTest(JSContext* cx, FailureSimulator& simulator);
3856 bool initParams(const CallArgs& args);
3857 bool test();
3859 private:
3860 bool setup();
3861 bool testThread(unsigned thread);
3862 bool testIteration(unsigned thread, unsigned iteration,
3863 bool& failureWasSimulated, MutableHandleValue exception);
3864 void cleanup();
3865 void teardown();
3867 JSContext* const cx;
3868 FailureSimulator& simulator;
3869 size_t compartmentCount;
3871 // Test parameters set by initParams.
3872 RootedFunction testFunction;
3873 unsigned threadStart = 0;
3874 unsigned threadEnd = 0;
3875 bool expectExceptionOnFailure = true;
3876 bool keepFailing = false;
3877 bool verbose = false;
3880 bool RunIterativeFailureTest(
3881 JSContext* cx, const CallArgs& args,
3882 IterativeFailureTest::FailureSimulator& simulator) {
3883 IterativeFailureTest test(cx, simulator);
3884 return test.initParams(args) && test.test();
3887 IterativeFailureTest::IterativeFailureTest(JSContext* cx,
3888 FailureSimulator& simulator)
3889 : cx(cx), simulator(simulator), testFunction(cx) {}
3891 bool IterativeFailureTest::test() {
3892 if (disableOOMFunctions) {
3893 return true;
3896 if (!setup()) {
3897 return false;
3900 auto onExit = mozilla::MakeScopeExit([this] { teardown(); });
3902 for (unsigned thread = threadStart; thread <= threadEnd; thread++) {
3903 if (!testThread(thread)) {
3904 return false;
3908 return true;
3911 bool IterativeFailureTest::setup() {
3912 if (!CheckCanSimulateOOM(cx)) {
3913 return false;
3916 // Disallow nested tests.
3917 if (cx->runningOOMTest) {
3918 JS_ReportErrorASCII(
3919 cx, "Nested call to iterative failure test is not allowed.");
3920 return false;
3922 cx->runningOOMTest = true;
3924 MOZ_ASSERT(!cx->isExceptionPending());
3926 # ifdef JS_GC_ZEAL
3927 JS_SetGCZeal(cx, 0, JS_DEFAULT_ZEAL_FREQ);
3928 # endif
3930 // Delazify the function here if necessary so we don't end up testing that.
3931 if (testFunction->isInterpreted() &&
3932 !JSFunction::getOrCreateScript(cx, testFunction)) {
3933 return false;
3936 compartmentCount = CountCompartments(cx);
3938 simulator.setup(cx);
3940 return true;
3943 bool IterativeFailureTest::testThread(unsigned thread) {
3944 if (verbose) {
3945 fprintf(stderr, "thread %u\n", thread);
3948 RootedValue exception(cx);
3950 unsigned iteration = 1;
3951 bool failureWasSimulated;
3952 do {
3953 if (!testIteration(thread, iteration, failureWasSimulated, &exception)) {
3954 return false;
3957 iteration++;
3958 } while (failureWasSimulated);
3960 if (verbose) {
3961 fprintf(stderr, " finished after %u iterations\n", iteration - 1);
3962 if (!exception.isUndefined()) {
3963 RootedString str(cx, JS::ToString(cx, exception));
3964 if (!str) {
3965 fprintf(stderr, " error while trying to print exception, giving up\n");
3966 return false;
3968 UniqueChars bytes(JS_EncodeStringToUTF8(cx, str));
3969 if (!bytes) {
3970 return false;
3972 fprintf(stderr, " threw %s\n", bytes.get());
3976 return true;
3979 bool IterativeFailureTest::testIteration(unsigned thread, unsigned iteration,
3980 bool& failureWasSimulated,
3981 MutableHandleValue exception) {
3982 if (verbose) {
3983 fprintf(stderr, " iteration %u\n", iteration);
3986 MOZ_RELEASE_ASSERT(!cx->isExceptionPending());
3988 simulator.startSimulating(cx, iteration, thread, keepFailing);
3990 RootedValue result(cx);
3991 bool ok = JS_CallFunction(cx, cx->global(), testFunction,
3992 HandleValueArray::empty(), &result);
3994 failureWasSimulated = simulator.stopSimulating();
3996 if (ok && cx->isExceptionPending()) {
3997 MOZ_CRASH(
3998 "Thunk execution succeeded but an exception was raised - missing error "
3999 "check?");
4002 if (!ok && !cx->isExceptionPending() && expectExceptionOnFailure) {
4003 MOZ_CRASH(
4004 "Thunk execution failed but no exception was raised - missing call to "
4005 "js::ReportOutOfMemory()?");
4008 // Note that it is possible that the function throws an exception unconnected
4009 // to the simulated failure, in which case we ignore it. More correct would be
4010 // to have the caller pass some kind of exception specification and to check
4011 // the exception against it.
4012 if (!failureWasSimulated && cx->isExceptionPending()) {
4013 if (!cx->getPendingException(exception)) {
4014 return false;
4017 cx->clearPendingException();
4019 cleanup();
4021 return true;
4024 void IterativeFailureTest::cleanup() {
4025 simulator.cleanup(cx);
4027 gc::FinishGC(cx);
4029 // Some tests create a new compartment or zone on every iteration. Our GC is
4030 // triggered by GC allocations and not by number of compartments or zones, so
4031 // these won't normally get cleaned up. The check here stops some tests
4032 // running out of memory. ("Gentlemen, you can't fight in here! This is the
4033 // War oom!")
4034 if (CountCompartments(cx) > compartmentCount + 100) {
4035 JS_GC(cx);
4036 compartmentCount = CountCompartments(cx);
4040 void IterativeFailureTest::teardown() {
4041 simulator.teardown(cx);
4043 cx->runningOOMTest = false;
4046 bool IterativeFailureTest::initParams(const CallArgs& args) {
4047 if (args.length() < 1 || args.length() > 2) {
4048 JS_ReportErrorASCII(cx, "function takes between 1 and 2 arguments.");
4049 return false;
4052 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
4053 JS_ReportErrorASCII(cx, "The first argument must be the function to test.");
4054 return false;
4056 testFunction = &args[0].toObject().as<JSFunction>();
4058 if (args.length() == 2) {
4059 if (args[1].isBoolean()) {
4060 expectExceptionOnFailure = args[1].toBoolean();
4061 } else if (args[1].isObject()) {
4062 RootedObject options(cx, &args[1].toObject());
4063 RootedValue value(cx);
4065 if (!JS_GetProperty(cx, options, "expectExceptionOnFailure", &value)) {
4066 return false;
4068 if (!value.isUndefined()) {
4069 expectExceptionOnFailure = ToBoolean(value);
4072 if (!JS_GetProperty(cx, options, "keepFailing", &value)) {
4073 return false;
4075 if (!value.isUndefined()) {
4076 keepFailing = ToBoolean(value);
4078 } else {
4079 JS_ReportErrorASCII(
4080 cx, "The optional second argument must be an object or a boolean.");
4081 return false;
4085 // There are some places where we do fail without raising an exception, so
4086 // we can't expose this to the fuzzers by default.
4087 if (fuzzingSafe) {
4088 expectExceptionOnFailure = false;
4091 // Test all threads by default except worker threads.
4092 threadStart = oom::FirstThreadTypeToTest;
4093 threadEnd = oom::LastThreadTypeToTest;
4095 // Test a single thread type if specified by the OOM_THREAD environment
4096 // variable.
4097 int threadOption = 0;
4098 if (EnvVarAsInt("OOM_THREAD", &threadOption)) {
4099 if (threadOption < oom::FirstThreadTypeToTest ||
4100 threadOption > oom::LastThreadTypeToTest) {
4101 JS_ReportErrorASCII(cx, "OOM_THREAD value out of range.");
4102 return false;
4105 threadStart = threadOption;
4106 threadEnd = threadOption;
4109 verbose = EnvVarIsDefined("OOM_VERBOSE");
4111 return true;
4114 struct OOMSimulator : public IterativeFailureTest::FailureSimulator {
4115 void setup(JSContext* cx) override { cx->runtime()->hadOutOfMemory = false; }
4117 void startSimulating(JSContext* cx, unsigned i, unsigned thread,
4118 bool keepFailing) override {
4119 MOZ_ASSERT(!cx->runtime()->hadOutOfMemory);
4120 js::oom::simulator.simulateFailureAfter(
4121 js::oom::FailureSimulator::Kind::OOM, i, thread, keepFailing);
4124 bool stopSimulating() override {
4125 bool handledOOM = js::oom::HadSimulatedOOM();
4126 js::oom::simulator.reset();
4127 return handledOOM;
4130 void cleanup(JSContext* cx) override {
4131 cx->runtime()->hadOutOfMemory = false;
4135 static bool OOMTest(JSContext* cx, unsigned argc, Value* vp) {
4136 CallArgs args = CallArgsFromVp(argc, vp);
4138 OOMSimulator simulator;
4139 if (!RunIterativeFailureTest(cx, args, simulator)) {
4140 return false;
4143 args.rval().setUndefined();
4144 return true;
4147 struct StackOOMSimulator : public IterativeFailureTest::FailureSimulator {
4148 void startSimulating(JSContext* cx, unsigned i, unsigned thread,
4149 bool keepFailing) override {
4150 js::oom::simulator.simulateFailureAfter(
4151 js::oom::FailureSimulator::Kind::StackOOM, i, thread, keepFailing);
4154 bool stopSimulating() override {
4155 bool handledOOM = js::oom::HadSimulatedStackOOM();
4156 js::oom::simulator.reset();
4157 return handledOOM;
4161 static bool StackTest(JSContext* cx, unsigned argc, Value* vp) {
4162 CallArgs args = CallArgsFromVp(argc, vp);
4164 StackOOMSimulator simulator;
4165 if (!RunIterativeFailureTest(cx, args, simulator)) {
4166 return false;
4169 args.rval().setUndefined();
4170 return true;
4173 struct FailingIterruptSimulator
4174 : public IterativeFailureTest::FailureSimulator {
4175 JSInterruptCallback* prevEnd = nullptr;
4177 static bool failingInterruptCallback(JSContext* cx) { return false; }
4179 void setup(JSContext* cx) override {
4180 prevEnd = cx->interruptCallbacks().end();
4181 JS_AddInterruptCallback(cx, failingInterruptCallback);
4184 void teardown(JSContext* cx) override {
4185 cx->interruptCallbacks().erase(prevEnd, cx->interruptCallbacks().end());
4188 void startSimulating(JSContext* cx, unsigned i, unsigned thread,
4189 bool keepFailing) override {
4190 js::oom::simulator.simulateFailureAfter(
4191 js::oom::FailureSimulator::Kind::Interrupt, i, thread, keepFailing);
4194 bool stopSimulating() override {
4195 bool handledInterrupt = js::oom::HadSimulatedInterrupt();
4196 js::oom::simulator.reset();
4197 return handledInterrupt;
4201 static bool InterruptTest(JSContext* cx, unsigned argc, Value* vp) {
4202 CallArgs args = CallArgsFromVp(argc, vp);
4204 FailingIterruptSimulator simulator;
4205 if (!RunIterativeFailureTest(cx, args, simulator)) {
4206 return false;
4209 args.rval().setUndefined();
4210 return true;
4213 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
4215 static bool SettlePromiseNow(JSContext* cx, unsigned argc, Value* vp) {
4216 CallArgs args = CallArgsFromVp(argc, vp);
4217 if (!args.requireAtLeast(cx, "settlePromiseNow", 1)) {
4218 return false;
4220 if (!args[0].isObject() || !args[0].toObject().is<PromiseObject>()) {
4221 JS_ReportErrorASCII(cx, "first argument must be a Promise object");
4222 return false;
4225 Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
4226 if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
4227 JS_ReportErrorASCII(
4228 cx, "async function/generator's promise shouldn't be manually settled");
4229 return false;
4232 if (promise->state() != JS::PromiseState::Pending) {
4233 JS_ReportErrorASCII(cx, "cannot settle an already-resolved promise");
4234 return false;
4237 if (IsPromiseWithDefaultResolvingFunction(promise)) {
4238 SetAlreadyResolvedPromiseWithDefaultResolvingFunction(promise);
4241 int32_t flags = promise->flags();
4242 promise->setFixedSlot(
4243 PromiseSlot_Flags,
4244 Int32Value(flags | PROMISE_FLAG_RESOLVED | PROMISE_FLAG_FULFILLED));
4245 promise->setFixedSlot(PromiseSlot_ReactionsOrResult, UndefinedValue());
4247 DebugAPI::onPromiseSettled(cx, promise);
4248 return true;
4251 static bool GetWaitForAllPromise(JSContext* cx, unsigned argc, Value* vp) {
4252 CallArgs args = CallArgsFromVp(argc, vp);
4253 if (!args.requireAtLeast(cx, "getWaitForAllPromise", 1)) {
4254 return false;
4256 if (!args[0].isObject() || !args[0].toObject().is<ArrayObject>() ||
4257 args[0].toObject().as<NativeObject>().isIndexed()) {
4258 JS_ReportErrorASCII(
4259 cx, "first argument must be a dense Array of Promise objects");
4260 return false;
4262 Rooted<NativeObject*> list(cx, &args[0].toObject().as<NativeObject>());
4263 RootedObjectVector promises(cx);
4264 uint32_t count = list->getDenseInitializedLength();
4265 if (!promises.resize(count)) {
4266 return false;
4269 for (uint32_t i = 0; i < count; i++) {
4270 RootedValue elem(cx, list->getDenseElement(i));
4271 if (!elem.isObject() || !elem.toObject().is<PromiseObject>()) {
4272 JS_ReportErrorASCII(
4273 cx, "Each entry in the passed-in Array must be a Promise");
4274 return false;
4276 promises[i].set(&elem.toObject());
4279 RootedObject resultPromise(cx, JS::GetWaitForAllPromise(cx, promises));
4280 if (!resultPromise) {
4281 return false;
4284 args.rval().set(ObjectValue(*resultPromise));
4285 return true;
4288 static bool ResolvePromise(JSContext* cx, unsigned argc, Value* vp) {
4289 CallArgs args = CallArgsFromVp(argc, vp);
4290 if (!args.requireAtLeast(cx, "resolvePromise", 2)) {
4291 return false;
4293 if (!args[0].isObject() ||
4294 !UncheckedUnwrap(&args[0].toObject())->is<PromiseObject>()) {
4295 JS_ReportErrorASCII(
4296 cx, "first argument must be a maybe-wrapped Promise object");
4297 return false;
4300 RootedObject promise(cx, &args[0].toObject());
4301 RootedValue resolution(cx, args[1]);
4302 mozilla::Maybe<AutoRealm> ar;
4303 if (IsWrapper(promise)) {
4304 promise = UncheckedUnwrap(promise);
4305 ar.emplace(cx, promise);
4306 if (!cx->compartment()->wrap(cx, &resolution)) {
4307 return false;
4311 if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
4312 JS_ReportErrorASCII(
4314 "async function/generator's promise shouldn't be manually resolved");
4315 return false;
4318 bool result = JS::ResolvePromise(cx, promise, resolution);
4319 if (result) {
4320 args.rval().setUndefined();
4322 return result;
4325 static bool RejectPromise(JSContext* cx, unsigned argc, Value* vp) {
4326 CallArgs args = CallArgsFromVp(argc, vp);
4327 if (!args.requireAtLeast(cx, "rejectPromise", 2)) {
4328 return false;
4330 if (!args[0].isObject() ||
4331 !UncheckedUnwrap(&args[0].toObject())->is<PromiseObject>()) {
4332 JS_ReportErrorASCII(
4333 cx, "first argument must be a maybe-wrapped Promise object");
4334 return false;
4337 RootedObject promise(cx, &args[0].toObject());
4338 RootedValue reason(cx, args[1]);
4339 mozilla::Maybe<AutoRealm> ar;
4340 if (IsWrapper(promise)) {
4341 promise = UncheckedUnwrap(promise);
4342 ar.emplace(cx, promise);
4343 if (!cx->compartment()->wrap(cx, &reason)) {
4344 return false;
4348 if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
4349 JS_ReportErrorASCII(
4351 "async function/generator's promise shouldn't be manually rejected");
4352 return false;
4355 bool result = JS::RejectPromise(cx, promise, reason);
4356 if (result) {
4357 args.rval().setUndefined();
4359 return result;
4362 static unsigned finalizeCount = 0;
4364 static void finalize_counter_finalize(JS::GCContext* gcx, JSObject* obj) {
4365 ++finalizeCount;
4368 static const JSClassOps FinalizeCounterClassOps = {
4369 nullptr, // addProperty
4370 nullptr, // delProperty
4371 nullptr, // enumerate
4372 nullptr, // newEnumerate
4373 nullptr, // resolve
4374 nullptr, // mayResolve
4375 finalize_counter_finalize, // finalize
4376 nullptr, // call
4377 nullptr, // construct
4378 nullptr, // trace
4381 static const JSClass FinalizeCounterClass = {
4382 "FinalizeCounter", JSCLASS_FOREGROUND_FINALIZE, &FinalizeCounterClassOps};
4384 static bool MakeFinalizeObserver(JSContext* cx, unsigned argc, Value* vp) {
4385 CallArgs args = CallArgsFromVp(argc, vp);
4387 JSObject* obj =
4388 JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, nullptr);
4389 if (!obj) {
4390 return false;
4393 args.rval().setObject(*obj);
4394 return true;
4397 static bool FinalizeCount(JSContext* cx, unsigned argc, Value* vp) {
4398 CallArgs args = CallArgsFromVp(argc, vp);
4399 args.rval().setInt32(finalizeCount);
4400 return true;
4403 static bool ResetFinalizeCount(JSContext* cx, unsigned argc, Value* vp) {
4404 CallArgs args = CallArgsFromVp(argc, vp);
4405 finalizeCount = 0;
4406 args.rval().setUndefined();
4407 return true;
4410 static bool DumpHeap(JSContext* cx, unsigned argc, Value* vp) {
4411 CallArgs args = CallArgsFromVp(argc, vp);
4413 FILE* dumpFile = stdout;
4414 auto closeFile = mozilla::MakeScopeExit([&dumpFile] {
4415 if (dumpFile != stdout) {
4416 fclose(dumpFile);
4420 if (args.length() > 1) {
4421 RootedObject callee(cx, &args.callee());
4422 ReportUsageErrorASCII(cx, callee, "Too many arguments");
4423 return false;
4426 if (!args.get(0).isUndefined()) {
4427 RootedString str(cx, ToString(cx, args[0]));
4428 if (!str) {
4429 return false;
4431 if (!fuzzingSafe) {
4432 UniqueChars fileNameBytes = JS_EncodeStringToUTF8(cx, str);
4433 if (!fileNameBytes) {
4434 return false;
4436 #ifdef XP_WIN
4437 UniqueWideChars wideFileNameBytes =
4438 JS::EncodeUtf8ToWide(cx, fileNameBytes.get());
4439 if (!wideFileNameBytes) {
4440 return false;
4442 dumpFile = _wfopen(wideFileNameBytes.get(), L"w");
4443 #else
4444 UniqueChars narrowFileNameBytes =
4445 JS::EncodeUtf8ToNarrow(cx, fileNameBytes.get());
4446 if (!narrowFileNameBytes) {
4447 return false;
4449 dumpFile = fopen(narrowFileNameBytes.get(), "w");
4450 #endif
4451 if (!dumpFile) {
4452 JS_ReportErrorUTF8(cx, "can't open %s", fileNameBytes.get());
4453 return false;
4458 js::DumpHeap(cx, dumpFile, js::IgnoreNurseryObjects);
4460 args.rval().setUndefined();
4461 return true;
4464 static bool Terminate(JSContext* cx, unsigned arg, Value* vp) {
4465 // Print a message to stderr in differential testing to help jsfunfuzz
4466 // find uncatchable-exception bugs.
4467 if (js::SupportDifferentialTesting()) {
4468 fprintf(stderr, "terminate called\n");
4471 JS_ClearPendingException(cx);
4472 return false;
4475 static bool ReadGeckoProfilingStack(JSContext* cx, unsigned argc, Value* vp) {
4476 CallArgs args = CallArgsFromVp(argc, vp);
4477 args.rval().setUndefined();
4479 // Return boolean 'false' if profiler is not enabled.
4480 if (!cx->runtime()->geckoProfiler().enabled()) {
4481 args.rval().setBoolean(false);
4482 return true;
4485 // Array holding physical jit stack frames.
4486 RootedObject stack(cx, NewDenseEmptyArray(cx));
4487 if (!stack) {
4488 return false;
4491 // If profiler sampling has been suppressed, return an empty
4492 // stack.
4493 if (!cx->isProfilerSamplingEnabled()) {
4494 args.rval().setObject(*stack);
4495 return true;
4498 struct InlineFrameInfo {
4499 InlineFrameInfo(const char* kind, UniqueChars label)
4500 : kind(kind), label(std::move(label)) {}
4501 const char* kind;
4502 UniqueChars label;
4505 Vector<Vector<InlineFrameInfo, 0, TempAllocPolicy>, 0, TempAllocPolicy>
4506 frameInfo(cx);
4508 JS::ProfilingFrameIterator::RegisterState state;
4509 for (JS::ProfilingFrameIterator i(cx, state); !i.done(); ++i) {
4510 MOZ_ASSERT(i.stackAddress() != nullptr);
4512 if (!frameInfo.emplaceBack(cx)) {
4513 return false;
4516 const size_t MaxInlineFrames = 16;
4517 JS::ProfilingFrameIterator::Frame frames[MaxInlineFrames];
4518 uint32_t nframes = i.extractStack(frames, 0, MaxInlineFrames);
4519 MOZ_ASSERT(nframes <= MaxInlineFrames);
4520 for (uint32_t i = 0; i < nframes; i++) {
4521 const char* frameKindStr = nullptr;
4522 switch (frames[i].kind) {
4523 case JS::ProfilingFrameIterator::Frame_BaselineInterpreter:
4524 frameKindStr = "baseline-interpreter";
4525 break;
4526 case JS::ProfilingFrameIterator::Frame_Baseline:
4527 frameKindStr = "baseline-jit";
4528 break;
4529 case JS::ProfilingFrameIterator::Frame_Ion:
4530 frameKindStr = "ion";
4531 break;
4532 case JS::ProfilingFrameIterator::Frame_Wasm:
4533 frameKindStr = "wasm";
4534 break;
4535 default:
4536 frameKindStr = "unknown";
4539 UniqueChars label =
4540 DuplicateStringToArena(js::StringBufferArena, cx, frames[i].label);
4541 if (!label) {
4542 return false;
4545 if (!frameInfo.back().emplaceBack(frameKindStr, std::move(label))) {
4546 return false;
4551 RootedObject inlineFrameInfo(cx);
4552 RootedString frameKind(cx);
4553 RootedString frameLabel(cx);
4554 RootedId idx(cx);
4556 const unsigned propAttrs = JSPROP_ENUMERATE;
4558 uint32_t physicalFrameNo = 0;
4559 for (auto& frame : frameInfo) {
4560 // Array holding all inline frames in a single physical jit stack frame.
4561 RootedObject inlineStack(cx, NewDenseEmptyArray(cx));
4562 if (!inlineStack) {
4563 return false;
4566 uint32_t inlineFrameNo = 0;
4567 for (auto& inlineFrame : frame) {
4568 // Object holding frame info.
4569 RootedObject inlineFrameInfo(cx, NewPlainObject(cx));
4570 if (!inlineFrameInfo) {
4571 return false;
4574 frameKind = NewStringCopyZ<CanGC>(cx, inlineFrame.kind);
4575 if (!frameKind) {
4576 return false;
4579 if (!JS_DefineProperty(cx, inlineFrameInfo, "kind", frameKind,
4580 propAttrs)) {
4581 return false;
4584 frameLabel = NewLatin1StringZ(cx, std::move(inlineFrame.label));
4585 if (!frameLabel) {
4586 return false;
4589 if (!JS_DefineProperty(cx, inlineFrameInfo, "label", frameLabel,
4590 propAttrs)) {
4591 return false;
4594 idx = PropertyKey::Int(inlineFrameNo);
4595 if (!JS_DefinePropertyById(cx, inlineStack, idx, inlineFrameInfo, 0)) {
4596 return false;
4599 ++inlineFrameNo;
4602 // Push inline array into main array.
4603 idx = PropertyKey::Int(physicalFrameNo);
4604 if (!JS_DefinePropertyById(cx, stack, idx, inlineStack, 0)) {
4605 return false;
4608 ++physicalFrameNo;
4611 args.rval().setObject(*stack);
4612 return true;
4615 static bool ReadGeckoInterpProfilingStack(JSContext* cx, unsigned argc,
4616 Value* vp) {
4617 CallArgs args = CallArgsFromVp(argc, vp);
4618 args.rval().setUndefined();
4620 // Return boolean 'false' if profiler is not enabled.
4621 if (!cx->runtime()->geckoProfiler().enabled()) {
4622 args.rval().setBoolean(false);
4623 return true;
4626 // Array with information about each frame.
4627 Rooted<JSObject*> stack(cx, NewDenseEmptyArray(cx));
4628 if (!stack) {
4629 return false;
4631 uint32_t stackIndex = 0;
4633 ProfilingStack* profStack = cx->geckoProfiler().getProfilingStack();
4634 MOZ_ASSERT(profStack);
4636 for (size_t i = 0; i < profStack->stackSize(); i++) {
4637 const auto& frame = profStack->frames[i];
4638 if (!frame.isJsFrame()) {
4639 continue;
4642 // Skip fake JS frame pushed for js::RunScript by GeckoProfilerEntryMarker.
4643 const char* dynamicStr = frame.dynamicString();
4644 if (!dynamicStr) {
4645 continue;
4648 Rooted<PlainObject*> frameInfo(cx, NewPlainObject(cx));
4649 if (!frameInfo) {
4650 return false;
4653 Rooted<JSString*> dynamicString(
4654 cx, JS_NewStringCopyUTF8Z(
4655 cx, JS::ConstUTF8CharsZ(dynamicStr, strlen(dynamicStr))));
4656 if (!dynamicString) {
4657 return false;
4659 if (!JS_DefineProperty(cx, frameInfo, "dynamicString", dynamicString,
4660 JSPROP_ENUMERATE)) {
4661 return false;
4664 if (!JS_DefineElement(cx, stack, stackIndex, frameInfo, JSPROP_ENUMERATE)) {
4665 return false;
4667 stackIndex++;
4670 args.rval().setObject(*stack);
4671 return true;
4674 static bool EnableOsiPointRegisterChecks(JSContext*, unsigned argc, Value* vp) {
4675 CallArgs args = CallArgsFromVp(argc, vp);
4676 #ifdef CHECK_OSIPOINT_REGISTERS
4677 jit::JitOptions.checkOsiPointRegisters = true;
4678 #endif
4679 args.rval().setUndefined();
4680 return true;
4683 static bool DisplayName(JSContext* cx, unsigned argc, Value* vp) {
4684 CallArgs args = CallArgsFromVp(argc, vp);
4685 if (!args.get(0).isObject() || !args[0].toObject().is<JSFunction>()) {
4686 RootedObject arg(cx, &args.callee());
4687 ReportUsageErrorASCII(cx, arg, "Must have one function argument");
4688 return false;
4691 JSFunction* fun = &args[0].toObject().as<JSFunction>();
4692 JS::Rooted<JSAtom*> str(cx);
4693 if (!fun->getDisplayAtom(cx, &str)) {
4694 return false;
4696 args.rval().setString(str ? str : cx->runtime()->emptyString.ref());
4697 return true;
4700 static bool IsAvxPresent(JSContext* cx, unsigned argc, Value* vp) {
4701 CallArgs args = CallArgsFromVp(argc, vp);
4702 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
4703 int minVersion = 1;
4704 if (argc > 0 && args.get(0).isNumber()) {
4705 minVersion = std::max(1, int(args[0].toNumber()));
4707 switch (minVersion) {
4708 case 1:
4709 args.rval().setBoolean(jit::Assembler::HasAVX());
4710 return true;
4711 case 2:
4712 args.rval().setBoolean(jit::Assembler::HasAVX2());
4713 return true;
4715 #endif
4716 args.rval().setBoolean(false);
4717 return true;
4720 class ShellAllocationMetadataBuilder : public AllocationMetadataBuilder {
4721 public:
4722 ShellAllocationMetadataBuilder() = default;
4724 virtual JSObject* build(JSContext* cx, HandleObject,
4725 AutoEnterOOMUnsafeRegion& oomUnsafe) const override;
4727 static const ShellAllocationMetadataBuilder metadataBuilder;
4730 JSObject* ShellAllocationMetadataBuilder::build(
4731 JSContext* cx, HandleObject, AutoEnterOOMUnsafeRegion& oomUnsafe) const {
4732 RootedObject obj(cx, NewPlainObject(cx));
4733 if (!obj) {
4734 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
4737 RootedObject stack(cx, NewDenseEmptyArray(cx));
4738 if (!stack) {
4739 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
4742 static int createdIndex = 0;
4743 createdIndex++;
4745 if (!JS_DefineProperty(cx, obj, "index", createdIndex, 0)) {
4746 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
4749 if (!JS_DefineProperty(cx, obj, "stack", stack, 0)) {
4750 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
4753 int stackIndex = 0;
4754 RootedId id(cx);
4755 RootedValue callee(cx);
4756 for (NonBuiltinScriptFrameIter iter(cx); !iter.done(); ++iter) {
4757 if (iter.isFunctionFrame() && iter.compartment() == cx->compartment()) {
4758 id = PropertyKey::Int(stackIndex);
4759 RootedObject callee(cx, iter.callee(cx));
4760 if (!JS_DefinePropertyById(cx, stack, id, callee, JSPROP_ENUMERATE)) {
4761 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
4763 stackIndex++;
4767 return obj;
4770 const ShellAllocationMetadataBuilder
4771 ShellAllocationMetadataBuilder::metadataBuilder;
4773 static bool EnableShellAllocationMetadataBuilder(JSContext* cx, unsigned argc,
4774 Value* vp) {
4775 CallArgs args = CallArgsFromVp(argc, vp);
4777 SetAllocationMetadataBuilder(
4778 cx, &ShellAllocationMetadataBuilder::metadataBuilder);
4780 args.rval().setUndefined();
4781 return true;
4784 static bool GetAllocationMetadata(JSContext* cx, unsigned argc, Value* vp) {
4785 CallArgs args = CallArgsFromVp(argc, vp);
4786 if (args.length() != 1 || !args[0].isObject()) {
4787 JS_ReportErrorASCII(cx, "Argument must be an object");
4788 return false;
4791 args.rval().setObjectOrNull(GetAllocationMetadata(&args[0].toObject()));
4792 return true;
4795 static bool testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp) {
4796 CallArgs args = CallArgsFromVp(argc, vp);
4798 // NOP when not in IonMonkey
4799 args.rval().setUndefined();
4800 return true;
4803 static bool testingFunc_bailAfter(JSContext* cx, unsigned argc, Value* vp) {
4804 CallArgs args = CallArgsFromVp(argc, vp);
4805 if (args.length() != 1 || !args[0].isInt32() || args[0].toInt32() < 0) {
4806 JS_ReportErrorASCII(
4807 cx, "Argument must be a positive number that fits in an int32");
4808 return false;
4811 #ifdef DEBUG
4812 if (auto* jitRuntime = cx->runtime()->jitRuntime()) {
4813 uint32_t bailAfter = args[0].toInt32();
4814 bool enableBailAfter = bailAfter > 0;
4815 if (jitRuntime->ionBailAfterEnabled() != enableBailAfter) {
4816 // Force JIT code to be recompiled with (or without) instrumentation.
4817 ReleaseAllJITCode(cx->gcContext());
4818 jitRuntime->setIonBailAfterEnabled(enableBailAfter);
4820 jitRuntime->setIonBailAfterCounter(bailAfter);
4822 #endif
4824 args.rval().setUndefined();
4825 return true;
4828 static bool testingFunc_invalidate(JSContext* cx, unsigned argc, Value* vp) {
4829 CallArgs args = CallArgsFromVp(argc, vp);
4831 // If the topmost frame is Ion/Warp, find the IonScript and invalidate it.
4832 FrameIter iter(cx);
4833 if (!iter.done() && iter.isIon()) {
4834 while (!iter.isPhysicalJitFrame()) {
4835 ++iter;
4837 if (iter.script()->hasIonScript()) {
4838 js::jit::Invalidate(cx, iter.script());
4842 args.rval().setUndefined();
4843 return true;
4846 static constexpr unsigned JitWarmupResetLimit = 20;
4847 static_assert(JitWarmupResetLimit <=
4848 unsigned(JSScript::MutableFlags::WarmupResets_MASK),
4849 "JitWarmupResetLimit exceeds max value");
4851 static bool testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp) {
4852 CallArgs args = CallArgsFromVp(argc, vp);
4854 if (!jit::IsBaselineJitEnabled(cx)) {
4855 return ReturnStringCopy(cx, args, "Baseline is disabled.");
4858 // Use frame iterator to inspect caller.
4859 FrameIter iter(cx);
4861 // We may be invoked directly, not in a JS context, e.g. if inJit is added as
4862 // a callback on the event queue.
4863 if (iter.done()) {
4864 args.rval().setBoolean(false);
4865 return true;
4868 if (iter.hasScript()) {
4869 // Detect repeated attempts to compile, resetting the counter if inJit
4870 // succeeds. Note: This script may have be inlined into its caller.
4871 if (iter.isJSJit()) {
4872 iter.script()->resetWarmUpResetCounter();
4873 } else if (iter.script()->getWarmUpResetCount() >= JitWarmupResetLimit) {
4874 return ReturnStringCopy(
4875 cx, args, "Compilation is being repeatedly prevented. Giving up.");
4879 // Returns true for any JIT (including WASM).
4880 MOZ_ASSERT_IF(iter.isJSJit(), cx->currentlyRunningInJit());
4881 args.rval().setBoolean(cx->currentlyRunningInJit());
4882 return true;
4885 static bool testingFunc_inIon(JSContext* cx, unsigned argc, Value* vp) {
4886 CallArgs args = CallArgsFromVp(argc, vp);
4888 if (!jit::IsIonEnabled(cx)) {
4889 return ReturnStringCopy(cx, args, "Ion is disabled.");
4892 // Use frame iterator to inspect caller.
4893 FrameIter iter(cx);
4895 // We may be invoked directly, not in a JS context, e.g. if inIon is added as
4896 // a callback on the event queue.
4897 if (iter.done()) {
4898 args.rval().setBoolean(false);
4899 return true;
4902 if (iter.hasScript()) {
4903 // Detect repeated attempts to compile, resetting the counter if inIon
4904 // succeeds. Note: This script may have be inlined into its caller.
4905 if (iter.isIon()) {
4906 iter.script()->resetWarmUpResetCounter();
4907 } else if (!iter.script()->canIonCompile()) {
4908 return ReturnStringCopy(cx, args, "Unable to Ion-compile this script.");
4909 } else if (iter.script()->getWarmUpResetCount() >= JitWarmupResetLimit) {
4910 return ReturnStringCopy(
4911 cx, args, "Compilation is being repeatedly prevented. Giving up.");
4915 args.rval().setBoolean(iter.isIon());
4916 return true;
4919 bool js::testingFunc_assertFloat32(JSContext* cx, unsigned argc, Value* vp) {
4920 CallArgs args = CallArgsFromVp(argc, vp);
4921 if (args.length() != 2) {
4922 JS_ReportErrorASCII(cx, "Expects only 2 arguments");
4923 return false;
4926 // NOP when not in IonMonkey
4927 args.rval().setUndefined();
4928 return true;
4931 static bool TestingFunc_assertJitStackInvariants(JSContext* cx, unsigned argc,
4932 Value* vp) {
4933 CallArgs args = CallArgsFromVp(argc, vp);
4935 jit::AssertJitStackInvariants(cx);
4936 args.rval().setUndefined();
4937 return true;
4940 bool js::testingFunc_assertRecoveredOnBailout(JSContext* cx, unsigned argc,
4941 Value* vp) {
4942 CallArgs args = CallArgsFromVp(argc, vp);
4943 if (args.length() != 2) {
4944 JS_ReportErrorASCII(cx, "Expects only 2 arguments");
4945 return false;
4948 // NOP when not in IonMonkey
4949 args.rval().setUndefined();
4950 return true;
4953 static bool GetJitCompilerOptions(JSContext* cx, unsigned argc, Value* vp) {
4954 CallArgs args = CallArgsFromVp(argc, vp);
4955 RootedObject info(cx, JS_NewPlainObject(cx));
4956 if (!info) {
4957 return false;
4960 uint32_t intValue = 0;
4961 RootedValue value(cx);
4963 #define JIT_COMPILER_MATCH(key, string) \
4964 opt = JSJITCOMPILER_##key; \
4965 if (JS_GetGlobalJitCompilerOption(cx, opt, &intValue)) { \
4966 value.setInt32(intValue); \
4967 if (!JS_SetProperty(cx, info, string, value)) return false; \
4970 JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION;
4971 JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH);
4972 #undef JIT_COMPILER_MATCH
4974 args.rval().setObject(*info);
4975 return true;
4978 static bool SetIonCheckGraphCoherency(JSContext* cx, unsigned argc, Value* vp) {
4979 CallArgs args = CallArgsFromVp(argc, vp);
4980 jit::JitOptions.checkGraphConsistency = ToBoolean(args.get(0));
4981 args.rval().setUndefined();
4982 return true;
4985 // A JSObject that holds structured clone data, similar to the C++ class
4986 // JSAutoStructuredCloneBuffer.
4987 class CloneBufferObject : public NativeObject {
4988 static const JSPropertySpec props_[3];
4990 static const size_t DATA_SLOT = 0;
4991 static const size_t SYNTHETIC_SLOT = 1;
4992 static const size_t NUM_SLOTS = 2;
4994 public:
4995 static const JSClass class_;
4997 static CloneBufferObject* Create(JSContext* cx) {
4998 RootedObject obj(cx, JS_NewObject(cx, &class_));
4999 if (!obj) {
5000 return nullptr;
5002 obj->as<CloneBufferObject>().setReservedSlot(DATA_SLOT,
5003 PrivateValue(nullptr));
5004 obj->as<CloneBufferObject>().setReservedSlot(SYNTHETIC_SLOT,
5005 BooleanValue(false));
5007 if (!JS_DefineProperties(cx, obj, props_)) {
5008 return nullptr;
5011 return &obj->as<CloneBufferObject>();
5014 static CloneBufferObject* Create(JSContext* cx,
5015 JSAutoStructuredCloneBuffer* buffer) {
5016 Rooted<CloneBufferObject*> obj(cx, Create(cx));
5017 if (!obj) {
5018 return nullptr;
5020 auto data = js::MakeUnique<JSStructuredCloneData>(buffer->scope());
5021 if (!data) {
5022 ReportOutOfMemory(cx);
5023 return nullptr;
5025 buffer->giveTo(data.get());
5026 obj->setData(data.release(), false);
5027 return obj;
5030 JSStructuredCloneData* data() const {
5031 return static_cast<JSStructuredCloneData*>(
5032 getReservedSlot(DATA_SLOT).toPrivate());
5035 bool isSynthetic() const {
5036 return getReservedSlot(SYNTHETIC_SLOT).toBoolean();
5039 void setData(JSStructuredCloneData* aData, bool synthetic) {
5040 MOZ_ASSERT(!data());
5041 setReservedSlot(DATA_SLOT, PrivateValue(aData));
5042 setReservedSlot(SYNTHETIC_SLOT, BooleanValue(synthetic));
5045 // Discard an owned clone buffer.
5046 void discard() {
5047 js_delete(data());
5048 setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
5051 static bool setCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
5052 Rooted<CloneBufferObject*> obj(
5053 cx, &args.thisv().toObject().as<CloneBufferObject>());
5055 const char* data = nullptr;
5056 UniqueChars dataOwner;
5057 size_t nbytes;
5059 if (args.get(0).isObject() && args[0].toObject().is<ArrayBufferObject>()) {
5060 ArrayBufferObject* buffer = &args[0].toObject().as<ArrayBufferObject>();
5061 bool isSharedMemory;
5062 uint8_t* dataBytes = nullptr;
5063 JS::GetArrayBufferLengthAndData(buffer, &nbytes, &isSharedMemory,
5064 &dataBytes);
5065 MOZ_ASSERT(!isSharedMemory);
5066 data = reinterpret_cast<char*>(dataBytes);
5067 } else {
5068 JSString* str = JS::ToString(cx, args.get(0));
5069 if (!str) {
5070 return false;
5072 dataOwner = JS_EncodeStringToLatin1(cx, str);
5073 if (!dataOwner) {
5074 return false;
5076 data = dataOwner.get();
5077 nbytes = JS_GetStringLength(str);
5080 if (nbytes == 0 || (nbytes % sizeof(uint64_t) != 0)) {
5081 JS_ReportErrorASCII(cx, "Invalid length for clonebuffer data");
5082 return false;
5085 auto buf = js::MakeUnique<JSStructuredCloneData>(
5086 JS::StructuredCloneScope::DifferentProcess);
5087 if (!buf || !buf->Init(nbytes)) {
5088 ReportOutOfMemory(cx);
5089 return false;
5092 MOZ_ALWAYS_TRUE(buf->AppendBytes(data, nbytes));
5093 obj->discard();
5094 obj->setData(buf.release(), true);
5096 args.rval().setUndefined();
5097 return true;
5100 static bool is(HandleValue v) {
5101 return v.isObject() && v.toObject().is<CloneBufferObject>();
5104 static bool setCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
5105 CallArgs args = CallArgsFromVp(argc, vp);
5106 return CallNonGenericMethod<is, setCloneBuffer_impl>(cx, args);
5109 static bool getData(JSContext* cx, Handle<CloneBufferObject*> obj,
5110 JSStructuredCloneData** data) {
5111 if (!obj->data()) {
5112 *data = nullptr;
5113 return true;
5116 bool hasTransferable;
5117 if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable)) {
5118 return false;
5121 if (hasTransferable) {
5122 JS_ReportErrorASCII(
5123 cx, "cannot retrieve structured clone buffer with transferables");
5124 return false;
5127 *data = obj->data();
5128 return true;
5131 static bool getCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
5132 Rooted<CloneBufferObject*> obj(
5133 cx, &args.thisv().toObject().as<CloneBufferObject>());
5134 MOZ_ASSERT(args.length() == 0);
5136 JSStructuredCloneData* data;
5137 if (!getData(cx, obj, &data)) {
5138 return false;
5141 size_t size = data->Size();
5142 UniqueChars buffer(js_pod_malloc<char>(size));
5143 if (!buffer) {
5144 ReportOutOfMemory(cx);
5145 return false;
5147 auto iter = data->Start();
5148 if (!data->ReadBytes(iter, buffer.get(), size)) {
5149 ReportOutOfMemory(cx);
5150 return false;
5152 JSString* str = JS_NewStringCopyN(cx, buffer.get(), size);
5153 if (!str) {
5154 return false;
5156 args.rval().setString(str);
5157 return true;
5160 static bool getCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
5161 CallArgs args = CallArgsFromVp(argc, vp);
5162 return CallNonGenericMethod<is, getCloneBuffer_impl>(cx, args);
5165 static bool getCloneBufferAsArrayBuffer_impl(JSContext* cx,
5166 const CallArgs& args) {
5167 Rooted<CloneBufferObject*> obj(
5168 cx, &args.thisv().toObject().as<CloneBufferObject>());
5169 MOZ_ASSERT(args.length() == 0);
5171 JSStructuredCloneData* data;
5172 if (!getData(cx, obj, &data)) {
5173 return false;
5176 size_t size = data->Size();
5177 UniqueChars buffer(js_pod_malloc<char>(size));
5178 if (!buffer) {
5179 ReportOutOfMemory(cx);
5180 return false;
5182 auto iter = data->Start();
5183 if (!data->ReadBytes(iter, buffer.get(), size)) {
5184 ReportOutOfMemory(cx);
5185 return false;
5188 JSObject* arrayBuffer =
5189 JS::NewArrayBufferWithContents(cx, size, std::move(buffer));
5190 if (!arrayBuffer) {
5191 return false;
5194 args.rval().setObject(*arrayBuffer);
5195 return true;
5198 static bool getCloneBufferAsArrayBuffer(JSContext* cx, unsigned int argc,
5199 JS::Value* vp) {
5200 CallArgs args = CallArgsFromVp(argc, vp);
5201 return CallNonGenericMethod<is, getCloneBufferAsArrayBuffer_impl>(cx, args);
5204 static void Finalize(JS::GCContext* gcx, JSObject* obj) {
5205 obj->as<CloneBufferObject>().discard();
5209 static const JSClassOps CloneBufferObjectClassOps = {
5210 nullptr, // addProperty
5211 nullptr, // delProperty
5212 nullptr, // enumerate
5213 nullptr, // newEnumerate
5214 nullptr, // resolve
5215 nullptr, // mayResolve
5216 CloneBufferObject::Finalize, // finalize
5217 nullptr, // call
5218 nullptr, // construct
5219 nullptr, // trace
5222 const JSClass CloneBufferObject::class_ = {
5223 "CloneBuffer",
5224 JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS) |
5225 JSCLASS_FOREGROUND_FINALIZE,
5226 &CloneBufferObjectClassOps};
5228 const JSPropertySpec CloneBufferObject::props_[] = {
5229 JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0),
5230 JS_PSGS("arraybuffer", getCloneBufferAsArrayBuffer, setCloneBuffer, 0),
5231 JS_PS_END};
5233 static mozilla::Maybe<JS::StructuredCloneScope> ParseCloneScope(
5234 JSContext* cx, HandleString str) {
5235 mozilla::Maybe<JS::StructuredCloneScope> scope;
5237 JSLinearString* scopeStr = str->ensureLinear(cx);
5238 if (!scopeStr) {
5239 return scope;
5242 if (StringEqualsLiteral(scopeStr, "SameProcess")) {
5243 scope.emplace(JS::StructuredCloneScope::SameProcess);
5244 } else if (StringEqualsLiteral(scopeStr, "DifferentProcess")) {
5245 scope.emplace(JS::StructuredCloneScope::DifferentProcess);
5246 } else if (StringEqualsLiteral(scopeStr, "DifferentProcessForIndexedDB")) {
5247 scope.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB);
5250 return scope;
5253 // A custom object that is serializable and transferable using
5254 // the engine's custom hooks. The callbacks log their activity
5255 // to a JSRuntime-wide log (tagging actions with IDs to distinguish them).
5256 class CustomSerializableObject : public NativeObject {
5257 static const size_t ID_SLOT = 0;
5258 static const size_t DETACHED_SLOT = 1;
5259 static const size_t BEHAVIOR_SLOT = 2;
5260 static const size_t NUM_SLOTS = 3;
5262 static constexpr size_t MAX_LOG_LEN = 100;
5264 // The activity log should be specific to a JSRuntime.
5265 struct ActivityLog {
5266 uint32_t buffer[MAX_LOG_LEN];
5267 size_t length = 0;
5269 static MOZ_THREAD_LOCAL(ActivityLog*) self;
5270 static ActivityLog* getThreadLog() {
5271 if (!self.initialized() || !self.get()) {
5272 self.infallibleInit();
5273 self.set(js_new<ActivityLog>());
5274 MOZ_RELEASE_ASSERT(self.get());
5275 if (!TlsContext.get()->runtime()->atExit(
5276 [](void* vpData) {
5277 auto* log = static_cast<ActivityLog*>(vpData);
5278 js_delete(log);
5280 self.get())) {
5281 AutoEnterOOMUnsafeRegion oomUnsafe;
5282 oomUnsafe.crash("atExit");
5285 return self.get();
5288 static bool log(int32_t id, char action) {
5289 return getThreadLog()->logImpl(id, action);
5292 bool logImpl(int32_t id, char action) {
5293 if (length + 2 > MAX_LOG_LEN) {
5294 return false;
5296 buffer[length++] = id;
5297 buffer[length++] = uint32_t(action);
5298 return true;
5302 public:
5303 enum class Behavior {
5304 Nothing = 0,
5305 FailDuringReadTransfer = 1,
5306 FailDuringRead = 2
5309 static constexpr JSClass class_ = {"CustomSerializable",
5310 JSCLASS_HAS_RESERVED_SLOTS(NUM_SLOTS)};
5312 static bool is(HandleValue v) {
5313 return v.isObject() && v.toObject().is<CustomSerializableObject>();
5316 static CustomSerializableObject* Create(JSContext* cx, int32_t id,
5317 Behavior behavior) {
5318 Rooted<CustomSerializableObject*> obj(
5319 cx, static_cast<CustomSerializableObject*>(JS_NewObject(cx, &class_)));
5320 if (!obj) {
5321 return nullptr;
5323 obj->setReservedSlot(ID_SLOT, Int32Value(id));
5324 obj->setReservedSlot(DETACHED_SLOT, BooleanValue(false));
5325 obj->setReservedSlot(BEHAVIOR_SLOT,
5326 Int32Value(static_cast<int32_t>(behavior)));
5328 if (!JS_DefineProperty(cx, obj, "log", getLog, clearLog, 0)) {
5329 return nullptr;
5332 return obj;
5335 public:
5336 static uint32_t tag() { return JS_SCTAG_USER_MIN; }
5338 static bool log(int32_t id, char action) {
5339 return ActivityLog::log(id, action);
5341 bool log(char action) {
5342 return log(getReservedSlot(ID_SLOT).toInt32(), action);
5345 void detach() { setReservedSlot(DETACHED_SLOT, BooleanValue(true)); }
5346 bool isDetached() { return getReservedSlot(DETACHED_SLOT).toBoolean(); }
5348 uint32_t id() const { return getReservedSlot(ID_SLOT).toInt32(); }
5349 Behavior behavior() {
5350 return static_cast<Behavior>(getReservedSlot(BEHAVIOR_SLOT).toInt32());
5353 static bool getLog(JSContext* cx, unsigned int argc, JS::Value* vp) {
5354 CallArgs args = CallArgsFromVp(argc, vp);
5355 return CallNonGenericMethod<is, getLog_impl>(cx, args);
5358 static bool getLog_impl(JSContext* cx, const CallArgs& args) {
5359 Rooted<CustomSerializableObject*> obj(
5360 cx, &args.thisv().toObject().as<CustomSerializableObject>());
5362 size_t len = ActivityLog::getThreadLog()->length;
5363 uint32_t* logBuffer = ActivityLog::getThreadLog()->buffer;
5365 Rooted<ArrayObject*> result(cx, NewDenseFullyAllocatedArray(cx, len));
5366 if (!result) {
5367 return false;
5369 result->ensureDenseInitializedLength(0, len);
5371 for (size_t p = 0; p < len; p += 2) {
5372 int32_t id = int32_t(logBuffer[p]);
5373 char action = char(logBuffer[p + 1]);
5374 result->setDenseElement(p, Int32Value(id));
5375 JSString* str = JS_NewStringCopyN(cx, &action, 1);
5376 if (!str) {
5377 return false;
5379 result->setDenseElement(p + 1, StringValue(str));
5382 args.rval().setObject(*result);
5383 return true;
5386 static bool clearLog(JSContext* cx, unsigned int argc, JS::Value* vp) {
5387 CallArgs args = CallArgsFromVp(argc, vp);
5388 if (!args.get(0).isNullOrUndefined()) {
5389 JS_ReportErrorASCII(cx, "log may only be assigned null/undefined");
5390 return false;
5392 ActivityLog::getThreadLog()->length = 0;
5393 args.rval().setUndefined();
5394 return true;
5397 static bool Write(JSContext* cx, JSStructuredCloneWriter* w,
5398 JS::HandleObject aObj, bool* sameProcessScopeRequired,
5399 void* closure) {
5400 Rooted<CustomSerializableObject*> obj(cx);
5402 if ((obj = aObj->maybeUnwrapIf<CustomSerializableObject>())) {
5403 obj->log('w');
5404 // Write a regular clone as a <tag, id> pair, followed by <0, behavior>.
5405 // Note that transferring will communicate the behavior via a different
5406 // mechanism.
5407 return JS_WriteUint32Pair(w, obj->tag(), obj->id()) &&
5408 JS_WriteUint32Pair(w, 0, static_cast<uint32_t>(obj->behavior()));
5411 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5412 JSMSG_SC_UNSUPPORTED_TYPE);
5413 return false;
5416 static JSObject* Read(JSContext* cx, JSStructuredCloneReader* r,
5417 const JS::CloneDataPolicy& cloneDataPolicy,
5418 uint32_t tag, uint32_t id, void* closure) {
5419 uint32_t dummy, behaviorData;
5420 if (!JS_ReadUint32Pair(r, &dummy, &behaviorData)) {
5421 return nullptr;
5423 if (dummy != 0 || id > INT32_MAX) {
5424 JS_ReportErrorASCII(cx, "out of range");
5425 return nullptr;
5428 auto b = static_cast<Behavior>(behaviorData);
5429 Rooted<CustomSerializableObject*> obj(
5430 cx, Create(cx, static_cast<int32_t>(id), b));
5431 if (!obj) {
5432 return nullptr;
5435 obj->log('r');
5436 if (obj->behavior() == Behavior::FailDuringRead) {
5437 JS_ReportErrorASCII(cx,
5438 "Failed as requested in read during deserialization");
5439 return nullptr;
5441 return obj;
5444 static bool CanTransfer(JSContext* cx, JS::Handle<JSObject*> wrapped,
5445 bool* sameProcessScopeRequired, void* closure) {
5446 Rooted<CustomSerializableObject*> obj(cx);
5448 if ((obj = wrapped->maybeUnwrapIf<CustomSerializableObject>())) {
5449 obj->log('?');
5450 // For now, all CustomSerializable objects are considered to be
5451 // transferable.
5452 return true;
5455 return false;
5458 static bool WriteTransfer(JSContext* cx, JS::Handle<JSObject*> aObj,
5459 void* closure, uint32_t* tag,
5460 JS::TransferableOwnership* ownership,
5461 void** content, uint64_t* extraData) {
5462 Rooted<CustomSerializableObject*> obj(cx);
5464 if ((obj = aObj->maybeUnwrapIf<CustomSerializableObject>())) {
5465 if (obj->isDetached()) {
5466 JS_ReportErrorASCII(cx, "Attempted to transfer detached object");
5467 return false;
5469 obj->log('W');
5470 *content = reinterpret_cast<void*>(obj->id());
5471 *extraData = static_cast<uint64_t>(obj->behavior());
5472 *tag = obj->tag();
5473 *ownership = JS::SCTAG_TMO_CUSTOM;
5474 obj->detach();
5475 return true;
5478 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5479 JSMSG_SC_NOT_TRANSFERABLE);
5480 return false;
5483 static bool ReadTransfer(JSContext* cx, JSStructuredCloneReader* r,
5484 const JS::CloneDataPolicy& cloneDataPolicy,
5485 uint32_t tag, void* content, uint64_t extraData,
5486 void* closure,
5487 JS::MutableHandleObject returnObject) {
5488 if (tag == CustomSerializableObject::tag()) {
5489 int32_t id = int32_t(reinterpret_cast<uintptr_t>(content));
5490 Rooted<CustomSerializableObject*> obj(
5491 cx, CustomSerializableObject::Create(
5492 cx, id, static_cast<Behavior>(extraData)));
5493 if (!obj) {
5494 return false;
5496 obj->log('R');
5497 if (obj->behavior() == Behavior::FailDuringReadTransfer) {
5498 return false;
5500 returnObject.set(obj);
5501 return true;
5504 return false;
5507 static void FreeTransfer(uint32_t tag, JS::TransferableOwnership ownership,
5508 void* content, uint64_t extraData, void* closure) {
5509 CustomSerializableObject::log(uint32_t(reinterpret_cast<intptr_t>(content)),
5510 'F');
5514 MOZ_THREAD_LOCAL(CustomSerializableObject::ActivityLog*)
5515 CustomSerializableObject::ActivityLog::self;
5517 static bool MakeSerializable(JSContext* cx, unsigned argc, Value* vp) {
5518 CallArgs args = CallArgsFromVp(argc, vp);
5520 int32_t id = 0;
5521 if (args.get(0).isInt32()) {
5522 id = args[0].toInt32();
5523 if (id < 0) {
5524 JS_ReportErrorASCII(cx, "id out of range");
5525 return false;
5528 CustomSerializableObject::Behavior behavior =
5529 CustomSerializableObject::Behavior::Nothing;
5530 if (args.get(1).isInt32()) {
5531 int32_t iv = args[1].toInt32();
5532 constexpr int32_t min =
5533 static_cast<int32_t>(CustomSerializableObject::Behavior::Nothing);
5534 constexpr int32_t max = static_cast<int32_t>(
5535 CustomSerializableObject::Behavior::FailDuringRead);
5536 if (iv < min || iv > max) {
5537 JS_ReportErrorASCII(cx, "behavior out of range");
5538 return false;
5540 behavior = static_cast<CustomSerializableObject::Behavior>(iv);
5543 JSObject* obj = CustomSerializableObject::Create(cx, id, behavior);
5544 if (!obj) {
5545 return false;
5548 args.rval().setObject(*obj);
5549 return true;
5552 static JSStructuredCloneCallbacks gCloneCallbacks = {
5553 .read = CustomSerializableObject::Read,
5554 .write = CustomSerializableObject::Write,
5555 .reportError = nullptr,
5556 .readTransfer = CustomSerializableObject::ReadTransfer,
5557 .writeTransfer = CustomSerializableObject::WriteTransfer,
5558 .freeTransfer = CustomSerializableObject::FreeTransfer,
5559 .canTransfer = CustomSerializableObject::CanTransfer,
5560 .sabCloned = nullptr};
5562 bool js::testingFunc_serialize(JSContext* cx, unsigned argc, Value* vp) {
5563 CallArgs args = CallArgsFromVp(argc, vp);
5565 if (js::SupportDifferentialTesting()) {
5566 RootedObject callee(cx, &args.callee());
5567 ReportUsageErrorASCII(cx, callee,
5568 "Function unavailable in differential testing mode.");
5569 return false;
5572 mozilla::Maybe<JSAutoStructuredCloneBuffer> clonebuf;
5573 JS::CloneDataPolicy policy;
5575 if (!args.get(2).isUndefined()) {
5576 RootedObject opts(cx, ToObject(cx, args.get(2)));
5577 if (!opts) {
5578 return false;
5581 RootedValue v(cx);
5582 if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v)) {
5583 return false;
5586 if (!v.isUndefined()) {
5587 JSString* str = JS::ToString(cx, v);
5588 if (!str) {
5589 return false;
5591 JSLinearString* poli = str->ensureLinear(cx);
5592 if (!poli) {
5593 return false;
5596 if (StringEqualsLiteral(poli, "allow")) {
5597 policy.allowSharedMemoryObjects();
5598 policy.allowIntraClusterClonableSharedObjects();
5599 } else if (StringEqualsLiteral(poli, "deny")) {
5600 // default
5601 } else {
5602 JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'");
5603 return false;
5607 if (!JS_GetProperty(cx, opts, "scope", &v)) {
5608 return false;
5611 if (!v.isUndefined()) {
5612 RootedString str(cx, JS::ToString(cx, v));
5613 if (!str) {
5614 return false;
5616 auto scope = ParseCloneScope(cx, str);
5617 if (!scope) {
5618 JS_ReportErrorASCII(cx, "Invalid structured clone scope");
5619 return false;
5621 clonebuf.emplace(*scope, &gCloneCallbacks, nullptr);
5625 if (!clonebuf) {
5626 clonebuf.emplace(JS::StructuredCloneScope::SameProcess, &gCloneCallbacks,
5627 nullptr);
5630 if (!clonebuf->write(cx, args.get(0), args.get(1), policy)) {
5631 return false;
5634 RootedObject obj(cx, CloneBufferObject::Create(cx, clonebuf.ptr()));
5635 if (!obj) {
5636 return false;
5639 args.rval().setObject(*obj);
5640 return true;
5643 static bool Deserialize(JSContext* cx, unsigned argc, Value* vp) {
5644 CallArgs args = CallArgsFromVp(argc, vp);
5646 if (js::SupportDifferentialTesting()) {
5647 RootedObject callee(cx, &args.callee());
5648 ReportUsageErrorASCII(cx, callee,
5649 "Function unavailable in differential testing mode.");
5650 return false;
5653 if (!args.get(0).isObject() || !args[0].toObject().is<CloneBufferObject>()) {
5654 JS_ReportErrorASCII(cx, "deserialize requires a clonebuffer argument");
5655 return false;
5657 Rooted<CloneBufferObject*> obj(cx,
5658 &args[0].toObject().as<CloneBufferObject>());
5660 JS::CloneDataPolicy policy;
5662 JS::StructuredCloneScope scope =
5663 obj->isSynthetic() ? JS::StructuredCloneScope::DifferentProcess
5664 : JS::StructuredCloneScope::SameProcess;
5665 if (args.get(1).isObject()) {
5666 RootedObject opts(cx, &args[1].toObject());
5667 if (!opts) {
5668 return false;
5671 RootedValue v(cx);
5672 if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v)) {
5673 return false;
5676 if (!v.isUndefined()) {
5677 JSString* str = JS::ToString(cx, v);
5678 if (!str) {
5679 return false;
5681 JSLinearString* poli = str->ensureLinear(cx);
5682 if (!poli) {
5683 return false;
5686 if (StringEqualsLiteral(poli, "allow")) {
5687 policy.allowSharedMemoryObjects();
5688 policy.allowIntraClusterClonableSharedObjects();
5689 } else if (StringEqualsLiteral(poli, "deny")) {
5690 // default
5691 } else {
5692 JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'");
5693 return false;
5697 if (!JS_GetProperty(cx, opts, "scope", &v)) {
5698 return false;
5701 if (!v.isUndefined()) {
5702 RootedString str(cx, JS::ToString(cx, v));
5703 if (!str) {
5704 return false;
5706 auto maybeScope = ParseCloneScope(cx, str);
5707 if (!maybeScope) {
5708 JS_ReportErrorASCII(cx, "Invalid structured clone scope");
5709 return false;
5712 if (*maybeScope < scope) {
5713 JS_ReportErrorASCII(cx,
5714 "Cannot use less restrictive scope "
5715 "than the deserialized clone buffer's scope");
5716 return false;
5719 scope = *maybeScope;
5723 // Clone buffer was already consumed?
5724 if (!obj->data()) {
5725 JS_ReportErrorASCII(cx,
5726 "deserialize given invalid clone buffer "
5727 "(transferables already consumed?)");
5728 return false;
5731 bool hasTransferable;
5732 if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable)) {
5733 return false;
5736 RootedValue deserialized(cx);
5737 if (!JS_ReadStructuredClone(cx, *obj->data(), JS_STRUCTURED_CLONE_VERSION,
5738 scope, &deserialized, policy, &gCloneCallbacks,
5739 nullptr)) {
5740 return false;
5742 args.rval().set(deserialized);
5744 // Consume any clone buffer with transferables; throw an error if it is
5745 // deserialized again.
5746 if (hasTransferable) {
5747 obj->discard();
5750 return true;
5753 static bool DetachArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
5754 CallArgs args = CallArgsFromVp(argc, vp);
5756 if (args.length() != 1) {
5757 JS_ReportErrorASCII(cx, "detachArrayBuffer() requires a single argument");
5758 return false;
5761 if (!args[0].isObject()) {
5762 JS_ReportErrorASCII(cx, "detachArrayBuffer must be passed an object");
5763 return false;
5766 RootedObject obj(cx, &args[0].toObject());
5767 if (!JS::DetachArrayBuffer(cx, obj)) {
5768 return false;
5771 args.rval().setUndefined();
5772 return true;
5775 static bool EnsureNonInline(JSContext* cx, unsigned argc, Value* vp) {
5776 CallArgs args = CallArgsFromVp(argc, vp);
5778 if (args.length() != 1) {
5779 JS_ReportErrorASCII(cx, "ensureNonInline() requires a single argument");
5780 return false;
5783 if (!args[0].isObject()) {
5784 JS_ReportErrorASCII(cx, "ensureNonInline must be passed an object");
5785 return false;
5788 RootedObject obj(cx, &args[0].toObject());
5789 if (!JS::EnsureNonInlineArrayBufferOrView(cx, obj)) {
5790 return false;
5793 args.rval().setUndefined();
5794 return true;
5797 static bool JSONStringify(JSContext* cx, unsigned argc, Value* vp) {
5798 CallArgs args = CallArgsFromVp(argc, vp);
5800 RootedValue value(cx, args.get(0));
5801 RootedValue behaviorVal(cx, args.get(1));
5802 StringifyBehavior behavior = StringifyBehavior::Normal;
5803 if (behaviorVal.isString()) {
5804 bool matches;
5805 #define MATCH(name) \
5806 if (!JS_StringEqualsLiteral(cx, behaviorVal.toString(), #name, &matches)) { \
5807 return false; \
5809 if (matches) { \
5810 behavior = StringifyBehavior::name; \
5812 MATCH(Normal)
5813 MATCH(FastOnly)
5814 MATCH(SlowOnly)
5815 MATCH(Compare)
5816 #undef MATCH
5819 JSStringBuilder sb(cx);
5820 if (!Stringify(cx, &value, nullptr, UndefinedValue(), sb, behavior)) {
5821 return false;
5824 if (!sb.empty()) {
5825 JSString* str = sb.finishString();
5826 if (!str) {
5827 return false;
5829 args.rval().setString(str);
5830 } else {
5831 args.rval().setUndefined();
5834 return true;
5837 static bool HelperThreadCount(JSContext* cx, unsigned argc, Value* vp) {
5838 CallArgs args = CallArgsFromVp(argc, vp);
5840 if (js::SupportDifferentialTesting()) {
5841 // Always return 0 to get consistent output with and without --no-threads.
5842 args.rval().setInt32(0);
5843 return true;
5846 if (CanUseExtraThreads()) {
5847 args.rval().setInt32(GetHelperThreadCount());
5848 } else {
5849 args.rval().setInt32(0);
5851 return true;
5854 static bool EnableShapeConsistencyChecks(JSContext* cx, unsigned argc,
5855 Value* vp) {
5856 CallArgs args = CallArgsFromVp(argc, vp);
5857 #ifdef DEBUG
5858 NativeObject::enableShapeConsistencyChecks();
5859 #endif
5860 args.rval().setUndefined();
5861 return true;
5864 // ShapeSnapshot holds information about an object's properties. This is used
5865 // for checking object and shape changes between two points in time.
5866 class ShapeSnapshot {
5867 HeapPtr<JSObject*> object_;
5868 HeapPtr<Shape*> shape_;
5869 HeapPtr<BaseShape*> baseShape_;
5870 ObjectFlags objectFlags_;
5872 GCVector<HeapPtr<Value>, 8> slots_;
5874 struct PropertySnapshot {
5875 HeapPtr<PropMap*> propMap;
5876 uint32_t propMapIndex;
5877 HeapPtr<PropertyKey> key;
5878 PropertyInfo prop;
5880 explicit PropertySnapshot(PropMap* map, uint32_t index)
5881 : propMap(map),
5882 propMapIndex(index),
5883 key(map->getKey(index)),
5884 prop(map->getPropertyInfo(index)) {}
5886 void trace(JSTracer* trc) {
5887 TraceEdge(trc, &propMap, "propMap");
5888 TraceEdge(trc, &key, "key");
5891 bool operator==(const PropertySnapshot& other) const {
5892 return propMap == other.propMap && propMapIndex == other.propMapIndex &&
5893 key == other.key && prop == other.prop;
5895 bool operator!=(const PropertySnapshot& other) const {
5896 return !operator==(other);
5899 GCVector<PropertySnapshot, 8> properties_;
5901 public:
5902 explicit ShapeSnapshot(JSContext* cx) : slots_(cx), properties_(cx) {}
5903 void checkSelf(JSContext* cx) const;
5904 void check(JSContext* cx, const ShapeSnapshot& other) const;
5905 bool init(JSObject* obj);
5906 void trace(JSTracer* trc);
5908 JSObject* object() const { return object_; }
5911 // A JSObject that holds a ShapeSnapshot.
5912 class ShapeSnapshotObject : public NativeObject {
5913 static constexpr size_t SnapshotSlot = 0;
5914 static constexpr size_t ReservedSlots = 1;
5916 public:
5917 static const JSClassOps classOps_;
5918 static const JSClass class_;
5920 bool hasSnapshot() const {
5921 // The snapshot may not be present yet if we GC during initialization.
5922 return !getReservedSlot(SnapshotSlot).isUndefined();
5925 ShapeSnapshot& snapshot() const {
5926 void* ptr = getReservedSlot(SnapshotSlot).toPrivate();
5927 MOZ_ASSERT(ptr);
5928 return *static_cast<ShapeSnapshot*>(ptr);
5931 static ShapeSnapshotObject* create(JSContext* cx, HandleObject obj);
5933 static void finalize(JS::GCContext* gcx, JSObject* obj) {
5934 if (obj->as<ShapeSnapshotObject>().hasSnapshot()) {
5935 js_delete(&obj->as<ShapeSnapshotObject>().snapshot());
5938 static void trace(JSTracer* trc, JSObject* obj) {
5939 if (obj->as<ShapeSnapshotObject>().hasSnapshot()) {
5940 obj->as<ShapeSnapshotObject>().snapshot().trace(trc);
5945 /*static */ const JSClassOps ShapeSnapshotObject::classOps_ = {
5946 nullptr, // addProperty
5947 nullptr, // delProperty
5948 nullptr, // enumerate
5949 nullptr, // newEnumerate
5950 nullptr, // resolve
5951 nullptr, // mayResolve
5952 ShapeSnapshotObject::finalize, // finalize
5953 nullptr, // call
5954 nullptr, // construct
5955 ShapeSnapshotObject::trace, // trace
5958 /*static */ const JSClass ShapeSnapshotObject::class_ = {
5959 "ShapeSnapshotObject",
5960 JSCLASS_HAS_RESERVED_SLOTS(ShapeSnapshotObject::ReservedSlots) |
5961 JSCLASS_BACKGROUND_FINALIZE,
5962 &ShapeSnapshotObject::classOps_};
5964 bool ShapeSnapshot::init(JSObject* obj) {
5965 object_ = obj;
5966 shape_ = obj->shape();
5967 baseShape_ = shape_->base();
5968 objectFlags_ = shape_->objectFlags();
5970 if (obj->is<NativeObject>()) {
5971 NativeObject* nobj = &obj->as<NativeObject>();
5973 // Snapshot the slot values.
5974 size_t slotSpan = nobj->slotSpan();
5975 if (!slots_.growBy(slotSpan)) {
5976 return false;
5978 for (size_t i = 0; i < slotSpan; i++) {
5979 slots_[i] = nobj->getSlot(i);
5982 // Snapshot property information.
5983 if (uint32_t len = nobj->shape()->propMapLength(); len > 0) {
5984 PropMap* map = nobj->shape()->propMap();
5985 while (true) {
5986 for (uint32_t i = 0; i < len; i++) {
5987 if (!map->hasKey(i)) {
5988 continue;
5990 if (!properties_.append(PropertySnapshot(map, i))) {
5991 return false;
5994 if (!map->hasPrevious()) {
5995 break;
5997 map = map->asLinked()->previous();
5998 len = PropMap::Capacity;
6003 return true;
6006 void ShapeSnapshot::trace(JSTracer* trc) {
6007 TraceEdge(trc, &object_, "object");
6008 TraceEdge(trc, &shape_, "shape");
6009 TraceEdge(trc, &baseShape_, "baseShape");
6010 slots_.trace(trc);
6011 properties_.trace(trc);
6014 void ShapeSnapshot::checkSelf(JSContext* cx) const {
6015 // Assertions based on a single snapshot.
6017 // Non-dictionary shapes must not be mutated.
6018 if (!shape_->isDictionary()) {
6019 MOZ_RELEASE_ASSERT(shape_->base() == baseShape_);
6020 MOZ_RELEASE_ASSERT(shape_->objectFlags() == objectFlags_);
6023 for (const PropertySnapshot& propSnapshot : properties_) {
6024 PropMap* propMap = propSnapshot.propMap;
6025 uint32_t propMapIndex = propSnapshot.propMapIndex;
6026 PropertyInfo prop = propSnapshot.prop;
6028 // Skip if the map no longer matches the snapshotted data. This can
6029 // only happen for dictionary maps because they can be mutated or compacted
6030 // after a shape change.
6031 if (!propMap->hasKey(propMapIndex) ||
6032 PropertySnapshot(propMap, propMapIndex) != propSnapshot) {
6033 MOZ_RELEASE_ASSERT(propMap->isDictionary());
6034 MOZ_RELEASE_ASSERT(object_->shape() != shape_);
6035 continue;
6038 // Ensure ObjectFlags depending on property information are set if needed.
6039 ObjectFlags expectedFlags = GetObjectFlagsForNewProperty(
6040 shape_->getObjectClass(), shape_->objectFlags(), propSnapshot.key,
6041 prop.flags(), cx);
6042 MOZ_RELEASE_ASSERT(expectedFlags == objectFlags_);
6044 // Accessors must have a PrivateGCThingValue(GetterSetter*) slot value.
6045 if (prop.isAccessorProperty()) {
6046 Value slotVal = slots_[prop.slot()];
6047 MOZ_RELEASE_ASSERT(slotVal.isPrivateGCThing());
6048 MOZ_RELEASE_ASSERT(slotVal.toGCThing()->is<GetterSetter>());
6051 // Data properties must not have a PrivateGCThingValue slot value.
6052 if (prop.isDataProperty()) {
6053 Value slotVal = slots_[prop.slot()];
6054 MOZ_RELEASE_ASSERT(!slotVal.isPrivateGCThing());
6059 void ShapeSnapshot::check(JSContext* cx, const ShapeSnapshot& later) const {
6060 checkSelf(cx);
6061 later.checkSelf(cx);
6063 if (object_ != later.object_) {
6064 // Snapshots are for different objects. Assert dictionary shapes aren't
6065 // shared.
6066 if (object_->is<NativeObject>()) {
6067 NativeObject* nobj = &object_->as<NativeObject>();
6068 if (nobj->inDictionaryMode()) {
6069 MOZ_RELEASE_ASSERT(shape_ != later.shape_);
6072 return;
6075 // We have two snapshots for the same object. Check the shape information
6076 // wasn't changed in invalid ways.
6078 // If the Shape is still the same, the object must have the same BaseShape,
6079 // ObjectFlags and property information.
6080 if (shape_ == later.shape_) {
6081 MOZ_RELEASE_ASSERT(objectFlags_ == later.objectFlags_);
6082 MOZ_RELEASE_ASSERT(baseShape_ == later.baseShape_);
6083 MOZ_RELEASE_ASSERT(slots_.length() == later.slots_.length());
6084 MOZ_RELEASE_ASSERT(properties_.length() == later.properties_.length());
6086 for (size_t i = 0; i < properties_.length(); i++) {
6087 MOZ_RELEASE_ASSERT(properties_[i] == later.properties_[i]);
6088 // Non-configurable accessor properties and non-configurable, non-writable
6089 // data properties shouldn't have had their slot mutated.
6090 PropertyInfo prop = properties_[i].prop;
6091 if (!prop.configurable()) {
6092 if (prop.isAccessorProperty() ||
6093 (prop.isDataProperty() && !prop.writable())) {
6094 size_t slot = prop.slot();
6095 MOZ_RELEASE_ASSERT(slots_[slot] == later.slots_[slot]);
6101 // Object flags should not be lost. The exception is the Indexed flag, it
6102 // can be cleared when densifying elements, so clear that flag first.
6104 ObjectFlags flags = objectFlags_;
6105 ObjectFlags flagsLater = later.objectFlags_;
6106 flags.clearFlag(ObjectFlag::Indexed);
6107 flagsLater.clearFlag(ObjectFlag::Indexed);
6108 MOZ_RELEASE_ASSERT((flags.toRaw() & flagsLater.toRaw()) == flags.toRaw());
6111 // If the HadGetterSetterChange flag wasn't set, all GetterSetter slots must
6112 // be unchanged.
6113 if (!later.objectFlags_.hasFlag(ObjectFlag::HadGetterSetterChange)) {
6114 for (size_t i = 0; i < slots_.length(); i++) {
6115 if (slots_[i].isPrivateGCThing() &&
6116 slots_[i].toGCThing()->is<GetterSetter>()) {
6117 MOZ_RELEASE_ASSERT(i < later.slots_.length());
6118 MOZ_RELEASE_ASSERT(later.slots_[i] == slots_[i]);
6124 // static
6125 ShapeSnapshotObject* ShapeSnapshotObject::create(JSContext* cx,
6126 HandleObject obj) {
6127 Rooted<UniquePtr<ShapeSnapshot>> snapshot(cx,
6128 cx->make_unique<ShapeSnapshot>(cx));
6129 if (!snapshot || !snapshot->init(obj)) {
6130 return nullptr;
6133 auto* snapshotObj = NewObjectWithGivenProto<ShapeSnapshotObject>(cx, nullptr);
6134 if (!snapshotObj) {
6135 return nullptr;
6137 snapshotObj->initReservedSlot(SnapshotSlot, PrivateValue(snapshot.release()));
6138 return snapshotObj;
6141 static bool CreateShapeSnapshot(JSContext* cx, unsigned argc, Value* vp) {
6142 CallArgs args = CallArgsFromVp(argc, vp);
6144 if (!args.get(0).isObject()) {
6145 JS_ReportErrorASCII(cx, "createShapeSnapshot requires an object argument");
6146 return false;
6149 RootedObject obj(cx, &args[0].toObject());
6150 auto* res = ShapeSnapshotObject::create(cx, obj);
6151 if (!res) {
6152 return false;
6155 res->snapshot().check(cx, res->snapshot());
6157 args.rval().setObject(*res);
6158 return true;
6161 static bool CheckShapeSnapshot(JSContext* cx, unsigned argc, Value* vp) {
6162 CallArgs args = CallArgsFromVp(argc, vp);
6164 if (!args.get(0).isObject() ||
6165 !args[0].toObject().is<ShapeSnapshotObject>()) {
6166 JS_ReportErrorASCII(cx, "checkShapeSnapshot requires a snapshot argument");
6167 return false;
6170 // Get the object to use from the snapshot if the second argument is not an
6171 // object.
6172 RootedObject obj(cx);
6173 if (args.get(1).isObject()) {
6174 obj = &args[1].toObject();
6175 } else {
6176 auto& snapshot = args[0].toObject().as<ShapeSnapshotObject>().snapshot();
6177 obj = snapshot.object();
6180 RootedObject otherSnapshot(cx, ShapeSnapshotObject::create(cx, obj));
6181 if (!otherSnapshot) {
6182 return false;
6185 auto& snapshot1 = args[0].toObject().as<ShapeSnapshotObject>().snapshot();
6186 auto& snapshot2 = otherSnapshot->as<ShapeSnapshotObject>().snapshot();
6187 snapshot1.check(cx, snapshot2);
6189 args.rval().setUndefined();
6190 return true;
6193 #if defined(DEBUG) || defined(JS_JITSPEW)
6194 static bool DumpObject(JSContext* cx, unsigned argc, Value* vp) {
6195 CallArgs args = CallArgsFromVp(argc, vp);
6196 RootedObject obj(cx, ToObject(cx, args.get(0)));
6197 if (!obj) {
6198 return false;
6201 DumpObject(obj);
6203 args.rval().setUndefined();
6204 return true;
6206 #endif
6208 static bool SharedMemoryEnabled(JSContext* cx, unsigned argc, Value* vp) {
6209 CallArgs args = CallArgsFromVp(argc, vp);
6210 args.rval().setBoolean(
6211 cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
6212 return true;
6215 static bool SharedArrayRawBufferRefcount(JSContext* cx, unsigned argc,
6216 Value* vp) {
6217 CallArgs args = CallArgsFromVp(argc, vp);
6218 if (args.length() != 1 || !args[0].isObject()) {
6219 JS_ReportErrorASCII(cx, "Expected SharedArrayBuffer object");
6220 return false;
6222 RootedObject obj(cx, &args[0].toObject());
6223 if (!obj->is<SharedArrayBufferObject>()) {
6224 JS_ReportErrorASCII(cx, "Expected SharedArrayBuffer object");
6225 return false;
6227 args.rval().setInt32(
6228 obj->as<SharedArrayBufferObject>().rawBufferObject()->refcount());
6229 return true;
6232 #ifdef NIGHTLY_BUILD
6233 static bool ObjectAddress(JSContext* cx, unsigned argc, Value* vp) {
6234 CallArgs args = CallArgsFromVp(argc, vp);
6236 if (js::SupportDifferentialTesting()) {
6237 RootedObject callee(cx, &args.callee());
6238 ReportUsageErrorASCII(cx, callee,
6239 "Function unavailable in differential testing mode.");
6240 return false;
6243 if (args.length() != 1) {
6244 RootedObject callee(cx, &args.callee());
6245 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
6246 return false;
6248 if (!args[0].isObject()) {
6249 RootedObject callee(cx, &args.callee());
6250 ReportUsageErrorASCII(cx, callee, "Expected object");
6251 return false;
6254 void* ptr = js::UncheckedUnwrap(&args[0].toObject(), true);
6255 char buffer[64];
6256 SprintfLiteral(buffer, "%p", ptr);
6258 return ReturnStringCopy(cx, args, buffer);
6261 static bool SharedAddress(JSContext* cx, unsigned argc, Value* vp) {
6262 CallArgs args = CallArgsFromVp(argc, vp);
6264 if (js::SupportDifferentialTesting()) {
6265 RootedObject callee(cx, &args.callee());
6266 ReportUsageErrorASCII(cx, callee,
6267 "Function unavailable in differential testing mode.");
6268 return false;
6271 if (args.length() != 1) {
6272 RootedObject callee(cx, &args.callee());
6273 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
6274 return false;
6276 if (!args[0].isObject()) {
6277 RootedObject callee(cx, &args.callee());
6278 ReportUsageErrorASCII(cx, callee, "Expected object");
6279 return false;
6282 RootedObject obj(cx, CheckedUnwrapStatic(&args[0].toObject()));
6283 if (!obj) {
6284 ReportAccessDenied(cx);
6285 return false;
6287 if (!obj->is<SharedArrayBufferObject>()) {
6288 JS_ReportErrorASCII(cx, "Argument must be a SharedArrayBuffer");
6289 return false;
6291 char buffer[64];
6292 uint32_t nchar = SprintfLiteral(
6293 buffer, "%p",
6294 obj->as<SharedArrayBufferObject>().dataPointerShared().unwrap(
6295 /*safeish*/));
6297 JSString* str = JS_NewStringCopyN(cx, buffer, nchar);
6298 if (!str) {
6299 return false;
6302 args.rval().setString(str);
6304 return true;
6306 #endif
6308 static bool HasInvalidatedTeleporting(JSContext* cx, unsigned argc, Value* vp) {
6309 CallArgs args = CallArgsFromVp(argc, vp);
6311 if (args.length() != 1 || !args[0].isObject()) {
6312 RootedObject callee(cx, &args.callee());
6313 ReportUsageErrorASCII(cx, callee, "Expected single object argument");
6314 return false;
6317 args.rval().setBoolean(args[0].toObject().hasInvalidatedTeleporting());
6318 return true;
6321 static bool DumpBacktrace(JSContext* cx, unsigned argc, Value* vp) {
6322 CallArgs args = CallArgsFromVp(argc, vp);
6323 DumpBacktrace(cx);
6324 args.rval().setUndefined();
6325 return true;
6328 static bool GetBacktrace(JSContext* cx, unsigned argc, Value* vp) {
6329 CallArgs args = CallArgsFromVp(argc, vp);
6331 bool showArgs = false;
6332 bool showLocals = false;
6333 bool showThisProps = false;
6335 if (args.length() > 1) {
6336 RootedObject callee(cx, &args.callee());
6337 ReportUsageErrorASCII(cx, callee, "Too many arguments");
6338 return false;
6341 if (args.length() == 1) {
6342 RootedObject cfg(cx, ToObject(cx, args[0]));
6343 if (!cfg) {
6344 return false;
6346 RootedValue v(cx);
6348 if (!JS_GetProperty(cx, cfg, "args", &v)) {
6349 return false;
6351 showArgs = ToBoolean(v);
6353 if (!JS_GetProperty(cx, cfg, "locals", &v)) {
6354 return false;
6356 showLocals = ToBoolean(v);
6358 if (!JS_GetProperty(cx, cfg, "thisprops", &v)) {
6359 return false;
6361 showThisProps = ToBoolean(v);
6364 JS::UniqueChars buf =
6365 JS::FormatStackDump(cx, showArgs, showLocals, showThisProps);
6366 if (!buf) {
6367 return false;
6370 size_t len;
6371 UniqueTwoByteChars ucbuf(JS::LossyUTF8CharsToNewTwoByteCharsZ(
6372 cx, JS::UTF8Chars(buf.get(), strlen(buf.get())),
6373 &len, js::MallocArena)
6374 .get());
6375 if (!ucbuf) {
6376 return false;
6378 JSString* str = JS_NewUCStringCopyN(cx, ucbuf.get(), len);
6379 if (!str) {
6380 return false;
6383 args.rval().setString(str);
6384 return true;
6387 static bool ReportOutOfMemory(JSContext* cx, unsigned argc, Value* vp) {
6388 CallArgs args = CallArgsFromVp(argc, vp);
6389 JS_ReportOutOfMemory(cx);
6390 cx->clearPendingException();
6391 args.rval().setUndefined();
6392 return true;
6395 static bool ThrowOutOfMemory(JSContext* cx, unsigned argc, Value* vp) {
6396 JS_ReportOutOfMemory(cx);
6397 return false;
6400 static bool ReportLargeAllocationFailure(JSContext* cx, unsigned argc,
6401 Value* vp) {
6402 CallArgs args = CallArgsFromVp(argc, vp);
6404 size_t bytes = JSRuntime::LARGE_ALLOCATION;
6405 if (args.length() >= 1) {
6406 if (!args[0].isInt32()) {
6407 RootedObject callee(cx, &args.callee());
6408 ReportUsageErrorASCII(cx, callee,
6409 "First argument must be an integer if specified.");
6410 return false;
6412 bytes = args[0].toInt32();
6415 void* buf = cx->runtime()->onOutOfMemoryCanGC(AllocFunction::Malloc,
6416 js::MallocArena, bytes);
6418 js_free(buf);
6419 args.rval().setUndefined();
6420 return true;
6423 namespace heaptools {
6425 using EdgeName = UniqueTwoByteChars;
6427 // An edge to a node from its predecessor in a path through the graph.
6428 class BackEdge {
6429 // The node from which this edge starts.
6430 JS::ubi::Node predecessor_;
6432 // The name of this edge.
6433 EdgeName name_;
6435 public:
6436 BackEdge() : name_(nullptr) {}
6437 // Construct an initialized back edge, taking ownership of |name|.
6438 BackEdge(JS::ubi::Node predecessor, EdgeName name)
6439 : predecessor_(predecessor), name_(std::move(name)) {}
6440 BackEdge(BackEdge&& rhs)
6441 : predecessor_(rhs.predecessor_), name_(std::move(rhs.name_)) {}
6442 BackEdge& operator=(BackEdge&& rhs) {
6443 MOZ_ASSERT(&rhs != this);
6444 this->~BackEdge();
6445 new (this) BackEdge(std::move(rhs));
6446 return *this;
6449 EdgeName forgetName() { return std::move(name_); }
6450 JS::ubi::Node predecessor() const { return predecessor_; }
6452 private:
6453 // No copy constructor or copying assignment.
6454 BackEdge(const BackEdge&) = delete;
6455 BackEdge& operator=(const BackEdge&) = delete;
6458 // A path-finding handler class for use with JS::ubi::BreadthFirst.
6459 struct FindPathHandler {
6460 using NodeData = BackEdge;
6461 using Traversal = JS::ubi::BreadthFirst<FindPathHandler>;
6463 FindPathHandler(JSContext* cx, JS::ubi::Node start, JS::ubi::Node target,
6464 MutableHandle<GCVector<Value>> nodes, Vector<EdgeName>& edges)
6465 : cx(cx),
6466 start(start),
6467 target(target),
6468 foundPath(false),
6469 nodes(nodes),
6470 edges(edges) {}
6472 bool operator()(Traversal& traversal, JS::ubi::Node origin,
6473 const JS::ubi::Edge& edge, BackEdge* backEdge, bool first) {
6474 // We take care of each node the first time we visit it, so there's
6475 // nothing to be done on subsequent visits.
6476 if (!first) {
6477 return true;
6480 // Record how we reached this node. This is the last edge on a
6481 // shortest path to this node.
6482 EdgeName edgeName =
6483 DuplicateStringToArena(js::StringBufferArena, cx, edge.name.get());
6484 if (!edgeName) {
6485 return false;
6487 *backEdge = BackEdge(origin, std::move(edgeName));
6489 // Have we reached our final target node?
6490 if (edge.referent == target) {
6491 // Record the path that got us here, which must be a shortest path.
6492 if (!recordPath(traversal, backEdge)) {
6493 return false;
6495 foundPath = true;
6496 traversal.stop();
6499 return true;
6502 // We've found a path to our target. Walk the backlinks to produce the
6503 // (reversed) path, saving the path in |nodes| and |edges|. |nodes| is
6504 // rooted, so it can hold the path's nodes as we leave the scope of
6505 // the AutoCheckCannotGC. Note that nodes are added to |visited| after we
6506 // return from operator() so we have to pass the target BackEdge* to this
6507 // function.
6508 bool recordPath(Traversal& traversal, BackEdge* targetBackEdge) {
6509 JS::ubi::Node here = target;
6511 do {
6512 BackEdge* backEdge = targetBackEdge;
6513 if (here != target) {
6514 Traversal::NodeMap::Ptr p = traversal.visited.lookup(here);
6515 MOZ_ASSERT(p);
6516 backEdge = &p->value();
6518 JS::ubi::Node predecessor = backEdge->predecessor();
6519 if (!nodes.append(predecessor.exposeToJS()) ||
6520 !edges.append(backEdge->forgetName())) {
6521 return false;
6523 here = predecessor;
6524 } while (here != start);
6526 return true;
6529 JSContext* cx;
6531 // The node we're starting from.
6532 JS::ubi::Node start;
6534 // The node we're looking for.
6535 JS::ubi::Node target;
6537 // True if we found a path to target, false if we didn't.
6538 bool foundPath;
6540 // The nodes and edges of the path --- should we find one. The path is
6541 // stored in reverse order, because that's how it's easiest for us to
6542 // construct it:
6543 // - edges[i] is the name of the edge from nodes[i] to nodes[i-1].
6544 // - edges[0] is the name of the edge from nodes[0] to the target.
6545 // - The last node, nodes[n-1], is the start node.
6546 MutableHandle<GCVector<Value>> nodes;
6547 Vector<EdgeName>& edges;
6550 } // namespace heaptools
6552 static bool FindPath(JSContext* cx, unsigned argc, Value* vp) {
6553 CallArgs args = CallArgsFromVp(argc, vp);
6554 if (!args.requireAtLeast(cx, "findPath", 2)) {
6555 return false;
6558 // We don't ToString non-objects given as 'start' or 'target', because this
6559 // test is all about object identity, and ToString doesn't preserve that.
6560 // Non-GCThing endpoints don't make much sense.
6561 if (!args[0].isObject() && !args[0].isString() && !args[0].isSymbol()) {
6562 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
6563 nullptr, "not an object, string, or symbol");
6564 return false;
6567 if (!args[1].isObject() && !args[1].isString() && !args[1].isSymbol()) {
6568 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
6569 nullptr, "not an object, string, or symbol");
6570 return false;
6573 Rooted<GCVector<Value>> nodes(cx, GCVector<Value>(cx));
6574 Vector<heaptools::EdgeName> edges(cx);
6577 // We can't tolerate the GC moving things around while we're searching
6578 // the heap. Check that nothing we do causes a GC.
6579 JS::AutoCheckCannotGC autoCannotGC;
6581 JS::ubi::Node start(args[0]), target(args[1]);
6583 heaptools::FindPathHandler handler(cx, start, target, &nodes, edges);
6584 heaptools::FindPathHandler::Traversal traversal(cx, handler, autoCannotGC);
6585 if (!traversal.addStart(start)) {
6586 ReportOutOfMemory(cx);
6587 return false;
6590 if (!traversal.traverse()) {
6591 if (!cx->isExceptionPending()) {
6592 ReportOutOfMemory(cx);
6594 return false;
6597 if (!handler.foundPath) {
6598 // We didn't find any paths from the start to the target.
6599 args.rval().setUndefined();
6600 return true;
6604 // |nodes| and |edges| contain the path from |start| to |target|, reversed.
6605 // Construct a JavaScript array describing the path from the start to the
6606 // target. Each element has the form:
6608 // {
6609 // node: <object or string or symbol>,
6610 // edge: <string describing outgoing edge from node>
6611 // }
6613 // or, if the node is some internal thing that isn't a proper JavaScript
6614 // value:
6616 // { node: undefined, edge: <string> }
6617 size_t length = nodes.length();
6618 Rooted<ArrayObject*> result(cx, NewDenseFullyAllocatedArray(cx, length));
6619 if (!result) {
6620 return false;
6622 result->ensureDenseInitializedLength(0, length);
6624 // Walk |nodes| and |edges| in the stored order, and construct the result
6625 // array in start-to-target order.
6626 for (size_t i = 0; i < length; i++) {
6627 // Build an object describing the node and edge.
6628 RootedObject obj(cx, NewPlainObject(cx));
6629 if (!obj) {
6630 return false;
6633 // Only define the "node" property if we're not fuzzing, to prevent the
6634 // fuzzers from messing with internal objects that we don't want to expose
6635 // to arbitrary JS.
6636 if (!fuzzingSafe) {
6637 RootedValue wrapped(cx, nodes[i]);
6638 if (!cx->compartment()->wrap(cx, &wrapped)) {
6639 return false;
6642 if (!JS_DefineProperty(cx, obj, "node", wrapped, JSPROP_ENUMERATE)) {
6643 return false;
6647 heaptools::EdgeName edgeName = std::move(edges[i]);
6649 size_t edgeNameLength = js_strlen(edgeName.get());
6650 RootedString edgeStr(
6651 cx, NewString<CanGC>(cx, std::move(edgeName), edgeNameLength));
6652 if (!edgeStr) {
6653 return false;
6656 if (!JS_DefineProperty(cx, obj, "edge", edgeStr, JSPROP_ENUMERATE)) {
6657 return false;
6660 result->setDenseElement(length - i - 1, ObjectValue(*obj));
6663 args.rval().setObject(*result);
6664 return true;
6667 static bool ShortestPaths(JSContext* cx, unsigned argc, Value* vp) {
6668 CallArgs args = CallArgsFromVp(argc, vp);
6669 if (!args.requireAtLeast(cx, "shortestPaths", 1)) {
6670 return false;
6673 if (!args[0].isObject() || !args[0].toObject().is<ArrayObject>()) {
6674 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
6675 nullptr, "not an array object");
6676 return false;
6679 Rooted<ArrayObject*> objs(cx, &args[0].toObject().as<ArrayObject>());
6681 RootedValue start(cx, NullValue());
6682 int32_t maxNumPaths = 3;
6684 if (!args.get(1).isUndefined()) {
6685 if (!args[1].isObject()) {
6686 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[1],
6687 nullptr, "not an options object");
6688 return false;
6691 RootedObject options(cx, &args[1].toObject());
6692 bool exists;
6693 if (!JS_HasProperty(cx, options, "start", &exists)) {
6694 return false;
6696 if (exists) {
6697 if (!JS_GetProperty(cx, options, "start", &start)) {
6698 return false;
6701 // Non-GCThing endpoints don't make much sense.
6702 if (!start.isGCThing()) {
6703 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, start,
6704 nullptr, "not a GC thing");
6705 return false;
6709 RootedValue v(cx, Int32Value(maxNumPaths));
6710 if (!JS_HasProperty(cx, options, "maxNumPaths", &exists)) {
6711 return false;
6713 if (exists) {
6714 if (!JS_GetProperty(cx, options, "maxNumPaths", &v)) {
6715 return false;
6717 if (!JS::ToInt32(cx, v, &maxNumPaths)) {
6718 return false;
6721 if (maxNumPaths <= 0) {
6722 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, v,
6723 nullptr, "not greater than 0");
6724 return false;
6728 // Ensure we have at least one target.
6729 size_t length = objs->getDenseInitializedLength();
6730 if (length == 0) {
6731 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
6732 nullptr,
6733 "not a dense array object with one or more elements");
6734 return false;
6737 for (size_t i = 0; i < length; i++) {
6738 RootedValue el(cx, objs->getDenseElement(i));
6739 if (!el.isGCThing()) {
6740 JS_ReportErrorASCII(cx, "Each target must be a GC thing");
6741 return false;
6745 // We accumulate the results into a GC-stable form, due to the fact that the
6746 // JS::ubi::ShortestPaths lifetime (when operating on the live heap graph)
6747 // is bounded within an AutoCheckCannotGC.
6748 Rooted<GCVector<GCVector<GCVector<Value>>>> values(
6749 cx, GCVector<GCVector<GCVector<Value>>>(cx));
6750 Vector<Vector<Vector<JS::ubi::EdgeName>>> names(cx);
6753 JS::ubi::Node root;
6755 JS::ubi::RootList rootList(cx, true);
6756 if (start.isNull()) {
6757 auto [ok, nogc] = rootList.init();
6758 (void)nogc; // Old compilers get anxious about nogc being unused.
6759 if (!ok) {
6760 ReportOutOfMemory(cx);
6761 return false;
6763 root = JS::ubi::Node(&rootList);
6764 } else {
6765 root = JS::ubi::Node(start);
6767 JS::AutoCheckCannotGC noGC(cx);
6769 JS::ubi::NodeSet targets;
6771 for (size_t i = 0; i < length; i++) {
6772 RootedValue val(cx, objs->getDenseElement(i));
6773 JS::ubi::Node node(val);
6774 if (!targets.put(node)) {
6775 ReportOutOfMemory(cx);
6776 return false;
6780 auto maybeShortestPaths = JS::ubi::ShortestPaths::Create(
6781 cx, noGC, maxNumPaths, root, std::move(targets));
6782 if (maybeShortestPaths.isNothing()) {
6783 ReportOutOfMemory(cx);
6784 return false;
6786 auto& shortestPaths = *maybeShortestPaths;
6788 for (size_t i = 0; i < length; i++) {
6789 if (!values.append(GCVector<GCVector<Value>>(cx)) ||
6790 !names.append(Vector<Vector<JS::ubi::EdgeName>>(cx))) {
6791 return false;
6794 RootedValue val(cx, objs->getDenseElement(i));
6795 JS::ubi::Node target(val);
6797 bool ok = shortestPaths.forEachPath(target, [&](JS::ubi::Path& path) {
6798 Rooted<GCVector<Value>> pathVals(cx, GCVector<Value>(cx));
6799 Vector<JS::ubi::EdgeName> pathNames(cx);
6801 for (auto& part : path) {
6802 if (!pathVals.append(part->predecessor().exposeToJS()) ||
6803 !pathNames.append(std::move(part->name()))) {
6804 return false;
6808 return values.back().append(std::move(pathVals.get())) &&
6809 names.back().append(std::move(pathNames));
6812 if (!ok) {
6813 return false;
6818 MOZ_ASSERT(values.length() == names.length());
6819 MOZ_ASSERT(values.length() == length);
6821 Rooted<ArrayObject*> results(cx, NewDenseFullyAllocatedArray(cx, length));
6822 if (!results) {
6823 return false;
6825 results->ensureDenseInitializedLength(0, length);
6827 for (size_t i = 0; i < length; i++) {
6828 size_t numPaths = values[i].length();
6829 MOZ_ASSERT(names[i].length() == numPaths);
6831 Rooted<ArrayObject*> pathsArray(cx,
6832 NewDenseFullyAllocatedArray(cx, numPaths));
6833 if (!pathsArray) {
6834 return false;
6836 pathsArray->ensureDenseInitializedLength(0, numPaths);
6838 for (size_t j = 0; j < numPaths; j++) {
6839 size_t pathLength = values[i][j].length();
6840 MOZ_ASSERT(names[i][j].length() == pathLength);
6842 Rooted<ArrayObject*> path(cx,
6843 NewDenseFullyAllocatedArray(cx, pathLength));
6844 if (!path) {
6845 return false;
6847 path->ensureDenseInitializedLength(0, pathLength);
6849 for (size_t k = 0; k < pathLength; k++) {
6850 Rooted<PlainObject*> part(cx, NewPlainObject(cx));
6851 if (!part) {
6852 return false;
6855 RootedValue predecessor(cx, values[i][j][k]);
6856 if (!cx->compartment()->wrap(cx, &predecessor) ||
6857 !JS_DefineProperty(cx, part, "predecessor", predecessor,
6858 JSPROP_ENUMERATE)) {
6859 return false;
6862 if (names[i][j][k]) {
6863 RootedString edge(cx,
6864 NewStringCopyZ<CanGC>(cx, names[i][j][k].get()));
6865 if (!edge ||
6866 !JS_DefineProperty(cx, part, "edge", edge, JSPROP_ENUMERATE)) {
6867 return false;
6871 path->setDenseElement(k, ObjectValue(*part));
6874 pathsArray->setDenseElement(j, ObjectValue(*path));
6877 results->setDenseElement(i, ObjectValue(*pathsArray));
6880 args.rval().setObject(*results);
6881 return true;
6884 static bool EvalReturningScope(JSContext* cx, unsigned argc, Value* vp) {
6885 CallArgs args = CallArgsFromVp(argc, vp);
6886 if (!args.requireAtLeast(cx, "evalReturningScope", 1)) {
6887 return false;
6890 RootedString str(cx, ToString(cx, args[0]));
6891 if (!str) {
6892 return false;
6895 JS::AutoFilename filename;
6896 uint32_t lineno;
6898 JS::DescribeScriptedCaller(cx, &filename, &lineno);
6900 // CompileOption should be created in the target global's realm.
6901 RootedObject global(cx);
6902 Maybe<JS::CompileOptions> maybeOptions;
6903 if (args.hasDefined(1)) {
6904 global = ToObject(cx, args[1]);
6905 if (!global) {
6906 return false;
6909 global = CheckedUnwrapDynamic(global, cx, /* stopAtWindowProxy = */ false);
6910 if (!global) {
6911 JS_ReportErrorASCII(cx, "Permission denied to access global");
6912 return false;
6914 if (!global->is<GlobalObject>()) {
6915 JS_ReportErrorASCII(cx, "Argument must be a global object");
6916 return false;
6919 JSAutoRealm ar(cx, global);
6920 maybeOptions.emplace(cx);
6921 } else {
6922 global = JS::CurrentGlobalOrNull(cx);
6923 maybeOptions.emplace(cx);
6926 CompileOptions& options = maybeOptions.ref();
6927 options.setFileAndLine(filename.get(), lineno);
6928 options.setNoScriptRval(true);
6929 options.setNonSyntacticScope(true);
6931 AutoStableStringChars linearChars(cx);
6932 if (!linearChars.initTwoByte(cx, str)) {
6933 return false;
6935 JS::SourceText<char16_t> srcBuf;
6936 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
6937 return false;
6940 RootedObject varObj(cx);
6943 // ExecuteInFrameScriptEnvironment requires the script be in the same
6944 // realm as the global. The script compilation should be done after
6945 // switching globals.
6946 AutoRealm ar(cx, global);
6948 RootedScript script(cx, JS::Compile(cx, options, srcBuf));
6949 if (!script) {
6950 return false;
6953 JS::RootedObject obj(cx, JS_NewPlainObject(cx));
6954 if (!obj) {
6955 return false;
6958 RootedObject lexicalScope(cx);
6959 if (!js::ExecuteInFrameScriptEnvironment(cx, obj, script, &lexicalScope)) {
6960 return false;
6963 varObj = lexicalScope->enclosingEnvironment()->enclosingEnvironment();
6964 MOZ_ASSERT(varObj->is<NonSyntacticVariablesObject>());
6967 RootedValue varObjVal(cx, ObjectValue(*varObj));
6968 if (!cx->compartment()->wrap(cx, &varObjVal)) {
6969 return false;
6972 args.rval().set(varObjVal);
6973 return true;
6976 static bool ByteSize(JSContext* cx, unsigned argc, Value* vp) {
6977 CallArgs args = CallArgsFromVp(argc, vp);
6978 mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf;
6981 // We can't tolerate the GC moving things around while we're using a
6982 // ubi::Node. Check that nothing we do causes a GC.
6983 JS::AutoCheckCannotGC autoCannotGC;
6985 JS::ubi::Node node = args.get(0);
6986 if (node) {
6987 args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
6988 } else {
6989 args.rval().setUndefined();
6992 return true;
6995 static bool ByteSizeOfScript(JSContext* cx, unsigned argc, Value* vp) {
6996 CallArgs args = CallArgsFromVp(argc, vp);
6997 if (!args.requireAtLeast(cx, "byteSizeOfScript", 1)) {
6998 return false;
7000 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
7001 JS_ReportErrorASCII(cx, "Argument must be a Function object");
7002 return false;
7005 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
7006 if (fun->isNativeFun()) {
7007 JS_ReportErrorASCII(cx, "Argument must be a scripted function");
7008 return false;
7011 RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
7012 if (!script) {
7013 return false;
7016 mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf;
7019 // We can't tolerate the GC moving things around while we're using a
7020 // ubi::Node. Check that nothing we do causes a GC.
7021 JS::AutoCheckCannotGC autoCannotGC;
7023 JS::ubi::Node node = script;
7024 if (node) {
7025 args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
7026 } else {
7027 args.rval().setUndefined();
7030 return true;
7033 static bool SetImmutablePrototype(JSContext* cx, unsigned argc, Value* vp) {
7034 CallArgs args = CallArgsFromVp(argc, vp);
7035 if (!args.get(0).isObject()) {
7036 JS_ReportErrorASCII(cx, "setImmutablePrototype: object expected");
7037 return false;
7040 RootedObject obj(cx, &args[0].toObject());
7042 bool succeeded;
7043 if (!js::SetImmutablePrototype(cx, obj, &succeeded)) {
7044 return false;
7047 args.rval().setBoolean(succeeded);
7048 return true;
7051 #ifdef DEBUG
7052 static bool DumpStringRepresentation(JSContext* cx, unsigned argc, Value* vp) {
7053 CallArgs args = CallArgsFromVp(argc, vp);
7055 RootedString str(cx, ToString(cx, args.get(0)));
7056 if (!str) {
7057 return false;
7060 Fprinter out(stderr);
7061 str->dumpRepresentation(out, 0);
7063 args.rval().setUndefined();
7064 return true;
7067 static bool GetStringRepresentation(JSContext* cx, unsigned argc, Value* vp) {
7068 CallArgs args = CallArgsFromVp(argc, vp);
7070 RootedString str(cx, ToString(cx, args.get(0)));
7071 if (!str) {
7072 return false;
7075 JSSprinter out(cx);
7076 if (!out.init()) {
7077 return false;
7079 str->dumpRepresentation(out, 0);
7081 JSString* rep = out.release(cx);
7082 if (!rep) {
7083 return false;
7086 args.rval().setString(rep);
7087 return true;
7090 #endif
7092 static bool ParseCompileOptionsForModule(JSContext* cx,
7093 JS::CompileOptions& options,
7094 JS::Handle<JSObject*> opts,
7095 bool& isModule) {
7096 JS::Rooted<JS::Value> v(cx);
7098 if (!JS_GetProperty(cx, opts, "module", &v)) {
7099 return false;
7101 if (!v.isUndefined() && JS::ToBoolean(v)) {
7102 options.setModule();
7103 isModule = true;
7105 // js::ParseCompileOptions should already be called.
7106 if (options.lineno == 0) {
7107 JS_ReportErrorASCII(cx, "Module cannot be compiled with lineNumber == 0");
7108 return false;
7110 } else {
7111 isModule = false;
7114 return true;
7117 static bool ParseCompileOptionsForInstantiate(JSContext* cx,
7118 JS::CompileOptions& options,
7119 JS::Handle<JSObject*> opts,
7120 bool& prepareForInstantiate) {
7121 JS::Rooted<JS::Value> v(cx);
7123 if (!JS_GetProperty(cx, opts, "prepareForInstantiate", &v)) {
7124 return false;
7126 if (!v.isUndefined()) {
7127 prepareForInstantiate = JS::ToBoolean(v);
7128 } else {
7129 prepareForInstantiate = false;
7132 return true;
7135 static bool CompileToStencil(JSContext* cx, uint32_t argc, Value* vp) {
7136 CallArgs args = CallArgsFromVp(argc, vp);
7138 if (!args.requireAtLeast(cx, "compileToStencil", 1)) {
7139 return false;
7141 if (!args[0].isString()) {
7142 const char* typeName = InformalValueTypeName(args[0]);
7143 JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
7144 return false;
7147 RootedString src(cx, ToString<CanGC>(cx, args[0]));
7148 if (!src) {
7149 return false;
7152 /* Linearize the string to obtain a char16_t* range. */
7153 AutoStableStringChars linearChars(cx);
7154 if (!linearChars.initTwoByte(cx, src)) {
7155 return false;
7157 JS::SourceText<char16_t> srcBuf;
7158 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
7159 return false;
7162 CompileOptions options(cx);
7163 RootedString displayURL(cx);
7164 RootedString sourceMapURL(cx);
7165 UniqueChars fileNameBytes;
7166 bool isModule = false;
7167 bool prepareForInstantiate = false;
7168 if (args.length() == 2) {
7169 if (!args[1].isObject()) {
7170 JS_ReportErrorASCII(
7171 cx, "compileToStencil: The 2nd argument must be an object");
7172 return false;
7175 RootedObject opts(cx, &args[1].toObject());
7177 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
7178 return false;
7180 if (!ParseCompileOptionsForModule(cx, options, opts, isModule)) {
7181 return false;
7183 if (!ParseCompileOptionsForInstantiate(cx, options, opts,
7184 prepareForInstantiate)) {
7185 return false;
7187 if (!js::ParseSourceOptions(cx, opts, &displayURL, &sourceMapURL)) {
7188 return false;
7192 AutoReportFrontendContext fc(cx);
7193 RefPtr<JS::Stencil> stencil;
7194 JS::CompilationStorage compileStorage;
7195 if (isModule) {
7196 stencil =
7197 JS::CompileModuleScriptToStencil(&fc, options, srcBuf, compileStorage);
7198 } else {
7199 stencil =
7200 JS::CompileGlobalScriptToStencil(&fc, options, srcBuf, compileStorage);
7202 if (!stencil) {
7203 return false;
7206 if (!SetSourceOptions(cx, &fc, stencil->source, displayURL, sourceMapURL)) {
7207 return false;
7210 JS::InstantiationStorage storage;
7211 if (prepareForInstantiate) {
7212 if (!JS::PrepareForInstantiate(&fc, *stencil, storage)) {
7213 return false;
7217 Rooted<js::StencilObject*> stencilObj(
7218 cx, js::StencilObject::create(cx, std::move(stencil)));
7219 if (!stencilObj) {
7220 return false;
7223 args.rval().setObject(*stencilObj);
7224 return true;
7227 static bool EvalStencil(JSContext* cx, uint32_t argc, Value* vp) {
7228 CallArgs args = CallArgsFromVp(argc, vp);
7230 if (!args.requireAtLeast(cx, "evalStencil", 1)) {
7231 return false;
7234 /* Prepare the input byte array. */
7235 if (!args[0].isObject()) {
7236 JS_ReportErrorASCII(cx, "evalStencil: Stencil object expected");
7237 return false;
7239 Rooted<js::StencilObject*> stencilObj(
7240 cx, args[0].toObject().maybeUnwrapIf<js::StencilObject>());
7241 if (!stencilObj) {
7242 JS_ReportErrorASCII(cx, "evalStencil: Stencil object expected");
7243 return false;
7246 if (stencilObj->stencil()->isModule()) {
7247 JS_ReportErrorASCII(cx,
7248 "evalStencil: Module stencil cannot be evaluated. Use "
7249 "instantiateModuleStencil instead");
7250 return false;
7253 CompileOptions options(cx);
7254 UniqueChars fileNameBytes;
7255 Rooted<JS::Value> privateValue(cx);
7256 Rooted<JSString*> elementAttributeName(cx);
7257 if (args.length() == 2) {
7258 if (!args[1].isObject()) {
7259 JS_ReportErrorASCII(cx,
7260 "evalStencil: The 2nd argument must be an object");
7261 return false;
7264 RootedObject opts(cx, &args[1].toObject());
7266 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
7267 return false;
7269 if (!js::ParseDebugMetadata(cx, opts, &privateValue,
7270 &elementAttributeName)) {
7271 return false;
7275 bool useDebugMetadata = !privateValue.isUndefined() || elementAttributeName;
7277 JS::InstantiateOptions instantiateOptions(options);
7278 if (useDebugMetadata) {
7279 instantiateOptions.hideScriptFromDebugger = true;
7282 if (!js::ValidateLazinessOfStencilAndGlobal(cx, *stencilObj->stencil())) {
7283 return false;
7286 RootedScript script(cx, JS::InstantiateGlobalStencil(cx, instantiateOptions,
7287 stencilObj->stencil()));
7288 if (!script) {
7289 return false;
7292 if (useDebugMetadata) {
7293 instantiateOptions.hideScriptFromDebugger = false;
7294 if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, privateValue,
7295 elementAttributeName, nullptr, nullptr)) {
7296 return false;
7300 RootedValue retVal(cx);
7301 if (!JS_ExecuteScript(cx, script, &retVal)) {
7302 return false;
7305 args.rval().set(retVal);
7306 return true;
7309 static bool CompileToStencilXDR(JSContext* cx, uint32_t argc, Value* vp) {
7310 CallArgs args = CallArgsFromVp(argc, vp);
7312 if (!args.requireAtLeast(cx, "compileToStencilXDR", 1)) {
7313 return false;
7316 RootedString src(cx, ToString<CanGC>(cx, args[0]));
7317 if (!src) {
7318 return false;
7321 /* Linearize the string to obtain a char16_t* range. */
7322 AutoStableStringChars linearChars(cx);
7323 if (!linearChars.initTwoByte(cx, src)) {
7324 return false;
7326 JS::SourceText<char16_t> srcBuf;
7327 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
7328 return false;
7331 CompileOptions options(cx);
7332 RootedString displayURL(cx);
7333 RootedString sourceMapURL(cx);
7334 UniqueChars fileNameBytes;
7335 bool isModule = false;
7336 if (args.length() == 2) {
7337 if (!args[1].isObject()) {
7338 JS_ReportErrorASCII(
7339 cx, "compileToStencilXDR: The 2nd argument must be an object");
7340 return false;
7343 RootedObject opts(cx, &args[1].toObject());
7345 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
7346 return false;
7348 if (!ParseCompileOptionsForModule(cx, options, opts, isModule)) {
7349 return false;
7351 if (!js::ParseSourceOptions(cx, opts, &displayURL, &sourceMapURL)) {
7352 return false;
7356 /* Compile the script text to stencil. */
7357 AutoReportFrontendContext fc(cx);
7358 frontend::NoScopeBindingCache scopeCache;
7359 Rooted<frontend::CompilationInput> input(cx,
7360 frontend::CompilationInput(options));
7361 UniquePtr<frontend::ExtensibleCompilationStencil> stencil;
7362 if (isModule) {
7363 stencil = frontend::ParseModuleToExtensibleStencil(
7364 cx, &fc, cx->tempLifoAlloc(), input.get(), &scopeCache, srcBuf);
7365 } else {
7366 stencil = frontend::CompileGlobalScriptToExtensibleStencil(
7367 cx, &fc, input.get(), &scopeCache, srcBuf, ScopeKind::Global);
7369 if (!stencil) {
7370 return false;
7373 if (!SetSourceOptions(cx, &fc, stencil->source, displayURL, sourceMapURL)) {
7374 return false;
7377 /* Serialize the stencil to XDR. */
7378 JS::TranscodeBuffer xdrBytes;
7380 frontend::BorrowingCompilationStencil borrowingStencil(*stencil);
7381 bool succeeded = false;
7382 if (!borrowingStencil.serializeStencils(cx, input.get(), xdrBytes,
7383 &succeeded)) {
7384 return false;
7386 if (!succeeded) {
7387 fc.clearAutoReport();
7388 JS_ReportErrorASCII(cx, "Encoding failure");
7389 return false;
7393 Rooted<StencilXDRBufferObject*> xdrObj(
7395 StencilXDRBufferObject::create(cx, xdrBytes.begin(), xdrBytes.length()));
7396 if (!xdrObj) {
7397 return false;
7400 args.rval().setObject(*xdrObj);
7401 return true;
7404 static bool EvalStencilXDR(JSContext* cx, uint32_t argc, Value* vp) {
7405 CallArgs args = CallArgsFromVp(argc, vp);
7407 if (!args.requireAtLeast(cx, "evalStencilXDR", 1)) {
7408 return false;
7411 /* Prepare the input byte array. */
7412 if (!args[0].isObject()) {
7413 JS_ReportErrorASCII(cx, "evalStencilXDR: Stencil XDR object expected");
7414 return false;
7416 Rooted<StencilXDRBufferObject*> xdrObj(
7417 cx, args[0].toObject().maybeUnwrapIf<StencilXDRBufferObject>());
7418 if (!xdrObj) {
7419 JS_ReportErrorASCII(cx, "evalStencilXDR: Stencil XDR object expected");
7420 return false;
7422 MOZ_ASSERT(xdrObj->hasBuffer());
7424 CompileOptions options(cx);
7425 UniqueChars fileNameBytes;
7426 Rooted<JS::Value> privateValue(cx);
7427 Rooted<JSString*> elementAttributeName(cx);
7428 if (args.length() == 2) {
7429 if (!args[1].isObject()) {
7430 JS_ReportErrorASCII(cx,
7431 "evalStencilXDR: The 2nd argument must be an object");
7432 return false;
7435 RootedObject opts(cx, &args[1].toObject());
7437 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
7438 return false;
7440 if (!js::ParseDebugMetadata(cx, opts, &privateValue,
7441 &elementAttributeName)) {
7442 return false;
7446 /* Prepare the CompilationStencil for decoding. */
7447 AutoReportFrontendContext fc(cx);
7448 frontend::CompilationStencil stencil(nullptr);
7450 /* Deserialize the stencil from XDR. */
7451 JS::TranscodeRange xdrRange(xdrObj->buffer(), xdrObj->bufferLength());
7452 bool succeeded = false;
7453 if (!stencil.deserializeStencils(&fc, options, xdrRange, &succeeded)) {
7454 return false;
7456 if (!succeeded) {
7457 fc.clearAutoReport();
7458 JS_ReportErrorASCII(cx, "Decoding failure");
7459 return false;
7462 if (stencil.isModule()) {
7463 fc.clearAutoReport();
7464 JS_ReportErrorASCII(cx,
7465 "evalStencilXDR: Module stencil cannot be evaluated. "
7466 "Use instantiateModuleStencilXDR instead");
7467 return false;
7470 if (!js::ValidateLazinessOfStencilAndGlobal(cx, stencil)) {
7471 return false;
7474 bool useDebugMetadata = !privateValue.isUndefined() || elementAttributeName;
7476 JS::InstantiateOptions instantiateOptions(options);
7477 if (useDebugMetadata) {
7478 instantiateOptions.hideScriptFromDebugger = true;
7480 RootedScript script(
7481 cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, &stencil));
7482 if (!script) {
7483 return false;
7486 if (useDebugMetadata) {
7487 instantiateOptions.hideScriptFromDebugger = false;
7488 if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, privateValue,
7489 elementAttributeName, nullptr, nullptr)) {
7490 return false;
7494 RootedValue retVal(cx);
7495 if (!JS_ExecuteScript(cx, script, &retVal)) {
7496 return false;
7499 args.rval().set(retVal);
7500 return true;
7503 static bool GetExceptionInfo(JSContext* cx, uint32_t argc, Value* vp) {
7504 CallArgs args = CallArgsFromVp(argc, vp);
7506 if (!args.requireAtLeast(cx, "getExceptionInfo", 1)) {
7507 return false;
7510 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
7511 JS_ReportErrorASCII(cx, "getExceptionInfo: expected function argument");
7512 return false;
7515 RootedValue rval(cx);
7516 if (JS_CallFunctionValue(cx, nullptr, args[0], JS::HandleValueArray::empty(),
7517 &rval)) {
7518 // Function didn't throw.
7519 args.rval().setNull();
7520 return true;
7523 // We currently don't support interrupts or forced returns.
7524 if (!cx->isExceptionPending()) {
7525 JS_ReportErrorASCII(cx, "getExceptionInfo: unsupported exception status");
7526 return false;
7529 RootedValue excVal(cx);
7530 Rooted<SavedFrame*> stack(cx);
7531 if (!GetAndClearExceptionAndStack(cx, &excVal, &stack)) {
7532 return false;
7535 RootedValue stackVal(cx);
7536 if (stack) {
7537 RootedString stackString(cx);
7538 if (!BuildStackString(cx, cx->realm()->principals(), stack, &stackString)) {
7539 return false;
7541 stackVal.setString(stackString);
7542 } else {
7543 stackVal.setNull();
7546 RootedObject obj(cx, NewPlainObject(cx));
7547 if (!obj) {
7548 return false;
7551 if (!JS_DefineProperty(cx, obj, "exception", excVal, JSPROP_ENUMERATE)) {
7552 return false;
7555 if (!JS_DefineProperty(cx, obj, "stack", stackVal, JSPROP_ENUMERATE)) {
7556 return false;
7559 args.rval().setObject(*obj);
7560 return true;
7563 class AllocationMarkerObject : public NativeObject {
7564 public:
7565 static const JSClass class_;
7568 const JSClass AllocationMarkerObject::class_ = {"AllocationMarker"};
7570 static bool AllocationMarker(JSContext* cx, unsigned argc, Value* vp) {
7571 CallArgs args = CallArgsFromVp(argc, vp);
7573 bool allocateInsideNursery = true;
7574 if (args.length() > 0 && args[0].isObject()) {
7575 RootedObject options(cx, &args[0].toObject());
7577 RootedValue nurseryVal(cx);
7578 if (!JS_GetProperty(cx, options, "nursery", &nurseryVal)) {
7579 return false;
7581 allocateInsideNursery = ToBoolean(nurseryVal);
7584 JSObject* obj =
7585 allocateInsideNursery
7586 ? NewObjectWithGivenProto<AllocationMarkerObject>(cx, nullptr)
7587 : NewTenuredObjectWithGivenProto<AllocationMarkerObject>(cx, nullptr);
7588 if (!obj) {
7589 return false;
7592 args.rval().setObject(*obj);
7593 return true;
7596 namespace gcCallback {
7598 struct MajorGC {
7599 int32_t depth;
7600 int32_t phases;
7603 static void majorGC(JSContext* cx, JSGCStatus status, JS::GCReason reason,
7604 void* data) {
7605 auto info = static_cast<MajorGC*>(data);
7606 if (!(info->phases & (1 << status))) {
7607 return;
7610 if (info->depth > 0) {
7611 info->depth--;
7612 JS::PrepareForFullGC(cx);
7613 JS::NonIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::API);
7614 info->depth++;
7618 struct MinorGC {
7619 int32_t phases;
7620 bool active;
7623 static void minorGC(JSContext* cx, JSGCStatus status, JS::GCReason reason,
7624 void* data) {
7625 auto info = static_cast<MinorGC*>(data);
7626 if (!(info->phases & (1 << status))) {
7627 return;
7630 if (info->active) {
7631 info->active = false;
7632 if (cx->zone() && !cx->zone()->isAtomsZone()) {
7633 cx->runtime()->gc.evictNursery(JS::GCReason::DEBUG_GC);
7635 info->active = true;
7639 // Process global, should really be runtime-local.
7640 static MajorGC majorGCInfo;
7641 static MinorGC minorGCInfo;
7643 static void enterNullRealm(JSContext* cx, JSGCStatus status,
7644 JS::GCReason reason, void* data) {
7645 JSAutoNullableRealm enterRealm(cx, nullptr);
7648 } /* namespace gcCallback */
7650 static bool SetGCCallback(JSContext* cx, unsigned argc, Value* vp) {
7651 CallArgs args = CallArgsFromVp(argc, vp);
7653 if (args.length() != 1) {
7654 JS_ReportErrorASCII(cx, "Wrong number of arguments");
7655 return false;
7658 RootedObject opts(cx, ToObject(cx, args[0]));
7659 if (!opts) {
7660 return false;
7663 RootedValue v(cx);
7664 if (!JS_GetProperty(cx, opts, "action", &v)) {
7665 return false;
7668 JSString* str = JS::ToString(cx, v);
7669 if (!str) {
7670 return false;
7672 Rooted<JSLinearString*> action(cx, str->ensureLinear(cx));
7673 if (!action) {
7674 return false;
7677 int32_t phases = 0;
7678 if (StringEqualsLiteral(action, "minorGC") ||
7679 StringEqualsLiteral(action, "majorGC")) {
7680 if (!JS_GetProperty(cx, opts, "phases", &v)) {
7681 return false;
7683 if (v.isUndefined()) {
7684 phases = (1 << JSGC_END);
7685 } else {
7686 JSString* str = JS::ToString(cx, v);
7687 if (!str) {
7688 return false;
7690 JSLinearString* phasesStr = str->ensureLinear(cx);
7691 if (!phasesStr) {
7692 return false;
7695 if (StringEqualsLiteral(phasesStr, "begin")) {
7696 phases = (1 << JSGC_BEGIN);
7697 } else if (StringEqualsLiteral(phasesStr, "end")) {
7698 phases = (1 << JSGC_END);
7699 } else if (StringEqualsLiteral(phasesStr, "both")) {
7700 phases = (1 << JSGC_BEGIN) | (1 << JSGC_END);
7701 } else {
7702 JS_ReportErrorASCII(cx, "Invalid callback phase");
7703 return false;
7708 if (StringEqualsLiteral(action, "minorGC")) {
7709 gcCallback::minorGCInfo.phases = phases;
7710 gcCallback::minorGCInfo.active = true;
7711 JS_SetGCCallback(cx, gcCallback::minorGC, &gcCallback::minorGCInfo);
7712 } else if (StringEqualsLiteral(action, "majorGC")) {
7713 if (!JS_GetProperty(cx, opts, "depth", &v)) {
7714 return false;
7716 int32_t depth = 1;
7717 if (!v.isUndefined()) {
7718 if (!ToInt32(cx, v, &depth)) {
7719 return false;
7722 if (depth < 0) {
7723 JS_ReportErrorASCII(cx, "Nesting depth cannot be negative");
7724 return false;
7726 if (depth + gcstats::MAX_PHASE_NESTING >
7727 gcstats::Statistics::MAX_SUSPENDED_PHASES) {
7728 JS_ReportErrorASCII(cx, "Nesting depth too large, would overflow");
7729 return false;
7732 gcCallback::majorGCInfo.phases = phases;
7733 gcCallback::majorGCInfo.depth = depth;
7734 JS_SetGCCallback(cx, gcCallback::majorGC, &gcCallback::majorGCInfo);
7735 } else if (StringEqualsLiteral(action, "enterNullRealm")) {
7736 JS_SetGCCallback(cx, gcCallback::enterNullRealm, nullptr);
7737 } else {
7738 JS_ReportErrorASCII(cx, "Unknown GC callback action");
7739 return false;
7742 args.rval().setUndefined();
7743 return true;
7746 #ifdef DEBUG
7747 static bool EnqueueMark(JSContext* cx, unsigned argc, Value* vp) {
7748 CallArgs args = CallArgsFromVp(argc, vp);
7749 gc::GCRuntime* gc = &cx->runtime()->gc;
7751 if (args.get(0).isString()) {
7752 RootedString val(cx, args[0].toString());
7753 if (!val->ensureLinear(cx)) {
7754 return false;
7756 if (!gc->appendTestMarkQueue(StringValue(val))) {
7757 JS_ReportOutOfMemory(cx);
7758 return false;
7760 } else if (args.get(0).isObject()) {
7761 if (!gc->appendTestMarkQueue(args[0])) {
7762 JS_ReportOutOfMemory(cx);
7763 return false;
7765 } else {
7766 JS_ReportErrorASCII(cx, "Argument must be a string or object");
7767 return false;
7770 args.rval().setUndefined();
7771 return true;
7774 static bool GetMarkQueue(JSContext* cx, unsigned argc, Value* vp) {
7775 CallArgs args = CallArgsFromVp(argc, vp);
7777 const auto& queue = cx->runtime()->gc.getTestMarkQueue();
7779 RootedObject result(cx, JS::NewArrayObject(cx, queue.length()));
7780 if (!result) {
7781 return false;
7783 for (size_t i = 0; i < queue.length(); i++) {
7784 RootedValue val(cx, queue[i]);
7785 if (!JS_WrapValue(cx, &val)) {
7786 return false;
7788 if (!JS_SetElement(cx, result, i, val)) {
7789 return false;
7793 args.rval().setObject(*result);
7794 return true;
7797 static bool ClearMarkQueue(JSContext* cx, unsigned argc, Value* vp) {
7798 CallArgs args = CallArgsFromVp(argc, vp);
7800 cx->runtime()->gc.clearTestMarkQueue();
7801 args.rval().setUndefined();
7802 return true;
7804 #endif // DEBUG
7806 static bool NurseryStringsEnabled(JSContext* cx, unsigned argc, Value* vp) {
7807 CallArgs args = CallArgsFromVp(argc, vp);
7808 args.rval().setBoolean(cx->zone()->allocNurseryStrings());
7809 return true;
7812 static bool IsNurseryAllocated(JSContext* cx, unsigned argc, Value* vp) {
7813 CallArgs args = CallArgsFromVp(argc, vp);
7814 if (!args.get(0).isGCThing()) {
7815 JS_ReportErrorASCII(
7816 cx, "The function takes one argument, which must be a GC thing");
7817 return false;
7820 args.rval().setBoolean(IsInsideNursery(args[0].toGCThing()));
7821 return true;
7824 static bool NumAllocSitesPretenured(JSContext* cx, unsigned argc, Value* vp) {
7825 CallArgs args = CallArgsFromVp(argc, vp);
7826 args.rval().setInt32(cx->realm()->numAllocSitesPretenured);
7827 return true;
7830 static bool GetLcovInfo(JSContext* cx, unsigned argc, Value* vp) {
7831 CallArgs args = CallArgsFromVp(argc, vp);
7833 if (args.length() > 1) {
7834 JS_ReportErrorASCII(cx, "Wrong number of arguments");
7835 return false;
7838 if (!coverage::IsLCovEnabled()) {
7839 JS_ReportErrorASCII(cx, "Coverage not enabled for process.");
7840 return false;
7843 RootedObject global(cx);
7844 if (args.hasDefined(0)) {
7845 global = ToObject(cx, args[0]);
7846 if (!global) {
7847 JS_ReportErrorASCII(cx, "Permission denied to access global");
7848 return false;
7850 global = CheckedUnwrapDynamic(global, cx, /* stopAtWindowProxy = */ false);
7851 if (!global) {
7852 ReportAccessDenied(cx);
7853 return false;
7855 if (!global->is<GlobalObject>()) {
7856 JS_ReportErrorASCII(cx, "Argument must be a global object");
7857 return false;
7859 } else {
7860 global = JS::CurrentGlobalOrNull(cx);
7863 size_t length = 0;
7864 UniqueChars content;
7866 AutoRealm ar(cx, global);
7867 content = js::GetCodeCoverageSummary(cx, &length);
7870 if (!content) {
7871 return false;
7874 JSString* str =
7875 JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(content.get(), length));
7876 if (!str) {
7877 return false;
7880 args.rval().setString(str);
7881 return true;
7884 #ifdef DEBUG
7885 static bool SetRNGState(JSContext* cx, unsigned argc, Value* vp) {
7886 CallArgs args = CallArgsFromVp(argc, vp);
7887 if (!args.requireAtLeast(cx, "SetRNGState", 2)) {
7888 return false;
7891 double d0;
7892 if (!ToNumber(cx, args[0], &d0)) {
7893 return false;
7896 double d1;
7897 if (!ToNumber(cx, args[1], &d1)) {
7898 return false;
7901 uint64_t seed0 = static_cast<uint64_t>(d0);
7902 uint64_t seed1 = static_cast<uint64_t>(d1);
7904 if (seed0 == 0 && seed1 == 0) {
7905 JS_ReportErrorASCII(cx, "RNG requires non-zero seed");
7906 return false;
7909 cx->realm()->getOrCreateRandomNumberGenerator().setState(seed0, seed1);
7911 args.rval().setUndefined();
7912 return true;
7914 #endif
7916 static bool GetTimeZone(JSContext* cx, unsigned argc, Value* vp) {
7917 CallArgs args = CallArgsFromVp(argc, vp);
7918 RootedObject callee(cx, &args.callee());
7920 if (args.length() != 0) {
7921 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
7922 return false;
7925 #ifndef __wasi__
7926 auto getTimeZone = [](std::time_t* now) -> const char* {
7927 std::tm local{};
7928 # if defined(_WIN32)
7929 _tzset();
7930 if (localtime_s(&local, now) == 0) {
7931 return _tzname[local.tm_isdst > 0];
7933 # else
7934 tzset();
7935 # if defined(HAVE_LOCALTIME_R)
7936 if (localtime_r(now, &local)) {
7937 # else
7938 std::tm* localtm = std::localtime(now);
7939 if (localtm) {
7940 *local = *localtm;
7941 # endif /* HAVE_LOCALTIME_R */
7943 # if defined(HAVE_TM_ZONE_TM_GMTOFF)
7944 return local.tm_zone;
7945 # else
7946 return tzname[local.tm_isdst > 0];
7947 # endif /* HAVE_TM_ZONE_TM_GMTOFF */
7949 # endif /* _WIN32 */
7950 return nullptr;
7953 std::time_t now = std::time(nullptr);
7954 if (now != static_cast<std::time_t>(-1)) {
7955 if (const char* tz = getTimeZone(&now)) {
7956 return ReturnStringCopy(cx, args, tz);
7959 #endif /* __wasi__ */
7960 args.rval().setUndefined();
7961 return true;
7965 * Validate time zone input. Accepts the following formats:
7966 * - "America/Chicago" (raw time zone)
7967 * - ":America/Chicago"
7968 * - "/this-part-is-ignored/zoneinfo/America/Chicago"
7969 * - ":/this-part-is-ignored/zoneinfo/America/Chicago"
7970 * - "/etc/localtime"
7971 * - ":/etc/localtime"
7972 * Once the raw time zone is parsed out of the string, it is checked
7973 * against the time zones from GetAvailableTimeZones(). Throws an
7974 * Error if the time zone is invalid.
7976 #if defined(JS_HAS_INTL_API) && !defined(__wasi__)
7977 static bool ValidateTimeZone(JSContext* cx, const char* timeZone) {
7978 static constexpr char zoneInfo[] = "/zoneinfo/";
7979 static constexpr size_t zoneInfoLength = sizeof(zoneInfo) - 1;
7981 size_t i = 0;
7982 if (timeZone[i] == ':') {
7983 ++i;
7985 const char* zoneInfoPtr = strstr(timeZone, zoneInfo);
7986 const char* timeZonePart = timeZone[i] == '/' && zoneInfoPtr
7987 ? zoneInfoPtr + zoneInfoLength
7988 : timeZone + i;
7990 if (!*timeZonePart) {
7991 JS_ReportErrorASCII(cx, "Invalid time zone format");
7992 return false;
7995 if (!strcmp(timeZonePart, "/etc/localtime")) {
7996 return true;
7999 auto timeZones = mozilla::intl::TimeZone::GetAvailableTimeZones();
8000 if (timeZones.isErr()) {
8001 intl::ReportInternalError(cx, timeZones.unwrapErr());
8002 return false;
8004 for (auto timeZoneName : timeZones.unwrap()) {
8005 if (timeZoneName.isErr()) {
8006 intl::ReportInternalError(cx);
8007 return false;
8010 if (!strcmp(timeZonePart, timeZoneName.unwrap().data())) {
8011 return true;
8015 JS_ReportErrorASCII(cx, "Unsupported time zone name: %s", timeZonePart);
8016 return false;
8018 #endif
8020 static bool SetTimeZone(JSContext* cx, unsigned argc, Value* vp) {
8021 CallArgs args = CallArgsFromVp(argc, vp);
8022 RootedObject callee(cx, &args.callee());
8024 if (args.length() != 1) {
8025 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
8026 return false;
8029 if (!args[0].isString() && !args[0].isUndefined()) {
8030 ReportUsageErrorASCII(cx, callee,
8031 "First argument should be a string or undefined");
8032 return false;
8035 #ifndef __wasi__
8036 auto setTimeZone = [](const char* value) {
8037 # if defined(_WIN32)
8038 return _putenv_s("TZ", value) == 0;
8039 # else
8040 return setenv("TZ", value, true) == 0;
8041 # endif /* _WIN32 */
8044 auto unsetTimeZone = []() {
8045 # if defined(_WIN32)
8046 return _putenv_s("TZ", "") == 0;
8047 # else
8048 return unsetenv("TZ") == 0;
8049 # endif /* _WIN32 */
8052 if (args[0].isString() && !args[0].toString()->empty()) {
8053 Rooted<JSLinearString*> str(cx, args[0].toString()->ensureLinear(cx));
8054 if (!str) {
8055 return false;
8058 if (!StringIsAscii(str)) {
8059 ReportUsageErrorASCII(cx, callee,
8060 "First argument contains non-ASCII characters");
8061 return false;
8064 UniqueChars timeZone = JS_EncodeStringToASCII(cx, str);
8065 if (!timeZone) {
8066 return false;
8069 const char* timeZoneStr = timeZone.get();
8070 # ifdef JS_HAS_INTL_API
8071 if (!ValidateTimeZone(cx, timeZoneStr)) {
8072 return false;
8074 # endif
8076 if (!setTimeZone(timeZoneStr)) {
8077 JS_ReportErrorASCII(cx, "Failed to set 'TZ' environment variable");
8078 return false;
8080 } else {
8081 if (!unsetTimeZone()) {
8082 JS_ReportErrorASCII(cx, "Failed to unset 'TZ' environment variable");
8083 return false;
8087 # if defined(_WIN32)
8088 _tzset();
8089 # else
8090 tzset();
8091 # endif /* _WIN32 */
8093 JS::ResetTimeZone();
8095 #endif /* __wasi__ */
8096 args.rval().setUndefined();
8097 return true;
8100 static bool GetCoreCount(JSContext* cx, unsigned argc, Value* vp) {
8101 CallArgs args = CallArgsFromVp(argc, vp);
8102 RootedObject callee(cx, &args.callee());
8104 if (args.length() != 0) {
8105 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
8106 return false;
8109 args.rval().setInt32(GetCPUCount());
8110 return true;
8113 static bool GetDefaultLocale(JSContext* cx, unsigned argc, Value* vp) {
8114 CallArgs args = CallArgsFromVp(argc, vp);
8115 RootedObject callee(cx, &args.callee());
8117 if (args.length() != 0) {
8118 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
8119 return false;
8122 UniqueChars locale = JS_GetDefaultLocale(cx);
8123 if (!locale) {
8124 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
8125 JSMSG_DEFAULT_LOCALE_ERROR);
8126 return false;
8129 return ReturnStringCopy(cx, args, locale.get());
8132 static bool SetDefaultLocale(JSContext* cx, unsigned argc, Value* vp) {
8133 CallArgs args = CallArgsFromVp(argc, vp);
8134 RootedObject callee(cx, &args.callee());
8136 if (args.length() != 1) {
8137 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
8138 return false;
8141 if (!args[0].isString() && !args[0].isUndefined()) {
8142 ReportUsageErrorASCII(cx, callee,
8143 "First argument should be a string or undefined");
8144 return false;
8147 if (args[0].isString() && !args[0].toString()->empty()) {
8148 RootedString str(cx, args[0].toString());
8149 UniqueChars locale = StringToLocale(cx, callee, str);
8150 if (!locale) {
8151 return false;
8154 if (!JS_SetDefaultLocale(cx->runtime(), locale.get())) {
8155 ReportOutOfMemory(cx);
8156 return false;
8158 } else {
8159 JS_ResetDefaultLocale(cx->runtime());
8162 args.rval().setUndefined();
8163 return true;
8166 #ifdef AFLFUZZ
8167 static bool AflLoop(JSContext* cx, unsigned argc, Value* vp) {
8168 CallArgs args = CallArgsFromVp(argc, vp);
8170 uint32_t max_cnt;
8171 if (!ToUint32(cx, args.get(0), &max_cnt)) {
8172 return false;
8175 args.rval().setBoolean(!!__AFL_LOOP(max_cnt));
8176 return true;
8178 #endif
8180 static bool MonotonicNow(JSContext* cx, unsigned argc, Value* vp) {
8181 CallArgs args = CallArgsFromVp(argc, vp);
8182 double now;
8184 // The std::chrono symbols are too new to be present in STL on all platforms we
8185 // care about, so use raw POSIX clock APIs when it might be necessary.
8186 #if defined(XP_UNIX) && !defined(XP_DARWIN)
8187 auto ComputeNow = [](const timespec& ts) {
8188 return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
8191 timespec ts;
8192 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
8193 // Use a monotonic clock if available.
8194 now = ComputeNow(ts);
8195 } else {
8196 // Use a realtime clock as fallback.
8197 if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
8198 // Fail if no clock is available.
8199 JS_ReportErrorASCII(cx, "can't retrieve system clock");
8200 return false;
8203 now = ComputeNow(ts);
8205 // Manually enforce atomicity on a non-monotonic clock.
8207 static mozilla::Atomic<bool, mozilla::ReleaseAcquire> spinLock;
8208 while (!spinLock.compareExchange(false, true)) {
8209 continue;
8212 static double lastNow = -FLT_MAX;
8213 now = lastNow = std::max(now, lastNow);
8215 spinLock = false;
8218 #else
8219 using std::chrono::duration_cast;
8220 using std::chrono::milliseconds;
8221 using std::chrono::steady_clock;
8222 now = duration_cast<milliseconds>(steady_clock::now().time_since_epoch())
8223 .count();
8224 #endif // XP_UNIX && !XP_DARWIN
8226 args.rval().setNumber(now);
8227 return true;
8230 static bool TimeSinceCreation(JSContext* cx, unsigned argc, Value* vp) {
8231 CallArgs args = CallArgsFromVp(argc, vp);
8232 double when =
8233 (mozilla::TimeStamp::Now() - mozilla::TimeStamp::ProcessCreation())
8234 .ToMilliseconds();
8235 args.rval().setNumber(when);
8236 return true;
8239 static bool GetInnerMostEnvironmentObject(JSContext* cx, unsigned argc,
8240 Value* vp) {
8241 CallArgs args = CallArgsFromVp(argc, vp);
8243 FrameIter iter(cx);
8244 if (iter.done()) {
8245 args.rval().setNull();
8246 return true;
8249 args.rval().setObjectOrNull(iter.environmentChain(cx));
8250 return true;
8253 static bool GetEnclosingEnvironmentObject(JSContext* cx, unsigned argc,
8254 Value* vp) {
8255 CallArgs args = CallArgsFromVp(argc, vp);
8256 if (!args.requireAtLeast(cx, "getEnclosingEnvironmentObject", 1)) {
8257 return false;
8260 if (!args[0].isObject()) {
8261 args.rval().setUndefined();
8262 return true;
8265 JSObject* envObj = &args[0].toObject();
8267 if (envObj->is<EnvironmentObject>()) {
8268 EnvironmentObject* env = &envObj->as<EnvironmentObject>();
8269 args.rval().setObject(env->enclosingEnvironment());
8270 return true;
8273 if (envObj->is<DebugEnvironmentProxy>()) {
8274 DebugEnvironmentProxy* envProxy = &envObj->as<DebugEnvironmentProxy>();
8275 args.rval().setObject(envProxy->enclosingEnvironment());
8276 return true;
8279 args.rval().setNull();
8280 return true;
8283 static bool GetEnvironmentObjectType(JSContext* cx, unsigned argc, Value* vp) {
8284 CallArgs args = CallArgsFromVp(argc, vp);
8285 if (!args.requireAtLeast(cx, "getEnvironmentObjectType", 1)) {
8286 return false;
8289 if (!args[0].isObject()) {
8290 args.rval().setUndefined();
8291 return true;
8294 JSObject* envObj = &args[0].toObject();
8296 if (envObj->is<EnvironmentObject>()) {
8297 EnvironmentObject* env = &envObj->as<EnvironmentObject>();
8298 JSString* str = JS_NewStringCopyZ(cx, env->typeString());
8299 args.rval().setString(str);
8300 return true;
8302 if (envObj->is<DebugEnvironmentProxy>()) {
8303 DebugEnvironmentProxy* envProxy = &envObj->as<DebugEnvironmentProxy>();
8304 EnvironmentObject* env = &envProxy->environment();
8305 char buf[256] = {'\0'};
8306 SprintfLiteral(buf, "[DebugProxy] %s", env->typeString());
8307 JSString* str = JS_NewStringCopyZ(cx, buf);
8308 args.rval().setString(str);
8309 return true;
8312 args.rval().setUndefined();
8313 return true;
8316 static bool AssertRealmFuseInvariants(JSContext* cx, unsigned argc, Value* vp) {
8317 CallArgs args = CallArgsFromVp(argc, vp);
8318 // Note: This will crash if any invariant isn't held, so it's sufficient to
8319 // simply return true always.
8320 cx->realm()->realmFuses.assertInvariants(cx);
8321 args.rval().setUndefined();
8322 return true;
8325 static bool GetFuseState(JSContext* cx, unsigned argc, Value* vp) {
8326 CallArgs args = CallArgsFromVp(argc, vp);
8328 cx->realm()->realmFuses.assertInvariants(cx);
8330 RootedObject returnObj(cx, JS_NewPlainObject(cx));
8331 if (!returnObj) {
8332 return false;
8335 RootedObject fuseObj(cx);
8336 RootedString intactStr(cx, NewStringCopyZ<CanGC>(cx, "intact"));
8337 if (!intactStr) {
8338 return false;
8341 RootedValue intactValue(cx);
8343 #define FUSE(Name, LowerName) \
8344 fuseObj = JS_NewPlainObject(cx); \
8345 if (!fuseObj) { \
8346 return false; \
8348 intactValue.setBoolean(cx->realm()->realmFuses.LowerName.intact()); \
8349 if (!JS_DefineProperty(cx, fuseObj, "intact", intactValue, \
8350 JSPROP_ENUMERATE)) { \
8351 return false; \
8353 if (!JS_DefineProperty(cx, returnObj, #Name, fuseObj, JSPROP_ENUMERATE)) { \
8354 return false; \
8357 FOR_EACH_REALM_FUSE(FUSE)
8358 #undef FUSE
8360 // Register hasSeenUndefinedFuse
8361 fuseObj = JS_NewPlainObject(cx);
8362 if (!fuseObj) {
8363 return false;
8365 intactValue.setBoolean(
8366 cx->runtime()->hasSeenObjectEmulateUndefinedFuse.ref().intact());
8367 if (!JS_DefineProperty(cx, fuseObj, "intact", intactValue,
8368 JSPROP_ENUMERATE)) {
8369 return false;
8372 if (!JS_DefineProperty(cx, returnObj, "hasSeenObjectEmulateUndefinedFuse",
8373 fuseObj, JSPROP_ENUMERATE)) {
8374 return false;
8377 args.rval().setObject(*returnObj);
8378 return true;
8381 static bool PopAllFusesInRealm(JSContext* cx, unsigned argc, Value* vp) {
8382 CallArgs args = CallArgsFromVp(argc, vp);
8384 MOZ_ASSERT(cx->realm());
8386 RealmFuses& realmFuses = cx->realm()->realmFuses;
8388 #define FUSE(Name, LowerName) realmFuses.LowerName.popFuse(cx, realmFuses);
8389 FOR_EACH_REALM_FUSE(FUSE)
8390 #undef FUSE
8392 args.rval().setUndefined();
8393 return true;
8396 static bool GetErrorNotes(JSContext* cx, unsigned argc, Value* vp) {
8397 CallArgs args = CallArgsFromVp(argc, vp);
8398 if (!args.requireAtLeast(cx, "getErrorNotes", 1)) {
8399 return false;
8402 if (!args[0].isObject() || !args[0].toObject().is<ErrorObject>()) {
8403 args.rval().setNull();
8404 return true;
8407 JSErrorReport* report = args[0].toObject().as<ErrorObject>().getErrorReport();
8408 if (!report) {
8409 args.rval().setNull();
8410 return true;
8413 RootedObject notesArray(cx, CreateErrorNotesArray(cx, report));
8414 if (!notesArray) {
8415 return false;
8418 args.rval().setObject(*notesArray);
8419 return true;
8422 static bool IsConstructor(JSContext* cx, unsigned argc, Value* vp) {
8423 CallArgs args = CallArgsFromVp(argc, vp);
8424 if (args.length() < 1) {
8425 args.rval().setBoolean(false);
8426 } else {
8427 args.rval().setBoolean(IsConstructor(args[0]));
8429 return true;
8432 static bool SetTimeResolution(JSContext* cx, unsigned argc, Value* vp) {
8433 CallArgs args = CallArgsFromVp(argc, vp);
8434 RootedObject callee(cx, &args.callee());
8436 if (!args.requireAtLeast(cx, "setTimeResolution", 2)) {
8437 return false;
8440 if (!args[0].isInt32()) {
8441 ReportUsageErrorASCII(cx, callee, "First argument must be an Int32.");
8442 return false;
8444 int32_t resolution = args[0].toInt32();
8446 if (!args[1].isBoolean()) {
8447 ReportUsageErrorASCII(cx, callee, "Second argument must be a Boolean");
8448 return false;
8450 bool jitter = args[1].toBoolean();
8452 JS::SetTimeResolutionUsec(resolution, jitter);
8454 args.rval().setUndefined();
8455 return true;
8458 static bool ScriptedCallerGlobal(JSContext* cx, unsigned argc, Value* vp) {
8459 CallArgs args = CallArgsFromVp(argc, vp);
8461 RootedObject obj(cx, JS::GetScriptedCallerGlobal(cx));
8462 if (!obj) {
8463 args.rval().setNull();
8464 return true;
8467 obj = ToWindowProxyIfWindow(obj);
8469 if (!cx->compartment()->wrap(cx, &obj)) {
8470 return false;
8473 args.rval().setObject(*obj);
8474 return true;
8477 static bool ObjectGlobal(JSContext* cx, unsigned argc, Value* vp) {
8478 CallArgs args = CallArgsFromVp(argc, vp);
8479 RootedObject callee(cx, &args.callee());
8481 if (!args.get(0).isObject()) {
8482 ReportUsageErrorASCII(cx, callee, "Argument must be an object");
8483 return false;
8486 RootedObject obj(cx, &args[0].toObject());
8487 if (IsCrossCompartmentWrapper(obj)) {
8488 args.rval().setNull();
8489 return true;
8492 obj = ToWindowProxyIfWindow(&obj->nonCCWGlobal());
8494 args.rval().setObject(*obj);
8495 return true;
8498 static bool IsSameCompartment(JSContext* cx, unsigned argc, Value* vp) {
8499 CallArgs args = CallArgsFromVp(argc, vp);
8500 RootedObject callee(cx, &args.callee());
8502 if (!args.get(0).isObject() || !args.get(1).isObject()) {
8503 ReportUsageErrorASCII(cx, callee, "Both arguments must be objects");
8504 return false;
8507 RootedObject obj1(cx, UncheckedUnwrap(&args[0].toObject()));
8508 RootedObject obj2(cx, UncheckedUnwrap(&args[1].toObject()));
8510 args.rval().setBoolean(obj1->compartment() == obj2->compartment());
8511 return true;
8514 static bool FirstGlobalInCompartment(JSContext* cx, unsigned argc, Value* vp) {
8515 CallArgs args = CallArgsFromVp(argc, vp);
8516 RootedObject callee(cx, &args.callee());
8518 if (!args.get(0).isObject()) {
8519 ReportUsageErrorASCII(cx, callee, "Argument must be an object");
8520 return false;
8523 RootedObject obj(cx, UncheckedUnwrap(&args[0].toObject()));
8524 obj = ToWindowProxyIfWindow(GetFirstGlobalInCompartment(obj->compartment()));
8526 if (!cx->compartment()->wrap(cx, &obj)) {
8527 return false;
8530 args.rval().setObject(*obj);
8531 return true;
8534 static bool AssertCorrectRealm(JSContext* cx, unsigned argc, Value* vp) {
8535 CallArgs args = CallArgsFromVp(argc, vp);
8536 MOZ_RELEASE_ASSERT(cx->realm() == args.callee().as<JSFunction>().realm());
8537 args.rval().setUndefined();
8538 return true;
8541 static bool GlobalLexicals(JSContext* cx, unsigned argc, Value* vp) {
8542 CallArgs args = CallArgsFromVp(argc, vp);
8544 Rooted<GlobalLexicalEnvironmentObject*> globalLexical(
8545 cx, &cx->global()->lexicalEnvironment());
8547 RootedIdVector props(cx);
8548 if (!GetPropertyKeys(cx, globalLexical, JSITER_HIDDEN, &props)) {
8549 return false;
8552 RootedObject res(cx, JS_NewPlainObject(cx));
8553 if (!res) {
8554 return false;
8557 RootedValue val(cx);
8558 for (size_t i = 0; i < props.length(); i++) {
8559 HandleId id = props[i];
8560 if (!JS_GetPropertyById(cx, globalLexical, id, &val)) {
8561 return false;
8563 if (val.isMagic(JS_UNINITIALIZED_LEXICAL)) {
8564 continue;
8566 if (!JS_DefinePropertyById(cx, res, id, val, JSPROP_ENUMERATE)) {
8567 return false;
8571 args.rval().setObject(*res);
8572 return true;
8575 static bool EncodeAsUtf8InBuffer(JSContext* cx, unsigned argc, Value* vp) {
8576 CallArgs args = CallArgsFromVp(argc, vp);
8577 if (!args.requireAtLeast(cx, "encodeAsUtf8InBuffer", 2)) {
8578 return false;
8581 RootedObject callee(cx, &args.callee());
8583 if (!args[0].isString()) {
8584 ReportUsageErrorASCII(cx, callee, "First argument must be a String");
8585 return false;
8588 // Create the amounts array early so that the raw pointer into Uint8Array
8589 // data has as short a lifetime as possible
8590 Rooted<ArrayObject*> array(cx, NewDenseFullyAllocatedArray(cx, 2));
8591 if (!array) {
8592 return false;
8594 array->ensureDenseInitializedLength(0, 2);
8596 JSObject* obj = args[1].isObject() ? &args[1].toObject() : nullptr;
8597 Rooted<JS::Uint8Array> view(cx, JS::Uint8Array::unwrap(obj));
8598 if (!view) {
8599 ReportUsageErrorASCII(cx, callee, "Second argument must be a Uint8Array");
8600 return false;
8603 mozilla::Span<uint8_t> span;
8604 bool isSharedMemory = false;
8606 // The hazard analysis does not track the data pointer, so it can neither
8607 // tell that `data` is dead if ReportUsageErrorASCII is called, nor that
8608 // its live range ends at the call to AsWritableChars(). Construct a
8609 // temporary scope to hide from the analysis. This should really be replaced
8610 // with a safer mechanism.
8611 JS::AutoCheckCannotGC nogc(cx);
8612 if (!view.isDetached()) {
8613 span = view.get().getData(&isSharedMemory, nogc);
8617 if (isSharedMemory || // exclude views of SharedArrayBuffers
8618 !span.data()) { // exclude views of detached ArrayBuffers
8619 ReportUsageErrorASCII(
8620 cx, callee,
8621 "Second argument must be an unshared, non-detached Uint8Array");
8622 return false;
8625 Maybe<std::tuple<size_t, size_t>> amounts =
8626 JS_EncodeStringToUTF8BufferPartial(cx, args[0].toString(),
8627 AsWritableChars(span));
8628 if (!amounts) {
8629 ReportOutOfMemory(cx);
8630 return false;
8633 auto [unitsRead, bytesWritten] = *amounts;
8635 array->initDenseElement(0, Int32Value(AssertedCast<int32_t>(unitsRead)));
8636 array->initDenseElement(1, Int32Value(AssertedCast<int32_t>(bytesWritten)));
8638 args.rval().setObject(*array);
8639 return true;
8642 JSScript* js::TestingFunctionArgumentToScript(
8643 JSContext* cx, HandleValue v, JSFunction** funp /* = nullptr */) {
8644 if (v.isString()) {
8645 // To convert a string to a script, compile it. Parse it as an ES6 Program.
8646 Rooted<JSString*> str(cx, v.toString());
8647 AutoStableStringChars linearChars(cx);
8648 if (!linearChars.initTwoByte(cx, str)) {
8649 return nullptr;
8651 SourceText<char16_t> source;
8652 if (!source.initMaybeBorrowed(cx, linearChars)) {
8653 return nullptr;
8656 CompileOptions options(cx);
8657 return JS::Compile(cx, options, source);
8660 RootedFunction fun(cx, JS_ValueToFunction(cx, v));
8661 if (!fun) {
8662 return nullptr;
8665 if (!fun->isInterpreted()) {
8666 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
8667 JSMSG_TESTING_SCRIPTS_ONLY);
8668 return nullptr;
8671 JSScript* script = JSFunction::getOrCreateScript(cx, fun);
8672 if (!script) {
8673 return nullptr;
8676 if (funp) {
8677 *funp = fun;
8680 return script;
8683 static bool BaselineCompile(JSContext* cx, unsigned argc, Value* vp) {
8684 CallArgs args = CallArgsFromVp(argc, vp);
8685 RootedObject callee(cx, &args.callee());
8687 RootedScript script(cx);
8688 if (args.length() == 0) {
8689 NonBuiltinScriptFrameIter iter(cx);
8690 if (iter.done()) {
8691 ReportUsageErrorASCII(cx, callee,
8692 "no script argument and no script caller");
8693 return false;
8695 script = iter.script();
8696 } else {
8697 script = TestingFunctionArgumentToScript(cx, args[0]);
8698 if (!script) {
8699 return false;
8703 bool forceDebug = false;
8704 if (args.length() > 1) {
8705 if (args.length() > 2) {
8706 ReportUsageErrorASCII(cx, callee, "too many arguments");
8707 return false;
8709 if (!args[1].isBoolean() && !args[1].isUndefined()) {
8710 ReportUsageErrorASCII(
8711 cx, callee, "forceDebugInstrumentation argument should be boolean");
8712 return false;
8714 forceDebug = ToBoolean(args[1]);
8717 const char* returnedStr = nullptr;
8718 do {
8719 // In order to check for differential behaviour, baselineCompile should have
8720 // the same output whether --no-baseline is used or not.
8721 if (js::SupportDifferentialTesting()) {
8722 returnedStr = "skipped (differential testing)";
8723 break;
8726 AutoRealm ar(cx, script);
8727 if (script->hasBaselineScript()) {
8728 if (forceDebug && !script->baselineScript()->hasDebugInstrumentation()) {
8729 // There isn't an easy way to do this for a script that might be on
8730 // stack right now. See
8731 // js::jit::RecompileOnStackBaselineScriptsForDebugMode.
8732 ReportUsageErrorASCII(
8733 cx, callee, "unsupported case: recompiling script for debug mode");
8734 return false;
8737 args.rval().setUndefined();
8738 return true;
8741 if (!jit::IsBaselineJitEnabled(cx)) {
8742 returnedStr = "baseline disabled";
8743 break;
8745 if (!script->canBaselineCompile()) {
8746 returnedStr = "can't compile";
8747 break;
8749 if (!cx->zone()->ensureJitZoneExists(cx)) {
8750 return false;
8753 jit::MethodStatus status = jit::BaselineCompile(cx, script, forceDebug);
8754 switch (status) {
8755 case jit::Method_Error:
8756 return false;
8757 case jit::Method_CantCompile:
8758 returnedStr = "can't compile";
8759 break;
8760 case jit::Method_Skipped:
8761 returnedStr = "skipped";
8762 break;
8763 case jit::Method_Compiled:
8764 args.rval().setUndefined();
8766 } while (false);
8768 if (returnedStr) {
8769 return ReturnStringCopy(cx, args, returnedStr);
8772 return true;
8775 static bool ClearKeptObjects(JSContext* cx, unsigned argc, Value* vp) {
8776 CallArgs args = CallArgsFromVp(argc, vp);
8777 JS::ClearKeptObjects(cx);
8778 args.rval().setUndefined();
8779 return true;
8782 static bool NumberToDouble(JSContext* cx, unsigned argc, Value* vp) {
8783 CallArgs args = CallArgsFromVp(argc, vp);
8784 if (!args.requireAtLeast(cx, "numberToDouble", 1)) {
8785 return false;
8788 if (!args[0].isNumber()) {
8789 RootedObject callee(cx, &args.callee());
8790 ReportUsageErrorASCII(cx, callee, "argument must be a number");
8791 return false;
8794 args.rval().setDouble(args[0].toNumber());
8795 return true;
8798 static bool GetICUOptions(JSContext* cx, unsigned argc, Value* vp) {
8799 CallArgs args = CallArgsFromVp(argc, vp);
8801 RootedObject info(cx, JS_NewPlainObject(cx));
8802 if (!info) {
8803 return false;
8806 #ifdef JS_HAS_INTL_API
8807 RootedString str(cx);
8809 str = NewStringCopy<CanGC>(cx, mozilla::intl::ICU4CLibrary::GetVersion());
8810 if (!str || !JS_DefineProperty(cx, info, "version", str, JSPROP_ENUMERATE)) {
8811 return false;
8814 str = NewStringCopy<CanGC>(cx, mozilla::intl::String::GetUnicodeVersion());
8815 if (!str || !JS_DefineProperty(cx, info, "unicode", str, JSPROP_ENUMERATE)) {
8816 return false;
8819 str = NewStringCopyZ<CanGC>(cx, mozilla::intl::Locale::GetDefaultLocale());
8820 if (!str || !JS_DefineProperty(cx, info, "locale", str, JSPROP_ENUMERATE)) {
8821 return false;
8824 auto tzdataVersion = mozilla::intl::TimeZone::GetTZDataVersion();
8825 if (tzdataVersion.isErr()) {
8826 intl::ReportInternalError(cx, tzdataVersion.unwrapErr());
8827 return false;
8830 str = NewStringCopy<CanGC>(cx, tzdataVersion.unwrap());
8831 if (!str || !JS_DefineProperty(cx, info, "tzdata", str, JSPROP_ENUMERATE)) {
8832 return false;
8835 intl::FormatBuffer<char16_t, intl::INITIAL_CHAR_BUFFER_SIZE> buf(cx);
8837 if (auto ok = DateTimeInfo::timeZoneId(DateTimeInfo::ForceUTC::No, buf);
8838 ok.isErr()) {
8839 intl::ReportInternalError(cx, ok.unwrapErr());
8840 return false;
8843 str = buf.toString(cx);
8844 if (!str || !JS_DefineProperty(cx, info, "timezone", str, JSPROP_ENUMERATE)) {
8845 return false;
8848 if (auto ok = mozilla::intl::TimeZone::GetHostTimeZone(buf); ok.isErr()) {
8849 intl::ReportInternalError(cx, ok.unwrapErr());
8850 return false;
8853 str = buf.toString(cx);
8854 if (!str ||
8855 !JS_DefineProperty(cx, info, "host-timezone", str, JSPROP_ENUMERATE)) {
8856 return false;
8858 #endif
8860 args.rval().setObject(*info);
8861 return true;
8864 static bool GetAvailableLocalesOf(JSContext* cx, unsigned argc, Value* vp) {
8865 CallArgs args = CallArgsFromVp(argc, vp);
8866 RootedObject callee(cx, &args.callee());
8868 if (!args.requireAtLeast(cx, "getAvailableLocalesOf", 1)) {
8869 return false;
8872 HandleValue arg = args[0];
8873 if (!arg.isString()) {
8874 ReportUsageErrorASCII(cx, callee, "First argument must be a string");
8875 return false;
8878 ArrayObject* result;
8879 #ifdef JS_HAS_INTL_API
8880 using SupportedLocaleKind = js::intl::SharedIntlData::SupportedLocaleKind;
8882 SupportedLocaleKind kind;
8884 JSLinearString* typeStr = arg.toString()->ensureLinear(cx);
8885 if (!typeStr) {
8886 return false;
8889 if (StringEqualsLiteral(typeStr, "Collator")) {
8890 kind = SupportedLocaleKind::Collator;
8891 } else if (StringEqualsLiteral(typeStr, "DateTimeFormat")) {
8892 kind = SupportedLocaleKind::DateTimeFormat;
8893 } else if (StringEqualsLiteral(typeStr, "DisplayNames")) {
8894 kind = SupportedLocaleKind::DisplayNames;
8895 } else if (StringEqualsLiteral(typeStr, "ListFormat")) {
8896 kind = SupportedLocaleKind::ListFormat;
8897 } else if (StringEqualsLiteral(typeStr, "NumberFormat")) {
8898 kind = SupportedLocaleKind::NumberFormat;
8899 } else if (StringEqualsLiteral(typeStr, "PluralRules")) {
8900 kind = SupportedLocaleKind::PluralRules;
8901 } else if (StringEqualsLiteral(typeStr, "RelativeTimeFormat")) {
8902 kind = SupportedLocaleKind::RelativeTimeFormat;
8903 } else if (StringEqualsLiteral(typeStr, "Segmenter")) {
8904 kind = SupportedLocaleKind::Segmenter;
8905 } else {
8906 ReportUsageErrorASCII(cx, callee, "Unsupported Intl constructor name");
8907 return false;
8911 intl::SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref();
8912 result = sharedIntlData.availableLocalesOf(cx, kind);
8913 #else
8914 result = NewDenseEmptyArray(cx);
8915 #endif
8916 if (!result) {
8917 return false;
8920 args.rval().setObject(*result);
8921 return true;
8924 static bool IsSmallFunction(JSContext* cx, unsigned argc, Value* vp) {
8925 CallArgs args = CallArgsFromVp(argc, vp);
8926 RootedObject callee(cx, &args.callee());
8928 if (!args.requireAtLeast(cx, "IsSmallFunction", 1)) {
8929 return false;
8932 HandleValue arg = args[0];
8933 if (!arg.isObject() || !arg.toObject().is<JSFunction>()) {
8934 ReportUsageErrorASCII(cx, callee, "First argument must be a function");
8935 return false;
8938 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
8939 if (!fun->isInterpreted()) {
8940 ReportUsageErrorASCII(cx, callee,
8941 "First argument must be an interpreted function");
8942 return false;
8945 JSScript* script = JSFunction::getOrCreateScript(cx, fun);
8946 if (!script) {
8947 return false;
8950 args.rval().setBoolean(jit::JitOptions.isSmallFunction(script));
8951 return true;
8954 static bool PCCountProfiling_Start(JSContext* cx, unsigned argc, Value* vp) {
8955 CallArgs args = CallArgsFromVp(argc, vp);
8957 JS::StartPCCountProfiling(cx);
8959 args.rval().setUndefined();
8960 return true;
8963 static bool PCCountProfiling_Stop(JSContext* cx, unsigned argc, Value* vp) {
8964 CallArgs args = CallArgsFromVp(argc, vp);
8966 JS::StopPCCountProfiling(cx);
8968 args.rval().setUndefined();
8969 return true;
8972 static bool PCCountProfiling_Purge(JSContext* cx, unsigned argc, Value* vp) {
8973 CallArgs args = CallArgsFromVp(argc, vp);
8975 JS::PurgePCCounts(cx);
8977 args.rval().setUndefined();
8978 return true;
8981 static bool PCCountProfiling_ScriptCount(JSContext* cx, unsigned argc,
8982 Value* vp) {
8983 CallArgs args = CallArgsFromVp(argc, vp);
8985 size_t length = JS::GetPCCountScriptCount(cx);
8987 args.rval().setNumber(double(length));
8988 return true;
8991 static bool PCCountProfiling_ScriptSummary(JSContext* cx, unsigned argc,
8992 Value* vp) {
8993 CallArgs args = CallArgsFromVp(argc, vp);
8994 if (!args.requireAtLeast(cx, "summary", 1)) {
8995 return false;
8998 uint32_t index;
8999 if (!JS::ToUint32(cx, args[0], &index)) {
9000 return false;
9003 JSString* str = JS::GetPCCountScriptSummary(cx, index);
9004 if (!str) {
9005 return false;
9008 args.rval().setString(str);
9009 return true;
9012 static bool PCCountProfiling_ScriptContents(JSContext* cx, unsigned argc,
9013 Value* vp) {
9014 CallArgs args = CallArgsFromVp(argc, vp);
9015 if (!args.requireAtLeast(cx, "contents", 1)) {
9016 return false;
9019 uint32_t index;
9020 if (!JS::ToUint32(cx, args[0], &index)) {
9021 return false;
9024 JSString* str = JS::GetPCCountScriptContents(cx, index);
9025 if (!str) {
9026 return false;
9029 args.rval().setString(str);
9030 return true;
9033 static bool NukeCCW(JSContext* cx, unsigned argc, Value* vp) {
9034 CallArgs args = CallArgsFromVp(argc, vp);
9036 if (args.length() != 1 || !args[0].isObject() ||
9037 !IsCrossCompartmentWrapper(&args[0].toObject())) {
9038 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS,
9039 "nukeCCW");
9040 return false;
9043 NukeCrossCompartmentWrapper(cx, &args[0].toObject());
9044 args.rval().setUndefined();
9045 return true;
9048 static bool FdLibM_Pow(JSContext* cx, unsigned argc, Value* vp) {
9049 CallArgs args = CallArgsFromVp(argc, vp);
9051 double x;
9052 if (!JS::ToNumber(cx, args.get(0), &x)) {
9053 return false;
9056 double y;
9057 if (!JS::ToNumber(cx, args.get(1), &y)) {
9058 return false;
9061 // Because C99 and ECMA specify different behavior for pow(), we need to wrap
9062 // the fdlibm call to make it ECMA compliant.
9063 if (!std::isfinite(y) && (x == 1.0 || x == -1.0)) {
9064 args.rval().setNaN();
9065 } else {
9066 args.rval().setDouble(fdlibm_pow(x, y));
9068 return true;
9071 // clang-format off
9072 static const JSFunctionSpecWithHelp TestingFunctions[] = {
9073 JS_FN_HELP("gc", ::GC, 0, 0,
9074 "gc([obj] | 'zone' [, ('shrinking' | 'last-ditch') ])",
9075 " Run the garbage collector.\n"
9076 " The first parameter describes which zones to collect: if an object is\n"
9077 " given, GC only its zone. If 'zone' is given, GC any zones that were\n"
9078 " scheduled via schedulegc.\n"
9079 " The second parameter is optional and may be 'shrinking' to perform a\n"
9080 " shrinking GC or 'last-ditch' for a shrinking, last-ditch GC."),
9082 JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
9083 "minorgc([aboutToOverflow])",
9084 " Run a minor collector on the Nursery. When aboutToOverflow is true, marks\n"
9085 " the store buffer as about-to-overflow before collecting."),
9087 JS_FN_HELP("maybegc", ::MaybeGC, 0, 0,
9088 "maybegc()",
9089 " Hint to the engine that now is an ok time to run the garbage collector.\n"),
9091 JS_FN_HELP("gcparam", GCParameter, 2, 0,
9092 "gcparam(name [, value])",
9093 " Wrapper for JS_[GS]etGCParameter. The name is one of:" GC_PARAMETER_ARGS_LIST),
9095 JS_FN_HELP("hasDisassembler", HasDisassembler, 0, 0,
9096 "hasDisassembler()",
9097 " Return true if a disassembler is present (for disnative and wasmDis)."),
9099 JS_FN_HELP("disnative", DisassembleNative, 2, 0,
9100 "disnative(fun,[path])",
9101 " Disassemble a function into its native code. Optionally write the native code bytes to a file on disk.\n"),
9103 JS_FN_HELP("relazifyFunctions", RelazifyFunctions, 0, 0,
9104 "relazifyFunctions(...)",
9105 " Perform a GC and allow relazification of functions. Accepts the same\n"
9106 " arguments as gc()."),
9108 JS_FN_HELP("getBuildConfiguration", GetBuildConfiguration, 1, 0,
9109 "getBuildConfiguration([option])",
9110 " Query the options SpiderMonkey was built with, or return an object\n"
9111 " with the options if no argument is given."),
9113 JS_FN_HELP("getRealmConfiguration", GetRealmConfiguration, 1, 0,
9114 "getRealmConfiguration([option])",
9115 " Query the runtime options SpiderMonkey is running with, or return an\n."
9116 " object with the options if no argument is given."),
9118 JS_FN_HELP("isLcovEnabled", ::IsLCovEnabled, 0, 0,
9119 "isLcovEnabled()",
9120 " Return true if JS LCov support is enabled."),
9122 JS_FN_HELP("trialInline", TrialInline, 0, 0,
9123 "trialInline()",
9124 " Perform trial-inlining for the caller's frame if it's a BaselineFrame."),
9126 JS_FN_HELP("hasChild", HasChild, 0, 0,
9127 "hasChild(parent, child)",
9128 " Return true if |child| is a child of |parent|, as determined by a call to\n"
9129 " TraceChildren"),
9131 JS_FN_HELP("setSavedStacksRNGState", SetSavedStacksRNGState, 1, 0,
9132 "setSavedStacksRNGState(seed)",
9133 " Set this compartment's SavedStacks' RNG state.\n"),
9135 JS_FN_HELP("getSavedFrameCount", GetSavedFrameCount, 0, 0,
9136 "getSavedFrameCount()",
9137 " Return the number of SavedFrame instances stored in this compartment's\n"
9138 " SavedStacks cache."),
9140 JS_FN_HELP("clearSavedFrames", ClearSavedFrames, 0, 0,
9141 "clearSavedFrames()",
9142 " Empty the current compartment's cache of SavedFrame objects, so that\n"
9143 " subsequent stack captures allocate fresh objects to represent frames.\n"
9144 " Clear the current stack's LiveSavedFrameCaches."),
9146 JS_FN_HELP("saveStack", SaveStack, 0, 0,
9147 "saveStack([maxDepth [, compartment]])",
9148 " Capture a stack. If 'maxDepth' is given, capture at most 'maxDepth' number\n"
9149 " of frames. If 'compartment' is given, allocate the js::SavedFrame instances\n"
9150 " with the given object's compartment."),
9152 JS_FN_HELP("captureFirstSubsumedFrame", CaptureFirstSubsumedFrame, 1, 0,
9153 "saveStack(object [, shouldIgnoreSelfHosted = true]])",
9154 " Capture a stack back to the first frame whose principals are subsumed by the\n"
9155 " object's compartment's principals. If 'shouldIgnoreSelfHosted' is given,\n"
9156 " control whether self-hosted frames are considered when checking principals."),
9158 JS_FN_HELP("callFunctionFromNativeFrame", CallFunctionFromNativeFrame, 1, 0,
9159 "callFunctionFromNativeFrame(function)",
9160 " Call 'function' with a (C++-)native frame on stack.\n"
9161 " Required for testing that SaveStack properly handles native frames."),
9163 JS_FN_HELP("callFunctionWithAsyncStack", CallFunctionWithAsyncStack, 0, 0,
9164 "callFunctionWithAsyncStack(function, stack, asyncCause)",
9165 " Call 'function', using the provided stack as the async stack responsible\n"
9166 " for the call, and propagate its return value or the exception it throws.\n"
9167 " The function is called with no arguments, and 'this' is 'undefined'. The\n"
9168 " specified |asyncCause| is attached to the provided stack frame."),
9170 JS_FN_HELP("enableTrackAllocations", EnableTrackAllocations, 0, 0,
9171 "enableTrackAllocations()",
9172 " Start capturing the JS stack at every allocation. Note that this sets an\n"
9173 " object metadata callback that will override any other object metadata\n"
9174 " callback that may be set."),
9176 JS_FN_HELP("disableTrackAllocations", DisableTrackAllocations, 0, 0,
9177 "disableTrackAllocations()",
9178 " Stop capturing the JS stack at every allocation."),
9180 JS_FN_HELP("setTestFilenameValidationCallback", SetTestFilenameValidationCallback, 0, 0,
9181 "setTestFilenameValidationCallback()",
9182 " Set the filename validation callback to a callback that accepts only\n"
9183 " filenames starting with 'safe' or (only in system realms) 'system'."),
9185 JS_FN_HELP("newObjectWithAddPropertyHook", NewObjectWithAddPropertyHook, 0, 0,
9186 "newObjectWithAddPropertyHook()",
9187 " Returns a new object with an addProperty JSClass hook. This hook\n"
9188 " increments the value of the _propertiesAdded data property on the object\n"
9189 " when a new property is added."),
9191 JS_FN_HELP("newObjectWithCallHook", NewObjectWithCallHook, 0, 0,
9192 "newObjectWithCallHook()",
9193 " Returns a new object with call/construct JSClass hooks. These hooks return\n"
9194 " a new object that contains the Values supplied by the caller."),
9196 JS_FN_HELP("newObjectWithManyReservedSlots", NewObjectWithManyReservedSlots, 0, 0,
9197 "newObjectWithManyReservedSlots()",
9198 " Returns a new object with many reserved slots. The slots are initialized to int32\n"
9199 " values. checkObjectWithManyReservedSlots can be used to check the slots still\n"
9200 " hold these values."),
9202 JS_FN_HELP("checkObjectWithManyReservedSlots", CheckObjectWithManyReservedSlots, 1, 0,
9203 "checkObjectWithManyReservedSlots(obj)",
9204 " Checks the reserved slots set by newObjectWithManyReservedSlots still hold the expected\n"
9205 " values."),
9207 JS_FN_HELP("getWatchtowerLog", GetWatchtowerLog, 0, 0,
9208 "getWatchtowerLog()",
9209 " Returns the Watchtower log recording object changes for objects for which\n"
9210 " addWatchtowerTarget was called. The internal log is cleared. The return\n"
9211 " value is an array of plain objects with the following properties:\n"
9212 " - kind: a string describing the kind of mutation, for example \"add-prop\"\n"
9213 " - object: the object being mutated\n"
9214 " - extra: an extra value, for example the name of the property being added"),
9216 JS_FN_HELP("addWatchtowerTarget", AddWatchtowerTarget, 1, 0,
9217 "addWatchtowerTarget(object)",
9218 " Invoke the watchtower callback for changes to this object."),
9220 JS_FN_HELP("newString", NewString, 2, 0,
9221 "newString(str[, options])",
9222 " Copies str's chars and returns a new string. Valid options:\n"
9223 " \n"
9224 " - tenured: allocate directly into the tenured heap.\n"
9225 " \n"
9226 " - twoByte: create a \"two byte\" string, not a latin1 string, regardless of the\n"
9227 " input string's characters. Latin1 will be used by default if possible\n"
9228 " (again regardless of the input string.)\n"
9229 " \n"
9230 " - external: create an external string. External strings are always twoByte and\n"
9231 " tenured.\n"
9232 " \n"
9233 " - maybeExternal: create an external string, unless the data fits within an\n"
9234 " inline string. Inline strings may be nursery-allocated."),
9236 JS_FN_HELP("ensureLinearString", EnsureLinearString, 1, 0,
9237 "ensureLinearString(str)",
9238 " Ensures str is a linear (non-rope) string and returns it."),
9240 JS_FN_HELP("representativeStringArray", RepresentativeStringArray, 0, 0,
9241 "representativeStringArray()",
9242 " Returns an array of strings that represent the various internal string\n"
9243 " types and character encodings."),
9245 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
9247 JS_FN_HELP("oomThreadTypes", OOMThreadTypes, 0, 0,
9248 "oomThreadTypes()",
9249 " Get the number of thread types that can be used as an argument for\n"
9250 " oomAfterAllocations() and oomAtAllocation()."),
9252 JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 2, 0,
9253 "oomAfterAllocations(count [,threadType])",
9254 " After 'count' js_malloc memory allocations, fail every following allocation\n"
9255 " (return nullptr). The optional thread type limits the effect to the\n"
9256 " specified type of helper thread."),
9258 JS_FN_HELP("oomAtAllocation", OOMAtAllocation, 2, 0,
9259 "oomAtAllocation(count [,threadType])",
9260 " After 'count' js_malloc memory allocations, fail the next allocation\n"
9261 " (return nullptr). The optional thread type limits the effect to the\n"
9262 " specified type of helper thread."),
9264 JS_FN_HELP("resetOOMFailure", ResetOOMFailure, 0, 0,
9265 "resetOOMFailure()",
9266 " Remove the allocation failure scheduled by either oomAfterAllocations() or\n"
9267 " oomAtAllocation() and return whether any allocation had been caused to fail."),
9269 JS_FN_HELP("oomTest", OOMTest, 0, 0,
9270 "oomTest(function, [expectExceptionOnFailure = true | options])",
9271 " Test that the passed function behaves correctly under OOM conditions by\n"
9272 " repeatedly executing it and simulating allocation failure at successive\n"
9273 " allocations until the function completes without seeing a failure.\n"
9274 " By default this tests that an exception is raised if execution fails, but\n"
9275 " this can be disabled by passing false as the optional second parameter.\n"
9276 " This is also disabled when --fuzzing-safe is specified.\n"
9277 " Alternatively an object can be passed to set the following options:\n"
9278 " expectExceptionOnFailure: bool - as described above.\n"
9279 " keepFailing: bool - continue to fail after first simulated failure.\n"
9280 "\n"
9281 " WARNING: By design, oomTest assumes the test-function follows the same\n"
9282 " code path each time it is called, right up to the point where OOM occurs.\n"
9283 " If on iteration 70 it finishes and caches a unit of work that saves 65\n"
9284 " allocations the next time we run, then the subsequent 65 allocation\n"
9285 " points will go untested.\n"
9286 "\n"
9287 " Things in this category include lazy parsing and baseline compilation,\n"
9288 " so it is very easy to accidentally write an oomTest that only tests one\n"
9289 " or the other of those, and not the functionality you meant to test!\n"
9290 " To avoid lazy parsing, call the test function once first before passing\n"
9291 " it to oomTest. The jits can be disabled via the test harness.\n"),
9293 JS_FN_HELP("stackTest", StackTest, 0, 0,
9294 "stackTest(function, [expectExceptionOnFailure = true])",
9295 " This function behaves exactly like oomTest with the difference that\n"
9296 " instead of simulating regular OOM conditions, it simulates the engine\n"
9297 " running out of stack space (failing recursion check).\n"
9298 "\n"
9299 " See the WARNING in help('oomTest').\n"),
9301 JS_FN_HELP("interruptTest", InterruptTest, 0, 0,
9302 "interruptTest(function)",
9303 " This function simulates interrupts similar to how oomTest simulates OOM conditions."
9304 "\n"
9305 " See the WARNING in help('oomTest').\n"),
9307 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
9309 JS_FN_HELP("newRope", NewRope, 3, 0,
9310 "newRope(left, right[, options])",
9311 " Creates a rope with the given left/right strings.\n"
9312 " Available options:\n"
9313 " nursery: bool - force the string to be created in/out of the nursery, if possible.\n"),
9315 JS_FN_HELP("isRope", IsRope, 1, 0,
9316 "isRope(str)",
9317 " Returns true if the parameter is a rope"),
9319 JS_FN_HELP("settlePromiseNow", SettlePromiseNow, 1, 0,
9320 "settlePromiseNow(promise)",
9321 " 'Settle' a 'promise' immediately. This just marks the promise as resolved\n"
9322 " with a value of `undefined` and causes the firing of any onPromiseSettled\n"
9323 " hooks set on Debugger instances that are observing the given promise's\n"
9324 " global as a debuggee."),
9325 JS_FN_HELP("getWaitForAllPromise", GetWaitForAllPromise, 1, 0,
9326 "getWaitForAllPromise(densePromisesArray)",
9327 " Calls the 'GetWaitForAllPromise' JSAPI function and returns the result\n"
9328 " Promise."),
9329 JS_FN_HELP("resolvePromise", ResolvePromise, 2, 0,
9330 "resolvePromise(promise, resolution)",
9331 " Resolve a Promise by calling the JSAPI function JS::ResolvePromise."),
9332 JS_FN_HELP("rejectPromise", RejectPromise, 2, 0,
9333 "rejectPromise(promise, reason)",
9334 " Reject a Promise by calling the JSAPI function JS::RejectPromise."),
9336 JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
9337 "makeFinalizeObserver()",
9338 " Get a special object whose finalization increases the counter returned\n"
9339 " by the finalizeCount function."),
9341 JS_FN_HELP("finalizeCount", FinalizeCount, 0, 0,
9342 "finalizeCount()",
9343 " Return the current value of the finalization counter that is incremented\n"
9344 " each time an object returned by the makeFinalizeObserver is finalized."),
9346 JS_FN_HELP("resetFinalizeCount", ResetFinalizeCount, 0, 0,
9347 "resetFinalizeCount()",
9348 " Reset the value returned by finalizeCount()."),
9350 JS_FN_HELP("gcPreserveCode", GCPreserveCode, 0, 0,
9351 "gcPreserveCode()",
9352 " Preserve JIT code during garbage collections."),
9354 #ifdef JS_GC_ZEAL
9355 JS_FN_HELP("gczeal", GCZeal, 2, 0,
9356 "gczeal([mode, [frequency]])",
9357 gc::ZealModeHelpText),
9359 JS_FN_HELP("unsetgczeal", UnsetGCZeal, 2, 0,
9360 "unsetgczeal(mode)",
9361 " Turn off a single zeal mode set with gczeal() and don't finish any ongoing\n"
9362 " collection that may be happening."),
9364 JS_FN_HELP("schedulegc", ScheduleGC, 1, 0,
9365 "schedulegc([num])",
9366 " If num is given, schedule a GC after num allocations.\n"
9367 " Returns the number of allocations before the next trigger."),
9369 JS_FN_HELP("selectforgc", SelectForGC, 0, 0,
9370 "selectforgc(obj1, obj2, ...)",
9371 " Schedule the given objects to be marked in the next GC slice."),
9373 JS_FN_HELP("verifyprebarriers", VerifyPreBarriers, 0, 0,
9374 "verifyprebarriers()",
9375 " Start or end a run of the pre-write barrier verifier."),
9377 JS_FN_HELP("verifypostbarriers", VerifyPostBarriers, 0, 0,
9378 "verifypostbarriers()",
9379 " Does nothing (the post-write barrier verifier has been remove)."),
9381 JS_FN_HELP("currentgc", CurrentGC, 0, 0,
9382 "currentgc()",
9383 " Report various information about the currently running incremental GC,\n"
9384 " if one is running."),
9386 JS_FN_HELP("deterministicgc", DeterministicGC, 1, 0,
9387 "deterministicgc(true|false)",
9388 " If true, only allow determinstic GCs to run."),
9390 JS_FN_HELP("dumpGCArenaInfo", DumpGCArenaInfo, 0, 0,
9391 "dumpGCArenaInfo()",
9392 " Prints information about the different GC things and how they are arranged\n"
9393 " in arenas.\n"),
9395 JS_FN_HELP("setMarkStackLimit", SetMarkStackLimit, 1, 0,
9396 "markStackLimit(limit)",
9397 " Sets a limit on the number of words used for the mark stack. Used to test OOM"
9398 " handling during marking.\n"),
9400 #endif
9402 JS_FN_HELP("gcstate", GCState, 0, 0,
9403 "gcstate([obj])",
9404 " Report the global GC state, or the GC state for the zone containing |obj|."),
9406 JS_FN_HELP("schedulezone", ScheduleZoneForGC, 1, 0,
9407 "schedulezone([obj | string])",
9408 " If obj is given, schedule a GC of obj's zone.\n"
9409 " If string is given, schedule a GC of the string's zone if possible."),
9411 JS_FN_HELP("startgc", StartGC, 1, 0,
9412 "startgc([n [, 'shrinking']])",
9413 " Start an incremental GC and run a slice that processes about n objects.\n"
9414 " If 'shrinking' is passesd as the optional second argument, perform a\n"
9415 " shrinking GC rather than a normal GC. If no zones have been selected with\n"
9416 " schedulezone(), a full GC will be performed."),
9418 JS_FN_HELP("finishgc", FinishGC, 0, 0,
9419 "finishgc()",
9420 " Finish an in-progress incremental GC, if none is running then do nothing."),
9422 JS_FN_HELP("gcslice", GCSlice, 1, 0,
9423 "gcslice([n [, options]])",
9424 " Start or continue an an incremental GC, running a slice that processes\n"
9425 " about n objects. Takes an optional options object, which may contain the\n"
9426 " following properties:\n"
9427 " dontStart: do not start a new incremental GC if one is not already\n"
9428 " running"),
9430 JS_FN_HELP("abortgc", AbortGC, 1, 0,
9431 "abortgc()",
9432 " Abort the current incremental GC."),
9434 JS_FN_HELP("setMallocMaxDirtyPageModifier", SetMallocMaxDirtyPageModifier, 1, 0,
9435 "setMallocMaxDirtyPageModifier(value)",
9436 " Change the maximum size of jemalloc's page cache. The value should be between\n"
9437 " -5 and 16 (inclusive). See moz_set_max_dirty_page_modifier.\n"),
9439 JS_FN_HELP("fullcompartmentchecks", FullCompartmentChecks, 1, 0,
9440 "fullcompartmentchecks(true|false)",
9441 " If true, check for compartment mismatches before every GC."),
9443 JS_FN_HELP("nondeterministicGetWeakMapKeys", NondeterministicGetWeakMapKeys, 1, 0,
9444 "nondeterministicGetWeakMapKeys(weakmap)",
9445 " Return an array of the keys in the given WeakMap."),
9447 JS_FN_HELP("internalConst", InternalConst, 1, 0,
9448 "internalConst(name)",
9449 " Query an internal constant for the engine. See InternalConst source for\n"
9450 " the list of constant names."),
9452 JS_FN_HELP("isProxy", IsProxy, 1, 0,
9453 "isProxy(obj)",
9454 " If true, obj is a proxy of some sort"),
9456 JS_FN_HELP("dumpHeap", DumpHeap, 1, 0,
9457 "dumpHeap([filename])",
9458 " Dump reachable and unreachable objects to the named file, or to stdout. Objects\n"
9459 " in the nursery are ignored, so if you wish to include them, consider calling\n"
9460 " minorgc() first."),
9462 JS_FN_HELP("terminate", Terminate, 0, 0,
9463 "terminate()",
9464 " Terminate JavaScript execution, as if we had run out of\n"
9465 " memory or been terminated by the slow script dialog."),
9467 JS_FN_HELP("readGeckoProfilingStack", ReadGeckoProfilingStack, 0, 0,
9468 "readGeckoProfilingStack()",
9469 " Reads the JIT/Wasm stack using ProfilingFrameIterator. Skips non-JIT/Wasm frames."),
9471 JS_FN_HELP("readGeckoInterpProfilingStack", ReadGeckoInterpProfilingStack, 0, 0,
9472 "readGeckoInterpProfilingStack()",
9473 " Reads the C++ interpreter profiling stack. Skips JIT/Wasm frames."),
9475 JS_FN_HELP("enableOsiPointRegisterChecks", EnableOsiPointRegisterChecks, 0, 0,
9476 "enableOsiPointRegisterChecks()",
9477 " Emit extra code to verify live regs at the start of a VM call are not\n"
9478 " modified before its OsiPoint."),
9480 JS_FN_HELP("displayName", DisplayName, 1, 0,
9481 "displayName(fn)",
9482 " Gets the display name for a function, which can possibly be a guessed or\n"
9483 " inferred name based on where the function was defined. This can be\n"
9484 " different from the 'name' property on the function."),
9486 JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable, 0, 0,
9487 "isAsmJSCompilationAvailable",
9488 " Returns whether asm.js compilation is currently available or whether it is disabled\n"
9489 " (e.g., by the debugger)."),
9491 JS_FN_HELP("getJitCompilerOptions", GetJitCompilerOptions, 0, 0,
9492 "getJitCompilerOptions()",
9493 " Return an object describing some of the JIT compiler options.\n"),
9495 JS_FN_HELP("isAsmJSModule", IsAsmJSModule, 1, 0,
9496 "isAsmJSModule(fn)",
9497 " Returns whether the given value is a function containing \"use asm\" that has been\n"
9498 " validated according to the asm.js spec."),
9500 JS_FN_HELP("isAsmJSFunction", IsAsmJSFunction, 1, 0,
9501 "isAsmJSFunction(fn)",
9502 " Returns whether the given value is a nested function in an asm.js module that has been\n"
9503 " both compile- and link-time validated."),
9505 JS_FN_HELP("isAvxPresent", IsAvxPresent, 0, 0,
9506 "isAvxPresent([minVersion])",
9507 " Returns whether AVX is present and enabled. If minVersion specified,\n"
9508 " use 1 - to check if AVX is enabled (default), 2 - if AVX2 is enabled."),
9510 JS_FN_HELP("wasmIsSupported", WasmIsSupported, 0, 0,
9511 "wasmIsSupported()",
9512 " Returns a boolean indicating whether WebAssembly is supported on the current device."),
9514 JS_FN_HELP("wasmIsSupportedByHardware", WasmIsSupportedByHardware, 0, 0,
9515 "wasmIsSupportedByHardware()",
9516 " Returns a boolean indicating whether WebAssembly is supported on the current hardware (regardless of whether we've enabled support)."),
9518 JS_FN_HELP("wasmDebuggingEnabled", WasmDebuggingEnabled, 0, 0,
9519 "wasmDebuggingEnabled()",
9520 " Returns a boolean indicating whether WebAssembly debugging is supported on the current device;\n"
9521 " returns false also if WebAssembly is not supported"),
9523 JS_FN_HELP("wasmStreamingEnabled", WasmStreamingEnabled, 0, 0,
9524 "wasmStreamingEnabled()",
9525 " Returns a boolean indicating whether WebAssembly caching is supported by the runtime."),
9527 JS_FN_HELP("wasmCachingEnabled", WasmCachingEnabled, 0, 0,
9528 "wasmCachingEnabled()",
9529 " Returns a boolean indicating whether WebAssembly caching is supported by the runtime."),
9531 JS_FN_HELP("wasmHugeMemorySupported", WasmHugeMemorySupported, 0, 0,
9532 "wasmHugeMemorySupported()",
9533 " Returns a boolean indicating whether WebAssembly supports using a large"
9534 " virtual memory reservation in order to elide bounds checks on this platform."),
9536 JS_FN_HELP("wasmMaxMemoryPages", WasmMaxMemoryPages, 1, 0,
9537 "wasmMaxMemoryPages(indexType)",
9538 " Returns an int with the maximum number of pages that can be allocated to a memory."
9539 " This is an implementation artifact that does depend on the index type, the hardware,"
9540 " the operating system, the build configuration, and flags. The result is constant for"
9541 " a given combination of those; there is no guarantee that that size allocation will"
9542 " always succeed, only that it can succeed in principle. The indexType is a string,"
9543 " 'i32' or 'i64'."),
9545 #define WASM_FEATURE(NAME, ...) \
9546 JS_FN_HELP("wasm" #NAME "Enabled", Wasm##NAME##Enabled, 0, 0, \
9547 "wasm" #NAME "Enabled()", \
9548 " Returns a boolean indicating whether the WebAssembly " #NAME " proposal is enabled."),
9549 JS_FOR_WASM_FEATURES(WASM_FEATURE)
9550 #undef WASM_FEATURE
9552 JS_FN_HELP("wasmThreadsEnabled", WasmThreadsEnabled, 0, 0,
9553 "wasmThreadsEnabled()",
9554 " Returns a boolean indicating whether the WebAssembly threads proposal is\n"
9555 " supported on the current device."),
9557 JS_FN_HELP("wasmSimdEnabled", WasmSimdEnabled, 0, 0,
9558 "wasmSimdEnabled()",
9559 " Returns a boolean indicating whether WebAssembly SIMD proposal is\n"
9560 " supported by the current device."),
9562 #if defined(ENABLE_WASM_SIMD) && defined(DEBUG)
9563 JS_FN_HELP("wasmSimdAnalysis", WasmSimdAnalysis, 1, 0,
9564 "wasmSimdAnalysis(...)",
9565 " Unstable API for white-box testing.\n"),
9566 #endif
9568 JS_FN_HELP("wasmGlobalFromArrayBuffer", WasmGlobalFromArrayBuffer, 2, 0,
9569 "wasmGlobalFromArrayBuffer(type, arrayBuffer)",
9570 " Create a WebAssembly.Global object from a provided ArrayBuffer. The type\n"
9571 " must be POD (i32, i64, f32, f64, v128). The buffer must be the same\n"
9572 " size as the type in bytes.\n"),
9573 JS_FN_HELP("wasmGlobalExtractLane", WasmGlobalExtractLane, 3, 0,
9574 "wasmGlobalExtractLane(global, laneInterp, laneIndex)",
9575 " Extract a lane from a WebAssembly.Global object that contains a v128 value\n"
9576 " and return it as a new WebAssembly.Global object of the appropriate type.\n"
9577 " The supported laneInterp values are i32x4, i64x2, f32x4, and\n"
9578 " f64x2.\n"),
9579 JS_FN_HELP("wasmGlobalsEqual", WasmGlobalsEqual, 2, 0,
9580 "wasmGlobalsEqual(globalA, globalB)",
9581 " Compares two WebAssembly.Global objects for if their types and values are\n"
9582 " equal. Mutability is not compared. Floating point values are compared for\n"
9583 " bitwise equality, not IEEE 754 equality.\n"),
9584 JS_FN_HELP("wasmGlobalIsNaN", WasmGlobalIsNaN, 2, 0,
9585 "wasmGlobalIsNaN(global, flavor)",
9586 " Compares a floating point WebAssembly.Global object for if its value is a\n"
9587 " specific NaN flavor. Valid flavors are `arithmetic_nan` and `canonical_nan`.\n"),
9588 JS_FN_HELP("wasmGlobalToString", WasmGlobalToString, 1, 0,
9589 "wasmGlobalToString(global)",
9590 " Returns a debug representation of the contents of a WebAssembly.Global\n"
9591 " object.\n"),
9592 JS_FN_HELP("wasmLosslessInvoke", WasmLosslessInvoke, 1, 0,
9593 "wasmLosslessInvoke(wasmFunc, args...)",
9594 " Invokes the provided WebAssembly function using a modified conversion\n"
9595 " function that allows providing a param as a WebAssembly.Global and\n"
9596 " returning a result as a WebAssembly.Global.\n"),
9598 JS_FN_HELP("wasmCompilersPresent", WasmCompilersPresent, 0, 0,
9599 "wasmCompilersPresent()",
9600 " Returns a string indicating the present wasm compilers: a comma-separated list\n"
9601 " of 'baseline', 'ion'. A compiler is present in the executable if it is compiled\n"
9602 " in and can generate code for the current architecture."),
9604 JS_FN_HELP("wasmCompileMode", WasmCompileMode, 0, 0,
9605 "wasmCompileMode()",
9606 " Returns a string indicating the available wasm compilers: 'baseline', 'ion',\n"
9607 " 'baseline+ion', or 'none'. A compiler is available if it is present in the\n"
9608 " executable and not disabled by switches or runtime conditions. At most one\n"
9609 " baseline and one optimizing compiler can be available."),
9611 JS_FN_HELP("wasmBaselineDisabledByFeatures", WasmBaselineDisabledByFeatures, 0, 0,
9612 "wasmBaselineDisabledByFeatures()",
9613 " If some feature is enabled at compile-time or run-time that prevents baseline\n"
9614 " from being used then this returns a truthy string describing the features that\n."
9615 " are disabling it. Otherwise it returns false."),
9617 JS_FN_HELP("wasmIonDisabledByFeatures", WasmIonDisabledByFeatures, 0, 0,
9618 "wasmIonDisabledByFeatures()",
9619 " If some feature is enabled at compile-time or run-time that prevents Ion\n"
9620 " from being used then this returns a truthy string describing the features that\n."
9621 " are disabling it. Otherwise it returns false."),
9623 JS_FN_HELP("wasmExtractCode", WasmExtractCode, 1, 0,
9624 "wasmExtractCode(module[, tier])",
9625 " Extracts generated machine code from WebAssembly.Module. The tier is a string,\n"
9626 " 'stable', 'best', 'baseline', or 'ion'; the default is 'stable'. If the request\n"
9627 " cannot be satisfied then null is returned. If the request is 'ion' then block\n"
9628 " until background compilation is complete."),
9630 JS_FN_HELP("wasmDis", WasmDisassemble, 1, 0,
9631 "wasmDis(wasmObject[, options])\n",
9632 " Disassembles generated machine code from an exported WebAssembly function,\n"
9633 " or from all the functions defined in the module or instance, exported and not.\n"
9634 " The `options` is an object with the following optional keys:\n"
9635 " asString: boolean - if true, return a string rather than printing on stderr,\n"
9636 " the default is false.\n"
9637 " tier: string - one of 'stable', 'best', 'baseline', or 'ion'; the default is\n"
9638 " 'stable'.\n"
9639 " kinds: string - if set, and the wasmObject is a module or instance, a\n"
9640 " comma-separated list of the following keys, the default is `Function`:\n"
9641 " Function - functions defined in the module\n"
9642 " InterpEntry - C++-to-wasm stubs\n"
9643 " JitEntry - jitted-js-to-wasm stubs\n"
9644 " ImportInterpExit - wasm-to-C++ stubs\n"
9645 " ImportJitExit - wasm-to-jitted-JS stubs\n"
9646 " all - all kinds, including obscure ones\n"),
9648 JS_FN_HELP("wasmHasTier2CompilationCompleted", WasmHasTier2CompilationCompleted, 1, 0,
9649 "wasmHasTier2CompilationCompleted(module)",
9650 " Returns a boolean indicating whether a given module has finished compiled code for tier2. \n"
9651 "This will return true early if compilation isn't two-tiered. "),
9653 JS_FN_HELP("wasmLoadedFromCache", WasmLoadedFromCache, 1, 0,
9654 "wasmLoadedFromCache(module)",
9655 " Returns a boolean indicating whether a given module was deserialized directly from a\n"
9656 " cache (as opposed to compiled from bytecode)."),
9658 JS_FN_HELP("wasmBuiltinI8VecMul", WasmBuiltinI8VecMul, 0, 0,
9659 "wasmBuiltinI8VecMul()",
9660 " Returns a module that implements an i8 vector pairwise multiplication intrinsic."),
9662 #ifdef ENABLE_WASM_GC
9663 JS_FN_HELP("wasmGcReadField", WasmGcReadField, 2, 0,
9664 "wasmGcReadField(obj, index)",
9665 " Gets a field of a WebAssembly GC struct or array."),
9667 JS_FN_HELP("wasmGcArrayLength", WasmGcArrayLength, 1, 0,
9668 "wasmGcArrayLength(arr)",
9669 " Gets the length of a WebAssembly GC array."),
9670 #endif // ENABLE_WASM_GC
9672 JS_FN_HELP("largeArrayBufferSupported", LargeArrayBufferSupported, 0, 0,
9673 "largeArrayBufferSupported()",
9674 " Returns true if array buffers larger than 2GB can be allocated."),
9676 JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0,
9677 "isLazyFunction(fun)",
9678 " True if fun is a lazy JSFunction."),
9680 JS_FN_HELP("isRelazifiableFunction", IsRelazifiableFunction, 1, 0,
9681 "isRelazifiableFunction(fun)",
9682 " True if fun is a JSFunction with a relazifiable JSScript."),
9684 JS_FN_HELP("hasSameBytecodeData", HasSameBytecodeData, 2, 0,
9685 "hasSameBytecodeData(fun1, fun2)",
9686 " True if fun1 and fun2 share the same copy of bytecode data. This will\n"
9687 " delazify the function if necessary."),
9689 JS_FN_HELP("enableShellAllocationMetadataBuilder", EnableShellAllocationMetadataBuilder, 0, 0,
9690 "enableShellAllocationMetadataBuilder()",
9691 " Use ShellAllocationMetadataBuilder to supply metadata for all newly created objects."),
9693 JS_FN_HELP("getAllocationMetadata", GetAllocationMetadata, 1, 0,
9694 "getAllocationMetadata(obj)",
9695 " Get the metadata for an object."),
9697 JS_INLINABLE_FN_HELP("bailout", testingFunc_bailout, 0, 0, TestBailout,
9698 "bailout()",
9699 " Force a bailout out of ionmonkey (if running in ionmonkey)."),
9701 JS_FN_HELP("bailAfter", testingFunc_bailAfter, 1, 0,
9702 "bailAfter(number)",
9703 " Start a counter to bail once after passing the given amount of possible bailout positions in\n"
9704 " ionmonkey.\n"),
9706 JS_FN_HELP("invalidate", testingFunc_invalidate, 0, 0,
9707 "invalidate()",
9708 " Force an immediate invalidation (if running in Warp)."),
9710 JS_FN_HELP("inJit", testingFunc_inJit, 0, 0,
9711 "inJit()",
9712 " Returns true when called within (jit-)compiled code. When jit compilation is disabled this\n"
9713 " function returns an error string. This function returns false in all other cases.\n"
9714 " Depending on truthiness, you should continue to wait for compilation to happen or stop execution.\n"),
9716 JS_FN_HELP("inIon", testingFunc_inIon, 0, 0,
9717 "inIon()",
9718 " Returns true when called within ion. When ion is disabled or when compilation is abnormally\n"
9719 " slow to start, this function returns an error string. Otherwise, this function returns false.\n"
9720 " This behaviour ensures that a falsy value means that we are not in ion, but expect a\n"
9721 " compilation to occur in the future. Conversely, a truthy value means that we are either in\n"
9722 " ion or that there is litle or no chance of ion ever compiling the current script."),
9724 JS_FN_HELP("assertJitStackInvariants", TestingFunc_assertJitStackInvariants, 0, 0,
9725 "assertJitStackInvariants()",
9726 " Iterates the Jit stack and check that stack invariants hold."),
9728 JS_FN_HELP("setIonCheckGraphCoherency", SetIonCheckGraphCoherency, 1, 0,
9729 "setIonCheckGraphCoherency(bool)",
9730 " Set whether Ion should perform graph consistency (DEBUG-only) assertions. These assertions\n"
9731 " are valuable and should be generally enabled, however they can be very expensive for large\n"
9732 " (wasm) programs."),
9734 JS_FN_HELP("serialize", testingFunc_serialize, 1, 0,
9735 "serialize(data, [transferables, [policy]])",
9736 " Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
9737 " clone buffer object. 'policy' may be an options hash. Valid keys:\n"
9738 " 'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n"
9739 " to specify whether SharedArrayBuffers may be serialized.\n"
9740 " 'scope' - SameProcess, DifferentProcess, or\n"
9741 " DifferentProcessForIndexedDB. Determines how some values will be\n"
9742 " serialized. Clone buffers may only be deserialized with a compatible\n"
9743 " scope. NOTE - For DifferentProcess/DifferentProcessForIndexedDB,\n"
9744 " must also set SharedArrayBuffer:'deny' if data contains any shared memory\n"
9745 " object."),
9747 JS_FN_HELP("deserialize", Deserialize, 1, 0,
9748 "deserialize(clonebuffer[, opts])",
9749 " Deserialize data generated by serialize. 'opts' may be an options hash.\n"
9750 " Valid keys:\n"
9751 " 'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n"
9752 " to specify whether SharedArrayBuffers may be serialized.\n"
9753 " 'scope', which limits the clone buffers that are considered\n"
9754 " valid. Allowed values: ''SameProcess', 'DifferentProcess',\n"
9755 " and 'DifferentProcessForIndexedDB'. So for example, a\n"
9756 " DifferentProcessForIndexedDB clone buffer may be deserialized in any scope, but\n"
9757 " a SameProcess clone buffer cannot be deserialized in a\n"
9758 " DifferentProcess scope."),
9760 JS_FN_HELP("detachArrayBuffer", DetachArrayBuffer, 1, 0,
9761 "detachArrayBuffer(buffer)",
9762 " Detach the given ArrayBuffer object from its memory, i.e. as if it\n"
9763 " had been transferred to a WebWorker."),
9765 JS_FN_HELP("makeSerializable", MakeSerializable, 1, 0,
9766 "makeSerializable(numeric id, [behavior])",
9767 " Make a custom serializable, transferable object. It will have a single accessor\n"
9768 " obj.log that will give a history of all operations on all such objects in the\n"
9769 " current thread as an array [id, action, id, action, ...] where the id\n"
9770 " is the number passed into this function, and the action is one of:\n"
9771 " ? - the canTransfer() hook was called.\n"
9772 " w - the write() hook was called.\n"
9773 " W - the writeTransfer() hook was called.\n"
9774 " R - the readTransfer() hook was called.\n"
9775 " r - the read() hook was called.\n"
9776 " F - the freeTransfer() hook was called.\n"
9777 " The `behavior` parameter can be used to force a failure during processing:\n"
9778 " 1 - fail during readTransfer() hook\n"
9779 " 2 - fail during read() hook\n"
9780 " Set the log to null to clear it."),
9782 JS_FN_HELP("ensureNonInline", EnsureNonInline, 1, 0,
9783 "ensureNonInline(view or buffer)",
9784 " Ensure that the memory for the given ArrayBuffer or ArrayBufferView\n"
9785 " is not inline."),
9787 JS_FN_HELP("JSONStringify", JSONStringify, 4, 0,
9788 "JSONStringify(value, behavior)",
9789 " Same as JSON.stringify(value), but allows setting behavior:\n"
9790 " Normal: the default\n"
9791 " FastOnly: throw if the fast path bails\n"
9792 " SlowOnly: skip the fast path entirely\n"
9793 " Compare: run both the fast and slow paths and compare the result. Crash if\n"
9794 " they do not match. If the fast path bails, no comparison is done."),
9796 JS_FN_HELP("helperThreadCount", HelperThreadCount, 0, 0,
9797 "helperThreadCount()",
9798 " Returns the number of helper threads available for off-thread tasks."),
9800 JS_FN_HELP("createShapeSnapshot", CreateShapeSnapshot, 1, 0,
9801 "createShapeSnapshot(obj)",
9802 " Returns an object containing a shape snapshot for use with\n"
9803 " checkShapeSnapshot.\n"),
9805 JS_FN_HELP("checkShapeSnapshot", CheckShapeSnapshot, 2, 0,
9806 "checkShapeSnapshot(snapshot, [obj])",
9807 " Check shape invariants based on the given snapshot and optional object.\n"
9808 " If there's no object argument, the snapshot's object is used.\n"),
9810 JS_FN_HELP("enableShapeConsistencyChecks", EnableShapeConsistencyChecks, 0, 0,
9811 "enableShapeConsistencyChecks()",
9812 " Enable some slow Shape assertions.\n"),
9814 JS_FN_HELP("reportOutOfMemory", ReportOutOfMemory, 0, 0,
9815 "reportOutOfMemory()",
9816 " Report OOM, then clear the exception and return undefined. For crash testing."),
9818 JS_FN_HELP("throwOutOfMemory", ThrowOutOfMemory, 0, 0,
9819 "throwOutOfMemory()",
9820 " Throw out of memory exception, for OOM handling testing."),
9822 JS_FN_HELP("reportLargeAllocationFailure", ReportLargeAllocationFailure, 0, 0,
9823 "reportLargeAllocationFailure([bytes])",
9824 " Call the large allocation failure callback, as though a large malloc call failed,\n"
9825 " then return undefined. In Gecko, this sends a memory pressure notification, which\n"
9826 " can free up some memory."),
9828 JS_FN_HELP("findPath", FindPath, 2, 0,
9829 "findPath(start, target)",
9830 " Return an array describing one of the shortest paths of GC heap edges from\n"
9831 " |start| to |target|, or |undefined| if |target| is unreachable from |start|.\n"
9832 " Each element of the array is either of the form:\n"
9833 " { node: <object or string>, edge: <string describing edge from node> }\n"
9834 " if the node is a JavaScript object or value; or of the form:\n"
9835 " { type: <string describing node>, edge: <string describing edge> }\n"
9836 " if the node is some internal thing that is not a proper JavaScript value\n"
9837 " (like a shape or a scope chain element). The destination of the i'th array\n"
9838 " element's edge is the node of the i+1'th array element; the destination of\n"
9839 " the last array element is implicitly |target|.\n"),
9841 #if defined(DEBUG)
9842 JS_FN_HELP("wasmMetadataAnalysis", wasmMetadataAnalysis, 1, 0,
9843 "wasmMetadataAnalysis(wasmObject)",
9844 " Prints an analysis of the size of metadata on this wasm object.\n"),
9845 #endif
9847 #if defined(DEBUG) || defined(JS_JITSPEW)
9848 JS_FN_HELP("dumpObject", DumpObject, 1, 0,
9849 "dumpObject()",
9850 " Dump an internal representation of an object."),
9851 #endif
9853 JS_FN_HELP("sharedMemoryEnabled", SharedMemoryEnabled, 0, 0,
9854 "sharedMemoryEnabled()",
9855 " Return true if SharedArrayBuffer and Atomics are enabled"),
9857 JS_FN_HELP("sharedArrayRawBufferRefcount", SharedArrayRawBufferRefcount, 0, 0,
9858 "sharedArrayRawBufferRefcount(sab)",
9859 " Return the reference count of the SharedArrayRawBuffer object held by sab"),
9861 #ifdef NIGHTLY_BUILD
9862 JS_FN_HELP("objectAddress", ObjectAddress, 1, 0,
9863 "objectAddress(obj)",
9864 " Return the current address of the object. For debugging only--this\n"
9865 " address may change during a moving GC."),
9867 JS_FN_HELP("sharedAddress", SharedAddress, 1, 0,
9868 "sharedAddress(obj)",
9869 " Return the address of the shared storage of a SharedArrayBuffer."),
9870 #endif
9872 JS_FN_HELP("hasInvalidatedTeleporting", HasInvalidatedTeleporting, 1, 0,
9873 "hasInvalidatedTeleporting(obj)",
9874 " Return true if the shape teleporting optimization has been disabled for |obj|."),
9876 JS_FN_HELP("evalReturningScope", EvalReturningScope, 1, 0,
9877 "evalReturningScope(scriptStr, [global])",
9878 " Evaluate the script in a new scope and return the scope.\n"
9879 " If |global| is present, clone the script to |global| before executing."),
9881 JS_FN_HELP("backtrace", DumpBacktrace, 1, 0,
9882 "backtrace()",
9883 " Dump out a brief backtrace."),
9885 JS_FN_HELP("getBacktrace", GetBacktrace, 1, 0,
9886 "getBacktrace([options])",
9887 " Return the current stack as a string. Takes an optional options object,\n"
9888 " which may contain any or all of the boolean properties:\n"
9889 " options.args - show arguments to each function\n"
9890 " options.locals - show local variables in each frame\n"
9891 " options.thisprops - show the properties of the 'this' object of each frame\n"),
9893 JS_FN_HELP("byteSize", ByteSize, 1, 0,
9894 "byteSize(value)",
9895 " Return the size in bytes occupied by |value|, or |undefined| if value\n"
9896 " is not allocated in memory.\n"),
9898 JS_FN_HELP("byteSizeOfScript", ByteSizeOfScript, 1, 0,
9899 "byteSizeOfScript(f)",
9900 " Return the size in bytes occupied by the function |f|'s JSScript.\n"),
9902 JS_FN_HELP("setImmutablePrototype", SetImmutablePrototype, 1, 0,
9903 "setImmutablePrototype(obj)",
9904 " Try to make obj's [[Prototype]] immutable, such that subsequent attempts to\n"
9905 " change it will fail. Return true if obj's [[Prototype]] was successfully made\n"
9906 " immutable (or if it already was immutable), false otherwise. Throws in case\n"
9907 " of internal error, or if the operation doesn't even make sense (for example,\n"
9908 " because the object is a revoked proxy)."),
9910 #ifdef DEBUG
9911 JS_FN_HELP("dumpStringRepresentation", DumpStringRepresentation, 1, 0,
9912 "dumpStringRepresentation(str)",
9913 " Print a human-readable description of how the string |str| is represented.\n"),
9915 JS_FN_HELP("stringRepresentation", GetStringRepresentation, 1, 0,
9916 "stringRepresentation(str)",
9917 " Return a human-readable description of how the string |str| is represented.\n"),
9919 #endif
9921 JS_FN_HELP("allocationMarker", AllocationMarker, 0, 0,
9922 "allocationMarker([options])",
9923 " Return a freshly allocated object whose [[Class]] name is\n"
9924 " \"AllocationMarker\". Such objects are allocated only by calls\n"
9925 " to this function, never implicitly by the system, making them\n"
9926 " suitable for use in allocation tooling tests. Takes an optional\n"
9927 " options object which may contain the following properties:\n"
9928 " * nursery: bool, whether to allocate the object in the nursery\n"),
9930 JS_FN_HELP("setGCCallback", SetGCCallback, 1, 0,
9931 "setGCCallback({action:\"...\", options...})",
9932 " Set the GC callback. action may be:\n"
9933 " 'minorGC' - run a nursery collection\n"
9934 " 'majorGC' - run a major collection, nesting up to a given 'depth'\n"),
9936 #ifdef DEBUG
9937 JS_FN_HELP("enqueueMark", EnqueueMark, 1, 0,
9938 "enqueueMark(obj|string)",
9939 " Add an object to the queue of objects to mark at the beginning every GC. (Note\n"
9940 " that the objects will actually be marked at the beginning of every slice, but\n"
9941 " after the first slice they will already be marked so nothing will happen.)\n"
9942 " \n"
9943 " Instead of an object, a few magic strings may be used:\n"
9944 " 'yield' - cause the current marking slice to end, as if the mark budget were\n"
9945 " exceeded.\n"
9946 " 'enter-weak-marking-mode' - divide the list into two segments. The items after\n"
9947 " this string will not be marked until we enter weak marking mode. Note that weak\n"
9948 " marking mode may be entered zero or multiple times for one GC.\n"
9949 " 'abort-weak-marking-mode' - same as above, but then abort weak marking to fall back\n"
9950 " on the old iterative marking code path.\n"
9951 " 'drain' - fully drain the mark stack before continuing.\n"
9952 " 'set-color-black' - force everything following in the mark queue to be marked black.\n"
9953 " 'set-color-gray' - continue with the regular GC until gray marking is possible, then force\n"
9954 " everything following in the mark queue to be marked gray.\n"
9955 " 'unset-color' - stop forcing the mark color."),
9957 JS_FN_HELP("clearMarkQueue", ClearMarkQueue, 0, 0,
9958 "clearMarkQueue()",
9959 " Cancel the special marking of all objects enqueue with enqueueMark()."),
9961 JS_FN_HELP("getMarkQueue", GetMarkQueue, 0, 0,
9962 "getMarkQueue()",
9963 " Return the current mark queue set up via enqueueMark calls. Note that all\n"
9964 " returned values will be wrapped into the current compartment, so this loses\n"
9965 " some fidelity."),
9966 #endif // DEBUG
9968 JS_FN_HELP("nurseryStringsEnabled", NurseryStringsEnabled, 0, 0,
9969 "nurseryStringsEnabled()",
9970 " Return whether strings are currently allocated in the nursery for the current\n"
9971 " global\n"),
9973 JS_FN_HELP("isNurseryAllocated", IsNurseryAllocated, 1, 0,
9974 "isNurseryAllocated(thing)",
9975 " Return whether a GC thing is nursery allocated.\n"),
9977 JS_FN_HELP("numAllocSitesPretenured", NumAllocSitesPretenured, 0, 0,
9978 "numAllocSitesPretenured()",
9979 " Return the number of allocation sites that were pretenured for the current\n"
9980 " global\n"),
9982 JS_FN_HELP("getLcovInfo", GetLcovInfo, 1, 0,
9983 "getLcovInfo(global)",
9984 " Generate LCOV tracefile for the given compartment. If no global are provided then\n"
9985 " the current global is used as the default one.\n"),
9987 #ifdef DEBUG
9988 JS_FN_HELP("setRNGState", SetRNGState, 2, 0,
9989 "setRNGState(seed0, seed1)",
9990 " Set this compartment's RNG state.\n"),
9991 #endif
9993 #ifdef AFLFUZZ
9994 JS_FN_HELP("aflloop", AflLoop, 1, 0,
9995 "aflloop(max_cnt)",
9996 " Call the __AFL_LOOP() runtime function (see AFL docs)\n"),
9997 #endif
9999 JS_FN_HELP("monotonicNow", MonotonicNow, 0, 0,
10000 "monotonicNow()",
10001 " Return a timestamp reflecting the current elapsed system time.\n"
10002 " This is monotonically increasing.\n"),
10004 JS_FN_HELP("timeSinceCreation", TimeSinceCreation, 0, 0,
10005 "TimeSinceCreation()",
10006 " Returns the time in milliseconds since process creation.\n"
10007 " This uses a clock compatible with the profiler.\n"),
10009 JS_FN_HELP("isConstructor", IsConstructor, 1, 0,
10010 "isConstructor(value)",
10011 " Returns whether the value is considered IsConstructor.\n"),
10013 JS_FN_HELP("getTimeZone", GetTimeZone, 0, 0,
10014 "getTimeZone()",
10015 " Get the current time zone.\n"),
10017 JS_FN_HELP("getDefaultLocale", GetDefaultLocale, 0, 0,
10018 "getDefaultLocale()",
10019 " Get the current default locale.\n"),
10021 JS_FN_HELP("getCoreCount", GetCoreCount, 0, 0,
10022 "getCoreCount()",
10023 " Get the number of CPU cores from the platform layer. Typically this\n"
10024 " means the number of hyperthreads on systems where that makes sense.\n"),
10026 JS_FN_HELP("setTimeResolution", SetTimeResolution, 2, 0,
10027 "setTimeResolution(resolution, jitter)",
10028 " Enables time clamping and jittering. Specify a time resolution in\n"
10029 " microseconds and whether or not to jitter\n"),
10031 JS_FN_HELP("scriptedCallerGlobal", ScriptedCallerGlobal, 0, 0,
10032 "scriptedCallerGlobal()",
10033 " Get the caller's global (or null). See JS::GetScriptedCallerGlobal.\n"),
10035 JS_FN_HELP("objectGlobal", ObjectGlobal, 1, 0,
10036 "objectGlobal(obj)",
10037 " Returns the object's global object or null if the object is a wrapper.\n"),
10039 JS_FN_HELP("isSameCompartment", IsSameCompartment, 2, 0,
10040 "isSameCompartment(obj1, obj2)",
10041 " Unwraps obj1 and obj2 and returns whether the unwrapped objects are\n"
10042 " same-compartment.\n"),
10044 JS_FN_HELP("firstGlobalInCompartment", FirstGlobalInCompartment, 1, 0,
10045 "firstGlobalInCompartment(obj)",
10046 " Returns the first global in obj's compartment.\n"),
10048 JS_FN_HELP("assertCorrectRealm", AssertCorrectRealm, 0, 0,
10049 "assertCorrectRealm()",
10050 " Asserts cx->realm matches callee->realm.\n"),
10052 JS_FN_HELP("globalLexicals", GlobalLexicals, 0, 0,
10053 "globalLexicals()",
10054 " Returns an object containing a copy of all global lexical bindings.\n"
10055 " Example use: let x = 1; assertEq(globalLexicals().x, 1);\n"),
10057 JS_FN_HELP("baselineCompile", BaselineCompile, 2, 0,
10058 "baselineCompile([fun/code], forceDebugInstrumentation=false)",
10059 " Baseline-compiles the given JS function or script.\n"
10060 " Without arguments, baseline-compiles the caller's script; but note\n"
10061 " that extra boilerplate is needed afterwards to cause the VM to start\n"
10062 " running the jitcode rather than staying in the interpreter:\n"
10063 " baselineCompile(); for (var i=0; i<1; i++) {} ...\n"
10064 " The interpreter will enter the new jitcode at the loop header unless\n"
10065 " baselineCompile returned a string or threw an error.\n"),
10067 JS_FN_HELP("encodeAsUtf8InBuffer", EncodeAsUtf8InBuffer, 2, 0,
10068 "encodeAsUtf8InBuffer(str, uint8Array)",
10069 " Encode as many whole code points from the string str into the provided\n"
10070 " Uint8Array as will completely fit in it, converting lone surrogates to\n"
10071 " REPLACEMENT CHARACTER. Return an array [r, w] where |r| is the\n"
10072 " number of 16-bit units read and |w| is the number of bytes of UTF-8\n"
10073 " written."),
10075 JS_FN_HELP("clearKeptObjects", ClearKeptObjects, 0, 0,
10076 "clearKeptObjects()",
10077 "Perform the ECMAScript ClearKeptObjects operation, clearing the list of\n"
10078 "observed WeakRef targets that are kept alive until the next synchronous\n"
10079 "sequence of ECMAScript execution completes. This is used for testing\n"
10080 "WeakRefs.\n"),
10082 JS_FN_HELP("numberToDouble", NumberToDouble, 1, 0,
10083 "numberToDouble(number)",
10084 " Return the input number as double-typed number."),
10086 JS_FN_HELP("getICUOptions", GetICUOptions, 0, 0,
10087 "getICUOptions()",
10088 " Return an object describing the following ICU options.\n\n"
10089 " version: a string containing the ICU version number, e.g. '67.1'\n"
10090 " unicode: a string containing the Unicode version number, e.g. '13.0'\n"
10091 " locale: the ICU default locale, e.g. 'en_US'\n"
10092 " tzdata: a string containing the tzdata version number, e.g. '2020a'\n"
10093 " timezone: the ICU default time zone, e.g. 'America/Los_Angeles'\n"
10094 " host-timezone: the host time zone, e.g. 'America/Los_Angeles'"),
10096 JS_FN_HELP("getAvailableLocalesOf", GetAvailableLocalesOf, 0, 0,
10097 "getAvailableLocalesOf(name)",
10098 " Return an array of all available locales for the given Intl constuctor."),
10100 JS_FN_HELP("isSmallFunction", IsSmallFunction, 1, 0,
10101 "isSmallFunction(fun)",
10102 " Returns true if a scripted function is small enough to be inlinable."),
10104 JS_FN_HELP("compileToStencil", CompileToStencil, 1, 0,
10105 "compileToStencil(string, [options])",
10106 " Parses the given string argument as js script, returns the stencil"
10107 " for it."),
10109 JS_FN_HELP("evalStencil", EvalStencil, 1, 0,
10110 "evalStencil(stencil, [options])",
10111 " Instantiates the given stencil, and evaluates the top-level script it"
10112 " defines."),
10114 JS_FN_HELP("compileToStencilXDR", CompileToStencilXDR, 1, 0,
10115 "compileToStencilXDR(string, [options])",
10116 " Parses the given string argument as js script, produces the stencil"
10117 " for it, XDR-encodes the stencil, and returns an object that contains the"
10118 " XDR buffer."),
10120 JS_FN_HELP("evalStencilXDR", EvalStencilXDR, 1, 0,
10121 "evalStencilXDR(stencilXDR, [options])",
10122 " Reads the given stencil XDR object, and evaluates the top-level script it"
10123 " defines."),
10125 JS_FN_HELP("getExceptionInfo", GetExceptionInfo, 1, 0,
10126 "getExceptionInfo(fun)",
10127 " Calls the given function and returns information about the exception it"
10128 " throws. Returns null if the function didn't throw an exception."),
10130 JS_FN_HELP("nukeCCW", NukeCCW, 1, 0,
10131 "nukeCCW(wrapper)",
10132 " Nuke a CrossCompartmentWrapper, which turns it into a DeadProxyObject."),
10134 JS_FN_HELP("assertRealmFuseInvariants", AssertRealmFuseInvariants, 0, 0,
10135 "assertRealmFuseInvariants()",
10136 " Runs the realm's fuse invariant checks -- these will crash on failure. "
10137 " Only available in fuzzing or debug builds, so usage should be guarded. "),
10139 JS_FN_HELP("popAllFusesInRealm", PopAllFusesInRealm, 0, 0,
10140 "popAllFusesInRealm()",
10141 " Pops all the fuses in the current realm"),
10143 JS_FS_HELP_END
10145 // clang-format on
10147 // clang-format off
10148 static const JSFunctionSpecWithHelp FuzzingUnsafeTestingFunctions[] = {
10149 JS_FN_HELP("getErrorNotes", GetErrorNotes, 1, 0,
10150 "getErrorNotes(error)",
10151 " Returns an array of error notes."),
10153 JS_FN_HELP("setTimeZone", SetTimeZone, 1, 0,
10154 "setTimeZone(tzname)",
10155 " Set the 'TZ' environment variable to the given time zone and applies the new time zone.\n"
10156 " The time zone given is validated according to the current environment.\n"
10157 " An empty string or undefined resets the time zone to its default value."),
10159 JS_FN_HELP("setDefaultLocale", SetDefaultLocale, 1, 0,
10160 "setDefaultLocale(locale)",
10161 " Set the runtime default locale to the given value.\n"
10162 " An empty string or undefined resets the runtime locale to its default value.\n"
10163 " NOTE: The input string is not fully validated, it must be a valid BCP-47 language tag."),
10165 JS_FN_HELP("isInStencilCache", IsInStencilCache, 1, 0,
10166 "isInStencilCache(fun)",
10167 " True if fun is available in the stencil cache."),
10169 JS_FN_HELP("waitForStencilCache", WaitForStencilCache, 1, 0,
10170 "waitForStencilCache(fun)",
10171 " Block main thread execution until the function is made available in the cache."),
10173 JS_FN_HELP("getInnerMostEnvironmentObject", GetInnerMostEnvironmentObject, 0, 0,
10174 "getInnerMostEnvironmentObject()",
10175 " Return the inner-most environment object for current execution."),
10177 JS_FN_HELP("getEnclosingEnvironmentObject", GetEnclosingEnvironmentObject, 1, 0,
10178 "getEnclosingEnvironmentObject(env)",
10179 " Return the enclosing environment object for given environment object."),
10181 JS_FN_HELP("getEnvironmentObjectType", GetEnvironmentObjectType, 1, 0,
10182 "getEnvironmentObjectType(env)",
10183 " Return a string represents the type of given environment object."),
10185 JS_FN_HELP("shortestPaths", ShortestPaths, 3, 0,
10186 "shortestPaths(targets, options)",
10187 " Return an array of arrays of shortest retaining paths. There is an array of\n"
10188 " shortest retaining paths for each object in |targets|. Each element in a path\n"
10189 " is of the form |{ predecessor, edge }|. |options| may contain:\n"
10190 " \n"
10191 " maxNumPaths: The maximum number of paths returned in each of those arrays\n"
10192 " (default 3).\n"
10193 " start: The object to start all paths from. If not given, then\n"
10194 " the starting point will be the set of GC roots."),
10196 JS_FN_HELP("getFuseState", GetFuseState, 0, 0,
10197 "getFuseState()",
10198 " Return an object describing the calling realm's fuse state, "
10199 "as well as the state of any runtime fuses."),
10201 JS_FS_HELP_END
10203 // clang-format on
10205 // clang-format off
10206 static const JSFunctionSpecWithHelp PCCountProfilingTestingFunctions[] = {
10207 JS_FN_HELP("start", PCCountProfiling_Start, 0, 0,
10208 "start()",
10209 " Start PC count profiling."),
10211 JS_FN_HELP("stop", PCCountProfiling_Stop, 0, 0,
10212 "stop()",
10213 " Stop PC count profiling."),
10215 JS_FN_HELP("purge", PCCountProfiling_Purge, 0, 0,
10216 "purge()",
10217 " Purge the collected PC count profiling data."),
10219 JS_FN_HELP("count", PCCountProfiling_ScriptCount, 0, 0,
10220 "count()",
10221 " Return the number of profiled scripts."),
10223 JS_FN_HELP("summary", PCCountProfiling_ScriptSummary, 1, 0,
10224 "summary(index)",
10225 " Return the PC count profiling summary for the given script index.\n"
10226 " The script index must be in the range [0, pc.count())."),
10228 JS_FN_HELP("contents", PCCountProfiling_ScriptContents, 1, 0,
10229 "contents(index)",
10230 " Return the complete profiling contents for the given script index.\n"
10231 " The script index must be in the range [0, pc.count())."),
10233 JS_FS_HELP_END
10235 // clang-format on
10237 // clang-format off
10238 static const JSFunctionSpecWithHelp FdLibMTestingFunctions[] = {
10239 JS_FN_HELP("pow", FdLibM_Pow, 2, 0,
10240 "pow(x, y)",
10241 " Return x ** y."),
10243 JS_FS_HELP_END
10245 // clang-format on
10247 bool js::InitTestingFunctions() { return disasmBuf.init(); }
10249 bool js::DefineTestingFunctions(JSContext* cx, HandleObject obj,
10250 bool fuzzingSafe_, bool disableOOMFunctions_) {
10251 fuzzingSafe = fuzzingSafe_;
10252 if (EnvVarIsDefined("MOZ_FUZZING_SAFE")) {
10253 fuzzingSafe = true;
10256 disableOOMFunctions = disableOOMFunctions_;
10258 if (!fuzzingSafe) {
10259 if (!JS_DefineFunctionsWithHelp(cx, obj, FuzzingUnsafeTestingFunctions)) {
10260 return false;
10263 RootedObject pccount(cx, JS_NewPlainObject(cx));
10264 if (!pccount) {
10265 return false;
10268 if (!JS_DefineProperty(cx, obj, "pccount", pccount, 0)) {
10269 return false;
10272 if (!JS_DefineFunctionsWithHelp(cx, pccount,
10273 PCCountProfilingTestingFunctions)) {
10274 return false;
10278 RootedObject fdlibm(cx, JS_NewPlainObject(cx));
10279 if (!fdlibm) {
10280 return false;
10283 if (!JS_DefineProperty(cx, obj, "fdlibm", fdlibm, 0)) {
10284 return false;
10287 if (!JS_DefineFunctionsWithHelp(cx, fdlibm, FdLibMTestingFunctions)) {
10288 return false;
10291 return JS_DefineFunctionsWithHelp(cx, obj, TestingFunctions);
10294 #ifdef FUZZING_JS_FUZZILLI
10295 uint32_t js::FuzzilliHashDouble(double value) {
10296 // We shouldn't GC here as this is called directly from IC code.
10297 AutoUnsafeCallWithABI unsafe;
10298 uint64_t v = mozilla::BitwiseCast<uint64_t>(value);
10299 return static_cast<uint32_t>(v) + static_cast<uint32_t>(v >> 32);
10302 uint32_t js::FuzzilliHashBigInt(BigInt* bigInt) {
10303 // We shouldn't GC here as this is called directly from IC code.
10304 AutoUnsafeCallWithABI unsafe;
10305 return bigInt->hash();
10308 void js::FuzzilliHashObject(JSContext* cx, JSObject* obj) {
10309 // called from IC and baseline/interpreter
10310 uint32_t hash;
10311 FuzzilliHashObjectInl(cx, obj, &hash);
10313 cx->executionHashInputs += 1;
10314 cx->executionHash = mozilla::RotateLeft(cx->executionHash + hash, 1);
10317 void js::FuzzilliHashObjectInl(JSContext* cx, JSObject* obj, uint32_t* out) {
10318 *out = 0;
10319 if (!js::SupportDifferentialTesting()) {
10320 return;
10323 RootedValue v(cx);
10324 v.setObject(*obj);
10326 JSAutoStructuredCloneBuffer JSCloner(
10327 JS::StructuredCloneScope::DifferentProcess, nullptr, nullptr);
10328 if (JSCloner.write(cx, v)) {
10329 JSStructuredCloneData& data = JSCloner.data();
10330 data.ForEachDataChunk([&](const char* aData, size_t aSize) {
10331 uint32_t h = mozilla::HashBytes(aData, aSize);
10332 h = (h << 1) | 1;
10333 *out ^= h;
10334 *out *= h;
10335 return true;
10337 } else if (JS_IsExceptionPending(cx)) {
10338 JS_ClearPendingException(cx);
10341 #endif