Bug 1921551 - React to sync sign in flow correctly r=android-reviewers,matt-tighe
[gecko.git] / js / src / builtin / TestingFunctions.cpp
blob7c0456d6a8d0c72b995e62278796d3528f4b6c3e
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/RefPtr.h"
20 #include "mozilla/ScopeExit.h"
21 #include "mozilla/Span.h"
22 #include "mozilla/Sprintf.h"
23 #include "mozilla/StringBuffer.h"
24 #include "mozilla/TextUtils.h"
25 #include "mozilla/ThreadLocal.h"
27 #include <algorithm>
28 #include <cfloat>
29 #include <cinttypes>
30 #include <cmath>
31 #include <cstdlib>
32 #include <ctime>
33 #include <functional>
34 #include <initializer_list>
35 #include <iterator>
36 #include <utility>
38 #if defined(XP_UNIX) && !defined(XP_DARWIN)
39 # include <time.h>
40 #else
41 # include <chrono>
42 #endif
44 #include "fdlibm.h"
45 #include "jsapi.h"
46 #include "jsfriendapi.h"
48 #ifdef JS_HAS_INTL_API
49 # include "builtin/intl/CommonFunctions.h"
50 # include "builtin/intl/FormatBuffer.h"
51 # include "builtin/intl/SharedIntlData.h"
52 #endif
53 #include "builtin/BigInt.h"
54 #include "builtin/JSON.h"
55 #include "builtin/MapObject.h"
56 #include "builtin/Promise.h"
57 #include "builtin/TestingUtility.h" // js::ParseCompileOptions, js::ParseDebugMetadata
58 #include "ds/IdValuePair.h" // js::IdValuePair
59 #include "frontend/BytecodeCompiler.h" // frontend::{CompileGlobalScriptToExtensibleStencil,ParseModuleToExtensibleStencil}
60 #include "frontend/CompilationStencil.h" // frontend::CompilationStencil
61 #include "frontend/FrontendContext.h" // AutoReportFrontendContext
62 #include "gc/GC.h"
63 #include "gc/GCEnum.h"
64 #include "gc/GCLock.h"
65 #include "gc/Zone.h"
66 #include "jit/BaselineJIT.h"
67 #include "jit/Disassemble.h"
68 #include "jit/InlinableNatives.h"
69 #include "jit/Invalidation.h"
70 #include "jit/Ion.h"
71 #include "jit/JitOptions.h"
72 #include "jit/JitRuntime.h"
73 #include "jit/TrialInlining.h"
74 #include "js/Array.h" // JS::NewArrayObject
75 #include "js/ArrayBuffer.h" // JS::{DetachArrayBuffer,GetArrayBufferLengthAndData,NewArrayBufferWithContents}
76 #include "js/CallAndConstruct.h" // JS::Call, JS::IsCallable, JS::IsConstructor, JS_CallFunction
77 #include "js/CharacterEncoding.h"
78 #include "js/CompilationAndEvaluation.h"
79 #include "js/CompileOptions.h"
80 #include "js/Conversions.h"
81 #include "js/Date.h"
82 #include "js/experimental/CodeCoverage.h" // js::GetCodeCoverageSummary
83 #include "js/experimental/CompileScript.h" // JS::ParseGlobalScript, JS::PrepareForInstantiate
84 #include "js/experimental/JSStencil.h" // JS::Stencil
85 #include "js/experimental/PCCountProfiling.h" // JS::{Start,Stop}PCCountProfiling, JS::PurgePCCounts, JS::GetPCCountScript{Count,Summary,Contents}
86 #include "js/experimental/TypedData.h" // JS_GetObjectAsUint8Array
87 #include "js/friend/DumpFunctions.h" // js::Dump{Backtrace,Heap,Object}, JS::FormatStackDump, js::IgnoreNurseryObjects
88 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
89 #include "js/friend/WindowProxy.h" // js::ToWindowProxyIfWindow
90 #include "js/GlobalObject.h"
91 #include "js/HashTable.h"
92 #include "js/Interrupt.h"
93 #include "js/LocaleSensitive.h"
94 #include "js/Prefs.h"
95 #include "js/Printf.h"
96 #include "js/PropertyAndElement.h" // JS_DefineProperties, JS_DefineProperty, JS_DefinePropertyById, JS_Enumerate, JS_GetProperty, JS_GetPropertyById, JS_HasProperty, JS_SetElement, JS_SetProperty
97 #include "js/PropertySpec.h"
98 #include "js/SourceText.h"
99 #include "js/StableStringChars.h"
100 #include "js/Stack.h"
101 #include "js/String.h" // JS::GetLinearStringLength, JS::StringToLinearString
102 #include "js/StructuredClone.h"
103 #include "js/UbiNode.h"
104 #include "js/UbiNodeBreadthFirst.h"
105 #include "js/UbiNodeShortestPaths.h"
106 #include "js/UniquePtr.h"
107 #include "js/Vector.h"
108 #include "js/Wrapper.h"
109 #include "threading/CpuCount.h"
110 #include "util/DifferentialTesting.h"
111 #include "util/StringBuilder.h"
112 #include "util/Text.h"
113 #include "vm/BooleanObject.h"
114 #include "vm/DateObject.h"
115 #include "vm/DateTime.h"
116 #include "vm/ErrorObject.h"
117 #include "vm/GlobalObject.h"
118 #include "vm/HelperThreads.h"
119 #include "vm/HelperThreadState.h"
120 #include "vm/Interpreter.h"
121 #include "vm/JSContext.h"
122 #include "vm/JSObject.h"
123 #include "vm/NumberObject.h"
124 #include "vm/PlainObject.h" // js::PlainObject
125 #include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseSlot_*
126 #include "vm/ProxyObject.h"
127 #include "vm/RealmFuses.h"
128 #include "vm/SavedStacks.h"
129 #include "vm/ScopeKind.h"
130 #include "vm/Stack.h"
131 #include "vm/StencilCache.h" // DelazificationCache
132 #include "vm/StencilObject.h" // StencilObject, StencilXDRBufferObject
133 #include "vm/StringObject.h"
134 #include "vm/StringType.h"
135 #include "wasm/AsmJS.h"
136 #include "wasm/WasmBaselineCompile.h"
137 #include "wasm/WasmBuiltinModule.h"
138 #include "wasm/WasmFeatures.h"
139 #include "wasm/WasmGcObject.h"
140 #include "wasm/WasmInstance.h"
141 #include "wasm/WasmIonCompile.h"
142 #include "wasm/WasmJS.h"
143 #include "wasm/WasmModule.h"
144 #include "wasm/WasmValType.h"
145 #include "wasm/WasmValue.h"
147 #include "debugger/DebugAPI-inl.h"
148 #include "vm/Compartment-inl.h"
149 #include "vm/EnvironmentObject-inl.h"
150 #include "vm/JSContext-inl.h"
151 #include "vm/JSObject-inl.h"
152 #include "vm/NativeObject-inl.h"
153 #include "vm/ObjectFlags-inl.h"
154 #include "vm/StringType-inl.h"
155 #include "wasm/WasmInstance-inl.h"
157 using namespace js;
159 using mozilla::AssertedCast;
160 using mozilla::AsWritableChars;
161 using mozilla::Maybe;
162 using mozilla::Span;
164 using JS::AutoStableStringChars;
165 using JS::CompileOptions;
166 using JS::SliceBudget;
167 using JS::SourceText;
168 using JS::WorkBudget;
170 // If fuzzingSafe is set, remove functionality that could cause problems with
171 // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
172 mozilla::Atomic<bool> js::fuzzingSafe(false);
174 // If disableOOMFunctions is set, disable functionality that causes artificial
175 // OOM conditions.
176 static mozilla::Atomic<bool> disableOOMFunctions(false);
178 static bool EnvVarIsDefined(const char* name) {
179 const char* value = getenv(name);
180 return value && *value;
183 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
184 static bool EnvVarAsInt(const char* name, int* valueOut) {
185 if (!EnvVarIsDefined(name)) {
186 return false;
189 *valueOut = atoi(getenv(name));
190 return true;
192 #endif
194 static bool GetRealmConfiguration(JSContext* cx, unsigned argc, Value* vp) {
195 CallArgs args = CallArgsFromVp(argc, vp);
196 RootedObject callee(cx, &args.callee());
197 RootedObject info(cx, JS_NewPlainObject(cx));
198 if (!info) {
199 return false;
201 if (args.length() > 1) {
202 ReportUsageErrorASCII(cx, callee, "Must have zero or one arguments");
203 return false;
205 if (args.length() == 1 && !args[0].isString()) {
206 ReportUsageErrorASCII(cx, callee, "Argument must be a string");
207 return false;
210 bool importAttributes = cx->options().importAttributes();
211 if (!JS_SetProperty(cx, info, "importAttributes",
212 importAttributes ? TrueHandleValue : FalseHandleValue)) {
213 return false;
216 if (args.length() == 1) {
217 RootedString str(cx, ToString(cx, args[0]));
218 if (!str) {
219 return false;
221 RootedId id(cx);
222 if (!JS_StringToId(cx, str, &id)) {
223 return false;
226 bool hasProperty;
227 if (JS_HasPropertyById(cx, info, id, &hasProperty) && hasProperty) {
228 // Returning a true/false from GetProperty
229 return GetProperty(cx, info, info, id, args.rval());
232 ReportUsageErrorASCII(cx, callee, "Invalid option name");
233 return false;
236 args.rval().setObject(*info);
237 return true;
240 static bool GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) {
241 CallArgs args = CallArgsFromVp(argc, vp);
242 RootedObject callee(cx, &args.callee());
243 RootedObject info(cx, JS_NewPlainObject(cx));
244 if (!info) {
245 return false;
247 if (args.length() > 1) {
248 ReportUsageErrorASCII(cx, callee, "Must have zero or one arguments");
249 return false;
251 if (args.length() == 1 && !args[0].isString()) {
252 ReportUsageErrorASCII(cx, callee, "Argument must be a string");
253 return false;
256 if (!JS_SetProperty(cx, info, "rooting-analysis", FalseHandleValue)) {
257 return false;
260 if (!JS_SetProperty(cx, info, "exact-rooting", TrueHandleValue)) {
261 return false;
264 if (!JS_SetProperty(cx, info, "trace-jscalls-api", FalseHandleValue)) {
265 return false;
268 if (!JS_SetProperty(cx, info, "incremental-gc", TrueHandleValue)) {
269 return false;
272 if (!JS_SetProperty(cx, info, "generational-gc", TrueHandleValue)) {
273 return false;
276 if (!JS_SetProperty(cx, info, "oom-backtraces", FalseHandleValue)) {
277 return false;
280 RootedValue value(cx);
281 #ifdef DEBUG
282 value = BooleanValue(true);
283 #else
284 value = BooleanValue(false);
285 #endif
286 if (!JS_SetProperty(cx, info, "debug", value)) {
287 return false;
290 #ifdef RELEASE_OR_BETA
291 value = BooleanValue(true);
292 #else
293 value = BooleanValue(false);
294 #endif
295 if (!JS_SetProperty(cx, info, "release_or_beta", value)) {
296 return false;
299 #ifdef EARLY_BETA_OR_EARLIER
300 value = BooleanValue(true);
301 #else
302 value = BooleanValue(false);
303 #endif
304 if (!JS_SetProperty(cx, info, "early_beta_or_earlier", value)) {
305 return false;
308 #ifdef MOZ_CODE_COVERAGE
309 value = BooleanValue(true);
310 #else
311 value = BooleanValue(false);
312 #endif
313 if (!JS_SetProperty(cx, info, "coverage", value)) {
314 return false;
317 #ifdef JS_HAS_CTYPES
318 value = BooleanValue(true);
319 #else
320 value = BooleanValue(false);
321 #endif
322 if (!JS_SetProperty(cx, info, "has-ctypes", value)) {
323 return false;
326 #if defined(_M_IX86) || defined(__i386__)
327 value = BooleanValue(true);
328 #else
329 value = BooleanValue(false);
330 #endif
331 if (!JS_SetProperty(cx, info, "x86", value)) {
332 return false;
335 #if defined(_M_X64) || defined(__x86_64__)
336 value = BooleanValue(true);
337 #else
338 value = BooleanValue(false);
339 #endif
340 if (!JS_SetProperty(cx, info, "x64", value)) {
341 return false;
344 #ifdef JS_CODEGEN_ARM
345 value = BooleanValue(true);
346 #else
347 value = BooleanValue(false);
348 #endif
349 if (!JS_SetProperty(cx, info, "arm", value)) {
350 return false;
353 #ifdef JS_SIMULATOR_ARM
354 value = BooleanValue(true);
355 #else
356 value = BooleanValue(false);
357 #endif
358 if (!JS_SetProperty(cx, info, "arm-simulator", value)) {
359 return false;
362 #ifdef ANDROID
363 value = BooleanValue(true);
364 #else
365 value = BooleanValue(false);
366 #endif
367 if (!JS_SetProperty(cx, info, "android", value)) {
368 return false;
371 #ifdef XP_WIN
372 value = BooleanValue(true);
373 #else
374 value = BooleanValue(false);
375 #endif
376 if (!JS_SetProperty(cx, info, "windows", value)) {
377 return false;
380 #ifdef XP_MACOSX
381 value = BooleanValue(true);
382 #else
383 value = BooleanValue(false);
384 #endif
385 if (!JS_SetProperty(cx, info, "osx", value)) {
386 return false;
389 #ifdef JS_CODEGEN_ARM64
390 value = BooleanValue(true);
391 #else
392 value = BooleanValue(false);
393 #endif
394 if (!JS_SetProperty(cx, info, "arm64", value)) {
395 return false;
398 #ifdef JS_SIMULATOR_ARM64
399 value = BooleanValue(true);
400 #else
401 value = BooleanValue(false);
402 #endif
403 if (!JS_SetProperty(cx, info, "arm64-simulator", value)) {
404 return false;
407 #ifdef JS_CODEGEN_MIPS32
408 value = BooleanValue(true);
409 #else
410 value = BooleanValue(false);
411 #endif
412 if (!JS_SetProperty(cx, info, "mips32", value)) {
413 return false;
416 #ifdef JS_CODEGEN_MIPS64
417 value = BooleanValue(true);
418 #else
419 value = BooleanValue(false);
420 #endif
421 if (!JS_SetProperty(cx, info, "mips64", value)) {
422 return false;
425 #ifdef JS_SIMULATOR_MIPS32
426 value = BooleanValue(true);
427 #else
428 value = BooleanValue(false);
429 #endif
430 if (!JS_SetProperty(cx, info, "mips32-simulator", value)) {
431 return false;
434 #ifdef JS_SIMULATOR_MIPS64
435 value = BooleanValue(true);
436 #else
437 value = BooleanValue(false);
438 #endif
439 if (!JS_SetProperty(cx, info, "mips64-simulator", value)) {
440 return false;
443 #ifdef JS_SIMULATOR
444 value = BooleanValue(true);
445 #else
446 value = BooleanValue(false);
447 #endif
448 if (!JS_SetProperty(cx, info, "simulator", value)) {
449 return false;
452 #ifdef __wasi__
453 value = BooleanValue(true);
454 #else
455 value = BooleanValue(false);
456 #endif
457 if (!JS_SetProperty(cx, info, "wasi", value)) {
458 return false;
461 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
462 value = BooleanValue(true);
463 #else
464 value = BooleanValue(false);
465 #endif
466 if (!JS_SetProperty(cx, info, "pbl", value)) {
467 return false;
470 #ifdef JS_CODEGEN_LOONG64
471 value = BooleanValue(true);
472 #else
473 value = BooleanValue(false);
474 #endif
475 if (!JS_SetProperty(cx, info, "loong64", value)) {
476 return false;
479 #ifdef JS_SIMULATOR_LOONG64
480 value = BooleanValue(true);
481 #else
482 value = BooleanValue(false);
483 #endif
484 if (!JS_SetProperty(cx, info, "loong64-simulator", value)) {
485 return false;
488 #ifdef JS_CODEGEN_RISCV64
489 value = BooleanValue(true);
490 #else
491 value = BooleanValue(false);
492 #endif
493 if (!JS_SetProperty(cx, info, "riscv64", value)) {
494 return false;
497 #ifdef JS_SIMULATOR_RISCV64
498 value = BooleanValue(true);
499 #else
500 value = BooleanValue(false);
501 #endif
502 if (!JS_SetProperty(cx, info, "riscv64-simulator", value)) {
503 return false;
506 #ifdef MOZ_ASAN
507 value = BooleanValue(true);
508 #else
509 value = BooleanValue(false);
510 #endif
511 if (!JS_SetProperty(cx, info, "asan", value)) {
512 return false;
515 #ifdef MOZ_TSAN
516 value = BooleanValue(true);
517 #else
518 value = BooleanValue(false);
519 #endif
520 if (!JS_SetProperty(cx, info, "tsan", value)) {
521 return false;
524 #ifdef MOZ_UBSAN
525 value = BooleanValue(true);
526 #else
527 value = BooleanValue(false);
528 #endif
529 if (!JS_SetProperty(cx, info, "ubsan", value)) {
530 return false;
533 #ifdef JS_GC_ZEAL
534 value = BooleanValue(true);
535 #else
536 value = BooleanValue(false);
537 #endif
538 if (!JS_SetProperty(cx, info, "has-gczeal", value)) {
539 return false;
542 #ifdef MOZ_PROFILING
543 value = BooleanValue(true);
544 #else
545 value = BooleanValue(false);
546 #endif
547 if (!JS_SetProperty(cx, info, "profiling", value)) {
548 return false;
551 #ifdef INCLUDE_MOZILLA_DTRACE
552 value = BooleanValue(true);
553 #else
554 value = BooleanValue(false);
555 #endif
556 if (!JS_SetProperty(cx, info, "dtrace", value)) {
557 return false;
560 #ifdef MOZ_VALGRIND
561 value = BooleanValue(true);
562 #else
563 value = BooleanValue(false);
564 #endif
565 if (!JS_SetProperty(cx, info, "valgrind", value)) {
566 return false;
569 #ifdef JS_HAS_INTL_API
570 value = BooleanValue(true);
571 #else
572 value = BooleanValue(false);
573 #endif
574 if (!JS_SetProperty(cx, info, "intl-api", value)) {
575 return false;
578 #if defined(SOLARIS)
579 value = BooleanValue(false);
580 #else
581 value = BooleanValue(true);
582 #endif
583 if (!JS_SetProperty(cx, info, "mapped-array-buffer", value)) {
584 return false;
587 #ifdef MOZ_MEMORY
588 value = BooleanValue(true);
589 #else
590 value = BooleanValue(false);
591 #endif
592 if (!JS_SetProperty(cx, info, "moz-memory", value)) {
593 return false;
596 value.setInt32(sizeof(void*));
597 if (!JS_SetProperty(cx, info, "pointer-byte-size", value)) {
598 return false;
601 #ifdef ENABLE_DECORATORS
602 value = BooleanValue(true);
603 #else
604 value = BooleanValue(false);
605 #endif
606 if (!JS_SetProperty(cx, info, "decorators", value)) {
607 return false;
610 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
611 value = BooleanValue(true);
612 #else
613 value = BooleanValue(false);
614 #endif
615 if (!JS_SetProperty(cx, info, "explicit-resource-management", value)) {
616 return false;
619 #ifdef FUZZING
620 value = BooleanValue(true);
621 #else
622 value = BooleanValue(false);
623 #endif
624 if (!JS_SetProperty(cx, info, "fuzzing-defined", value)) {
625 return false;
628 value = Int32Value(JSFatInlineString::MAX_LENGTH_LATIN1);
629 if (!JS_SetProperty(cx, info, "inline-latin1-chars", value)) {
630 return false;
633 value = Int32Value(JSFatInlineString::MAX_LENGTH_TWO_BYTE);
634 if (!JS_SetProperty(cx, info, "inline-two-byte-chars", value)) {
635 return false;
638 value = Int32Value(JSThinInlineString::MAX_LENGTH_LATIN1);
639 if (!JS_SetProperty(cx, info, "thin-inline-latin1-chars", value)) {
640 return false;
643 value = Int32Value(JSThinInlineString::MAX_LENGTH_TWO_BYTE);
644 if (!JS_SetProperty(cx, info, "thin-inline-two-byte-chars", value)) {
645 return false;
648 if (js::ThinInlineAtom::EverInstantiated) {
649 value = Int32Value(js::ThinInlineAtom::MAX_LENGTH_LATIN1);
650 if (!JS_SetProperty(cx, info, "thin-inline-atom-latin1-chars", value)) {
651 return false;
654 value = Int32Value(js::ThinInlineAtom::MAX_LENGTH_TWO_BYTE);
655 if (!JS_SetProperty(cx, info, "thin-inline-atom-two-byte-chars", value)) {
656 return false;
660 value = Int32Value(js::FatInlineAtom::MAX_LENGTH_LATIN1);
661 if (!JS_SetProperty(cx, info, "fat-inline-atom-latin1-chars", value)) {
662 return false;
665 value = Int32Value(js::FatInlineAtom::MAX_LENGTH_TWO_BYTE);
666 if (!JS_SetProperty(cx, info, "fat-inline-atom-two-byte-chars", value)) {
667 return false;
670 if (args.length() == 1) {
671 RootedString str(cx, ToString(cx, args[0]));
672 if (!str) {
673 return false;
675 RootedId id(cx);
676 if (!JS_StringToId(cx, str, &id)) {
677 return false;
680 bool hasProperty;
681 if (JS_HasPropertyById(cx, info, id, &hasProperty) && hasProperty) {
682 // Returning a true/false from GetProperty
683 return GetProperty(cx, info, info, id, args.rval());
686 ReportUsageErrorASCII(cx, callee, "Invalid option name");
687 return false;
690 args.rval().setObject(*info);
691 return true;
694 static bool IsLCovEnabled(JSContext* cx, unsigned argc, Value* vp) {
695 CallArgs args = CallArgsFromVp(argc, vp);
696 args.rval().setBoolean(coverage::IsLCovEnabled());
697 return true;
700 static bool TrialInline(JSContext* cx, unsigned argc, Value* vp) {
701 CallArgs args = CallArgsFromVp(argc, vp);
702 args.rval().setUndefined();
704 FrameIter iter(cx);
705 if (iter.done() || !iter.isBaseline() || iter.realm() != cx->realm()) {
706 return true;
709 jit::BaselineFrame* frame = iter.abstractFramePtr().asBaselineFrame();
710 if (!jit::CanIonCompileScript(cx, frame->script())) {
711 return true;
714 return jit::DoTrialInlining(cx, frame);
717 static bool ReturnStringCopy(JSContext* cx, CallArgs& args,
718 const char* message) {
719 JSString* str = JS_NewStringCopyZ(cx, message);
720 if (!str) {
721 return false;
724 args.rval().setString(str);
725 return true;
728 static bool MaybeGC(JSContext* cx, unsigned argc, Value* vp) {
729 CallArgs args = CallArgsFromVp(argc, vp);
730 JS_MaybeGC(cx);
731 args.rval().setUndefined();
732 return true;
735 static bool GC(JSContext* cx, unsigned argc, Value* vp) {
736 CallArgs args = CallArgsFromVp(argc, vp);
739 * If the first argument is 'zone', we collect any zones previously
740 * scheduled for GC via schedulegc. If the first argument is an object, we
741 * collect the object's zone (and any other zones scheduled for
742 * GC). Otherwise, we collect all zones.
744 bool zone = false;
745 if (args.length() >= 1) {
746 Value arg = args[0];
747 if (arg.isString()) {
748 if (!JS_StringEqualsLiteral(cx, arg.toString(), "zone", &zone)) {
749 return false;
751 } else if (arg.isObject()) {
752 PrepareZoneForGC(cx, UncheckedUnwrap(&arg.toObject())->zone());
753 zone = true;
757 JS::GCOptions options = JS::GCOptions::Normal;
758 JS::GCReason reason = JS::GCReason::API;
759 if (args.length() >= 2) {
760 Value arg = args[1];
761 if (arg.isString()) {
762 bool shrinking = false;
763 bool last_ditch = false;
764 if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking",
765 &shrinking)) {
766 return false;
768 if (!JS_StringEqualsLiteral(cx, arg.toString(), "last-ditch",
769 &last_ditch)) {
770 return false;
772 if (shrinking) {
773 options = JS::GCOptions::Shrink;
774 } else if (last_ditch) {
775 options = JS::GCOptions::Shrink;
776 reason = JS::GCReason::LAST_DITCH;
781 size_t preBytes = cx->runtime()->gc.heapSize.bytes();
783 if (zone) {
784 PrepareForDebugGC(cx->runtime());
785 } else {
786 JS::PrepareForFullGC(cx);
789 JS::NonIncrementalGC(cx, options, reason);
791 char buf[256] = {'\0'};
792 if (!js::SupportDifferentialTesting()) {
793 SprintfLiteral(buf, "before %zu, after %zu\n", preBytes,
794 cx->runtime()->gc.heapSize.bytes());
796 return ReturnStringCopy(cx, args, buf);
799 static bool MinorGC(JSContext* cx, unsigned argc, Value* vp) {
800 CallArgs args = CallArgsFromVp(argc, vp);
801 if (args.get(0) == BooleanValue(true)) {
802 gc::GCRuntime& gc = cx->runtime()->gc;
803 if (gc.nursery().isEnabled()) {
804 gc.storeBuffer().setAboutToOverflow(JS::GCReason::FULL_GENERIC_BUFFER);
808 cx->minorGC(JS::GCReason::API);
809 args.rval().setUndefined();
810 return true;
813 #define PARAM_NAME_LIST_ENTRY(name, key, writable) " " name
814 #define GC_PARAMETER_ARGS_LIST FOR_EACH_GC_PARAM(PARAM_NAME_LIST_ENTRY)
816 static bool GCParameter(JSContext* cx, unsigned argc, Value* vp) {
817 CallArgs args = CallArgsFromVp(argc, vp);
819 JSString* str = ToString(cx, args.get(0));
820 if (!str) {
821 return false;
824 UniqueChars name = EncodeLatin1(cx, str);
825 if (!name) {
826 return false;
829 JSGCParamKey param;
830 bool writable;
831 if (!GetGCParameterInfo(name.get(), &param, &writable)) {
832 JS_ReportErrorASCII(
833 cx, "the first argument must be one of:" GC_PARAMETER_ARGS_LIST);
834 return false;
837 // Request mode.
838 if (args.length() == 1) {
839 uint32_t value = JS_GetGCParameter(cx, param);
840 args.rval().setNumber(value);
841 return true;
844 if (!writable) {
845 JS_ReportErrorASCII(cx, "Attempt to change read-only parameter %s",
846 name.get());
847 return false;
850 if (fuzzingSafe) {
851 // Some Params are not yet fuzzing safe and so we silently skip
852 // changing said parameters.
853 switch (param) {
854 case JSGC_SEMISPACE_NURSERY_ENABLED:
855 args.rval().setUndefined();
856 return true;
857 default:
858 break;
862 if (disableOOMFunctions) {
863 switch (param) {
864 case JSGC_MAX_BYTES:
865 case JSGC_MAX_NURSERY_BYTES:
866 args.rval().setUndefined();
867 return true;
868 default:
869 break;
873 double d;
874 if (!ToNumber(cx, args[1], &d)) {
875 return false;
878 if (d < 0 || d > UINT32_MAX) {
879 JS_ReportErrorASCII(cx, "Parameter value out of range");
880 return false;
883 uint32_t value = floor(d);
884 bool ok = cx->runtime()->gc.setParameter(cx, param, value);
885 if (!ok) {
886 JS_ReportErrorASCII(cx, "Parameter value out of range");
887 return false;
890 args.rval().setUndefined();
891 return true;
894 static bool FinishBackgroundFree(JSContext* cx, unsigned argc, Value* vp) {
895 CallArgs args = CallArgsFromVp(argc, vp);
896 cx->runtime()->gc.waitBackgroundFreeEnd();
897 args.rval().setUndefined();
898 return true;
901 static bool RelazifyFunctions(JSContext* cx, unsigned argc, Value* vp) {
902 // Relazifying functions on GC is usually only done for compartments that are
903 // not active. To aid fuzzing, this testing function allows us to relazify
904 // even if the compartment is active.
906 CallArgs args = CallArgsFromVp(argc, vp);
908 // Disable relazification of all scripts on stack. It is a pervasive
909 // assumption in the engine that running scripts still have bytecode.
910 for (AllScriptFramesIter i(cx); !i.done(); ++i) {
911 i.script()->clearAllowRelazify();
914 cx->runtime()->allowRelazificationForTesting = true;
916 JS::PrepareForFullGC(cx);
917 JS::NonIncrementalGC(cx, JS::GCOptions::Shrink, JS::GCReason::API);
919 cx->runtime()->allowRelazificationForTesting = false;
921 args.rval().setUndefined();
922 return true;
925 static bool IsProxy(JSContext* cx, unsigned argc, Value* vp) {
926 CallArgs args = CallArgsFromVp(argc, vp);
927 if (args.length() != 1) {
928 JS_ReportErrorASCII(cx, "the function takes exactly one argument");
929 return false;
931 if (!args[0].isObject()) {
932 args.rval().setBoolean(false);
933 return true;
935 args.rval().setBoolean(args[0].toObject().is<ProxyObject>());
936 return true;
939 static bool WasmIsSupported(JSContext* cx, unsigned argc, Value* vp) {
940 CallArgs args = CallArgsFromVp(argc, vp);
941 args.rval().setBoolean(wasm::HasSupport(cx) &&
942 wasm::AnyCompilerAvailable(cx));
943 return true;
946 static bool WasmIsSupportedByHardware(JSContext* cx, unsigned argc, Value* vp) {
947 CallArgs args = CallArgsFromVp(argc, vp);
948 args.rval().setBoolean(wasm::HasPlatformSupport());
949 return true;
952 static bool WasmDebuggingEnabled(JSContext* cx, unsigned argc, Value* vp) {
953 CallArgs args = CallArgsFromVp(argc, vp);
954 args.rval().setBoolean(wasm::HasSupport(cx) && wasm::BaselineAvailable(cx));
955 return true;
958 static bool WasmStreamingEnabled(JSContext* cx, unsigned argc, Value* vp) {
959 CallArgs args = CallArgsFromVp(argc, vp);
960 args.rval().setBoolean(wasm::StreamingCompilationAvailable(cx));
961 return true;
964 static bool WasmCachingEnabled(JSContext* cx, unsigned argc, Value* vp) {
965 CallArgs args = CallArgsFromVp(argc, vp);
966 args.rval().setBoolean(wasm::CodeCachingAvailable(cx));
967 return true;
970 static bool WasmHugeMemorySupported(JSContext* cx, unsigned argc, Value* vp) {
971 CallArgs args = CallArgsFromVp(argc, vp);
972 #ifdef WASM_SUPPORTS_HUGE_MEMORY
973 args.rval().setBoolean(true);
974 #else
975 args.rval().setBoolean(false);
976 #endif
977 return true;
980 static bool WasmMaxMemoryPages(JSContext* cx, unsigned argc, Value* vp) {
981 CallArgs args = CallArgsFromVp(argc, vp);
982 if (args.length() < 1) {
983 JS_ReportErrorASCII(cx, "not enough arguments");
984 return false;
986 if (!args.get(0).isString()) {
987 JS_ReportErrorASCII(cx, "index type must be a string");
988 return false;
990 RootedString s(cx, args.get(0).toString());
991 Rooted<JSLinearString*> ls(cx, s->ensureLinear(cx));
992 if (!ls) {
993 return false;
995 if (StringEqualsLiteral(ls, "i32")) {
996 args.rval().setInt32(
997 int32_t(wasm::MaxMemoryPages(wasm::IndexType::I32).value()));
998 return true;
1000 if (StringEqualsLiteral(ls, "i64")) {
1001 #ifdef ENABLE_WASM_MEMORY64
1002 if (wasm::Memory64Available(cx)) {
1003 args.rval().setInt32(
1004 int32_t(wasm::MaxMemoryPages(wasm::IndexType::I64).value()));
1005 return true;
1007 #endif
1008 JS_ReportErrorASCII(cx, "memory64 not enabled");
1009 return false;
1011 JS_ReportErrorASCII(cx, "bad index type");
1012 return false;
1015 static bool WasmThreadsEnabled(JSContext* cx, unsigned argc, Value* vp) {
1016 CallArgs args = CallArgsFromVp(argc, vp);
1017 args.rval().setBoolean(wasm::ThreadsAvailable(cx));
1018 return true;
1021 #define WASM_FEATURE(NAME, ...) \
1022 static bool Wasm##NAME##Enabled(JSContext* cx, unsigned argc, Value* vp) { \
1023 CallArgs args = CallArgsFromVp(argc, vp); \
1024 args.rval().setBoolean(wasm::NAME##Available(cx)); \
1025 return true; \
1027 JS_FOR_WASM_FEATURES(WASM_FEATURE);
1028 #undef WASM_FEATURE
1030 static bool WasmSimdEnabled(JSContext* cx, unsigned argc, Value* vp) {
1031 CallArgs args = CallArgsFromVp(argc, vp);
1032 args.rval().setBoolean(wasm::SimdAvailable(cx));
1033 return true;
1036 static bool WasmCompilersPresent(JSContext* cx, unsigned argc, Value* vp) {
1037 CallArgs args = CallArgsFromVp(argc, vp);
1039 char buf[256];
1040 *buf = 0;
1041 if (wasm::BaselinePlatformSupport()) {
1042 strcat(buf, "baseline");
1044 if (wasm::IonPlatformSupport()) {
1045 if (*buf) {
1046 strcat(buf, ",");
1048 strcat(buf, "ion");
1051 JSString* result = JS_NewStringCopyZ(cx, buf);
1052 if (!result) {
1053 return false;
1056 args.rval().setString(result);
1057 return true;
1060 static bool WasmCompileMode(JSContext* cx, unsigned argc, Value* vp) {
1061 CallArgs args = CallArgsFromVp(argc, vp);
1063 // This triplet of predicates will select zero or one baseline compiler and
1064 // zero or one optimizing compiler.
1065 bool baseline = wasm::BaselineAvailable(cx);
1066 bool ion = wasm::IonAvailable(cx);
1067 bool none = !baseline && !ion;
1068 bool tiered = baseline && ion;
1070 JSStringBuilder result(cx);
1071 if (none && !result.append("none")) {
1072 return false;
1074 if (baseline && !result.append("baseline")) {
1075 return false;
1077 if (tiered && !result.append("+")) {
1078 return false;
1080 if (ion && !result.append("ion")) {
1081 return false;
1083 if (JSString* str = result.finishString()) {
1084 args.rval().setString(str);
1085 return true;
1087 return false;
1090 static bool WasmBaselineDisabledByFeatures(JSContext* cx, unsigned argc,
1091 Value* vp) {
1092 CallArgs args = CallArgsFromVp(argc, vp);
1093 bool isDisabled = false;
1094 JSStringBuilder reason(cx);
1095 if (!wasm::BaselineDisabledByFeatures(cx, &isDisabled, &reason)) {
1096 return false;
1098 if (isDisabled) {
1099 JSString* result = reason.finishString();
1100 if (!result) {
1101 return false;
1103 args.rval().setString(result);
1104 } else {
1105 args.rval().setBoolean(false);
1107 return true;
1110 static bool WasmIonDisabledByFeatures(JSContext* cx, unsigned argc, Value* vp) {
1111 CallArgs args = CallArgsFromVp(argc, vp);
1112 bool isDisabled = false;
1113 JSStringBuilder reason(cx);
1114 if (!wasm::IonDisabledByFeatures(cx, &isDisabled, &reason)) {
1115 return false;
1117 if (isDisabled) {
1118 JSString* result = reason.finishString();
1119 if (!result) {
1120 return false;
1122 args.rval().setString(result);
1123 } else {
1124 args.rval().setBoolean(false);
1126 return true;
1129 #ifdef ENABLE_WASM_SIMD
1130 # ifdef DEBUG
1131 static char lastAnalysisResult[1024];
1133 namespace js {
1134 namespace wasm {
1135 void ReportSimdAnalysis(const char* data) {
1136 strncpy(lastAnalysisResult, data, sizeof(lastAnalysisResult));
1137 lastAnalysisResult[sizeof(lastAnalysisResult) - 1] = 0;
1139 } // namespace wasm
1140 } // namespace js
1142 // Unstable API for white-box testing of SIMD optimizations.
1144 // Current API: takes no arguments, returns a string describing the last Simd
1145 // simplification applied.
1147 static bool WasmSimdAnalysis(JSContext* cx, unsigned argc, Value* vp) {
1148 CallArgs args = CallArgsFromVp(argc, vp);
1149 JSString* result =
1150 JS_NewStringCopyZ(cx, *lastAnalysisResult ? lastAnalysisResult : "none");
1151 if (!result) {
1152 return false;
1154 args.rval().setString(result);
1155 *lastAnalysisResult = (char)0;
1156 return true;
1158 # endif
1159 #endif
1161 static bool WasmGlobalFromArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
1162 if (!wasm::HasSupport(cx)) {
1163 JS_ReportErrorASCII(cx, "wasm support unavailable");
1164 return false;
1166 CallArgs args = CallArgsFromVp(argc, vp);
1168 if (args.length() < 2) {
1169 JS_ReportErrorASCII(cx, "not enough arguments");
1170 return false;
1173 // Get the type of the value
1174 wasm::ValType valType;
1175 if (!wasm::ToValType(cx, args.get(0), &valType)) {
1176 return false;
1179 // Get the array buffer for the value
1180 if (!args.get(1).isObject() ||
1181 !args.get(1).toObject().is<ArrayBufferObject>()) {
1182 JS_ReportErrorASCII(cx, "argument is not an array buffer");
1183 return false;
1185 Rooted<ArrayBufferObject*> buffer(
1186 cx, &args.get(1).toObject().as<ArrayBufferObject>());
1188 // Only allow POD to be created from bytes
1189 switch (valType.kind()) {
1190 case wasm::ValType::I32:
1191 case wasm::ValType::I64:
1192 case wasm::ValType::F32:
1193 case wasm::ValType::F64:
1194 case wasm::ValType::V128:
1195 break;
1196 default:
1197 JS_ReportErrorASCII(
1198 cx, "invalid valtype for creating WebAssembly.Global from bytes");
1199 return false;
1202 // Check we have all the bytes we need
1203 if (valType.size() != buffer->byteLength()) {
1204 JS_ReportErrorASCII(cx, "array buffer has incorrect size");
1205 return false;
1208 // Copy the bytes from buffer into a tagged val
1209 wasm::RootedVal val(cx);
1210 val.get().initFromRootedLocation(valType, buffer->dataPointer());
1212 // Create the global object
1213 RootedObject proto(
1214 cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmGlobal));
1215 if (!proto) {
1216 return false;
1218 Rooted<WasmGlobalObject*> result(
1219 cx, WasmGlobalObject::create(cx, val, false, proto));
1220 if (!result) {
1221 return false;
1224 args.rval().setObject(*result.get());
1225 return true;
1228 enum class LaneInterp {
1229 I32x4,
1230 I64x2,
1231 F32x4,
1232 F64x2,
1235 size_t LaneInterpLanes(LaneInterp interp) {
1236 switch (interp) {
1237 case LaneInterp::I32x4:
1238 return 4;
1239 case LaneInterp::I64x2:
1240 return 2;
1241 case LaneInterp::F32x4:
1242 return 4;
1243 case LaneInterp::F64x2:
1244 return 2;
1245 default:
1246 MOZ_ASSERT_UNREACHABLE();
1247 return 0;
1251 static bool ToLaneInterp(JSContext* cx, HandleValue v, LaneInterp* out) {
1252 RootedString interpStr(cx, ToString(cx, v));
1253 if (!interpStr) {
1254 return false;
1256 Rooted<JSLinearString*> interpLinearStr(cx, interpStr->ensureLinear(cx));
1257 if (!interpLinearStr) {
1258 return false;
1261 if (StringEqualsLiteral(interpLinearStr, "i32x4")) {
1262 *out = LaneInterp::I32x4;
1263 return true;
1264 } else if (StringEqualsLiteral(interpLinearStr, "i64x2")) {
1265 *out = LaneInterp::I64x2;
1266 return true;
1267 } else if (StringEqualsLiteral(interpLinearStr, "f32x4")) {
1268 *out = LaneInterp::F32x4;
1269 return true;
1270 } else if (StringEqualsLiteral(interpLinearStr, "f64x2")) {
1271 *out = LaneInterp::F64x2;
1272 return true;
1275 JS_ReportErrorASCII(cx, "invalid lane interpretation");
1276 return false;
1279 static bool WasmGlobalExtractLane(JSContext* cx, unsigned argc, Value* vp) {
1280 if (!wasm::HasSupport(cx)) {
1281 JS_ReportErrorASCII(cx, "wasm support unavailable");
1282 return false;
1284 CallArgs args = CallArgsFromVp(argc, vp);
1286 if (args.length() < 3) {
1287 JS_ReportErrorASCII(cx, "not enough arguments");
1288 return false;
1291 // Get the global value
1292 if (!args.get(0).isObject() ||
1293 !args.get(0).toObject().is<WasmGlobalObject>()) {
1294 JS_ReportErrorASCII(cx, "argument is not wasm value");
1295 return false;
1297 Rooted<WasmGlobalObject*> global(
1298 cx, &args.get(0).toObject().as<WasmGlobalObject>());
1300 // Check that we have a v128 value
1301 if (global->type().kind() != wasm::ValType::V128) {
1302 JS_ReportErrorASCII(cx, "global is not a v128 value");
1303 return false;
1305 wasm::V128 v128 = global->val().get().v128();
1307 // Get the passed interpretation of lanes
1308 LaneInterp interp;
1309 if (!ToLaneInterp(cx, args.get(1), &interp)) {
1310 return false;
1313 // Get the lane to extract
1314 int32_t lane;
1315 if (!ToInt32(cx, args.get(2), &lane)) {
1316 return false;
1319 // Check that the lane interp is valid
1320 if (lane < 0 || size_t(lane) >= LaneInterpLanes(interp)) {
1321 JS_ReportErrorASCII(cx, "invalid lane for interp");
1322 return false;
1325 wasm::RootedVal val(cx);
1326 switch (interp) {
1327 case LaneInterp::I32x4: {
1328 uint32_t i;
1329 v128.extractLane<uint32_t>(lane, &i);
1330 val.set(wasm::Val(i));
1331 break;
1333 case LaneInterp::I64x2: {
1334 uint64_t i;
1335 v128.extractLane<uint64_t>(lane, &i);
1336 val.set(wasm::Val(i));
1337 break;
1339 case LaneInterp::F32x4: {
1340 float f;
1341 v128.extractLane<float>(lane, &f);
1342 val.set(wasm::Val(f));
1343 break;
1345 case LaneInterp::F64x2: {
1346 double d;
1347 v128.extractLane<double>(lane, &d);
1348 val.set(wasm::Val(d));
1349 break;
1351 default:
1352 MOZ_ASSERT_UNREACHABLE();
1355 RootedObject proto(
1356 cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmGlobal));
1357 Rooted<WasmGlobalObject*> result(
1358 cx, WasmGlobalObject::create(cx, val, false, proto));
1359 args.rval().setObject(*result.get());
1360 return true;
1363 static bool WasmGlobalsEqual(JSContext* cx, unsigned argc, Value* vp) {
1364 if (!wasm::HasSupport(cx)) {
1365 JS_ReportErrorASCII(cx, "wasm support unavailable");
1366 return false;
1368 CallArgs args = CallArgsFromVp(argc, vp);
1370 if (args.length() < 2) {
1371 JS_ReportErrorASCII(cx, "not enough arguments");
1372 return false;
1375 if (!args.get(0).isObject() ||
1376 !args.get(0).toObject().is<WasmGlobalObject>() ||
1377 !args.get(1).isObject() ||
1378 !args.get(1).toObject().is<WasmGlobalObject>()) {
1379 JS_ReportErrorASCII(cx, "argument is not wasm value");
1380 return false;
1383 Rooted<WasmGlobalObject*> a(cx,
1384 &args.get(0).toObject().as<WasmGlobalObject>());
1385 Rooted<WasmGlobalObject*> b(cx,
1386 &args.get(1).toObject().as<WasmGlobalObject>());
1388 if (a->type().kind() != b->type().kind()) {
1389 JS_ReportErrorASCII(cx, "globals are of different kind");
1390 return false;
1393 bool result;
1394 const wasm::Val& aVal = a->val().get();
1395 const wasm::Val& bVal = b->val().get();
1396 switch (a->type().kind()) {
1397 case wasm::ValType::I32: {
1398 result = aVal.i32() == bVal.i32();
1399 break;
1401 case wasm::ValType::I64: {
1402 result = aVal.i64() == bVal.i64();
1403 break;
1405 case wasm::ValType::F32: {
1406 result = mozilla::BitwiseCast<uint32_t>(aVal.f32()) ==
1407 mozilla::BitwiseCast<uint32_t>(bVal.f32());
1408 break;
1410 case wasm::ValType::F64: {
1411 result = mozilla::BitwiseCast<uint64_t>(aVal.f64()) ==
1412 mozilla::BitwiseCast<uint64_t>(bVal.f64());
1413 break;
1415 case wasm::ValType::V128: {
1416 // Don't know the interpretation of the v128, so we only can do an exact
1417 // bitwise equality. Testing code can use wasmGlobalExtractLane to
1418 // workaround this if needed.
1419 result = aVal.v128() == bVal.v128();
1420 break;
1422 case wasm::ValType::Ref: {
1423 result = aVal.ref() == bVal.ref();
1424 break;
1426 default:
1427 JS_ReportErrorASCII(cx, "unsupported type");
1428 return false;
1430 args.rval().setBoolean(result);
1431 return true;
1434 // Flavors of NaN values for WebAssembly.
1435 // See
1436 // https://webassembly.github.io/spec/core/syntax/values.html#floating-point.
1437 enum class NaNFlavor {
1438 // A canonical NaN value.
1439 // - the sign bit is unspecified,
1440 // - the 8-bit exponent is set to all 1s
1441 // - the MSB of the payload is set to 1 (a quieted NaN) and all others to 0.
1442 Canonical,
1443 // An arithmetic NaN. This is the same as a canonical NaN including that the
1444 // payload MSB is set to 1, but one or more of the remaining payload bits MAY
1445 // BE set to 1 (a canonical NaN specifies all 0s).
1446 Arithmetic,
1449 static bool IsNaNFlavor(uint32_t bits, NaNFlavor flavor) {
1450 switch (flavor) {
1451 case NaNFlavor::Canonical: {
1452 return (bits & 0x7fffffff) == 0x7fc00000;
1454 case NaNFlavor::Arithmetic: {
1455 const uint32_t ArithmeticNaN = 0x7f800000;
1456 const uint32_t ArithmeticPayloadMSB = 0x00400000;
1457 bool isNaN = (bits & ArithmeticNaN) == ArithmeticNaN;
1458 bool isMSBSet = (bits & ArithmeticPayloadMSB) == ArithmeticPayloadMSB;
1459 return isNaN && isMSBSet;
1461 default:
1462 MOZ_CRASH();
1466 static bool IsNaNFlavor(uint64_t bits, NaNFlavor flavor) {
1467 switch (flavor) {
1468 case NaNFlavor::Canonical: {
1469 return (bits & 0x7fffffffffffffff) == 0x7ff8000000000000;
1471 case NaNFlavor::Arithmetic: {
1472 uint64_t ArithmeticNaN = 0x7ff0000000000000;
1473 uint64_t ArithmeticPayloadMSB = 0x0008000000000000;
1474 bool isNaN = (bits & ArithmeticNaN) == ArithmeticNaN;
1475 bool isMsbSet = (bits & ArithmeticPayloadMSB) == ArithmeticPayloadMSB;
1476 return isNaN && isMsbSet;
1478 default:
1479 MOZ_CRASH();
1483 static bool ToNaNFlavor(JSContext* cx, HandleValue v, NaNFlavor* out) {
1484 RootedString flavorStr(cx, ToString(cx, v));
1485 if (!flavorStr) {
1486 return false;
1488 Rooted<JSLinearString*> flavorLinearStr(cx, flavorStr->ensureLinear(cx));
1489 if (!flavorLinearStr) {
1490 return false;
1493 if (StringEqualsLiteral(flavorLinearStr, "canonical_nan")) {
1494 *out = NaNFlavor::Canonical;
1495 return true;
1496 } else if (StringEqualsLiteral(flavorLinearStr, "arithmetic_nan")) {
1497 *out = NaNFlavor::Arithmetic;
1498 return true;
1501 JS_ReportErrorASCII(cx, "invalid nan flavor");
1502 return false;
1505 static bool WasmGlobalIsNaN(JSContext* cx, unsigned argc, Value* vp) {
1506 if (!wasm::HasSupport(cx)) {
1507 JS_ReportErrorASCII(cx, "wasm support unavailable");
1508 return false;
1510 CallArgs args = CallArgsFromVp(argc, vp);
1512 if (args.length() < 2) {
1513 JS_ReportErrorASCII(cx, "not enough arguments");
1514 return false;
1517 if (!args.get(0).isObject() ||
1518 !args.get(0).toObject().is<WasmGlobalObject>()) {
1519 JS_ReportErrorASCII(cx, "argument is not wasm value");
1520 return false;
1522 Rooted<WasmGlobalObject*> global(
1523 cx, &args.get(0).toObject().as<WasmGlobalObject>());
1525 NaNFlavor flavor;
1526 if (!ToNaNFlavor(cx, args.get(1), &flavor)) {
1527 return false;
1530 bool result;
1531 const wasm::Val& val = global->val().get();
1532 switch (global->type().kind()) {
1533 case wasm::ValType::F32: {
1534 result = IsNaNFlavor(mozilla::BitwiseCast<uint32_t>(val.f32()), flavor);
1535 break;
1537 case wasm::ValType::F64: {
1538 result = IsNaNFlavor(mozilla::BitwiseCast<uint64_t>(val.f64()), flavor);
1539 break;
1541 default:
1542 JS_ReportErrorASCII(cx, "global is not a floating point value");
1543 return false;
1545 args.rval().setBoolean(result);
1546 return true;
1549 static bool WasmGlobalToString(JSContext* cx, unsigned argc, Value* vp) {
1550 if (!wasm::HasSupport(cx)) {
1551 JS_ReportErrorASCII(cx, "wasm support unavailable");
1552 return false;
1554 CallArgs args = CallArgsFromVp(argc, vp);
1556 if (args.length() < 1) {
1557 JS_ReportErrorASCII(cx, "not enough arguments");
1558 return false;
1560 if (!args.get(0).isObject() ||
1561 !args.get(0).toObject().is<WasmGlobalObject>()) {
1562 JS_ReportErrorASCII(cx, "argument is not wasm value");
1563 return false;
1565 Rooted<WasmGlobalObject*> global(
1566 cx, &args.get(0).toObject().as<WasmGlobalObject>());
1567 const wasm::Val& globalVal = global->val().get();
1569 UniqueChars result;
1570 switch (globalVal.type().kind()) {
1571 case wasm::ValType::I32: {
1572 result = JS_smprintf("i32:%" PRIx32, globalVal.i32());
1573 break;
1575 case wasm::ValType::I64: {
1576 result = JS_smprintf("i64:%" PRIx64, globalVal.i64());
1577 break;
1579 case wasm::ValType::F32: {
1580 result = JS_smprintf("f32:%f", globalVal.f32());
1581 break;
1583 case wasm::ValType::F64: {
1584 result = JS_smprintf("f64:%lf", globalVal.f64());
1585 break;
1587 case wasm::ValType::V128: {
1588 wasm::V128 v128 = globalVal.v128();
1589 result = JS_smprintf(
1590 "v128:%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x", v128.bytes[0],
1591 v128.bytes[1], v128.bytes[2], v128.bytes[3], v128.bytes[4],
1592 v128.bytes[5], v128.bytes[6], v128.bytes[7], v128.bytes[8],
1593 v128.bytes[9], v128.bytes[10], v128.bytes[11], v128.bytes[12],
1594 v128.bytes[13], v128.bytes[14], v128.bytes[15]);
1595 break;
1597 case wasm::ValType::Ref: {
1598 result = JS_smprintf("ref:%" PRIxPTR, globalVal.ref().rawValue());
1599 break;
1601 default:
1602 MOZ_ASSERT_UNREACHABLE();
1605 args.rval().setString(JS_NewStringCopyZ(cx, result.get()));
1606 return true;
1609 static bool WasmLosslessInvoke(JSContext* cx, unsigned argc, Value* vp) {
1610 if (!wasm::HasSupport(cx)) {
1611 JS_ReportErrorASCII(cx, "wasm support unavailable");
1612 return false;
1614 CallArgs args = CallArgsFromVp(argc, vp);
1616 if (args.length() < 1) {
1617 JS_ReportErrorASCII(cx, "not enough arguments");
1618 return false;
1620 if (!args.get(0).isObject() || !args.get(0).toObject().is<JSFunction>()) {
1621 JS_ReportErrorASCII(cx, "argument is not an object");
1622 return false;
1625 RootedFunction func(cx, &args[0].toObject().as<JSFunction>());
1626 if (!func || !wasm::IsWasmExportedFunction(func)) {
1627 JS_ReportErrorASCII(cx, "argument is not an exported wasm function");
1628 return false;
1631 // Switch to the function's realm
1632 AutoRealm ar(cx, func);
1634 // Get the instance and funcIndex for calling the function
1635 wasm::Instance& instance = wasm::ExportedFunctionToInstance(func);
1636 uint32_t funcIndex = wasm::ExportedFunctionToFuncIndex(func);
1638 // Set up a modified call frame following the standard JS
1639 // [callee, this, arguments...] convention.
1640 RootedValueVector wasmCallFrame(cx);
1641 size_t len = 2 + args.length();
1642 if (!wasmCallFrame.resize(len)) {
1643 return false;
1645 wasmCallFrame[0].set(ObjectValue(*func));
1646 wasmCallFrame[1].set(args.thisv());
1647 // Copy over the arguments needed to invoke the provided wasm function,
1648 // skipping the wasm function we're calling that is at `args.get(0)`.
1649 for (size_t i = 1; i < args.length(); i++) {
1650 size_t wasmArg = i - 1;
1651 wasmCallFrame[2 + wasmArg].set(args.get(i));
1653 size_t wasmArgc = argc - 1;
1654 CallArgs wasmCallArgs(CallArgsFromVp(wasmArgc, wasmCallFrame.begin()));
1656 // Invoke the function with the new call frame
1657 bool result = instance.callExport(cx, funcIndex, wasmCallArgs,
1658 wasm::CoercionLevel::Lossless);
1659 // Assign the wasm rval to our rval
1660 args.rval().set(wasmCallArgs.rval());
1661 return result;
1664 static bool ConvertToTier(JSContext* cx, HandleValue value,
1665 const wasm::Code& code, wasm::Tier* tier) {
1666 RootedString option(cx, JS::ToString(cx, value));
1668 if (!option) {
1669 return false;
1672 bool stableTier = false;
1673 bool bestTier = false;
1674 bool baselineTier = false;
1675 bool ionTier = false;
1677 if (!JS_StringEqualsLiteral(cx, option, "stable", &stableTier) ||
1678 !JS_StringEqualsLiteral(cx, option, "best", &bestTier) ||
1679 !JS_StringEqualsLiteral(cx, option, "baseline", &baselineTier) ||
1680 !JS_StringEqualsLiteral(cx, option, "ion", &ionTier)) {
1681 return false;
1684 if (stableTier) {
1685 *tier = code.stableCompleteTier();
1686 } else if (bestTier) {
1687 *tier = code.bestCompleteTier();
1688 } else if (baselineTier) {
1689 *tier = wasm::Tier::Baseline;
1690 } else if (ionTier) {
1691 *tier = wasm::Tier::Optimized;
1692 } else {
1693 // You can omit the argument but you can't pass just anything you like
1694 return false;
1697 return true;
1700 static bool WasmExtractCode(JSContext* cx, unsigned argc, Value* vp) {
1701 if (!wasm::HasSupport(cx)) {
1702 JS_ReportErrorASCII(cx, "wasm support unavailable");
1703 return false;
1706 CallArgs args = CallArgsFromVp(argc, vp);
1708 if (!args.get(0).isObject()) {
1709 JS_ReportErrorASCII(cx, "argument is not an object");
1710 return false;
1713 Rooted<WasmModuleObject*> module(
1714 cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>());
1715 if (!module) {
1716 JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
1717 return false;
1720 wasm::Tier tier = module->module().code().stableCompleteTier();
1721 if (args.length() > 1 &&
1722 !ConvertToTier(cx, args[1], module->module().code(), &tier)) {
1723 args.rval().setNull();
1724 return false;
1727 RootedValue result(cx);
1728 if (!module->module().extractCode(cx, tier, &result)) {
1729 return false;
1732 args.rval().set(result);
1733 return true;
1736 struct DisasmBuffer {
1737 JSStringBuilder builder;
1738 bool oom;
1739 explicit DisasmBuffer(JSContext* cx) : builder(cx), oom(false) {}
1742 static bool HasDisassembler(JSContext* cx, unsigned argc, Value* vp) {
1743 CallArgs args = CallArgsFromVp(argc, vp);
1744 args.rval().setBoolean(jit::HasDisassembler());
1745 return true;
1748 MOZ_THREAD_LOCAL(DisasmBuffer*) disasmBuf;
1750 static void captureDisasmText(const char* text) {
1751 DisasmBuffer* buf = disasmBuf.get();
1752 if (!buf->builder.append(text, strlen(text)) || !buf->builder.append('\n')) {
1753 buf->oom = true;
1757 static bool DisassembleNative(JSContext* cx, unsigned argc, Value* vp) {
1758 CallArgs args = CallArgsFromVp(argc, vp);
1759 args.rval().setUndefined();
1761 if (args.length() < 1) {
1762 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1763 JSMSG_MORE_ARGS_NEEDED, "disnative", "1", "",
1764 "0");
1765 return false;
1768 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
1769 JS_ReportErrorASCII(cx, "The first argument must be a function.");
1770 return false;
1773 JSSprinter sprinter(cx);
1774 if (!sprinter.init()) {
1775 return false;
1778 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
1780 uint8_t* jit_begin = nullptr;
1781 uint8_t* jit_end = nullptr;
1783 if (fun->isAsmJSNative() || fun->isWasmWithJitEntry()) {
1784 if (fun->isAsmJSNative()) {
1785 return false;
1787 sprinter.printf("; backend=asmjs\n");
1788 sprinter.printf("; backend=wasm\n");
1790 js::wasm::Instance& inst = fun->wasmInstance();
1791 const js::wasm::Code& code = inst.code();
1792 const uint32_t funcIndex = code.getFuncIndex(&*fun);
1793 const js::wasm::CodeBlock& codeBlock = inst.code().funcCodeBlock(funcIndex);
1794 const js::wasm::CodeSegment& segment = *codeBlock.segment;
1795 const js::wasm::FuncExport& func = codeBlock.lookupFuncExport(funcIndex);
1796 const js::wasm::CodeRange& codeRange = codeBlock.codeRange(func);
1798 jit_begin = segment.base() + codeRange.begin();
1799 jit_end = segment.base() + codeRange.end();
1800 } else if (fun->hasJitScript()) {
1801 JSScript* script = fun->nonLazyScript();
1802 if (script == nullptr) {
1803 return false;
1806 js::jit::IonScript* ion =
1807 script->hasIonScript() ? script->ionScript() : nullptr;
1808 js::jit::BaselineScript* baseline =
1809 script->hasBaselineScript() ? script->baselineScript() : nullptr;
1810 if (ion && ion->method()) {
1811 sprinter.printf("; backend=ion\n");
1812 jit_begin = ion->method()->raw();
1813 jit_end = ion->method()->rawEnd();
1814 } else if (baseline) {
1815 sprinter.printf("; backend=baseline\n");
1816 jit_begin = baseline->method()->raw();
1817 jit_end = baseline->method()->rawEnd();
1819 } else {
1820 JS_ReportErrorASCII(cx,
1821 "The function hasn't been warmed up, hence no JIT code "
1822 "to disassemble.");
1823 return false;
1826 if (jit_begin == nullptr || jit_end == nullptr) {
1827 return false;
1830 #ifdef JS_CODEGEN_ARM
1831 // The ARM32 disassembler is currently not fuzzing-safe because it doesn't
1832 // handle constant pools correctly (bug 1875363).
1833 if (fuzzingSafe) {
1834 JS_ReportErrorASCII(cx, "disnative is not fuzzing-safe on ARM32");
1835 return false;
1837 #endif
1839 // Dump the raw code to a file before disassembling in case
1840 // finishString triggers a GC and discards the jitcode.
1841 if (!fuzzingSafe && args.length() > 1 && args[1].isString()) {
1842 RootedString str(cx, args[1].toString());
1843 JS::UniqueChars fileNameBytes = JS_EncodeStringToUTF8(cx, str);
1845 const char* fileName = fileNameBytes.get();
1846 if (!fileName) {
1847 ReportOutOfMemory(cx);
1848 return false;
1851 FILE* f = fopen(fileName, "w");
1852 if (!f) {
1853 JS_ReportErrorASCII(cx, "Could not open file for writing.");
1854 return false;
1857 uintptr_t expected_length = reinterpret_cast<uintptr_t>(jit_end) -
1858 reinterpret_cast<uintptr_t>(jit_begin);
1859 if (expected_length != fwrite(jit_begin, jit_end - jit_begin, 1, f)) {
1860 JS_ReportErrorASCII(cx, "Did not write all function bytes to the file.");
1861 fclose(f);
1862 return false;
1864 fclose(f);
1867 DisasmBuffer buf(cx);
1868 disasmBuf.set(&buf);
1869 auto onFinish = mozilla::MakeScopeExit([&] { disasmBuf.set(nullptr); });
1871 jit::Disassemble(jit_begin, jit_end - jit_begin, &captureDisasmText);
1873 if (buf.oom) {
1874 ReportOutOfMemory(cx);
1875 return false;
1877 JSString* sresult = buf.builder.finishString();
1878 if (!sresult) {
1879 ReportOutOfMemory(cx);
1880 return false;
1882 sprinter.putString(cx, sresult);
1884 JSString* str = sprinter.release(cx);
1885 if (!str) {
1886 return false;
1889 args[0].setUndefined();
1890 args.rval().setString(str);
1892 return true;
1895 static bool ComputeTier(JSContext* cx, const wasm::Code& code,
1896 HandleValue tierSelection, wasm::Tier* tier) {
1897 *tier = code.stableCompleteTier();
1898 if (!tierSelection.isUndefined() &&
1899 !ConvertToTier(cx, tierSelection, code, tier)) {
1900 JS_ReportErrorASCII(cx, "invalid tier");
1901 return false;
1904 return true;
1907 template <typename DisasmFunction>
1908 static bool DisassembleIt(JSContext* cx, bool asString, MutableHandleValue rval,
1909 DisasmFunction&& disassembleIt) {
1910 if (asString) {
1911 DisasmBuffer buf(cx);
1912 disasmBuf.set(&buf);
1913 auto onFinish = mozilla::MakeScopeExit([&] { disasmBuf.set(nullptr); });
1914 disassembleIt(captureDisasmText);
1915 if (buf.oom) {
1916 ReportOutOfMemory(cx);
1917 return false;
1919 JSString* sresult = buf.builder.finishString();
1920 if (!sresult) {
1921 ReportOutOfMemory(cx);
1922 return false;
1924 rval.setString(sresult);
1925 return true;
1928 disassembleIt([](const char* text) { fprintf(stderr, "%s\n", text); });
1929 return true;
1932 static bool WasmDisassembleFunction(JSContext* cx, const HandleFunction& func,
1933 HandleValue tierSelection, bool asString,
1934 MutableHandleValue rval) {
1935 wasm::Instance& instance = wasm::ExportedFunctionToInstance(func);
1936 uint32_t funcIndex = wasm::ExportedFunctionToFuncIndex(func);
1937 wasm::Tier tier;
1939 if (!ComputeTier(cx, instance.code(), tierSelection, &tier)) {
1940 return false;
1943 if (!instance.code().funcHasTier(funcIndex, tier)) {
1944 JS_ReportErrorASCII(cx, "function missing selected tier");
1945 return false;
1948 return DisassembleIt(
1949 cx, asString, rval, [&](void (*captureText)(const char*)) {
1950 instance.disassembleExport(cx, funcIndex, tier, captureText);
1954 static bool WasmDisassembleCode(JSContext* cx, const wasm::Code& code,
1955 HandleValue tierSelection, int kindSelection,
1956 bool asString, MutableHandleValue rval) {
1957 wasm::Tier tier;
1958 if (!ComputeTier(cx, code, tierSelection, &tier)) {
1959 return false;
1962 return DisassembleIt(cx, asString, rval,
1963 [&](void (*captureText)(const char*)) {
1964 code.disassemble(cx, tier, kindSelection, captureText);
1968 static bool WasmDisassemble(JSContext* cx, unsigned argc, Value* vp) {
1969 if (!wasm::HasSupport(cx)) {
1970 JS_ReportErrorASCII(cx, "wasm support unavailable");
1971 return false;
1974 CallArgs args = CallArgsFromVp(argc, vp);
1976 args.rval().set(UndefinedValue());
1978 if (!args.get(0).isObject()) {
1979 JS_ReportErrorASCII(cx, "argument is not an object");
1980 return false;
1983 bool asString = false;
1984 RootedValue tierSelection(cx);
1985 int kindSelection = (1 << wasm::CodeRange::Function);
1986 if (args.length() > 1 && args[1].isObject()) {
1987 RootedObject options(cx, &args[1].toObject());
1988 RootedValue val(cx);
1990 if (!JS_GetProperty(cx, options, "asString", &val)) {
1991 return false;
1993 asString = val.isBoolean() && val.toBoolean();
1995 if (!JS_GetProperty(cx, options, "tier", &tierSelection)) {
1996 return false;
1999 if (!JS_GetProperty(cx, options, "kinds", &val)) {
2000 return false;
2002 if (val.isString() && val.toString()->hasLatin1Chars()) {
2003 AutoStableStringChars stable(cx);
2004 if (!stable.init(cx, val.toString())) {
2005 return false;
2007 const char* p = (const char*)(stable.latin1Chars());
2008 const char* end = p + val.toString()->length();
2009 int selection = 0;
2010 for (;;) {
2011 if (strncmp(p, "Function", 8) == 0) {
2012 selection |= (1 << wasm::CodeRange::Function);
2013 p += 8;
2014 } else if (strncmp(p, "InterpEntry", 11) == 0) {
2015 selection |= (1 << wasm::CodeRange::InterpEntry);
2016 p += 11;
2017 } else if (strncmp(p, "JitEntry", 8) == 0) {
2018 selection |= (1 << wasm::CodeRange::JitEntry);
2019 p += 8;
2020 } else if (strncmp(p, "ImportInterpExit", 16) == 0) {
2021 selection |= (1 << wasm::CodeRange::ImportInterpExit);
2022 p += 16;
2023 } else if (strncmp(p, "ImportJitExit", 13) == 0) {
2024 selection |= (1 << wasm::CodeRange::ImportJitExit);
2025 p += 13;
2026 } else if (strncmp(p, "all", 3) == 0) {
2027 selection = ~0;
2028 p += 3;
2029 } else {
2030 break;
2032 if (p == end || *p != ',') {
2033 break;
2035 p++;
2037 if (p == end) {
2038 kindSelection = selection;
2039 } else {
2040 JS_ReportErrorASCII(cx, "argument object has invalid `kinds`");
2041 return false;
2046 RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>());
2047 if (func && wasm::IsWasmExportedFunction(func)) {
2048 return WasmDisassembleFunction(cx, func, tierSelection, asString,
2049 args.rval());
2051 if (args[0].toObject().is<WasmModuleObject>()) {
2052 return WasmDisassembleCode(
2053 cx, args[0].toObject().as<WasmModuleObject>().module().code(),
2054 tierSelection, kindSelection, asString, args.rval());
2056 if (args[0].toObject().is<WasmInstanceObject>()) {
2057 return WasmDisassembleCode(
2058 cx, args[0].toObject().as<WasmInstanceObject>().instance().code(),
2059 tierSelection, kindSelection, asString, args.rval());
2061 JS_ReportErrorASCII(
2062 cx, "argument is not an exported wasm function or a wasm module");
2063 return false;
2066 static bool WasmFunctionTier(JSContext* cx, unsigned argc, Value* vp) {
2067 if (!wasm::HasSupport(cx)) {
2068 JS_ReportErrorASCII(cx, "wasm support unavailable");
2069 return false;
2072 CallArgs args = CallArgsFromVp(argc, vp);
2074 args.rval().set(UndefinedValue());
2076 if (!args.get(0).isObject()) {
2077 JS_ReportErrorASCII(cx, "argument is not an object");
2078 return false;
2081 RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>());
2082 if (func && wasm::IsWasmExportedFunction(func)) {
2083 uint32_t funcIndex = wasm::ExportedFunctionToFuncIndex(func);
2084 wasm::Instance& instance = wasm::ExportedFunctionToInstance(func);
2085 wasm::Tier tier = instance.code().funcTier(funcIndex);
2086 RootedString tierString(cx, JS_NewStringCopyZ(cx, wasm::ToString(tier)));
2087 if (!tierString) {
2088 ReportOutOfMemory(cx);
2089 return false;
2091 args.rval().set(StringValue(tierString));
2092 return true;
2094 JS_ReportErrorASCII(cx, "argument is not an exported wasm function");
2095 return false;
2098 static bool ToIonDumpContents(JSContext* cx, HandleValue value,
2099 wasm::IonDumpContents* contents) {
2100 RootedString option(cx, JS::ToString(cx, value));
2102 if (!option) {
2103 return false;
2106 bool isEqual = false;
2107 if (!JS_StringEqualsLiteral(cx, option, "mir", &isEqual) || isEqual) {
2108 *contents = wasm::IonDumpContents::UnoptimizedMIR;
2109 return isEqual;
2110 } else if (!JS_StringEqualsLiteral(cx, option, "unopt-mir", &isEqual) ||
2111 isEqual) {
2112 *contents = wasm::IonDumpContents::UnoptimizedMIR;
2113 return isEqual;
2114 } else if (!JS_StringEqualsLiteral(cx, option, "opt-mir", &isEqual) ||
2115 isEqual) {
2116 *contents = wasm::IonDumpContents::OptimizedMIR;
2117 return isEqual;
2118 } else if (!JS_StringEqualsLiteral(cx, option, "lir", &isEqual) || isEqual) {
2119 *contents = wasm::IonDumpContents::LIR;
2120 return isEqual;
2121 } else {
2122 return false;
2126 static bool WasmDumpIon(JSContext* cx, unsigned argc, Value* vp) {
2127 if (!wasm::HasSupport(cx)) {
2128 JS_ReportErrorASCII(cx, "wasm support unavailable");
2129 return false;
2132 CallArgs args = CallArgsFromVp(argc, vp);
2134 args.rval().set(UndefinedValue());
2136 if (!args.get(0).isObject()) {
2137 JS_ReportErrorASCII(cx, "argument is not an object");
2138 return false;
2141 uint32_t targetFuncIndex;
2142 if (!ToUint32(cx, args.get(1), &targetFuncIndex)) {
2143 JS_ReportErrorASCII(cx, "argument is not a func index");
2144 return false;
2147 wasm::IonDumpContents contents = wasm::IonDumpContents::Default;
2148 if (args.length() > 2 && !ToIonDumpContents(cx, args.get(2), &contents)) {
2149 JS_ReportErrorASCII(cx, "argument is not a valid dump contents");
2150 return false;
2153 SharedMem<uint8_t*> dataPointer;
2154 size_t byteLength;
2155 if (!IsBufferSource(args.get(0).toObjectOrNull(), &dataPointer,
2156 &byteLength)) {
2157 JS_ReportErrorASCII(cx, "argument is not a buffer source");
2158 return false;
2161 wasm::MutableBytes bytecode = cx->new_<wasm::ShareableBytes>();
2162 if (!bytecode) {
2163 return false;
2165 if (!bytecode->append(dataPointer.unwrap(), byteLength)) {
2166 ReportOutOfMemory(cx);
2167 return false;
2170 UniqueChars error;
2171 JSSprinter out(cx);
2172 if (!out.init()) {
2173 ReportOutOfMemory(cx);
2174 return false;
2177 if (!wasm::DumpIonFunctionInModule(*bytecode, targetFuncIndex, contents, out,
2178 &error)) {
2179 if (error) {
2180 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2181 JSMSG_WASM_COMPILE_ERROR, error.get());
2182 return false;
2184 ReportOutOfMemory(cx);
2185 return false;
2188 JSString* str = out.release(cx);
2189 if (!str) {
2190 ReportOutOfMemory(cx);
2191 return false;
2193 args.rval().set(StringValue(str));
2194 return true;
2197 enum class Flag { Tier2Complete, Deserialized, ParsedBranchHints };
2199 static bool WasmReturnFlag(JSContext* cx, unsigned argc, Value* vp, Flag flag) {
2200 CallArgs args = CallArgsFromVp(argc, vp);
2202 if (!args.get(0).isObject()) {
2203 JS_ReportErrorASCII(cx, "argument is not an object");
2204 return false;
2207 Rooted<WasmModuleObject*> module(
2208 cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>());
2209 if (!module) {
2210 JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
2211 return false;
2214 bool b;
2215 switch (flag) {
2216 case Flag::Tier2Complete:
2217 b = !module->module().testingTier2Active();
2218 break;
2219 case Flag::Deserialized:
2220 b = module->module().loggingDeserialized();
2221 break;
2222 case Flag::ParsedBranchHints:
2223 b = !module->module().codeMeta().branchHints.failedParse();
2224 break;
2227 args.rval().set(BooleanValue(b));
2228 return true;
2231 static bool wasmMetadataAnalysis(JSContext* cx, unsigned argc, Value* vp) {
2232 CallArgs args = CallArgsFromVp(argc, vp);
2234 if (!args.get(0).isObject()) {
2235 JS_ReportErrorASCII(cx, "argument is not an object");
2236 return false;
2239 if (args[0].toObject().is<WasmModuleObject>()) {
2240 HashMap<const char*, uint32_t, mozilla::CStringHasher, SystemAllocPolicy>
2241 hashmap = args[0]
2242 .toObject()
2243 .as<WasmModuleObject>()
2244 .module()
2245 .code()
2246 .metadataAnalysis(cx);
2247 if (hashmap.empty()) {
2248 JS_ReportErrorASCII(cx, "Metadata analysis has failed");
2249 return false;
2252 // metadataAnalysis returned a map of {key, value} with various statistics
2253 // convert it into a dictionary to be used by JS
2254 Rooted<IdValueVector> props(cx, IdValueVector(cx));
2256 for (auto iter = hashmap.iter(); !iter.done(); iter.next()) {
2257 const auto* key = iter.get().key();
2258 auto value = iter.get().value();
2260 JSString* string = JS_NewStringCopyZ(cx, key);
2261 if (!string) {
2262 return false;
2265 if (!props.append(
2266 IdValuePair(NameToId(string->asLinear().toPropertyName(cx)),
2267 NumberValue(value)))) {
2268 return false;
2272 JSObject* results = NewPlainObjectWithUniqueNames(cx, props);
2273 if (!results) {
2274 return false;
2276 args.rval().setObject(*results);
2278 return true;
2281 JS_ReportErrorASCII(
2282 cx, "argument is not an exported wasm function or a wasm module");
2284 return false;
2287 static bool WasmHasTier2CompilationCompleted(JSContext* cx, unsigned argc,
2288 Value* vp) {
2289 return WasmReturnFlag(cx, argc, vp, Flag::Tier2Complete);
2292 static bool WasmLoadedFromCache(JSContext* cx, unsigned argc, Value* vp) {
2293 return WasmReturnFlag(cx, argc, vp, Flag::Deserialized);
2296 #ifdef ENABLE_WASM_BRANCH_HINTING
2297 static bool WasmParsedBranchHints(JSContext* cx, unsigned argc, Value* vp) {
2298 return WasmReturnFlag(cx, argc, vp, Flag::ParsedBranchHints);
2300 #endif // ENABLE_WASM_BRANCH_HINTING
2302 static bool WasmBuiltinI8VecMul(JSContext* cx, unsigned argc, Value* vp) {
2303 if (!wasm::HasSupport(cx)) {
2304 JS_ReportErrorASCII(cx, "wasm support unavailable");
2305 return false;
2308 CallArgs args = CallArgsFromVp(argc, vp);
2310 Rooted<WasmModuleObject*> module(cx);
2311 if (!wasm::CompileBuiltinModule(cx, wasm::BuiltinModuleId::SelfTest,
2312 &module)) {
2313 return false;
2315 args.rval().set(ObjectValue(*module.get()));
2316 return true;
2319 #ifdef ENABLE_WASM_GC
2320 static bool WasmGcReadField(JSContext* cx, unsigned argc, Value* vp) {
2321 CallArgs args = CallArgsFromVp(argc, vp);
2322 RootedObject callee(cx, &args.callee());
2324 if (!args.requireAtLeast(cx, "wasmGcReadField", 2)) {
2325 return false;
2328 if (!args[0].isObject() || !args[0].toObject().is<WasmGcObject>()) {
2329 ReportUsageErrorASCII(cx, callee,
2330 "First argument must be a WebAssembly GC object");
2331 return false;
2334 int32_t fieldIndex;
2335 if (!JS::ToInt32(cx, args[1], &fieldIndex) || fieldIndex < 0) {
2336 ReportUsageErrorASCII(cx, callee,
2337 "Second argument must be a non-negative integer");
2338 return false;
2341 Rooted<WasmGcObject*> gcObject(cx, &args[0].toObject().as<WasmGcObject>());
2342 Rooted<Value> gcValue(cx);
2343 if (!WasmGcObject::loadValue(cx, gcObject, jsid::Int(int32_t(fieldIndex)),
2344 &gcValue)) {
2345 return false;
2348 args.rval().set(gcValue);
2349 return true;
2352 static bool WasmGcArrayLength(JSContext* cx, unsigned argc, Value* vp) {
2353 CallArgs args = CallArgsFromVp(argc, vp);
2354 RootedObject callee(cx, &args.callee());
2356 if (!args.requireAtLeast(cx, "wasmGcArrayLength", 1)) {
2357 return false;
2360 if (!args[0].isObject() || !args[0].toObject().is<WasmArrayObject>()) {
2361 ReportUsageErrorASCII(cx, callee,
2362 "First argument must be a WebAssembly GC array");
2363 return false;
2366 WasmArrayObject& arr = args[0].toObject().as<WasmArrayObject>();
2367 args.rval().setInt32(int32_t(arr.numElements_));
2368 return true;
2370 #endif // ENABLE_WASM_GC
2372 static bool LargeArrayBufferSupported(JSContext* cx, unsigned argc, Value* vp) {
2373 CallArgs args = CallArgsFromVp(argc, vp);
2374 args.rval().setBoolean(ArrayBufferObject::ByteLengthLimit >
2375 ArrayBufferObject::ByteLengthLimitForSmallBuffer);
2376 return true;
2379 static bool IsLazyFunction(JSContext* cx, unsigned argc, Value* vp) {
2380 CallArgs args = CallArgsFromVp(argc, vp);
2381 if (args.length() != 1) {
2382 JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
2383 return false;
2385 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
2386 JS_ReportErrorASCII(cx, "The first argument should be a function.");
2387 return false;
2389 JSFunction* fun = &args[0].toObject().as<JSFunction>();
2390 args.rval().setBoolean(fun->isInterpreted() && !fun->hasBytecode());
2391 return true;
2394 static bool IsRelazifiableFunction(JSContext* cx, unsigned argc, Value* vp) {
2395 CallArgs args = CallArgsFromVp(argc, vp);
2396 if (args.length() != 1) {
2397 JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
2398 return false;
2400 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
2401 JS_ReportErrorASCII(cx, "The first argument should be a function.");
2402 return false;
2405 JSFunction* fun = &args[0].toObject().as<JSFunction>();
2406 args.rval().setBoolean(fun->hasBytecode() &&
2407 fun->nonLazyScript()->allowRelazify());
2408 return true;
2411 static bool IsInStencilCache(JSContext* cx, unsigned argc, Value* vp) {
2412 CallArgs args = CallArgsFromVp(argc, vp);
2413 if (args.length() != 1) {
2414 JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
2415 return false;
2418 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
2419 JS_ReportErrorASCII(cx, "The first argument should be a function.");
2420 return false;
2423 if (fuzzingSafe) {
2424 // When running code concurrently to fill-up the stencil cache, the content
2425 // is not garanteed to be present.
2426 args.rval().setBoolean(false);
2427 return true;
2430 JSFunction* fun = &args[0].toObject().as<JSFunction>();
2431 BaseScript* script = fun->baseScript();
2432 RefPtr<ScriptSource> ss = script->scriptSource();
2433 DelazificationCache& cache = DelazificationCache::getSingleton();
2434 auto guard = cache.isSourceCached(ss);
2435 if (!guard) {
2436 args.rval().setBoolean(false);
2437 return true;
2440 StencilContext key(ss, script->extent());
2441 frontend::CompilationStencil* stencil = cache.lookup(guard, key);
2442 args.rval().setBoolean(bool(stencil));
2443 return true;
2446 static bool WaitForStencilCache(JSContext* cx, unsigned argc, Value* vp) {
2447 CallArgs args = CallArgsFromVp(argc, vp);
2448 if (args.length() != 1) {
2449 JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
2450 return false;
2453 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
2454 JS_ReportErrorASCII(cx, "The first argument should be a function.");
2455 return false;
2457 args.rval().setUndefined();
2459 JSFunction* fun = &args[0].toObject().as<JSFunction>();
2460 BaseScript* script = fun->baseScript();
2461 RefPtr<ScriptSource> ss = script->scriptSource();
2462 DelazificationCache& cache = DelazificationCache::getSingleton();
2463 StencilContext key(ss, script->extent());
2465 AutoLockHelperThreadState lock;
2466 if (!HelperThreadState().isInitialized(lock)) {
2467 return true;
2470 while (true) {
2472 // This capture a Mutex that we have to release before using the wait
2473 // function.
2474 auto guard = cache.isSourceCached(ss);
2475 if (!guard) {
2476 return true;
2479 frontend::CompilationStencil* stencil = cache.lookup(guard, key);
2480 if (stencil) {
2481 break;
2485 HelperThreadState().wait(lock);
2487 return true;
2490 static bool HasSameBytecodeData(JSContext* cx, unsigned argc, Value* vp) {
2491 CallArgs args = CallArgsFromVp(argc, vp);
2492 if (args.length() != 2) {
2493 JS_ReportErrorASCII(cx, "The function takes exactly two argument.");
2494 return false;
2497 auto GetSharedData = [](JSContext* cx,
2498 HandleValue v) -> SharedImmutableScriptData* {
2499 if (!v.isObject()) {
2500 JS_ReportErrorASCII(cx, "The arguments must be interpreted functions.");
2501 return nullptr;
2504 RootedObject obj(cx, CheckedUnwrapDynamic(&v.toObject(), cx));
2505 if (!obj) {
2506 return nullptr;
2509 if (!obj->is<JSFunction>() || !obj->as<JSFunction>().isInterpreted()) {
2510 JS_ReportErrorASCII(cx, "The arguments must be interpreted functions.");
2511 return nullptr;
2514 AutoRealm ar(cx, obj);
2515 RootedFunction fun(cx, &obj->as<JSFunction>());
2516 RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
2517 if (!script) {
2518 return nullptr;
2521 MOZ_ASSERT(script->sharedData());
2522 return script->sharedData();
2525 // NOTE: We use RefPtr below to keep the data alive across possible GC since
2526 // the functions may be in different Zones.
2528 RefPtr<SharedImmutableScriptData> sharedData1 = GetSharedData(cx, args[0]);
2529 if (!sharedData1) {
2530 return false;
2533 RefPtr<SharedImmutableScriptData> sharedData2 = GetSharedData(cx, args[1]);
2534 if (!sharedData2) {
2535 return false;
2538 args.rval().setBoolean(sharedData1 == sharedData2);
2539 return true;
2542 static bool InternalConst(JSContext* cx, unsigned argc, Value* vp) {
2543 CallArgs args = CallArgsFromVp(argc, vp);
2544 if (args.length() == 0) {
2545 JS_ReportErrorASCII(cx, "the function takes exactly one argument");
2546 return false;
2549 JSString* str = ToString(cx, args[0]);
2550 if (!str) {
2551 return false;
2553 JSLinearString* linear = JS_EnsureLinearString(cx, str);
2554 if (!linear) {
2555 return false;
2558 if (JS_LinearStringEqualsLiteral(linear, "MARK_STACK_BASE_CAPACITY")) {
2559 args.rval().setNumber(uint32_t(js::MARK_STACK_BASE_CAPACITY));
2560 } else {
2561 JS_ReportErrorASCII(cx, "unknown const name");
2562 return false;
2564 return true;
2567 static bool GCPreserveCode(JSContext* cx, unsigned argc, Value* vp) {
2568 CallArgs args = CallArgsFromVp(argc, vp);
2570 if (args.length() != 0) {
2571 RootedObject callee(cx, &args.callee());
2572 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2573 return false;
2576 cx->runtime()->gc.setAlwaysPreserveCode();
2578 args.rval().setUndefined();
2579 return true;
2582 #ifdef JS_GC_ZEAL
2584 static bool ParseGCZealMode(JSContext* cx, const CallArgs& args,
2585 uint8_t* zeal) {
2586 uint32_t value;
2587 if (!ToUint32(cx, args.get(0), &value)) {
2588 return false;
2591 if (value > uint32_t(gc::ZealMode::Limit)) {
2592 JS_ReportErrorASCII(cx, "gczeal argument out of range");
2593 return false;
2596 *zeal = static_cast<uint8_t>(value);
2597 return true;
2600 static bool GCZeal(JSContext* cx, unsigned argc, Value* vp) {
2601 CallArgs args = CallArgsFromVp(argc, vp);
2603 if (args.length() > 2) {
2604 RootedObject callee(cx, &args.callee());
2605 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2606 return false;
2609 if (args.length() == 0) {
2610 uint32_t zealBits, unused1, unused2;
2611 cx->runtime()->gc.getZealBits(&zealBits, &unused1, &unused2);
2612 args.rval().setNumber(zealBits);
2613 return true;
2616 uint8_t zeal;
2617 if (!ParseGCZealMode(cx, args, &zeal)) {
2618 return false;
2621 uint32_t frequency = JS::ShellDefaultGCZealFrequency;
2622 if (args.length() >= 2) {
2623 if (!ToUint32(cx, args.get(1), &frequency)) {
2624 return false;
2628 JS::SetGCZeal(cx, zeal, frequency);
2629 args.rval().setUndefined();
2630 return true;
2633 static bool UnsetGCZeal(JSContext* cx, unsigned argc, Value* vp) {
2634 CallArgs args = CallArgsFromVp(argc, vp);
2636 if (args.length() > 1) {
2637 RootedObject callee(cx, &args.callee());
2638 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2639 return false;
2642 uint8_t zeal;
2643 if (!ParseGCZealMode(cx, args, &zeal)) {
2644 return false;
2647 JS::UnsetGCZeal(cx, zeal);
2648 args.rval().setUndefined();
2649 return true;
2652 static bool ScheduleGC(JSContext* cx, unsigned argc, Value* vp) {
2653 CallArgs args = CallArgsFromVp(argc, vp);
2655 if (args.length() > 1) {
2656 RootedObject callee(cx, &args.callee());
2657 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2658 return false;
2661 if (args.length() == 0) {
2662 /* Fetch next zeal trigger only. */
2663 } else if (args[0].isNumber()) {
2664 /* Schedule a GC to happen after |arg| allocations. */
2665 JS::ScheduleGC(cx, std::max(int(args[0].toNumber()), 0));
2666 } else {
2667 RootedObject callee(cx, &args.callee());
2668 ReportUsageErrorASCII(cx, callee, "Bad argument - expecting number");
2669 return false;
2672 uint32_t zealBits;
2673 uint32_t freq;
2674 uint32_t next;
2675 JS::GetGCZealBits(cx, &zealBits, &freq, &next);
2676 args.rval().setInt32(next);
2677 return true;
2680 static bool SelectForGC(JSContext* cx, unsigned argc, Value* vp) {
2681 CallArgs args = CallArgsFromVp(argc, vp);
2684 * The selectedForMarking set is intended to be manually marked at slice
2685 * start to detect missing pre-barriers. It is invalid for nursery things
2686 * to be in the set, so evict the nursery before adding items.
2688 cx->runtime()->gc.evictNursery();
2690 for (unsigned i = 0; i < args.length(); i++) {
2691 if (args[i].isObject()) {
2692 if (!cx->runtime()->gc.selectForMarking(&args[i].toObject())) {
2693 return false;
2698 args.rval().setUndefined();
2699 return true;
2702 static bool VerifyPreBarriers(JSContext* cx, unsigned argc, Value* vp) {
2703 CallArgs args = CallArgsFromVp(argc, vp);
2705 if (args.length() > 0) {
2706 RootedObject callee(cx, &args.callee());
2707 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2708 return false;
2711 gc::VerifyBarriers(cx->runtime(), gc::PreBarrierVerifier);
2712 args.rval().setUndefined();
2713 return true;
2716 static bool VerifyPostBarriers(JSContext* cx, unsigned argc, Value* vp) {
2717 // This is a no-op since the post barrier verifier was removed.
2718 CallArgs args = CallArgsFromVp(argc, vp);
2719 if (args.length()) {
2720 RootedObject callee(cx, &args.callee());
2721 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2722 return false;
2724 args.rval().setUndefined();
2725 return true;
2728 static bool CurrentGC(JSContext* cx, unsigned argc, Value* vp) {
2729 CallArgs args = CallArgsFromVp(argc, vp);
2731 if (args.length() != 0) {
2732 RootedObject callee(cx, &args.callee());
2733 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2734 return false;
2737 RootedObject result(cx, JS_NewPlainObject(cx));
2738 if (!result) {
2739 return false;
2742 js::gc::GCRuntime& gc = cx->runtime()->gc;
2743 const char* state = StateName(gc.state());
2745 RootedString str(cx, JS_NewStringCopyZ(cx, state));
2746 if (!str) {
2747 return false;
2749 RootedValue val(cx, StringValue(str));
2750 if (!JS_DefineProperty(cx, result, "incrementalState", val,
2751 JSPROP_ENUMERATE)) {
2752 return false;
2755 if (gc.state() == js::gc::State::Sweep) {
2756 val = Int32Value(gc.getCurrentSweepGroupIndex());
2757 if (!JS_DefineProperty(cx, result, "sweepGroup", val, JSPROP_ENUMERATE)) {
2758 return false;
2762 val = BooleanValue(gc.isIncrementalGCInProgress() && gc.isShrinkingGC());
2763 if (!JS_DefineProperty(cx, result, "isShrinking", val, JSPROP_ENUMERATE)) {
2764 return false;
2767 val = Int32Value(gc.gcNumber());
2768 if (!JS_DefineProperty(cx, result, "number", val, JSPROP_ENUMERATE)) {
2769 return false;
2772 val = Int32Value(gc.minorGCCount());
2773 if (!JS_DefineProperty(cx, result, "minorCount", val, JSPROP_ENUMERATE)) {
2774 return false;
2777 val = Int32Value(gc.majorGCCount());
2778 if (!JS_DefineProperty(cx, result, "majorCount", val, JSPROP_ENUMERATE)) {
2779 return false;
2782 val = BooleanValue(gc.isFullGc());
2783 if (!JS_DefineProperty(cx, result, "isFull", val, JSPROP_ENUMERATE)) {
2784 return false;
2787 val = BooleanValue(gc.isCompactingGc());
2788 if (!JS_DefineProperty(cx, result, "isCompacting", val, JSPROP_ENUMERATE)) {
2789 return false;
2792 # ifdef DEBUG
2793 val = Int32Value(gc.testMarkQueuePos());
2794 if (!JS_DefineProperty(cx, result, "queuePos", val, JSPROP_ENUMERATE)) {
2795 return false;
2797 # endif
2799 args.rval().setObject(*result);
2800 return true;
2803 static bool DeterministicGC(JSContext* cx, unsigned argc, Value* vp) {
2804 CallArgs args = CallArgsFromVp(argc, vp);
2806 if (args.length() != 1) {
2807 RootedObject callee(cx, &args.callee());
2808 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2809 return false;
2812 cx->runtime()->gc.setDeterministic(ToBoolean(args[0]));
2813 args.rval().setUndefined();
2814 return true;
2817 static bool DumpGCArenaInfo(JSContext* cx, unsigned argc, Value* vp) {
2818 CallArgs args = CallArgsFromVp(argc, vp);
2819 js::gc::DumpArenaInfo();
2820 args.rval().setUndefined();
2821 return true;
2824 static bool SetMarkStackLimit(JSContext* cx, unsigned argc, Value* vp) {
2825 CallArgs args = CallArgsFromVp(argc, vp);
2826 if (args.length() != 1) {
2827 RootedObject callee(cx, &args.callee());
2828 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2829 return false;
2832 int32_t value;
2833 if (!ToInt32(cx, args[0], &value) || value <= 0) {
2834 JS_ReportErrorASCII(cx, "Bad argument to SetMarkStackLimit");
2835 return false;
2838 if (JS::IsIncrementalGCInProgress(cx)) {
2839 JS_ReportErrorASCII(
2840 cx, "Attempt to set markStackLimit while a GC is in progress");
2841 return false;
2844 JSRuntime* runtime = cx->runtime();
2845 AutoLockGC lock(runtime);
2846 runtime->gc.setMarkStackLimit(value, lock);
2847 args.rval().setUndefined();
2848 return true;
2851 #endif /* JS_GC_ZEAL */
2853 static bool SetMallocMaxDirtyPageModifier(JSContext* cx, unsigned argc,
2854 Value* vp) {
2855 CallArgs args = CallArgsFromVp(argc, vp);
2856 if (args.length() != 1) {
2857 RootedObject callee(cx, &args.callee());
2858 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2859 return false;
2862 constexpr int32_t MinSupportedValue = -5;
2863 constexpr int32_t MaxSupportedValue = 16;
2865 int32_t value;
2866 if (!ToInt32(cx, args[0], &value)) {
2867 return false;
2869 if (value < MinSupportedValue || value > MaxSupportedValue) {
2870 JS_ReportErrorASCII(cx, "Bad argument to setMallocMaxDirtyPageModifier");
2871 return false;
2874 moz_set_max_dirty_page_modifier(value);
2876 args.rval().setUndefined();
2877 return true;
2880 static bool GCState(JSContext* cx, unsigned argc, Value* vp) {
2881 CallArgs args = CallArgsFromVp(argc, vp);
2883 if (args.length() > 1) {
2884 RootedObject callee(cx, &args.callee());
2885 ReportUsageErrorASCII(cx, callee, "Too many arguments");
2886 return false;
2889 const char* state;
2891 if (args.length() == 1) {
2892 if (!args[0].isObject()) {
2893 RootedObject callee(cx, &args.callee());
2894 ReportUsageErrorASCII(cx, callee, "Expected object");
2895 return false;
2898 JSObject* obj = UncheckedUnwrap(&args[0].toObject());
2899 state = gc::StateName(obj->zone()->gcState());
2900 } else {
2901 state = gc::StateName(cx->runtime()->gc.state());
2904 return ReturnStringCopy(cx, args, state);
2907 static bool ScheduleZoneForGC(JSContext* cx, unsigned argc, Value* vp) {
2908 CallArgs args = CallArgsFromVp(argc, vp);
2910 if (args.length() != 1) {
2911 RootedObject callee(cx, &args.callee());
2912 ReportUsageErrorASCII(cx, callee, "Expecting a single argument");
2913 return false;
2916 if (args[0].isObject()) {
2917 // Ensure that |zone| is collected during the next GC.
2918 Zone* zone = UncheckedUnwrap(&args[0].toObject())->zone();
2919 PrepareZoneForGC(cx, zone);
2920 } else if (args[0].isString()) {
2921 // This allows us to schedule the atoms zone for GC.
2922 Zone* zone = args[0].toString()->zoneFromAnyThread();
2923 if (!CurrentThreadCanAccessZone(zone)) {
2924 RootedObject callee(cx, &args.callee());
2925 ReportUsageErrorASCII(cx, callee, "Specified zone not accessible for GC");
2926 return false;
2928 PrepareZoneForGC(cx, zone);
2929 } else {
2930 RootedObject callee(cx, &args.callee());
2931 ReportUsageErrorASCII(cx, callee,
2932 "Bad argument - expecting object or string");
2933 return false;
2936 args.rval().setUndefined();
2937 return true;
2940 static bool StartGC(JSContext* cx, unsigned argc, Value* vp) {
2941 CallArgs args = CallArgsFromVp(argc, vp);
2943 if (args.length() > 2) {
2944 RootedObject callee(cx, &args.callee());
2945 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2946 return false;
2949 auto budget = SliceBudget::unlimited();
2950 if (args.length() >= 1) {
2951 uint32_t work = 0;
2952 if (!ToUint32(cx, args[0], &work)) {
2953 return false;
2955 budget = SliceBudget(WorkBudget(work));
2958 bool shrinking = false;
2959 if (args.length() >= 2) {
2960 Value arg = args[1];
2961 if (arg.isString()) {
2962 if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking",
2963 &shrinking)) {
2964 return false;
2969 JSRuntime* rt = cx->runtime();
2970 if (rt->gc.isIncrementalGCInProgress()) {
2971 RootedObject callee(cx, &args.callee());
2972 JS_ReportErrorASCII(cx, "Incremental GC already in progress");
2973 return false;
2976 JS::GCOptions options =
2977 shrinking ? JS::GCOptions::Shrink : JS::GCOptions::Normal;
2978 rt->gc.startDebugGC(options, budget);
2980 args.rval().setUndefined();
2981 return true;
2984 static bool FinishGC(JSContext* cx, unsigned argc, Value* vp) {
2985 CallArgs args = CallArgsFromVp(argc, vp);
2987 if (args.length() > 0) {
2988 RootedObject callee(cx, &args.callee());
2989 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
2990 return false;
2993 JSRuntime* rt = cx->runtime();
2994 if (rt->gc.isIncrementalGCInProgress()) {
2995 rt->gc.finishGC(JS::GCReason::DEBUG_GC);
2998 args.rval().setUndefined();
2999 return true;
3002 static bool GCSlice(JSContext* cx, unsigned argc, Value* vp) {
3003 CallArgs args = CallArgsFromVp(argc, vp);
3005 if (args.length() > 2) {
3006 RootedObject callee(cx, &args.callee());
3007 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
3008 return false;
3011 auto budget = SliceBudget::unlimited();
3012 if (args.length() >= 1) {
3013 uint32_t work = 0;
3014 if (!ToUint32(cx, args[0], &work)) {
3015 return false;
3017 budget = SliceBudget(WorkBudget(work));
3020 bool dontStart = false;
3021 if (args.get(1).isObject()) {
3022 RootedObject options(cx, &args[1].toObject());
3023 RootedValue v(cx);
3024 if (!JS_GetProperty(cx, options, "dontStart", &v)) {
3025 return false;
3027 dontStart = ToBoolean(v);
3030 JSRuntime* rt = cx->runtime();
3031 if (rt->gc.isIncrementalGCInProgress()) {
3032 rt->gc.debugGCSlice(budget);
3033 } else if (!dontStart) {
3034 rt->gc.startDebugGC(JS::GCOptions::Normal, budget);
3037 args.rval().setUndefined();
3038 return true;
3041 static bool AbortGC(JSContext* cx, unsigned argc, Value* vp) {
3042 CallArgs args = CallArgsFromVp(argc, vp);
3044 if (args.length() != 0) {
3045 RootedObject callee(cx, &args.callee());
3046 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
3047 return false;
3050 JS::AbortIncrementalGC(cx);
3051 args.rval().setUndefined();
3052 return true;
3055 static bool FullCompartmentChecks(JSContext* cx, unsigned argc, Value* vp) {
3056 CallArgs args = CallArgsFromVp(argc, vp);
3058 if (args.length() != 1) {
3059 RootedObject callee(cx, &args.callee());
3060 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
3061 return false;
3064 cx->runtime()->gc.setFullCompartmentChecks(ToBoolean(args[0]));
3065 args.rval().setUndefined();
3066 return true;
3069 static bool NondeterministicGetWeakMapKeys(JSContext* cx, unsigned argc,
3070 Value* vp) {
3071 CallArgs args = CallArgsFromVp(argc, vp);
3073 if (args.length() != 1) {
3074 RootedObject callee(cx, &args.callee());
3075 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
3076 return false;
3078 if (!args[0].isObject()) {
3079 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3080 JSMSG_NOT_EXPECTED_TYPE,
3081 "nondeterministicGetWeakMapKeys", "WeakMap",
3082 InformalValueTypeName(args[0]));
3083 return false;
3085 RootedObject arr(cx);
3086 RootedObject mapObj(cx, &args[0].toObject());
3087 if (!JS_NondeterministicGetWeakMapKeys(cx, mapObj, &arr)) {
3088 return false;
3090 if (!arr) {
3091 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3092 JSMSG_NOT_EXPECTED_TYPE,
3093 "nondeterministicGetWeakMapKeys", "WeakMap",
3094 args[0].toObject().getClass()->name);
3095 return false;
3097 args.rval().setObject(*arr);
3098 return true;
3101 class HasChildTracer final : public JS::CallbackTracer {
3102 RootedValue child_;
3103 bool found_;
3105 void onChild(JS::GCCellPtr thing, const char* name) override {
3106 if (thing.asCell() == child_.toGCThing()) {
3107 found_ = true;
3111 public:
3112 HasChildTracer(JSContext* cx, HandleValue child)
3113 : JS::CallbackTracer(cx, JS::TracerKind::Callback,
3114 JS::WeakMapTraceAction::TraceKeysAndValues),
3115 child_(cx, child),
3116 found_(false) {}
3118 bool found() const { return found_; }
3121 static bool HasChild(JSContext* cx, unsigned argc, Value* vp) {
3122 CallArgs args = CallArgsFromVp(argc, vp);
3123 RootedValue parent(cx, args.get(0));
3124 RootedValue child(cx, args.get(1));
3126 if (!parent.isGCThing() || !child.isGCThing()) {
3127 args.rval().setBoolean(false);
3128 return true;
3131 HasChildTracer trc(cx, child);
3132 TraceChildren(&trc, JS::GCCellPtr(parent.toGCThing(), parent.traceKind()));
3133 args.rval().setBoolean(trc.found());
3134 return true;
3137 static bool SetSavedStacksRNGState(JSContext* cx, unsigned argc, Value* vp) {
3138 CallArgs args = CallArgsFromVp(argc, vp);
3139 if (!args.requireAtLeast(cx, "setSavedStacksRNGState", 1)) {
3140 return false;
3143 int32_t seed;
3144 if (!ToInt32(cx, args[0], &seed)) {
3145 return false;
3148 // Either one or the other of the seed arguments must be non-zero;
3149 // make this true no matter what value 'seed' has.
3150 cx->realm()->savedStacks().setRNGState(seed, (seed + 1) * 33);
3151 return true;
3154 static bool GetSavedFrameCount(JSContext* cx, unsigned argc, Value* vp) {
3155 CallArgs args = CallArgsFromVp(argc, vp);
3156 args.rval().setNumber(cx->realm()->savedStacks().count());
3157 return true;
3160 static bool ClearSavedFrames(JSContext* cx, unsigned argc, Value* vp) {
3161 CallArgs args = CallArgsFromVp(argc, vp);
3163 js::SavedStacks& savedStacks = cx->realm()->savedStacks();
3164 savedStacks.clear();
3166 for (ActivationIterator iter(cx); !iter.done(); ++iter) {
3167 iter->clearLiveSavedFrameCache();
3170 args.rval().setUndefined();
3171 return true;
3174 static bool SaveStack(JSContext* cx, unsigned argc, Value* vp) {
3175 CallArgs args = CallArgsFromVp(argc, vp);
3177 JS::StackCapture capture((JS::AllFrames()));
3178 if (args.length() >= 1) {
3179 double maxDouble;
3180 if (!ToNumber(cx, args[0], &maxDouble)) {
3181 return false;
3183 if (std::isnan(maxDouble) || maxDouble < 0 || maxDouble > UINT32_MAX) {
3184 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
3185 nullptr, "not a valid maximum frame count");
3186 return false;
3188 uint32_t max = uint32_t(maxDouble);
3189 if (max > 0) {
3190 capture = JS::StackCapture(JS::MaxFrames(max));
3194 RootedObject compartmentObject(cx);
3195 if (args.length() >= 2) {
3196 if (!args[1].isObject()) {
3197 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
3198 nullptr, "not an object");
3199 return false;
3201 compartmentObject = UncheckedUnwrap(&args[1].toObject());
3202 if (!compartmentObject) {
3203 return false;
3207 RootedObject stack(cx);
3209 Maybe<AutoRealm> ar;
3210 if (compartmentObject) {
3211 ar.emplace(cx, compartmentObject);
3213 if (!JS::CaptureCurrentStack(cx, &stack, std::move(capture))) {
3214 return false;
3218 if (stack && !cx->compartment()->wrap(cx, &stack)) {
3219 return false;
3222 args.rval().setObjectOrNull(stack);
3223 return true;
3226 static bool CaptureFirstSubsumedFrame(JSContext* cx, unsigned argc,
3227 JS::Value* vp) {
3228 CallArgs args = CallArgsFromVp(argc, vp);
3229 if (!args.requireAtLeast(cx, "captureFirstSubsumedFrame", 1)) {
3230 return false;
3233 if (!args[0].isObject()) {
3234 JS_ReportErrorASCII(cx, "The argument must be an object");
3235 return false;
3238 RootedObject obj(cx, &args[0].toObject());
3239 obj = CheckedUnwrapStatic(obj);
3240 if (!obj) {
3241 JS_ReportErrorASCII(cx, "Denied permission to object.");
3242 return false;
3245 JS::StackCapture capture(
3246 JS::FirstSubsumedFrame(cx, obj->nonCCWRealm()->principals()));
3247 if (args.length() > 1) {
3248 capture.as<JS::FirstSubsumedFrame>().ignoreSelfHosted =
3249 JS::ToBoolean(args[1]);
3252 JS::RootedObject capturedStack(cx);
3253 if (!JS::CaptureCurrentStack(cx, &capturedStack, std::move(capture))) {
3254 return false;
3257 args.rval().setObjectOrNull(capturedStack);
3258 return true;
3261 static bool CallFunctionFromNativeFrame(JSContext* cx, unsigned argc,
3262 Value* vp) {
3263 CallArgs args = CallArgsFromVp(argc, vp);
3265 if (args.length() != 1) {
3266 JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
3267 return false;
3269 if (!args[0].isObject() || !IsCallable(args[0])) {
3270 JS_ReportErrorASCII(cx, "The first argument should be a function.");
3271 return false;
3274 RootedObject function(cx, &args[0].toObject());
3275 return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(),
3276 args.rval());
3279 static bool CallFunctionWithAsyncStack(JSContext* cx, unsigned argc,
3280 Value* vp) {
3281 CallArgs args = CallArgsFromVp(argc, vp);
3283 if (args.length() != 3) {
3284 JS_ReportErrorASCII(cx, "The function takes exactly three arguments.");
3285 return false;
3287 if (!args[0].isObject() || !IsCallable(args[0])) {
3288 JS_ReportErrorASCII(cx, "The first argument should be a function.");
3289 return false;
3291 if (!args[1].isObject() || !args[1].toObject().is<SavedFrame>()) {
3292 JS_ReportErrorASCII(cx, "The second argument should be a SavedFrame.");
3293 return false;
3295 if (!args[2].isString() || args[2].toString()->empty()) {
3296 JS_ReportErrorASCII(cx, "The third argument should be a non-empty string.");
3297 return false;
3300 RootedObject function(cx, &args[0].toObject());
3301 RootedObject stack(cx, &args[1].toObject());
3302 RootedString asyncCause(cx, args[2].toString());
3303 UniqueChars utf8Cause = JS_EncodeStringToUTF8(cx, asyncCause);
3304 if (!utf8Cause) {
3305 MOZ_ASSERT(cx->isExceptionPending());
3306 return false;
3309 JS::AutoSetAsyncStackForNewCalls sas(
3310 cx, stack, utf8Cause.get(),
3311 JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
3312 return Call(cx, UndefinedHandleValue, function, JS::HandleValueArray::empty(),
3313 args.rval());
3316 static bool EnableTrackAllocations(JSContext* cx, unsigned argc, Value* vp) {
3317 SetAllocationMetadataBuilder(cx, &SavedStacks::metadataBuilder);
3318 return true;
3321 static bool DisableTrackAllocations(JSContext* cx, unsigned argc, Value* vp) {
3322 SetAllocationMetadataBuilder(cx, nullptr);
3323 return true;
3326 static bool SetTestFilenameValidationCallback(JSContext* cx, unsigned argc,
3327 Value* vp) {
3328 CallArgs args = CallArgsFromVp(argc, vp);
3330 // Accept all filenames that start with "safe". In system code also accept
3331 // filenames starting with "system".
3332 auto testCb = [](JSContext* cx, const char* filename) -> bool {
3333 if (strstr(filename, "safe") == filename) {
3334 return true;
3336 if (cx->realm()->isSystem() && strstr(filename, "system") == filename) {
3337 return true;
3340 return false;
3342 JS::SetFilenameValidationCallback(testCb);
3344 args.rval().setUndefined();
3345 return true;
3348 static JSAtom* GetPropertiesAddedName(JSContext* cx) {
3349 const char* propName = "_propertiesAdded";
3350 return Atomize(cx, propName, strlen(propName));
3353 static bool NewObjectWithAddPropertyHook(JSContext* cx, unsigned argc,
3354 Value* vp) {
3355 CallArgs args = CallArgsFromVp(argc, vp);
3357 auto addPropHook = [](JSContext* cx, HandleObject obj, HandleId id,
3358 HandleValue v) -> bool {
3359 Rooted<JSAtom*> propName(cx, GetPropertiesAddedName(cx));
3360 if (!propName) {
3361 return false;
3363 // Don't do anything if we're adding the _propertiesAdded property.
3364 RootedId propId(cx, AtomToId(propName));
3365 if (id == propId) {
3366 return true;
3368 // Increment _propertiesAdded.
3369 RootedValue val(cx);
3370 if (!JS_GetPropertyById(cx, obj, propId, &val)) {
3371 return false;
3373 if (!val.isInt32() || val.toInt32() == INT32_MAX) {
3374 return true;
3376 val.setInt32(val.toInt32() + 1);
3377 return JS_DefinePropertyById(cx, obj, propId, val, 0);
3380 static const JSClassOps classOps = {
3381 addPropHook, // addProperty
3382 nullptr, // delProperty
3383 nullptr, // enumerate
3384 nullptr, // newEnumerate
3385 nullptr, // resolve
3386 nullptr, // mayResolve
3387 nullptr, // finalize
3388 nullptr, // call
3389 nullptr, // construct
3390 nullptr, // trace
3392 static const JSClass cls = {
3393 "ObjectWithAddPropHook",
3395 &classOps,
3398 RootedObject obj(cx, JS_NewObject(cx, &cls));
3399 if (!obj) {
3400 return false;
3403 // Initialize _propertiesAdded to 0.
3404 Rooted<JSAtom*> propName(cx, GetPropertiesAddedName(cx));
3405 if (!propName) {
3406 return false;
3408 RootedId propId(cx, AtomToId(propName));
3409 RootedValue val(cx, Int32Value(0));
3410 if (!JS_DefinePropertyById(cx, obj, propId, val, 0)) {
3411 return false;
3414 args.rval().setObject(*obj);
3415 return true;
3418 static bool NewObjectWithCallHook(JSContext* cx, unsigned argc, Value* vp) {
3419 CallArgs args = CallArgsFromVp(argc, vp);
3421 static auto hookShared = [](JSContext* cx, CallArgs& args) {
3422 Rooted<PlainObject*> obj(cx, NewPlainObject(cx));
3423 if (!obj) {
3424 return false;
3427 // Define |this|. We can't expose the MagicValue to JS, so we use
3428 // "<is_constructing>" in that case.
3429 Rooted<Value> thisv(cx, args.thisv());
3430 if (thisv.isMagic(JS_IS_CONSTRUCTING)) {
3431 JSString* str = NewStringCopyZ<CanGC>(cx, "<is_constructing>");
3432 if (!str) {
3433 return false;
3435 thisv.setString(str);
3437 if (!DefineDataProperty(cx, obj, cx->names().this_, thisv,
3438 JSPROP_ENUMERATE)) {
3439 return false;
3442 // Define |callee|.
3443 if (!DefineDataProperty(cx, obj, cx->names().callee, args.calleev(),
3444 JSPROP_ENUMERATE)) {
3445 return false;
3448 // Define |arguments| array.
3449 Rooted<ArrayObject*> arr(
3450 cx, NewDenseCopiedArray(cx, args.length(), args.array()));
3451 if (!arr) {
3452 return false;
3454 Rooted<Value> arrVal(cx, ObjectValue(*arr));
3455 if (!DefineDataProperty(cx, obj, cx->names().arguments, arrVal,
3456 JSPROP_ENUMERATE)) {
3457 return false;
3460 // Define |newTarget| if constructing.
3461 if (args.isConstructing()) {
3462 const char* propName = "newTarget";
3463 Rooted<JSAtom*> name(cx, Atomize(cx, propName, strlen(propName)));
3464 if (!name) {
3465 return false;
3467 Rooted<PropertyKey> key(cx, NameToId(name->asPropertyName()));
3468 if (!DefineDataProperty(cx, obj, key, args.newTarget(),
3469 JSPROP_ENUMERATE)) {
3470 return false;
3474 args.rval().setObject(*obj);
3475 return true;
3478 static auto callHook = [](JSContext* cx, unsigned argc, Value* vp) {
3479 CallArgs args = CallArgsFromVp(argc, vp);
3480 MOZ_ASSERT(!args.isConstructing());
3481 return hookShared(cx, args);
3483 static auto constructHook = [](JSContext* cx, unsigned argc, Value* vp) {
3484 CallArgs args = CallArgsFromVp(argc, vp);
3485 MOZ_ASSERT(args.isConstructing());
3486 return hookShared(cx, args);
3489 static const JSClassOps classOps = {
3490 nullptr, // addProperty
3491 nullptr, // delProperty
3492 nullptr, // enumerate
3493 nullptr, // newEnumerate
3494 nullptr, // resolve
3495 nullptr, // mayResolve
3496 nullptr, // finalize
3497 callHook, // call
3498 constructHook, // construct
3499 nullptr, // trace
3501 static const JSClass cls = {
3502 "ObjectWithCallHook",
3504 &classOps,
3507 Rooted<JSObject*> obj(cx, JS_NewObject(cx, &cls));
3508 if (!obj) {
3509 return false;
3512 args.rval().setObject(*obj);
3513 return true;
3516 static constexpr JSClass ObjectWithManyReservedSlotsClass = {
3517 "ObjectWithManyReservedSlots",
3518 JSCLASS_HAS_RESERVED_SLOTS(40),
3521 static bool NewObjectWithManyReservedSlots(JSContext* cx, unsigned argc,
3522 Value* vp) {
3523 CallArgs args = CallArgsFromVp(argc, vp);
3525 static constexpr size_t NumReservedSlots =
3526 JSCLASS_RESERVED_SLOTS(&ObjectWithManyReservedSlotsClass);
3527 static_assert(NumReservedSlots > NativeObject::MAX_FIXED_SLOTS);
3529 RootedObject obj(cx, JS_NewObject(cx, &ObjectWithManyReservedSlotsClass));
3530 if (!obj) {
3531 return false;
3534 for (size_t i = 0; i < NumReservedSlots; i++) {
3535 JS_SetReservedSlot(obj, i, Int32Value(i));
3538 args.rval().setObject(*obj);
3539 return true;
3542 static bool CheckObjectWithManyReservedSlots(JSContext* cx, unsigned argc,
3543 Value* vp) {
3544 CallArgs args = CallArgsFromVp(argc, vp);
3546 if (args.length() != 1 || !args[0].isObject() ||
3547 args[0].toObject().getClass() != &ObjectWithManyReservedSlotsClass) {
3548 JS_ReportErrorASCII(cx,
3549 "Expected object from newObjectWithManyReservedSlots");
3550 return false;
3553 JSObject* obj = &args[0].toObject();
3555 static constexpr size_t NumReservedSlots =
3556 JSCLASS_RESERVED_SLOTS(&ObjectWithManyReservedSlotsClass);
3558 for (size_t i = 0; i < NumReservedSlots; i++) {
3559 MOZ_RELEASE_ASSERT(JS::GetReservedSlot(obj, i).toInt32() == int32_t(i));
3562 args.rval().setUndefined();
3563 return true;
3566 static bool GetWatchtowerLog(JSContext* cx, unsigned argc, Value* vp) {
3567 CallArgs args = CallArgsFromVp(argc, vp);
3569 Rooted<GCVector<Value>> values(cx, GCVector<Value>(cx));
3571 if (auto* log = cx->runtime()->watchtowerTestingLog.ref().get()) {
3572 Rooted<JSObject*> elem(cx);
3573 for (PlainObject* obj : *log) {
3574 elem = obj;
3575 if (!cx->compartment()->wrap(cx, &elem)) {
3576 return false;
3578 if (!values.append(ObjectValue(*elem))) {
3579 return false;
3582 log->clearAndFree();
3585 ArrayObject* arr = NewDenseCopiedArray(cx, values.length(), values.begin());
3586 if (!arr) {
3587 return false;
3590 args.rval().setObject(*arr);
3591 return true;
3594 static bool AddWatchtowerTarget(JSContext* cx, unsigned argc, Value* vp) {
3595 CallArgs args = CallArgsFromVp(argc, vp);
3597 if (args.length() != 1 || !args[0].isObject()) {
3598 JS_ReportErrorASCII(cx, "Expected a single object argument.");
3599 return false;
3602 if (!cx->runtime()->watchtowerTestingLog.ref()) {
3603 auto vec = cx->make_unique<JSRuntime::RootedPlainObjVec>(cx);
3604 if (!vec) {
3605 return false;
3607 cx->runtime()->watchtowerTestingLog = std::move(vec);
3610 RootedObject obj(cx, &args[0].toObject());
3611 if (!JSObject::setUseWatchtowerTestingLog(cx, obj)) {
3612 return false;
3615 args.rval().setUndefined();
3616 return true;
3619 struct TestExternalString : public JSExternalStringCallbacks {
3620 void finalize(JS::Latin1Char* chars) const override { js_free(chars); }
3621 void finalize(char16_t* chars) const override { js_free(chars); }
3622 size_t sizeOfBuffer(const JS::Latin1Char* chars,
3623 mozilla::MallocSizeOf mallocSizeOf) const override {
3624 return mallocSizeOf(chars);
3626 size_t sizeOfBuffer(const char16_t* chars,
3627 mozilla::MallocSizeOf mallocSizeOf) const override {
3628 return mallocSizeOf(chars);
3632 static constexpr TestExternalString TestExternalStringCallbacks;
3634 static bool NewString(JSContext* cx, unsigned argc, Value* vp) {
3635 CallArgs args = CallArgsFromVp(argc, vp);
3637 RootedString src(cx, ToString(cx, args.get(0)));
3638 if (!src) {
3639 return false;
3642 gc::Heap heap = gc::Heap::Default;
3643 bool wantTwoByte = false;
3644 bool forceExternal = false;
3645 bool maybeExternal = false;
3646 bool newStringBuffer = false;
3647 bool shareStringBuffer = false;
3648 uint32_t capacity = 0;
3650 if (args.get(1).isObject()) {
3651 RootedObject options(cx, &args[1].toObject());
3652 RootedValue v(cx);
3653 bool requestTenured = false;
3654 struct BoolSetting {
3655 const char* name;
3656 bool* value;
3658 for (auto [name, setting] :
3659 {BoolSetting{"tenured", &requestTenured},
3660 BoolSetting{"twoByte", &wantTwoByte},
3661 BoolSetting{"external", &forceExternal},
3662 BoolSetting{"maybeExternal", &maybeExternal},
3663 BoolSetting{"newStringBuffer", &newStringBuffer},
3664 BoolSetting{"shareStringBuffer", &shareStringBuffer}}) {
3665 if (!JS_GetProperty(cx, options, name, &v)) {
3666 return false;
3668 *setting = ToBoolean(v); // false if not given (or otherwise undefined)
3670 struct Uint32Setting {
3671 const char* name;
3672 uint32_t* value;
3674 for (auto [name, setting] : {Uint32Setting{"capacity", &capacity}}) {
3675 if (!JS_GetProperty(cx, options, name, &v)) {
3676 return false;
3678 int32_t i32;
3679 if (!ToInt32(cx, v, &i32)) {
3680 return false;
3682 if (i32 < 0) {
3683 JS_ReportErrorASCII(cx, "nonnegative value required");
3684 return false;
3686 *setting = static_cast<uint32_t>(i32);
3689 heap = requestTenured ? gc::Heap::Tenured : gc::Heap::Default;
3690 if (forceExternal || maybeExternal) {
3691 wantTwoByte = true;
3693 unsigned kinds = forceExternal + maybeExternal + (capacity != 0) +
3694 newStringBuffer + shareStringBuffer;
3695 if (kinds > 1) {
3696 JS_ReportErrorASCII(cx,
3697 "external, capacity, and stringBuffer options can "
3698 "not be used at the same time");
3699 return false;
3703 auto len = src->length();
3704 RootedString dest(cx);
3706 if (forceExternal || maybeExternal) {
3707 auto buf = cx->make_pod_array<char16_t>(len);
3708 if (!buf) {
3709 return false;
3712 if (!JS_CopyStringChars(cx, mozilla::Range<char16_t>(buf.get(), len),
3713 src)) {
3714 return false;
3717 bool isExternal = true;
3718 if (forceExternal) {
3719 dest = JSExternalString::new_(cx, buf.get(), len,
3720 &TestExternalStringCallbacks);
3721 } else {
3722 dest = NewMaybeExternalString(
3723 cx, buf.get(), len, &TestExternalStringCallbacks, &isExternal, heap);
3725 if (dest && isExternal) {
3726 (void)buf.release(); // Ownership was transferred.
3728 } else if (shareStringBuffer) {
3729 if (!src->isLinear() || !src->asLinear().hasStringBuffer()) {
3730 JS_ReportErrorASCII(cx, "source string must have a string buffer");
3731 return false;
3733 RefPtr<mozilla::StringBuffer> buffer = src->asLinear().stringBuffer();
3734 if (src->hasLatin1Chars()) {
3735 Rooted<JSString::OwnedChars<Latin1Char>> owned(cx, std::move(buffer),
3736 len);
3737 dest =
3738 JSLinearString::newValidLength<CanGC, Latin1Char>(cx, &owned, heap);
3739 } else {
3740 Rooted<JSString::OwnedChars<char16_t>> owned(cx, std::move(buffer), len);
3741 dest = JSLinearString::newValidLength<CanGC, char16_t>(cx, &owned, heap);
3743 } else {
3744 AutoStableStringChars stable(cx);
3745 if (!wantTwoByte && src->hasLatin1Chars()) {
3746 if (!stable.init(cx, src)) {
3747 return false;
3749 } else {
3750 if (!stable.initTwoByte(cx, src)) {
3751 return false;
3754 if (newStringBuffer) {
3755 auto allocString = [&](const auto* chars) -> JSLinearString* {
3756 using CharT =
3757 std::remove_const_t<std::remove_pointer_t<decltype(chars)>>;
3759 if (JSInlineString::lengthFits<CharT>(len)) {
3760 JS_ReportErrorASCII(cx, "Cannot create small non-inline strings");
3761 return nullptr;
3764 RefPtr<mozilla::StringBuffer> buffer =
3765 mozilla::StringBuffer::Create(chars, len);
3766 if (!buffer) {
3767 ReportOutOfMemory(cx);
3768 return nullptr;
3771 Rooted<JSString::OwnedChars<CharT>> owned(cx, std::move(buffer), len);
3772 return JSLinearString::newValidLength<CanGC, CharT>(cx, &owned, heap);
3774 if (stable.isLatin1()) {
3775 dest = allocString(stable.latin1Chars());
3776 } else {
3777 dest = allocString(stable.twoByteChars());
3779 } else if (capacity) {
3780 if (capacity < len) {
3781 capacity = len;
3783 if (len == 0) {
3784 JS_ReportErrorASCII(cx, "Cannot set capacity of empty string");
3785 return false;
3788 auto createLinearString = [&](const auto* chars) -> JSLinearString* {
3789 using CharT =
3790 std::remove_const_t<std::remove_pointer_t<decltype(chars)>>;
3792 if (JSInlineString::lengthFits<CharT>(len)) {
3793 JS_ReportErrorASCII(cx, "Cannot create small non-inline strings");
3794 return nullptr;
3797 auto news =
3798 cx->make_pod_arena_array<CharT>(js::StringBufferArena, capacity);
3799 if (!news) {
3800 return nullptr;
3802 mozilla::PodCopy(news.get(), chars, len);
3803 Rooted<JSString::OwnedChars<CharT>> owned(cx, std::move(news), len);
3804 return JSLinearString::newValidLength<CanGC, CharT>(cx, &owned, heap);
3807 if (stable.isLatin1()) {
3808 dest = createLinearString(stable.latin1Chars());
3809 } else {
3810 dest = createLinearString(stable.twoByteChars());
3812 if (dest) {
3813 dest->asLinear().makeExtensible(capacity);
3815 } else if (wantTwoByte) {
3816 dest = NewStringCopyNDontDeflate<CanGC>(cx, stable.twoByteChars(), len,
3817 heap);
3818 } else if (stable.isLatin1()) {
3819 dest = NewStringCopyN<CanGC>(cx, stable.latin1Chars(), len, heap);
3820 } else {
3821 // Normal behavior: auto-deflate to latin1 if possible.
3822 dest = NewStringCopyN<CanGC>(cx, stable.twoByteChars(), len, heap);
3826 if (!dest) {
3827 return false;
3830 args.rval().setString(dest);
3831 return true;
3834 static bool NewDependentString(JSContext* cx, unsigned argc, Value* vp) {
3835 CallArgs args = CallArgsFromVp(argc, vp);
3837 RootedString src(cx, ToString(cx, args.get(0)));
3838 if (!src) {
3839 return false;
3842 uint64_t indexStart = 0;
3843 mozilla::Maybe<uint64_t> indexEnd;
3844 gc::Heap heap = gc::Heap::Default;
3845 mozilla::Maybe<gc::Heap> requiredHeap;
3847 if (!ToIndex(cx, args.get(1), &indexStart)) {
3848 return false;
3851 Rooted<Value> options(cx);
3852 if (args.get(2).isObject()) {
3853 options = args[2];
3854 } else {
3855 uint64_t idx;
3856 if (args.hasDefined(2)) {
3857 if (!ToIndex(cx, args.get(2), &idx)) {
3858 return false;
3860 indexEnd.emplace(idx);
3862 options = args.get(3);
3865 if (options.isObject()) {
3866 Rooted<Value> v(cx);
3867 Rooted<JSObject*> optObj(cx, &options.toObject());
3868 if (!JS_GetProperty(cx, optObj, "tenured", &v)) {
3869 return false;
3871 if (v.isBoolean()) {
3872 requiredHeap.emplace(v.toBoolean() ? gc::Heap::Tenured
3873 : gc::Heap::Default);
3874 heap = *requiredHeap;
3878 if (indexEnd.isNothing()) {
3879 // Read the length now that no more JS code can run.
3880 indexEnd.emplace(src->length());
3882 if (indexStart > src->length() || *indexEnd > src->length() ||
3883 indexStart >= *indexEnd) {
3884 JS_ReportErrorASCII(cx, "invalid dependent string bounds");
3885 return false;
3887 if (!src->ensureLinear(cx)) {
3888 return false;
3890 Rooted<JSString*> result(
3891 cx, js::NewDependentString(cx, src, indexStart, *indexEnd - indexStart,
3892 heap));
3893 if (!result) {
3894 return false;
3896 if (!result->isDependent()) {
3897 JS_ReportErrorASCII(cx, "resulting string is not dependent (too short?)");
3898 return false;
3901 if (requiredHeap.isSome()) {
3902 if ((*requiredHeap == gc::Heap::Tenured) != result->isTenured()) {
3903 if (result->isTenured()) {
3904 JS_ReportErrorASCII(cx, "nursery string created in tenured heap");
3905 return false;
3906 } else {
3907 JS_ReportErrorASCII(cx, "tenured string created in nursery heap");
3908 return false;
3913 args.rval().setString(result);
3914 return true;
3917 // Warning! This will let you create ropes that I'm not sure would be possible
3918 // otherwise, specifically:
3920 // - a rope with a zero-length child
3921 // - a rope that would fit into an inline string
3923 static bool NewRope(JSContext* cx, unsigned argc, Value* vp) {
3924 CallArgs args = CallArgsFromVp(argc, vp);
3926 if (!args.get(0).isString() || !args.get(1).isString()) {
3927 JS_ReportErrorASCII(cx, "newRope requires two string arguments.");
3928 return false;
3931 gc::Heap heap = js::gc::Heap::Default;
3932 if (args.get(2).isObject()) {
3933 RootedObject options(cx, &args[2].toObject());
3934 RootedValue v(cx);
3935 if (!JS_GetProperty(cx, options, "nursery", &v)) {
3936 return false;
3938 if (!v.isUndefined() && !ToBoolean(v)) {
3939 heap = js::gc::Heap::Tenured;
3943 RootedString left(cx, args[0].toString());
3944 RootedString right(cx, args[1].toString());
3945 size_t length = JS_GetStringLength(left) + JS_GetStringLength(right);
3946 if (length > JSString::MAX_LENGTH) {
3947 JS_ReportErrorASCII(cx, "rope length exceeds maximum string length");
3948 return false;
3951 // Disallow creating ropes where one side is empty.
3952 if (left->empty() || right->empty()) {
3953 JS_ReportErrorASCII(cx, "rope child mustn't be the empty string");
3954 return false;
3957 // Disallow creating ropes which fit into inline strings.
3958 if (left->hasLatin1Chars() && right->hasLatin1Chars()) {
3959 if (JSInlineString::lengthFits<JS::Latin1Char>(length)) {
3960 JS_ReportErrorASCII(cx, "Cannot create small non-inline ropes");
3961 return false;
3963 } else {
3964 if (JSInlineString::lengthFits<char16_t>(length)) {
3965 JS_ReportErrorASCII(cx, "Cannot create small non-inline ropes");
3966 return false;
3970 auto* str = JSRope::new_<CanGC>(cx, left, right, length, heap);
3971 if (!str) {
3972 return false;
3975 args.rval().setString(str);
3976 return true;
3979 static bool IsRope(JSContext* cx, unsigned argc, Value* vp) {
3980 CallArgs args = CallArgsFromVp(argc, vp);
3982 if (!args.get(0).isString()) {
3983 JS_ReportErrorASCII(cx, "isRope requires a string argument.");
3984 return false;
3987 JSString* str = args[0].toString();
3988 args.rval().setBoolean(str->isRope());
3989 return true;
3992 static bool EnsureLinearString(JSContext* cx, unsigned argc, Value* vp) {
3993 CallArgs args = CallArgsFromVp(argc, vp);
3995 if (args.length() != 1 || !args[0].isString()) {
3996 JS_ReportErrorASCII(
3997 cx, "ensureLinearString takes exactly one string argument.");
3998 return false;
4001 JSLinearString* linear = args[0].toString()->ensureLinear(cx);
4002 if (!linear) {
4003 return false;
4006 args.rval().setString(linear);
4007 return true;
4010 static bool RepresentativeStringArray(JSContext* cx, unsigned argc, Value* vp) {
4011 CallArgs args = CallArgsFromVp(argc, vp);
4013 RootedObject array(cx, JS::NewArrayObject(cx, 0));
4014 if (!array) {
4015 return false;
4018 if (!JSString::fillWithRepresentatives(cx, array.as<ArrayObject>())) {
4019 return false;
4022 args.rval().setObject(*array);
4023 return true;
4026 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
4028 static bool OOMThreadTypes(JSContext* cx, unsigned argc, Value* vp) {
4029 CallArgs args = CallArgsFromVp(argc, vp);
4030 args.rval().setInt32(js::THREAD_TYPE_MAX);
4031 return true;
4034 static bool CheckCanSimulateOOM(JSContext* cx) {
4035 if (js::oom::GetThreadType() != js::THREAD_TYPE_MAIN) {
4036 JS_ReportErrorASCII(
4037 cx, "Simulated OOM failure is only supported on the main thread");
4038 return false;
4041 return true;
4044 static bool SetupOOMFailure(JSContext* cx, bool failAlways, unsigned argc,
4045 Value* vp) {
4046 CallArgs args = CallArgsFromVp(argc, vp);
4048 if (disableOOMFunctions) {
4049 args.rval().setUndefined();
4050 return true;
4053 if (args.length() < 1) {
4054 JS_ReportErrorASCII(cx, "Count argument required");
4055 return false;
4058 if (args.length() > 2) {
4059 JS_ReportErrorASCII(cx, "Too many arguments");
4060 return false;
4063 int32_t count;
4064 if (!JS::ToInt32(cx, args.get(0), &count)) {
4065 return false;
4068 if (count <= 0) {
4069 JS_ReportErrorASCII(cx, "OOM cutoff should be positive");
4070 return false;
4073 uint32_t targetThread = js::THREAD_TYPE_MAIN;
4074 if (args.length() > 1 && !ToUint32(cx, args[1], &targetThread)) {
4075 return false;
4078 if (targetThread == js::THREAD_TYPE_NONE ||
4079 targetThread == js::THREAD_TYPE_WORKER ||
4080 targetThread >= js::THREAD_TYPE_MAX) {
4081 JS_ReportErrorASCII(cx, "Invalid thread type specified");
4082 return false;
4085 if (!CheckCanSimulateOOM(cx)) {
4086 return false;
4089 js::oom::simulator.simulateFailureAfter(js::oom::FailureSimulator::Kind::OOM,
4090 count, targetThread, failAlways);
4091 args.rval().setUndefined();
4092 return true;
4095 static bool OOMAfterAllocations(JSContext* cx, unsigned argc, Value* vp) {
4096 return SetupOOMFailure(cx, true, argc, vp);
4099 static bool OOMAtAllocation(JSContext* cx, unsigned argc, Value* vp) {
4100 return SetupOOMFailure(cx, false, argc, vp);
4103 static bool ResetOOMFailure(JSContext* cx, unsigned argc, Value* vp) {
4104 CallArgs args = CallArgsFromVp(argc, vp);
4106 if (!CheckCanSimulateOOM(cx)) {
4107 return false;
4110 args.rval().setBoolean(js::oom::HadSimulatedOOM());
4111 js::oom::simulator.reset();
4112 return true;
4115 static size_t CountCompartments(JSContext* cx) {
4116 size_t count = 0;
4117 for (auto zone : cx->runtime()->gc.zones()) {
4118 count += zone->compartments().length();
4120 return count;
4123 // Iterative failure testing: test a function by simulating failures at indexed
4124 // locations throughout the normal execution path and checking that the
4125 // resulting state of the environment is consistent with the error result.
4127 // For example, trigger OOM at every allocation point and test that the function
4128 // either recovers and succeeds or raises an exception and fails.
4130 class MOZ_STACK_CLASS IterativeFailureTest {
4131 public:
4132 struct FailureSimulator {
4133 virtual void setup(JSContext* cx) {}
4134 virtual void teardown(JSContext* cx) {}
4135 virtual void startSimulating(JSContext* cx, unsigned iteration,
4136 unsigned thread, bool keepFailing) = 0;
4137 virtual bool stopSimulating() = 0;
4138 virtual void cleanup(JSContext* cx) {}
4141 IterativeFailureTest(JSContext* cx, FailureSimulator& simulator);
4142 bool initParams(const CallArgs& args);
4143 bool test();
4145 private:
4146 bool setup();
4147 bool testThread(unsigned thread);
4148 bool testIteration(unsigned thread, unsigned iteration,
4149 bool& failureWasSimulated, MutableHandleValue exception);
4150 void cleanup();
4151 void teardown();
4153 JSContext* const cx;
4154 FailureSimulator& simulator;
4155 size_t compartmentCount;
4157 // Test parameters set by initParams.
4158 RootedFunction testFunction;
4159 unsigned threadStart = 0;
4160 unsigned threadEnd = 0;
4161 bool expectExceptionOnFailure = true;
4162 bool keepFailing = false;
4163 bool verbose = false;
4166 bool RunIterativeFailureTest(
4167 JSContext* cx, const CallArgs& args,
4168 IterativeFailureTest::FailureSimulator& simulator) {
4169 IterativeFailureTest test(cx, simulator);
4170 return test.initParams(args) && test.test();
4173 IterativeFailureTest::IterativeFailureTest(JSContext* cx,
4174 FailureSimulator& simulator)
4175 : cx(cx), simulator(simulator), testFunction(cx) {}
4177 bool IterativeFailureTest::test() {
4178 if (disableOOMFunctions) {
4179 return true;
4182 if (!setup()) {
4183 return false;
4186 auto onExit = mozilla::MakeScopeExit([this] { teardown(); });
4188 for (unsigned thread = threadStart; thread <= threadEnd; thread++) {
4189 if (!testThread(thread)) {
4190 return false;
4194 return true;
4197 bool IterativeFailureTest::setup() {
4198 if (!CheckCanSimulateOOM(cx)) {
4199 return false;
4202 // Disallow nested tests.
4203 if (cx->runningOOMTest) {
4204 JS_ReportErrorASCII(
4205 cx, "Nested call to iterative failure test is not allowed.");
4206 return false;
4208 cx->runningOOMTest = true;
4210 MOZ_ASSERT(!cx->isExceptionPending());
4212 # ifdef JS_GC_ZEAL
4213 JS::SetGCZeal(cx, 0, JS::ShellDefaultGCZealFrequency);
4214 # endif
4216 // Delazify the function here if necessary so we don't end up testing that.
4217 if (testFunction->isInterpreted() &&
4218 !JSFunction::getOrCreateScript(cx, testFunction)) {
4219 return false;
4222 compartmentCount = CountCompartments(cx);
4224 simulator.setup(cx);
4226 return true;
4229 bool IterativeFailureTest::testThread(unsigned thread) {
4230 if (verbose) {
4231 fprintf(stderr, "thread %u\n", thread);
4234 RootedValue exception(cx);
4236 unsigned iteration = 1;
4237 bool failureWasSimulated;
4238 do {
4239 if (!testIteration(thread, iteration, failureWasSimulated, &exception)) {
4240 return false;
4243 iteration++;
4244 } while (failureWasSimulated);
4246 if (verbose) {
4247 fprintf(stderr, " finished after %u iterations\n", iteration - 1);
4248 if (!exception.isUndefined()) {
4249 RootedString str(cx, JS::ToString(cx, exception));
4250 if (!str) {
4251 fprintf(stderr, " error while trying to print exception, giving up\n");
4252 return false;
4254 UniqueChars bytes(JS_EncodeStringToUTF8(cx, str));
4255 if (!bytes) {
4256 return false;
4258 fprintf(stderr, " threw %s\n", bytes.get());
4262 return true;
4265 bool IterativeFailureTest::testIteration(unsigned thread, unsigned iteration,
4266 bool& failureWasSimulated,
4267 MutableHandleValue exception) {
4268 if (verbose) {
4269 fprintf(stderr, " iteration %u\n", iteration);
4272 MOZ_RELEASE_ASSERT(!cx->isExceptionPending());
4274 simulator.startSimulating(cx, iteration, thread, keepFailing);
4276 RootedValue result(cx);
4277 bool ok = JS_CallFunction(cx, cx->global(), testFunction,
4278 HandleValueArray::empty(), &result);
4280 failureWasSimulated = simulator.stopSimulating();
4282 if (ok && cx->isExceptionPending()) {
4283 MOZ_CRASH(
4284 "Thunk execution succeeded but an exception was raised - missing error "
4285 "check?");
4288 if (!ok && !cx->isExceptionPending() && expectExceptionOnFailure) {
4289 MOZ_CRASH(
4290 "Thunk execution failed but no exception was raised - missing call to "
4291 "js::ReportOutOfMemory()?");
4294 // Note that it is possible that the function throws an exception unconnected
4295 // to the simulated failure, in which case we ignore it. More correct would be
4296 // to have the caller pass some kind of exception specification and to check
4297 // the exception against it.
4298 if (!failureWasSimulated && cx->isExceptionPending()) {
4299 if (!cx->getPendingException(exception)) {
4300 return false;
4303 cx->clearPendingException();
4305 cleanup();
4307 return true;
4310 void IterativeFailureTest::cleanup() {
4311 simulator.cleanup(cx);
4313 gc::FinishGC(cx);
4315 // Some tests create a new compartment or zone on every iteration. Our GC is
4316 // triggered by GC allocations and not by number of compartments or zones, so
4317 // these won't normally get cleaned up. The check here stops some tests
4318 // running out of memory. ("Gentlemen, you can't fight in here! This is the
4319 // War oom!")
4320 if (CountCompartments(cx) > compartmentCount + 100) {
4321 JS_GC(cx);
4322 compartmentCount = CountCompartments(cx);
4326 void IterativeFailureTest::teardown() {
4327 simulator.teardown(cx);
4329 cx->runningOOMTest = false;
4332 bool IterativeFailureTest::initParams(const CallArgs& args) {
4333 if (args.length() < 1 || args.length() > 2) {
4334 JS_ReportErrorASCII(cx, "function takes between 1 and 2 arguments.");
4335 return false;
4338 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
4339 JS_ReportErrorASCII(cx, "The first argument must be the function to test.");
4340 return false;
4342 testFunction = &args[0].toObject().as<JSFunction>();
4344 if (args.length() == 2) {
4345 if (args[1].isBoolean()) {
4346 expectExceptionOnFailure = args[1].toBoolean();
4347 } else if (args[1].isObject()) {
4348 RootedObject options(cx, &args[1].toObject());
4349 RootedValue value(cx);
4351 if (!JS_GetProperty(cx, options, "expectExceptionOnFailure", &value)) {
4352 return false;
4354 if (!value.isUndefined()) {
4355 expectExceptionOnFailure = ToBoolean(value);
4358 if (!JS_GetProperty(cx, options, "keepFailing", &value)) {
4359 return false;
4361 if (!value.isUndefined()) {
4362 keepFailing = ToBoolean(value);
4364 } else {
4365 JS_ReportErrorASCII(
4366 cx, "The optional second argument must be an object or a boolean.");
4367 return false;
4371 // There are some places where we do fail without raising an exception, so
4372 // we can't expose this to the fuzzers by default.
4373 if (fuzzingSafe) {
4374 expectExceptionOnFailure = false;
4377 // Test all threads by default except worker threads.
4378 threadStart = oom::FirstThreadTypeToTest;
4379 threadEnd = oom::LastThreadTypeToTest;
4381 // Test a single thread type if specified by the OOM_THREAD environment
4382 // variable.
4383 int threadOption = 0;
4384 if (EnvVarAsInt("OOM_THREAD", &threadOption)) {
4385 if (threadOption < oom::FirstThreadTypeToTest ||
4386 threadOption > oom::LastThreadTypeToTest) {
4387 JS_ReportErrorASCII(cx, "OOM_THREAD value out of range.");
4388 return false;
4391 threadStart = threadOption;
4392 threadEnd = threadOption;
4395 verbose = EnvVarIsDefined("OOM_VERBOSE");
4397 return true;
4400 struct OOMSimulator : public IterativeFailureTest::FailureSimulator {
4401 void setup(JSContext* cx) override { cx->runtime()->hadOutOfMemory = false; }
4403 void startSimulating(JSContext* cx, unsigned i, unsigned thread,
4404 bool keepFailing) override {
4405 MOZ_ASSERT(!cx->runtime()->hadOutOfMemory);
4406 js::oom::simulator.simulateFailureAfter(
4407 js::oom::FailureSimulator::Kind::OOM, i, thread, keepFailing);
4410 bool stopSimulating() override {
4411 bool handledOOM = js::oom::HadSimulatedOOM();
4412 js::oom::simulator.reset();
4413 return handledOOM;
4416 void cleanup(JSContext* cx) override {
4417 cx->runtime()->hadOutOfMemory = false;
4421 static bool OOMTest(JSContext* cx, unsigned argc, Value* vp) {
4422 CallArgs args = CallArgsFromVp(argc, vp);
4424 OOMSimulator simulator;
4425 if (!RunIterativeFailureTest(cx, args, simulator)) {
4426 return false;
4429 args.rval().setUndefined();
4430 return true;
4433 struct StackOOMSimulator : public IterativeFailureTest::FailureSimulator {
4434 void startSimulating(JSContext* cx, unsigned i, unsigned thread,
4435 bool keepFailing) override {
4436 js::oom::simulator.simulateFailureAfter(
4437 js::oom::FailureSimulator::Kind::StackOOM, i, thread, keepFailing);
4440 bool stopSimulating() override {
4441 bool handledOOM = js::oom::HadSimulatedStackOOM();
4442 js::oom::simulator.reset();
4443 return handledOOM;
4447 static bool StackTest(JSContext* cx, unsigned argc, Value* vp) {
4448 CallArgs args = CallArgsFromVp(argc, vp);
4450 StackOOMSimulator simulator;
4451 if (!RunIterativeFailureTest(cx, args, simulator)) {
4452 return false;
4455 args.rval().setUndefined();
4456 return true;
4459 struct FailingIterruptSimulator
4460 : public IterativeFailureTest::FailureSimulator {
4461 JSInterruptCallback* prevEnd = nullptr;
4463 static bool failingInterruptCallback(JSContext* cx) { return false; }
4465 void setup(JSContext* cx) override {
4466 prevEnd = cx->interruptCallbacks().end();
4467 JS_AddInterruptCallback(cx, failingInterruptCallback);
4470 void teardown(JSContext* cx) override {
4471 cx->interruptCallbacks().erase(prevEnd, cx->interruptCallbacks().end());
4474 void startSimulating(JSContext* cx, unsigned i, unsigned thread,
4475 bool keepFailing) override {
4476 js::oom::simulator.simulateFailureAfter(
4477 js::oom::FailureSimulator::Kind::Interrupt, i, thread, keepFailing);
4480 bool stopSimulating() override {
4481 bool handledInterrupt = js::oom::HadSimulatedInterrupt();
4482 js::oom::simulator.reset();
4483 return handledInterrupt;
4487 static bool InterruptTest(JSContext* cx, unsigned argc, Value* vp) {
4488 CallArgs args = CallArgsFromVp(argc, vp);
4490 FailingIterruptSimulator simulator;
4491 if (!RunIterativeFailureTest(cx, args, simulator)) {
4492 return false;
4495 args.rval().setUndefined();
4496 return true;
4499 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
4501 static bool SettlePromiseNow(JSContext* cx, unsigned argc, Value* vp) {
4502 CallArgs args = CallArgsFromVp(argc, vp);
4503 if (!args.requireAtLeast(cx, "settlePromiseNow", 1)) {
4504 return false;
4506 if (!args[0].isObject() || !args[0].toObject().is<PromiseObject>()) {
4507 JS_ReportErrorASCII(cx, "first argument must be a Promise object");
4508 return false;
4511 Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
4512 if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
4513 JS_ReportErrorASCII(
4514 cx, "async function/generator's promise shouldn't be manually settled");
4515 return false;
4518 if (promise->state() != JS::PromiseState::Pending) {
4519 JS_ReportErrorASCII(cx, "cannot settle an already-resolved promise");
4520 return false;
4523 if (IsPromiseWithDefaultResolvingFunction(promise)) {
4524 SetAlreadyResolvedPromiseWithDefaultResolvingFunction(promise);
4527 int32_t flags = promise->flags();
4528 promise->setFixedSlot(
4529 PromiseSlot_Flags,
4530 Int32Value(flags | PROMISE_FLAG_RESOLVED | PROMISE_FLAG_FULFILLED));
4531 promise->setFixedSlot(PromiseSlot_ReactionsOrResult, UndefinedValue());
4533 DebugAPI::onPromiseSettled(cx, promise);
4534 return true;
4537 static bool GetWaitForAllPromise(JSContext* cx, unsigned argc, Value* vp) {
4538 CallArgs args = CallArgsFromVp(argc, vp);
4539 if (!args.requireAtLeast(cx, "getWaitForAllPromise", 1)) {
4540 return false;
4542 if (!args[0].isObject() || !args[0].toObject().is<ArrayObject>() ||
4543 args[0].toObject().as<NativeObject>().isIndexed()) {
4544 JS_ReportErrorASCII(
4545 cx, "first argument must be a dense Array of Promise objects");
4546 return false;
4548 Rooted<NativeObject*> list(cx, &args[0].toObject().as<NativeObject>());
4549 RootedObjectVector promises(cx);
4550 uint32_t count = list->getDenseInitializedLength();
4551 if (!promises.resize(count)) {
4552 return false;
4555 for (uint32_t i = 0; i < count; i++) {
4556 RootedValue elem(cx, list->getDenseElement(i));
4557 if (!elem.isObject() || !elem.toObject().is<PromiseObject>()) {
4558 JS_ReportErrorASCII(
4559 cx, "Each entry in the passed-in Array must be a Promise");
4560 return false;
4562 promises[i].set(&elem.toObject());
4565 RootedObject resultPromise(cx, JS::GetWaitForAllPromise(cx, promises));
4566 if (!resultPromise) {
4567 return false;
4570 args.rval().set(ObjectValue(*resultPromise));
4571 return true;
4574 static bool ResolvePromise(JSContext* cx, unsigned argc, Value* vp) {
4575 CallArgs args = CallArgsFromVp(argc, vp);
4576 if (!args.requireAtLeast(cx, "resolvePromise", 2)) {
4577 return false;
4579 if (!args[0].isObject() ||
4580 !UncheckedUnwrap(&args[0].toObject())->is<PromiseObject>()) {
4581 JS_ReportErrorASCII(
4582 cx, "first argument must be a maybe-wrapped Promise object");
4583 return false;
4586 RootedObject promise(cx, &args[0].toObject());
4587 RootedValue resolution(cx, args[1]);
4588 mozilla::Maybe<AutoRealm> ar;
4589 if (IsWrapper(promise)) {
4590 promise = UncheckedUnwrap(promise);
4591 ar.emplace(cx, promise);
4592 if (!cx->compartment()->wrap(cx, &resolution)) {
4593 return false;
4597 if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
4598 JS_ReportErrorASCII(
4600 "async function/generator's promise shouldn't be manually resolved");
4601 return false;
4604 bool result = JS::ResolvePromise(cx, promise, resolution);
4605 if (result) {
4606 args.rval().setUndefined();
4608 return result;
4611 static bool RejectPromise(JSContext* cx, unsigned argc, Value* vp) {
4612 CallArgs args = CallArgsFromVp(argc, vp);
4613 if (!args.requireAtLeast(cx, "rejectPromise", 2)) {
4614 return false;
4616 if (!args[0].isObject() ||
4617 !UncheckedUnwrap(&args[0].toObject())->is<PromiseObject>()) {
4618 JS_ReportErrorASCII(
4619 cx, "first argument must be a maybe-wrapped Promise object");
4620 return false;
4623 RootedObject promise(cx, &args[0].toObject());
4624 RootedValue reason(cx, args[1]);
4625 mozilla::Maybe<AutoRealm> ar;
4626 if (IsWrapper(promise)) {
4627 promise = UncheckedUnwrap(promise);
4628 ar.emplace(cx, promise);
4629 if (!cx->compartment()->wrap(cx, &reason)) {
4630 return false;
4634 if (IsPromiseForAsyncFunctionOrGenerator(promise)) {
4635 JS_ReportErrorASCII(
4637 "async function/generator's promise shouldn't be manually rejected");
4638 return false;
4641 bool result = JS::RejectPromise(cx, promise, reason);
4642 if (result) {
4643 args.rval().setUndefined();
4645 return result;
4648 static unsigned finalizeCount = 0;
4650 static void finalize_counter_finalize(JS::GCContext* gcx, JSObject* obj) {
4651 ++finalizeCount;
4654 static const JSClassOps FinalizeCounterClassOps = {
4655 nullptr, // addProperty
4656 nullptr, // delProperty
4657 nullptr, // enumerate
4658 nullptr, // newEnumerate
4659 nullptr, // resolve
4660 nullptr, // mayResolve
4661 finalize_counter_finalize, // finalize
4662 nullptr, // call
4663 nullptr, // construct
4664 nullptr, // trace
4667 static const JSClass FinalizeCounterClass = {
4668 "FinalizeCounter",
4669 JSCLASS_FOREGROUND_FINALIZE,
4670 &FinalizeCounterClassOps,
4673 static bool MakeFinalizeObserver(JSContext* cx, unsigned argc, Value* vp) {
4674 CallArgs args = CallArgsFromVp(argc, vp);
4676 JSObject* obj =
4677 JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, nullptr);
4678 if (!obj) {
4679 return false;
4682 args.rval().setObject(*obj);
4683 return true;
4686 static bool FinalizeCount(JSContext* cx, unsigned argc, Value* vp) {
4687 CallArgs args = CallArgsFromVp(argc, vp);
4688 args.rval().setInt32(finalizeCount);
4689 return true;
4692 static bool ResetFinalizeCount(JSContext* cx, unsigned argc, Value* vp) {
4693 CallArgs args = CallArgsFromVp(argc, vp);
4694 finalizeCount = 0;
4695 args.rval().setUndefined();
4696 return true;
4699 static bool DumpHeap(JSContext* cx, unsigned argc, Value* vp) {
4700 CallArgs args = CallArgsFromVp(argc, vp);
4702 FILE* dumpFile = stdout;
4703 auto closeFile = mozilla::MakeScopeExit([&dumpFile] {
4704 if (dumpFile && dumpFile != stdout) {
4705 fclose(dumpFile);
4709 if (args.length() > 1) {
4710 RootedObject callee(cx, &args.callee());
4711 ReportUsageErrorASCII(cx, callee, "Too many arguments");
4712 return false;
4715 if (!args.get(0).isUndefined()) {
4716 RootedString str(cx, ToString(cx, args[0]));
4717 if (!str) {
4718 return false;
4720 if (!fuzzingSafe) {
4721 UniqueChars fileNameBytes = JS_EncodeStringToUTF8(cx, str);
4722 if (!fileNameBytes) {
4723 return false;
4725 #ifdef XP_WIN
4726 UniqueWideChars wideFileNameBytes =
4727 JS::EncodeUtf8ToWide(cx, fileNameBytes.get());
4728 if (!wideFileNameBytes) {
4729 return false;
4731 dumpFile = _wfopen(wideFileNameBytes.get(), L"w");
4732 #else
4733 UniqueChars narrowFileNameBytes =
4734 JS::EncodeUtf8ToNarrow(cx, fileNameBytes.get());
4735 if (!narrowFileNameBytes) {
4736 return false;
4738 dumpFile = fopen(narrowFileNameBytes.get(), "w");
4739 #endif
4740 if (!dumpFile) {
4741 JS_ReportErrorUTF8(cx, "can't open %s", fileNameBytes.get());
4742 return false;
4747 js::DumpHeap(cx, dumpFile, js::IgnoreNurseryObjects);
4749 args.rval().setUndefined();
4750 return true;
4753 static bool Terminate(JSContext* cx, unsigned arg, Value* vp) {
4754 // Print a message to stderr in differential testing to help jsfunfuzz
4755 // find uncatchable-exception bugs.
4756 if (js::SupportDifferentialTesting()) {
4757 fprintf(stderr, "terminate called\n");
4760 JS_ClearPendingException(cx);
4761 return false;
4764 static bool ReadGeckoProfilingStack(JSContext* cx, unsigned argc, Value* vp) {
4765 CallArgs args = CallArgsFromVp(argc, vp);
4766 args.rval().setUndefined();
4768 // Return boolean 'false' if profiler is not enabled.
4769 if (!cx->runtime()->geckoProfiler().enabled()) {
4770 args.rval().setBoolean(false);
4771 return true;
4774 // Array holding physical jit stack frames.
4775 RootedObject stack(cx, NewDenseEmptyArray(cx));
4776 if (!stack) {
4777 return false;
4780 // If profiler sampling has been suppressed, return an empty
4781 // stack.
4782 if (!cx->isProfilerSamplingEnabled()) {
4783 args.rval().setObject(*stack);
4784 return true;
4787 struct InlineFrameInfo {
4788 InlineFrameInfo(const char* kind, UniqueChars label)
4789 : kind(kind), label(std::move(label)) {}
4790 const char* kind;
4791 UniqueChars label;
4794 Vector<Vector<InlineFrameInfo, 0, TempAllocPolicy>, 0, TempAllocPolicy>
4795 frameInfo(cx);
4797 JS::ProfilingFrameIterator::RegisterState state;
4798 for (JS::ProfilingFrameIterator i(cx, state); !i.done(); ++i) {
4799 MOZ_ASSERT(i.stackAddress() != nullptr);
4801 if (!frameInfo.emplaceBack(cx)) {
4802 return false;
4805 const size_t MaxInlineFrames = 16;
4806 JS::ProfilingFrameIterator::Frame frames[MaxInlineFrames];
4807 uint32_t nframes = i.extractStack(frames, 0, MaxInlineFrames);
4808 MOZ_ASSERT(nframes <= MaxInlineFrames);
4809 for (uint32_t i = 0; i < nframes; i++) {
4810 const char* frameKindStr = nullptr;
4811 switch (frames[i].kind) {
4812 case JS::ProfilingFrameIterator::Frame_BaselineInterpreter:
4813 frameKindStr = "baseline-interpreter";
4814 break;
4815 case JS::ProfilingFrameIterator::Frame_Baseline:
4816 frameKindStr = "baseline-jit";
4817 break;
4818 case JS::ProfilingFrameIterator::Frame_Ion:
4819 frameKindStr = "ion";
4820 break;
4821 case JS::ProfilingFrameIterator::Frame_WasmBaseline:
4822 case JS::ProfilingFrameIterator::Frame_WasmIon:
4823 case JS::ProfilingFrameIterator::Frame_WasmOther:
4824 frameKindStr = "wasm";
4825 break;
4826 default:
4827 frameKindStr = "unknown";
4830 UniqueChars label =
4831 DuplicateStringToArena(js::StringBufferArena, cx, frames[i].label);
4832 if (!label) {
4833 return false;
4836 if (!frameInfo.back().emplaceBack(frameKindStr, std::move(label))) {
4837 return false;
4842 RootedObject inlineFrameInfo(cx);
4843 RootedString frameKind(cx);
4844 RootedString frameLabel(cx);
4845 RootedId idx(cx);
4847 const unsigned propAttrs = JSPROP_ENUMERATE;
4849 uint32_t physicalFrameNo = 0;
4850 for (auto& frame : frameInfo) {
4851 // Array holding all inline frames in a single physical jit stack frame.
4852 RootedObject inlineStack(cx, NewDenseEmptyArray(cx));
4853 if (!inlineStack) {
4854 return false;
4857 uint32_t inlineFrameNo = 0;
4858 for (auto& inlineFrame : frame) {
4859 // Object holding frame info.
4860 RootedObject inlineFrameInfo(cx, NewPlainObject(cx));
4861 if (!inlineFrameInfo) {
4862 return false;
4865 frameKind = NewStringCopyZ<CanGC>(cx, inlineFrame.kind);
4866 if (!frameKind) {
4867 return false;
4870 if (!JS_DefineProperty(cx, inlineFrameInfo, "kind", frameKind,
4871 propAttrs)) {
4872 return false;
4875 frameLabel = NewLatin1StringZ(cx, std::move(inlineFrame.label));
4876 if (!frameLabel) {
4877 return false;
4880 if (!JS_DefineProperty(cx, inlineFrameInfo, "label", frameLabel,
4881 propAttrs)) {
4882 return false;
4885 idx = PropertyKey::Int(inlineFrameNo);
4886 if (!JS_DefinePropertyById(cx, inlineStack, idx, inlineFrameInfo, 0)) {
4887 return false;
4890 ++inlineFrameNo;
4893 // Push inline array into main array.
4894 idx = PropertyKey::Int(physicalFrameNo);
4895 if (!JS_DefinePropertyById(cx, stack, idx, inlineStack, 0)) {
4896 return false;
4899 ++physicalFrameNo;
4902 args.rval().setObject(*stack);
4903 return true;
4906 static bool ReadGeckoInterpProfilingStack(JSContext* cx, unsigned argc,
4907 Value* vp) {
4908 CallArgs args = CallArgsFromVp(argc, vp);
4909 args.rval().setUndefined();
4911 // Return boolean 'false' if profiler is not enabled.
4912 if (!cx->runtime()->geckoProfiler().enabled()) {
4913 args.rval().setBoolean(false);
4914 return true;
4917 // Array with information about each frame.
4918 Rooted<JSObject*> stack(cx, NewDenseEmptyArray(cx));
4919 if (!stack) {
4920 return false;
4922 uint32_t stackIndex = 0;
4924 ProfilingStack* profStack = cx->geckoProfiler().getProfilingStack();
4925 MOZ_ASSERT(profStack);
4927 for (size_t i = 0; i < profStack->stackSize(); i++) {
4928 const auto& frame = profStack->frames[i];
4929 if (!frame.isJsFrame()) {
4930 continue;
4933 // Skip fake JS frame pushed for js::RunScript by GeckoProfilerEntryMarker.
4934 const char* dynamicStr = frame.dynamicString();
4935 if (!dynamicStr) {
4936 continue;
4939 Rooted<PlainObject*> frameInfo(cx, NewPlainObject(cx));
4940 if (!frameInfo) {
4941 return false;
4944 Rooted<JSString*> dynamicString(
4945 cx, JS_NewStringCopyUTF8Z(
4946 cx, JS::ConstUTF8CharsZ(dynamicStr, strlen(dynamicStr))));
4947 if (!dynamicString) {
4948 return false;
4950 if (!JS_DefineProperty(cx, frameInfo, "dynamicString", dynamicString,
4951 JSPROP_ENUMERATE)) {
4952 return false;
4955 if (!JS_DefineElement(cx, stack, stackIndex, frameInfo, JSPROP_ENUMERATE)) {
4956 return false;
4958 stackIndex++;
4961 args.rval().setObject(*stack);
4962 return true;
4965 static bool EnableOsiPointRegisterChecks(JSContext*, unsigned argc, Value* vp) {
4966 CallArgs args = CallArgsFromVp(argc, vp);
4967 #ifdef CHECK_OSIPOINT_REGISTERS
4968 jit::JitOptions.checkOsiPointRegisters = true;
4969 #endif
4970 args.rval().setUndefined();
4971 return true;
4974 static bool DisplayName(JSContext* cx, unsigned argc, Value* vp) {
4975 CallArgs args = CallArgsFromVp(argc, vp);
4976 if (!args.get(0).isObject() || !args[0].toObject().is<JSFunction>()) {
4977 RootedObject arg(cx, &args.callee());
4978 ReportUsageErrorASCII(cx, arg, "Must have one function argument");
4979 return false;
4982 JSFunction* fun = &args[0].toObject().as<JSFunction>();
4983 JS::Rooted<JSAtom*> str(cx);
4984 if (!fun->getDisplayAtom(cx, &str)) {
4985 return false;
4987 args.rval().setString(str ? str : cx->runtime()->emptyString.ref());
4988 return true;
4991 static bool IsAvxPresent(JSContext* cx, unsigned argc, Value* vp) {
4992 CallArgs args = CallArgsFromVp(argc, vp);
4993 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
4994 int minVersion = 1;
4995 if (argc > 0 && args.get(0).isNumber()) {
4996 minVersion = std::max(1, int(args[0].toNumber()));
4998 switch (minVersion) {
4999 case 1:
5000 args.rval().setBoolean(jit::Assembler::HasAVX());
5001 return true;
5002 case 2:
5003 args.rval().setBoolean(jit::Assembler::HasAVX2());
5004 return true;
5006 #endif
5007 args.rval().setBoolean(false);
5008 return true;
5011 class ShellAllocationMetadataBuilder : public AllocationMetadataBuilder {
5012 public:
5013 ShellAllocationMetadataBuilder() = default;
5015 virtual JSObject* build(JSContext* cx, HandleObject,
5016 AutoEnterOOMUnsafeRegion& oomUnsafe) const override;
5018 static const ShellAllocationMetadataBuilder metadataBuilder;
5021 JSObject* ShellAllocationMetadataBuilder::build(
5022 JSContext* cx, HandleObject, AutoEnterOOMUnsafeRegion& oomUnsafe) const {
5023 RootedObject obj(cx, NewPlainObject(cx));
5024 if (!obj) {
5025 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
5028 RootedObject stack(cx, NewDenseEmptyArray(cx));
5029 if (!stack) {
5030 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
5033 static int createdIndex = 0;
5034 createdIndex++;
5036 if (!JS_DefineProperty(cx, obj, "index", createdIndex, 0)) {
5037 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
5040 if (!JS_DefineProperty(cx, obj, "stack", stack, 0)) {
5041 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
5044 int stackIndex = 0;
5045 RootedId id(cx);
5046 RootedValue callee(cx);
5047 for (NonBuiltinScriptFrameIter iter(cx); !iter.done(); ++iter) {
5048 if (iter.isFunctionFrame() && iter.compartment() == cx->compartment()) {
5049 id = PropertyKey::Int(stackIndex);
5050 RootedObject callee(cx, iter.callee(cx));
5051 if (!JS_DefinePropertyById(cx, stack, id, callee, JSPROP_ENUMERATE)) {
5052 oomUnsafe.crash("ShellAllocationMetadataBuilder::build");
5054 stackIndex++;
5058 return obj;
5061 const ShellAllocationMetadataBuilder
5062 ShellAllocationMetadataBuilder::metadataBuilder;
5064 static bool EnableShellAllocationMetadataBuilder(JSContext* cx, unsigned argc,
5065 Value* vp) {
5066 CallArgs args = CallArgsFromVp(argc, vp);
5068 SetAllocationMetadataBuilder(
5069 cx, &ShellAllocationMetadataBuilder::metadataBuilder);
5071 args.rval().setUndefined();
5072 return true;
5075 static bool GetAllocationMetadata(JSContext* cx, unsigned argc, Value* vp) {
5076 CallArgs args = CallArgsFromVp(argc, vp);
5077 if (args.length() != 1 || !args[0].isObject()) {
5078 JS_ReportErrorASCII(cx, "Argument must be an object");
5079 return false;
5082 args.rval().setObjectOrNull(GetAllocationMetadata(&args[0].toObject()));
5083 return true;
5086 static bool testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp) {
5087 CallArgs args = CallArgsFromVp(argc, vp);
5089 // NOP when not in IonMonkey
5090 args.rval().setUndefined();
5091 return true;
5094 static bool testingFunc_bailAfter(JSContext* cx, unsigned argc, Value* vp) {
5095 CallArgs args = CallArgsFromVp(argc, vp);
5096 if (args.length() != 1 || !args[0].isInt32() || args[0].toInt32() < 0) {
5097 JS_ReportErrorASCII(
5098 cx, "Argument must be a positive number that fits in an int32");
5099 return false;
5102 #ifdef DEBUG
5103 if (auto* jitRuntime = cx->runtime()->jitRuntime()) {
5104 uint32_t bailAfter = args[0].toInt32();
5105 bool enableBailAfter = bailAfter > 0;
5106 if (jitRuntime->ionBailAfterEnabled() != enableBailAfter) {
5107 // Force JIT code to be recompiled with (or without) instrumentation.
5108 ReleaseAllJITCode(cx->gcContext());
5109 jitRuntime->setIonBailAfterEnabled(enableBailAfter);
5111 jitRuntime->setIonBailAfterCounter(bailAfter);
5113 #endif
5115 args.rval().setUndefined();
5116 return true;
5119 static bool testingFunc_invalidate(JSContext* cx, unsigned argc, Value* vp) {
5120 CallArgs args = CallArgsFromVp(argc, vp);
5122 // If the topmost frame is Ion/Warp, find the IonScript and invalidate it.
5123 FrameIter iter(cx);
5124 if (!iter.done() && iter.isIon()) {
5125 while (!iter.isPhysicalJitFrame()) {
5126 ++iter;
5128 if (iter.script()->hasIonScript()) {
5129 js::jit::Invalidate(cx, iter.script());
5133 args.rval().setUndefined();
5134 return true;
5137 static constexpr unsigned JitWarmupResetLimit = 20;
5138 static_assert(JitWarmupResetLimit <=
5139 unsigned(JSScript::MutableFlags::WarmupResets_MASK),
5140 "JitWarmupResetLimit exceeds max value");
5142 static bool testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp) {
5143 CallArgs args = CallArgsFromVp(argc, vp);
5145 if (!jit::IsBaselineJitEnabled(cx)) {
5146 return ReturnStringCopy(cx, args, "Baseline is disabled.");
5149 // Use frame iterator to inspect caller.
5150 FrameIter iter(cx);
5152 // We may be invoked directly, not in a JS context, e.g. if inJit is added as
5153 // a callback on the event queue.
5154 if (iter.done()) {
5155 args.rval().setBoolean(false);
5156 return true;
5159 if (iter.hasScript()) {
5160 // Detect repeated attempts to compile, resetting the counter if inJit
5161 // succeeds. Note: This script may have be inlined into its caller.
5162 if (iter.isJSJit()) {
5163 iter.script()->resetWarmUpResetCounter();
5164 } else if (iter.script()->getWarmUpResetCount() >= JitWarmupResetLimit) {
5165 return ReturnStringCopy(
5166 cx, args, "Compilation is being repeatedly prevented. Giving up.");
5170 // Returns true for any JIT (including WASM).
5171 MOZ_ASSERT_IF(iter.isJSJit(), cx->currentlyRunningInJit());
5172 args.rval().setBoolean(cx->currentlyRunningInJit());
5173 return true;
5176 static bool testingFunc_inIon(JSContext* cx, unsigned argc, Value* vp) {
5177 CallArgs args = CallArgsFromVp(argc, vp);
5179 if (!jit::IsIonEnabled(cx)) {
5180 return ReturnStringCopy(cx, args, "Ion is disabled.");
5183 // Use frame iterator to inspect caller.
5184 FrameIter iter(cx);
5186 // We may be invoked directly, not in a JS context, e.g. if inIon is added as
5187 // a callback on the event queue.
5188 if (iter.done()) {
5189 args.rval().setBoolean(false);
5190 return true;
5193 if (iter.hasScript()) {
5194 // Detect repeated attempts to compile, resetting the counter if inIon
5195 // succeeds. Note: This script may have be inlined into its caller.
5196 if (iter.isIon()) {
5197 iter.script()->resetWarmUpResetCounter();
5198 } else if (!iter.script()->canIonCompile()) {
5199 return ReturnStringCopy(cx, args, "Unable to Ion-compile this script.");
5200 } else if (iter.script()->getWarmUpResetCount() >= JitWarmupResetLimit) {
5201 return ReturnStringCopy(
5202 cx, args, "Compilation is being repeatedly prevented. Giving up.");
5206 args.rval().setBoolean(iter.isIon());
5207 return true;
5210 bool js::testingFunc_assertFloat32(JSContext* cx, unsigned argc, Value* vp) {
5211 CallArgs args = CallArgsFromVp(argc, vp);
5212 if (args.length() != 2) {
5213 JS_ReportErrorASCII(cx, "Expects only 2 arguments");
5214 return false;
5217 // NOP when not in IonMonkey
5218 args.rval().setUndefined();
5219 return true;
5222 static bool TestingFunc_assertJitStackInvariants(JSContext* cx, unsigned argc,
5223 Value* vp) {
5224 CallArgs args = CallArgsFromVp(argc, vp);
5226 jit::AssertJitStackInvariants(cx);
5227 args.rval().setUndefined();
5228 return true;
5231 bool js::testingFunc_assertRecoveredOnBailout(JSContext* cx, unsigned argc,
5232 Value* vp) {
5233 CallArgs args = CallArgsFromVp(argc, vp);
5234 if (args.length() != 2) {
5235 JS_ReportErrorASCII(cx, "Expects only 2 arguments");
5236 return false;
5239 // NOP when not in IonMonkey
5240 args.rval().setUndefined();
5241 return true;
5244 static bool GetJitCompilerOptions(JSContext* cx, unsigned argc, Value* vp) {
5245 CallArgs args = CallArgsFromVp(argc, vp);
5246 RootedObject info(cx, JS_NewPlainObject(cx));
5247 if (!info) {
5248 return false;
5251 uint32_t intValue = 0;
5252 RootedValue value(cx);
5254 #define JIT_COMPILER_MATCH(key, string) \
5255 opt = JSJITCOMPILER_##key; \
5256 if (JS_GetGlobalJitCompilerOption(cx, opt, &intValue)) { \
5257 value.setInt32(intValue); \
5258 if (!JS_SetProperty(cx, info, string, value)) return false; \
5261 JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION;
5262 JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH);
5263 #undef JIT_COMPILER_MATCH
5265 args.rval().setObject(*info);
5266 return true;
5269 static bool SetIonCheckGraphCoherency(JSContext* cx, unsigned argc, Value* vp) {
5270 CallArgs args = CallArgsFromVp(argc, vp);
5271 jit::JitOptions.checkGraphConsistency = ToBoolean(args.get(0));
5272 args.rval().setUndefined();
5273 return true;
5276 // A JSObject that holds structured clone data, similar to the C++ class
5277 // JSAutoStructuredCloneBuffer.
5278 class CloneBufferObject : public NativeObject {
5279 static const JSPropertySpec props_[3];
5281 static const size_t DATA_SLOT = 0;
5282 static const size_t SYNTHETIC_SLOT = 1;
5283 static const size_t NUM_SLOTS = 2;
5285 public:
5286 static const JSClass class_;
5288 static CloneBufferObject* Create(JSContext* cx) {
5289 RootedObject obj(cx, JS_NewObject(cx, &class_));
5290 if (!obj) {
5291 return nullptr;
5293 obj->as<CloneBufferObject>().setReservedSlot(DATA_SLOT,
5294 PrivateValue(nullptr));
5295 obj->as<CloneBufferObject>().setReservedSlot(SYNTHETIC_SLOT,
5296 BooleanValue(false));
5298 if (!JS_DefineProperties(cx, obj, props_)) {
5299 return nullptr;
5302 return &obj->as<CloneBufferObject>();
5305 static CloneBufferObject* Create(JSContext* cx,
5306 JSAutoStructuredCloneBuffer* buffer) {
5307 Rooted<CloneBufferObject*> obj(cx, Create(cx));
5308 if (!obj) {
5309 return nullptr;
5311 auto data = js::MakeUnique<JSStructuredCloneData>(buffer->scope());
5312 if (!data) {
5313 ReportOutOfMemory(cx);
5314 return nullptr;
5316 buffer->giveTo(data.get());
5317 obj->setData(data.release(), false);
5318 return obj;
5321 JSStructuredCloneData* data() const {
5322 return static_cast<JSStructuredCloneData*>(
5323 getReservedSlot(DATA_SLOT).toPrivate());
5326 bool isSynthetic() const {
5327 return getReservedSlot(SYNTHETIC_SLOT).toBoolean();
5330 void setData(JSStructuredCloneData* aData, bool synthetic) {
5331 MOZ_ASSERT(!data());
5332 setReservedSlot(DATA_SLOT, PrivateValue(aData));
5333 setReservedSlot(SYNTHETIC_SLOT, BooleanValue(synthetic));
5336 // Discard an owned clone buffer.
5337 void discard() {
5338 js_delete(data());
5339 setReservedSlot(DATA_SLOT, PrivateValue(nullptr));
5342 static bool setCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
5343 Rooted<CloneBufferObject*> obj(
5344 cx, &args.thisv().toObject().as<CloneBufferObject>());
5346 const char* data = nullptr;
5347 UniqueChars dataOwner;
5348 size_t nbytes;
5350 if (args.get(0).isObject() && args[0].toObject().is<ArrayBufferObject>()) {
5351 ArrayBufferObject* buffer = &args[0].toObject().as<ArrayBufferObject>();
5352 bool isSharedMemory;
5353 uint8_t* dataBytes = nullptr;
5354 JS::GetArrayBufferLengthAndData(buffer, &nbytes, &isSharedMemory,
5355 &dataBytes);
5356 MOZ_ASSERT(!isSharedMemory);
5357 data = reinterpret_cast<char*>(dataBytes);
5358 } else {
5359 JSString* str = JS::ToString(cx, args.get(0));
5360 if (!str) {
5361 return false;
5363 dataOwner = JS_EncodeStringToLatin1(cx, str);
5364 if (!dataOwner) {
5365 return false;
5367 data = dataOwner.get();
5368 nbytes = JS_GetStringLength(str);
5371 if (nbytes == 0 || (nbytes % sizeof(uint64_t) != 0)) {
5372 JS_ReportErrorASCII(cx, "Invalid length for clonebuffer data");
5373 return false;
5376 auto buf = js::MakeUnique<JSStructuredCloneData>(
5377 JS::StructuredCloneScope::DifferentProcess);
5378 if (!buf || !buf->Init(nbytes)) {
5379 ReportOutOfMemory(cx);
5380 return false;
5383 MOZ_ALWAYS_TRUE(buf->AppendBytes(data, nbytes));
5384 obj->discard();
5385 obj->setData(buf.release(), true);
5387 args.rval().setUndefined();
5388 return true;
5391 static bool is(HandleValue v) {
5392 return v.isObject() && v.toObject().is<CloneBufferObject>();
5395 static bool setCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
5396 CallArgs args = CallArgsFromVp(argc, vp);
5397 return CallNonGenericMethod<is, setCloneBuffer_impl>(cx, args);
5400 static bool getData(JSContext* cx, Handle<CloneBufferObject*> obj,
5401 JSStructuredCloneData** data) {
5402 if (!obj->data()) {
5403 *data = nullptr;
5404 return true;
5407 bool hasTransferable;
5408 if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable)) {
5409 return false;
5412 if (hasTransferable) {
5413 JS_ReportErrorASCII(
5414 cx, "cannot retrieve structured clone buffer with transferables");
5415 return false;
5418 *data = obj->data();
5419 return true;
5422 static bool getCloneBuffer_impl(JSContext* cx, const CallArgs& args) {
5423 Rooted<CloneBufferObject*> obj(
5424 cx, &args.thisv().toObject().as<CloneBufferObject>());
5425 MOZ_ASSERT(args.length() == 0);
5427 JSStructuredCloneData* data;
5428 if (!getData(cx, obj, &data)) {
5429 return false;
5432 if (data == nullptr) {
5433 args.rval().setUndefined();
5434 return true;
5437 size_t size = data->Size();
5438 UniqueChars buffer(js_pod_malloc<char>(size));
5439 if (!buffer) {
5440 ReportOutOfMemory(cx);
5441 return false;
5443 auto iter = data->Start();
5444 if (!data->ReadBytes(iter, buffer.get(), size)) {
5445 ReportOutOfMemory(cx);
5446 return false;
5448 JSString* str = JS_NewStringCopyN(cx, buffer.get(), size);
5449 if (!str) {
5450 return false;
5452 args.rval().setString(str);
5453 return true;
5456 static bool getCloneBuffer(JSContext* cx, unsigned int argc, JS::Value* vp) {
5457 CallArgs args = CallArgsFromVp(argc, vp);
5458 return CallNonGenericMethod<is, getCloneBuffer_impl>(cx, args);
5461 static bool getCloneBufferAsArrayBuffer_impl(JSContext* cx,
5462 const CallArgs& args) {
5463 Rooted<CloneBufferObject*> obj(
5464 cx, &args.thisv().toObject().as<CloneBufferObject>());
5465 MOZ_ASSERT(args.length() == 0);
5467 JSStructuredCloneData* data;
5468 if (!getData(cx, obj, &data)) {
5469 return false;
5472 if (data == nullptr) {
5473 args.rval().setUndefined();
5474 return true;
5477 size_t size = data->Size();
5478 UniqueChars buffer(js_pod_malloc<char>(size));
5479 if (!buffer) {
5480 ReportOutOfMemory(cx);
5481 return false;
5483 auto iter = data->Start();
5484 if (!data->ReadBytes(iter, buffer.get(), size)) {
5485 ReportOutOfMemory(cx);
5486 return false;
5489 JSObject* arrayBuffer =
5490 JS::NewArrayBufferWithContents(cx, size, std::move(buffer));
5491 if (!arrayBuffer) {
5492 return false;
5495 args.rval().setObject(*arrayBuffer);
5496 return true;
5499 static bool getCloneBufferAsArrayBuffer(JSContext* cx, unsigned int argc,
5500 JS::Value* vp) {
5501 CallArgs args = CallArgsFromVp(argc, vp);
5502 return CallNonGenericMethod<is, getCloneBufferAsArrayBuffer_impl>(cx, args);
5505 static void Finalize(JS::GCContext* gcx, JSObject* obj) {
5506 obj->as<CloneBufferObject>().discard();
5510 static const JSClassOps CloneBufferObjectClassOps = {
5511 nullptr, // addProperty
5512 nullptr, // delProperty
5513 nullptr, // enumerate
5514 nullptr, // newEnumerate
5515 nullptr, // resolve
5516 nullptr, // mayResolve
5517 CloneBufferObject::Finalize, // finalize
5518 nullptr, // call
5519 nullptr, // construct
5520 nullptr, // trace
5523 const JSClass CloneBufferObject::class_ = {
5524 "CloneBuffer",
5525 JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS) |
5526 JSCLASS_FOREGROUND_FINALIZE,
5527 &CloneBufferObjectClassOps,
5530 const JSPropertySpec CloneBufferObject::props_[] = {
5531 JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0),
5532 JS_PSGS("arraybuffer", getCloneBufferAsArrayBuffer, setCloneBuffer, 0),
5533 JS_PS_END,
5536 static mozilla::Maybe<JS::StructuredCloneScope> ParseCloneScope(
5537 JSContext* cx, HandleString str) {
5538 mozilla::Maybe<JS::StructuredCloneScope> scope;
5540 JSLinearString* scopeStr = str->ensureLinear(cx);
5541 if (!scopeStr) {
5542 return scope;
5545 if (StringEqualsLiteral(scopeStr, "SameProcess")) {
5546 scope.emplace(JS::StructuredCloneScope::SameProcess);
5547 } else if (StringEqualsLiteral(scopeStr, "DifferentProcess")) {
5548 scope.emplace(JS::StructuredCloneScope::DifferentProcess);
5549 } else if (StringEqualsLiteral(scopeStr, "DifferentProcessForIndexedDB")) {
5550 scope.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB);
5553 return scope;
5556 // A custom object that is serializable and transferable using
5557 // the engine's custom hooks. The callbacks log their activity
5558 // to a JSRuntime-wide log (tagging actions with IDs to distinguish them).
5559 class CustomSerializableObject : public NativeObject {
5560 static const size_t ID_SLOT = 0;
5561 static const size_t DETACHED_SLOT = 1;
5562 static const size_t BEHAVIOR_SLOT = 2;
5563 static const size_t NUM_SLOTS = 3;
5565 static constexpr size_t MAX_LOG_LEN = 100;
5567 // The activity log should be specific to a JSRuntime.
5568 struct ActivityLog {
5569 uint32_t buffer[MAX_LOG_LEN];
5570 size_t length = 0;
5572 static MOZ_THREAD_LOCAL(ActivityLog*) self;
5573 static ActivityLog* getThreadLog() {
5574 if (!self.initialized() || !self.get()) {
5575 self.infallibleInit();
5576 AutoEnterOOMUnsafeRegion oomUnsafe;
5577 self.set(js_new<ActivityLog>());
5578 if (!self.get()) {
5579 oomUnsafe.crash("allocating activity log");
5581 if (!TlsContext.get()->runtime()->atExit(
5582 [](void* vpData) {
5583 auto* log = static_cast<ActivityLog*>(vpData);
5584 js_delete(log);
5586 self.get())) {
5587 oomUnsafe.crash("atExit");
5590 return self.get();
5593 static bool log(int32_t id, char action) {
5594 return getThreadLog()->logImpl(id, action);
5597 bool logImpl(int32_t id, char action) {
5598 if (length + 2 > MAX_LOG_LEN) {
5599 return false;
5601 buffer[length++] = id;
5602 buffer[length++] = uint32_t(action);
5603 return true;
5607 public:
5608 enum class Behavior {
5609 Nothing = 0,
5610 FailDuringReadTransfer = 1,
5611 FailDuringRead = 2
5614 static constexpr JSClass class_ = {
5615 "CustomSerializable",
5616 JSCLASS_HAS_RESERVED_SLOTS(NUM_SLOTS),
5619 static bool is(HandleValue v) {
5620 return v.isObject() && v.toObject().is<CustomSerializableObject>();
5623 static CustomSerializableObject* Create(JSContext* cx, int32_t id,
5624 Behavior behavior) {
5625 Rooted<CustomSerializableObject*> obj(
5626 cx, static_cast<CustomSerializableObject*>(JS_NewObject(cx, &class_)));
5627 if (!obj) {
5628 return nullptr;
5630 obj->setReservedSlot(ID_SLOT, Int32Value(id));
5631 obj->setReservedSlot(DETACHED_SLOT, BooleanValue(false));
5632 obj->setReservedSlot(BEHAVIOR_SLOT,
5633 Int32Value(static_cast<int32_t>(behavior)));
5635 if (!JS_DefineProperty(cx, obj, "log", getLog, clearLog, 0)) {
5636 return nullptr;
5639 return obj;
5642 public:
5643 static uint32_t tag() { return JS_SCTAG_USER_MIN; }
5645 static bool log(int32_t id, char action) {
5646 return ActivityLog::log(id, action);
5648 bool log(char action) {
5649 return log(getReservedSlot(ID_SLOT).toInt32(), action);
5652 void detach() { setReservedSlot(DETACHED_SLOT, BooleanValue(true)); }
5653 bool isDetached() { return getReservedSlot(DETACHED_SLOT).toBoolean(); }
5655 uint32_t id() const { return getReservedSlot(ID_SLOT).toInt32(); }
5656 Behavior behavior() {
5657 return static_cast<Behavior>(getReservedSlot(BEHAVIOR_SLOT).toInt32());
5660 static bool getLog(JSContext* cx, unsigned int argc, JS::Value* vp) {
5661 CallArgs args = CallArgsFromVp(argc, vp);
5662 return CallNonGenericMethod<is, getLog_impl>(cx, args);
5665 static bool getLog_impl(JSContext* cx, const CallArgs& args) {
5666 Rooted<CustomSerializableObject*> obj(
5667 cx, &args.thisv().toObject().as<CustomSerializableObject>());
5669 size_t len = ActivityLog::getThreadLog()->length;
5670 uint32_t* logBuffer = ActivityLog::getThreadLog()->buffer;
5672 Rooted<ArrayObject*> result(cx, NewDenseFullyAllocatedArray(cx, len));
5673 if (!result) {
5674 return false;
5676 result->ensureDenseInitializedLength(0, len);
5678 for (size_t p = 0; p < len; p += 2) {
5679 int32_t id = int32_t(logBuffer[p]);
5680 char action = char(logBuffer[p + 1]);
5681 result->setDenseElement(p, Int32Value(id));
5682 JSString* str = JS_NewStringCopyN(cx, &action, 1);
5683 if (!str) {
5684 return false;
5686 result->setDenseElement(p + 1, StringValue(str));
5689 args.rval().setObject(*result);
5690 return true;
5693 static bool clearLog(JSContext* cx, unsigned int argc, JS::Value* vp) {
5694 CallArgs args = CallArgsFromVp(argc, vp);
5695 if (!args.get(0).isNullOrUndefined()) {
5696 JS_ReportErrorASCII(cx, "log may only be assigned null/undefined");
5697 return false;
5699 ActivityLog::getThreadLog()->length = 0;
5700 args.rval().setUndefined();
5701 return true;
5704 static bool Write(JSContext* cx, JSStructuredCloneWriter* w,
5705 JS::HandleObject aObj, bool* sameProcessScopeRequired,
5706 void* closure) {
5707 Rooted<CustomSerializableObject*> obj(cx);
5709 if ((obj = aObj->maybeUnwrapIf<CustomSerializableObject>())) {
5710 obj->log('w');
5711 // Write a regular clone as a <tag, id> pair, followed by <0, behavior>.
5712 // Note that transferring will communicate the behavior via a different
5713 // mechanism.
5714 return JS_WriteUint32Pair(w, obj->tag(), obj->id()) &&
5715 JS_WriteUint32Pair(w, 0, static_cast<uint32_t>(obj->behavior()));
5718 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5719 JSMSG_SC_UNSUPPORTED_TYPE);
5720 return false;
5723 static JSObject* Read(JSContext* cx, JSStructuredCloneReader* r,
5724 const JS::CloneDataPolicy& cloneDataPolicy,
5725 uint32_t tag, uint32_t id, void* closure) {
5726 uint32_t dummy, behaviorData;
5727 if (!JS_ReadUint32Pair(r, &dummy, &behaviorData)) {
5728 return nullptr;
5730 if (dummy != 0 || id > INT32_MAX) {
5731 JS_ReportErrorASCII(cx, "out of range");
5732 return nullptr;
5735 auto b = static_cast<Behavior>(behaviorData);
5736 Rooted<CustomSerializableObject*> obj(
5737 cx, Create(cx, static_cast<int32_t>(id), b));
5738 if (!obj) {
5739 return nullptr;
5742 obj->log('r');
5743 if (obj->behavior() == Behavior::FailDuringRead) {
5744 JS_ReportErrorASCII(cx,
5745 "Failed as requested in read during deserialization");
5746 return nullptr;
5748 return obj;
5751 static bool CanTransfer(JSContext* cx, JS::Handle<JSObject*> wrapped,
5752 bool* sameProcessScopeRequired, void* closure) {
5753 Rooted<CustomSerializableObject*> obj(cx);
5755 if ((obj = wrapped->maybeUnwrapIf<CustomSerializableObject>())) {
5756 obj->log('?');
5757 // For now, all CustomSerializable objects are considered to be
5758 // transferable.
5759 return true;
5762 return false;
5765 static bool WriteTransfer(JSContext* cx, JS::Handle<JSObject*> aObj,
5766 void* closure, uint32_t* tag,
5767 JS::TransferableOwnership* ownership,
5768 void** content, uint64_t* extraData) {
5769 Rooted<CustomSerializableObject*> obj(cx);
5771 if ((obj = aObj->maybeUnwrapIf<CustomSerializableObject>())) {
5772 if (obj->isDetached()) {
5773 JS_ReportErrorASCII(cx, "Attempted to transfer detached object");
5774 return false;
5776 obj->log('W');
5777 *content = reinterpret_cast<void*>(obj->id());
5778 *extraData = static_cast<uint64_t>(obj->behavior());
5779 *tag = obj->tag();
5780 *ownership = JS::SCTAG_TMO_CUSTOM;
5781 obj->detach();
5782 return true;
5785 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5786 JSMSG_SC_NOT_TRANSFERABLE);
5787 return false;
5790 static bool ReadTransfer(JSContext* cx, JSStructuredCloneReader* r,
5791 const JS::CloneDataPolicy& cloneDataPolicy,
5792 uint32_t tag, void* content, uint64_t extraData,
5793 void* closure,
5794 JS::MutableHandleObject returnObject) {
5795 if (tag == CustomSerializableObject::tag()) {
5796 int32_t id = int32_t(reinterpret_cast<uintptr_t>(content));
5797 Rooted<CustomSerializableObject*> obj(
5798 cx, CustomSerializableObject::Create(
5799 cx, id, static_cast<Behavior>(extraData)));
5800 if (!obj) {
5801 return false;
5803 obj->log('R');
5804 if (obj->behavior() == Behavior::FailDuringReadTransfer) {
5805 return false;
5807 returnObject.set(obj);
5808 return true;
5811 return false;
5814 static void FreeTransfer(uint32_t tag, JS::TransferableOwnership ownership,
5815 void* content, uint64_t extraData, void* closure) {
5816 CustomSerializableObject::log(uint32_t(reinterpret_cast<intptr_t>(content)),
5817 'F');
5821 MOZ_THREAD_LOCAL(CustomSerializableObject::ActivityLog*)
5822 CustomSerializableObject::ActivityLog::self;
5824 static bool MakeSerializable(JSContext* cx, unsigned argc, Value* vp) {
5825 CallArgs args = CallArgsFromVp(argc, vp);
5827 int32_t id = 0;
5828 if (args.get(0).isInt32()) {
5829 id = args[0].toInt32();
5830 if (id < 0) {
5831 JS_ReportErrorASCII(cx, "id out of range");
5832 return false;
5835 CustomSerializableObject::Behavior behavior =
5836 CustomSerializableObject::Behavior::Nothing;
5837 if (args.get(1).isInt32()) {
5838 int32_t iv = args[1].toInt32();
5839 constexpr int32_t min =
5840 static_cast<int32_t>(CustomSerializableObject::Behavior::Nothing);
5841 constexpr int32_t max = static_cast<int32_t>(
5842 CustomSerializableObject::Behavior::FailDuringRead);
5843 if (iv < min || iv > max) {
5844 JS_ReportErrorASCII(cx, "behavior out of range");
5845 return false;
5847 behavior = static_cast<CustomSerializableObject::Behavior>(iv);
5850 JSObject* obj = CustomSerializableObject::Create(cx, id, behavior);
5851 if (!obj) {
5852 return false;
5855 args.rval().setObject(*obj);
5856 return true;
5859 static JSStructuredCloneCallbacks gCloneCallbacks = {
5860 .read = CustomSerializableObject::Read,
5861 .write = CustomSerializableObject::Write,
5862 .reportError = nullptr,
5863 .readTransfer = CustomSerializableObject::ReadTransfer,
5864 .writeTransfer = CustomSerializableObject::WriteTransfer,
5865 .freeTransfer = CustomSerializableObject::FreeTransfer,
5866 .canTransfer = CustomSerializableObject::CanTransfer,
5867 .sabCloned = nullptr};
5869 bool js::testingFunc_serialize(JSContext* cx, unsigned argc, Value* vp) {
5870 CallArgs args = CallArgsFromVp(argc, vp);
5872 if (js::SupportDifferentialTesting()) {
5873 RootedObject callee(cx, &args.callee());
5874 ReportUsageErrorASCII(cx, callee,
5875 "Function unavailable in differential testing mode.");
5876 return false;
5879 mozilla::Maybe<JSAutoStructuredCloneBuffer> clonebuf;
5880 JS::CloneDataPolicy policy;
5882 if (!args.get(2).isUndefined()) {
5883 RootedObject opts(cx, ToObject(cx, args.get(2)));
5884 if (!opts) {
5885 return false;
5888 RootedValue v(cx);
5889 if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v)) {
5890 return false;
5893 if (!v.isUndefined()) {
5894 JSString* str = JS::ToString(cx, v);
5895 if (!str) {
5896 return false;
5898 JSLinearString* poli = str->ensureLinear(cx);
5899 if (!poli) {
5900 return false;
5903 if (StringEqualsLiteral(poli, "allow")) {
5904 policy.allowSharedMemoryObjects();
5905 policy.allowIntraClusterClonableSharedObjects();
5906 } else if (StringEqualsLiteral(poli, "deny")) {
5907 // default
5908 } else {
5909 JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'");
5910 return false;
5914 if (!JS_GetProperty(cx, opts, "scope", &v)) {
5915 return false;
5918 if (!v.isUndefined()) {
5919 RootedString str(cx, JS::ToString(cx, v));
5920 if (!str) {
5921 return false;
5923 auto scope = ParseCloneScope(cx, str);
5924 if (!scope) {
5925 JS_ReportErrorASCII(cx, "Invalid structured clone scope");
5926 return false;
5928 clonebuf.emplace(*scope, &gCloneCallbacks, nullptr);
5932 if (!clonebuf) {
5933 clonebuf.emplace(JS::StructuredCloneScope::SameProcess, &gCloneCallbacks,
5934 nullptr);
5937 if (!clonebuf->write(cx, args.get(0), args.get(1), policy)) {
5938 return false;
5941 RootedObject obj(cx, CloneBufferObject::Create(cx, clonebuf.ptr()));
5942 if (!obj) {
5943 return false;
5946 args.rval().setObject(*obj);
5947 return true;
5950 static bool Deserialize(JSContext* cx, unsigned argc, Value* vp) {
5951 CallArgs args = CallArgsFromVp(argc, vp);
5953 if (js::SupportDifferentialTesting()) {
5954 RootedObject callee(cx, &args.callee());
5955 ReportUsageErrorASCII(cx, callee,
5956 "Function unavailable in differential testing mode.");
5957 return false;
5960 if (!args.get(0).isObject() || !args[0].toObject().is<CloneBufferObject>()) {
5961 JS_ReportErrorASCII(cx, "deserialize requires a clonebuffer argument");
5962 return false;
5964 Rooted<CloneBufferObject*> obj(cx,
5965 &args[0].toObject().as<CloneBufferObject>());
5967 JS::CloneDataPolicy policy;
5969 JS::StructuredCloneScope scope =
5970 obj->isSynthetic() ? JS::StructuredCloneScope::DifferentProcess
5971 : JS::StructuredCloneScope::SameProcess;
5972 if (args.get(1).isObject()) {
5973 RootedObject opts(cx, &args[1].toObject());
5974 if (!opts) {
5975 return false;
5978 RootedValue v(cx);
5979 if (!JS_GetProperty(cx, opts, "SharedArrayBuffer", &v)) {
5980 return false;
5983 if (!v.isUndefined()) {
5984 JSString* str = JS::ToString(cx, v);
5985 if (!str) {
5986 return false;
5988 JSLinearString* poli = str->ensureLinear(cx);
5989 if (!poli) {
5990 return false;
5993 if (StringEqualsLiteral(poli, "allow")) {
5994 policy.allowSharedMemoryObjects();
5995 policy.allowIntraClusterClonableSharedObjects();
5996 } else if (StringEqualsLiteral(poli, "deny")) {
5997 // default
5998 } else {
5999 JS_ReportErrorASCII(cx, "Invalid policy value for 'SharedArrayBuffer'");
6000 return false;
6004 if (!JS_GetProperty(cx, opts, "scope", &v)) {
6005 return false;
6008 if (!v.isUndefined()) {
6009 RootedString str(cx, JS::ToString(cx, v));
6010 if (!str) {
6011 return false;
6013 auto maybeScope = ParseCloneScope(cx, str);
6014 if (!maybeScope) {
6015 JS_ReportErrorASCII(cx, "Invalid structured clone scope");
6016 return false;
6019 if (*maybeScope < scope) {
6020 JS_ReportErrorASCII(cx,
6021 "Cannot use less restrictive scope "
6022 "than the deserialized clone buffer's scope");
6023 return false;
6026 scope = *maybeScope;
6030 // Clone buffer was already consumed?
6031 if (!obj->data()) {
6032 JS_ReportErrorASCII(cx,
6033 "deserialize given invalid clone buffer "
6034 "(transferables already consumed?)");
6035 return false;
6038 bool hasTransferable;
6039 if (!JS_StructuredCloneHasTransferables(*obj->data(), &hasTransferable)) {
6040 return false;
6043 RootedValue deserialized(cx);
6044 if (!JS_ReadStructuredClone(cx, *obj->data(), JS_STRUCTURED_CLONE_VERSION,
6045 scope, &deserialized, policy, &gCloneCallbacks,
6046 nullptr)) {
6047 return false;
6049 args.rval().set(deserialized);
6051 // Consume any clone buffer with transferables; throw an error if it is
6052 // deserialized again.
6053 if (hasTransferable) {
6054 obj->discard();
6057 return true;
6060 static bool DetachArrayBuffer(JSContext* cx, unsigned argc, Value* vp) {
6061 CallArgs args = CallArgsFromVp(argc, vp);
6063 if (args.length() != 1) {
6064 JS_ReportErrorASCII(cx, "detachArrayBuffer() requires a single argument");
6065 return false;
6068 if (!args[0].isObject()) {
6069 JS_ReportErrorASCII(cx, "detachArrayBuffer must be passed an object");
6070 return false;
6073 RootedObject obj(cx, &args[0].toObject());
6074 if (!JS::DetachArrayBuffer(cx, obj)) {
6075 return false;
6078 args.rval().setUndefined();
6079 return true;
6082 static bool EnsureNonInline(JSContext* cx, unsigned argc, Value* vp) {
6083 CallArgs args = CallArgsFromVp(argc, vp);
6084 Rooted<JSObject*> callee(cx, &args.callee());
6086 if (!args.get(0).isObject()) {
6087 js::ReportUsageErrorASCII(cx, callee, "Single object argument required");
6088 return false;
6091 RootedObject obj(cx, &args[0].toObject());
6092 if (!JS::EnsureNonInlineArrayBufferOrView(cx, obj)) {
6093 return false;
6096 args.rval().setUndefined();
6097 return true;
6100 static bool PinArrayBufferOrViewLength(JSContext* cx, unsigned argc,
6101 Value* vp) {
6102 CallArgs args = CallArgsFromVp(argc, vp);
6103 Rooted<JSObject*> callee(cx, &args.callee());
6105 if (!args.get(0).isObject()) {
6106 js::ReportUsageErrorASCII(
6107 cx, callee, "ArrayBuffer or ArrayBufferView argument required");
6108 return false;
6110 RootedObject obj(cx, &args[0].toObject());
6111 if (!obj->canUnwrapAs<ArrayBufferViewObject>() &&
6112 !obj->canUnwrapAs<ArrayBufferObjectMaybeShared>()) {
6113 js::ReportUsageErrorASCII(
6114 cx, callee, "ArrayBuffer or ArrayBufferView argument required");
6115 return false;
6118 bool pin = args.get(1).isUndefined() ? true : ToBoolean(args.get(1));
6120 args.rval().setBoolean(JS::PinArrayBufferOrViewLength(obj, pin));
6121 return true;
6124 static bool JSONStringify(JSContext* cx, unsigned argc, Value* vp) {
6125 CallArgs args = CallArgsFromVp(argc, vp);
6127 RootedValue value(cx, args.get(0));
6128 RootedValue behaviorVal(cx, args.get(1));
6129 StringifyBehavior behavior = StringifyBehavior::Normal;
6130 if (behaviorVal.isString()) {
6131 bool matches;
6132 #define MATCH(name) \
6133 if (!JS_StringEqualsLiteral(cx, behaviorVal.toString(), #name, &matches)) { \
6134 return false; \
6136 if (matches) { \
6137 behavior = StringifyBehavior::name; \
6139 MATCH(Normal)
6140 MATCH(FastOnly)
6141 MATCH(SlowOnly)
6142 MATCH(Compare)
6143 #undef MATCH
6146 JSStringBuilder sb(cx);
6147 if (!Stringify(cx, &value, nullptr, UndefinedValue(), sb, behavior)) {
6148 return false;
6151 if (!sb.empty()) {
6152 JSString* str = sb.finishString();
6153 if (!str) {
6154 return false;
6156 args.rval().setString(str);
6157 } else {
6158 args.rval().setUndefined();
6161 return true;
6164 static bool HelperThreadCount(JSContext* cx, unsigned argc, Value* vp) {
6165 CallArgs args = CallArgsFromVp(argc, vp);
6167 if (js::SupportDifferentialTesting()) {
6168 // Always return 0 to get consistent output with and without --no-threads.
6169 args.rval().setInt32(0);
6170 return true;
6173 if (CanUseExtraThreads()) {
6174 args.rval().setInt32(GetHelperThreadCount());
6175 } else {
6176 args.rval().setInt32(0);
6178 return true;
6181 static bool EnableShapeConsistencyChecks(JSContext* cx, unsigned argc,
6182 Value* vp) {
6183 CallArgs args = CallArgsFromVp(argc, vp);
6184 #ifdef DEBUG
6185 NativeObject::enableShapeConsistencyChecks();
6186 #endif
6187 args.rval().setUndefined();
6188 return true;
6191 // ShapeSnapshot holds information about an object's properties. This is used
6192 // for checking object and shape changes between two points in time.
6193 class ShapeSnapshot {
6194 HeapPtr<JSObject*> object_;
6195 HeapPtr<Shape*> shape_;
6196 HeapPtr<BaseShape*> baseShape_;
6197 ObjectFlags objectFlags_;
6199 GCVector<HeapPtr<Value>, 8> slots_;
6201 struct PropertySnapshot {
6202 HeapPtr<PropMap*> propMap;
6203 uint32_t propMapIndex;
6204 HeapPtr<PropertyKey> key;
6205 PropertyInfo prop;
6207 explicit PropertySnapshot(PropMap* map, uint32_t index)
6208 : propMap(map),
6209 propMapIndex(index),
6210 key(map->getKey(index)),
6211 prop(map->getPropertyInfo(index)) {}
6213 void trace(JSTracer* trc) {
6214 TraceEdge(trc, &propMap, "propMap");
6215 TraceEdge(trc, &key, "key");
6218 bool operator==(const PropertySnapshot& other) const {
6219 return propMap == other.propMap && propMapIndex == other.propMapIndex &&
6220 key == other.key && prop == other.prop;
6222 bool operator!=(const PropertySnapshot& other) const {
6223 return !operator==(other);
6226 GCVector<PropertySnapshot, 8> properties_;
6228 public:
6229 explicit ShapeSnapshot(JSContext* cx) : slots_(cx), properties_(cx) {}
6230 void checkSelf(JSContext* cx) const;
6231 void check(JSContext* cx, const ShapeSnapshot& other) const;
6232 bool init(JSObject* obj);
6233 void trace(JSTracer* trc);
6235 JSObject* object() const { return object_; }
6238 // A JSObject that holds a ShapeSnapshot.
6239 class ShapeSnapshotObject : public NativeObject {
6240 static constexpr size_t SnapshotSlot = 0;
6241 static constexpr size_t ReservedSlots = 1;
6243 public:
6244 static const JSClassOps classOps_;
6245 static const JSClass class_;
6247 bool hasSnapshot() const {
6248 // The snapshot may not be present yet if we GC during initialization.
6249 return !getReservedSlot(SnapshotSlot).isUndefined();
6252 ShapeSnapshot& snapshot() const {
6253 void* ptr = getReservedSlot(SnapshotSlot).toPrivate();
6254 MOZ_ASSERT(ptr);
6255 return *static_cast<ShapeSnapshot*>(ptr);
6258 static ShapeSnapshotObject* create(JSContext* cx, HandleObject obj);
6260 static void finalize(JS::GCContext* gcx, JSObject* obj) {
6261 if (obj->as<ShapeSnapshotObject>().hasSnapshot()) {
6262 js_delete(&obj->as<ShapeSnapshotObject>().snapshot());
6265 static void trace(JSTracer* trc, JSObject* obj) {
6266 if (obj->as<ShapeSnapshotObject>().hasSnapshot()) {
6267 obj->as<ShapeSnapshotObject>().snapshot().trace(trc);
6272 /*static */ const JSClassOps ShapeSnapshotObject::classOps_ = {
6273 nullptr, // addProperty
6274 nullptr, // delProperty
6275 nullptr, // enumerate
6276 nullptr, // newEnumerate
6277 nullptr, // resolve
6278 nullptr, // mayResolve
6279 ShapeSnapshotObject::finalize, // finalize
6280 nullptr, // call
6281 nullptr, // construct
6282 ShapeSnapshotObject::trace, // trace
6285 /*static */ const JSClass ShapeSnapshotObject::class_ = {
6286 "ShapeSnapshotObject",
6287 JSCLASS_HAS_RESERVED_SLOTS(ShapeSnapshotObject::ReservedSlots) |
6288 JSCLASS_BACKGROUND_FINALIZE,
6289 &ShapeSnapshotObject::classOps_,
6292 bool ShapeSnapshot::init(JSObject* obj) {
6293 object_ = obj;
6294 shape_ = obj->shape();
6295 baseShape_ = shape_->base();
6296 objectFlags_ = shape_->objectFlags();
6298 if (obj->is<NativeObject>()) {
6299 NativeObject* nobj = &obj->as<NativeObject>();
6301 // Snapshot the slot values.
6302 size_t slotSpan = nobj->slotSpan();
6303 if (!slots_.growBy(slotSpan)) {
6304 return false;
6306 for (size_t i = 0; i < slotSpan; i++) {
6307 slots_[i] = nobj->getSlot(i);
6310 // Snapshot property information.
6311 if (uint32_t len = nobj->shape()->propMapLength(); len > 0) {
6312 PropMap* map = nobj->shape()->propMap();
6313 while (true) {
6314 for (uint32_t i = 0; i < len; i++) {
6315 if (!map->hasKey(i)) {
6316 continue;
6318 if (!properties_.append(PropertySnapshot(map, i))) {
6319 return false;
6322 if (!map->hasPrevious()) {
6323 break;
6325 map = map->asLinked()->previous();
6326 len = PropMap::Capacity;
6331 return true;
6334 void ShapeSnapshot::trace(JSTracer* trc) {
6335 TraceEdge(trc, &object_, "object");
6336 TraceEdge(trc, &shape_, "shape");
6337 TraceEdge(trc, &baseShape_, "baseShape");
6338 slots_.trace(trc);
6339 properties_.trace(trc);
6342 void ShapeSnapshot::checkSelf(JSContext* cx) const {
6343 // Assertions based on a single snapshot.
6345 // Non-dictionary shapes must not be mutated.
6346 if (!shape_->isDictionary()) {
6347 MOZ_RELEASE_ASSERT(shape_->base() == baseShape_);
6348 MOZ_RELEASE_ASSERT(shape_->objectFlags() == objectFlags_);
6351 for (const PropertySnapshot& propSnapshot : properties_) {
6352 PropMap* propMap = propSnapshot.propMap;
6353 uint32_t propMapIndex = propSnapshot.propMapIndex;
6354 PropertyInfo prop = propSnapshot.prop;
6356 // Skip if the map no longer matches the snapshotted data. This can
6357 // only happen for dictionary maps because they can be mutated or compacted
6358 // after a shape change.
6359 if (!propMap->hasKey(propMapIndex) ||
6360 PropertySnapshot(propMap, propMapIndex) != propSnapshot) {
6361 MOZ_RELEASE_ASSERT(propMap->isDictionary());
6362 MOZ_RELEASE_ASSERT(object_->shape() != shape_);
6363 continue;
6366 // Ensure ObjectFlags depending on property information are set if needed.
6367 ObjectFlags expectedFlags = GetObjectFlagsForNewProperty(
6368 shape_->getObjectClass(), shape_->objectFlags(), propSnapshot.key,
6369 prop.flags(), cx);
6370 MOZ_RELEASE_ASSERT(expectedFlags == objectFlags_);
6372 // Accessors must have a PrivateGCThingValue(GetterSetter*) slot value.
6373 if (prop.isAccessorProperty()) {
6374 Value slotVal = slots_[prop.slot()];
6375 MOZ_RELEASE_ASSERT(slotVal.isPrivateGCThing());
6376 MOZ_RELEASE_ASSERT(slotVal.toGCThing()->is<GetterSetter>());
6379 // Data properties must not have a PrivateGCThingValue slot value.
6380 if (prop.isDataProperty()) {
6381 Value slotVal = slots_[prop.slot()];
6382 MOZ_RELEASE_ASSERT(!slotVal.isPrivateGCThing());
6387 void ShapeSnapshot::check(JSContext* cx, const ShapeSnapshot& later) const {
6388 checkSelf(cx);
6389 later.checkSelf(cx);
6391 if (object_ != later.object_) {
6392 // Snapshots are for different objects. Assert dictionary shapes aren't
6393 // shared.
6394 if (object_->is<NativeObject>()) {
6395 NativeObject* nobj = &object_->as<NativeObject>();
6396 if (nobj->inDictionaryMode()) {
6397 MOZ_RELEASE_ASSERT(nobj->shape() != later.shape_);
6400 return;
6403 // We have two snapshots for the same object. Check the shape information
6404 // wasn't changed in invalid ways.
6406 // If the Shape is still the same, the object must have the same BaseShape,
6407 // ObjectFlags and property information.
6408 if (shape_ == later.shape_) {
6409 MOZ_RELEASE_ASSERT(objectFlags_ == later.objectFlags_);
6410 MOZ_RELEASE_ASSERT(baseShape_ == later.baseShape_);
6411 MOZ_RELEASE_ASSERT(slots_.length() == later.slots_.length());
6412 MOZ_RELEASE_ASSERT(properties_.length() == later.properties_.length());
6414 for (size_t i = 0; i < properties_.length(); i++) {
6415 MOZ_RELEASE_ASSERT(properties_[i] == later.properties_[i]);
6416 // Non-configurable accessor properties and non-configurable, non-writable
6417 // data properties shouldn't have had their slot mutated.
6418 PropertyInfo prop = properties_[i].prop;
6419 if (!prop.configurable()) {
6420 if (prop.isAccessorProperty() ||
6421 (prop.isDataProperty() && !prop.writable())) {
6422 size_t slot = prop.slot();
6423 MOZ_RELEASE_ASSERT(slots_[slot] == later.slots_[slot]);
6429 // Object flags should not be lost. The exception is the Indexed flag, it
6430 // can be cleared when densifying elements, so clear that flag first.
6432 ObjectFlags flags = objectFlags_;
6433 ObjectFlags flagsLater = later.objectFlags_;
6434 flags.clearFlag(ObjectFlag::Indexed);
6435 flagsLater.clearFlag(ObjectFlag::Indexed);
6436 MOZ_RELEASE_ASSERT((flags.toRaw() & flagsLater.toRaw()) == flags.toRaw());
6439 // If the HadGetterSetterChange flag wasn't set, all GetterSetter slots must
6440 // be unchanged.
6441 if (!later.objectFlags_.hasFlag(ObjectFlag::HadGetterSetterChange)) {
6442 for (size_t i = 0; i < slots_.length(); i++) {
6443 if (slots_[i].isPrivateGCThing() &&
6444 slots_[i].toGCThing()->is<GetterSetter>()) {
6445 MOZ_RELEASE_ASSERT(i < later.slots_.length());
6446 MOZ_RELEASE_ASSERT(later.slots_[i] == slots_[i]);
6452 // static
6453 ShapeSnapshotObject* ShapeSnapshotObject::create(JSContext* cx,
6454 HandleObject obj) {
6455 Rooted<UniquePtr<ShapeSnapshot>> snapshot(cx,
6456 cx->make_unique<ShapeSnapshot>(cx));
6457 if (!snapshot || !snapshot->init(obj)) {
6458 return nullptr;
6461 auto* snapshotObj = NewObjectWithGivenProto<ShapeSnapshotObject>(cx, nullptr);
6462 if (!snapshotObj) {
6463 return nullptr;
6465 snapshotObj->initReservedSlot(SnapshotSlot, PrivateValue(snapshot.release()));
6466 return snapshotObj;
6469 static bool CreateShapeSnapshot(JSContext* cx, unsigned argc, Value* vp) {
6470 CallArgs args = CallArgsFromVp(argc, vp);
6472 if (!args.get(0).isObject()) {
6473 JS_ReportErrorASCII(cx, "createShapeSnapshot requires an object argument");
6474 return false;
6477 RootedObject obj(cx, &args[0].toObject());
6478 auto* res = ShapeSnapshotObject::create(cx, obj);
6479 if (!res) {
6480 return false;
6483 res->snapshot().check(cx, res->snapshot());
6485 args.rval().setObject(*res);
6486 return true;
6489 static bool CheckShapeSnapshot(JSContext* cx, unsigned argc, Value* vp) {
6490 CallArgs args = CallArgsFromVp(argc, vp);
6492 if (!args.get(0).isObject() ||
6493 !args[0].toObject().is<ShapeSnapshotObject>()) {
6494 JS_ReportErrorASCII(cx, "checkShapeSnapshot requires a snapshot argument");
6495 return false;
6498 // Get the object to use from the snapshot if the second argument is not an
6499 // object.
6500 RootedObject obj(cx);
6501 if (args.get(1).isObject()) {
6502 obj = &args[1].toObject();
6503 } else {
6504 auto& snapshot = args[0].toObject().as<ShapeSnapshotObject>().snapshot();
6505 obj = snapshot.object();
6508 RootedObject otherSnapshot(cx, ShapeSnapshotObject::create(cx, obj));
6509 if (!otherSnapshot) {
6510 return false;
6513 auto& snapshot1 = args[0].toObject().as<ShapeSnapshotObject>().snapshot();
6514 auto& snapshot2 = otherSnapshot->as<ShapeSnapshotObject>().snapshot();
6515 snapshot1.check(cx, snapshot2);
6517 args.rval().setUndefined();
6518 return true;
6521 #if defined(DEBUG) || defined(JS_JITSPEW)
6522 static bool DumpObject(JSContext* cx, unsigned argc, Value* vp) {
6523 CallArgs args = CallArgsFromVp(argc, vp);
6524 RootedObject obj(cx, ToObject(cx, args.get(0)));
6525 if (!obj) {
6526 return false;
6529 DumpObject(obj);
6531 args.rval().setUndefined();
6532 return true;
6535 static bool DumpValue(JSContext* cx, unsigned argc, Value* vp) {
6536 CallArgs args = CallArgsFromVp(argc, vp);
6537 args.get(0).get().dump();
6538 args.rval().setUndefined();
6539 return true;
6542 static bool DumpValueToString(JSContext* cx, unsigned argc, Value* vp) {
6543 CallArgs args = CallArgsFromVp(argc, vp);
6545 JSSprinter out(cx);
6546 if (!out.init()) {
6547 return false;
6549 args.get(0).get().dump(out);
6551 JSString* rep = out.release(cx);
6552 if (!rep) {
6553 return false;
6556 args.rval().setString(rep);
6557 return true;
6559 #endif
6561 static bool SharedMemoryEnabled(JSContext* cx, unsigned argc, Value* vp) {
6562 CallArgs args = CallArgsFromVp(argc, vp);
6563 args.rval().setBoolean(
6564 cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
6565 return true;
6568 static bool SharedArrayRawBufferRefcount(JSContext* cx, unsigned argc,
6569 Value* vp) {
6570 CallArgs args = CallArgsFromVp(argc, vp);
6571 if (args.length() != 1 || !args[0].isObject()) {
6572 JS_ReportErrorASCII(cx, "Expected SharedArrayBuffer object");
6573 return false;
6575 RootedObject obj(cx, &args[0].toObject());
6576 if (!obj->is<SharedArrayBufferObject>()) {
6577 JS_ReportErrorASCII(cx, "Expected SharedArrayBuffer object");
6578 return false;
6580 args.rval().setInt32(
6581 obj->as<SharedArrayBufferObject>().rawBufferObject()->refcount());
6582 return true;
6585 #ifdef NIGHTLY_BUILD
6586 static bool ObjectAddress(JSContext* cx, unsigned argc, Value* vp) {
6587 CallArgs args = CallArgsFromVp(argc, vp);
6589 if (js::SupportDifferentialTesting()) {
6590 RootedObject callee(cx, &args.callee());
6591 ReportUsageErrorASCII(cx, callee,
6592 "Function unavailable in differential testing mode.");
6593 return false;
6596 if (args.length() != 1) {
6597 RootedObject callee(cx, &args.callee());
6598 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
6599 return false;
6601 if (!args[0].isObject()) {
6602 RootedObject callee(cx, &args.callee());
6603 ReportUsageErrorASCII(cx, callee, "Expected object");
6604 return false;
6607 void* ptr = js::UncheckedUnwrap(&args[0].toObject(), true);
6608 char buffer[64];
6609 SprintfLiteral(buffer, "%p", ptr);
6611 return ReturnStringCopy(cx, args, buffer);
6614 static bool SharedAddress(JSContext* cx, unsigned argc, Value* vp) {
6615 CallArgs args = CallArgsFromVp(argc, vp);
6617 if (js::SupportDifferentialTesting()) {
6618 RootedObject callee(cx, &args.callee());
6619 ReportUsageErrorASCII(cx, callee,
6620 "Function unavailable in differential testing mode.");
6621 return false;
6624 if (args.length() != 1) {
6625 RootedObject callee(cx, &args.callee());
6626 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
6627 return false;
6629 if (!args[0].isObject()) {
6630 RootedObject callee(cx, &args.callee());
6631 ReportUsageErrorASCII(cx, callee, "Expected object");
6632 return false;
6635 RootedObject obj(cx, CheckedUnwrapStatic(&args[0].toObject()));
6636 if (!obj) {
6637 ReportAccessDenied(cx);
6638 return false;
6640 if (!obj->is<SharedArrayBufferObject>()) {
6641 JS_ReportErrorASCII(cx, "Argument must be a SharedArrayBuffer");
6642 return false;
6644 char buffer[64];
6645 uint32_t nchar = SprintfLiteral(
6646 buffer, "%p",
6647 obj->as<SharedArrayBufferObject>().dataPointerShared().unwrap(
6648 /*safeish*/));
6650 JSString* str = JS_NewStringCopyN(cx, buffer, nchar);
6651 if (!str) {
6652 return false;
6655 args.rval().setString(str);
6657 return true;
6659 #endif
6661 static bool HasInvalidatedTeleporting(JSContext* cx, unsigned argc, Value* vp) {
6662 CallArgs args = CallArgsFromVp(argc, vp);
6664 if (args.length() != 1 || !args[0].isObject()) {
6665 RootedObject callee(cx, &args.callee());
6666 ReportUsageErrorASCII(cx, callee, "Expected single object argument");
6667 return false;
6670 args.rval().setBoolean(args[0].toObject().hasInvalidatedTeleporting());
6671 return true;
6674 static bool DumpBacktrace(JSContext* cx, unsigned argc, Value* vp) {
6675 CallArgs args = CallArgsFromVp(argc, vp);
6676 DumpBacktrace(cx);
6677 args.rval().setUndefined();
6678 return true;
6681 static bool GetBacktrace(JSContext* cx, unsigned argc, Value* vp) {
6682 CallArgs args = CallArgsFromVp(argc, vp);
6684 bool showArgs = false;
6685 bool showLocals = false;
6686 bool showThisProps = false;
6688 if (args.length() > 1) {
6689 RootedObject callee(cx, &args.callee());
6690 ReportUsageErrorASCII(cx, callee, "Too many arguments");
6691 return false;
6694 if (args.length() == 1) {
6695 RootedObject cfg(cx, ToObject(cx, args[0]));
6696 if (!cfg) {
6697 return false;
6699 RootedValue v(cx);
6701 if (!JS_GetProperty(cx, cfg, "args", &v)) {
6702 return false;
6704 showArgs = ToBoolean(v);
6706 if (!JS_GetProperty(cx, cfg, "locals", &v)) {
6707 return false;
6709 showLocals = ToBoolean(v);
6711 if (!JS_GetProperty(cx, cfg, "thisprops", &v)) {
6712 return false;
6714 showThisProps = ToBoolean(v);
6717 JS::UniqueChars buf =
6718 JS::FormatStackDump(cx, showArgs, showLocals, showThisProps);
6719 if (!buf) {
6720 return false;
6723 size_t len;
6724 UniqueTwoByteChars ucbuf(JS::LossyUTF8CharsToNewTwoByteCharsZ(
6725 cx, JS::UTF8Chars(buf.get(), strlen(buf.get())),
6726 &len, js::MallocArena)
6727 .get());
6728 if (!ucbuf) {
6729 return false;
6731 JSString* str = JS_NewUCStringCopyN(cx, ucbuf.get(), len);
6732 if (!str) {
6733 return false;
6736 args.rval().setString(str);
6737 return true;
6740 static bool ReportOutOfMemory(JSContext* cx, unsigned argc, Value* vp) {
6741 CallArgs args = CallArgsFromVp(argc, vp);
6742 JS_ReportOutOfMemory(cx);
6743 cx->clearPendingException();
6744 args.rval().setUndefined();
6745 return true;
6748 static bool ThrowOutOfMemory(JSContext* cx, unsigned argc, Value* vp) {
6749 JS_ReportOutOfMemory(cx);
6750 return false;
6753 static bool ReportLargeAllocationFailure(JSContext* cx, unsigned argc,
6754 Value* vp) {
6755 CallArgs args = CallArgsFromVp(argc, vp);
6757 size_t bytes = JSRuntime::LARGE_ALLOCATION;
6758 if (args.length() >= 1) {
6759 if (!args[0].isInt32()) {
6760 RootedObject callee(cx, &args.callee());
6761 ReportUsageErrorASCII(cx, callee,
6762 "First argument must be an integer if specified.");
6763 return false;
6765 bytes = args[0].toInt32();
6768 void* buf = cx->runtime()->onOutOfMemoryCanGC(AllocFunction::Malloc,
6769 js::MallocArena, bytes);
6771 js_free(buf);
6772 args.rval().setUndefined();
6773 return true;
6776 namespace heaptools {
6778 using EdgeName = UniqueTwoByteChars;
6780 // An edge to a node from its predecessor in a path through the graph.
6781 class BackEdge {
6782 // The node from which this edge starts.
6783 JS::ubi::Node predecessor_;
6785 // The name of this edge.
6786 EdgeName name_;
6788 public:
6789 BackEdge() : name_(nullptr) {}
6790 // Construct an initialized back edge, taking ownership of |name|.
6791 BackEdge(JS::ubi::Node predecessor, EdgeName name)
6792 : predecessor_(predecessor), name_(std::move(name)) {}
6793 BackEdge(BackEdge&& rhs)
6794 : predecessor_(rhs.predecessor_), name_(std::move(rhs.name_)) {}
6795 BackEdge& operator=(BackEdge&& rhs) {
6796 MOZ_ASSERT(&rhs != this);
6797 this->~BackEdge();
6798 new (this) BackEdge(std::move(rhs));
6799 return *this;
6802 EdgeName forgetName() { return std::move(name_); }
6803 JS::ubi::Node predecessor() const { return predecessor_; }
6805 private:
6806 // No copy constructor or copying assignment.
6807 BackEdge(const BackEdge&) = delete;
6808 BackEdge& operator=(const BackEdge&) = delete;
6811 // A path-finding handler class for use with JS::ubi::BreadthFirst.
6812 struct FindPathHandler {
6813 using NodeData = BackEdge;
6814 using Traversal = JS::ubi::BreadthFirst<FindPathHandler>;
6816 FindPathHandler(JSContext* cx, JS::ubi::Node start, JS::ubi::Node target,
6817 MutableHandle<GCVector<Value>> nodes, Vector<EdgeName>& edges)
6818 : cx(cx),
6819 start(start),
6820 target(target),
6821 foundPath(false),
6822 nodes(nodes),
6823 edges(edges) {}
6825 bool operator()(Traversal& traversal, JS::ubi::Node origin,
6826 const JS::ubi::Edge& edge, BackEdge* backEdge, bool first) {
6827 // We take care of each node the first time we visit it, so there's
6828 // nothing to be done on subsequent visits.
6829 if (!first) {
6830 return true;
6833 // Record how we reached this node. This is the last edge on a
6834 // shortest path to this node.
6835 EdgeName edgeName =
6836 DuplicateStringToArena(js::StringBufferArena, cx, edge.name.get());
6837 if (!edgeName) {
6838 return false;
6840 *backEdge = BackEdge(origin, std::move(edgeName));
6842 // Have we reached our final target node?
6843 if (edge.referent == target) {
6844 // Record the path that got us here, which must be a shortest path.
6845 if (!recordPath(traversal, backEdge)) {
6846 return false;
6848 foundPath = true;
6849 traversal.stop();
6852 return true;
6855 // We've found a path to our target. Walk the backlinks to produce the
6856 // (reversed) path, saving the path in |nodes| and |edges|. |nodes| is
6857 // rooted, so it can hold the path's nodes as we leave the scope of
6858 // the AutoCheckCannotGC. Note that nodes are added to |visited| after we
6859 // return from operator() so we have to pass the target BackEdge* to this
6860 // function.
6861 bool recordPath(Traversal& traversal, BackEdge* targetBackEdge) {
6862 JS::ubi::Node here = target;
6864 do {
6865 BackEdge* backEdge = targetBackEdge;
6866 if (here != target) {
6867 Traversal::NodeMap::Ptr p = traversal.visited.lookup(here);
6868 MOZ_ASSERT(p);
6869 backEdge = &p->value();
6871 JS::ubi::Node predecessor = backEdge->predecessor();
6872 if (!nodes.append(predecessor.exposeToJS()) ||
6873 !edges.append(backEdge->forgetName())) {
6874 return false;
6876 here = predecessor;
6877 } while (here != start);
6879 return true;
6882 JSContext* cx;
6884 // The node we're starting from.
6885 JS::ubi::Node start;
6887 // The node we're looking for.
6888 JS::ubi::Node target;
6890 // True if we found a path to target, false if we didn't.
6891 bool foundPath;
6893 // The nodes and edges of the path --- should we find one. The path is
6894 // stored in reverse order, because that's how it's easiest for us to
6895 // construct it:
6896 // - edges[i] is the name of the edge from nodes[i] to nodes[i-1].
6897 // - edges[0] is the name of the edge from nodes[0] to the target.
6898 // - The last node, nodes[n-1], is the start node.
6899 MutableHandle<GCVector<Value>> nodes;
6900 Vector<EdgeName>& edges;
6903 } // namespace heaptools
6905 static bool FindPath(JSContext* cx, unsigned argc, Value* vp) {
6906 CallArgs args = CallArgsFromVp(argc, vp);
6907 if (!args.requireAtLeast(cx, "findPath", 2)) {
6908 return false;
6911 // We don't ToString non-objects given as 'start' or 'target', because this
6912 // test is all about object identity, and ToString doesn't preserve that.
6913 // Non-GCThing endpoints don't make much sense.
6914 if (!args[0].isObject() && !args[0].isString() && !args[0].isSymbol()) {
6915 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
6916 nullptr, "not an object, string, or symbol");
6917 return false;
6920 if (!args[1].isObject() && !args[1].isString() && !args[1].isSymbol()) {
6921 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
6922 nullptr, "not an object, string, or symbol");
6923 return false;
6926 Rooted<GCVector<Value>> nodes(cx, GCVector<Value>(cx));
6927 Vector<heaptools::EdgeName> edges(cx);
6930 // We can't tolerate the GC moving things around while we're searching
6931 // the heap. Check that nothing we do causes a GC.
6932 JS::AutoCheckCannotGC autoCannotGC;
6934 JS::ubi::Node start(args[0]), target(args[1]);
6936 heaptools::FindPathHandler handler(cx, start, target, &nodes, edges);
6937 heaptools::FindPathHandler::Traversal traversal(cx, handler, autoCannotGC);
6938 if (!traversal.addStart(start)) {
6939 ReportOutOfMemory(cx);
6940 return false;
6943 if (!traversal.traverse()) {
6944 if (!cx->isExceptionPending()) {
6945 ReportOutOfMemory(cx);
6947 return false;
6950 if (!handler.foundPath) {
6951 // We didn't find any paths from the start to the target.
6952 args.rval().setUndefined();
6953 return true;
6957 // |nodes| and |edges| contain the path from |start| to |target|, reversed.
6958 // Construct a JavaScript array describing the path from the start to the
6959 // target. Each element has the form:
6961 // {
6962 // node: <object or string or symbol>,
6963 // edge: <string describing outgoing edge from node>
6964 // }
6966 // or, if the node is some internal thing that isn't a proper JavaScript
6967 // value:
6969 // { node: undefined, edge: <string> }
6970 size_t length = nodes.length();
6971 Rooted<ArrayObject*> result(cx, NewDenseFullyAllocatedArray(cx, length));
6972 if (!result) {
6973 return false;
6975 result->ensureDenseInitializedLength(0, length);
6977 // Walk |nodes| and |edges| in the stored order, and construct the result
6978 // array in start-to-target order.
6979 for (size_t i = 0; i < length; i++) {
6980 // Build an object describing the node and edge.
6981 RootedObject obj(cx, NewPlainObject(cx));
6982 if (!obj) {
6983 return false;
6986 // Only define the "node" property if we're not fuzzing, to prevent the
6987 // fuzzers from messing with internal objects that we don't want to expose
6988 // to arbitrary JS.
6989 if (!fuzzingSafe) {
6990 RootedValue wrapped(cx, nodes[i]);
6991 if (!cx->compartment()->wrap(cx, &wrapped)) {
6992 return false;
6995 if (!JS_DefineProperty(cx, obj, "node", wrapped, JSPROP_ENUMERATE)) {
6996 return false;
7000 heaptools::EdgeName edgeName = std::move(edges[i]);
7002 size_t edgeNameLength = js_strlen(edgeName.get());
7003 RootedString edgeStr(
7004 cx, NewString<CanGC>(cx, std::move(edgeName), edgeNameLength));
7005 if (!edgeStr) {
7006 return false;
7009 if (!JS_DefineProperty(cx, obj, "edge", edgeStr, JSPROP_ENUMERATE)) {
7010 return false;
7013 result->setDenseElement(length - i - 1, ObjectValue(*obj));
7016 args.rval().setObject(*result);
7017 return true;
7020 static bool ShortestPaths(JSContext* cx, unsigned argc, Value* vp) {
7021 CallArgs args = CallArgsFromVp(argc, vp);
7022 if (!args.requireAtLeast(cx, "shortestPaths", 1)) {
7023 return false;
7026 if (!args[0].isObject() || !args[0].toObject().is<ArrayObject>()) {
7027 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
7028 nullptr, "not an array object");
7029 return false;
7032 Rooted<ArrayObject*> objs(cx, &args[0].toObject().as<ArrayObject>());
7034 RootedValue start(cx, NullValue());
7035 int32_t maxNumPaths = 3;
7037 if (!args.get(1).isUndefined()) {
7038 if (!args[1].isObject()) {
7039 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[1],
7040 nullptr, "not an options object");
7041 return false;
7044 RootedObject options(cx, &args[1].toObject());
7045 bool exists;
7046 if (!JS_HasProperty(cx, options, "start", &exists)) {
7047 return false;
7049 if (exists) {
7050 if (!JS_GetProperty(cx, options, "start", &start)) {
7051 return false;
7054 // Non-GCThing endpoints don't make much sense.
7055 if (!start.isGCThing()) {
7056 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, start,
7057 nullptr, "not a GC thing");
7058 return false;
7062 RootedValue v(cx, Int32Value(maxNumPaths));
7063 if (!JS_HasProperty(cx, options, "maxNumPaths", &exists)) {
7064 return false;
7066 if (exists) {
7067 if (!JS_GetProperty(cx, options, "maxNumPaths", &v)) {
7068 return false;
7070 if (!JS::ToInt32(cx, v, &maxNumPaths)) {
7071 return false;
7074 if (maxNumPaths <= 0) {
7075 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, v,
7076 nullptr, "not greater than 0");
7077 return false;
7081 // Ensure we have at least one target.
7082 size_t length = objs->getDenseInitializedLength();
7083 if (length == 0) {
7084 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
7085 nullptr,
7086 "not a dense array object with one or more elements");
7087 return false;
7090 for (size_t i = 0; i < length; i++) {
7091 RootedValue el(cx, objs->getDenseElement(i));
7092 if (!el.isGCThing()) {
7093 JS_ReportErrorASCII(cx, "Each target must be a GC thing");
7094 return false;
7098 // We accumulate the results into a GC-stable form, due to the fact that the
7099 // JS::ubi::ShortestPaths lifetime (when operating on the live heap graph)
7100 // is bounded within an AutoCheckCannotGC.
7101 Rooted<GCVector<GCVector<GCVector<Value>>>> values(
7102 cx, GCVector<GCVector<GCVector<Value>>>(cx));
7103 Vector<Vector<Vector<JS::ubi::EdgeName>>> names(cx);
7106 JS::ubi::Node root;
7108 JS::ubi::RootList rootList(cx, true);
7109 if (start.isNull()) {
7110 auto [ok, nogc] = rootList.init();
7111 (void)nogc; // Old compilers get anxious about nogc being unused.
7112 if (!ok) {
7113 ReportOutOfMemory(cx);
7114 return false;
7116 root = JS::ubi::Node(&rootList);
7117 } else {
7118 root = JS::ubi::Node(start);
7120 JS::AutoCheckCannotGC noGC(cx);
7122 JS::ubi::NodeSet targets;
7124 for (size_t i = 0; i < length; i++) {
7125 RootedValue val(cx, objs->getDenseElement(i));
7126 JS::ubi::Node node(val);
7127 if (!targets.put(node)) {
7128 ReportOutOfMemory(cx);
7129 return false;
7133 auto maybeShortestPaths = JS::ubi::ShortestPaths::Create(
7134 cx, noGC, maxNumPaths, root, std::move(targets));
7135 if (maybeShortestPaths.isNothing()) {
7136 ReportOutOfMemory(cx);
7137 return false;
7139 auto& shortestPaths = *maybeShortestPaths;
7141 for (size_t i = 0; i < length; i++) {
7142 if (!values.append(GCVector<GCVector<Value>>(cx)) ||
7143 !names.append(Vector<Vector<JS::ubi::EdgeName>>(cx))) {
7144 return false;
7147 RootedValue val(cx, objs->getDenseElement(i));
7148 JS::ubi::Node target(val);
7150 bool ok = shortestPaths.forEachPath(target, [&](JS::ubi::Path& path) {
7151 Rooted<GCVector<Value>> pathVals(cx, GCVector<Value>(cx));
7152 Vector<JS::ubi::EdgeName> pathNames(cx);
7154 for (auto& part : path) {
7155 if (!pathVals.append(part->predecessor().exposeToJS()) ||
7156 !pathNames.append(std::move(part->name()))) {
7157 return false;
7161 return values.back().append(std::move(pathVals.get())) &&
7162 names.back().append(std::move(pathNames));
7165 if (!ok) {
7166 return false;
7171 MOZ_ASSERT(values.length() == names.length());
7172 MOZ_ASSERT(values.length() == length);
7174 Rooted<ArrayObject*> results(cx, NewDenseFullyAllocatedArray(cx, length));
7175 if (!results) {
7176 return false;
7178 results->ensureDenseInitializedLength(0, length);
7180 for (size_t i = 0; i < length; i++) {
7181 size_t numPaths = values[i].length();
7182 MOZ_ASSERT(names[i].length() == numPaths);
7184 Rooted<ArrayObject*> pathsArray(cx,
7185 NewDenseFullyAllocatedArray(cx, numPaths));
7186 if (!pathsArray) {
7187 return false;
7189 pathsArray->ensureDenseInitializedLength(0, numPaths);
7191 for (size_t j = 0; j < numPaths; j++) {
7192 size_t pathLength = values[i][j].length();
7193 MOZ_ASSERT(names[i][j].length() == pathLength);
7195 Rooted<ArrayObject*> path(cx,
7196 NewDenseFullyAllocatedArray(cx, pathLength));
7197 if (!path) {
7198 return false;
7200 path->ensureDenseInitializedLength(0, pathLength);
7202 for (size_t k = 0; k < pathLength; k++) {
7203 Rooted<PlainObject*> part(cx, NewPlainObject(cx));
7204 if (!part) {
7205 return false;
7208 RootedValue predecessor(cx, values[i][j][k]);
7209 if (!cx->compartment()->wrap(cx, &predecessor) ||
7210 !JS_DefineProperty(cx, part, "predecessor", predecessor,
7211 JSPROP_ENUMERATE)) {
7212 return false;
7215 if (names[i][j][k]) {
7216 RootedString edge(cx,
7217 NewStringCopyZ<CanGC>(cx, names[i][j][k].get()));
7218 if (!edge ||
7219 !JS_DefineProperty(cx, part, "edge", edge, JSPROP_ENUMERATE)) {
7220 return false;
7224 path->setDenseElement(k, ObjectValue(*part));
7227 pathsArray->setDenseElement(j, ObjectValue(*path));
7230 results->setDenseElement(i, ObjectValue(*pathsArray));
7233 args.rval().setObject(*results);
7234 return true;
7237 static bool EvalReturningScope(JSContext* cx, unsigned argc, Value* vp) {
7238 CallArgs args = CallArgsFromVp(argc, vp);
7239 if (!args.requireAtLeast(cx, "evalReturningScope", 1)) {
7240 return false;
7243 RootedString str(cx, ToString(cx, args[0]));
7244 if (!str) {
7245 return false;
7248 JS::AutoFilename filename;
7249 uint32_t lineno;
7251 JS::DescribeScriptedCaller(cx, &filename, &lineno);
7253 // CompileOption should be created in the target global's realm.
7254 RootedObject global(cx);
7255 Maybe<JS::CompileOptions> maybeOptions;
7256 if (args.hasDefined(1)) {
7257 global = ToObject(cx, args[1]);
7258 if (!global) {
7259 return false;
7262 global = CheckedUnwrapDynamic(global, cx, /* stopAtWindowProxy = */ false);
7263 if (!global) {
7264 JS_ReportErrorASCII(cx, "Permission denied to access global");
7265 return false;
7267 if (!global->is<GlobalObject>()) {
7268 JS_ReportErrorASCII(cx, "Argument must be a global object");
7269 return false;
7272 JSAutoRealm ar(cx, global);
7273 maybeOptions.emplace(cx);
7274 } else {
7275 global = JS::CurrentGlobalOrNull(cx);
7276 maybeOptions.emplace(cx);
7279 CompileOptions& options = maybeOptions.ref();
7280 options.setFileAndLine(filename.get(), lineno);
7281 options.setNoScriptRval(true);
7282 options.setNonSyntacticScope(true);
7284 AutoStableStringChars linearChars(cx);
7285 if (!linearChars.initTwoByte(cx, str)) {
7286 return false;
7288 JS::SourceText<char16_t> srcBuf;
7289 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
7290 return false;
7293 RootedObject varObj(cx);
7296 // ExecuteInFrameScriptEnvironment requires the script be in the same
7297 // realm as the global. The script compilation should be done after
7298 // switching globals.
7299 AutoRealm ar(cx, global);
7301 RootedScript script(cx, JS::Compile(cx, options, srcBuf));
7302 if (!script) {
7303 return false;
7306 JS::RootedObject obj(cx, JS_NewPlainObject(cx));
7307 if (!obj) {
7308 return false;
7311 RootedObject lexicalScope(cx);
7312 if (!js::ExecuteInFrameScriptEnvironment(cx, obj, script, &lexicalScope)) {
7313 return false;
7316 varObj = lexicalScope->enclosingEnvironment()->enclosingEnvironment();
7317 MOZ_ASSERT(varObj->is<NonSyntacticVariablesObject>());
7320 RootedValue varObjVal(cx, ObjectValue(*varObj));
7321 if (!cx->compartment()->wrap(cx, &varObjVal)) {
7322 return false;
7325 args.rval().set(varObjVal);
7326 return true;
7329 static bool ByteSize(JSContext* cx, unsigned argc, Value* vp) {
7330 CallArgs args = CallArgsFromVp(argc, vp);
7331 mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf;
7334 // We can't tolerate the GC moving things around while we're using a
7335 // ubi::Node. Check that nothing we do causes a GC.
7336 JS::AutoCheckCannotGC autoCannotGC;
7338 JS::ubi::Node node = args.get(0);
7339 if (node) {
7340 args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
7341 } else {
7342 args.rval().setUndefined();
7345 return true;
7348 static bool ByteSizeOfScript(JSContext* cx, unsigned argc, Value* vp) {
7349 CallArgs args = CallArgsFromVp(argc, vp);
7350 if (!args.requireAtLeast(cx, "byteSizeOfScript", 1)) {
7351 return false;
7353 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
7354 JS_ReportErrorASCII(cx, "Argument must be a Function object");
7355 return false;
7358 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
7359 if (fun->isNativeFun()) {
7360 JS_ReportErrorASCII(cx, "Argument must be a scripted function");
7361 return false;
7364 RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
7365 if (!script) {
7366 return false;
7369 mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf;
7372 // We can't tolerate the GC moving things around while we're using a
7373 // ubi::Node. Check that nothing we do causes a GC.
7374 JS::AutoCheckCannotGC autoCannotGC;
7376 JS::ubi::Node node = script;
7377 if (node) {
7378 args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
7379 } else {
7380 args.rval().setUndefined();
7383 return true;
7386 static bool SetImmutablePrototype(JSContext* cx, unsigned argc, Value* vp) {
7387 CallArgs args = CallArgsFromVp(argc, vp);
7388 if (!args.get(0).isObject()) {
7389 JS_ReportErrorASCII(cx, "setImmutablePrototype: object expected");
7390 return false;
7393 RootedObject obj(cx, &args[0].toObject());
7395 bool succeeded;
7396 if (!js::SetImmutablePrototype(cx, obj, &succeeded)) {
7397 return false;
7400 args.rval().setBoolean(succeeded);
7401 return true;
7404 #if defined(DEBUG) || defined(JS_JITSPEW)
7405 static bool DumpStringRepresentation(JSContext* cx, unsigned argc, Value* vp) {
7406 CallArgs args = CallArgsFromVp(argc, vp);
7408 RootedString str(cx, ToString(cx, args.get(0)));
7409 if (!str) {
7410 return false;
7413 Fprinter out(stderr);
7414 str->dumpRepresentation(out);
7416 args.rval().setUndefined();
7417 return true;
7420 static bool GetStringRepresentation(JSContext* cx, unsigned argc, Value* vp) {
7421 CallArgs args = CallArgsFromVp(argc, vp);
7423 RootedString str(cx, ToString(cx, args.get(0)));
7424 if (!str) {
7425 return false;
7428 JSSprinter out(cx);
7429 if (!out.init()) {
7430 return false;
7432 str->dumpRepresentation(out);
7434 JSString* rep = out.release(cx);
7435 if (!rep) {
7436 return false;
7439 args.rval().setString(rep);
7440 return true;
7442 #endif
7444 static bool ParseCompileOptionsForModule(JSContext* cx,
7445 JS::CompileOptions& options,
7446 JS::Handle<JSObject*> opts,
7447 bool& isModule) {
7448 JS::Rooted<JS::Value> v(cx);
7450 if (!JS_GetProperty(cx, opts, "module", &v)) {
7451 return false;
7453 if (!v.isUndefined() && JS::ToBoolean(v)) {
7454 options.setModule();
7455 isModule = true;
7457 if (!ValidateModuleCompileOptions(cx, options)) {
7458 return false;
7460 } else {
7461 isModule = false;
7464 return true;
7467 static bool ParseCompileOptionsForInstantiate(JSContext* cx,
7468 JS::CompileOptions& options,
7469 JS::Handle<JSObject*> opts,
7470 bool& prepareForInstantiate) {
7471 JS::Rooted<JS::Value> v(cx);
7473 if (!JS_GetProperty(cx, opts, "prepareForInstantiate", &v)) {
7474 return false;
7476 if (!v.isUndefined()) {
7477 prepareForInstantiate = JS::ToBoolean(v);
7478 } else {
7479 prepareForInstantiate = false;
7482 return true;
7485 static bool CompileToStencil(JSContext* cx, uint32_t argc, Value* vp) {
7486 CallArgs args = CallArgsFromVp(argc, vp);
7488 if (!args.requireAtLeast(cx, "compileToStencil", 1)) {
7489 return false;
7491 if (!args[0].isString()) {
7492 const char* typeName = InformalValueTypeName(args[0]);
7493 JS_ReportErrorASCII(cx, "expected string to parse, got %s", typeName);
7494 return false;
7497 RootedString src(cx, ToString<CanGC>(cx, args[0]));
7498 if (!src) {
7499 return false;
7502 /* Linearize the string to obtain a char16_t* range. */
7503 AutoStableStringChars linearChars(cx);
7504 if (!linearChars.initTwoByte(cx, src)) {
7505 return false;
7507 JS::SourceText<char16_t> srcBuf;
7508 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
7509 return false;
7512 CompileOptions options(cx);
7513 options.setFile("<compileToStencil>");
7515 RootedString displayURL(cx);
7516 RootedString sourceMapURL(cx);
7517 UniqueChars fileNameBytes;
7518 bool isModule = false;
7519 bool prepareForInstantiate = false;
7520 if (args.length() == 2) {
7521 if (!args[1].isObject()) {
7522 JS_ReportErrorASCII(
7523 cx, "compileToStencil: The 2nd argument must be an object");
7524 return false;
7527 RootedObject opts(cx, &args[1].toObject());
7529 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
7530 return false;
7532 if (!ParseCompileOptionsForModule(cx, options, opts, isModule)) {
7533 return false;
7535 if (!ParseCompileOptionsForInstantiate(cx, options, opts,
7536 prepareForInstantiate)) {
7537 return false;
7539 if (!js::ParseSourceOptions(cx, opts, &displayURL, &sourceMapURL)) {
7540 return false;
7544 AutoReportFrontendContext fc(cx);
7545 RefPtr<JS::Stencil> stencil;
7546 if (isModule) {
7547 stencil = JS::CompileModuleScriptToStencil(&fc, options, srcBuf);
7548 } else {
7549 stencil = JS::CompileGlobalScriptToStencil(&fc, options, srcBuf);
7551 if (!stencil) {
7552 return false;
7555 if (!SetSourceOptions(cx, &fc, stencil->source, displayURL, sourceMapURL)) {
7556 return false;
7559 JS::InstantiationStorage storage;
7560 if (prepareForInstantiate) {
7561 if (!JS::PrepareForInstantiate(&fc, *stencil, storage)) {
7562 return false;
7566 Rooted<js::StencilObject*> stencilObj(
7567 cx, js::StencilObject::create(cx, std::move(stencil)));
7568 if (!stencilObj) {
7569 return false;
7572 args.rval().setObject(*stencilObj);
7573 return true;
7576 static bool EvalStencil(JSContext* cx, uint32_t argc, Value* vp) {
7577 CallArgs args = CallArgsFromVp(argc, vp);
7579 if (!args.requireAtLeast(cx, "evalStencil", 1)) {
7580 return false;
7583 /* Prepare the input byte array. */
7584 if (!args[0].isObject()) {
7585 JS_ReportErrorASCII(cx, "evalStencil: Stencil object expected");
7586 return false;
7588 Rooted<js::StencilObject*> stencilObj(
7589 cx, args[0].toObject().maybeUnwrapIf<js::StencilObject>());
7590 if (!stencilObj) {
7591 JS_ReportErrorASCII(cx, "evalStencil: Stencil object expected");
7592 return false;
7595 if (stencilObj->stencil()->isModule()) {
7596 JS_ReportErrorASCII(cx,
7597 "evalStencil: Module stencil cannot be evaluated. Use "
7598 "instantiateModuleStencil instead");
7599 return false;
7602 CompileOptions options(cx);
7603 UniqueChars fileNameBytes;
7604 Rooted<JS::Value> privateValue(cx);
7605 Rooted<JSString*> elementAttributeName(cx);
7606 if (args.length() == 2) {
7607 if (!args[1].isObject()) {
7608 JS_ReportErrorASCII(cx,
7609 "evalStencil: The 2nd argument must be an object");
7610 return false;
7613 RootedObject opts(cx, &args[1].toObject());
7615 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
7616 return false;
7618 if (!js::ParseDebugMetadata(cx, opts, &privateValue,
7619 &elementAttributeName)) {
7620 return false;
7624 bool useDebugMetadata = !privateValue.isUndefined() || elementAttributeName;
7626 JS::InstantiateOptions instantiateOptions(options);
7627 if (useDebugMetadata) {
7628 instantiateOptions.hideScriptFromDebugger = true;
7631 if (!js::ValidateLazinessOfStencilAndGlobal(cx, *stencilObj->stencil())) {
7632 return false;
7635 RootedScript script(cx, JS::InstantiateGlobalStencil(cx, instantiateOptions,
7636 stencilObj->stencil()));
7637 if (!script) {
7638 return false;
7641 if (useDebugMetadata) {
7642 instantiateOptions.hideScriptFromDebugger = false;
7643 if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, privateValue,
7644 elementAttributeName, nullptr, nullptr)) {
7645 return false;
7649 RootedValue retVal(cx);
7650 if (!JS_ExecuteScript(cx, script, &retVal)) {
7651 return false;
7654 args.rval().set(retVal);
7655 return true;
7658 static bool CompileToStencilXDR(JSContext* cx, uint32_t argc, Value* vp) {
7659 CallArgs args = CallArgsFromVp(argc, vp);
7661 if (!args.requireAtLeast(cx, "compileToStencilXDR", 1)) {
7662 return false;
7665 RootedString src(cx, ToString<CanGC>(cx, args[0]));
7666 if (!src) {
7667 return false;
7670 /* Linearize the string to obtain a char16_t* range. */
7671 AutoStableStringChars linearChars(cx);
7672 if (!linearChars.initTwoByte(cx, src)) {
7673 return false;
7675 JS::SourceText<char16_t> srcBuf;
7676 if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
7677 return false;
7680 CompileOptions options(cx);
7681 options.setFile("<compileToStencilXDR>");
7683 RootedString displayURL(cx);
7684 RootedString sourceMapURL(cx);
7685 UniqueChars fileNameBytes;
7686 bool isModule = false;
7687 if (args.length() == 2) {
7688 if (!args[1].isObject()) {
7689 JS_ReportErrorASCII(
7690 cx, "compileToStencilXDR: The 2nd argument must be an object");
7691 return false;
7694 RootedObject opts(cx, &args[1].toObject());
7696 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
7697 return false;
7699 if (!ParseCompileOptionsForModule(cx, options, opts, isModule)) {
7700 return false;
7702 if (!js::ParseSourceOptions(cx, opts, &displayURL, &sourceMapURL)) {
7703 return false;
7707 /* Compile the script text to stencil. */
7708 AutoReportFrontendContext fc(cx);
7709 frontend::NoScopeBindingCache scopeCache;
7710 Rooted<frontend::CompilationInput> input(cx,
7711 frontend::CompilationInput(options));
7712 UniquePtr<frontend::ExtensibleCompilationStencil> stencil;
7713 if (isModule) {
7714 stencil = frontend::ParseModuleToExtensibleStencil(
7715 cx, &fc, cx->tempLifoAlloc(), input.get(), &scopeCache, srcBuf);
7716 } else {
7717 stencil = frontend::CompileGlobalScriptToExtensibleStencil(
7718 cx, &fc, input.get(), &scopeCache, srcBuf, ScopeKind::Global);
7720 if (!stencil) {
7721 return false;
7724 if (!SetSourceOptions(cx, &fc, stencil->source, displayURL, sourceMapURL)) {
7725 return false;
7728 /* Serialize the stencil to XDR. */
7729 JS::TranscodeBuffer xdrBytes;
7731 frontend::BorrowingCompilationStencil borrowingStencil(*stencil);
7732 bool succeeded = false;
7733 if (!borrowingStencil.serializeStencils(cx, input.get(), xdrBytes,
7734 &succeeded)) {
7735 return false;
7737 if (!succeeded) {
7738 fc.clearAutoReport();
7739 JS_ReportErrorASCII(cx, "Encoding failure");
7740 return false;
7744 Rooted<StencilXDRBufferObject*> xdrObj(
7746 StencilXDRBufferObject::create(cx, xdrBytes.begin(), xdrBytes.length()));
7747 if (!xdrObj) {
7748 return false;
7751 args.rval().setObject(*xdrObj);
7752 return true;
7755 static bool EvalStencilXDR(JSContext* cx, uint32_t argc, Value* vp) {
7756 CallArgs args = CallArgsFromVp(argc, vp);
7758 if (!args.requireAtLeast(cx, "evalStencilXDR", 1)) {
7759 return false;
7762 /* Prepare the input byte array. */
7763 if (!args[0].isObject()) {
7764 JS_ReportErrorASCII(cx, "evalStencilXDR: Stencil XDR object expected");
7765 return false;
7767 Rooted<StencilXDRBufferObject*> xdrObj(
7768 cx, args[0].toObject().maybeUnwrapIf<StencilXDRBufferObject>());
7769 if (!xdrObj) {
7770 JS_ReportErrorASCII(cx, "evalStencilXDR: Stencil XDR object expected");
7771 return false;
7773 MOZ_ASSERT(xdrObj->hasBuffer());
7775 CompileOptions options(cx);
7776 UniqueChars fileNameBytes;
7777 Rooted<JS::Value> privateValue(cx);
7778 Rooted<JSString*> elementAttributeName(cx);
7779 if (args.length() == 2) {
7780 if (!args[1].isObject()) {
7781 JS_ReportErrorASCII(cx,
7782 "evalStencilXDR: The 2nd argument must be an object");
7783 return false;
7786 RootedObject opts(cx, &args[1].toObject());
7788 if (!js::ParseCompileOptions(cx, options, opts, &fileNameBytes)) {
7789 return false;
7791 if (!js::ParseDebugMetadata(cx, opts, &privateValue,
7792 &elementAttributeName)) {
7793 return false;
7797 /* Prepare the CompilationStencil for decoding. */
7798 AutoReportFrontendContext fc(cx);
7799 frontend::CompilationStencil stencil(nullptr);
7801 /* Deserialize the stencil from XDR. */
7802 JS::TranscodeRange xdrRange(xdrObj->buffer(), xdrObj->bufferLength());
7803 bool succeeded = false;
7804 if (!stencil.deserializeStencils(&fc, options, xdrRange, &succeeded)) {
7805 return false;
7807 if (!succeeded) {
7808 fc.clearAutoReport();
7809 JS_ReportErrorASCII(cx, "Decoding failure");
7810 return false;
7813 if (stencil.isModule()) {
7814 fc.clearAutoReport();
7815 JS_ReportErrorASCII(cx,
7816 "evalStencilXDR: Module stencil cannot be evaluated. "
7817 "Use instantiateModuleStencilXDR instead");
7818 return false;
7821 if (!js::ValidateLazinessOfStencilAndGlobal(cx, stencil)) {
7822 return false;
7825 bool useDebugMetadata = !privateValue.isUndefined() || elementAttributeName;
7827 JS::InstantiateOptions instantiateOptions(options);
7828 if (useDebugMetadata) {
7829 instantiateOptions.hideScriptFromDebugger = true;
7831 RootedScript script(
7832 cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, &stencil));
7833 if (!script) {
7834 return false;
7837 if (useDebugMetadata) {
7838 instantiateOptions.hideScriptFromDebugger = false;
7839 if (!JS::UpdateDebugMetadata(cx, script, instantiateOptions, privateValue,
7840 elementAttributeName, nullptr, nullptr)) {
7841 return false;
7845 RootedValue retVal(cx);
7846 if (!JS_ExecuteScript(cx, script, &retVal)) {
7847 return false;
7850 args.rval().set(retVal);
7851 return true;
7854 static bool GetExceptionInfo(JSContext* cx, uint32_t argc, Value* vp) {
7855 CallArgs args = CallArgsFromVp(argc, vp);
7857 if (!args.requireAtLeast(cx, "getExceptionInfo", 1)) {
7858 return false;
7861 if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
7862 JS_ReportErrorASCII(cx, "getExceptionInfo: expected function argument");
7863 return false;
7866 RootedValue rval(cx);
7867 if (JS_CallFunctionValue(cx, nullptr, args[0], JS::HandleValueArray::empty(),
7868 &rval)) {
7869 // Function didn't throw.
7870 args.rval().setNull();
7871 return true;
7874 // We currently don't support interrupts or forced returns.
7875 if (!cx->isExceptionPending()) {
7876 JS_ReportErrorASCII(cx, "getExceptionInfo: unsupported exception status");
7877 return false;
7880 RootedValue excVal(cx);
7881 Rooted<SavedFrame*> stack(cx);
7882 if (!GetAndClearExceptionAndStack(cx, &excVal, &stack)) {
7883 return false;
7886 RootedValue stackVal(cx);
7887 if (stack) {
7888 RootedString stackString(cx);
7889 if (!BuildStackString(cx, cx->realm()->principals(), stack, &stackString)) {
7890 return false;
7892 stackVal.setString(stackString);
7893 } else {
7894 stackVal.setNull();
7897 RootedObject obj(cx, NewPlainObject(cx));
7898 if (!obj) {
7899 return false;
7902 if (!JS_DefineProperty(cx, obj, "exception", excVal, JSPROP_ENUMERATE)) {
7903 return false;
7906 if (!JS_DefineProperty(cx, obj, "stack", stackVal, JSPROP_ENUMERATE)) {
7907 return false;
7910 args.rval().setObject(*obj);
7911 return true;
7914 class AllocationMarkerObject : public NativeObject {
7915 public:
7916 static const JSClass class_;
7919 const JSClass AllocationMarkerObject::class_ = {
7920 "AllocationMarker",
7923 static bool AllocationMarker(JSContext* cx, unsigned argc, Value* vp) {
7924 CallArgs args = CallArgsFromVp(argc, vp);
7926 bool allocateInsideNursery = true;
7927 if (args.length() > 0 && args[0].isObject()) {
7928 RootedObject options(cx, &args[0].toObject());
7930 RootedValue nurseryVal(cx);
7931 if (!JS_GetProperty(cx, options, "nursery", &nurseryVal)) {
7932 return false;
7934 allocateInsideNursery = ToBoolean(nurseryVal);
7937 JSObject* obj =
7938 allocateInsideNursery
7939 ? NewObjectWithGivenProto<AllocationMarkerObject>(cx, nullptr)
7940 : NewTenuredObjectWithGivenProto<AllocationMarkerObject>(cx, nullptr);
7941 if (!obj) {
7942 return false;
7945 args.rval().setObject(*obj);
7946 return true;
7949 namespace gcCallback {
7951 struct MajorGC {
7952 int32_t depth;
7953 int32_t phases;
7956 static void majorGC(JSContext* cx, JSGCStatus status, JS::GCReason reason,
7957 void* data) {
7958 auto info = static_cast<MajorGC*>(data);
7959 if (!(info->phases & (1 << status))) {
7960 return;
7963 if (info->depth > 0) {
7964 info->depth--;
7965 JS::PrepareForFullGC(cx);
7966 JS::NonIncrementalGC(cx, JS::GCOptions::Normal, JS::GCReason::API);
7967 info->depth++;
7971 struct MinorGC {
7972 int32_t phases;
7973 bool active;
7976 static void minorGC(JSContext* cx, JSGCStatus status, JS::GCReason reason,
7977 void* data) {
7978 auto info = static_cast<MinorGC*>(data);
7979 if (!(info->phases & (1 << status))) {
7980 return;
7983 if (info->active) {
7984 info->active = false;
7985 if (cx->zone() && !cx->zone()->isAtomsZone()) {
7986 cx->runtime()->gc.evictNursery(JS::GCReason::DEBUG_GC);
7988 info->active = true;
7992 // Process global, should really be runtime-local.
7993 static MajorGC majorGCInfo;
7994 static MinorGC minorGCInfo;
7996 static void enterNullRealm(JSContext* cx, JSGCStatus status,
7997 JS::GCReason reason, void* data) {
7998 JSAutoNullableRealm enterRealm(cx, nullptr);
8001 } /* namespace gcCallback */
8003 static bool SetGCCallback(JSContext* cx, unsigned argc, Value* vp) {
8004 CallArgs args = CallArgsFromVp(argc, vp);
8006 if (args.length() != 1) {
8007 JS_ReportErrorASCII(cx, "Wrong number of arguments");
8008 return false;
8011 RootedObject opts(cx, ToObject(cx, args[0]));
8012 if (!opts) {
8013 return false;
8016 RootedValue v(cx);
8017 if (!JS_GetProperty(cx, opts, "action", &v)) {
8018 return false;
8021 JSString* str = JS::ToString(cx, v);
8022 if (!str) {
8023 return false;
8025 Rooted<JSLinearString*> action(cx, str->ensureLinear(cx));
8026 if (!action) {
8027 return false;
8030 int32_t phases = 0;
8031 if (StringEqualsLiteral(action, "minorGC") ||
8032 StringEqualsLiteral(action, "majorGC")) {
8033 if (!JS_GetProperty(cx, opts, "phases", &v)) {
8034 return false;
8036 if (v.isUndefined()) {
8037 phases = (1 << JSGC_END);
8038 } else {
8039 JSString* str = JS::ToString(cx, v);
8040 if (!str) {
8041 return false;
8043 JSLinearString* phasesStr = str->ensureLinear(cx);
8044 if (!phasesStr) {
8045 return false;
8048 if (StringEqualsLiteral(phasesStr, "begin")) {
8049 phases = (1 << JSGC_BEGIN);
8050 } else if (StringEqualsLiteral(phasesStr, "end")) {
8051 phases = (1 << JSGC_END);
8052 } else if (StringEqualsLiteral(phasesStr, "both")) {
8053 phases = (1 << JSGC_BEGIN) | (1 << JSGC_END);
8054 } else {
8055 JS_ReportErrorASCII(cx, "Invalid callback phase");
8056 return false;
8061 if (StringEqualsLiteral(action, "minorGC")) {
8062 gcCallback::minorGCInfo.phases = phases;
8063 gcCallback::minorGCInfo.active = true;
8064 JS_SetGCCallback(cx, gcCallback::minorGC, &gcCallback::minorGCInfo);
8065 } else if (StringEqualsLiteral(action, "majorGC")) {
8066 if (!JS_GetProperty(cx, opts, "depth", &v)) {
8067 return false;
8069 int32_t depth = 1;
8070 if (!v.isUndefined()) {
8071 if (!ToInt32(cx, v, &depth)) {
8072 return false;
8075 if (depth < 0) {
8076 JS_ReportErrorASCII(cx, "Nesting depth cannot be negative");
8077 return false;
8079 if (depth + gcstats::MAX_PHASE_NESTING >
8080 gcstats::Statistics::MAX_SUSPENDED_PHASES) {
8081 JS_ReportErrorASCII(cx, "Nesting depth too large, would overflow");
8082 return false;
8085 gcCallback::majorGCInfo.phases = phases;
8086 gcCallback::majorGCInfo.depth = depth;
8087 JS_SetGCCallback(cx, gcCallback::majorGC, &gcCallback::majorGCInfo);
8088 } else if (StringEqualsLiteral(action, "enterNullRealm")) {
8089 JS_SetGCCallback(cx, gcCallback::enterNullRealm, nullptr);
8090 } else {
8091 JS_ReportErrorASCII(cx, "Unknown GC callback action");
8092 return false;
8095 args.rval().setUndefined();
8096 return true;
8099 #ifdef DEBUG
8100 static bool EnqueueMark(JSContext* cx, unsigned argc, Value* vp) {
8101 CallArgs args = CallArgsFromVp(argc, vp);
8102 gc::GCRuntime* gc = &cx->runtime()->gc;
8104 if (gc->isIncrementalGCInProgress() &&
8105 gc->hasZealMode(gc::ZealMode::IncrementalMarkingValidator)) {
8106 JS_ReportErrorASCII(
8108 "Can't add to test mark queue during incremental GC if marking "
8109 "validation is enabled as this can cause failures");
8110 return false;
8113 if (args.get(0).isString()) {
8114 RootedString val(cx, args[0].toString());
8115 if (!val->ensureLinear(cx)) {
8116 return false;
8118 if (!gc->appendTestMarkQueue(StringValue(val))) {
8119 JS_ReportOutOfMemory(cx);
8120 return false;
8122 } else if (args.get(0).isObject()) {
8123 if (!gc->appendTestMarkQueue(args[0])) {
8124 JS_ReportOutOfMemory(cx);
8125 return false;
8127 } else {
8128 JS_ReportErrorASCII(cx, "Argument must be a string or object");
8129 return false;
8132 args.rval().setUndefined();
8133 return true;
8136 static bool GetMarkQueue(JSContext* cx, unsigned argc, Value* vp) {
8137 CallArgs args = CallArgsFromVp(argc, vp);
8139 const auto& queue = cx->runtime()->gc.getTestMarkQueue();
8141 RootedObject result(cx, JS::NewArrayObject(cx, queue.length()));
8142 if (!result) {
8143 return false;
8145 for (size_t i = 0; i < queue.length(); i++) {
8146 RootedValue val(cx, queue[i]);
8147 if (!JS_WrapValue(cx, &val)) {
8148 return false;
8150 if (!JS_SetElement(cx, result, i, val)) {
8151 return false;
8155 args.rval().setObject(*result);
8156 return true;
8159 static bool ClearMarkQueue(JSContext* cx, unsigned argc, Value* vp) {
8160 CallArgs args = CallArgsFromVp(argc, vp);
8162 cx->runtime()->gc.clearTestMarkQueue();
8163 args.rval().setUndefined();
8164 return true;
8166 #endif // DEBUG
8168 static bool NurseryStringsEnabled(JSContext* cx, unsigned argc, Value* vp) {
8169 CallArgs args = CallArgsFromVp(argc, vp);
8170 args.rval().setBoolean(cx->zone()->allocNurseryStrings());
8171 return true;
8174 static bool IsNurseryAllocated(JSContext* cx, unsigned argc, Value* vp) {
8175 CallArgs args = CallArgsFromVp(argc, vp);
8176 if (!args.get(0).isGCThing()) {
8177 JS_ReportErrorASCII(
8178 cx, "The function takes one argument, which must be a GC thing");
8179 return false;
8182 args.rval().setBoolean(IsInsideNursery(args[0].toGCThing()));
8183 return true;
8186 static bool NumAllocSitesPretenured(JSContext* cx, unsigned argc, Value* vp) {
8187 CallArgs args = CallArgsFromVp(argc, vp);
8188 args.rval().setInt32(cx->realm()->numAllocSitesPretenured);
8189 return true;
8192 static bool GetLcovInfo(JSContext* cx, unsigned argc, Value* vp) {
8193 CallArgs args = CallArgsFromVp(argc, vp);
8195 if (args.length() > 1) {
8196 JS_ReportErrorASCII(cx, "Wrong number of arguments");
8197 return false;
8200 if (!coverage::IsLCovEnabled()) {
8201 JS_ReportErrorASCII(cx, "Coverage not enabled for process.");
8202 return false;
8205 RootedObject global(cx);
8206 if (args.hasDefined(0)) {
8207 global = ToObject(cx, args[0]);
8208 if (!global) {
8209 JS_ReportErrorASCII(cx, "Permission denied to access global");
8210 return false;
8212 global = CheckedUnwrapDynamic(global, cx, /* stopAtWindowProxy = */ false);
8213 if (!global) {
8214 ReportAccessDenied(cx);
8215 return false;
8217 if (!global->is<GlobalObject>()) {
8218 JS_ReportErrorASCII(cx, "Argument must be a global object");
8219 return false;
8221 } else {
8222 global = JS::CurrentGlobalOrNull(cx);
8225 size_t length = 0;
8226 UniqueChars content;
8228 AutoRealm ar(cx, global);
8229 content = js::GetCodeCoverageSummary(cx, &length);
8232 if (!content) {
8233 return false;
8236 JSString* str =
8237 JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(content.get(), length));
8238 if (!str) {
8239 return false;
8242 args.rval().setString(str);
8243 return true;
8246 #ifdef DEBUG
8247 static bool SetRNGState(JSContext* cx, unsigned argc, Value* vp) {
8248 CallArgs args = CallArgsFromVp(argc, vp);
8249 if (!args.requireAtLeast(cx, "SetRNGState", 2)) {
8250 return false;
8253 double d0;
8254 if (!ToNumber(cx, args[0], &d0)) {
8255 return false;
8258 double d1;
8259 if (!ToNumber(cx, args[1], &d1)) {
8260 return false;
8263 uint64_t seed0 = static_cast<uint64_t>(d0);
8264 uint64_t seed1 = static_cast<uint64_t>(d1);
8266 if (seed0 == 0 && seed1 == 0) {
8267 JS_ReportErrorASCII(cx, "RNG requires non-zero seed");
8268 return false;
8271 cx->realm()->getOrCreateRandomNumberGenerator().setState(seed0, seed1);
8273 args.rval().setUndefined();
8274 return true;
8276 #endif
8278 static bool GetTimeZone(JSContext* cx, unsigned argc, Value* vp) {
8279 CallArgs args = CallArgsFromVp(argc, vp);
8280 RootedObject callee(cx, &args.callee());
8282 if (args.length() != 0) {
8283 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
8284 return false;
8287 #ifndef __wasi__
8288 auto getTimeZone = [](std::time_t* now) -> const char* {
8289 std::tm local{};
8290 # if defined(_WIN32)
8291 _tzset();
8292 if (localtime_s(&local, now) == 0) {
8293 return _tzname[local.tm_isdst > 0];
8295 # else
8296 tzset();
8297 # if defined(HAVE_LOCALTIME_R)
8298 if (localtime_r(now, &local)) {
8299 # else
8300 std::tm* localtm = std::localtime(now);
8301 if (localtm) {
8302 *local = *localtm;
8303 # endif /* HAVE_LOCALTIME_R */
8305 # if defined(HAVE_TM_ZONE_TM_GMTOFF)
8306 return local.tm_zone;
8307 # else
8308 return tzname[local.tm_isdst > 0];
8309 # endif /* HAVE_TM_ZONE_TM_GMTOFF */
8311 # endif /* _WIN32 */
8312 return nullptr;
8315 std::time_t now = std::time(nullptr);
8316 if (now != static_cast<std::time_t>(-1)) {
8317 if (const char* tz = getTimeZone(&now)) {
8318 return ReturnStringCopy(cx, args, tz);
8321 #endif /* __wasi__ */
8322 args.rval().setUndefined();
8323 return true;
8327 * Validate time zone input. Accepts the following formats:
8328 * - "America/Chicago" (raw time zone)
8329 * - ":America/Chicago"
8330 * - "/this-part-is-ignored/zoneinfo/America/Chicago"
8331 * - ":/this-part-is-ignored/zoneinfo/America/Chicago"
8332 * - "/etc/localtime"
8333 * - ":/etc/localtime"
8334 * Once the raw time zone is parsed out of the string, it is checked
8335 * against the time zones from GetAvailableTimeZones(). Throws an
8336 * Error if the time zone is invalid.
8338 #if defined(JS_HAS_INTL_API) && !defined(__wasi__)
8339 static bool ValidateTimeZone(JSContext* cx, const char* timeZone) {
8340 static constexpr char zoneInfo[] = "/zoneinfo/";
8341 static constexpr size_t zoneInfoLength = sizeof(zoneInfo) - 1;
8343 size_t i = 0;
8344 if (timeZone[i] == ':') {
8345 ++i;
8347 const char* zoneInfoPtr = strstr(timeZone, zoneInfo);
8348 const char* timeZonePart = timeZone[i] == '/' && zoneInfoPtr
8349 ? zoneInfoPtr + zoneInfoLength
8350 : timeZone + i;
8352 if (!*timeZonePart) {
8353 JS_ReportErrorASCII(cx, "Invalid time zone format");
8354 return false;
8357 if (!strcmp(timeZonePart, "/etc/localtime")) {
8358 return true;
8361 auto timeZones = mozilla::intl::TimeZone::GetAvailableTimeZones();
8362 if (timeZones.isErr()) {
8363 intl::ReportInternalError(cx, timeZones.unwrapErr());
8364 return false;
8366 for (auto timeZoneName : timeZones.unwrap()) {
8367 if (timeZoneName.isErr()) {
8368 intl::ReportInternalError(cx);
8369 return false;
8372 if (!strcmp(timeZonePart, timeZoneName.unwrap().data())) {
8373 return true;
8377 JS_ReportErrorASCII(cx, "Unsupported time zone name: %s", timeZonePart);
8378 return false;
8380 #endif
8382 static bool SetTimeZone(JSContext* cx, unsigned argc, Value* vp) {
8383 CallArgs args = CallArgsFromVp(argc, vp);
8384 RootedObject callee(cx, &args.callee());
8386 if (args.length() != 1) {
8387 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
8388 return false;
8391 if (!args[0].isString() && !args[0].isUndefined()) {
8392 ReportUsageErrorASCII(cx, callee,
8393 "First argument should be a string or undefined");
8394 return false;
8397 #ifndef __wasi__
8398 auto setTimeZone = [](const char* value) {
8399 # if defined(_WIN32)
8400 return _putenv_s("TZ", value) == 0;
8401 # else
8402 return setenv("TZ", value, true) == 0;
8403 # endif /* _WIN32 */
8406 auto unsetTimeZone = []() {
8407 # if defined(_WIN32)
8408 return _putenv_s("TZ", "") == 0;
8409 # else
8410 return unsetenv("TZ") == 0;
8411 # endif /* _WIN32 */
8414 if (args[0].isString() && !args[0].toString()->empty()) {
8415 Rooted<JSLinearString*> str(cx, args[0].toString()->ensureLinear(cx));
8416 if (!str) {
8417 return false;
8420 if (!StringIsAscii(str)) {
8421 ReportUsageErrorASCII(cx, callee,
8422 "First argument contains non-ASCII characters");
8423 return false;
8426 UniqueChars timeZone = JS_EncodeStringToASCII(cx, str);
8427 if (!timeZone) {
8428 return false;
8431 const char* timeZoneStr = timeZone.get();
8432 # ifdef JS_HAS_INTL_API
8433 if (!ValidateTimeZone(cx, timeZoneStr)) {
8434 return false;
8436 # endif
8438 if (!setTimeZone(timeZoneStr)) {
8439 JS_ReportErrorASCII(cx, "Failed to set 'TZ' environment variable");
8440 return false;
8442 } else {
8443 if (!unsetTimeZone()) {
8444 JS_ReportErrorASCII(cx, "Failed to unset 'TZ' environment variable");
8445 return false;
8449 # if defined(_WIN32)
8450 _tzset();
8451 # else
8452 tzset();
8453 # endif /* _WIN32 */
8455 JS::ResetTimeZone();
8457 #endif /* __wasi__ */
8458 args.rval().setUndefined();
8459 return true;
8462 static bool GetCoreCount(JSContext* cx, unsigned argc, Value* vp) {
8463 CallArgs args = CallArgsFromVp(argc, vp);
8464 RootedObject callee(cx, &args.callee());
8466 if (args.length() != 0) {
8467 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
8468 return false;
8471 args.rval().setInt32(GetCPUCount());
8472 return true;
8475 static bool GetDefaultLocale(JSContext* cx, unsigned argc, Value* vp) {
8476 CallArgs args = CallArgsFromVp(argc, vp);
8477 RootedObject callee(cx, &args.callee());
8479 if (args.length() != 0) {
8480 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
8481 return false;
8484 UniqueChars locale = JS_GetDefaultLocale(cx);
8485 if (!locale) {
8486 return false;
8489 return ReturnStringCopy(cx, args, locale.get());
8492 static bool SetDefaultLocale(JSContext* cx, unsigned argc, Value* vp) {
8493 CallArgs args = CallArgsFromVp(argc, vp);
8494 RootedObject callee(cx, &args.callee());
8496 if (args.length() != 1) {
8497 ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
8498 return false;
8501 if (!args[0].isString() && !args[0].isUndefined()) {
8502 ReportUsageErrorASCII(cx, callee,
8503 "First argument should be a string or undefined");
8504 return false;
8507 if (args[0].isString() && !args[0].toString()->empty()) {
8508 RootedString str(cx, args[0].toString());
8509 UniqueChars locale = StringToLocale(cx, callee, str);
8510 if (!locale) {
8511 return false;
8514 if (!JS_SetDefaultLocale(cx->runtime(), locale.get())) {
8515 ReportOutOfMemory(cx);
8516 return false;
8518 } else {
8519 JS_ResetDefaultLocale(cx->runtime());
8522 args.rval().setUndefined();
8523 return true;
8526 #ifdef AFLFUZZ
8527 static bool AflLoop(JSContext* cx, unsigned argc, Value* vp) {
8528 CallArgs args = CallArgsFromVp(argc, vp);
8530 uint32_t max_cnt;
8531 if (!ToUint32(cx, args.get(0), &max_cnt)) {
8532 return false;
8535 args.rval().setBoolean(!!__AFL_LOOP(max_cnt));
8536 return true;
8538 #endif
8540 static bool MonotonicNow(JSContext* cx, unsigned argc, Value* vp) {
8541 CallArgs args = CallArgsFromVp(argc, vp);
8542 double now;
8544 // The std::chrono symbols are too new to be present in STL on all platforms we
8545 // care about, so use raw POSIX clock APIs when it might be necessary.
8546 #if defined(XP_UNIX) && !defined(XP_DARWIN)
8547 auto ComputeNow = [](const timespec& ts) {
8548 return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
8551 timespec ts;
8552 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
8553 // Use a monotonic clock if available.
8554 now = ComputeNow(ts);
8555 } else {
8556 // Use a realtime clock as fallback.
8557 if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
8558 // Fail if no clock is available.
8559 JS_ReportErrorASCII(cx, "can't retrieve system clock");
8560 return false;
8563 now = ComputeNow(ts);
8565 // Manually enforce atomicity on a non-monotonic clock.
8567 static mozilla::Atomic<bool, mozilla::ReleaseAcquire> spinLock;
8568 while (!spinLock.compareExchange(false, true)) {
8569 continue;
8572 static double lastNow = -FLT_MAX;
8573 now = lastNow = std::max(now, lastNow);
8575 spinLock = false;
8578 #else
8579 using std::chrono::duration_cast;
8580 using std::chrono::milliseconds;
8581 using std::chrono::steady_clock;
8582 now = duration_cast<milliseconds>(steady_clock::now().time_since_epoch())
8583 .count();
8584 #endif // XP_UNIX && !XP_DARWIN
8586 args.rval().setNumber(now);
8587 return true;
8590 static bool TimeSinceCreation(JSContext* cx, unsigned argc, Value* vp) {
8591 CallArgs args = CallArgsFromVp(argc, vp);
8592 double when =
8593 (mozilla::TimeStamp::Now() - mozilla::TimeStamp::ProcessCreation())
8594 .ToMilliseconds();
8595 args.rval().setNumber(when);
8596 return true;
8599 static bool GetInnerMostEnvironmentObject(JSContext* cx, unsigned argc,
8600 Value* vp) {
8601 CallArgs args = CallArgsFromVp(argc, vp);
8603 FrameIter iter(cx);
8604 if (iter.done()) {
8605 args.rval().setNull();
8606 return true;
8609 args.rval().setObjectOrNull(iter.environmentChain(cx));
8610 return true;
8613 static bool GetEnclosingEnvironmentObject(JSContext* cx, unsigned argc,
8614 Value* vp) {
8615 CallArgs args = CallArgsFromVp(argc, vp);
8616 if (!args.requireAtLeast(cx, "getEnclosingEnvironmentObject", 1)) {
8617 return false;
8620 if (!args[0].isObject()) {
8621 args.rval().setUndefined();
8622 return true;
8625 JSObject* envObj = &args[0].toObject();
8627 if (envObj->is<EnvironmentObject>()) {
8628 EnvironmentObject* env = &envObj->as<EnvironmentObject>();
8629 args.rval().setObject(env->enclosingEnvironment());
8630 return true;
8633 if (envObj->is<DebugEnvironmentProxy>()) {
8634 DebugEnvironmentProxy* envProxy = &envObj->as<DebugEnvironmentProxy>();
8635 args.rval().setObject(envProxy->enclosingEnvironment());
8636 return true;
8639 args.rval().setNull();
8640 return true;
8643 static bool GetEnvironmentObjectType(JSContext* cx, unsigned argc, Value* vp) {
8644 CallArgs args = CallArgsFromVp(argc, vp);
8645 if (!args.requireAtLeast(cx, "getEnvironmentObjectType", 1)) {
8646 return false;
8649 if (!args[0].isObject()) {
8650 args.rval().setUndefined();
8651 return true;
8654 JSObject* envObj = &args[0].toObject();
8656 if (envObj->is<EnvironmentObject>()) {
8657 EnvironmentObject* env = &envObj->as<EnvironmentObject>();
8658 JSString* str = JS_NewStringCopyZ(cx, env->typeString());
8659 args.rval().setString(str);
8660 return true;
8662 if (envObj->is<DebugEnvironmentProxy>()) {
8663 DebugEnvironmentProxy* envProxy = &envObj->as<DebugEnvironmentProxy>();
8664 EnvironmentObject* env = &envProxy->environment();
8665 char buf[256] = {'\0'};
8666 SprintfLiteral(buf, "[DebugProxy] %s", env->typeString());
8667 JSString* str = JS_NewStringCopyZ(cx, buf);
8668 args.rval().setString(str);
8669 return true;
8672 args.rval().setUndefined();
8673 return true;
8676 static bool AssertRealmFuseInvariants(JSContext* cx, unsigned argc, Value* vp) {
8677 CallArgs args = CallArgsFromVp(argc, vp);
8678 // Note: This will crash if any invariant isn't held, so it's sufficient to
8679 // simply return true always.
8680 cx->realm()->realmFuses.assertInvariants(cx);
8681 args.rval().setUndefined();
8682 return true;
8685 static bool GetFuseState(JSContext* cx, unsigned argc, Value* vp) {
8686 CallArgs args = CallArgsFromVp(argc, vp);
8688 cx->realm()->realmFuses.assertInvariants(cx);
8690 RootedObject returnObj(cx, JS_NewPlainObject(cx));
8691 if (!returnObj) {
8692 return false;
8695 RootedObject fuseObj(cx);
8696 RootedString intactStr(cx, NewStringCopyZ<CanGC>(cx, "intact"));
8697 if (!intactStr) {
8698 return false;
8701 RootedValue intactValue(cx);
8703 #define FUSE(Name, LowerName) \
8704 fuseObj = JS_NewPlainObject(cx); \
8705 if (!fuseObj) { \
8706 return false; \
8708 intactValue.setBoolean(cx->realm()->realmFuses.LowerName.intact()); \
8709 if (!JS_DefineProperty(cx, fuseObj, "intact", intactValue, \
8710 JSPROP_ENUMERATE)) { \
8711 return false; \
8713 if (!JS_DefineProperty(cx, returnObj, #Name, fuseObj, JSPROP_ENUMERATE)) { \
8714 return false; \
8717 FOR_EACH_REALM_FUSE(FUSE)
8718 #undef FUSE
8720 // Register hasSeenUndefinedFuse
8721 fuseObj = JS_NewPlainObject(cx);
8722 if (!fuseObj) {
8723 return false;
8725 intactValue.setBoolean(
8726 cx->runtime()->hasSeenObjectEmulateUndefinedFuse.ref().intact());
8727 if (!JS_DefineProperty(cx, fuseObj, "intact", intactValue,
8728 JSPROP_ENUMERATE)) {
8729 return false;
8732 if (!JS_DefineProperty(cx, returnObj, "hasSeenObjectEmulateUndefinedFuse",
8733 fuseObj, JSPROP_ENUMERATE)) {
8734 return false;
8737 args.rval().setObject(*returnObj);
8738 return true;
8741 static bool PopAllFusesInRealm(JSContext* cx, unsigned argc, Value* vp) {
8742 CallArgs args = CallArgsFromVp(argc, vp);
8744 MOZ_ASSERT(cx->realm());
8746 RealmFuses& realmFuses = cx->realm()->realmFuses;
8748 #define FUSE(Name, LowerName) realmFuses.LowerName.popFuse(cx, realmFuses);
8749 FOR_EACH_REALM_FUSE(FUSE)
8750 #undef FUSE
8752 args.rval().setUndefined();
8753 return true;
8756 static bool GetAllPrefNames(JSContext* cx, unsigned argc, Value* vp) {
8757 CallArgs args = CallArgsFromVp(argc, vp);
8759 RootedValueVector values(cx);
8761 auto addPref = [cx, &values](const char* name) {
8762 JSString* s = JS_NewStringCopyZ(cx, name);
8763 if (!s) {
8764 return false;
8766 return values.append(StringValue(s));
8769 #define ADD_NAME(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \
8770 if (!addPref(NAME)) { \
8771 return false; \
8773 FOR_EACH_JS_PREF(ADD_NAME)
8774 #undef ADD_NAME
8776 ArrayObject* arr = NewDenseCopiedArray(cx, values.length(), values.begin());
8777 if (!arr) {
8778 return false;
8781 args.rval().setObject(*arr);
8782 return true;
8785 static bool GetPrefValue(JSContext* cx, unsigned argc, Value* vp) {
8786 CallArgs args = CallArgsFromVp(argc, vp);
8787 if (!args.requireAtLeast(cx, "getPrefValue", 1)) {
8788 return false;
8791 if (!args[0].isString()) {
8792 JS_ReportErrorASCII(cx, "expected string argument");
8793 return false;
8796 Rooted<JSLinearString*> name(cx, args[0].toString()->ensureLinear(cx));
8797 if (!name) {
8798 return false;
8801 auto setReturnValue = [&args](auto value) {
8802 using T = decltype(value);
8803 if constexpr (std::is_same_v<T, bool>) {
8804 args.rval().setBoolean(value);
8805 } else {
8806 static_assert(std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>);
8807 args.rval().setNumber(value);
8811 // Search for a matching pref and return its value.
8812 #define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \
8813 if (StringEqualsAscii(name, NAME)) { \
8814 setReturnValue(JS::Prefs::CPP_NAME()); \
8815 return true; \
8817 FOR_EACH_JS_PREF(CHECK_PREF)
8818 #undef CHECK_PREF
8820 JS_ReportErrorASCII(cx, "invalid pref name");
8821 return false;
8824 static bool GetErrorNotes(JSContext* cx, unsigned argc, Value* vp) {
8825 CallArgs args = CallArgsFromVp(argc, vp);
8826 if (!args.requireAtLeast(cx, "getErrorNotes", 1)) {
8827 return false;
8830 if (!args[0].isObject() || !args[0].toObject().is<ErrorObject>()) {
8831 args.rval().setNull();
8832 return true;
8835 JSErrorReport* report = args[0].toObject().as<ErrorObject>().getErrorReport();
8836 if (!report) {
8837 args.rval().setNull();
8838 return true;
8841 RootedObject notesArray(cx, CreateErrorNotesArray(cx, report));
8842 if (!notesArray) {
8843 return false;
8846 args.rval().setObject(*notesArray);
8847 return true;
8850 static bool IsConstructor(JSContext* cx, unsigned argc, Value* vp) {
8851 CallArgs args = CallArgsFromVp(argc, vp);
8852 if (args.length() < 1) {
8853 args.rval().setBoolean(false);
8854 } else {
8855 args.rval().setBoolean(IsConstructor(args[0]));
8857 return true;
8860 static bool SetTimeResolution(JSContext* cx, unsigned argc, Value* vp) {
8861 CallArgs args = CallArgsFromVp(argc, vp);
8862 RootedObject callee(cx, &args.callee());
8864 if (!args.requireAtLeast(cx, "setTimeResolution", 2)) {
8865 return false;
8868 if (!args[0].isInt32()) {
8869 ReportUsageErrorASCII(cx, callee, "First argument must be an Int32.");
8870 return false;
8872 int32_t resolution = args[0].toInt32();
8874 if (!args[1].isBoolean()) {
8875 ReportUsageErrorASCII(cx, callee, "Second argument must be a Boolean");
8876 return false;
8878 bool jitter = args[1].toBoolean();
8880 JS::SetTimeResolutionUsec(resolution, jitter);
8882 args.rval().setUndefined();
8883 return true;
8886 static bool ScriptedCallerGlobal(JSContext* cx, unsigned argc, Value* vp) {
8887 CallArgs args = CallArgsFromVp(argc, vp);
8889 RootedObject obj(cx, JS::GetScriptedCallerGlobal(cx));
8890 if (!obj) {
8891 args.rval().setNull();
8892 return true;
8895 obj = ToWindowProxyIfWindow(obj);
8897 if (!cx->compartment()->wrap(cx, &obj)) {
8898 return false;
8901 args.rval().setObject(*obj);
8902 return true;
8905 static bool ObjectGlobal(JSContext* cx, unsigned argc, Value* vp) {
8906 CallArgs args = CallArgsFromVp(argc, vp);
8907 RootedObject callee(cx, &args.callee());
8909 if (!args.get(0).isObject()) {
8910 ReportUsageErrorASCII(cx, callee, "Argument must be an object");
8911 return false;
8914 RootedObject obj(cx, &args[0].toObject());
8915 if (IsCrossCompartmentWrapper(obj)) {
8916 args.rval().setNull();
8917 return true;
8920 obj = ToWindowProxyIfWindow(&obj->nonCCWGlobal());
8922 args.rval().setObject(*obj);
8923 return true;
8926 static bool IsSameCompartment(JSContext* cx, unsigned argc, Value* vp) {
8927 CallArgs args = CallArgsFromVp(argc, vp);
8928 RootedObject callee(cx, &args.callee());
8930 if (!args.get(0).isObject() || !args.get(1).isObject()) {
8931 ReportUsageErrorASCII(cx, callee, "Both arguments must be objects");
8932 return false;
8935 RootedObject obj1(cx, UncheckedUnwrap(&args[0].toObject()));
8936 RootedObject obj2(cx, UncheckedUnwrap(&args[1].toObject()));
8938 args.rval().setBoolean(obj1->compartment() == obj2->compartment());
8939 return true;
8942 static bool FirstGlobalInCompartment(JSContext* cx, unsigned argc, Value* vp) {
8943 CallArgs args = CallArgsFromVp(argc, vp);
8944 RootedObject callee(cx, &args.callee());
8946 if (!args.get(0).isObject()) {
8947 ReportUsageErrorASCII(cx, callee, "Argument must be an object");
8948 return false;
8951 RootedObject obj(cx, UncheckedUnwrap(&args[0].toObject()));
8952 obj = ToWindowProxyIfWindow(GetFirstGlobalInCompartment(obj->compartment()));
8954 if (!cx->compartment()->wrap(cx, &obj)) {
8955 return false;
8958 args.rval().setObject(*obj);
8959 return true;
8962 static bool AssertCorrectRealm(JSContext* cx, unsigned argc, Value* vp) {
8963 CallArgs args = CallArgsFromVp(argc, vp);
8964 MOZ_RELEASE_ASSERT(cx->realm() == args.callee().as<JSFunction>().realm());
8965 args.rval().setUndefined();
8966 return true;
8969 static bool GlobalLexicals(JSContext* cx, unsigned argc, Value* vp) {
8970 CallArgs args = CallArgsFromVp(argc, vp);
8972 Rooted<GlobalLexicalEnvironmentObject*> globalLexical(
8973 cx, &cx->global()->lexicalEnvironment());
8975 RootedIdVector props(cx);
8976 if (!GetPropertyKeys(cx, globalLexical, JSITER_HIDDEN, &props)) {
8977 return false;
8980 RootedObject res(cx, JS_NewPlainObject(cx));
8981 if (!res) {
8982 return false;
8985 RootedValue val(cx);
8986 for (size_t i = 0; i < props.length(); i++) {
8987 HandleId id = props[i];
8988 if (!JS_GetPropertyById(cx, globalLexical, id, &val)) {
8989 return false;
8991 if (val.isMagic(JS_UNINITIALIZED_LEXICAL)) {
8992 continue;
8994 if (!JS_DefinePropertyById(cx, res, id, val, JSPROP_ENUMERATE)) {
8995 return false;
8999 args.rval().setObject(*res);
9000 return true;
9003 static bool EncodeAsUtf8InBuffer(JSContext* cx, unsigned argc, Value* vp) {
9004 CallArgs args = CallArgsFromVp(argc, vp);
9005 if (!args.requireAtLeast(cx, "encodeAsUtf8InBuffer", 2)) {
9006 return false;
9009 RootedObject callee(cx, &args.callee());
9011 if (!args[0].isString()) {
9012 ReportUsageErrorASCII(cx, callee, "First argument must be a String");
9013 return false;
9016 // Create the amounts array early so that the raw pointer into Uint8Array
9017 // data has as short a lifetime as possible
9018 Rooted<ArrayObject*> array(cx, NewDenseFullyAllocatedArray(cx, 2));
9019 if (!array) {
9020 return false;
9022 array->ensureDenseInitializedLength(0, 2);
9024 JSObject* obj = args[1].isObject() ? &args[1].toObject() : nullptr;
9025 Rooted<JS::Uint8Array> view(cx, JS::Uint8Array::unwrap(obj));
9026 if (!view) {
9027 ReportUsageErrorASCII(cx, callee, "Second argument must be a Uint8Array");
9028 return false;
9031 mozilla::Span<uint8_t> span;
9032 bool isSharedMemory = false;
9034 // The hazard analysis does not track the data pointer, so it can neither
9035 // tell that `data` is dead if ReportUsageErrorASCII is called, nor that
9036 // its live range ends at the call to AsWritableChars(). Construct a
9037 // temporary scope to hide from the analysis. This should really be replaced
9038 // with a safer mechanism.
9039 JS::AutoCheckCannotGC nogc(cx);
9040 if (!view.isDetached()) {
9041 span = view.get().getData(&isSharedMemory, nogc);
9045 if (isSharedMemory || // exclude views of SharedArrayBuffers
9046 !span.data()) { // exclude views of detached ArrayBuffers
9047 ReportUsageErrorASCII(
9048 cx, callee,
9049 "Second argument must be an unshared, non-detached Uint8Array");
9050 return false;
9053 Maybe<std::tuple<size_t, size_t>> amounts =
9054 JS_EncodeStringToUTF8BufferPartial(cx, args[0].toString(),
9055 AsWritableChars(span));
9056 if (!amounts) {
9057 ReportOutOfMemory(cx);
9058 return false;
9061 auto [unitsRead, bytesWritten] = *amounts;
9063 array->initDenseElement(0, Int32Value(AssertedCast<int32_t>(unitsRead)));
9064 array->initDenseElement(1, Int32Value(AssertedCast<int32_t>(bytesWritten)));
9066 args.rval().setObject(*array);
9067 return true;
9070 JSScript* js::TestingFunctionArgumentToScript(
9071 JSContext* cx, HandleValue v, JSFunction** funp /* = nullptr */) {
9072 if (v.isString()) {
9073 // To convert a string to a script, compile it. Parse it as an ES6 Program.
9074 Rooted<JSString*> str(cx, v.toString());
9075 AutoStableStringChars linearChars(cx);
9076 if (!linearChars.initTwoByte(cx, str)) {
9077 return nullptr;
9079 SourceText<char16_t> source;
9080 if (!source.initMaybeBorrowed(cx, linearChars)) {
9081 return nullptr;
9084 CompileOptions options(cx);
9085 return JS::Compile(cx, options, source);
9088 RootedFunction fun(cx, JS_ValueToFunction(cx, v));
9089 if (!fun) {
9090 return nullptr;
9093 if (!fun->isInterpreted()) {
9094 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
9095 JSMSG_TESTING_SCRIPTS_ONLY);
9096 return nullptr;
9099 JSScript* script = JSFunction::getOrCreateScript(cx, fun);
9100 if (!script) {
9101 return nullptr;
9104 if (funp) {
9105 *funp = fun;
9108 return script;
9111 static bool BaselineCompile(JSContext* cx, unsigned argc, Value* vp) {
9112 CallArgs args = CallArgsFromVp(argc, vp);
9113 RootedObject callee(cx, &args.callee());
9115 RootedScript script(cx);
9116 if (args.length() == 0) {
9117 NonBuiltinScriptFrameIter iter(cx);
9118 if (iter.done()) {
9119 ReportUsageErrorASCII(cx, callee,
9120 "no script argument and no script caller");
9121 return false;
9123 script = iter.script();
9124 } else {
9125 script = TestingFunctionArgumentToScript(cx, args[0]);
9126 if (!script) {
9127 return false;
9131 bool forceDebug = false;
9132 if (args.length() > 1) {
9133 if (args.length() > 2) {
9134 ReportUsageErrorASCII(cx, callee, "too many arguments");
9135 return false;
9137 if (!args[1].isBoolean() && !args[1].isUndefined()) {
9138 ReportUsageErrorASCII(
9139 cx, callee, "forceDebugInstrumentation argument should be boolean");
9140 return false;
9142 forceDebug = ToBoolean(args[1]);
9145 const char* returnedStr = nullptr;
9146 do {
9147 // In order to check for differential behaviour, baselineCompile should have
9148 // the same output whether --no-baseline is used or not.
9149 if (js::SupportDifferentialTesting()) {
9150 returnedStr = "skipped (differential testing)";
9151 break;
9154 AutoRealm ar(cx, script);
9155 if (script->hasBaselineScript()) {
9156 if (forceDebug && !script->baselineScript()->hasDebugInstrumentation()) {
9157 // There isn't an easy way to do this for a script that might be on
9158 // stack right now. See
9159 // js::jit::RecompileOnStackBaselineScriptsForDebugMode.
9160 ReportUsageErrorASCII(
9161 cx, callee, "unsupported case: recompiling script for debug mode");
9162 return false;
9165 args.rval().setUndefined();
9166 return true;
9169 if (!jit::IsBaselineJitEnabled(cx)) {
9170 returnedStr = "baseline disabled";
9171 break;
9173 if (!script->canBaselineCompile()) {
9174 returnedStr = "can't compile";
9175 break;
9177 if (!cx->zone()->ensureJitZoneExists(cx)) {
9178 return false;
9181 jit::MethodStatus status = jit::BaselineCompile(cx, script, forceDebug);
9182 switch (status) {
9183 case jit::Method_Error:
9184 return false;
9185 case jit::Method_CantCompile:
9186 returnedStr = "can't compile";
9187 break;
9188 case jit::Method_Skipped:
9189 returnedStr = "skipped";
9190 break;
9191 case jit::Method_Compiled:
9192 args.rval().setUndefined();
9194 } while (false);
9196 if (returnedStr) {
9197 return ReturnStringCopy(cx, args, returnedStr);
9200 return true;
9203 static bool ClearKeptObjects(JSContext* cx, unsigned argc, Value* vp) {
9204 CallArgs args = CallArgsFromVp(argc, vp);
9205 JS::ClearKeptObjects(cx);
9206 args.rval().setUndefined();
9207 return true;
9210 static bool NumberToDouble(JSContext* cx, unsigned argc, Value* vp) {
9211 CallArgs args = CallArgsFromVp(argc, vp);
9212 if (!args.requireAtLeast(cx, "numberToDouble", 1)) {
9213 return false;
9216 if (!args[0].isNumber()) {
9217 RootedObject callee(cx, &args.callee());
9218 ReportUsageErrorASCII(cx, callee, "argument must be a number");
9219 return false;
9222 args.rval().setDouble(args[0].toNumber());
9223 return true;
9226 static bool GetICUOptions(JSContext* cx, unsigned argc, Value* vp) {
9227 CallArgs args = CallArgsFromVp(argc, vp);
9229 RootedObject info(cx, JS_NewPlainObject(cx));
9230 if (!info) {
9231 return false;
9234 #ifdef JS_HAS_INTL_API
9235 RootedString str(cx);
9237 str = NewStringCopy<CanGC>(cx, mozilla::intl::ICU4CLibrary::GetVersion());
9238 if (!str || !JS_DefineProperty(cx, info, "version", str, JSPROP_ENUMERATE)) {
9239 return false;
9242 str = NewStringCopy<CanGC>(cx, mozilla::intl::String::GetUnicodeVersion());
9243 if (!str || !JS_DefineProperty(cx, info, "unicode", str, JSPROP_ENUMERATE)) {
9244 return false;
9247 str = NewStringCopyZ<CanGC>(cx, mozilla::intl::Locale::GetDefaultLocale());
9248 if (!str || !JS_DefineProperty(cx, info, "locale", str, JSPROP_ENUMERATE)) {
9249 return false;
9252 auto tzdataVersion = mozilla::intl::TimeZone::GetTZDataVersion();
9253 if (tzdataVersion.isErr()) {
9254 intl::ReportInternalError(cx, tzdataVersion.unwrapErr());
9255 return false;
9258 str = NewStringCopy<CanGC>(cx, tzdataVersion.unwrap());
9259 if (!str || !JS_DefineProperty(cx, info, "tzdata", str, JSPROP_ENUMERATE)) {
9260 return false;
9263 intl::FormatBuffer<char16_t, intl::INITIAL_CHAR_BUFFER_SIZE> buf(cx);
9265 if (auto ok = DateTimeInfo::timeZoneId(DateTimeInfo::ForceUTC::No, buf);
9266 ok.isErr()) {
9267 intl::ReportInternalError(cx, ok.unwrapErr());
9268 return false;
9271 str = buf.toString(cx);
9272 if (!str || !JS_DefineProperty(cx, info, "timezone", str, JSPROP_ENUMERATE)) {
9273 return false;
9276 if (auto ok = mozilla::intl::TimeZone::GetHostTimeZone(buf); ok.isErr()) {
9277 intl::ReportInternalError(cx, ok.unwrapErr());
9278 return false;
9281 str = buf.toString(cx);
9282 if (!str ||
9283 !JS_DefineProperty(cx, info, "host-timezone", str, JSPROP_ENUMERATE)) {
9284 return false;
9286 #endif
9288 args.rval().setObject(*info);
9289 return true;
9292 static bool GetAvailableLocalesOf(JSContext* cx, unsigned argc, Value* vp) {
9293 CallArgs args = CallArgsFromVp(argc, vp);
9294 RootedObject callee(cx, &args.callee());
9296 if (!args.requireAtLeast(cx, "getAvailableLocalesOf", 1)) {
9297 return false;
9300 HandleValue arg = args[0];
9301 if (!arg.isString()) {
9302 ReportUsageErrorASCII(cx, callee, "First argument must be a string");
9303 return false;
9306 ArrayObject* result;
9307 #ifdef JS_HAS_INTL_API
9308 using SupportedLocaleKind = js::intl::SharedIntlData::SupportedLocaleKind;
9310 SupportedLocaleKind kind;
9312 JSLinearString* typeStr = arg.toString()->ensureLinear(cx);
9313 if (!typeStr) {
9314 return false;
9317 if (StringEqualsLiteral(typeStr, "Collator")) {
9318 kind = SupportedLocaleKind::Collator;
9319 } else if (StringEqualsLiteral(typeStr, "DateTimeFormat")) {
9320 kind = SupportedLocaleKind::DateTimeFormat;
9321 } else if (StringEqualsLiteral(typeStr, "DisplayNames")) {
9322 kind = SupportedLocaleKind::DisplayNames;
9323 } else if (StringEqualsLiteral(typeStr, "ListFormat")) {
9324 kind = SupportedLocaleKind::ListFormat;
9325 } else if (StringEqualsLiteral(typeStr, "NumberFormat")) {
9326 kind = SupportedLocaleKind::NumberFormat;
9327 } else if (StringEqualsLiteral(typeStr, "PluralRules")) {
9328 kind = SupportedLocaleKind::PluralRules;
9329 } else if (StringEqualsLiteral(typeStr, "RelativeTimeFormat")) {
9330 kind = SupportedLocaleKind::RelativeTimeFormat;
9331 } else if (StringEqualsLiteral(typeStr, "Segmenter")) {
9332 kind = SupportedLocaleKind::Segmenter;
9333 } else {
9334 ReportUsageErrorASCII(cx, callee, "Unsupported Intl constructor name");
9335 return false;
9339 intl::SharedIntlData& sharedIntlData = cx->runtime()->sharedIntlData.ref();
9340 result = sharedIntlData.availableLocalesOf(cx, kind);
9341 #else
9342 result = NewDenseEmptyArray(cx);
9343 #endif
9344 if (!result) {
9345 return false;
9348 args.rval().setObject(*result);
9349 return true;
9352 static bool IsSmallFunction(JSContext* cx, unsigned argc, Value* vp) {
9353 CallArgs args = CallArgsFromVp(argc, vp);
9354 RootedObject callee(cx, &args.callee());
9356 if (!args.requireAtLeast(cx, "IsSmallFunction", 1)) {
9357 return false;
9360 HandleValue arg = args[0];
9361 if (!arg.isObject() || !arg.toObject().is<JSFunction>()) {
9362 ReportUsageErrorASCII(cx, callee, "First argument must be a function");
9363 return false;
9366 RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
9367 if (!fun->isInterpreted()) {
9368 ReportUsageErrorASCII(cx, callee,
9369 "First argument must be an interpreted function");
9370 return false;
9373 JSScript* script = JSFunction::getOrCreateScript(cx, fun);
9374 if (!script) {
9375 return false;
9378 args.rval().setBoolean(jit::JitOptions.isSmallFunction(script));
9379 return true;
9382 static bool PCCountProfiling_Start(JSContext* cx, unsigned argc, Value* vp) {
9383 CallArgs args = CallArgsFromVp(argc, vp);
9385 JS::StartPCCountProfiling(cx);
9387 args.rval().setUndefined();
9388 return true;
9391 static bool PCCountProfiling_Stop(JSContext* cx, unsigned argc, Value* vp) {
9392 CallArgs args = CallArgsFromVp(argc, vp);
9394 JS::StopPCCountProfiling(cx);
9396 args.rval().setUndefined();
9397 return true;
9400 static bool PCCountProfiling_Purge(JSContext* cx, unsigned argc, Value* vp) {
9401 CallArgs args = CallArgsFromVp(argc, vp);
9403 JS::PurgePCCounts(cx);
9405 args.rval().setUndefined();
9406 return true;
9409 static bool PCCountProfiling_ScriptCount(JSContext* cx, unsigned argc,
9410 Value* vp) {
9411 CallArgs args = CallArgsFromVp(argc, vp);
9413 size_t length = JS::GetPCCountScriptCount(cx);
9415 args.rval().setNumber(double(length));
9416 return true;
9419 static bool PCCountProfiling_ScriptSummary(JSContext* cx, unsigned argc,
9420 Value* vp) {
9421 CallArgs args = CallArgsFromVp(argc, vp);
9422 if (!args.requireAtLeast(cx, "summary", 1)) {
9423 return false;
9426 uint32_t index;
9427 if (!JS::ToUint32(cx, args[0], &index)) {
9428 return false;
9431 JSString* str = JS::GetPCCountScriptSummary(cx, index);
9432 if (!str) {
9433 return false;
9436 args.rval().setString(str);
9437 return true;
9440 static bool PCCountProfiling_ScriptContents(JSContext* cx, unsigned argc,
9441 Value* vp) {
9442 CallArgs args = CallArgsFromVp(argc, vp);
9443 if (!args.requireAtLeast(cx, "contents", 1)) {
9444 return false;
9447 uint32_t index;
9448 if (!JS::ToUint32(cx, args[0], &index)) {
9449 return false;
9452 JSString* str = JS::GetPCCountScriptContents(cx, index);
9453 if (!str) {
9454 return false;
9457 args.rval().setString(str);
9458 return true;
9461 static bool NukeCCW(JSContext* cx, unsigned argc, Value* vp) {
9462 CallArgs args = CallArgsFromVp(argc, vp);
9464 if (args.length() != 1 || !args[0].isObject() ||
9465 !IsCrossCompartmentWrapper(&args[0].toObject())) {
9466 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS,
9467 "nukeCCW");
9468 return false;
9471 NukeCrossCompartmentWrapper(cx, &args[0].toObject());
9472 args.rval().setUndefined();
9473 return true;
9476 static bool IsCCW(JSContext* cx, unsigned argc, Value* vp) {
9477 CallArgs args = CallArgsFromVp(argc, vp);
9479 if (args.length() != 1 || !args[0].isObject()) {
9480 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_ARGS,
9481 "IsCCW");
9482 return false;
9485 args.rval().setBoolean(IsCrossCompartmentWrapper(&args[0].toObject()));
9486 return true;
9489 static bool FdLibM_Pow(JSContext* cx, unsigned argc, Value* vp) {
9490 CallArgs args = CallArgsFromVp(argc, vp);
9492 double x;
9493 if (!JS::ToNumber(cx, args.get(0), &x)) {
9494 return false;
9497 double y;
9498 if (!JS::ToNumber(cx, args.get(1), &y)) {
9499 return false;
9502 // Because C99 and ECMA specify different behavior for pow(), we need to wrap
9503 // the fdlibm call to make it ECMA compliant.
9504 if (!std::isfinite(y) && (x == 1.0 || x == -1.0)) {
9505 args.rval().setNaN();
9506 } else {
9507 args.rval().setDouble(fdlibm_pow(x, y));
9509 return true;
9512 static bool HadOutOfMemory(JSContext* cx, unsigned argc, Value* vp) {
9513 CallArgs args = CallArgsFromVp(argc, vp);
9514 args.rval().setBoolean(cx->runtime()->hadOutOfMemory);
9515 return true;
9518 // clang-format off
9519 static const JSFunctionSpecWithHelp TestingFunctions[] = {
9520 JS_FN_HELP("gc", ::GC, 0, 0,
9521 "gc([obj] | 'zone' [, ('shrinking' | 'last-ditch') ])",
9522 " Run the garbage collector.\n"
9523 " The first parameter describes which zones to collect: if an object is\n"
9524 " given, GC only its zone. If 'zone' is given, GC any zones that were\n"
9525 " scheduled via schedulegc.\n"
9526 " The second parameter is optional and may be 'shrinking' to perform a\n"
9527 " shrinking GC or 'last-ditch' for a shrinking, last-ditch GC."),
9529 JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
9530 "minorgc([aboutToOverflow])",
9531 " Run a minor collector on the Nursery. When aboutToOverflow is true, marks\n"
9532 " the store buffer as about-to-overflow before collecting."),
9534 JS_FN_HELP("maybegc", ::MaybeGC, 0, 0,
9535 "maybegc()",
9536 " Hint to the engine that now is an ok time to run the garbage collector.\n"),
9538 JS_FN_HELP("gcparam", GCParameter, 2, 0,
9539 "gcparam(name [, value])",
9540 " Wrapper for JS_[GS]etGCParameter. The name is one of:" GC_PARAMETER_ARGS_LIST),
9542 JS_FN_HELP("finishBackgroundFree", FinishBackgroundFree, 0, 0,
9543 "finishBackgroundFree()",
9544 " Wait for the GC's background free task to finish.\n"),
9546 JS_FN_HELP("hasDisassembler", HasDisassembler, 0, 0,
9547 "hasDisassembler()",
9548 " Return true if a disassembler is present (for disnative and wasmDis)."),
9550 JS_FN_HELP("disnative", DisassembleNative, 2, 0,
9551 "disnative(fun,[path])",
9552 " Disassemble a function into its native code. Optionally write the native code bytes to a file on disk.\n"),
9554 JS_FN_HELP("relazifyFunctions", RelazifyFunctions, 0, 0,
9555 "relazifyFunctions(...)",
9556 " Perform a GC and allow relazification of functions. Accepts the same\n"
9557 " arguments as gc()."),
9559 JS_FN_HELP("getBuildConfiguration", GetBuildConfiguration, 1, 0,
9560 "getBuildConfiguration([option])",
9561 " Query the options SpiderMonkey was built with, or return an object\n"
9562 " with the options if no argument is given."),
9564 JS_FN_HELP("getRealmConfiguration", GetRealmConfiguration, 1, 0,
9565 "getRealmConfiguration([option])",
9566 " Query the runtime options SpiderMonkey is running with, or return an\n."
9567 " object with the options if no argument is given."),
9569 JS_FN_HELP("isLcovEnabled", ::IsLCovEnabled, 0, 0,
9570 "isLcovEnabled()",
9571 " Return true if JS LCov support is enabled."),
9573 JS_FN_HELP("trialInline", TrialInline, 0, 0,
9574 "trialInline()",
9575 " Perform trial-inlining for the caller's frame if it's a BaselineFrame."),
9577 JS_FN_HELP("hasChild", HasChild, 0, 0,
9578 "hasChild(parent, child)",
9579 " Return true if |child| is a child of |parent|, as determined by a call to\n"
9580 " TraceChildren"),
9582 JS_FN_HELP("setSavedStacksRNGState", SetSavedStacksRNGState, 1, 0,
9583 "setSavedStacksRNGState(seed)",
9584 " Set this compartment's SavedStacks' RNG state.\n"),
9586 JS_FN_HELP("getSavedFrameCount", GetSavedFrameCount, 0, 0,
9587 "getSavedFrameCount()",
9588 " Return the number of SavedFrame instances stored in this compartment's\n"
9589 " SavedStacks cache."),
9591 JS_FN_HELP("clearSavedFrames", ClearSavedFrames, 0, 0,
9592 "clearSavedFrames()",
9593 " Empty the current compartment's cache of SavedFrame objects, so that\n"
9594 " subsequent stack captures allocate fresh objects to represent frames.\n"
9595 " Clear the current stack's LiveSavedFrameCaches."),
9597 JS_FN_HELP("saveStack", SaveStack, 0, 0,
9598 "saveStack([maxDepth [, compartment]])",
9599 " Capture a stack. If 'maxDepth' is given, capture at most 'maxDepth' number\n"
9600 " of frames. If 'compartment' is given, allocate the js::SavedFrame instances\n"
9601 " with the given object's compartment."),
9603 JS_FN_HELP("captureFirstSubsumedFrame", CaptureFirstSubsumedFrame, 1, 0,
9604 "saveStack(object [, shouldIgnoreSelfHosted = true]])",
9605 " Capture a stack back to the first frame whose principals are subsumed by the\n"
9606 " object's compartment's principals. If 'shouldIgnoreSelfHosted' is given,\n"
9607 " control whether self-hosted frames are considered when checking principals."),
9609 JS_FN_HELP("callFunctionFromNativeFrame", CallFunctionFromNativeFrame, 1, 0,
9610 "callFunctionFromNativeFrame(function)",
9611 " Call 'function' with a (C++-)native frame on stack.\n"
9612 " Required for testing that SaveStack properly handles native frames."),
9614 JS_FN_HELP("callFunctionWithAsyncStack", CallFunctionWithAsyncStack, 0, 0,
9615 "callFunctionWithAsyncStack(function, stack, asyncCause)",
9616 " Call 'function', using the provided stack as the async stack responsible\n"
9617 " for the call, and propagate its return value or the exception it throws.\n"
9618 " The function is called with no arguments, and 'this' is 'undefined'. The\n"
9619 " specified |asyncCause| is attached to the provided stack frame."),
9621 JS_FN_HELP("enableTrackAllocations", EnableTrackAllocations, 0, 0,
9622 "enableTrackAllocations()",
9623 " Start capturing the JS stack at every allocation. Note that this sets an\n"
9624 " object metadata callback that will override any other object metadata\n"
9625 " callback that may be set."),
9627 JS_FN_HELP("disableTrackAllocations", DisableTrackAllocations, 0, 0,
9628 "disableTrackAllocations()",
9629 " Stop capturing the JS stack at every allocation."),
9631 JS_FN_HELP("setTestFilenameValidationCallback", SetTestFilenameValidationCallback, 0, 0,
9632 "setTestFilenameValidationCallback()",
9633 " Set the filename validation callback to a callback that accepts only\n"
9634 " filenames starting with 'safe' or (only in system realms) 'system'."),
9636 JS_FN_HELP("newObjectWithAddPropertyHook", NewObjectWithAddPropertyHook, 0, 0,
9637 "newObjectWithAddPropertyHook()",
9638 " Returns a new object with an addProperty JSClass hook. This hook\n"
9639 " increments the value of the _propertiesAdded data property on the object\n"
9640 " when a new property is added."),
9642 JS_FN_HELP("newObjectWithCallHook", NewObjectWithCallHook, 0, 0,
9643 "newObjectWithCallHook()",
9644 " Returns a new object with call/construct JSClass hooks. These hooks return\n"
9645 " a new object that contains the Values supplied by the caller."),
9647 JS_FN_HELP("newObjectWithManyReservedSlots", NewObjectWithManyReservedSlots, 0, 0,
9648 "newObjectWithManyReservedSlots()",
9649 " Returns a new object with many reserved slots. The slots are initialized to int32\n"
9650 " values. checkObjectWithManyReservedSlots can be used to check the slots still\n"
9651 " hold these values."),
9653 JS_FN_HELP("checkObjectWithManyReservedSlots", CheckObjectWithManyReservedSlots, 1, 0,
9654 "checkObjectWithManyReservedSlots(obj)",
9655 " Checks the reserved slots set by newObjectWithManyReservedSlots still hold the expected\n"
9656 " values."),
9658 JS_FN_HELP("getWatchtowerLog", GetWatchtowerLog, 0, 0,
9659 "getWatchtowerLog()",
9660 " Returns the Watchtower log recording object changes for objects for which\n"
9661 " addWatchtowerTarget was called. The internal log is cleared. The return\n"
9662 " value is an array of plain objects with the following properties:\n"
9663 " - kind: a string describing the kind of mutation, for example \"add-prop\"\n"
9664 " - object: the object being mutated\n"
9665 " - extra: an extra value, for example the name of the property being added"),
9667 JS_FN_HELP("addWatchtowerTarget", AddWatchtowerTarget, 1, 0,
9668 "addWatchtowerTarget(object)",
9669 " Invoke the watchtower callback for changes to this object."),
9671 JS_FN_HELP("newString", NewString, 2, 0,
9672 "newString(str[, options])",
9673 " Copies str's chars and returns a new string. Valid options:\n"
9674 " \n"
9675 " - tenured: allocate directly into the tenured heap.\n"
9676 " \n"
9677 " - twoByte: create a \"two byte\" string, not a latin1 string, regardless of the\n"
9678 " input string's characters. Latin1 will be used by default if possible\n"
9679 " (again regardless of the input string.)\n"
9680 " \n"
9681 " - newStringBuffer: create a new string that uses a refcounted StringBuffer for\n"
9682 " the characters.\n"
9683 " \n"
9684 " - shareStringBuffer: create a new string that shares str's StringBuffer.\n"
9685 " \n"
9686 " - external: create an external string. External strings are always twoByte and\n"
9687 " tenured.\n"
9688 " \n"
9689 " - maybeExternal: create an external string, unless the data fits within an\n"
9690 " inline string. Inline strings may be nursery-allocated."),
9692 JS_FN_HELP("newDependentString", NewDependentString, 2, 0,
9693 "newDependentString(str, indexStart[, indexEnd] [, options])",
9694 " Essentially the same as str.substring() but insist on\n"
9695 " creating a dependent string and failing if not. Also has options to\n"
9696 " control the heap the string object is allocated into:\n"
9697 " \n"
9698 " - tenured: if true, allocate in the tenured heap or throw. If false,\n"
9699 " allocate in the nursery or throw."),
9701 JS_FN_HELP("ensureLinearString", EnsureLinearString, 1, 0,
9702 "ensureLinearString(str)",
9703 " Ensures str is a linear (non-rope) string and returns it."),
9705 JS_FN_HELP("representativeStringArray", RepresentativeStringArray, 0, 0,
9706 "representativeStringArray()",
9707 " Returns an array of strings that represent the various internal string\n"
9708 " types and character encodings."),
9710 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
9712 JS_FN_HELP("oomThreadTypes", OOMThreadTypes, 0, 0,
9713 "oomThreadTypes()",
9714 " Get the number of thread types that can be used as an argument for\n"
9715 " oomAfterAllocations() and oomAtAllocation()."),
9717 JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 2, 0,
9718 "oomAfterAllocations(count [,threadType])",
9719 " After 'count' js_malloc memory allocations, fail every following allocation\n"
9720 " (return nullptr). The optional thread type limits the effect to the\n"
9721 " specified type of helper thread."),
9723 JS_FN_HELP("oomAtAllocation", OOMAtAllocation, 2, 0,
9724 "oomAtAllocation(count [,threadType])",
9725 " After 'count' js_malloc memory allocations, fail the next allocation\n"
9726 " (return nullptr). The optional thread type limits the effect to the\n"
9727 " specified type of helper thread."),
9729 JS_FN_HELP("resetOOMFailure", ResetOOMFailure, 0, 0,
9730 "resetOOMFailure()",
9731 " Remove the allocation failure scheduled by either oomAfterAllocations() or\n"
9732 " oomAtAllocation() and return whether any allocation had been caused to fail."),
9734 JS_FN_HELP("oomTest", OOMTest, 0, 0,
9735 "oomTest(function, [expectExceptionOnFailure = true | options])",
9736 " Test that the passed function behaves correctly under OOM conditions by\n"
9737 " repeatedly executing it and simulating allocation failure at successive\n"
9738 " allocations until the function completes without seeing a failure.\n"
9739 " By default this tests that an exception is raised if execution fails, but\n"
9740 " this can be disabled by passing false as the optional second parameter.\n"
9741 " This is also disabled when --fuzzing-safe is specified.\n"
9742 " Alternatively an object can be passed to set the following options:\n"
9743 " expectExceptionOnFailure: bool - as described above.\n"
9744 " keepFailing: bool - continue to fail after first simulated failure.\n"
9745 "\n"
9746 " WARNING: By design, oomTest assumes the test-function follows the same\n"
9747 " code path each time it is called, right up to the point where OOM occurs.\n"
9748 " If on iteration 70 it finishes and caches a unit of work that saves 65\n"
9749 " allocations the next time we run, then the subsequent 65 allocation\n"
9750 " points will go untested.\n"
9751 "\n"
9752 " Things in this category include lazy parsing and baseline compilation,\n"
9753 " so it is very easy to accidentally write an oomTest that only tests one\n"
9754 " or the other of those, and not the functionality you meant to test!\n"
9755 " To avoid lazy parsing, call the test function once first before passing\n"
9756 " it to oomTest. The jits can be disabled via the test harness.\n"),
9758 JS_FN_HELP("stackTest", StackTest, 0, 0,
9759 "stackTest(function, [expectExceptionOnFailure = true])",
9760 " This function behaves exactly like oomTest with the difference that\n"
9761 " instead of simulating regular OOM conditions, it simulates the engine\n"
9762 " running out of stack space (failing recursion check).\n"
9763 "\n"
9764 " See the WARNING in help('oomTest').\n"),
9766 JS_FN_HELP("interruptTest", InterruptTest, 0, 0,
9767 "interruptTest(function)",
9768 " This function simulates interrupts similar to how oomTest simulates OOM conditions."
9769 "\n"
9770 " See the WARNING in help('oomTest').\n"),
9772 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
9774 JS_FN_HELP("newRope", NewRope, 3, 0,
9775 "newRope(left, right[, options])",
9776 " Creates a rope with the given left/right strings.\n"
9777 " Available options:\n"
9778 " nursery: bool - force the string to be created in/out of the nursery, if possible.\n"),
9780 JS_FN_HELP("isRope", IsRope, 1, 0,
9781 "isRope(str)",
9782 " Returns true if the parameter is a rope"),
9784 JS_FN_HELP("settlePromiseNow", SettlePromiseNow, 1, 0,
9785 "settlePromiseNow(promise)",
9786 " 'Settle' a 'promise' immediately. This just marks the promise as resolved\n"
9787 " with a value of `undefined` and causes the firing of any onPromiseSettled\n"
9788 " hooks set on Debugger instances that are observing the given promise's\n"
9789 " global as a debuggee."),
9790 JS_FN_HELP("getWaitForAllPromise", GetWaitForAllPromise, 1, 0,
9791 "getWaitForAllPromise(densePromisesArray)",
9792 " Calls the 'GetWaitForAllPromise' JSAPI function and returns the result\n"
9793 " Promise."),
9794 JS_FN_HELP("resolvePromise", ResolvePromise, 2, 0,
9795 "resolvePromise(promise, resolution)",
9796 " Resolve a Promise by calling the JSAPI function JS::ResolvePromise."),
9797 JS_FN_HELP("rejectPromise", RejectPromise, 2, 0,
9798 "rejectPromise(promise, reason)",
9799 " Reject a Promise by calling the JSAPI function JS::RejectPromise."),
9801 JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
9802 "makeFinalizeObserver()",
9803 " Get a special object whose finalization increases the counter returned\n"
9804 " by the finalizeCount function."),
9806 JS_FN_HELP("finalizeCount", FinalizeCount, 0, 0,
9807 "finalizeCount()",
9808 " Return the current value of the finalization counter that is incremented\n"
9809 " each time an object returned by the makeFinalizeObserver is finalized."),
9811 JS_FN_HELP("resetFinalizeCount", ResetFinalizeCount, 0, 0,
9812 "resetFinalizeCount()",
9813 " Reset the value returned by finalizeCount()."),
9815 JS_FN_HELP("gcPreserveCode", GCPreserveCode, 0, 0,
9816 "gcPreserveCode()",
9817 " Preserve JIT code during garbage collections."),
9819 #ifdef JS_GC_ZEAL
9820 JS_FN_HELP("gczeal", GCZeal, 2, 0,
9821 "gczeal([mode, [frequency]])",
9822 gc::ZealModeHelpText),
9824 JS_FN_HELP("unsetgczeal", UnsetGCZeal, 2, 0,
9825 "unsetgczeal(mode)",
9826 " Turn off a single zeal mode set with gczeal() and don't finish any ongoing\n"
9827 " collection that may be happening."),
9829 JS_FN_HELP("schedulegc", ScheduleGC, 1, 0,
9830 "schedulegc([num])",
9831 " If num is given, schedule a GC after num allocations.\n"
9832 " Returns the number of allocations before the next trigger."),
9834 JS_FN_HELP("selectforgc", SelectForGC, 0, 0,
9835 "selectforgc(obj1, obj2, ...)",
9836 " Schedule the given objects to be marked in the next GC slice."),
9838 JS_FN_HELP("verifyprebarriers", VerifyPreBarriers, 0, 0,
9839 "verifyprebarriers()",
9840 " Start or end a run of the pre-write barrier verifier."),
9842 JS_FN_HELP("verifypostbarriers", VerifyPostBarriers, 0, 0,
9843 "verifypostbarriers()",
9844 " Does nothing (the post-write barrier verifier has been remove)."),
9846 JS_FN_HELP("currentgc", CurrentGC, 0, 0,
9847 "currentgc()",
9848 " Report various information about the currently running incremental GC,\n"
9849 " if one is running."),
9851 JS_FN_HELP("deterministicgc", DeterministicGC, 1, 0,
9852 "deterministicgc(true|false)",
9853 " If true, only allow determinstic GCs to run."),
9855 JS_FN_HELP("dumpGCArenaInfo", DumpGCArenaInfo, 0, 0,
9856 "dumpGCArenaInfo()",
9857 " Prints information about the different GC things and how they are arranged\n"
9858 " in arenas.\n"),
9860 JS_FN_HELP("setMarkStackLimit", SetMarkStackLimit, 1, 0,
9861 "markStackLimit(limit)",
9862 " Sets a limit on the number of words used for the mark stack. Used to test OOM"
9863 " handling during marking.\n"),
9865 #endif
9867 JS_FN_HELP("gcstate", GCState, 0, 0,
9868 "gcstate([obj])",
9869 " Report the global GC state, or the GC state for the zone containing |obj|."),
9871 JS_FN_HELP("schedulezone", ScheduleZoneForGC, 1, 0,
9872 "schedulezone([obj | string])",
9873 " If obj is given, schedule a GC of obj's zone.\n"
9874 " If string is given, schedule a GC of the string's zone if possible."),
9876 JS_FN_HELP("startgc", StartGC, 1, 0,
9877 "startgc([n [, 'shrinking']])",
9878 " Start an incremental GC and run a slice that processes about n objects.\n"
9879 " If 'shrinking' is passesd as the optional second argument, perform a\n"
9880 " shrinking GC rather than a normal GC. If no zones have been selected with\n"
9881 " schedulezone(), a full GC will be performed."),
9883 JS_FN_HELP("finishgc", FinishGC, 0, 0,
9884 "finishgc()",
9885 " Finish an in-progress incremental GC, if none is running then do nothing."),
9887 JS_FN_HELP("gcslice", GCSlice, 1, 0,
9888 "gcslice([n [, options]])",
9889 " Start or continue an an incremental GC, running a slice that processes\n"
9890 " about n objects. Takes an optional options object, which may contain the\n"
9891 " following properties:\n"
9892 " dontStart: do not start a new incremental GC if one is not already\n"
9893 " running"),
9895 JS_FN_HELP("abortgc", AbortGC, 1, 0,
9896 "abortgc()",
9897 " Abort the current incremental GC."),
9899 JS_FN_HELP("setMallocMaxDirtyPageModifier", SetMallocMaxDirtyPageModifier, 1, 0,
9900 "setMallocMaxDirtyPageModifier(value)",
9901 " Change the maximum size of jemalloc's page cache. The value should be between\n"
9902 " -5 and 16 (inclusive). See moz_set_max_dirty_page_modifier.\n"),
9904 JS_FN_HELP("fullcompartmentchecks", FullCompartmentChecks, 1, 0,
9905 "fullcompartmentchecks(true|false)",
9906 " If true, check for compartment mismatches before every GC."),
9908 JS_FN_HELP("nondeterministicGetWeakMapKeys", NondeterministicGetWeakMapKeys, 1, 0,
9909 "nondeterministicGetWeakMapKeys(weakmap)",
9910 " Return an array of the keys in the given WeakMap."),
9912 JS_FN_HELP("internalConst", InternalConst, 1, 0,
9913 "internalConst(name)",
9914 " Query an internal constant for the engine. See InternalConst source for\n"
9915 " the list of constant names."),
9917 JS_FN_HELP("isProxy", IsProxy, 1, 0,
9918 "isProxy(obj)",
9919 " If true, obj is a proxy of some sort"),
9921 JS_FN_HELP("dumpHeap", DumpHeap, 1, 0,
9922 "dumpHeap([filename])",
9923 " Dump reachable and unreachable objects to the named file, or to stdout. Objects\n"
9924 " in the nursery are ignored, so if you wish to include them, consider calling\n"
9925 " minorgc() first."),
9927 JS_FN_HELP("terminate", Terminate, 0, 0,
9928 "terminate()",
9929 " Terminate JavaScript execution, as if we had run out of\n"
9930 " memory or been terminated by the slow script dialog."),
9932 JS_FN_HELP("readGeckoProfilingStack", ReadGeckoProfilingStack, 0, 0,
9933 "readGeckoProfilingStack()",
9934 " Reads the JIT/Wasm stack using ProfilingFrameIterator. Skips non-JIT/Wasm frames."),
9936 JS_FN_HELP("readGeckoInterpProfilingStack", ReadGeckoInterpProfilingStack, 0, 0,
9937 "readGeckoInterpProfilingStack()",
9938 " Reads the C++ interpreter profiling stack. Skips JIT/Wasm frames."),
9940 JS_FN_HELP("enableOsiPointRegisterChecks", EnableOsiPointRegisterChecks, 0, 0,
9941 "enableOsiPointRegisterChecks()",
9942 " Emit extra code to verify live regs at the start of a VM call are not\n"
9943 " modified before its OsiPoint."),
9945 JS_FN_HELP("displayName", DisplayName, 1, 0,
9946 "displayName(fn)",
9947 " Gets the display name for a function, which can possibly be a guessed or\n"
9948 " inferred name based on where the function was defined. This can be\n"
9949 " different from the 'name' property on the function."),
9951 JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable, 0, 0,
9952 "isAsmJSCompilationAvailable",
9953 " Returns whether asm.js compilation is currently available or whether it is disabled\n"
9954 " (e.g., by the debugger)."),
9956 JS_FN_HELP("getJitCompilerOptions", GetJitCompilerOptions, 0, 0,
9957 "getJitCompilerOptions()",
9958 " Return an object describing some of the JIT compiler options.\n"),
9960 JS_FN_HELP("isAsmJSModule", IsAsmJSModule, 1, 0,
9961 "isAsmJSModule(fn)",
9962 " Returns whether the given value is a function containing \"use asm\" that has been\n"
9963 " validated according to the asm.js spec."),
9965 JS_FN_HELP("isAsmJSFunction", IsAsmJSFunction, 1, 0,
9966 "isAsmJSFunction(fn)",
9967 " Returns whether the given value is a nested function in an asm.js module that has been\n"
9968 " both compile- and link-time validated."),
9970 JS_FN_HELP("isAvxPresent", IsAvxPresent, 0, 0,
9971 "isAvxPresent([minVersion])",
9972 " Returns whether AVX is present and enabled. If minVersion specified,\n"
9973 " use 1 - to check if AVX is enabled (default), 2 - if AVX2 is enabled."),
9975 JS_FN_HELP("wasmIsSupported", WasmIsSupported, 0, 0,
9976 "wasmIsSupported()",
9977 " Returns a boolean indicating whether WebAssembly is supported on the current device."),
9979 JS_FN_HELP("wasmIsSupportedByHardware", WasmIsSupportedByHardware, 0, 0,
9980 "wasmIsSupportedByHardware()",
9981 " Returns a boolean indicating whether WebAssembly is supported on the current hardware (regardless of whether we've enabled support)."),
9983 JS_FN_HELP("wasmDebuggingEnabled", WasmDebuggingEnabled, 0, 0,
9984 "wasmDebuggingEnabled()",
9985 " Returns a boolean indicating whether WebAssembly debugging is supported on the current device;\n"
9986 " returns false also if WebAssembly is not supported"),
9988 JS_FN_HELP("wasmStreamingEnabled", WasmStreamingEnabled, 0, 0,
9989 "wasmStreamingEnabled()",
9990 " Returns a boolean indicating whether WebAssembly caching is supported by the runtime."),
9992 JS_FN_HELP("wasmCachingEnabled", WasmCachingEnabled, 0, 0,
9993 "wasmCachingEnabled()",
9994 " Returns a boolean indicating whether WebAssembly caching is supported by the runtime."),
9996 JS_FN_HELP("wasmHugeMemorySupported", WasmHugeMemorySupported, 0, 0,
9997 "wasmHugeMemorySupported()",
9998 " Returns a boolean indicating whether WebAssembly supports using a large"
9999 " virtual memory reservation in order to elide bounds checks on this platform."),
10001 JS_FN_HELP("wasmMaxMemoryPages", WasmMaxMemoryPages, 1, 0,
10002 "wasmMaxMemoryPages(indexType)",
10003 " Returns an int with the maximum number of pages that can be allocated to a memory."
10004 " This is an implementation artifact that does depend on the index type, the hardware,"
10005 " the operating system, the build configuration, and flags. The result is constant for"
10006 " a given combination of those; there is no guarantee that that size allocation will"
10007 " always succeed, only that it can succeed in principle. The indexType is a string,"
10008 " 'i32' or 'i64'."),
10010 #define WASM_FEATURE(NAME, ...) \
10011 JS_FN_HELP("wasm" #NAME "Enabled", Wasm##NAME##Enabled, 0, 0, \
10012 "wasm" #NAME "Enabled()", \
10013 " Returns a boolean indicating whether the WebAssembly " #NAME " proposal is enabled."),
10014 JS_FOR_WASM_FEATURES(WASM_FEATURE)
10015 #undef WASM_FEATURE
10017 JS_FN_HELP("wasmThreadsEnabled", WasmThreadsEnabled, 0, 0,
10018 "wasmThreadsEnabled()",
10019 " Returns a boolean indicating whether the WebAssembly threads proposal is\n"
10020 " supported on the current device."),
10022 JS_FN_HELP("wasmSimdEnabled", WasmSimdEnabled, 0, 0,
10023 "wasmSimdEnabled()",
10024 " Returns a boolean indicating whether WebAssembly SIMD proposal is\n"
10025 " supported by the current device."),
10027 #if defined(ENABLE_WASM_SIMD) && defined(DEBUG)
10028 JS_FN_HELP("wasmSimdAnalysis", WasmSimdAnalysis, 1, 0,
10029 "wasmSimdAnalysis(...)",
10030 " Unstable API for white-box testing.\n"),
10031 #endif
10033 JS_FN_HELP("wasmGlobalFromArrayBuffer", WasmGlobalFromArrayBuffer, 2, 0,
10034 "wasmGlobalFromArrayBuffer(type, arrayBuffer)",
10035 " Create a WebAssembly.Global object from a provided ArrayBuffer. The type\n"
10036 " must be POD (i32, i64, f32, f64, v128). The buffer must be the same\n"
10037 " size as the type in bytes.\n"),
10038 JS_FN_HELP("wasmGlobalExtractLane", WasmGlobalExtractLane, 3, 0,
10039 "wasmGlobalExtractLane(global, laneInterp, laneIndex)",
10040 " Extract a lane from a WebAssembly.Global object that contains a v128 value\n"
10041 " and return it as a new WebAssembly.Global object of the appropriate type.\n"
10042 " The supported laneInterp values are i32x4, i64x2, f32x4, and\n"
10043 " f64x2.\n"),
10044 JS_FN_HELP("wasmGlobalsEqual", WasmGlobalsEqual, 2, 0,
10045 "wasmGlobalsEqual(globalA, globalB)",
10046 " Compares two WebAssembly.Global objects for if their types and values are\n"
10047 " equal. Mutability is not compared. Floating point values are compared for\n"
10048 " bitwise equality, not IEEE 754 equality.\n"),
10049 JS_FN_HELP("wasmGlobalIsNaN", WasmGlobalIsNaN, 2, 0,
10050 "wasmGlobalIsNaN(global, flavor)",
10051 " Compares a floating point WebAssembly.Global object for if its value is a\n"
10052 " specific NaN flavor. Valid flavors are `arithmetic_nan` and `canonical_nan`.\n"),
10053 JS_FN_HELP("wasmGlobalToString", WasmGlobalToString, 1, 0,
10054 "wasmGlobalToString(global)",
10055 " Returns a debug representation of the contents of a WebAssembly.Global\n"
10056 " object.\n"),
10057 JS_FN_HELP("wasmLosslessInvoke", WasmLosslessInvoke, 1, 0,
10058 "wasmLosslessInvoke(wasmFunc, args...)",
10059 " Invokes the provided WebAssembly function using a modified conversion\n"
10060 " function that allows providing a param as a WebAssembly.Global and\n"
10061 " returning a result as a WebAssembly.Global.\n"),
10063 JS_FN_HELP("wasmCompilersPresent", WasmCompilersPresent, 0, 0,
10064 "wasmCompilersPresent()",
10065 " Returns a string indicating the present wasm compilers: a comma-separated list\n"
10066 " of 'baseline', 'ion'. A compiler is present in the executable if it is compiled\n"
10067 " in and can generate code for the current architecture."),
10069 JS_FN_HELP("wasmCompileMode", WasmCompileMode, 0, 0,
10070 "wasmCompileMode()",
10071 " Returns a string indicating the available wasm compilers: 'baseline', 'ion',\n"
10072 " 'baseline+ion', or 'none'. A compiler is available if it is present in the\n"
10073 " executable and not disabled by switches or runtime conditions. At most one\n"
10074 " baseline and one optimizing compiler can be available."),
10076 JS_FN_HELP("wasmBaselineDisabledByFeatures", WasmBaselineDisabledByFeatures, 0, 0,
10077 "wasmBaselineDisabledByFeatures()",
10078 " If some feature is enabled at compile-time or run-time that prevents baseline\n"
10079 " from being used then this returns a truthy string describing the features that\n."
10080 " are disabling it. Otherwise it returns false."),
10082 JS_FN_HELP("wasmIonDisabledByFeatures", WasmIonDisabledByFeatures, 0, 0,
10083 "wasmIonDisabledByFeatures()",
10084 " If some feature is enabled at compile-time or run-time that prevents Ion\n"
10085 " from being used then this returns a truthy string describing the features that\n."
10086 " are disabling it. Otherwise it returns false."),
10088 JS_FN_HELP("wasmFunctionTier", WasmFunctionTier, 1, 0,
10089 "wasmFunctionTier(wasmFunc)\n",
10090 " Returns the best compiled tier for a function. Either 'baseline' or 'optimized'."),
10092 JS_FN_HELP("wasmHasTier2CompilationCompleted", WasmHasTier2CompilationCompleted, 1, 0,
10093 "wasmHasTier2CompilationCompleted(module)",
10094 " Returns a boolean indicating whether a given module has finished compiled code for tier2. \n"
10095 "This will return true early if compilation isn't two-tiered. "),
10097 JS_FN_HELP("wasmLoadedFromCache", WasmLoadedFromCache, 1, 0,
10098 "wasmLoadedFromCache(module)",
10099 " Returns a boolean indicating whether a given module was deserialized directly from a\n"
10100 " cache (as opposed to compiled from bytecode)."),
10102 JS_FN_HELP("wasmBuiltinI8VecMul", WasmBuiltinI8VecMul, 0, 0,
10103 "wasmBuiltinI8VecMul()",
10104 " Returns a module that implements an i8 vector pairwise multiplication intrinsic."),
10106 #ifdef ENABLE_WASM_GC
10107 JS_FN_HELP("wasmGcReadField", WasmGcReadField, 2, 0,
10108 "wasmGcReadField(obj, index)",
10109 " Gets a field of a WebAssembly GC struct or array."),
10111 JS_FN_HELP("wasmGcArrayLength", WasmGcArrayLength, 1, 0,
10112 "wasmGcArrayLength(arr)",
10113 " Gets the length of a WebAssembly GC array."),
10114 #endif // ENABLE_WASM_GC
10116 #ifdef ENABLE_WASM_BRANCH_HINTING
10117 JS_FN_HELP("wasmParsedBranchHints", WasmParsedBranchHints, 1, 0,
10118 "wasmParsedBranchHints(module)",
10119 " Returns a boolean indicating whether a given module has successfully parsed a\n"
10120 " custom branch hinting section."),
10122 #endif // ENABLE_WASM_BRANCH_HINTING
10124 JS_FN_HELP("largeArrayBufferSupported", LargeArrayBufferSupported, 0, 0,
10125 "largeArrayBufferSupported()",
10126 " Returns true if array buffers larger than 2GB can be allocated."),
10128 JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0,
10129 "isLazyFunction(fun)",
10130 " True if fun is a lazy JSFunction."),
10132 JS_FN_HELP("isRelazifiableFunction", IsRelazifiableFunction, 1, 0,
10133 "isRelazifiableFunction(fun)",
10134 " True if fun is a JSFunction with a relazifiable JSScript."),
10136 JS_FN_HELP("hasSameBytecodeData", HasSameBytecodeData, 2, 0,
10137 "hasSameBytecodeData(fun1, fun2)",
10138 " True if fun1 and fun2 share the same copy of bytecode data. This will\n"
10139 " delazify the function if necessary."),
10141 JS_FN_HELP("enableShellAllocationMetadataBuilder", EnableShellAllocationMetadataBuilder, 0, 0,
10142 "enableShellAllocationMetadataBuilder()",
10143 " Use ShellAllocationMetadataBuilder to supply metadata for all newly created objects."),
10145 JS_FN_HELP("getAllocationMetadata", GetAllocationMetadata, 1, 0,
10146 "getAllocationMetadata(obj)",
10147 " Get the metadata for an object."),
10149 JS_INLINABLE_FN_HELP("bailout", testingFunc_bailout, 0, 0, TestBailout,
10150 "bailout()",
10151 " Force a bailout out of ionmonkey (if running in ionmonkey)."),
10153 JS_FN_HELP("bailAfter", testingFunc_bailAfter, 1, 0,
10154 "bailAfter(number)",
10155 " Start a counter to bail once after passing the given amount of possible bailout positions in\n"
10156 " ionmonkey.\n"),
10158 JS_FN_HELP("invalidate", testingFunc_invalidate, 0, 0,
10159 "invalidate()",
10160 " Force an immediate invalidation (if running in Warp)."),
10162 JS_FN_HELP("inJit", testingFunc_inJit, 0, 0,
10163 "inJit()",
10164 " Returns true when called within (jit-)compiled code. When jit compilation is disabled this\n"
10165 " function returns an error string. This function returns false in all other cases.\n"
10166 " Depending on truthiness, you should continue to wait for compilation to happen or stop execution.\n"),
10168 JS_FN_HELP("inIon", testingFunc_inIon, 0, 0,
10169 "inIon()",
10170 " Returns true when called within ion. When ion is disabled or when compilation is abnormally\n"
10171 " slow to start, this function returns an error string. Otherwise, this function returns false.\n"
10172 " This behaviour ensures that a falsy value means that we are not in ion, but expect a\n"
10173 " compilation to occur in the future. Conversely, a truthy value means that we are either in\n"
10174 " ion or that there is litle or no chance of ion ever compiling the current script."),
10176 JS_FN_HELP("assertJitStackInvariants", TestingFunc_assertJitStackInvariants, 0, 0,
10177 "assertJitStackInvariants()",
10178 " Iterates the Jit stack and check that stack invariants hold."),
10180 JS_FN_HELP("setIonCheckGraphCoherency", SetIonCheckGraphCoherency, 1, 0,
10181 "setIonCheckGraphCoherency(bool)",
10182 " Set whether Ion should perform graph consistency (DEBUG-only) assertions. These assertions\n"
10183 " are valuable and should be generally enabled, however they can be very expensive for large\n"
10184 " (wasm) programs."),
10186 JS_FN_HELP("serialize", testingFunc_serialize, 1, 0,
10187 "serialize(data, [transferables, [policy]])",
10188 " Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
10189 " clone buffer object. 'policy' may be an options hash. Valid keys:\n"
10190 " 'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n"
10191 " to specify whether SharedArrayBuffers may be serialized.\n"
10192 " 'scope' - SameProcess, DifferentProcess, or\n"
10193 " DifferentProcessForIndexedDB. Determines how some values will be\n"
10194 " serialized. Clone buffers may only be deserialized with a compatible\n"
10195 " scope. NOTE - For DifferentProcess/DifferentProcessForIndexedDB,\n"
10196 " must also set SharedArrayBuffer:'deny' if data contains any shared memory\n"
10197 " object."),
10199 JS_FN_HELP("deserialize", Deserialize, 1, 0,
10200 "deserialize(clonebuffer[, opts])",
10201 " Deserialize data generated by serialize. 'opts' may be an options hash.\n"
10202 " Valid keys:\n"
10203 " 'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n"
10204 " to specify whether SharedArrayBuffers may be serialized.\n"
10205 " 'scope', which limits the clone buffers that are considered\n"
10206 " valid. Allowed values: ''SameProcess', 'DifferentProcess',\n"
10207 " and 'DifferentProcessForIndexedDB'. So for example, a\n"
10208 " DifferentProcessForIndexedDB clone buffer may be deserialized in any scope, but\n"
10209 " a SameProcess clone buffer cannot be deserialized in a\n"
10210 " DifferentProcess scope."),
10212 JS_FN_HELP("detachArrayBuffer", DetachArrayBuffer, 1, 0,
10213 "detachArrayBuffer(buffer)",
10214 " Detach the given ArrayBuffer object from its memory, i.e. as if it\n"
10215 " had been transferred to a WebWorker."),
10217 JS_FN_HELP("makeSerializable", MakeSerializable, 1, 0,
10218 "makeSerializable(numeric id, [behavior])",
10219 " Make a custom serializable, transferable object. It will have a single accessor\n"
10220 " obj.log that will give a history of all operations on all such objects in the\n"
10221 " current thread as an array [id, action, id, action, ...] where the id\n"
10222 " is the number passed into this function, and the action is one of:\n"
10223 " ? - the canTransfer() hook was called.\n"
10224 " w - the write() hook was called.\n"
10225 " W - the writeTransfer() hook was called.\n"
10226 " R - the readTransfer() hook was called.\n"
10227 " r - the read() hook was called.\n"
10228 " F - the freeTransfer() hook was called.\n"
10229 " The `behavior` parameter can be used to force a failure during processing:\n"
10230 " 1 - fail during readTransfer() hook\n"
10231 " 2 - fail during read() hook\n"
10232 " Set the log to null to clear it."),
10234 JS_FN_HELP("ensureNonInline", EnsureNonInline, 1, 0,
10235 "ensureNonInline(view or buffer)",
10236 " Ensure that the memory for the given ArrayBuffer or ArrayBufferView\n"
10237 " is not inline."),
10239 JS_FN_HELP("pinArrayBufferOrViewLength", PinArrayBufferOrViewLength, 1, 0,
10240 "pinArrayBufferOrViewLength(view or buffer[, pin])",
10241 " Prevent or allow (if `pin` is false) changes to the length of the given\n"
10242 " ArrayBuffer or ArrayBufferView. `pin` defaults to true."),
10244 JS_FN_HELP("JSONStringify", JSONStringify, 4, 0,
10245 "JSONStringify(value, behavior)",
10246 " Same as JSON.stringify(value), but allows setting behavior:\n"
10247 " Normal: the default\n"
10248 " FastOnly: throw if the fast path bails\n"
10249 " SlowOnly: skip the fast path entirely\n"
10250 " Compare: run both the fast and slow paths and compare the result. Crash if\n"
10251 " they do not match. If the fast path bails, no comparison is done."),
10253 JS_FN_HELP("helperThreadCount", HelperThreadCount, 0, 0,
10254 "helperThreadCount()",
10255 " Returns the number of helper threads available for off-thread tasks."),
10257 JS_FN_HELP("createShapeSnapshot", CreateShapeSnapshot, 1, 0,
10258 "createShapeSnapshot(obj)",
10259 " Returns an object containing a shape snapshot for use with\n"
10260 " checkShapeSnapshot.\n"),
10262 JS_FN_HELP("checkShapeSnapshot", CheckShapeSnapshot, 2, 0,
10263 "checkShapeSnapshot(snapshot, [obj])",
10264 " Check shape invariants based on the given snapshot and optional object.\n"
10265 " If there's no object argument, the snapshot's object is used.\n"),
10267 JS_FN_HELP("enableShapeConsistencyChecks", EnableShapeConsistencyChecks, 0, 0,
10268 "enableShapeConsistencyChecks()",
10269 " Enable some slow Shape assertions.\n"),
10271 JS_FN_HELP("reportOutOfMemory", ReportOutOfMemory, 0, 0,
10272 "reportOutOfMemory()",
10273 " Report OOM, then clear the exception and return undefined. For crash testing."),
10275 JS_FN_HELP("throwOutOfMemory", ThrowOutOfMemory, 0, 0,
10276 "throwOutOfMemory()",
10277 " Throw out of memory exception, for OOM handling testing."),
10279 JS_FN_HELP("reportLargeAllocationFailure", ReportLargeAllocationFailure, 0, 0,
10280 "reportLargeAllocationFailure([bytes])",
10281 " Call the large allocation failure callback, as though a large malloc call failed,\n"
10282 " then return undefined. In Gecko, this sends a memory pressure notification, which\n"
10283 " can free up some memory."),
10285 JS_FN_HELP("findPath", FindPath, 2, 0,
10286 "findPath(start, target)",
10287 " Return an array describing one of the shortest paths of GC heap edges from\n"
10288 " |start| to |target|, or |undefined| if |target| is unreachable from |start|.\n"
10289 " Each element of the array is either of the form:\n"
10290 " { node: <object or string>, edge: <string describing edge from node> }\n"
10291 " if the node is a JavaScript object or value; or of the form:\n"
10292 " { type: <string describing node>, edge: <string describing edge> }\n"
10293 " if the node is some internal thing that is not a proper JavaScript value\n"
10294 " (like a shape or a scope chain element). The destination of the i'th array\n"
10295 " element's edge is the node of the i+1'th array element; the destination of\n"
10296 " the last array element is implicitly |target|.\n"),
10298 JS_FN_HELP("wasmMetadataAnalysis", wasmMetadataAnalysis, 1, 0,
10299 "wasmMetadataAnalysis(wasmObject)",
10300 " Prints an analysis of the size of metadata on this wasm object.\n"),
10302 JS_FN_HELP("sharedMemoryEnabled", SharedMemoryEnabled, 0, 0,
10303 "sharedMemoryEnabled()",
10304 " Return true if SharedArrayBuffer and Atomics are enabled"),
10306 JS_FN_HELP("sharedArrayRawBufferRefcount", SharedArrayRawBufferRefcount, 0, 0,
10307 "sharedArrayRawBufferRefcount(sab)",
10308 " Return the reference count of the SharedArrayRawBuffer object held by sab"),
10310 #ifdef NIGHTLY_BUILD
10311 JS_FN_HELP("objectAddress", ObjectAddress, 1, 0,
10312 "objectAddress(obj)",
10313 " Return the current address of the object. For debugging only--this\n"
10314 " address may change during a moving GC."),
10316 JS_FN_HELP("sharedAddress", SharedAddress, 1, 0,
10317 "sharedAddress(obj)",
10318 " Return the address of the shared storage of a SharedArrayBuffer."),
10319 #endif
10321 JS_FN_HELP("hasInvalidatedTeleporting", HasInvalidatedTeleporting, 1, 0,
10322 "hasInvalidatedTeleporting(obj)",
10323 " Return true if the shape teleporting optimization has been disabled for |obj|."),
10325 JS_FN_HELP("evalReturningScope", EvalReturningScope, 1, 0,
10326 "evalReturningScope(scriptStr, [global])",
10327 " Evaluate the script in a new scope and return the scope.\n"
10328 " If |global| is present, clone the script to |global| before executing."),
10330 JS_FN_HELP("backtrace", DumpBacktrace, 1, 0,
10331 "backtrace()",
10332 " Dump out a brief backtrace."),
10334 JS_FN_HELP("getBacktrace", GetBacktrace, 1, 0,
10335 "getBacktrace([options])",
10336 " Return the current stack as a string. Takes an optional options object,\n"
10337 " which may contain any or all of the boolean properties:\n"
10338 " options.args - show arguments to each function\n"
10339 " options.locals - show local variables in each frame\n"
10340 " options.thisprops - show the properties of the 'this' object of each frame\n"),
10342 JS_FN_HELP("byteSize", ByteSize, 1, 0,
10343 "byteSize(value)",
10344 " Return the size in bytes occupied by |value|, or |undefined| if value\n"
10345 " is not allocated in memory.\n"),
10347 JS_FN_HELP("byteSizeOfScript", ByteSizeOfScript, 1, 0,
10348 "byteSizeOfScript(f)",
10349 " Return the size in bytes occupied by the function |f|'s JSScript.\n"),
10351 JS_FN_HELP("setImmutablePrototype", SetImmutablePrototype, 1, 0,
10352 "setImmutablePrototype(obj)",
10353 " Try to make obj's [[Prototype]] immutable, such that subsequent attempts to\n"
10354 " change it will fail. Return true if obj's [[Prototype]] was successfully made\n"
10355 " immutable (or if it already was immutable), false otherwise. Throws in case\n"
10356 " of internal error, or if the operation doesn't even make sense (for example,\n"
10357 " because the object is a revoked proxy)."),
10359 JS_FN_HELP("allocationMarker", AllocationMarker, 0, 0,
10360 "allocationMarker([options])",
10361 " Return a freshly allocated object whose [[Class]] name is\n"
10362 " \"AllocationMarker\". Such objects are allocated only by calls\n"
10363 " to this function, never implicitly by the system, making them\n"
10364 " suitable for use in allocation tooling tests. Takes an optional\n"
10365 " options object which may contain the following properties:\n"
10366 " * nursery: bool, whether to allocate the object in the nursery\n"),
10368 JS_FN_HELP("setGCCallback", SetGCCallback, 1, 0,
10369 "setGCCallback({action:\"...\", options...})",
10370 " Set the GC callback. action may be:\n"
10371 " 'minorGC' - run a nursery collection\n"
10372 " 'majorGC' - run a major collection, nesting up to a given 'depth'\n"),
10374 #ifdef DEBUG
10375 JS_FN_HELP("enqueueMark", EnqueueMark, 1, 0,
10376 "enqueueMark(obj|string)",
10377 " Add an object to the queue of objects to mark at the beginning every GC. (Note\n"
10378 " that the objects will actually be marked at the beginning of every slice, but\n"
10379 " after the first slice they will already be marked so nothing will happen.)\n"
10380 " \n"
10381 " Instead of an object, a few magic strings may be used:\n"
10382 " 'yield' - cause the current marking slice to end, as if the mark budget were\n"
10383 " exceeded.\n"
10384 " 'enter-weak-marking-mode' - divide the list into two segments. The items after\n"
10385 " this string will not be marked until we enter weak marking mode. Note that weak\n"
10386 " marking mode may be entered zero or multiple times for one GC.\n"
10387 " 'abort-weak-marking-mode' - same as above, but then abort weak marking to fall back\n"
10388 " on the old iterative marking code path.\n"
10389 " 'drain' - fully drain the mark stack before continuing.\n"
10390 " 'set-color-black' - force everything following in the mark queue to be marked black.\n"
10391 " 'set-color-gray' - continue with the regular GC until gray marking is possible, then force\n"
10392 " everything following in the mark queue to be marked gray.\n"
10393 " 'unset-color' - stop forcing the mark color."),
10395 JS_FN_HELP("clearMarkQueue", ClearMarkQueue, 0, 0,
10396 "clearMarkQueue()",
10397 " Cancel the special marking of all objects enqueue with enqueueMark()."),
10399 JS_FN_HELP("getMarkQueue", GetMarkQueue, 0, 0,
10400 "getMarkQueue()",
10401 " Return the current mark queue set up via enqueueMark calls. Note that all\n"
10402 " returned values will be wrapped into the current compartment, so this loses\n"
10403 " some fidelity."),
10404 #endif // DEBUG
10406 JS_FN_HELP("nurseryStringsEnabled", NurseryStringsEnabled, 0, 0,
10407 "nurseryStringsEnabled()",
10408 " Return whether strings are currently allocated in the nursery for the current\n"
10409 " global\n"),
10411 JS_FN_HELP("isNurseryAllocated", IsNurseryAllocated, 1, 0,
10412 "isNurseryAllocated(thing)",
10413 " Return whether a GC thing is nursery allocated.\n"),
10415 JS_FN_HELP("numAllocSitesPretenured", NumAllocSitesPretenured, 0, 0,
10416 "numAllocSitesPretenured()",
10417 " Return the number of allocation sites that were pretenured for the current\n"
10418 " global\n"),
10420 JS_FN_HELP("getLcovInfo", GetLcovInfo, 1, 0,
10421 "getLcovInfo(global)",
10422 " Generate LCOV tracefile for the given compartment. If no global are provided then\n"
10423 " the current global is used as the default one.\n"),
10425 #ifdef DEBUG
10426 JS_FN_HELP("setRNGState", SetRNGState, 2, 0,
10427 "setRNGState(seed0, seed1)",
10428 " Set this compartment's RNG state.\n"),
10429 #endif
10431 #ifdef AFLFUZZ
10432 JS_FN_HELP("aflloop", AflLoop, 1, 0,
10433 "aflloop(max_cnt)",
10434 " Call the __AFL_LOOP() runtime function (see AFL docs)\n"),
10435 #endif
10437 JS_FN_HELP("monotonicNow", MonotonicNow, 0, 0,
10438 "monotonicNow()",
10439 " Return a timestamp reflecting the current elapsed system time.\n"
10440 " This is monotonically increasing.\n"),
10442 JS_FN_HELP("timeSinceCreation", TimeSinceCreation, 0, 0,
10443 "TimeSinceCreation()",
10444 " Returns the time in milliseconds since process creation.\n"
10445 " This uses a clock compatible with the profiler.\n"),
10447 JS_FN_HELP("isConstructor", IsConstructor, 1, 0,
10448 "isConstructor(value)",
10449 " Returns whether the value is considered IsConstructor.\n"),
10451 JS_FN_HELP("getTimeZone", GetTimeZone, 0, 0,
10452 "getTimeZone()",
10453 " Get the current time zone.\n"),
10455 JS_FN_HELP("getDefaultLocale", GetDefaultLocale, 0, 0,
10456 "getDefaultLocale()",
10457 " Get the current default locale.\n"),
10459 JS_FN_HELP("getCoreCount", GetCoreCount, 0, 0,
10460 "getCoreCount()",
10461 " Get the number of CPU cores from the platform layer. Typically this\n"
10462 " means the number of hyperthreads on systems where that makes sense.\n"),
10464 JS_FN_HELP("setTimeResolution", SetTimeResolution, 2, 0,
10465 "setTimeResolution(resolution, jitter)",
10466 " Enables time clamping and jittering. Specify a time resolution in\n"
10467 " microseconds and whether or not to jitter\n"),
10469 JS_FN_HELP("scriptedCallerGlobal", ScriptedCallerGlobal, 0, 0,
10470 "scriptedCallerGlobal()",
10471 " Get the caller's global (or null). See JS::GetScriptedCallerGlobal.\n"),
10473 JS_FN_HELP("objectGlobal", ObjectGlobal, 1, 0,
10474 "objectGlobal(obj)",
10475 " Returns the object's global object or null if the object is a wrapper.\n"),
10477 JS_FN_HELP("isSameCompartment", IsSameCompartment, 2, 0,
10478 "isSameCompartment(obj1, obj2)",
10479 " Unwraps obj1 and obj2 and returns whether the unwrapped objects are\n"
10480 " same-compartment.\n"),
10482 JS_FN_HELP("firstGlobalInCompartment", FirstGlobalInCompartment, 1, 0,
10483 "firstGlobalInCompartment(obj)",
10484 " Returns the first global in obj's compartment.\n"),
10486 JS_FN_HELP("assertCorrectRealm", AssertCorrectRealm, 0, 0,
10487 "assertCorrectRealm()",
10488 " Asserts cx->realm matches callee->realm.\n"),
10490 JS_FN_HELP("globalLexicals", GlobalLexicals, 0, 0,
10491 "globalLexicals()",
10492 " Returns an object containing a copy of all global lexical bindings.\n"
10493 " Example use: let x = 1; assertEq(globalLexicals().x, 1);\n"),
10495 JS_FN_HELP("baselineCompile", BaselineCompile, 2, 0,
10496 "baselineCompile([fun/code], forceDebugInstrumentation=false)",
10497 " Baseline-compiles the given JS function or script.\n"
10498 " Without arguments, baseline-compiles the caller's script; but note\n"
10499 " that extra boilerplate is needed afterwards to cause the VM to start\n"
10500 " running the jitcode rather than staying in the interpreter:\n"
10501 " baselineCompile(); for (var i=0; i<1; i++) {} ...\n"
10502 " The interpreter will enter the new jitcode at the loop header unless\n"
10503 " baselineCompile returned a string or threw an error.\n"),
10505 JS_FN_HELP("encodeAsUtf8InBuffer", EncodeAsUtf8InBuffer, 2, 0,
10506 "encodeAsUtf8InBuffer(str, uint8Array)",
10507 " Encode as many whole code points from the string str into the provided\n"
10508 " Uint8Array as will completely fit in it, converting lone surrogates to\n"
10509 " REPLACEMENT CHARACTER. Return an array [r, w] where |r| is the\n"
10510 " number of 16-bit units read and |w| is the number of bytes of UTF-8\n"
10511 " written."),
10513 JS_FN_HELP("clearKeptObjects", ClearKeptObjects, 0, 0,
10514 "clearKeptObjects()",
10515 "Perform the ECMAScript ClearKeptObjects operation, clearing the list of\n"
10516 "observed WeakRef targets that are kept alive until the next synchronous\n"
10517 "sequence of ECMAScript execution completes. This is used for testing\n"
10518 "WeakRefs.\n"),
10520 JS_FN_HELP("numberToDouble", NumberToDouble, 1, 0,
10521 "numberToDouble(number)",
10522 " Return the input number as double-typed number."),
10524 JS_FN_HELP("getICUOptions", GetICUOptions, 0, 0,
10525 "getICUOptions()",
10526 " Return an object describing the following ICU options.\n\n"
10527 " version: a string containing the ICU version number, e.g. '67.1'\n"
10528 " unicode: a string containing the Unicode version number, e.g. '13.0'\n"
10529 " locale: the ICU default locale, e.g. 'en_US'\n"
10530 " tzdata: a string containing the tzdata version number, e.g. '2020a'\n"
10531 " timezone: the ICU default time zone, e.g. 'America/Los_Angeles'\n"
10532 " host-timezone: the host time zone, e.g. 'America/Los_Angeles'"),
10534 JS_FN_HELP("getAvailableLocalesOf", GetAvailableLocalesOf, 0, 0,
10535 "getAvailableLocalesOf(name)",
10536 " Return an array of all available locales for the given Intl constuctor."),
10538 JS_FN_HELP("isSmallFunction", IsSmallFunction, 1, 0,
10539 "isSmallFunction(fun)",
10540 " Returns true if a scripted function is small enough to be inlinable."),
10542 JS_FN_HELP("compileToStencil", CompileToStencil, 1, 0,
10543 "compileToStencil(string, [options])",
10544 " Parses the given string argument as js script, returns the stencil"
10545 " for it."),
10547 JS_FN_HELP("evalStencil", EvalStencil, 1, 0,
10548 "evalStencil(stencil, [options])",
10549 " Instantiates the given stencil, and evaluates the top-level script it"
10550 " defines."),
10552 JS_FN_HELP("compileToStencilXDR", CompileToStencilXDR, 1, 0,
10553 "compileToStencilXDR(string, [options])",
10554 " Parses the given string argument as js script, produces the stencil"
10555 " for it, XDR-encodes the stencil, and returns an object that contains the"
10556 " XDR buffer."),
10558 JS_FN_HELP("evalStencilXDR", EvalStencilXDR, 1, 0,
10559 "evalStencilXDR(stencilXDR, [options])",
10560 " Reads the given stencil XDR object, and evaluates the top-level script it"
10561 " defines."),
10563 JS_FN_HELP("getExceptionInfo", GetExceptionInfo, 1, 0,
10564 "getExceptionInfo(fun)",
10565 " Calls the given function and returns information about the exception it"
10566 " throws. Returns null if the function didn't throw an exception."),
10568 JS_FN_HELP("nukeCCW", NukeCCW, 1, 0,
10569 "nukeCCW(wrapper)",
10570 " Nuke a CrossCompartmentWrapper, which turns it into a DeadProxyObject."),
10572 JS_FN_HELP("assertRealmFuseInvariants", AssertRealmFuseInvariants, 0, 0,
10573 "assertRealmFuseInvariants()",
10574 " Runs the realm's fuse invariant checks -- these will crash on failure. "
10575 " Only available in fuzzing or debug builds, so usage should be guarded. "),
10577 JS_FN_HELP("isCCW", IsCCW, 1, 0,
10578 "isCCW(object)",
10579 " Return true if an object is a CCW."),
10581 JS_FN_HELP("popAllFusesInRealm", PopAllFusesInRealm, 0, 0,
10582 "popAllFusesInRealm()",
10583 " Pops all the fuses in the current realm"),
10585 JS_FN_HELP("getAllPrefNames", GetAllPrefNames, 0, 0,
10586 "getAllPrefNames()",
10587 " Returns an array containing the names of all JS prefs."),
10589 JS_FN_HELP("getPrefValue", GetPrefValue, 1, 0,
10590 "getPrefValue(name)",
10591 " Return the value of the JS pref with the given name."),
10593 JS_FN_HELP("hadOutOfMemory", HadOutOfMemory, 0, 0,
10594 "hadOutOfMemory()",
10595 " Return the runtime's internal hadOutOfMemory flag that is set when\n"
10596 " out of memory is hit with an exception being propagated. "),
10598 JS_FS_HELP_END
10600 // clang-format on
10602 // clang-format off
10603 static const JSFunctionSpecWithHelp FuzzingUnsafeTestingFunctions[] = {
10604 JS_FN_HELP("getErrorNotes", GetErrorNotes, 1, 0,
10605 "getErrorNotes(error)",
10606 " Returns an array of error notes."),
10608 JS_FN_HELP("setTimeZone", SetTimeZone, 1, 0,
10609 "setTimeZone(tzname)",
10610 " Set the 'TZ' environment variable to the given time zone and applies the new time zone.\n"
10611 " The time zone given is validated according to the current environment.\n"
10612 " An empty string or undefined resets the time zone to its default value."),
10614 JS_FN_HELP("setDefaultLocale", SetDefaultLocale, 1, 0,
10615 "setDefaultLocale(locale)",
10616 " Set the runtime default locale to the given value.\n"
10617 " An empty string or undefined resets the runtime locale to its default value.\n"
10618 " NOTE: The input string is not fully validated, it must be a valid BCP-47 language tag."),
10620 JS_FN_HELP("isInStencilCache", IsInStencilCache, 1, 0,
10621 "isInStencilCache(fun)",
10622 " True if fun is available in the stencil cache."),
10624 JS_FN_HELP("waitForStencilCache", WaitForStencilCache, 1, 0,
10625 "waitForStencilCache(fun)",
10626 " Block main thread execution until the function is made available in the cache."),
10628 JS_FN_HELP("getInnerMostEnvironmentObject", GetInnerMostEnvironmentObject, 0, 0,
10629 "getInnerMostEnvironmentObject()",
10630 " Return the inner-most environment object for current execution."),
10632 JS_FN_HELP("getEnclosingEnvironmentObject", GetEnclosingEnvironmentObject, 1, 0,
10633 "getEnclosingEnvironmentObject(env)",
10634 " Return the enclosing environment object for given environment object."),
10636 JS_FN_HELP("getEnvironmentObjectType", GetEnvironmentObjectType, 1, 0,
10637 "getEnvironmentObjectType(env)",
10638 " Return a string represents the type of given environment object."),
10640 JS_FN_HELP("shortestPaths", ShortestPaths, 3, 0,
10641 "shortestPaths(targets, options)",
10642 " Return an array of arrays of shortest retaining paths. There is an array of\n"
10643 " shortest retaining paths for each object in |targets|. Each element in a path\n"
10644 " is of the form |{ predecessor, edge }|. |options| may contain:\n"
10645 " \n"
10646 " maxNumPaths: The maximum number of paths returned in each of those arrays\n"
10647 " (default 3).\n"
10648 " start: The object to start all paths from. If not given, then\n"
10649 " the starting point will be the set of GC roots."),
10651 JS_FN_HELP("getFuseState", GetFuseState, 0, 0,
10652 "getFuseState()",
10653 " Return an object describing the calling realm's fuse state, "
10654 "as well as the state of any runtime fuses."),
10656 #if defined(DEBUG) || defined(JS_JITSPEW)
10657 JS_FN_HELP("dumpObject", DumpObject, 1, 0,
10658 "dumpObject(obj)",
10659 " Dump an internal representation of an object."),
10661 JS_FN_HELP("dumpValue", DumpValue, 1, 0,
10662 "dumpValue(v)",
10663 " Dump an internal representation of a value."),
10665 JS_FN_HELP("dumpValueToString", DumpValueToString, 1, 0,
10666 "dumpValue(v)",
10667 " Return a dump of an internal representation of a value."),
10669 JS_FN_HELP("dumpStringRepresentation", DumpStringRepresentation, 1, 0,
10670 "dumpStringRepresentation(str)",
10671 " Print a human-readable description of how the string |str| is represented.\n"),
10673 JS_FN_HELP("stringRepresentation", GetStringRepresentation, 1, 0,
10674 "stringRepresentation(str)",
10675 " Return a human-readable description of how the string |str| is represented.\n"),
10676 #endif
10678 JS_FN_HELP("wasmExtractCode", WasmExtractCode, 1, 0,
10679 "wasmExtractCode(module[, tier])",
10680 " Extracts generated machine code from WebAssembly.Module. The tier is a string,\n"
10681 " 'stable', 'best', 'baseline', or 'ion'; the default is 'stable'. If the request\n"
10682 " cannot be satisfied then null is returned. If the request is 'ion' then block\n"
10683 " until background compilation is complete."),
10685 JS_FN_HELP("wasmDis", WasmDisassemble, 1, 0,
10686 "wasmDis(wasmObject[, options])\n",
10687 " Disassembles generated machine code from an exported WebAssembly function,\n"
10688 " or from all the functions defined in the module or instance, exported and not.\n"
10689 " The `options` is an object with the following optional keys:\n"
10690 " asString: boolean - if true, return a string rather than printing on stderr,\n"
10691 " the default is false.\n"
10692 " tier: string - one of 'stable', 'best', 'baseline', or 'ion'; the default is\n"
10693 " 'stable'.\n"
10694 " kinds: string - if set, and the wasmObject is a module or instance, a\n"
10695 " comma-separated list of the following keys, the default is `Function`:\n"
10696 " Function - functions defined in the module\n"
10697 " InterpEntry - C++-to-wasm stubs\n"
10698 " JitEntry - jitted-js-to-wasm stubs\n"
10699 " ImportInterpExit - wasm-to-C++ stubs\n"
10700 " ImportJitExit - wasm-to-jitted-JS stubs\n"
10701 " all - all kinds, including obscure ones\n"),
10703 JS_FN_HELP("wasmDumpIon", WasmDumpIon, 2, 0,
10704 "wasmDumpIon(bytecode, funcIndex, [, contents])\n",
10705 "wasmDumpIon(bytecode, funcIndex, [, contents])"
10706 " Returns a dump of compiling a function in the specified module with Ion."
10707 " The `contents` flag controls what is dumped. one of:"
10708 " `mir` | `unopt-mir`: Unoptimized MIR (the default)"
10709 " `opt-mir`: Optimized MIR"
10710 " `lir`: LIR"),
10712 JS_FS_HELP_END
10714 // clang-format on
10716 // clang-format off
10717 static const JSFunctionSpecWithHelp PCCountProfilingTestingFunctions[] = {
10718 JS_FN_HELP("start", PCCountProfiling_Start, 0, 0,
10719 "start()",
10720 " Start PC count profiling."),
10722 JS_FN_HELP("stop", PCCountProfiling_Stop, 0, 0,
10723 "stop()",
10724 " Stop PC count profiling."),
10726 JS_FN_HELP("purge", PCCountProfiling_Purge, 0, 0,
10727 "purge()",
10728 " Purge the collected PC count profiling data."),
10730 JS_FN_HELP("count", PCCountProfiling_ScriptCount, 0, 0,
10731 "count()",
10732 " Return the number of profiled scripts."),
10734 JS_FN_HELP("summary", PCCountProfiling_ScriptSummary, 1, 0,
10735 "summary(index)",
10736 " Return the PC count profiling summary for the given script index.\n"
10737 " The script index must be in the range [0, pc.count())."),
10739 JS_FN_HELP("contents", PCCountProfiling_ScriptContents, 1, 0,
10740 "contents(index)",
10741 " Return the complete profiling contents for the given script index.\n"
10742 " The script index must be in the range [0, pc.count())."),
10744 JS_FS_HELP_END
10746 // clang-format on
10748 // clang-format off
10749 static const JSFunctionSpecWithHelp FdLibMTestingFunctions[] = {
10750 JS_FN_HELP("pow", FdLibM_Pow, 2, 0,
10751 "pow(x, y)",
10752 " Return x ** y."),
10754 JS_FS_HELP_END
10756 // clang-format on
10758 bool js::InitTestingFunctions() { return disasmBuf.init(); }
10760 bool js::DefineTestingFunctions(JSContext* cx, HandleObject obj,
10761 bool fuzzingSafe_, bool disableOOMFunctions_) {
10762 fuzzingSafe = fuzzingSafe_;
10763 if (EnvVarIsDefined("MOZ_FUZZING_SAFE")) {
10764 fuzzingSafe = true;
10767 disableOOMFunctions = disableOOMFunctions_;
10769 if (!fuzzingSafe) {
10770 if (!JS_DefineFunctionsWithHelp(cx, obj, FuzzingUnsafeTestingFunctions)) {
10771 return false;
10774 RootedObject pccount(cx, JS_NewPlainObject(cx));
10775 if (!pccount) {
10776 return false;
10779 if (!JS_DefineProperty(cx, obj, "pccount", pccount, 0)) {
10780 return false;
10783 if (!JS_DefineFunctionsWithHelp(cx, pccount,
10784 PCCountProfilingTestingFunctions)) {
10785 return false;
10789 RootedObject fdlibm(cx, JS_NewPlainObject(cx));
10790 if (!fdlibm) {
10791 return false;
10794 if (!JS_DefineProperty(cx, obj, "fdlibm", fdlibm, 0)) {
10795 return false;
10798 if (!JS_DefineFunctionsWithHelp(cx, fdlibm, FdLibMTestingFunctions)) {
10799 return false;
10802 return JS_DefineFunctionsWithHelp(cx, obj, TestingFunctions);
10805 #ifdef FUZZING_JS_FUZZILLI
10806 uint32_t js::FuzzilliHashDouble(double value) {
10807 // We shouldn't GC here as this is called directly from IC code.
10808 AutoUnsafeCallWithABI unsafe;
10809 uint64_t v = mozilla::BitwiseCast<uint64_t>(value);
10810 return static_cast<uint32_t>(v) + static_cast<uint32_t>(v >> 32);
10813 uint32_t js::FuzzilliHashBigInt(BigInt* bigInt) {
10814 // We shouldn't GC here as this is called directly from IC code.
10815 AutoUnsafeCallWithABI unsafe;
10816 return bigInt->hash();
10819 void js::FuzzilliHashObject(JSContext* cx, JSObject* obj) {
10820 // called from IC and baseline/interpreter
10821 uint32_t hash;
10822 FuzzilliHashObjectInl(cx, obj, &hash);
10824 cx->executionHashInputs += 1;
10825 cx->executionHash = mozilla::RotateLeft(cx->executionHash + hash, 1);
10828 void js::FuzzilliHashObjectInl(JSContext* cx, JSObject* obj, uint32_t* out) {
10829 *out = 0;
10830 if (!js::SupportDifferentialTesting()) {
10831 return;
10834 RootedValue v(cx);
10835 v.setObject(*obj);
10837 JSAutoStructuredCloneBuffer JSCloner(
10838 JS::StructuredCloneScope::DifferentProcess, nullptr, nullptr);
10839 if (JSCloner.write(cx, v)) {
10840 JSStructuredCloneData& data = JSCloner.data();
10841 data.ForEachDataChunk([&](const char* aData, size_t aSize) {
10842 uint32_t h = mozilla::HashBytes(aData, aSize);
10843 h = (h << 1) | 1;
10844 *out ^= h;
10845 *out *= h;
10846 return true;
10848 } else if (JS_IsExceptionPending(cx)) {
10849 JS_ClearPendingException(cx);
10852 #endif