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