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