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