Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
* vim: set ts=8 sts=2 et sw=2 tw=80:
3
* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "builtin/TestingFunctions.h"
8
9
#include "mozilla/Atomics.h"
10
#include "mozilla/Casting.h"
11
#include "mozilla/FloatingPoint.h"
12
#include "mozilla/Maybe.h"
13
#include "mozilla/Span.h"
14
#include "mozilla/Sprintf.h"
15
#include "mozilla/TextUtils.h"
16
#include "mozilla/Tuple.h"
17
#include "mozilla/Unused.h"
18
19
#include <algorithm>
20
#include <cfloat>
21
#include <cmath>
22
#include <cstdlib>
23
#include <ctime>
24
#include <utility>
25
26
#if defined(XP_UNIX) && !defined(XP_DARWIN)
27
# include <time.h>
28
#else
29
# include <chrono>
30
#endif
31
32
#include "jsapi.h"
33
#include "jsfriendapi.h"
34
35
#include "builtin/Promise.h"
36
#include "builtin/SelfHostingDefines.h"
37
#ifdef DEBUG
38
# include "frontend/TokenStream.h"
39
# include "irregexp/RegExpAST.h"
40
# include "irregexp/RegExpEngine.h"
41
# include "irregexp/RegExpParser.h"
42
#endif
43
#include "gc/Allocator.h"
44
#include "gc/Zone.h"
45
#include "jit/BaselineJIT.h"
46
#include "jit/InlinableNatives.h"
47
#include "jit/Ion.h"
48
#include "jit/JitRealm.h"
49
#include "js/Array.h" // JS::NewArrayObject
50
#include "js/ArrayBuffer.h" // JS::{DetachArrayBuffer,GetArrayBufferLengthAndData,NewArrayBufferWithContents}
51
#include "js/CharacterEncoding.h"
52
#include "js/CompilationAndEvaluation.h"
53
#include "js/CompileOptions.h"
54
#include "js/Date.h"
55
#include "js/Debug.h"
56
#include "js/HashTable.h"
57
#include "js/LocaleSensitive.h"
58
#include "js/PropertySpec.h"
59
#include "js/RegExpFlags.h" // JS::RegExpFlag, JS::RegExpFlags
60
#include "js/SourceText.h"
61
#include "js/StableStringChars.h"
62
#include "js/StructuredClone.h"
63
#include "js/UbiNode.h"
64
#include "js/UbiNodeBreadthFirst.h"
65
#include "js/UbiNodeShortestPaths.h"
66
#include "js/UniquePtr.h"
67
#include "js/Vector.h"
68
#include "js/Wrapper.h"
69
#include "threading/CpuCount.h"
70
#include "util/StringBuffer.h"
71
#include "util/Text.h"
72
#include "vm/AsyncFunction.h"
73
#include "vm/AsyncIteration.h"
74
#include "vm/ErrorObject.h"
75
#include "vm/GlobalObject.h"
76
#include "vm/Interpreter.h"
77
#include "vm/Iteration.h"
78
#include "vm/JSContext.h"
79
#include "vm/JSObject.h"
80
#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseSlot_*
81
#include "vm/ProxyObject.h"
82
#include "vm/SavedStacks.h"
83
#include "vm/Stack.h"
84
#include "vm/StringType.h"
85
#include "vm/TraceLogging.h"
86
#include "wasm/AsmJS.h"
87
#include "wasm/WasmBaselineCompile.h"
88
#include "wasm/WasmCraneliftCompile.h"
89
#include "wasm/WasmInstance.h"
90
#include "wasm/WasmIonCompile.h"
91
#include "wasm/WasmJS.h"
92
#include "wasm/WasmModule.h"
93
#include "wasm/WasmSignalHandlers.h"
94
#include "wasm/WasmTesting.h"
95
#include "wasm/WasmTypes.h"
96
97
#include "debugger/DebugAPI-inl.h"
98
#include "vm/Compartment-inl.h"
99
#include "vm/EnvironmentObject-inl.h"
100
#include "vm/JSContext-inl.h"
101
#include "vm/JSObject-inl.h"
102
#include "vm/NativeObject-inl.h"
103
#include "vm/StringType-inl.h"
104
105
using namespace js;
106
107
using mozilla::ArrayLength;
108
using mozilla::AssertedCast;
109
using mozilla::AsWritableChars;
110
using mozilla::MakeSpan;
111
using mozilla::Maybe;
112
using mozilla::Tie;
113
using mozilla::Tuple;
114
115
using JS::AutoStableStringChars;
116
using JS::CompileOptions;
117
using JS::RegExpFlag;
118
using JS::RegExpFlags;
119
using JS::SourceOwnership;
120
using JS::SourceText;
121
122
// If fuzzingSafe is set, remove functionality that could cause problems with
123
// fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
124
mozilla::Atomic<bool> fuzzingSafe(false);
125
126
// If disableOOMFunctions is set, disable functionality that causes artificial
127
// OOM conditions.
128
static mozilla::Atomic<bool> disableOOMFunctions(false);
129
130
static bool EnvVarIsDefined(const char* name) {
131
const char* value = getenv(name);
132
return value && *value;
133
}
134
135
#if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
136
static bool EnvVarAsInt(const char* name, int* valueOut) {
137
if (!EnvVarIsDefined(name)) {
138
return false;
139
}
140
141
*valueOut = atoi(getenv(name));
142
return true;
143
}
144
#endif
145
146
static bool GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp) {
147
CallArgs args = CallArgsFromVp(argc, vp);
148
RootedObject info(cx, JS_NewPlainObject(cx));
149
if (!info) {
150
return false;
151
}
152
153
if (!JS_SetProperty(cx, info, "rooting-analysis", FalseHandleValue)) {
154
return false;
155
}
156
157
if (!JS_SetProperty(cx, info, "exact-rooting", TrueHandleValue)) {
158
return false;
159
}
160
161
if (!JS_SetProperty(cx, info, "trace-jscalls-api", FalseHandleValue)) {
162
return false;
163
}
164
165
if (!JS_SetProperty(cx, info, "incremental-gc", TrueHandleValue)) {
166
return false;
167
}
168
169
if (!JS_SetProperty(cx, info, "generational-gc", TrueHandleValue)) {
170
return false;
171
}
172
173
if (!JS_SetProperty(cx, info, "oom-backtraces", FalseHandleValue)) {
174
return false;
175
}
176
177
RootedValue value(cx);
178
#ifdef DEBUG
179
value = BooleanValue(true);
180
#else
181
value = BooleanValue(false);
182
#endif
183
if (!JS_SetProperty(cx, info, "debug", value)) {
184
return false;
185
}
186
187
#ifdef RELEASE_OR_BETA
188
value = BooleanValue(true);
189
#else
190
value = BooleanValue(false);
191
#endif
192
if (!JS_SetProperty(cx, info, "release_or_beta", value)) {
193
return false;
194
}
195
196
#ifdef MOZ_CODE_COVERAGE
197
value = BooleanValue(true);
198
#else
199
value = BooleanValue(false);
200
#endif
201
if (!JS_SetProperty(cx, info, "coverage", value)) {
202
return false;
203
}
204
205
#ifdef JS_HAS_CTYPES
206
value = BooleanValue(true);
207
#else
208
value = BooleanValue(false);
209
#endif
210
if (!JS_SetProperty(cx, info, "has-ctypes", value)) {
211
return false;
212
}
213
214
#if defined(_M_IX86) || defined(__i386__)
215
value = BooleanValue(true);
216
#else
217
value = BooleanValue(false);
218
#endif
219
if (!JS_SetProperty(cx, info, "x86", value)) {
220
return false;
221
}
222
223
#if defined(_M_X64) || defined(__x86_64__)
224
value = BooleanValue(true);
225
#else
226
value = BooleanValue(false);
227
#endif
228
if (!JS_SetProperty(cx, info, "x64", value)) {
229
return false;
230
}
231
232
#ifdef JS_CODEGEN_ARM
233
value = BooleanValue(true);
234
#else
235
value = BooleanValue(false);
236
#endif
237
if (!JS_SetProperty(cx, info, "arm", value)) {
238
return false;
239
}
240
241
#ifdef JS_SIMULATOR_ARM
242
value = BooleanValue(true);
243
#else
244
value = BooleanValue(false);
245
#endif
246
if (!JS_SetProperty(cx, info, "arm-simulator", value)) {
247
return false;
248
}
249
250
#ifdef ANDROID
251
value = BooleanValue(true);
252
#else
253
value = BooleanValue(false);
254
#endif
255
if (!JS_SetProperty(cx, info, "android", value)) {
256
return false;
257
}
258
259
#ifdef XP_WIN
260
value = BooleanValue(true);
261
#else
262
value = BooleanValue(false);
263
#endif
264
if (!JS_SetProperty(cx, info, "windows", value)) {
265
return false;
266
}
267
268
#ifdef JS_CODEGEN_ARM64
269
value = BooleanValue(true);
270
#else
271
value = BooleanValue(false);
272
#endif
273
if (!JS_SetProperty(cx, info, "arm64", value)) {
274
return false;
275
}
276
277
#ifdef JS_SIMULATOR_ARM64
278
value = BooleanValue(true);
279
#else
280
value = BooleanValue(false);
281
#endif
282
if (!JS_SetProperty(cx, info, "arm64-simulator", value)) {
283
return false;
284
}
285
286
#ifdef JS_CODEGEN_MIPS32
287
value = BooleanValue(true);
288
#else
289
value = BooleanValue(false);
290
#endif
291
if (!JS_SetProperty(cx, info, "mips32", value)) {
292
return false;
293
}
294
295
#ifdef JS_CODEGEN_MIPS64
296
value = BooleanValue(true);
297
#else
298
value = BooleanValue(false);
299
#endif
300
if (!JS_SetProperty(cx, info, "mips64", value)) {
301
return false;
302
}
303
304
#ifdef JS_SIMULATOR_MIPS32
305
value = BooleanValue(true);
306
#else
307
value = BooleanValue(false);
308
#endif
309
if (!JS_SetProperty(cx, info, "mips32-simulator", value)) {
310
return false;
311
}
312
313
#ifdef JS_SIMULATOR_MIPS64
314
value = BooleanValue(true);
315
#else
316
value = BooleanValue(false);
317
#endif
318
if (!JS_SetProperty(cx, info, "mips64-simulator", value)) {
319
return false;
320
}
321
322
#ifdef MOZ_ASAN
323
value = BooleanValue(true);
324
#else
325
value = BooleanValue(false);
326
#endif
327
if (!JS_SetProperty(cx, info, "asan", value)) {
328
return false;
329
}
330
331
#ifdef MOZ_TSAN
332
value = BooleanValue(true);
333
#else
334
value = BooleanValue(false);
335
#endif
336
if (!JS_SetProperty(cx, info, "tsan", value)) {
337
return false;
338
}
339
340
#ifdef MOZ_UBSAN
341
value = BooleanValue(true);
342
#else
343
value = BooleanValue(false);
344
#endif
345
if (!JS_SetProperty(cx, info, "ubsan", value)) {
346
return false;
347
}
348
349
#ifdef JS_GC_ZEAL
350
value = BooleanValue(true);
351
#else
352
value = BooleanValue(false);
353
#endif
354
if (!JS_SetProperty(cx, info, "has-gczeal", value)) {
355
return false;
356
}
357
358
#ifdef JS_MORE_DETERMINISTIC
359
value = BooleanValue(true);
360
#else
361
value = BooleanValue(false);
362
#endif
363
if (!JS_SetProperty(cx, info, "more-deterministic", value)) {
364
return false;
365
}
366
367
#ifdef MOZ_PROFILING
368
value = BooleanValue(true);
369
#else
370
value = BooleanValue(false);
371
#endif
372
if (!JS_SetProperty(cx, info, "profiling", value)) {
373
return false;
374
}
375
376
#ifdef INCLUDE_MOZILLA_DTRACE
377
value = BooleanValue(true);
378
#else
379
value = BooleanValue(false);
380
#endif
381
if (!JS_SetProperty(cx, info, "dtrace", value)) {
382
return false;
383
}
384
385
#ifdef MOZ_VALGRIND
386
value = BooleanValue(true);
387
#else
388
value = BooleanValue(false);
389
#endif
390
if (!JS_SetProperty(cx, info, "valgrind", value)) {
391
return false;
392
}
393
394
#ifdef JS_HAS_TYPED_OBJECTS
395
value = BooleanValue(true);
396
#else
397
value = BooleanValue(false);
398
#endif
399
if (!JS_SetProperty(cx, info, "typed-objects", value)) {
400
return false;
401
}
402
403
#ifdef JS_HAS_INTL_API
404
value = BooleanValue(true);
405
#else
406
value = BooleanValue(false);
407
#endif
408
if (!JS_SetProperty(cx, info, "intl-api", value)) {
409
return false;
410
}
411
412
#if defined(SOLARIS)
413
value = BooleanValue(false);
414
#else
415
value = BooleanValue(true);
416
#endif
417
if (!JS_SetProperty(cx, info, "mapped-array-buffer", value)) {
418
return false;
419
}
420
421
#ifdef MOZ_MEMORY
422
value = BooleanValue(true);
423
#else
424
value = BooleanValue(false);
425
#endif
426
if (!JS_SetProperty(cx, info, "moz-memory", value)) {
427
return false;
428
}
429
430
#if defined(JS_BUILD_BINAST)
431
value = BooleanValue(true);
432
#else
433
value = BooleanValue(false);
434
#endif
435
if (!JS_SetProperty(cx, info, "binast", value)) {
436
return false;
437
}
438
439
value.setInt32(sizeof(void*));
440
if (!JS_SetProperty(cx, info, "pointer-byte-size", value)) {
441
return false;
442
}
443
444
args.rval().setObject(*info);
445
return true;
446
}
447
448
static bool IsLCovEnabled(JSContext* cx, unsigned argc, Value* vp) {
449
CallArgs args = CallArgsFromVp(argc, vp);
450
args.rval().setBoolean(coverage::IsLCovEnabled());
451
return true;
452
}
453
454
static bool IsTypeInferenceEnabled(JSContext* cx, unsigned argc, Value* vp) {
455
CallArgs args = CallArgsFromVp(argc, vp);
456
args.rval().setBoolean(js::IsTypeInferenceEnabled());
457
return true;
458
}
459
460
static bool ReturnStringCopy(JSContext* cx, CallArgs& args,
461
const char* message) {
462
JSString* str = JS_NewStringCopyZ(cx, message);
463
if (!str) {
464
return false;
465
}
466
467
args.rval().setString(str);
468
return true;
469
}
470
471
static bool GC(JSContext* cx, unsigned argc, Value* vp) {
472
CallArgs args = CallArgsFromVp(argc, vp);
473
474
/*
475
* If the first argument is 'zone', we collect any zones previously
476
* scheduled for GC via schedulegc. If the first argument is an object, we
477
* collect the object's zone (and any other zones scheduled for
478
* GC). Otherwise, we collect all zones.
479
*/
480
bool zone = false;
481
if (args.length() >= 1) {
482
Value arg = args[0];
483
if (arg.isString()) {
484
if (!JS_StringEqualsLiteral(cx, arg.toString(), "zone", &zone)) {
485
return false;
486
}
487
} else if (arg.isObject()) {
488
PrepareZoneForGC(UncheckedUnwrap(&arg.toObject())->zone());
489
zone = true;
490
}
491
}
492
493
JSGCInvocationKind gckind = GC_NORMAL;
494
JS::GCReason reason = JS::GCReason::API;
495
if (args.length() >= 2) {
496
Value arg = args[1];
497
if (arg.isString()) {
498
bool shrinking = false;
499
bool last_ditch = false;
500
if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking",
501
&shrinking)) {
502
return false;
503
}
504
if (!JS_StringEqualsLiteral(cx, arg.toString(), "last-ditch",
505
&last_ditch)) {
506
return false;
507
}
508
if (shrinking) {
509
gckind = GC_SHRINK;
510
} else if (last_ditch) {
511
gckind = GC_SHRINK;
512
reason = JS::GCReason::LAST_DITCH;
513
}
514
}
515
}
516
517
#ifndef JS_MORE_DETERMINISTIC
518
size_t preBytes = cx->runtime()->gc.heapSize.bytes();
519
#endif
520
521
if (zone) {
522
PrepareForDebugGC(cx->runtime());
523
} else {
524
JS::PrepareForFullGC(cx);
525
}
526
527
JS::NonIncrementalGC(cx, gckind, reason);
528
529
char buf[256] = {'\0'};
530
#ifndef JS_MORE_DETERMINISTIC
531
SprintfLiteral(buf, "before %zu, after %zu\n", preBytes,
532
cx->runtime()->gc.heapSize.bytes());
533
#endif
534
return ReturnStringCopy(cx, args, buf);
535
}
536
537
static bool MinorGC(JSContext* cx, unsigned argc, Value* vp) {
538
CallArgs args = CallArgsFromVp(argc, vp);
539
if (args.get(0) == BooleanValue(true)) {
540
cx->runtime()->gc.storeBuffer().setAboutToOverflow(
541
JS::GCReason::FULL_GENERIC_BUFFER);
542
}
543
544
cx->minorGC(JS::GCReason::API);
545
args.rval().setUndefined();
546
return true;
547
}
548
549
#define FOR_EACH_GC_PARAM(_) \
550
_("maxBytes", JSGC_MAX_BYTES, true) \
551
_("minNurseryBytes", JSGC_MIN_NURSERY_BYTES, true) \
552
_("maxNurseryBytes", JSGC_MAX_NURSERY_BYTES, true) \
553
_("gcBytes", JSGC_BYTES, false) \
554
_("nurseryBytes", JSGC_NURSERY_BYTES, false) \
555
_("gcNumber", JSGC_NUMBER, false) \
556
_("mode", JSGC_MODE, true) \
557
_("unusedChunks", JSGC_UNUSED_CHUNKS, false) \
558
_("totalChunks", JSGC_TOTAL_CHUNKS, false) \
559
_("sliceTimeBudgetMS", JSGC_SLICE_TIME_BUDGET_MS, true) \
560
_("markStackLimit", JSGC_MARK_STACK_LIMIT, true) \
561
_("highFrequencyTimeLimit", JSGC_HIGH_FREQUENCY_TIME_LIMIT, true) \
562
_("highFrequencyLowLimit", JSGC_HIGH_FREQUENCY_LOW_LIMIT, true) \
563
_("highFrequencyHighLimit", JSGC_HIGH_FREQUENCY_HIGH_LIMIT, true) \
564
_("highFrequencyHeapGrowthMax", JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX, true) \
565
_("highFrequencyHeapGrowthMin", JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN, true) \
566
_("lowFrequencyHeapGrowth", JSGC_LOW_FREQUENCY_HEAP_GROWTH, true) \
567
_("dynamicHeapGrowth", JSGC_DYNAMIC_HEAP_GROWTH, true) \
568
_("dynamicMarkSlice", JSGC_DYNAMIC_MARK_SLICE, true) \
569
_("allocationThreshold", JSGC_ALLOCATION_THRESHOLD, true) \
570
_("nonIncrementalFactor", JSGC_NON_INCREMENTAL_FACTOR, true) \
571
_("avoidInterruptFactor", JSGC_AVOID_INTERRUPT_FACTOR, true) \
572
_("minEmptyChunkCount", JSGC_MIN_EMPTY_CHUNK_COUNT, true) \
573
_("maxEmptyChunkCount", JSGC_MAX_EMPTY_CHUNK_COUNT, true) \
574
_("compactingEnabled", JSGC_COMPACTING_ENABLED, true) \
575
_("minLastDitchGCPeriod", JSGC_MIN_LAST_DITCH_GC_PERIOD, true) \
576
_("nurseryFreeThresholdForIdleCollection", \
577
JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION, true) \
578
_("nurseryFreeThresholdForIdleCollectionPercent", \
579
JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT, true) \
580
_("pretenureThreshold", JSGC_PRETENURE_THRESHOLD, true) \
581
_("pretenureGroupThreshold", JSGC_PRETENURE_GROUP_THRESHOLD, true) \
582
_("zoneAllocDelayKB", JSGC_ZONE_ALLOC_DELAY_KB, true) \
583
_("mallocThresholdBase", JSGC_MALLOC_THRESHOLD_BASE, true) \
584
_("mallocGrowthFactor", JSGC_MALLOC_GROWTH_FACTOR, true)
585
586
static const struct ParamInfo {
587
const char* name;
588
JSGCParamKey param;
589
bool writable;
590
} paramMap[] = {
591
#define DEFINE_PARAM_INFO(name, key, writable) {name, key, writable},
592
FOR_EACH_GC_PARAM(DEFINE_PARAM_INFO)
593
#undef DEFINE_PARAM_INFO
594
};
595
596
#define PARAM_NAME_LIST_ENTRY(name, key, writable) " " name
597
#define GC_PARAMETER_ARGS_LIST FOR_EACH_GC_PARAM(PARAM_NAME_LIST_ENTRY)
598
599
static bool GCParameter(JSContext* cx, unsigned argc, Value* vp) {
600
CallArgs args = CallArgsFromVp(argc, vp);
601
602
JSString* str = ToString(cx, args.get(0));
603
if (!str) {
604
return false;
605
}
606
607
JSLinearString* linearStr = JS_EnsureLinearString(cx, str);
608
if (!linearStr) {
609
return false;
610
}
611
612
size_t paramIndex = 0;
613
for (;; paramIndex++) {
614
if (paramIndex == ArrayLength(paramMap)) {
615
JS_ReportErrorASCII(
616
cx, "the first argument must be one of:" GC_PARAMETER_ARGS_LIST);
617
return false;
618
}
619
if (JS_LinearStringEqualsAscii(linearStr, paramMap[paramIndex].name)) {
620
break;
621
}
622
}
623
const ParamInfo& info = paramMap[paramIndex];
624
JSGCParamKey param = info.param;
625
626
// Request mode.
627
if (args.length() == 1) {
628
uint32_t value = JS_GetGCParameter(cx, param);
629
args.rval().setNumber(value);
630
return true;
631
}
632
633
if (!info.writable) {
634
JS_ReportErrorASCII(cx, "Attempt to change read-only parameter %s",
635
info.name);
636
return false;
637
}
638
639
if (disableOOMFunctions) {
640
switch (param) {
641
case JSGC_MAX_BYTES:
642
case JSGC_MAX_NURSERY_BYTES:
643
args.rval().setUndefined();
644
return true;
645
default:
646
break;
647
}
648
}
649
650
double d;
651
if (!ToNumber(cx, args[1], &d)) {
652
return false;
653
}
654
655
if (d < 0 || d > UINT32_MAX) {
656
JS_ReportErrorASCII(cx, "Parameter value out of range");
657
return false;
658
}
659
660
uint32_t value = floor(d);
661
if (param == JSGC_MARK_STACK_LIMIT && JS::IsIncrementalGCInProgress(cx)) {
662
JS_ReportErrorASCII(
663
cx, "attempt to set markStackLimit while a GC is in progress");
664
return false;
665
}
666
667
bool ok = cx->runtime()->gc.setParameter(param, value);
668
if (!ok) {
669
JS_ReportErrorASCII(cx, "Parameter value out of range");
670
return false;
671
}
672
673
args.rval().setUndefined();
674
return true;
675
}
676
677
static bool RelazifyFunctions(JSContext* cx, unsigned argc, Value* vp) {
678
// Relazifying functions on GC is usually only done for compartments that are
679
// not active. To aid fuzzing, this testing function allows us to relazify
680
// even if the compartment is active.
681
682
CallArgs args = CallArgsFromVp(argc, vp);
683
684
// Disable relazification of all scripts on stack. It is a pervasive
685
// assumption in the engine that running scripts still have bytecode.
686
for (AllScriptFramesIter i(cx); !i.done(); ++i) {
687
i.script()->clearAllowRelazify();
688
}
689
690
cx->runtime()->allowRelazificationForTesting = true;
691
692
JS::PrepareForFullGC(cx);
693
JS::NonIncrementalGC(cx, GC_SHRINK, JS::GCReason::API);
694
695
cx->runtime()->allowRelazificationForTesting = false;
696
697
args.rval().setUndefined();
698
return true;
699
}
700
701
static bool IsProxy(JSContext* cx, unsigned argc, Value* vp) {
702
CallArgs args = CallArgsFromVp(argc, vp);
703
if (args.length() != 1) {
704
JS_ReportErrorASCII(cx, "the function takes exactly one argument");
705
return false;
706
}
707
if (!args[0].isObject()) {
708
args.rval().setBoolean(false);
709
return true;
710
}
711
args.rval().setBoolean(args[0].toObject().is<ProxyObject>());
712
return true;
713
}
714
715
static bool WasmIsSupported(JSContext* cx, unsigned argc, Value* vp) {
716
CallArgs args = CallArgsFromVp(argc, vp);
717
args.rval().setBoolean(wasm::HasSupport(cx));
718
return true;
719
}
720
721
static bool WasmIsSupportedByHardware(JSContext* cx, unsigned argc, Value* vp) {
722
CallArgs args = CallArgsFromVp(argc, vp);
723
args.rval().setBoolean(wasm::HasPlatformSupport(cx));
724
return true;
725
}
726
727
static bool WasmDebuggingIsSupported(JSContext* cx, unsigned argc, Value* vp) {
728
CallArgs args = CallArgsFromVp(argc, vp);
729
args.rval().setBoolean(wasm::HasSupport(cx) && wasm::BaselineAvailable(cx));
730
return true;
731
}
732
733
static bool WasmStreamingIsSupported(JSContext* cx, unsigned argc, Value* vp) {
734
CallArgs args = CallArgsFromVp(argc, vp);
735
args.rval().setBoolean(wasm::StreamingCompilationAvailable(cx));
736
return true;
737
}
738
739
static bool WasmCachingIsSupported(JSContext* cx, unsigned argc, Value* vp) {
740
CallArgs args = CallArgsFromVp(argc, vp);
741
args.rval().setBoolean(wasm::CodeCachingAvailable(cx));
742
return true;
743
}
744
745
static bool WasmHugeMemoryIsSupported(JSContext* cx, unsigned argc, Value* vp) {
746
CallArgs args = CallArgsFromVp(argc, vp);
747
#ifdef WASM_SUPPORTS_HUGE_MEMORY
748
args.rval().setBoolean(true);
749
#else
750
args.rval().setBoolean(false);
751
#endif
752
return true;
753
}
754
755
static bool WasmThreadsSupported(JSContext* cx, unsigned argc, Value* vp) {
756
CallArgs args = CallArgsFromVp(argc, vp);
757
args.rval().setBoolean(wasm::ThreadsAvailable(cx));
758
return true;
759
}
760
761
static bool WasmBulkMemSupported(JSContext* cx, unsigned argc, Value* vp) {
762
CallArgs args = CallArgsFromVp(argc, vp);
763
#ifdef ENABLE_WASM_BULKMEM_OPS
764
bool isSupported = true;
765
#else
766
bool isSupported =
767
cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
768
#endif
769
args.rval().setBoolean(isSupported);
770
return true;
771
}
772
773
static bool WasmReftypesEnabled(JSContext* cx, unsigned argc, Value* vp) {
774
CallArgs args = CallArgsFromVp(argc, vp);
775
args.rval().setBoolean(wasm::ReftypesAvailable(cx));
776
return true;
777
}
778
779
static bool WasmGcEnabled(JSContext* cx, unsigned argc, Value* vp) {
780
CallArgs args = CallArgsFromVp(argc, vp);
781
args.rval().setBoolean(wasm::GcTypesAvailable(cx));
782
return true;
783
}
784
785
static bool WasmMultiValueEnabled(JSContext* cx, unsigned argc, Value* vp) {
786
CallArgs args = CallArgsFromVp(argc, vp);
787
args.rval().setBoolean(wasm::MultiValuesAvailable(cx));
788
return true;
789
}
790
791
static bool WasmBigIntEnabled(JSContext* cx, unsigned argc, Value* vp) {
792
CallArgs args = CallArgsFromVp(argc, vp);
793
args.rval().setBoolean(wasm::I64BigIntConversionAvailable(cx));
794
return true;
795
}
796
797
static bool WasmCompilersPresent(JSContext* cx, unsigned argc, Value* vp) {
798
CallArgs args = CallArgsFromVp(argc, vp);
799
800
char buf[256];
801
*buf = 0;
802
if (wasm::BaselinePlatformSupport()) {
803
strcat(buf, "baseline");
804
}
805
if (wasm::IonPlatformSupport()) {
806
if (*buf) {
807
strcat(buf, ",");
808
}
809
strcat(buf, "ion");
810
}
811
if (wasm::CraneliftPlatformSupport()) {
812
if (*buf) {
813
strcat(buf, ",");
814
}
815
strcat(buf, "cranelift");
816
}
817
818
JSString* result = JS_NewStringCopyZ(cx, buf);
819
if (!result) {
820
return false;
821
}
822
823
args.rval().setString(result);
824
return true;
825
}
826
827
static bool WasmCompileMode(JSContext* cx, unsigned argc, Value* vp) {
828
CallArgs args = CallArgsFromVp(argc, vp);
829
830
// This triplet of predicates will select zero or one baseline compiler and
831
// zero or one optimizing compiler.
832
bool baseline = wasm::BaselineAvailable(cx);
833
bool ion = wasm::IonAvailable(cx);
834
bool cranelift = wasm::CraneliftAvailable(cx);
835
836
MOZ_ASSERT(!(ion && cranelift));
837
838
JSString* result;
839
if (!wasm::HasSupport(cx)) {
840
result = JS_NewStringCopyZ(cx, "none");
841
} else if (baseline && ion) {
842
result = JS_NewStringCopyZ(cx, "baseline+ion");
843
} else if (baseline && cranelift) {
844
result = JS_NewStringCopyZ(cx, "baseline+cranelift");
845
} else if (baseline) {
846
result = JS_NewStringCopyZ(cx, "baseline");
847
} else if (cranelift) {
848
result = JS_NewStringCopyZ(cx, "cranelift");
849
} else {
850
result = JS_NewStringCopyZ(cx, "ion");
851
}
852
853
if (!result) {
854
return false;
855
}
856
857
args.rval().setString(result);
858
return true;
859
}
860
861
static bool WasmCraneliftDisabledByFeatures(JSContext* cx, unsigned argc,
862
Value* vp) {
863
CallArgs args = CallArgsFromVp(argc, vp);
864
bool isDisabled = false;
865
JSStringBuilder reason(cx);
866
if (!wasm::CraneliftDisabledByFeatures(cx, &isDisabled, &reason)) {
867
return false;
868
}
869
if (isDisabled) {
870
JSString* result = reason.finishString();
871
if (!result) {
872
return false;
873
}
874
args.rval().setString(result);
875
} else {
876
args.rval().setBoolean(false);
877
}
878
return true;
879
}
880
881
static bool WasmIonDisabledByFeatures(JSContext* cx, unsigned argc, Value* vp) {
882
CallArgs args = CallArgsFromVp(argc, vp);
883
bool isDisabled = false;
884
JSStringBuilder reason(cx);
885
if (!wasm::IonDisabledByFeatures(cx, &isDisabled, &reason)) {
886
return false;
887
}
888
if (isDisabled) {
889
JSString* result = reason.finishString();
890
if (!result) {
891
return false;
892
}
893
args.rval().setString(result);
894
} else {
895
args.rval().setBoolean(false);
896
}
897
return true;
898
}
899
900
static bool WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp) {
901
CallArgs args = CallArgsFromVp(argc, vp);
902
RootedObject callee(cx, &args.callee());
903
904
if (!args.requireAtLeast(cx, "wasmTextToBinary", 1)) {
905
return false;
906
}
907
908
if (!args[0].isString()) {
909
ReportUsageErrorASCII(cx, callee, "First argument must be a String");
910
return false;
911
}
912
913
size_t textLen = args[0].toString()->length();
914
915
AutoStableStringChars twoByteChars(cx);
916
if (!twoByteChars.initTwoByte(cx, args[0].toString())) {
917
return false;
918
}
919
920
wasm::Bytes bytes;
921
UniqueChars error;
922
if (!wasm::TextToBinary(twoByteChars.twoByteChars(), textLen, &bytes,
923
&error)) {
924
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL,
925
error.get() ? error.get() : "out of memory");
926
return false;
927
}
928
929
RootedObject binary(cx, JS_NewUint8Array(cx, bytes.length()));
930
if (!binary) {
931
return false;
932
}
933
934
memcpy(binary->as<TypedArrayObject>().dataPointerUnshared(), bytes.begin(),
935
bytes.length());
936
937
args.rval().setObject(*binary);
938
return true;
939
}
940
941
static bool WasmCodeOffsets(JSContext* cx, unsigned argc, Value* vp) {
942
CallArgs args = CallArgsFromVp(argc, vp);
943
RootedObject callee(cx, &args.callee());
944
945
if (!args.requireAtLeast(cx, "wasmCodeOffsets", 1)) {
946
return false;
947
}
948
949
if (!args.get(0).isObject()) {
950
JS_ReportErrorASCII(cx, "argument is not an object");
951
return false;
952
}
953
954
SharedMem<uint8_t*> bytes;
955
size_t byteLength;
956
957
JSObject* bufferObject = &args[0].toObject();
958
JSObject* unwrappedBufferObject = CheckedUnwrapStatic(bufferObject);
959
if (!unwrappedBufferObject ||
960
!IsBufferSource(unwrappedBufferObject, &bytes, &byteLength)) {
961
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
962
JSMSG_WASM_BAD_BUF_ARG);
963
return false;
964
}
965
966
wasm::Uint32Vector offsets;
967
wasm::CodeOffsets(bytes.unwrap(), byteLength, &offsets);
968
969
RootedObject jsOffsets(cx, JS::NewArrayObject(cx, offsets.length()));
970
if (!jsOffsets) {
971
return false;
972
}
973
for (size_t i = 0; i < offsets.length(); i++) {
974
uint32_t offset = offsets[i];
975
RootedValue offsetVal(cx, NumberValue(offset));
976
if (!JS_SetElement(cx, jsOffsets, i, offsetVal)) {
977
return false;
978
}
979
}
980
args.rval().setObject(*jsOffsets);
981
return true;
982
}
983
984
static bool ConvertToTier(JSContext* cx, HandleValue value,
985
const wasm::Code& code, wasm::Tier* tier) {
986
RootedString option(cx, JS::ToString(cx, value));
987
988
if (!option) {
989
return false;
990
}
991
992
bool stableTier = false;
993
bool bestTier = false;
994
bool baselineTier = false;
995
bool ionTier = false;
996
997
if (!JS_StringEqualsLiteral(cx, option, "stable", &stableTier) ||
998
!JS_StringEqualsLiteral(cx, option, "best", &bestTier) ||
999
!JS_StringEqualsLiteral(cx, option, "baseline", &baselineTier) ||
1000
!JS_StringEqualsLiteral(cx, option, "ion", &ionTier)) {
1001
return false;
1002
}
1003
1004
if (stableTier) {
1005
*tier = code.stableTier();
1006
} else if (bestTier) {
1007
*tier = code.bestTier();
1008
} else if (baselineTier) {
1009
*tier = wasm::Tier::Baseline;
1010
} else if (ionTier) {
1011
*tier = wasm::Tier::Optimized;
1012
} else {
1013
// You can omit the argument but you can't pass just anything you like
1014
return false;
1015
}
1016
1017
return true;
1018
}
1019
1020
static bool WasmExtractCode(JSContext* cx, unsigned argc, Value* vp) {
1021
if (!cx->options().wasm()) {
1022
JS_ReportErrorASCII(cx, "wasm support unavailable");
1023
return false;
1024
}
1025
1026
CallArgs args = CallArgsFromVp(argc, vp);
1027
1028
if (!args.get(0).isObject()) {
1029
JS_ReportErrorASCII(cx, "argument is not an object");
1030
return false;
1031
}
1032
1033
Rooted<WasmModuleObject*> module(
1034
cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>());
1035
if (!module) {
1036
JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
1037
return false;
1038
}
1039
1040
wasm::Tier tier = module->module().code().stableTier();
1041
;
1042
if (args.length() > 1 &&
1043
!ConvertToTier(cx, args[1], module->module().code(), &tier)) {
1044
args.rval().setNull();
1045
return false;
1046
}
1047
1048
RootedValue result(cx);
1049
if (!module->module().extractCode(cx, tier, &result)) {
1050
return false;
1051
}
1052
1053
args.rval().set(result);
1054
return true;
1055
}
1056
1057
static bool WasmDisassemble(JSContext* cx, unsigned argc, Value* vp) {
1058
if (!cx->options().wasm()) {
1059
JS_ReportErrorASCII(cx, "wasm support unavailable");
1060
return false;
1061
}
1062
1063
CallArgs args = CallArgsFromVp(argc, vp);
1064
1065
args.rval().set(UndefinedValue());
1066
1067
if (!args.get(0).isObject()) {
1068
JS_ReportErrorASCII(cx, "argument is not an object");
1069
return false;
1070
}
1071
1072
RootedFunction func(cx, args[0].toObject().maybeUnwrapIf<JSFunction>());
1073
1074
if (!func || !wasm::IsWasmExportedFunction(func)) {
1075
JS_ReportErrorASCII(cx, "argument is not an exported wasm function");
1076
return false;
1077
}
1078
1079
wasm::Instance& instance = wasm::ExportedFunctionToInstance(func);
1080
uint32_t funcIndex = wasm::ExportedFunctionToFuncIndex(func);
1081
1082
wasm::Tier tier = instance.code().stableTier();
1083
1084
if (args.length() > 1 &&
1085
!ConvertToTier(cx, args[1], instance.code(), &tier)) {
1086
JS_ReportErrorASCII(cx, "invalid tier");
1087
return false;
1088
}
1089
1090
if (!instance.code().hasTier(tier)) {
1091
JS_ReportErrorASCII(cx, "function missing selected tier");
1092
return false;
1093
}
1094
1095
instance.disassembleExport(cx, funcIndex, tier, [](const char* text) {
1096
fprintf(stderr, "%s\n", text);
1097
});
1098
1099
return true;
1100
}
1101
1102
enum class Flag { Tier2Complete, Deserialized };
1103
1104
static bool WasmReturnFlag(JSContext* cx, unsigned argc, Value* vp, Flag flag) {
1105
CallArgs args = CallArgsFromVp(argc, vp);
1106
1107
if (!args.get(0).isObject()) {
1108
JS_ReportErrorASCII(cx, "argument is not an object");
1109
return false;
1110
}
1111
1112
Rooted<WasmModuleObject*> module(
1113
cx, args[0].toObject().maybeUnwrapIf<WasmModuleObject>());
1114
if (!module) {
1115
JS_ReportErrorASCII(cx, "argument is not a WebAssembly.Module");
1116
return false;
1117
}
1118
1119
bool b;
1120
switch (flag) {
1121
case Flag::Tier2Complete:
1122
b = !module->module().testingTier2Active();
1123
break;
1124
case Flag::Deserialized:
1125
b = module->module().loggingDeserialized();
1126
break;
1127
}
1128
1129
args.rval().set(BooleanValue(b));
1130
return true;
1131
}
1132
1133
static bool WasmHasTier2CompilationCompleted(JSContext* cx, unsigned argc,
1134
Value* vp) {
1135
return WasmReturnFlag(cx, argc, vp, Flag::Tier2Complete);
1136
}
1137
1138
static bool WasmLoadedFromCache(JSContext* cx, unsigned argc, Value* vp) {
1139
return WasmReturnFlag(cx, argc, vp, Flag::Deserialized);
1140
}
1141
1142
static bool IsLazyFunction(JSContext* cx, unsigned argc, Value* vp) {
1143
CallArgs args = CallArgsFromVp(argc, vp);
1144
if (args.length() != 1) {
1145
JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
1146
return false;
1147
}
1148
if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
1149
JS_ReportErrorASCII(cx, "The first argument should be a function.");
1150
return false;
1151
}
1152
JSFunction* fun = &args[0].toObject().as<JSFunction>();
1153
args.rval().setBoolean(fun->isInterpreted() && !fun->hasBytecode());
1154
return true;
1155
}
1156
1157
static bool IsRelazifiableFunction(JSContext* cx, unsigned argc, Value* vp) {
1158
CallArgs args = CallArgsFromVp(argc, vp);
1159
if (args.length() != 1) {
1160
JS_ReportErrorASCII(cx, "The function takes exactly one argument.");
1161
return false;
1162
}
1163
if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
1164
JS_ReportErrorASCII(cx, "The first argument should be a function.");
1165
return false;
1166
}
1167
1168
JSFunction* fun = &args[0].toObject().as<JSFunction>();
1169
args.rval().setBoolean(fun->hasBytecode() &&
1170
fun->nonLazyScript()->allowRelazify());
1171
return true;
1172
}
1173
1174
static bool InternalConst(JSContext* cx, unsigned argc, Value* vp) {
1175
CallArgs args = CallArgsFromVp(argc, vp);
1176
if (args.length() == 0) {
1177
JS_ReportErrorASCII(cx, "the function takes exactly one argument");
1178
return false;
1179
}
1180
1181
JSString* str = ToString(cx, args[0]);
1182
if (!str) {
1183
return false;
1184
}
1185
JSLinearString* linear = JS_EnsureLinearString(cx, str);
1186
if (!linear) {
1187
return false;
1188
}
1189
1190
if (JS_LinearStringEqualsLiteral(linear,
1191
"INCREMENTAL_MARK_STACK_BASE_CAPACITY")) {
1192
args.rval().setNumber(uint32_t(js::INCREMENTAL_MARK_STACK_BASE_CAPACITY));
1193
} else {
1194
JS_ReportErrorASCII(cx, "unknown const name");
1195
return false;
1196
}
1197
return true;
1198
}
1199
1200
static bool GCPreserveCode(JSContext* cx, unsigned argc, Value* vp) {
1201
CallArgs args = CallArgsFromVp(argc, vp);
1202
1203
if (args.length() != 0) {
1204
RootedObject callee(cx, &args.callee());
1205
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1206
return false;
1207
}
1208
1209
cx->runtime()->gc.setAlwaysPreserveCode();
1210
1211
args.rval().setUndefined();
1212
return true;
1213
}
1214
1215
#ifdef JS_GC_ZEAL
1216
1217
static bool ParseGCZealMode(JSContext* cx, const CallArgs& args,
1218
uint8_t* zeal) {
1219
uint32_t value;
1220
if (!ToUint32(cx, args.get(0), &value)) {
1221
return false;
1222
}
1223
1224
if (value > uint32_t(gc::ZealMode::Limit)) {
1225
JS_ReportErrorASCII(cx, "gczeal argument out of range");
1226
return false;
1227
}
1228
1229
*zeal = static_cast<uint8_t>(value);
1230
return true;
1231
}
1232
1233
static bool GCZeal(JSContext* cx, unsigned argc, Value* vp) {
1234
CallArgs args = CallArgsFromVp(argc, vp);
1235
1236
if (args.length() > 2) {
1237
RootedObject callee(cx, &args.callee());
1238
ReportUsageErrorASCII(cx, callee, "Too many arguments");
1239
return false;
1240
}
1241
1242
uint8_t zeal;
1243
if (!ParseGCZealMode(cx, args, &zeal)) {
1244
return false;
1245
}
1246
1247
uint32_t frequency = JS_DEFAULT_ZEAL_FREQ;
1248
if (args.length() >= 2) {
1249
if (!ToUint32(cx, args.get(1), &frequency)) {
1250
return false;
1251
}
1252
}
1253
1254
JS_SetGCZeal(cx, zeal, frequency);
1255
args.rval().setUndefined();
1256
return true;
1257
}
1258
1259
static bool UnsetGCZeal(JSContext* cx, unsigned argc, Value* vp) {
1260
CallArgs args = CallArgsFromVp(argc, vp);
1261
1262
if (args.length() > 1) {
1263
RootedObject callee(cx, &args.callee());
1264
ReportUsageErrorASCII(cx, callee, "Too many arguments");
1265
return false;
1266
}
1267
1268
uint8_t zeal;
1269
if (!ParseGCZealMode(cx, args, &zeal)) {
1270
return false;
1271
}
1272
1273
JS_UnsetGCZeal(cx, zeal);
1274
args.rval().setUndefined();
1275
return true;
1276
}
1277
1278
static bool ScheduleGC(JSContext* cx, unsigned argc, Value* vp) {
1279
CallArgs args = CallArgsFromVp(argc, vp);
1280
1281
if (args.length() > 1) {
1282
RootedObject callee(cx, &args.callee());
1283
ReportUsageErrorASCII(cx, callee, "Too many arguments");
1284
return false;
1285
}
1286
1287
if (args.length() == 0) {
1288
/* Fetch next zeal trigger only. */
1289
} else if (args[0].isNumber()) {
1290
/* Schedule a GC to happen after |arg| allocations. */
1291
JS_ScheduleGC(cx, std::max(int(args[0].toNumber()), 0));
1292
} else {
1293
RootedObject callee(cx, &args.callee());
1294
ReportUsageErrorASCII(cx, callee, "Bad argument - expecting number");
1295
return false;
1296
}
1297
1298
uint32_t zealBits;
1299
uint32_t freq;
1300
uint32_t next;
1301
JS_GetGCZealBits(cx, &zealBits, &freq, &next);
1302
args.rval().setInt32(next);
1303
return true;
1304
}
1305
1306
static bool SelectForGC(JSContext* cx, unsigned argc, Value* vp) {
1307
CallArgs args = CallArgsFromVp(argc, vp);
1308
1309
/*
1310
* The selectedForMarking set is intended to be manually marked at slice
1311
* start to detect missing pre-barriers. It is invalid for nursery things
1312
* to be in the set, so evict the nursery before adding items.
1313
*/
1314
cx->runtime()->gc.evictNursery();
1315
1316
for (unsigned i = 0; i < args.length(); i++) {
1317
if (args[i].isObject()) {
1318
if (!cx->runtime()->gc.selectForMarking(&args[i].toObject())) {
1319
return false;
1320
}
1321
}
1322
}
1323
1324
args.rval().setUndefined();
1325
return true;
1326
}
1327
1328
static bool VerifyPreBarriers(JSContext* cx, unsigned argc, Value* vp) {
1329
CallArgs args = CallArgsFromVp(argc, vp);
1330
1331
if (args.length() > 0) {
1332
RootedObject callee(cx, &args.callee());
1333
ReportUsageErrorASCII(cx, callee, "Too many arguments");
1334
return false;
1335
}
1336
1337
gc::VerifyBarriers(cx->runtime(), gc::PreBarrierVerifier);
1338
args.rval().setUndefined();
1339
return true;
1340
}
1341
1342
static bool VerifyPostBarriers(JSContext* cx, unsigned argc, Value* vp) {
1343
// This is a no-op since the post barrier verifier was removed.
1344
CallArgs args = CallArgsFromVp(argc, vp);
1345
if (args.length()) {
1346
RootedObject callee(cx, &args.callee());
1347
ReportUsageErrorASCII(cx, callee, "Too many arguments");
1348
return false;
1349
}
1350
args.rval().setUndefined();
1351
return true;
1352
}
1353
1354
static bool CurrentGC(JSContext* cx, unsigned argc, Value* vp) {
1355
CallArgs args = CallArgsFromVp(argc, vp);
1356
1357
if (args.length() != 0) {
1358
RootedObject callee(cx, &args.callee());
1359
ReportUsageErrorASCII(cx, callee, "Too many arguments");
1360
return false;
1361
}
1362
1363
RootedObject result(cx, JS_NewPlainObject(cx));
1364
if (!result) {
1365
return false;
1366
}
1367
1368
js::gc::GCRuntime& gc = cx->runtime()->gc;
1369
const char* state = StateName(gc.state());
1370
1371
RootedString str(cx, JS_NewStringCopyZ(cx, state));
1372
if (!str) {
1373
return false;
1374
}
1375
RootedValue val(cx, StringValue(str));
1376
if (!JS_DefineProperty(cx, result, "incrementalState", val,
1377
JSPROP_ENUMERATE)) {
1378
return false;
1379
}
1380
1381
if (gc.state() == js::gc::State::Sweep) {
1382
val = Int32Value(gc.getCurrentSweepGroupIndex());
1383
if (!JS_DefineProperty(cx, result, "sweepGroup", val, JSPROP_ENUMERATE)) {
1384
return false;
1385
}
1386
}
1387
1388
val = BooleanValue(gc.isShrinkingGC());
1389
if (!JS_DefineProperty(cx, result, "isShrinking", val, JSPROP_ENUMERATE)) {
1390
return false;
1391
}
1392
1393
val = Int32Value(gc.gcNumber());
1394
if (!JS_DefineProperty(cx, result, "number", val, JSPROP_ENUMERATE)) {
1395
return false;
1396
}
1397
1398
val = Int32Value(gc.minorGCCount());
1399
if (!JS_DefineProperty(cx, result, "minorCount", val, JSPROP_ENUMERATE)) {
1400
return false;
1401
}
1402
1403
val = Int32Value(gc.majorGCCount());
1404
if (!JS_DefineProperty(cx, result, "majorCount", val, JSPROP_ENUMERATE)) {
1405
return false;
1406
}
1407
1408
val = BooleanValue(gc.isFullGc());
1409
if (!JS_DefineProperty(cx, result, "isFull", val, JSPROP_ENUMERATE)) {
1410
return false;
1411
}
1412
1413
val = BooleanValue(gc.isCompactingGc());
1414
if (!JS_DefineProperty(cx, result, "isCompacting", val, JSPROP_ENUMERATE)) {
1415
return false;
1416
}
1417
1418
# ifdef DEBUG
1419
val = Int32Value(gc.marker.queuePos);
1420
if (!JS_DefineProperty(cx, result, "queuePos", val, JSPROP_ENUMERATE)) {
1421
return false;
1422
}
1423
# endif
1424
1425
args.rval().setObject(*result);
1426
return true;
1427
}
1428
1429
static bool DeterministicGC(JSContext* cx, unsigned argc, Value* vp) {
1430
CallArgs args = CallArgsFromVp(argc, vp);
1431
1432
if (args.length() != 1) {
1433
RootedObject callee(cx, &args.callee());
1434
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1435
return false;
1436
}
1437
1438
cx->runtime()->gc.setDeterministic(ToBoolean(args[0]));
1439
args.rval().setUndefined();
1440
return true;
1441
}
1442
1443
static bool DumpGCArenaInfo(JSContext* cx, unsigned argc, Value* vp) {
1444
CallArgs args = CallArgsFromVp(argc, vp);
1445
js::gc::DumpArenaInfo();
1446
args.rval().setUndefined();
1447
return true;
1448
}
1449
1450
#endif /* JS_GC_ZEAL */
1451
1452
static bool GCState(JSContext* cx, unsigned argc, Value* vp) {
1453
CallArgs args = CallArgsFromVp(argc, vp);
1454
1455
if (args.length() > 1) {
1456
RootedObject callee(cx, &args.callee());
1457
ReportUsageErrorASCII(cx, callee, "Too many arguments");
1458
return false;
1459
}
1460
1461
const char* state;
1462
1463
if (args.length() == 1) {
1464
if (!args[0].isObject()) {
1465
RootedObject callee(cx, &args.callee());
1466
ReportUsageErrorASCII(cx, callee, "Expected object");
1467
return false;
1468
}
1469
1470
JSObject* obj = UncheckedUnwrap(&args[0].toObject());
1471
state = gc::StateName(obj->zone()->gcState());
1472
} else {
1473
state = gc::StateName(cx->runtime()->gc.state());
1474
}
1475
1476
return ReturnStringCopy(cx, args, state);
1477
}
1478
1479
static bool ScheduleZoneForGC(JSContext* cx, unsigned argc, Value* vp) {
1480
CallArgs args = CallArgsFromVp(argc, vp);
1481
1482
if (args.length() != 1) {
1483
RootedObject callee(cx, &args.callee());
1484
ReportUsageErrorASCII(cx, callee, "Expecting a single argument");
1485
return false;
1486
}
1487
1488
if (args[0].isObject()) {
1489
// Ensure that |zone| is collected during the next GC.
1490
Zone* zone = UncheckedUnwrap(&args[0].toObject())->zone();
1491
PrepareZoneForGC(zone);
1492
} else if (args[0].isString()) {
1493
// This allows us to schedule the atoms zone for GC.
1494
Zone* zone = args[0].toString()->zoneFromAnyThread();
1495
if (!CurrentThreadCanAccessZone(zone)) {
1496
RootedObject callee(cx, &args.callee());
1497
ReportUsageErrorASCII(cx, callee, "Specified zone not accessible for GC");
1498
return false;
1499
}
1500
PrepareZoneForGC(zone);
1501
} else {
1502
RootedObject callee(cx, &args.callee());
1503
ReportUsageErrorASCII(cx, callee,
1504
"Bad argument - expecting object or string");
1505
return false;
1506
}
1507
1508
args.rval().setUndefined();
1509
return true;
1510
}
1511
1512
static bool StartGC(JSContext* cx, unsigned argc, Value* vp) {
1513
CallArgs args = CallArgsFromVp(argc, vp);
1514
1515
if (args.length() > 2) {
1516
RootedObject callee(cx, &args.callee());
1517
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1518
return false;
1519
}
1520
1521
auto budget = SliceBudget::unlimited();
1522
if (args.length() >= 1) {
1523
uint32_t work = 0;
1524
if (!ToUint32(cx, args[0], &work)) {
1525
return false;
1526
}
1527
budget = SliceBudget(WorkBudget(work));
1528
}
1529
1530
bool shrinking = false;
1531
if (args.length() >= 2) {
1532
Value arg = args[1];
1533
if (arg.isString()) {
1534
if (!JS_StringEqualsLiteral(cx, arg.toString(), "shrinking",
1535
&shrinking)) {
1536
return false;
1537
}
1538
}
1539
}
1540
1541
JSRuntime* rt = cx->runtime();
1542
if (rt->gc.isIncrementalGCInProgress()) {
1543
RootedObject callee(cx, &args.callee());
1544
JS_ReportErrorASCII(cx, "Incremental GC already in progress");
1545
return false;
1546
}
1547
1548
JSGCInvocationKind gckind = shrinking ? GC_SHRINK : GC_NORMAL;
1549
rt->gc.startDebugGC(gckind, budget);
1550
1551
args.rval().setUndefined();
1552
return true;
1553
}
1554
1555
static bool FinishGC(JSContext* cx, unsigned argc, Value* vp) {
1556
CallArgs args = CallArgsFromVp(argc, vp);
1557
1558
if (args.length() > 0) {
1559
RootedObject callee(cx, &args.callee());
1560
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1561
return false;
1562
}
1563
1564
JSRuntime* rt = cx->runtime();
1565
if (rt->gc.isIncrementalGCInProgress()) {
1566
rt->gc.finishGC(JS::GCReason::DEBUG_GC);
1567
}
1568
1569
args.rval().setUndefined();
1570
return true;
1571
}
1572
1573
static bool GCSlice(JSContext* cx, unsigned argc, Value* vp) {
1574
CallArgs args = CallArgsFromVp(argc, vp);
1575
1576
if (args.length() > 1) {
1577
RootedObject callee(cx, &args.callee());
1578
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1579
return false;
1580
}
1581
1582
auto budget = SliceBudget::unlimited();
1583
if (args.length() == 1) {
1584
uint32_t work = 0;
1585
if (!ToUint32(cx, args[0], &work)) {
1586
return false;
1587
}
1588
budget = SliceBudget(WorkBudget(work));
1589
}
1590
1591
JSRuntime* rt = cx->runtime();
1592
if (!rt->gc.isIncrementalGCInProgress()) {
1593
rt->gc.startDebugGC(GC_NORMAL, budget);
1594
} else {
1595
rt->gc.debugGCSlice(budget);
1596
}
1597
1598
args.rval().setUndefined();
1599
return true;
1600
}
1601
1602
static bool AbortGC(JSContext* cx, unsigned argc, Value* vp) {
1603
CallArgs args = CallArgsFromVp(argc, vp);
1604
1605
if (args.length() != 0) {
1606
RootedObject callee(cx, &args.callee());
1607
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1608
return false;
1609
}
1610
1611
JS::AbortIncrementalGC(cx);
1612
args.rval().setUndefined();
1613
return true;
1614
}
1615
1616
static bool FullCompartmentChecks(JSContext* cx, unsigned argc, Value* vp) {
1617
CallArgs args = CallArgsFromVp(argc, vp);
1618
1619
if (args.length() != 1) {
1620
RootedObject callee(cx, &args.callee());
1621
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1622
return false;
1623
}
1624
1625
cx->runtime()->gc.setFullCompartmentChecks(ToBoolean(args[0]));
1626
args.rval().setUndefined();
1627
return true;
1628
}
1629
1630
static bool NondeterministicGetWeakMapKeys(JSContext* cx, unsigned argc,
1631
Value* vp) {
1632
CallArgs args = CallArgsFromVp(argc, vp);
1633
1634
if (args.length() != 1) {
1635
RootedObject callee(cx, &args.callee());
1636
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
1637
return false;
1638
}
1639
if (!args[0].isObject()) {
1640
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1641
JSMSG_NOT_EXPECTED_TYPE,
1642
"nondeterministicGetWeakMapKeys", "WeakMap",
1643
InformalValueTypeName(args[0]));
1644
return false;
1645
}
1646
RootedObject arr(cx);
1647
RootedObject mapObj(cx, &args[0].toObject());
1648
if (!JS_NondeterministicGetWeakMapKeys(cx, mapObj, &arr)) {
1649
return false;
1650
}
1651
if (!arr) {
1652
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1653
JSMSG_NOT_EXPECTED_TYPE,
1654
"nondeterministicGetWeakMapKeys", "WeakMap",
1655
args[0].toObject().getClass()->name);
1656
return false;
1657
}
1658
args.rval().setObject(*arr);
1659
return true;
1660
}
1661
1662
class HasChildTracer final : public JS::CallbackTracer {
1663
RootedValue child_;
1664
bool found_;
1665
1666
bool onChild(const JS::GCCellPtr& thing) override {
1667
if (thing.asCell() == child_.toGCThing()) {
1668
found_ = true;
1669
}
1670
return true;
1671
}
1672
1673
public:
1674
HasChildTracer(JSContext* cx, HandleValue child)
1675
: JS::CallbackTracer(cx, TraceWeakMapKeysValues),
1676
child_(cx, child),
1677
found_(false) {}
1678
1679
bool found() const { return found_; }
1680
};
1681
1682
static bool HasChild(JSContext* cx, unsigned argc, Value* vp) {
1683
CallArgs args = CallArgsFromVp(argc, vp);
1684
RootedValue parent(cx, args.get(0));
1685
RootedValue child(cx, args.get(1));
1686
1687
if (!parent.isGCThing() || !child.isGCThing()) {
1688
args.rval().setBoolean(false);
1689
return true;
1690
}
1691
1692
HasChildTracer trc(cx, child);
1693
TraceChildren(&trc, parent.toGCThing(), parent.traceKind());
1694
args.rval().setBoolean(trc.found());
1695
return true;
1696
}
1697
1698
static bool SetSavedStacksRNGState(JSContext* cx, unsigned argc, Value* vp) {
1699
CallArgs args = CallArgsFromVp(argc, vp);
1700
if (!args.requireAtLeast(cx, "setSavedStacksRNGState", 1)) {
1701
return false;
1702
}
1703
1704
int32_t seed;
1705
if (!ToInt32(cx, args[0], &seed)) {
1706
return false;
1707
}
1708
1709
// Either one or the other of the seed arguments must be non-zero;
1710
// make this true no matter what value 'seed' has.
1711
cx->realm()->savedStacks().setRNGState(seed, (seed + 1) * 33);
1712
return true;
1713
}
1714
1715
static bool GetSavedFrameCount(JSContext* cx, unsigned argc, Value* vp) {
1716
CallArgs args = CallArgsFromVp(argc, vp);
1717
args.rval().setNumber(cx->realm()->savedStacks().count());
1718
return true;
1719
}
1720
1721
static bool ClearSavedFrames(JSContext* cx, unsigned argc, Value* vp) {
1722
CallArgs args = CallArgsFromVp(argc, vp);
1723
1724
js::SavedStacks& savedStacks = cx->realm()->savedStacks();
1725
savedStacks.clear();
1726
1727
for (ActivationIterator iter(cx); !iter.done(); ++iter) {
1728
iter->clearLiveSavedFrameCache();
1729
}
1730
1731
args.rval().setUndefined();
1732
return true;
1733
}
1734
1735
static bool SaveStack(JSContext* cx, unsigned argc, Value* vp) {
1736
CallArgs args = CallArgsFromVp(argc, vp);
1737
1738
JS::StackCapture capture((JS::AllFrames()));
1739
if (args.length() >= 1) {
1740
double maxDouble;
1741
if (!ToNumber(cx, args[0], &maxDouble)) {
1742
return false;
1743
}
1744
if (mozilla::IsNaN(maxDouble) || maxDouble < 0 || maxDouble > UINT32_MAX) {
1745
ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, args[0],
1746
nullptr, "not a valid maximum frame count");
1747
return false;
1748
}
1749
uint32_t max = uint32_t(maxDouble);
1750
if (max > 0) {
1751
capture = JS::StackCapture(JS::MaxFrames(max));
1752
}
1753
}
1754
1755
RootedObject compartmentObject(cx);
1756
if (args.length() >= 2) {
1757
if (!args[1].isObject()) {